From b12a97e05293b485fedc1c7aeb1043efd285e5f9 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 24 Apr 2023 23:31:20 +0900 Subject: [PATCH 0001/1020] arm64: dts: apple: t8112: Remove always-on from the PMP node This should now work properly with power domain dependencies. With "apple,always-on" removed from ps_pmp add it as dependency for the dcp* power-domains. Fixes dcp crashes on power state changes. TODO: investigate if it is enough to power ps_pmp on during SetPowerState calls. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112-pmgr.dtsi | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi index 7c050c6f2707a1..118694dd9b5f06 100644 --- a/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi @@ -672,7 +672,7 @@ #power-domain-cells = <0>; #reset-cells = <0>; label = "disp0_fe"; - power-domains = <&ps_disp0_sys>; + power-domains = <&ps_disp0_sys>, <&ps_pmp>; apple,always-on; /* TODO: figure out if we can enable PM here */ }; @@ -691,7 +691,7 @@ #power-domain-cells = <0>; #reset-cells = <0>; label = "dispext_fe"; - power-domains = <&ps_dispext_sys>; + power-domains = <&ps_dispext_sys>, <&ps_pmp>; }; ps_dispext_cpu0: power-controller@3c8 { @@ -773,7 +773,6 @@ #power-domain-cells = <0>; #reset-cells = <0>; label = "pmp"; - apple,always-on; }; ps_pms_sram: power-controller@418 { From 7591c3e39f4310a0fc01dcdf4412654b06b06654 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 14 Feb 2023 10:07:49 +0100 Subject: [PATCH 0002/1020] arm64: dts: apple: t8112: Add wlan/bt PCIe device nodes Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112-j473.dts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j473.dts b/arch/arm64/boot/dts/apple/t8112-j473.dts index 06fe257f08be49..4362d9081bd7b9 100644 --- a/arch/arm64/boot/dts/apple/t8112-j473.dts +++ b/arch/arm64/boot/dts/apple/t8112-j473.dts @@ -17,7 +17,9 @@ model = "Apple Mac mini (M2, 2023)"; aliases { + bluetooth0 = &bluetooth0; ethernet0 = ðernet0; + wifi0 = &wifi0; }; }; @@ -28,6 +30,22 @@ */ &port00 { bus-range = <1 1>; + wifi0: wifi@0,0 { + compatible = "pci14e4,4434"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + /* To be filled by the loader */ + local-mac-address = [00 10 18 00 00 10]; + apple,antenna-sku = "XX"; + brcm,board-type = "apple,miyake"; + }; + + bluetooth0: bluetooth@0,1 { + compatible = "pci14e4,5f72"; + reg = <0x10100 0x0 0x0 0x0 0x0>; + /* To be filled by the loader */ + local-bd-address = [00 00 00 00 00 00]; + brcm,board-type = "apple,miyake"; + }; }; &port01 { From 198ea3ef9ff66707f7b10f1370204176e826526d Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 15 Feb 2022 18:54:35 +0900 Subject: [PATCH 0003/1020] arm64: dts: apple: t8112: Add PMU NVMEM and SMC RTC/reboot nodes Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8112.dtsi | 78 ++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index 1666e6ab250bc0..e68f5042e1b339 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -641,6 +641,84 @@ }; }; + nub_spmi: spmi@23d0d9300 { + compatible = "apple,t8112-spmi", "apple,spmi"; + reg = <0x2 0x3d714000 0x0 0x100>; + #address-cells = <2>; + #size-cells = <0>; + interrupt-parent = <&aic>; + interrupts = , + ; + + pmu1: pmu@e { + compatible = "apple,stowe-pmu", "apple,spmi-pmu"; + reg = <0xe SPMI_USID>; + #address-cells = <1>; + #size-cells = <1>; + + rtc_nvmem@f800 { + compatible = "apple,spmi-pmu-nvmem"; + reg = <0xf800 0x300>; + #address-cells = <1>; + #size-cells = <1>; + + pm_setting: pm-setting@1 { + reg = <0x1 0x1>; + }; + + rtc_offset: rtc-offset@100 { + reg = <0x100 0x6>; + }; + }; + + legacy_nvmem@f700 { + compatible = "apple,spmi-pmu-nvmem"; + reg = <0xf700 0x20>; + #address-cells = <1>; + #size-cells = <1>; + + boot_stage: boot-stage@1 { + reg = <0x1 0x1>; + }; + + boot_error_count: boot-error-count@2 { + reg = <0x2 0x1>; + bits = <0 4>; + }; + + panic_count: panic-count@2 { + reg = <0x2 0x1>; + bits = <4 4>; + }; + + boot_error_stage: boot-error-stage@3 { + reg = <0x3 0x1>; + }; + + shutdown_flag: shutdown-flag@f { + reg = <0xf 0x1>; + bits = <3 1>; + }; + }; + + scrpad_nvmem@8000 { + compatible = "apple,spmi-pmu-nvmem"; + reg = <0x8000 0x2800>; + #address-cells = <1>; + #size-cells = <1>; + + fault_shadow: fault-shadow@67b { + reg = <0x67b 0x10>; + }; + + socd: socd@b00 { + reg = <0xb00 0x400>; + }; + }; + + }; + }; + pinctrl_nub: pinctrl@23d1f0000 { compatible = "apple,t8112-pinctrl", "apple,pinctrl"; reg = <0x2 0x3d1f0000 0x0 0x4000>; From 234e235d5f08e78b688848bd0d30f6b8be38baaf Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 2 Feb 2023 14:33:26 +0100 Subject: [PATCH 0004/1020] arm64: dts: apple: t8112-j493: Add spi3 node Used for the touchbar, clock frequency is probably wrong. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112.dtsi | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index e68f5042e1b339..1b5031a3c932fd 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -467,6 +467,20 @@ status = "disabled"; }; + spi3: spi@23510c000 { + compatible = "apple,t8112-spi", "apple,spi"; + reg = <0x2 0x3510c000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + clocks = <&clkref>; + pinctrl-0 = <&spi3_pins>; + pinctrl-names = "default"; + power-domains = <&ps_spi3>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + serial0: serial@235200000 { compatible = "apple,s5l-uart"; reg = <0x2 0x35200000 0x0 0x1000>; @@ -627,10 +641,10 @@ }; spi3_pins: spi3-pins { - pinmux = , - , - , - ; + pinmux = , + , + , + ; }; pcie_pins: pcie-pins { From cace51fa825b209e59c5cac0bade66907f3dfb37 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 4 Feb 2022 12:59:39 +0900 Subject: [PATCH 0005/1020] arm64: dts: apple: t8112: Add SMC node to devicetree Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8112-j413.dts | 1 + arch/arm64/boot/dts/apple/t8112-j473.dts | 2 ++ arch/arm64/boot/dts/apple/t8112-j493.dts | 1 + arch/arm64/boot/dts/apple/t8112.dtsi | 38 ++++++++++++++++++++++++ 4 files changed, 42 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j413.dts b/arch/arm64/boot/dts/apple/t8112-j413.dts index 6f69658623bf89..66049c75b8d0e1 100644 --- a/arch/arm64/boot/dts/apple/t8112-j413.dts +++ b/arch/arm64/boot/dts/apple/t8112-j413.dts @@ -42,6 +42,7 @@ */ &port00 { bus-range = <1 1>; + pwren-gpios = <&smc_gpio 13 GPIO_ACTIVE_HIGH>; wifi0: wifi@0,0 { compatible = "pci14e4,4433"; reg = <0x10000 0x0 0x0 0x0 0x0>; diff --git a/arch/arm64/boot/dts/apple/t8112-j473.dts b/arch/arm64/boot/dts/apple/t8112-j473.dts index 4362d9081bd7b9..33904f3c9ff8f4 100644 --- a/arch/arm64/boot/dts/apple/t8112-j473.dts +++ b/arch/arm64/boot/dts/apple/t8112-j473.dts @@ -30,6 +30,7 @@ */ &port00 { bus-range = <1 1>; + pwren-gpios = <&smc_gpio 13 GPIO_ACTIVE_HIGH>; wifi0: wifi@0,0 { compatible = "pci14e4,4434"; reg = <0x10000 0x0 0x0 0x0 0x0>; @@ -50,6 +51,7 @@ &port01 { bus-range = <2 2>; + pwren-gpios = <&smc_gpio 24 GPIO_ACTIVE_HIGH>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/apple/t8112-j493.dts b/arch/arm64/boot/dts/apple/t8112-j493.dts index 0ad908349f5540..1ca48f9d71c75f 100644 --- a/arch/arm64/boot/dts/apple/t8112-j493.dts +++ b/arch/arm64/boot/dts/apple/t8112-j493.dts @@ -42,6 +42,7 @@ */ &port00 { bus-range = <1 1>; + pwren-gpios = <&smc_gpio 13 GPIO_ACTIVE_HIGH>; wifi0: wifi@0,0 { compatible = "pci14e4,4425"; reg = <0x10000 0x0 0x0 0x0 0x0>; diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index 1b5031a3c932fd..c35d2a99715a1a 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -771,6 +771,44 @@ interrupts = ; }; + smc_mbox: mbox@23e408000 { + compatible = "apple,t8112-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x2 0x3e408000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + }; + + smc: smc@23e400000 { + compatible = "apple,t8112-smc", "apple,smc"; + reg = <0x2 0x3e400000 0x0 0x4000>, + <0x2 0x3fe00000 0x0 0x100000>; + reg-names = "smc", "sram"; + mboxes = <&smc_mbox>; + + smc_gpio: gpio { + gpio-controller; + #gpio-cells = <2>; + }; + + smc_rtc: rtc { + nvmem-cells = <&rtc_offset>; + nvmem-cell-names = "rtc_offset"; + }; + + smc_reboot: reboot { + nvmem-cells = <&shutdown_flag>, <&boot_stage>, + <&boot_error_count>, <&panic_count>, <&pm_setting>; + nvmem-cell-names = "shutdown_flag", "boot_stage", + "boot_error_count", "panic_count", "pm_setting"; + }; + }; + pinctrl_smc: pinctrl@23e820000 { compatible = "apple,t8112-pinctrl", "apple,pinctrl"; reg = <0x2 0x3e820000 0x0 0x4000>; From fb21802efb1da56448ece203e7b8c802d387aa0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 19 Feb 2022 09:49:59 +0100 Subject: [PATCH 0006/1020] arm64: dts: apple: t8112*: Put in audio nodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112-j413.dts | 84 +++++++++++++++++++++++ arch/arm64/boot/dts/apple/t8112-j473.dts | 52 +++++++++++++++ arch/arm64/boot/dts/apple/t8112-j493.dts | 85 ++++++++++++++++++++++++ 3 files changed, 221 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j413.dts b/arch/arm64/boot/dts/apple/t8112-j413.dts index 66049c75b8d0e1..be0a141cf99427 100644 --- a/arch/arm64/boot/dts/apple/t8112-j413.dts +++ b/arch/arm64/boot/dts/apple/t8112-j413.dts @@ -72,6 +72,51 @@ }; }; +&i2c1 { + speaker_left_woof: codec@38 { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x38>; + shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Left Woofer"; + }; + + speaker_left_tweet: codec@39 { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x39>; + shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Left Tweeter"; + }; +}; + +&i2c3 { + speaker_right_woof: codec@3b { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x3b>; + shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Right Woofer"; + }; + + speaker_right_tweet: codec@3c { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x3c>; + shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Right Tweeter"; + }; + + jack_codec: codec@4b { + compatible = "cirrus,cs42l84"; + reg = <0x4b>; + reset-gpios = <&pinctrl_nub 12 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + interrupts-extended = <&pinctrl_ap 149 IRQ_TYPE_LEVEL_LOW>; + sound-name-prefix = "Jack"; + }; +}; + &i2c4 { status = "okay"; }; @@ -79,3 +124,42 @@ &fpwm1 { status = "okay"; }; + +/ { + sound { + compatible = "apple,j413-macaudio", "apple,macaudio"; + model = "MacBook Air J413 integrated audio"; + + dai-link@0 { + link-name = "Speakers"; + + /* + * DANGER ZONE: You can blow your speakers! + * + * The drivers are not ready, and unless you are careful + * to attenuate the audio stream, you run the risk of + * blowing your speakers. + */ + status = "disabled"; + + cpu { + sound-dai = <&mca 0>, <&mca 1>; + }; + codec { + sound-dai = <&speaker_left_woof>, <&speaker_left_tweet>, + <&speaker_right_woof>, <&speaker_right_tweet>; + }; + }; + + dai-link@1 { + link-name = "Headphone Jack"; + + cpu { + sound-dai = <&mca 2>; + }; + codec { + sound-dai = <&jack_codec>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/apple/t8112-j473.dts b/arch/arm64/boot/dts/apple/t8112-j473.dts index 33904f3c9ff8f4..83b254b0a6e4dd 100644 --- a/arch/arm64/boot/dts/apple/t8112-j473.dts +++ b/arch/arm64/boot/dts/apple/t8112-j473.dts @@ -72,3 +72,55 @@ &pcie2_dart { status = "okay"; }; + +&i2c1 { + speaker_amp: codec@38 { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x38>; + shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + interrupt-parent = <&pinctrl_ap>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + }; + + jack_codec: codec@4b { + compatible = "cirrus,cs42l84"; + reg = <0x4b>; + reset-gpios = <&pinctrl_nub 12 GPIO_ACTIVE_HIGH>; + interrupt-parent = <&pinctrl_ap>; + interrupts = <149 IRQ_TYPE_LEVEL_LOW>; + #sound-dai-cells = <0>; + cirrus,ts-inv = <1>; + sound-name-prefix = "Jack"; + }; +}; + +/ { + sound { + compatible = "apple,j473-macaudio", "apple,macaudio"; + model = "Mac mini J473"; + + dai-link@0 { + link-name = "Speaker"; + + cpu { + sound-dai = <&mca 0>; + }; + codec { + sound-dai = <&speaker_amp>; + }; + }; + + dai-link@1 { + link-name = "Headphone Jack"; + + cpu { + sound-dai = <&mca 2>; + }; + codec { + sound-dai = <&jack_codec>; + }; + }; + + }; +}; diff --git a/arch/arm64/boot/dts/apple/t8112-j493.dts b/arch/arm64/boot/dts/apple/t8112-j493.dts index 1ca48f9d71c75f..fc0b274991316b 100644 --- a/arch/arm64/boot/dts/apple/t8112-j493.dts +++ b/arch/arm64/boot/dts/apple/t8112-j493.dts @@ -61,6 +61,51 @@ }; }; +&i2c1 { + speaker_left_rear: codec@38 { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x38>; + shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Left Rear"; + }; + + speaker_left_front: codec@39 { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x39>; + shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Left Front"; + }; +}; + +&i2c3 { + speaker_right_rear: codec@3b { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x3b>; + shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Right Rear"; + }; + + speaker_right_front: codec@3c { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x3c>; + shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Right Front"; + }; + + jack_codec: codec@4b { + compatible = "cirrus,cs42l84"; + reg = <0x4b>; + reset-gpios = <&pinctrl_nub 12 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + interrupts-extended = <&pinctrl_ap 149 IRQ_TYPE_LEVEL_LOW>; + sound-name-prefix = "Jack"; + }; +}; + &i2c4 { status = "okay"; }; @@ -68,3 +113,43 @@ &fpwm1 { status = "okay"; }; + +/ { + sound { + compatible = "apple,j493-macaudio", "apple,macaudio"; + model = "MacBook Pro J493 integrated audio"; + + dai-link@0 { + link-name = "Speakers"; + + /* + * DANGER ZONE: You can blow your speakers! + * + * The drivers are not ready, and unless you are careful + * to attenuate the audio stream, you run the risk of + * blowing your speakers. + */ + status = "disabled"; + + cpu { + sound-dai = <&mca 0>, <&mca 1>; + }; + codec { + sound-dai = <&speaker_left_front>, <&speaker_left_rear>, + <&speaker_right_front>, <&speaker_right_rear>; + }; + }; + + dai-link@1 { + link-name = "Headphone Jack"; + + cpu { + sound-dai = <&mca 2>; + }; + codec { + sound-dai = <&jack_codec>; + }; + }; + }; +}; + From dfeac2b68912ef3e5031a942c7c40ccc686b8f05 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 2 Feb 2023 12:50:56 +0100 Subject: [PATCH 0007/1020] arm64: dts: apple: t8112: Add dwc3 nodes Signed-off-by: Hector Martin Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112-j413.dts | 12 +++++ arch/arm64/boot/dts/apple/t8112-j473.dts | 12 +++++ arch/arm64/boot/dts/apple/t8112-j493.dts | 12 +++++ arch/arm64/boot/dts/apple/t8112-jxxx.dtsi | 51 +++++++++++++++++++ arch/arm64/boot/dts/apple/t8112.dtsi | 60 +++++++++++++++++++++++ 5 files changed, 147 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j413.dts b/arch/arm64/boot/dts/apple/t8112-j413.dts index be0a141cf99427..0d070a89a8408e 100644 --- a/arch/arm64/boot/dts/apple/t8112-j413.dts +++ b/arch/arm64/boot/dts/apple/t8112-j413.dts @@ -61,6 +61,18 @@ }; }; +/* + * Provide labels for the USB type C ports. + */ + +&typec0 { + label = "USB-C Left-back"; +}; + +&typec1 { + label = "USB-C Left-front"; +}; + &i2c0 { /* MagSafe port */ hpm5: usb-pd@3a { diff --git a/arch/arm64/boot/dts/apple/t8112-j473.dts b/arch/arm64/boot/dts/apple/t8112-j473.dts index 83b254b0a6e4dd..4705ec980211c4 100644 --- a/arch/arm64/boot/dts/apple/t8112-j473.dts +++ b/arch/arm64/boot/dts/apple/t8112-j473.dts @@ -23,6 +23,18 @@ }; }; +/* + * Provide labels for the USB type C ports. + */ + +&typec0 { + label = "USB-C Back-left"; +}; + +&typec1 { + label = "USB-C Back-right"; +}; + /* * Force the bus number assignments so that we can declare some of the * on-board devices and properties that are populated by the bootloader diff --git a/arch/arm64/boot/dts/apple/t8112-j493.dts b/arch/arm64/boot/dts/apple/t8112-j493.dts index fc0b274991316b..f158a004a28ca3 100644 --- a/arch/arm64/boot/dts/apple/t8112-j493.dts +++ b/arch/arm64/boot/dts/apple/t8112-j493.dts @@ -61,6 +61,18 @@ }; }; +/* + * Provide labels for the USB type C ports. + */ + +&typec0 { + label = "USB-C Left-back"; +}; + +&typec1 { + label = "USB-C Left-front"; +}; + &i2c1 { speaker_left_rear: codec@38 { compatible = "ti,sn012776", "ti,tas2764"; diff --git a/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi b/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi index f5edf61113e7aa..2c14f4479c401f 100644 --- a/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi +++ b/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi @@ -53,6 +53,23 @@ interrupt-parent = <&pinctrl_ap>; interrupts = <8 IRQ_TYPE_LEVEL_LOW>; interrupt-names = "irq"; + + typec0: connector { + compatible = "usb-c-connector"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec0_con_hs: endpoint { + remote-endpoint = <&typec0_usb_hs>; + }; + }; + }; + }; }; hpm1: usb-pd@3f { @@ -61,6 +78,40 @@ interrupt-parent = <&pinctrl_ap>; interrupts = <8 IRQ_TYPE_LEVEL_LOW>; interrupt-names = "irq"; + + typec1: connector { + compatible = "usb-c-connector"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec1_con_hs: endpoint { + remote-endpoint = <&typec1_usb_hs>; + }; + }; + }; + }; + }; +}; + +/* USB controllers */ +&dwc3_0 { + port { + typec0_usb_hs: endpoint { + remote-endpoint = <&typec0_con_hs>; + }; + }; +}; + +&dwc3_1 { + port { + typec1_usb_hs: endpoint { + remote-endpoint = <&typec1_con_hs>; + }; }; }; diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index c35d2a99715a1a..8ab5d6cd11d568 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -885,6 +885,66 @@ resets = <&ps_ans>; }; + dwc3_0: usb@382280000 { + compatible = "apple,t8112-dwc3", "apple,dwc3", "snps,dwc3"; + reg = <0x3 0x82280000 0x0 0x100000>; + interrupt-parent = <&aic>; + interrupts = ; + dr_mode = "otg"; + usb-role-switch; + role-switch-default-mode = "host"; + iommus = <&dwc3_0_dart_0 0>, <&dwc3_0_dart_1 1>; + power-domains = <&ps_atc0_usb>; + }; + + dwc3_0_dart_0: iommu@382f00000 { + compatible = "apple,t8112-dart", "apple,t8110-dart"; + reg = <0x3 0x82f00000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #iommu-cells = <1>; + power-domains = <&ps_atc0_usb>; + }; + + dwc3_0_dart_1: iommu@382f80000 { + compatible = "apple,t8112-dart", "apple,t8110-dart"; + reg = <0x3 0x82f80000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #iommu-cells = <1>; + power-domains = <&ps_atc0_usb>; + }; + + dwc3_1: usb@502280000 { + compatible = "apple,t8112-dwc3", "apple,dwc3", "snps,dwc3"; + reg = <0x5 0x02280000 0x0 0x100000>; + interrupt-parent = <&aic>; + interrupts = ; + dr_mode = "otg"; + usb-role-switch; + role-switch-default-mode = "host"; + iommus = <&dwc3_1_dart_0 0>, <&dwc3_1_dart_1 1>; + power-domains = <&ps_atc1_usb>; + }; + + dwc3_1_dart_0: iommu@502f00000 { + compatible = "apple,t8112-dart", "apple,t8110-dart"; + reg = <0x5 0x02f00000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #iommu-cells = <1>; + power-domains = <&ps_atc1_usb>; + }; + + dwc3_1_dart_1: iommu@502f80000 { + compatible = "apple,t8112-dart", "apple,t8110-dart"; + reg = <0x5 0x02f80000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #iommu-cells = <1>; + power-domains = <&ps_atc1_usb>; + }; + pcie0_dart: iommu@681008000 { compatible = "apple,t8110-dart"; reg = <0x6 0x81008000 0x0 0x4000>; From df8e1b8cd933e329e7b938977daaabc538bb41eb Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 2 Feb 2023 11:15:35 +0100 Subject: [PATCH 0008/1020] arm64: dts: apple: t8112: Add mtp device nodes for j413/j493 Those provide trackpad and keyboard for j413/j493. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112-j413.dts | 33 +++++++++++ arch/arm64/boot/dts/apple/t8112-j493.dts | 32 +++++++++++ arch/arm64/boot/dts/apple/t8112.dtsi | 73 ++++++++++++++++++++++++ 3 files changed, 138 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j413.dts b/arch/arm64/boot/dts/apple/t8112-j413.dts index 0d070a89a8408e..bce2774ff0a406 100644 --- a/arch/arm64/boot/dts/apple/t8112-j413.dts +++ b/arch/arm64/boot/dts/apple/t8112-j413.dts @@ -175,3 +175,36 @@ }; }; }; + +&mtp { + status = "okay"; +}; +&mtp_mbox { + status = "okay"; +}; +&mtp_dart { + status = "okay"; +}; +&mtp_dockchannel { + status = "okay"; +}; +&mtp_hid { + apple,afe-reset-gpios = <&smc_gpio 8 GPIO_ACTIVE_LOW>; + apple,stm-reset-gpios = <&smc_gpio 24 GPIO_ACTIVE_LOW>; + + multi-touch { + firmware-name = "apple/tpmtfw-j413.bin"; + }; + + keyboard { + }; + + stm { + }; + + actuator { + }; + + tp_accel { + }; +}; diff --git a/arch/arm64/boot/dts/apple/t8112-j493.dts b/arch/arm64/boot/dts/apple/t8112-j493.dts index f158a004a28ca3..919b0762b723b5 100644 --- a/arch/arm64/boot/dts/apple/t8112-j493.dts +++ b/arch/arm64/boot/dts/apple/t8112-j493.dts @@ -165,3 +165,35 @@ }; }; +&mtp { + status = "okay"; +}; +&mtp_mbox { + status = "okay"; +}; +&mtp_dart { + status = "okay"; +}; +&mtp_dockchannel { + status = "okay"; +}; +&mtp_hid { + apple,afe-reset-gpios = <&smc_gpio 8 GPIO_ACTIVE_LOW>; + apple,stm-reset-gpios = <&smc_gpio 24 GPIO_ACTIVE_LOW>; + + multi-touch { + firmware-name = "apple/tpmtfw-j493.bin"; + }; + + keyboard { + }; + + stm { + }; + + actuator { + }; + + tp_accel { + }; +}; diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index 8ab5d6cd11d568..4904574aa2f931 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -851,6 +851,79 @@ ; }; + mtp: mtp@24e400000 { + compatible = "apple,t8112-mtp", "apple,t8112-rtk-helper-asc4", "apple,mtp", "apple,rtk-helper-asc4"; + reg = <0x2 0x4e400000 0x0 0x4000>, + <0x2 0x4ec00000 0x0 0x100000>; + reg-names = "asc", "sram"; + mboxes = <&mtp_mbox>; + iommus = <&mtp_dart 1>; + #helper-cells = <0>; + + status = "disabled"; + }; + + mtp_mbox: mbox@24e408000 { + compatible = "apple,t8112-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x2 0x4e408000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + + status = "disabled"; + }; + + mtp_dart: iommu@24e808000 { + compatible = "apple,t8112-dart", "apple,t8110-dart"; + reg = <0x2 0x4e808000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #iommu-cells = <1>; + + status = "disabled"; + }; + + mtp_dockchannel: fifo@24eb14000 { + compatible = "apple,t8112-dockchannel", "apple,dockchannel"; + reg = <0x2 0x4eb14000 0x0 0x4000>; + reg-names = "irq"; + interrupt-parent = <&aic>; + interrupts = ; + + ranges = <0 0x2 0x4eb28000 0x20000>; + #address-cells = <1>; + #size-cells = <1>; + + interrupt-controller; + #interrupt-cells = <2>; + + status = "disabled"; + + mtp_hid: input@8000 { + compatible = "apple,dockchannel-hid"; + reg = <0x8000 0x4000>, + <0xc000 0x4000>, + <0x0000 0x4000>, + <0x4000 0x4000>; + reg-names = "config", "data", + "rmt-config", "rmt-data"; + iommus = <&mtp_dart 1>; + interrupt-parent = <&mtp_dockchannel>; + interrupts = <2 IRQ_TYPE_LEVEL_HIGH>, + <3 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "tx", "rx"; + + apple,fifo-size = <0x800>; + apple,helper-cpu = <&mtp>; + }; + + }; + ans_mbox: mbox@277408000 { compatible = "apple,t8112-asc-mailbox", "apple,asc-mailbox-v4"; reg = <0x2 0x77408000 0x0 0x4000>; From a6662f9a0c3859b99c1c31e1c6a7e432d94f5386 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 26 Nov 2021 15:37:23 +0900 Subject: [PATCH 0009/1020] arm64: dts: apple: t8103: Add dwc3 nodes Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8103-j274.dts | 12 +++++ arch/arm64/boot/dts/apple/t8103-j293.dts | 12 +++++ arch/arm64/boot/dts/apple/t8103-j313.dts | 12 +++++ arch/arm64/boot/dts/apple/t8103-j456.dts | 12 +++++ arch/arm64/boot/dts/apple/t8103-j457.dts | 12 +++++ arch/arm64/boot/dts/apple/t8103-jxxx.dtsi | 51 +++++++++++++++++++ arch/arm64/boot/dts/apple/t8103.dtsi | 60 +++++++++++++++++++++++ 7 files changed, 171 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-j274.dts b/arch/arm64/boot/dts/apple/t8103-j274.dts index 1c3e37f86d46d7..968fe22163d443 100644 --- a/arch/arm64/boot/dts/apple/t8103-j274.dts +++ b/arch/arm64/boot/dts/apple/t8103-j274.dts @@ -29,6 +29,18 @@ brcm,board-type = "apple,atlantisb"; }; +/* + * Provide labels for the USB type C ports. + */ + +&typec0 { + label = "USB-C Back-left"; +}; + +&typec1 { + label = "USB-C Back-right"; +}; + /* * Force the bus number assignments so that we can declare some of the * on-board devices and properties that are populated by the bootloader diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts index 56b0c67bfcda32..58ab9b4f765ef7 100644 --- a/arch/arm64/boot/dts/apple/t8103-j293.dts +++ b/arch/arm64/boot/dts/apple/t8103-j293.dts @@ -38,6 +38,18 @@ brcm,board-type = "apple,honshu"; }; +/* + * Provide labels for the USB type C ports. + */ + +&typec0 { + label = "USB-C Left-back"; +}; + +&typec1 { + label = "USB-C Left-front"; +}; + &i2c2 { status = "okay"; }; diff --git a/arch/arm64/boot/dts/apple/t8103-j313.dts b/arch/arm64/boot/dts/apple/t8103-j313.dts index 97a4344d8dca68..bce9b911009e2b 100644 --- a/arch/arm64/boot/dts/apple/t8103-j313.dts +++ b/arch/arm64/boot/dts/apple/t8103-j313.dts @@ -41,3 +41,15 @@ &fpwm1 { status = "okay"; }; + +/* + * Provide labels for the USB type C ports. + */ + +&typec0 { + label = "USB-C Left-back"; +}; + +&typec1 { + label = "USB-C Left-front"; +}; diff --git a/arch/arm64/boot/dts/apple/t8103-j456.dts b/arch/arm64/boot/dts/apple/t8103-j456.dts index 58c8e43789b486..9983e11cacdf19 100644 --- a/arch/arm64/boot/dts/apple/t8103-j456.dts +++ b/arch/arm64/boot/dts/apple/t8103-j456.dts @@ -47,6 +47,18 @@ }; }; +/* + * Provide labels for the USB type C ports. + */ + +&typec0 { + label = "USB-C Back-right"; +}; + +&typec1 { + label = "USB-C Back-right-middle"; +}; + /* * Force the bus number assignments so that we can declare some of the * on-board devices and properties that are populated by the bootloader diff --git a/arch/arm64/boot/dts/apple/t8103-j457.dts b/arch/arm64/boot/dts/apple/t8103-j457.dts index 152f95fd49a211..a622ff607d4075 100644 --- a/arch/arm64/boot/dts/apple/t8103-j457.dts +++ b/arch/arm64/boot/dts/apple/t8103-j457.dts @@ -29,6 +29,18 @@ brcm,board-type = "apple,santorini"; }; +/* + * Provide labels for the USB type C ports. + */ + +&typec0 { + label = "USB-C Back-right"; +}; + +&typec1 { + label = "USB-C Back-left"; +}; + /* * Force the bus number assignments so that we can declare some of the * on-board devices and properties that are populated by the bootloader diff --git a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi index 5988a4eb6efaa0..36dba76c06fd47 100644 --- a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi @@ -53,6 +53,23 @@ interrupt-parent = <&pinctrl_ap>; interrupts = <106 IRQ_TYPE_LEVEL_LOW>; interrupt-names = "irq"; + + typec0: connector { + compatible = "usb-c-connector"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec0_con_hs: endpoint { + remote-endpoint = <&typec0_usb_hs>; + }; + }; + }; + }; }; hpm1: usb-pd@3f { @@ -61,6 +78,40 @@ interrupt-parent = <&pinctrl_ap>; interrupts = <106 IRQ_TYPE_LEVEL_LOW>; interrupt-names = "irq"; + + typec1: connector { + compatible = "usb-c-connector"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec1_con_hs: endpoint { + remote-endpoint = <&typec1_usb_hs>; + }; + }; + }; + }; + }; +}; + +/* USB controllers */ +&dwc3_0 { + port { + typec0_usb_hs: endpoint { + remote-endpoint = <&typec0_con_hs>; + }; + }; +}; + +&dwc3_1 { + port { + typec1_usb_hs: endpoint { + remote-endpoint = <&typec1_con_hs>; + }; }; }; diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index 9b0dad6b618444..4580f207fa1f07 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -717,6 +717,66 @@ resets = <&ps_ans2>; }; + dwc3_0: usb@382280000 { + compatible = "apple,t8103-dwc3", "apple,dwc3", "snps,dwc3"; + reg = <0x3 0x82280000 0x0 0x100000>; + interrupt-parent = <&aic>; + interrupts = ; + dr_mode = "otg"; + usb-role-switch; + role-switch-default-mode = "host"; + iommus = <&dwc3_0_dart_0 0>, <&dwc3_0_dart_1 1>; + power-domains = <&ps_atc0_usb>; + }; + + dwc3_0_dart_0: iommu@382f00000 { + compatible = "apple,t8103-dart"; + reg = <0x3 0x82f00000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #iommu-cells = <1>; + power-domains = <&ps_atc0_usb>; + }; + + dwc3_0_dart_1: iommu@382f80000 { + compatible = "apple,t8103-dart"; + reg = <0x3 0x82f80000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #iommu-cells = <1>; + power-domains = <&ps_atc0_usb>; + }; + + dwc3_1: usb@502280000 { + compatible = "apple,t8103-dwc3", "apple,dwc3", "snps,dwc3"; + reg = <0x5 0x02280000 0x0 0x100000>; + interrupt-parent = <&aic>; + interrupts = ; + dr_mode = "otg"; + usb-role-switch; + role-switch-default-mode = "host"; + iommus = <&dwc3_1_dart_0 0>, <&dwc3_1_dart_1 1>; + power-domains = <&ps_atc1_usb>; + }; + + dwc3_1_dart_0: iommu@502f00000 { + compatible = "apple,t8103-dart"; + reg = <0x5 0x02f00000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #iommu-cells = <1>; + power-domains = <&ps_atc1_usb>; + }; + + dwc3_1_dart_1: iommu@502f80000 { + compatible = "apple,t8103-dart"; + reg = <0x5 0x02f80000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #iommu-cells = <1>; + power-domains = <&ps_atc1_usb>; + }; + pcie0_dart_0: iommu@681008000 { compatible = "apple,t8103-dart"; reg = <0x6 0x81008000 0x0 0x4000>; From 574d80240a477ea185b75e3d25cdf852651b76f8 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Fri, 26 Nov 2021 00:24:15 +0100 Subject: [PATCH 0010/1020] arm64: dts: apple: t8103: Add spi3/keyboard nodes Enables keyboard and touchpad input on MacBook Air (M1, 2020) and MacBook Pro (13-inch, M1, 2020). Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8103-j293.dts | 21 ++++++++++++++++++ arch/arm64/boot/dts/apple/t8103-j313.dts | 21 ++++++++++++++++++ arch/arm64/boot/dts/apple/t8103.dtsi | 28 ++++++++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts index 58ab9b4f765ef7..0be0437c3ac230 100644 --- a/arch/arm64/boot/dts/apple/t8103-j293.dts +++ b/arch/arm64/boot/dts/apple/t8103-j293.dts @@ -50,6 +50,27 @@ label = "USB-C Left-front"; }; +&spi3 { + status = "okay"; + + hid-transport@0 { + compatible = "apple,spi-hid-transport"; + reg = <0>; + spi-max-frequency = <8000000>; + /* + * Apple's ADT specifies 20us CS change delays, and the + * SPI HID interface metadata specifies 45us. Using either + * seems not to be reliable, but adding both works, so + * best guess is they are cumulative. + */ + spi-cs-setup-delay-ns = <65000>; + spi-cs-hold-delay-ns = <65000>; + spi-cs-inactive-delay-ns = <250000>; + spien-gpios = <&pinctrl_ap 195 0>; + interrupts-extended = <&pinctrl_nub 13 IRQ_TYPE_LEVEL_LOW>; + }; +}; + &i2c2 { status = "okay"; }; diff --git a/arch/arm64/boot/dts/apple/t8103-j313.dts b/arch/arm64/boot/dts/apple/t8103-j313.dts index bce9b911009e2b..7b13e16957ead7 100644 --- a/arch/arm64/boot/dts/apple/t8103-j313.dts +++ b/arch/arm64/boot/dts/apple/t8103-j313.dts @@ -53,3 +53,24 @@ &typec1 { label = "USB-C Left-front"; }; + +&spi3 { + status = "okay"; + + hid-transport@0 { + compatible = "apple,spi-hid-transport"; + reg = <0>; + spi-max-frequency = <8000000>; + /* + * Apple's ADT specifies 20us CS change delays, and the + * SPI HID interface metadata specifies 45us. Using either + * seems not to be reliable, but adding both works, so + * best guess is they are cumulative. + */ + spi-cs-setup-delay-ns = <65000>; + spi-cs-hold-delay-ns = <65000>; + spi-cs-inactive-delay-ns = <250000>; + spien-gpios = <&pinctrl_ap 195 0>; + interrupts-extended = <&pinctrl_nub 13 IRQ_TYPE_LEVEL_LOW>; + }; +}; diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index 4580f207fa1f07..4318c89fc6cbe4 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -326,6 +326,13 @@ clock-output-names = "clkref"; }; + clk_120m: clock-120m { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <120000000>; + clock-output-names = "clk_120m"; + }; + /* * This is a fabulated representation of the input clock * to NCO since we don't know the true clock tree. @@ -441,6 +448,20 @@ status = "disabled"; }; + spi3: spi@23510c000 { + compatible = "apple,t8103-spi", "apple,spi"; + reg = <0x2 0x3510c000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + clocks = <&clk_120m>; + pinctrl-0 = <&spi3_pins>; + pinctrl-names = "default"; + power-domains = <&ps_spi3>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; /* only used in J293/J313 */ + }; + serial0: serial@235200000 { compatible = "apple,s5l-uart"; reg = <0x2 0x35200000 0x0 0x1000>; @@ -597,6 +618,13 @@ ; }; + spi3_pins: spi3-pins { + pinmux = , + , + , + ; + }; + pcie_pins: pcie-pins { pinmux = , , From 1e66aa83477553a92f4432aea82be5979ddad2ef Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 6 Feb 2022 21:22:29 +0900 Subject: [PATCH 0011/1020] arm64: dts: apple: Add PCI power enable GPIOs t8103: - WLAN (SMC PMU GPIO #13) t600x: - WLAN (SMC PMU GPIO #13) - SD (SMC PMU GPIO #26) Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8103-jxxx.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi index 36dba76c06fd47..b70cd5f64b4cc6 100644 --- a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi @@ -122,6 +122,7 @@ */ &port00 { bus-range = <1 1>; + pwren-gpios = <&smc_gpio 13 GPIO_ACTIVE_HIGH>; wifi0: network@0,0 { compatible = "pci14e4,4425"; reg = <0x10000 0x0 0x0 0x0 0x0>; From b5de3ee1fdd72c160411534cba176acf65f2a5cf Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 4 Feb 2022 12:59:39 +0900 Subject: [PATCH 0012/1020] arm64: dts: apple: Add SMC node to t8103/t6001 devicetrees Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8103.dtsi | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index 4318c89fc6cbe4..e28d07e6954d19 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -669,6 +669,32 @@ interrupts = ; }; + smc_mbox: mbox@23e408000 { + compatible = "apple,t8103-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x2 0x3e408000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + }; + + smc: smc@23e400000 { + compatible = "apple,t8103-smc", "apple,smc"; + reg = <0x2 0x3e400000 0x0 0x4000>, + <0x2 0x3fe00000 0x0 0x100000>; + reg-names = "smc", "sram"; + mboxes = <&smc_mbox>; + + smc_gpio: gpio { + gpio-controller; + #gpio-cells = <2>; + }; + }; + pinctrl_smc: pinctrl@23e820000 { compatible = "apple,t8103-pinctrl", "apple,pinctrl"; reg = <0x2 0x3e820000 0x0 0x4000>; From 8bd8ccc04e1f89c332ac4847efe1f27959b21824 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 15 Feb 2022 18:54:35 +0900 Subject: [PATCH 0013/1020] arm64: dts: apple: Add PMU NVMEM and SMC RTC/reboot nodes Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8103.dtsi | 88 ++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index e28d07e6954d19..db00ef3de56385 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -11,6 +11,7 @@ #include #include #include +#include / { compatible = "apple,t8103", "apple,arm-platform"; @@ -632,6 +633,81 @@ }; }; + nub_spmi: spmi@23d0d9300 { + compatible = "apple,t8103-spmi", "apple,spmi"; + reg = <0x2 0x3d0d9300 0x0 0x100>; + #address-cells = <2>; + #size-cells = <0>; + + pmu1: pmu@f { + compatible = "apple,sera-pmu", "apple,spmi-pmu"; + reg = <0xf SPMI_USID>; + #address-cells = <1>; + #size-cells = <1>; + + rtc_nvmem@d000 { + compatible = "apple,spmi-pmu-nvmem"; + reg = <0xd000 0x300>; + #address-cells = <1>; + #size-cells = <1>; + + pm_setting: pm-setting@1 { + reg = <0x1 0x1>; + }; + + rtc_offset: rtc-offset@100 { + reg = <0x100 0x6>; + }; + }; + + legacy_nvmem@9f00 { + compatible = "apple,spmi-pmu-nvmem"; + reg = <0x9f00 0x20>; + #address-cells = <1>; + #size-cells = <1>; + + boot_stage: boot-stage@1 { + reg = <0x1 0x1>; + }; + + boot_error_count: boot-error-count@2 { + reg = <0x2 0x1>; + bits = <0 4>; + }; + + panic_count: panic-count@2 { + reg = <0x2 0x1>; + bits = <4 4>; + }; + + boot_error_stage: boot-error-stage@3 { + reg = <0x3 0x1>; + }; + + shutdown_flag: shutdown-flag@f { + reg = <0xf 0x1>; + bits = <3 1>; + }; + }; + + scrpad_nvmem@a000 { + compatible = "apple,spmi-pmu-nvmem"; + reg = <0xa000 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + + fault_shadow: fault-shadow@67b { + reg = <0x67b 0x10>; + }; + + socd: socd@b00 { + reg = <0xb00 0x400>; + }; + }; + + }; + }; + pinctrl_nub: pinctrl@23d1f0000 { compatible = "apple,t8103-pinctrl", "apple,pinctrl"; reg = <0x2 0x3d1f0000 0x0 0x4000>; @@ -693,6 +769,18 @@ gpio-controller; #gpio-cells = <2>; }; + + smc_rtc: rtc { + nvmem-cells = <&rtc_offset>; + nvmem-cell-names = "rtc_offset"; + }; + + smc_reboot: reboot { + nvmem-cells = <&shutdown_flag>, <&boot_stage>, + <&boot_error_count>, <&panic_count>, <&pm_setting>; + nvmem-cell-names = "shutdown_flag", "boot_stage", + "boot_error_count", "panic_count", "pm_setting"; + }; }; pinctrl_smc: pinctrl@23e820000 { From 075db5d7436a9963cb4139428f978b5122f0d3af Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 3 Mar 2022 02:20:39 +0900 Subject: [PATCH 0014/1020] arm64: dts: apple: Mark ATC USB AON domains as always-on Shutting these down breaks dwc3 init done by the firmware. We probably never want to do this anyway. It might be possible remove this once a PHY driver is in place to do the init properly, but it may not be worth it. Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8103-pmgr.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi index 9645861a858c1a..1646e82bdc3692 100644 --- a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi @@ -1103,6 +1103,7 @@ #power-domain-cells = <0>; #reset-cells = <0>; label = "atc0_usb_aon"; + apple,always-on; /* Needs to stay on for dwc3 to work */ }; ps_atc1_usb_aon: power-controller@90 { @@ -1111,6 +1112,7 @@ #power-domain-cells = <0>; #reset-cells = <0>; label = "atc1_usb_aon"; + apple,always-on; /* Needs to stay on for dwc3 to work */ }; ps_atc0_usb: power-controller@98 { From 39c599be9f73b365f6b837868743967f49e64dfa Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 17 Mar 2022 23:49:07 +0900 Subject: [PATCH 0015/1020] arm64: dts: apple: Keep PCIe power domain on This causes flakiness if shut down; don't do it until we find out what's going on. Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8103-pmgr.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi index 1646e82bdc3692..18c1814585eb24 100644 --- a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi @@ -717,6 +717,7 @@ #reset-cells = <0>; label = "apcie_gp"; power-domains = <&ps_apcie>; + apple,always-on; /* Breaks things if shut down */ }; ps_ans2: power-controller@3f0 { From 762a56f3f526697c178dbf70520b696fcdceef83 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 4 Feb 2022 12:59:39 +0900 Subject: [PATCH 0016/1020] arm64: dts: apple: Add SMC node to t600x devicetrees Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t600x-die0.dtsi | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index b1c875e692c8fb..f787d9267c2fc8 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -24,6 +24,32 @@ power-domains = <&ps_aic>; }; + smc_mbox: mbox@290408000 { + compatible = "apple,t6000-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x2 0x90408000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + }; + + smc: smc@290400000 { + compatible = "apple,t6000-smc", "apple,smc"; + reg = <0x2 0x90400000 0x0 0x4000>, + <0x2 0x91e00000 0x0 0x100000>; + reg-names = "smc", "sram"; + mboxes = <&smc_mbox>; + + smc_gpio: gpio { + gpio-controller; + #gpio-cells = <2>; + }; + }; + pinctrl_smc: pinctrl@290820000 { compatible = "apple,t6000-pinctrl", "apple,pinctrl"; reg = <0x2 0x90820000 0x0 0x4000>; From 912c59350dc5b9f115488aa8f98bf82a87a8d84f Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 15 Feb 2022 18:54:35 +0900 Subject: [PATCH 0017/1020] arm64: dts: apple: Add PMU NVMEM and SMC RTC/reboot nodes Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t6001.dtsi | 1 + arch/arm64/boot/dts/apple/t6002.dtsi | 1 + arch/arm64/boot/dts/apple/t600x-die0.dtsi | 87 +++++++++++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6001.dtsi b/arch/arm64/boot/dts/apple/t6001.dtsi index 620b17e4031f06..d2cf81926f284c 100644 --- a/arch/arm64/boot/dts/apple/t6001.dtsi +++ b/arch/arm64/boot/dts/apple/t6001.dtsi @@ -11,6 +11,7 @@ #include #include #include +#include #include "multi-die-cpp.h" diff --git a/arch/arm64/boot/dts/apple/t6002.dtsi b/arch/arm64/boot/dts/apple/t6002.dtsi index a963a5011799a0..e36f422d257d8f 100644 --- a/arch/arm64/boot/dts/apple/t6002.dtsi +++ b/arch/arm64/boot/dts/apple/t6002.dtsi @@ -11,6 +11,7 @@ #include #include #include +#include #include "multi-die-cpp.h" diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index f787d9267c2fc8..6fa873ffcbb5c0 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -48,6 +48,18 @@ gpio-controller; #gpio-cells = <2>; }; + + smc_rtc: rtc { + nvmem-cells = <&rtc_offset>; + nvmem-cell-names = "rtc_offset"; + }; + + smc_reboot: reboot { + nvmem-cells = <&shutdown_flag>, <&boot_stage>, + <&boot_error_count>, <&panic_count>, <&pm_setting>; + nvmem-cell-names = "shutdown_flag", "boot_stage", + "boot_error_count", "panic_count", "pm_setting"; + }; }; pinctrl_smc: pinctrl@290820000 { @@ -79,6 +91,81 @@ interrupts = ; }; + nub_spmi0: spmi@2920a1300 { + compatible = "apple,t6000-spmi", "apple,spmi"; + reg = <0x2 0x920a1300 0x0 0x100>; + #address-cells = <2>; + #size-cells = <0>; + + pmu1: pmu@f { + compatible = "apple,maverick-pmu", "apple,spmi-pmu"; + reg = <0xf SPMI_USID>; + #address-cells = <1>; + #size-cells = <1>; + + rtc_nvmem@1400 { + compatible = "apple,spmi-pmu-nvmem"; + reg = <0x1400 0x20>; + #address-cells = <1>; + #size-cells = <1>; + + pm_setting: pm-setting@5 { + reg = <0x5 0x1>; + }; + + rtc_offset: rtc-offset@11 { + reg = <0x11 0x6>; + }; + }; + + legacy_nvmem@6000 { + compatible = "apple,spmi-pmu-nvmem"; + reg = <0x6000 0x20>; + #address-cells = <1>; + #size-cells = <1>; + + boot_stage: boot-stage@1 { + reg = <0x1 0x1>; + }; + + boot_error_count: boot-error-count@2 { + reg = <0x2 0x1>; + bits = <0 4>; + }; + + panic_count: panic-count@2 { + reg = <0x2 0x1>; + bits = <4 4>; + }; + + boot_error_stage: boot-error-stage@3 { + reg = <0x3 0x1>; + }; + + shutdown_flag: shutdown-flag@f { + reg = <0xf 0x1>; + bits = <3 1>; + }; + }; + + scrpad_nvmem@8000 { + compatible = "apple,spmi-pmu-nvmem"; + reg = <0x8000 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + + fault_shadow: fault-shadow@67b { + reg = <0x67b 0x10>; + }; + + socd: socd@b00 { + reg = <0xb00 0x400>; + }; + }; + + }; + }; + sio_dart_0: iommu@39b004000 { compatible = "apple,t6000-dart"; reg = <0x3 0x9b004000 0x0 0x4000>; From b1267a6ae3032d0d9c8a778531952effd8f1c78f Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 9 Dec 2021 21:58:10 +0900 Subject: [PATCH 0018/1020] arm64: dts: apple: t6000: Add spi1 node Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t600x-common.dtsi | 7 +++++++ arch/arm64/boot/dts/apple/t600x-die0.dtsi | 14 ++++++++++++++ arch/arm64/boot/dts/apple/t600x-gpio-pins.dtsi | 7 +++++++ 3 files changed, 28 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-common.dtsi b/arch/arm64/boot/dts/apple/t600x-common.dtsi index fa8ead69936366..87dfc13d74171f 100644 --- a/arch/arm64/boot/dts/apple/t600x-common.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-common.dtsi @@ -362,6 +362,13 @@ clock-output-names = "clkref"; }; + clk_200m: clock-200m { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <200000000>; + clock-output-names = "clk_200m"; + }; + /* * This is a fabulated representation of the input clock * to NCO since we don't know the true clock tree. diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index 6fa873ffcbb5c0..ac30b5933e8a0e 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -276,6 +276,20 @@ status = "disabled"; }; + spi1: spi@39b104000 { + compatible = "apple,t6000-spi", "apple,spi"; + reg = <0x3 0x9b104000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clk_200m>; + pinctrl-0 = <&spi1_pins>; + pinctrl-names = "default"; + power-domains = <&ps_spi1>; + status = "disabled"; + }; + serial0: serial@39b200000 { compatible = "apple,s5l-uart"; reg = <0x3 0x9b200000 0x0 0x1000>; diff --git a/arch/arm64/boot/dts/apple/t600x-gpio-pins.dtsi b/arch/arm64/boot/dts/apple/t600x-gpio-pins.dtsi index b31f1a7a2b3fc3..855dcf30a50292 100644 --- a/arch/arm64/boot/dts/apple/t600x-gpio-pins.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-gpio-pins.dtsi @@ -36,6 +36,13 @@ ; }; + spi1_pins: spi1-pins { + pinmux = , + , + , + ; + }; + pcie_pins: pcie-pins { pinmux = , , From 5e3a493a0f05b5b46c61299877290d3964f3e9d0 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 9 Dec 2021 21:58:29 +0900 Subject: [PATCH 0019/1020] arm64: dts: apple: t600x-j314-j316: Add NOR flash node Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index 2e471dfe43cf88..c1f1a4b385fca7 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -94,6 +94,18 @@ clock-frequency = <1068000000>; }; +&spi1 { + status = "disabled"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x0>; + spi-max-frequency = <25000000>; + #address-cells = <1>; + #size-cells = <1>; + }; +}; + /* PCIe devices */ &port00 { /* WLAN */ From 09942e2710bf3b4510d6692fe936365be1e2a961 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 11 Nov 2021 21:31:21 +0100 Subject: [PATCH 0020/1020] arm64: dts: apple: t600x: Add spi3 node Used for keyboard and touchpad input on MacBook Pro (14/16-inch, M1 Pro/Max, 2021). Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t600x-die0.dtsi | 14 ++++++++++++++ arch/arm64/boot/dts/apple/t600x-gpio-pins.dtsi | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index ac30b5933e8a0e..152c878ab3e005 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -290,6 +290,20 @@ status = "disabled"; }; + spi3: spi@39b10c000 { + compatible = "apple,t6000-spi", "apple,spi"; + reg = <0x3 0x9b10c000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clkref>; + pinctrl-0 = <&spi3_pins>; + pinctrl-names = "default"; + power-domains = <&ps_spi3>; + status = "disabled"; + }; + serial0: serial@39b200000 { compatible = "apple,s5l-uart"; reg = <0x3 0x9b200000 0x0 0x1000>; diff --git a/arch/arm64/boot/dts/apple/t600x-gpio-pins.dtsi b/arch/arm64/boot/dts/apple/t600x-gpio-pins.dtsi index 855dcf30a50292..1a994c3c1b79f0 100644 --- a/arch/arm64/boot/dts/apple/t600x-gpio-pins.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-gpio-pins.dtsi @@ -43,6 +43,13 @@ ; }; + spi3_pins: spi3-pins { + pinmux = , + , + , + ; + }; + pcie_pins: pcie-pins { pinmux = , , From b63b731cfeb0ff065a3212e8eb5d1b1facab0b8b Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 11 Nov 2021 21:31:21 +0100 Subject: [PATCH 0021/1020] arm64: dts: apple: j31[46]: Add keyboard nodes Enables keyboard and touchpad input on MacBook Pro (14/16-inch, M1 Pro/Max, 2021). Signed-off-by: Janne Grunau --- .../arm64/boot/dts/apple/t600x-j314-j316.dtsi | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index c1f1a4b385fca7..02571363c7ae71 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -106,6 +106,27 @@ }; }; +&spi3 { + status = "okay"; + + hid-transport@0 { + compatible = "apple,spi-hid-transport"; + reg = <0>; + spi-max-frequency = <8000000>; + /* + * Apple's ADT specifies 20us CS change delays, and the + * SPI HID interface metadata specifies 45us. Using either + * seems not to be reliable, but adding both works, so + * best guess is they are cumulative. + */ + spi-cs-setup-delay-ns = <65000>; + spi-cs-hold-delay-ns = <65000>; + spi-cs-inactive-delay-ns = <250000>; + spien-gpios = <&pinctrl_ap 194 0>; + interrupts-extended = <&pinctrl_nub 6 IRQ_TYPE_LEVEL_LOW>; + }; +}; + /* PCIe devices */ &port00 { /* WLAN */ From 4d2d10952e759b57e6b15b48d173cb49adb4cdeb Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 17 May 2022 23:54:26 +0200 Subject: [PATCH 0022/1020] arm64: dts: apple: t600x: Add dwc3 nodes Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t6002-j375d.dts | 64 +++++++++ arch/arm64/boot/dts/apple/t600x-dieX.dtsi | 124 ++++++++++++++++++ .../arm64/boot/dts/apple/t600x-j314-j316.dtsi | 92 +++++++++++++ arch/arm64/boot/dts/apple/t600x-j375.dtsi | 105 +++++++++++++++ 4 files changed, 385 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6002-j375d.dts b/arch/arm64/boot/dts/apple/t6002-j375d.dts index 3365429bdc8be9..d3d4f4461deecb 100644 --- a/arch/arm64/boot/dts/apple/t6002-j375d.dts +++ b/arch/arm64/boot/dts/apple/t6002-j375d.dts @@ -26,6 +26,24 @@ interrupt-parent = <&pinctrl_ap>; interrupts = <174 IRQ_TYPE_LEVEL_LOW>; interrupt-names = "irq"; + + typec4: connector { + compatible = "usb-c-connector"; + label = "USB-C Front Right"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec4_con_hs: endpoint { + remote-endpoint = <&typec4_usb_hs>; + }; + }; + }; + }; }; /* front-left */ @@ -35,9 +53,55 @@ interrupt-parent = <&pinctrl_ap>; interrupts = <174 IRQ_TYPE_LEVEL_LOW>; interrupt-names = "irq"; + + typec5: connector { + compatible = "usb-c-connector"; + label = "USB-C Front Left"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec5_con_hs: endpoint { + remote-endpoint = <&typec5_usb_hs>; + }; + }; + }; + }; + }; +}; + +/* USB controllers on die 1 */ +&dwc3_0_die1 { + port { + typec4_usb_hs: endpoint { + remote-endpoint = <&typec4_con_hs>; + }; }; }; +&dwc3_1_die1 { + port { + typec5_usb_hs: endpoint { + remote-endpoint = <&typec5_con_hs>; + }; + }; +}; + +/* delete unused USB nodes on die 1 */ + +/delete-node/ &dwc3_2_dart_0_die1; +/delete-node/ &dwc3_2_dart_1_die1; +/delete-node/ &dwc3_2_die1; + +/delete-node/ &dwc3_3_dart_0_die1; +/delete-node/ &dwc3_3_dart_1_die1; +/delete-node/ &dwc3_3_die1; + + /* delete unused always-on power-domains on die 1 */ /delete-node/ &ps_atc2_usb_aon_die1; diff --git a/arch/arm64/boot/dts/apple/t600x-dieX.dtsi b/arch/arm64/boot/dts/apple/t600x-dieX.dtsi index a32ff0c9d7b0c2..a5f2ef5aab7929 100644 --- a/arch/arm64/boot/dts/apple/t600x-dieX.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-dieX.dtsi @@ -119,3 +119,127 @@ interrupt-controller; #interrupt-cells = <2>; }; + + DIE_NODE(dwc3_0_dart_0): iommu@702f00000 { + compatible = "apple,t6000-dart"; + reg = <0x7 0x02f00000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_atc0_usb)>; + #iommu-cells = <1>; + }; + + DIE_NODE(dwc3_0_dart_1): iommu@702f80000 { + compatible = "apple,t6000-dart"; + reg = <0x7 0x02f80000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_atc0_usb)>; + #iommu-cells = <1>; + }; + + DIE_NODE(dwc3_0): usb@702280000 { + compatible = "apple,t6000-dwc3", "apple,dwc3", "snps,dwc3"; + reg = <0x7 0x02280000 0x0 0x100000>; + interrupt-parent = <&aic>; + interrupts = ; + /* dr_mode = "otg"; */ + usb-role-switch; + role-switch-default-mode = "host"; + iommus = <&DIE_NODE(dwc3_0_dart_0) 0>, + <&DIE_NODE(dwc3_0_dart_1) 1>; + power-domains = <&DIE_NODE(ps_atc0_usb)>; + }; + + DIE_NODE(dwc3_1_dart_0): iommu@b02f00000 { + compatible = "apple,t6000-dart"; + reg = <0xb 0x02f00000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_atc1_usb)>; + #iommu-cells = <1>; + }; + + DIE_NODE(dwc3_1_dart_1): iommu@b02f80000 { + compatible = "apple,t6000-dart"; + reg = <0xb 0x02f80000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_atc1_usb)>; + #iommu-cells = <1>; + }; + + DIE_NODE(dwc3_1): usb@b02280000 { + compatible = "apple,t6000-dwc3", "apple,dwc3", "snps,dwc3"; + reg = <0xb 0x02280000 0x0 0x100000>; + interrupt-parent = <&aic>; + interrupts = ; + /* dr_mode = "otg"; */ + usb-role-switch; + role-switch-default-mode = "host"; + iommus = <&DIE_NODE(dwc3_1_dart_0) 0>, + <&DIE_NODE(dwc3_1_dart_1) 1>; + power-domains = <&DIE_NODE(ps_atc1_usb)>; + }; + + DIE_NODE(dwc3_2_dart_0): iommu@f02f00000 { + compatible = "apple,t6000-dart"; + reg = <0xf 0x02f00000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_atc2_usb)>; + #iommu-cells = <1>; + }; + + DIE_NODE(dwc3_2_dart_1): iommu@f02f80000 { + compatible = "apple,t6000-dart"; + reg = <0xf 0x02f80000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_atc2_usb)>; + #iommu-cells = <1>; + }; + + DIE_NODE(dwc3_2): usb@f02280000 { + compatible = "apple,t6000-dwc3", "apple,dwc3", "snps,dwc3"; + reg = <0xf 0x02280000 0x0 0x100000>; + interrupt-parent = <&aic>; + interrupts = ; + /* dr_mode = "otg"; */ + usb-role-switch; + role-switch-default-mode = "host"; + iommus = <&DIE_NODE(dwc3_2_dart_0) 0>, + <&DIE_NODE(dwc3_2_dart_1) 1>; + power-domains = <&DIE_NODE(ps_atc2_usb)>; + }; + + DIE_NODE(dwc3_3_dart_0): iommu@1302f00000 { + compatible = "apple,t6000-dart"; + reg = <0x13 0x02f00000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_atc3_usb)>; + #iommu-cells = <1>; + }; + + DIE_NODE(dwc3_3_dart_1): iommu@1302f80000 { + compatible = "apple,t6000-dart"; + reg = <0x13 0x02f80000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_atc3_usb)>; + #iommu-cells = <1>; + }; + + DIE_NODE(dwc3_3): usb@1302280000 { + compatible = "apple,t6000-dwc3", "apple,dwc3", "snps,dwc3"; + reg = <0x13 0x02280000 0x0 0x100000>; + interrupt-parent = <&aic>; + interrupts = ; + /* dr_mode = "otg"; */ + usb-role-switch; + role-switch-default-mode = "host"; + iommus = <&DIE_NODE(dwc3_3_dart_0) 0>, + <&DIE_NODE(dwc3_3_dart_1) 1>; + power-domains = <&DIE_NODE(ps_atc3_usb)>; + }; diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index 02571363c7ae71..ebd7db2e08efa4 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -62,6 +62,24 @@ interrupt-parent = <&pinctrl_ap>; interrupts = <174 IRQ_TYPE_LEVEL_LOW>; interrupt-names = "irq"; + + typec0: connector { + compatible = "usb-c-connector"; + label = "USB-C Left Rear"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec0_con_hs: endpoint { + remote-endpoint = <&typec0_usb_hs>; + }; + }; + }; + }; }; hpm1: usb-pd@3f { @@ -70,6 +88,24 @@ interrupt-parent = <&pinctrl_ap>; interrupts = <174 IRQ_TYPE_LEVEL_LOW>; interrupt-names = "irq"; + + typec1: connector { + compatible = "usb-c-connector"; + label = "USB-C Left Front"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec1_con_hs: endpoint { + remote-endpoint = <&typec1_usb_hs>; + }; + }; + }; + }; }; hpm2: usb-pd@3b { @@ -78,6 +114,24 @@ interrupt-parent = <&pinctrl_ap>; interrupts = <174 IRQ_TYPE_LEVEL_LOW>; interrupt-names = "irq"; + + typec2: connector { + compatible = "usb-c-connector"; + label = "USB-C Right"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec2_con_hs: endpoint { + remote-endpoint = <&typec2_usb_hs>; + }; + }; + }; + }; }; /* MagSafe port */ @@ -152,3 +206,41 @@ &fpwm0 { status = "okay"; }; + +/* USB controllers */ +&dwc3_0 { + port { + typec0_usb_hs: endpoint { + remote-endpoint = <&typec0_con_hs>; + }; + }; +}; + +&dwc3_1 { + port { + typec1_usb_hs: endpoint { + remote-endpoint = <&typec1_con_hs>; + }; + }; +}; + +&dwc3_2 { + port { + typec2_usb_hs: endpoint { + remote-endpoint = <&typec2_con_hs>; + }; + }; +}; + +/* ATC3 is used for DisplayPort -> HDMI only */ +&dwc3_3_dart_0 { + status = "disabled"; +}; + +&dwc3_3_dart_1 { + status = "disabled"; +}; + +&dwc3_3 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi index 1e5a19e49b089d..68cbe5bd801d25 100644 --- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi @@ -48,6 +48,24 @@ interrupt-parent = <&pinctrl_ap>; interrupts = <174 IRQ_TYPE_LEVEL_LOW>; interrupt-names = "irq"; + + typec0: connector { + compatible = "usb-c-connector"; + label = "USB-C Back Left"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec0_con_hs: endpoint { + remote-endpoint = <&typec0_usb_hs>; + }; + }; + }; + }; }; hpm1: usb-pd@3f { @@ -56,6 +74,24 @@ interrupt-parent = <&pinctrl_ap>; interrupts = <174 IRQ_TYPE_LEVEL_LOW>; interrupt-names = "irq"; + + typec1: connector { + compatible = "usb-c-connector"; + label = "USB-C Back Left Middle"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec1_con_hs: endpoint { + remote-endpoint = <&typec1_usb_hs>; + }; + }; + }; + }; }; hpm2: usb-pd@3b { @@ -64,6 +100,24 @@ interrupt-parent = <&pinctrl_ap>; interrupts = <174 IRQ_TYPE_LEVEL_LOW>; interrupt-names = "irq"; + + typec2: connector { + compatible = "usb-c-connector"; + label = "USB-C Back Right Middle"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec2_con_hs: endpoint { + remote-endpoint = <&typec2_usb_hs>; + }; + }; + }; + }; }; hpm3: usb-pd@3c { @@ -72,6 +126,57 @@ interrupt-parent = <&pinctrl_ap>; interrupts = <174 IRQ_TYPE_LEVEL_LOW>; interrupt-names = "irq"; + + typec3: connector { + compatible = "usb-c-connector"; + label = "USB-C Back Right"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec3_con_hs: endpoint { + remote-endpoint = <&typec3_usb_hs>; + }; + }; + }; + }; + }; +}; + +/* USB controllers */ +&dwc3_0 { + port { + typec0_usb_hs: endpoint { + remote-endpoint = <&typec0_con_hs>; + }; + }; +}; + +&dwc3_1 { + port { + typec1_usb_hs: endpoint { + remote-endpoint = <&typec1_con_hs>; + }; + }; +}; + +&dwc3_2 { + port { + typec2_usb_hs: endpoint { + remote-endpoint = <&typec2_con_hs>; + }; + }; +}; + +&dwc3_3 { + port { + typec3_usb_hs: endpoint { + remote-endpoint = <&typec3_con_hs>; + }; }; }; From 8b013c357fe8b76f21fa096d91616f88e5aa465f Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 21 Dec 2021 17:07:17 +0900 Subject: [PATCH 0023/1020] arm64: dts: apple: Add WiFi module and antenna properties Add the new module-instance/antenna-sku properties required to select WiFi firmwares properly to all board device trees. Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t6000-j314s.dts | 4 ++++ arch/arm64/boot/dts/apple/t6000-j316s.dts | 4 ++++ arch/arm64/boot/dts/apple/t6001-j314c.dts | 4 ++++ arch/arm64/boot/dts/apple/t6001-j316c.dts | 4 ++++ arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi | 2 ++ arch/arm64/boot/dts/apple/t600x-j375.dtsi | 3 +++ 6 files changed, 21 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6000-j314s.dts b/arch/arm64/boot/dts/apple/t6000-j314s.dts index c9e192848fe3f9..ac35870ca129ce 100644 --- a/arch/arm64/boot/dts/apple/t6000-j314s.dts +++ b/arch/arm64/boot/dts/apple/t6000-j314s.dts @@ -16,3 +16,7 @@ compatible = "apple,j314s", "apple,t6000", "apple,arm-platform"; model = "Apple MacBook Pro (14-inch, M1 Pro, 2021)"; }; + +&wifi0 { + brcm,board-type = "apple,maldives"; +}; diff --git a/arch/arm64/boot/dts/apple/t6000-j316s.dts b/arch/arm64/boot/dts/apple/t6000-j316s.dts index ff1803ce23001c..77d6d8c14d741e 100644 --- a/arch/arm64/boot/dts/apple/t6000-j316s.dts +++ b/arch/arm64/boot/dts/apple/t6000-j316s.dts @@ -16,3 +16,7 @@ compatible = "apple,j316s", "apple,t6000", "apple,arm-platform"; model = "Apple MacBook Pro (16-inch, M1 Pro, 2021)"; }; + +&wifi0 { + brcm,board-type = "apple,madagascar"; +}; diff --git a/arch/arm64/boot/dts/apple/t6001-j314c.dts b/arch/arm64/boot/dts/apple/t6001-j314c.dts index 1761d15b98c12f..0a5655792a8f1c 100644 --- a/arch/arm64/boot/dts/apple/t6001-j314c.dts +++ b/arch/arm64/boot/dts/apple/t6001-j314c.dts @@ -16,3 +16,7 @@ compatible = "apple,j314c", "apple,t6001", "apple,arm-platform"; model = "Apple MacBook Pro (14-inch, M1 Max, 2021)"; }; + +&wifi0 { + brcm,board-type = "apple,maldives"; +}; diff --git a/arch/arm64/boot/dts/apple/t6001-j316c.dts b/arch/arm64/boot/dts/apple/t6001-j316c.dts index 750e9beeffc0aa..9c215531ea543e 100644 --- a/arch/arm64/boot/dts/apple/t6001-j316c.dts +++ b/arch/arm64/boot/dts/apple/t6001-j316c.dts @@ -16,3 +16,7 @@ compatible = "apple,j316c", "apple,t6001", "apple,arm-platform"; model = "Apple MacBook Pro (16-inch, M1 Max, 2021)"; }; + +&wifi0 { + brcm,board-type = "apple,madagascar"; +}; diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index ebd7db2e08efa4..e3a7ed2706d7c6 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -186,9 +186,11 @@ /* WLAN */ bus-range = <1 1>; wifi0: wifi@0,0 { + compatible = "pci14e4,4433"; reg = <0x10000 0x0 0x0 0x0 0x0>; /* To be filled by the loader */ local-mac-address = [00 10 18 00 00 10]; + apple,antenna-sku = "XX"; }; }; diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi index 68cbe5bd801d25..bb2f1efce4a8a4 100644 --- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi @@ -190,6 +190,9 @@ bus-range = <1 1>; wifi0: wifi@0,0 { reg = <0x10000 0x0 0x0 0x0 0x0>; + compatible = "pci14e4,4433"; + brcm,board-type = "apple,okinawa"; + apple,antenna-sku = "XX"; /* To be filled by the loader */ local-mac-address = [00 10 18 00 00 10]; }; From 0156270ba28a184c7545954280dba194615b6b93 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 6 Feb 2022 21:22:29 +0900 Subject: [PATCH 0024/1020] arm64: dts: apple: Add PCI power enable GPIOs t8103: - WLAN (SMC PMU GPIO #13) t600x: - WLAN (SMC PMU GPIO #13) - SD (SMC PMU GPIO #26) Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi | 2 ++ arch/arm64/boot/dts/apple/t600x-j375.dtsi | 3 +++ 2 files changed, 5 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index e3a7ed2706d7c6..2343ea2d057e42 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -185,6 +185,7 @@ &port00 { /* WLAN */ bus-range = <1 1>; + pwren-gpios = <&smc_gpio 13 GPIO_ACTIVE_HIGH>; wifi0: wifi@0,0 { compatible = "pci14e4,4433"; reg = <0x10000 0x0 0x0 0x0 0x0>; @@ -197,6 +198,7 @@ &port01 { /* SD card reader */ bus-range = <2 2>; + pwren-gpios = <&smc_gpio 26 GPIO_ACTIVE_HIGH>; sdhci0: mmc@0,0 { compatible = "pci17a0,9755"; reg = <0x20000 0x0 0x0 0x0 0x0>; diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi index bb2f1efce4a8a4..dea1d0be740468 100644 --- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi @@ -188,6 +188,7 @@ &port00 { /* WLAN */ bus-range = <1 1>; + pwren-gpios = <&smc_gpio 13 GPIO_ACTIVE_HIGH>; wifi0: wifi@0,0 { reg = <0x10000 0x0 0x0 0x0 0x0>; compatible = "pci14e4,4433"; @@ -201,6 +202,7 @@ &port01 { /* SD card reader */ bus-range = <2 2>; + pwren-gpios = <&smc_gpio 26 GPIO_ACTIVE_HIGH>; sdhci0: mmc@0,0 { compatible = "pci17a0,9755"; reg = <0x20000 0x0 0x0 0x0 0x0>; @@ -223,6 +225,7 @@ &port03 { /* USB xHCI */ bus-range = <4 4>; + pwren-gpios = <&smc_gpio 20 GPIO_ACTIVE_HIGH>; status = "okay"; }; From f292ea622e953ccd2ec20afb1df04e994f4c4b78 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 27 Jun 2022 22:21:34 +0900 Subject: [PATCH 0025/1020] arm64: dts: apple: t8103: Fix spi4 power domain sort order Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8103-pmgr.dtsi | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi index 18c1814585eb24..299b1f51b54179 100644 --- a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi @@ -387,6 +387,15 @@ power-domains = <&ps_sio>, <&ps_spi_p>; }; + ps_spi4: power-controller@260 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x260 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "spi4"; + power-domains = <&ps_sio>, <&ps_spi_p>; + }; + ps_uart_n: power-controller@268 { compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; reg = <0x268 4>; @@ -558,15 +567,6 @@ apple,always-on; /* Memory controller */ }; - ps_spi4: power-controller@260 { - compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; - reg = <0x260 4>; - #power-domain-cells = <0>; - #reset-cells = <0>; - label = "spi4"; - power-domains = <&ps_sio>, <&ps_spi_p>; - }; - ps_dcs0: power-controller@300 { compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; reg = <0x300 4>; From 24ae32d918bc1bc08aacd7e2906fd3538c3e05ee Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 11 Jul 2022 20:05:02 +0900 Subject: [PATCH 0026/1020] arm64: dts: apple: t600x: Add bluetooth device trees Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t6000-j314s.dts | 4 ++++ arch/arm64/boot/dts/apple/t6000-j316s.dts | 4 ++++ arch/arm64/boot/dts/apple/t6001-j314c.dts | 4 ++++ arch/arm64/boot/dts/apple/t6001-j316c.dts | 4 ++++ arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi | 8 ++++++++ arch/arm64/boot/dts/apple/t600x-j375.dtsi | 9 +++++++++ 6 files changed, 33 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6000-j314s.dts b/arch/arm64/boot/dts/apple/t6000-j314s.dts index ac35870ca129ce..1430b91ff1b152 100644 --- a/arch/arm64/boot/dts/apple/t6000-j314s.dts +++ b/arch/arm64/boot/dts/apple/t6000-j314s.dts @@ -20,3 +20,7 @@ &wifi0 { brcm,board-type = "apple,maldives"; }; + +&bluetooth0 { + brcm,board-type = "apple,maldives"; +}; diff --git a/arch/arm64/boot/dts/apple/t6000-j316s.dts b/arch/arm64/boot/dts/apple/t6000-j316s.dts index 77d6d8c14d741e..da0cbe7d96736b 100644 --- a/arch/arm64/boot/dts/apple/t6000-j316s.dts +++ b/arch/arm64/boot/dts/apple/t6000-j316s.dts @@ -20,3 +20,7 @@ &wifi0 { brcm,board-type = "apple,madagascar"; }; + +&bluetooth0 { + brcm,board-type = "apple,madagascar"; +}; diff --git a/arch/arm64/boot/dts/apple/t6001-j314c.dts b/arch/arm64/boot/dts/apple/t6001-j314c.dts index 0a5655792a8f1c..c37097dcfdb304 100644 --- a/arch/arm64/boot/dts/apple/t6001-j314c.dts +++ b/arch/arm64/boot/dts/apple/t6001-j314c.dts @@ -20,3 +20,7 @@ &wifi0 { brcm,board-type = "apple,maldives"; }; + +&bluetooth0 { + brcm,board-type = "apple,maldives"; +}; diff --git a/arch/arm64/boot/dts/apple/t6001-j316c.dts b/arch/arm64/boot/dts/apple/t6001-j316c.dts index 9c215531ea543e..3bc6e0c3294cf9 100644 --- a/arch/arm64/boot/dts/apple/t6001-j316c.dts +++ b/arch/arm64/boot/dts/apple/t6001-j316c.dts @@ -20,3 +20,7 @@ &wifi0 { brcm,board-type = "apple,madagascar"; }; + +&bluetooth0 { + brcm,board-type = "apple,madagascar"; +}; diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index 2343ea2d057e42..4fd90bf33e1f9e 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -13,6 +13,7 @@ / { aliases { + bluetooth0 = &bluetooth0; serial0 = &serial0; wifi0 = &wifi0; }; @@ -193,6 +194,13 @@ local-mac-address = [00 10 18 00 00 10]; apple,antenna-sku = "XX"; }; + + bluetooth0: network@0,1 { + compatible = "pci14e4,5f71"; + reg = <0x10100 0x0 0x0 0x0 0x0>; + /* To be filled by the loader */ + local-bd-address = [00 00 00 00 00 00]; + }; }; &port01 { diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi index dea1d0be740468..e9260a84695abe 100644 --- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi @@ -11,6 +11,7 @@ / { aliases { + bluetooth0 = &bluetooth0; serial0 = &serial0; wifi0 = &wifi0; }; @@ -197,6 +198,14 @@ /* To be filled by the loader */ local-mac-address = [00 10 18 00 00 10]; }; + + bluetooth0: network@0,1 { + compatible = "pci14e4,5f71"; + brcm,board-type = "apple,okinawa"; + reg = <0x10100 0x0 0x0 0x0 0x0>; + /* To be filled by the loader */ + local-bd-address = [00 00 00 00 00 00]; + }; }; &port01 { From 84ccd057a1f6a8eff6bc57712247679b550fe6f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 19 Feb 2022 09:49:59 +0100 Subject: [PATCH 0027/1020] arm64: dts: apple: t8103*: Put in audio nodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- arch/arm64/boot/dts/apple/t8103-j274.dts | 50 ++++++++++++++ arch/arm64/boot/dts/apple/t8103-j293.dts | 85 ++++++++++++++++++++++++ arch/arm64/boot/dts/apple/t8103-j313.dts | 68 +++++++++++++++++++ arch/arm64/boot/dts/apple/t8103-j456.dts | 31 +++++++++ arch/arm64/boot/dts/apple/t8103-j457.dts | 31 +++++++++ 5 files changed, 265 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-j274.dts b/arch/arm64/boot/dts/apple/t8103-j274.dts index 968fe22163d443..6782d4bed42995 100644 --- a/arch/arm64/boot/dts/apple/t8103-j274.dts +++ b/arch/arm64/boot/dts/apple/t8103-j274.dts @@ -70,6 +70,56 @@ status = "okay"; }; +&i2c1 { + speaker_amp: codec@31 { + compatible = "ti,tas5770l", "ti,tas2770"; + reg = <0x31>; + shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + }; +}; + &i2c2 { status = "okay"; + + jack_codec: codec@48 { + compatible = "cirrus,cs42l83"; + reg = <0x48>; + reset-gpios = <&pinctrl_nub 11 GPIO_ACTIVE_HIGH>; + interrupt-parent = <&pinctrl_ap>; + interrupts = <183 IRQ_TYPE_LEVEL_LOW>; + #sound-dai-cells = <0>; + cirrus,ts-inv = <1>; + sound-name-prefix = "Jack"; + }; +}; + +/ { + sound { + compatible = "apple,j274-macaudio", "apple,macaudio"; + model = "Mac mini J274 integrated audio"; + + dai-link@0 { + link-name = "Speaker"; + + cpu { + sound-dai = <&mca 0>; + }; + codec { + sound-dai = <&speaker_amp>; + }; + }; + + dai-link@1 { + link-name = "Headphone Jack"; + + cpu { + sound-dai = <&mca 2>; + }; + codec { + sound-dai = <&jack_codec>; + }; + }; + + }; }; diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts index 0be0437c3ac230..2ab533ae753917 100644 --- a/arch/arm64/boot/dts/apple/t8103-j293.dts +++ b/arch/arm64/boot/dts/apple/t8103-j293.dts @@ -71,8 +71,55 @@ }; }; +&i2c1 { + speaker_left_rear: codec@31 { + compatible = "ti,tas5770l", "ti,tas2770"; + reg = <0x31>; + shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Left Rear"; + }; + + speaker_left_front: codec@32 { + compatible = "ti,tas5770l", "ti,tas2770"; + reg = <0x32>; + shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Left Front"; + }; +}; + &i2c2 { status = "okay"; + + jack_codec: codec@48 { + compatible = "cirrus,cs42l83"; + reg = <0x48>; + reset-gpios = <&pinctrl_nub 11 GPIO_ACTIVE_HIGH>; + interrupt-parent = <&pinctrl_ap>; + interrupts = <183 IRQ_TYPE_LEVEL_LOW>; + #sound-dai-cells = <0>; + cirrus,ts-inv = <1>; + sound-name-prefix = "Jack"; + }; +}; + +&i2c3 { + speaker_right_rear: codec@34 { + compatible = "ti,tas5770l", "ti,tas2770"; + reg = <0x34>; + shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Right Rear"; + }; + + speaker_right_front: codec@35 { + compatible = "ti,tas5770l", "ti,tas2770"; + reg = <0x35>; + shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Right Front"; + }; }; &i2c4 { @@ -82,3 +129,41 @@ &fpwm1 { status = "okay"; }; +/ { + sound { + compatible = "apple,j293-macaudio", "apple,macaudio"; + model = "MacBook Pro J293 integrated audio"; + + dai-link@0 { + link-name = "Speakers"; + + /* + * DANGER ZONE: You can blow your speakers! + * + * The drivers are not ready, and unless you are careful + * to attenuate the audio stream, you run the risk of + * blowing your speakers. + */ + status = "disabled"; + + cpu { + sound-dai = <&mca 0>, <&mca 1>; + }; + codec { + sound-dai = <&speaker_left_front>, <&speaker_left_rear>, + <&speaker_right_front>, <&speaker_right_rear>; + }; + }; + + dai-link@1 { + link-name = "Headphone Jack"; + + cpu { + sound-dai = <&mca 2>; + }; + codec { + sound-dai = <&jack_codec>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/apple/t8103-j313.dts b/arch/arm64/boot/dts/apple/t8103-j313.dts index 7b13e16957ead7..c20d7d109fa32d 100644 --- a/arch/arm64/boot/dts/apple/t8103-j313.dts +++ b/arch/arm64/boot/dts/apple/t8103-j313.dts @@ -74,3 +74,71 @@ interrupts-extended = <&pinctrl_nub 13 IRQ_TYPE_LEVEL_LOW>; }; }; + +&i2c1 { + speaker_left: codec@31 { + compatible = "ti,tas5770l", "ti,tas2770"; + reg = <0x31>; + shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Left"; + }; +}; + +&i2c3 { + speaker_right: codec@34 { + compatible = "ti,tas5770l", "ti,tas2770"; + reg = <0x34>; + shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Right"; + }; + + jack_codec: codec@48 { + compatible = "cirrus,cs42l83"; + reg = <0x48>; + reset-gpios = <&pinctrl_nub 11 GPIO_ACTIVE_HIGH>; + interrupt-parent = <&pinctrl_ap>; + interrupts = <183 IRQ_TYPE_LEVEL_LOW>; + #sound-dai-cells = <0>; + cirrus,ts-inv = <1>; + sound-name-prefix = "Jack"; + }; +}; + +/ { + sound { + compatible = "apple,j313-macaudio", "apple,macaudio"; + model = "MacBook Air J313 integrated audio"; + + dai-link@0 { + link-name = "Speakers"; + /* + * DANGER ZONE: You can blow your speakers! + * + * The drivers are not ready, and unless you are careful + * to attenuate the audio stream, you run the risk of + * blowing your speakers. + */ + status = "disabled"; + + cpu { + sound-dai = <&mca 0>, <&mca 1>; + }; + codec { + sound-dai = <&speaker_left>, <&speaker_right>; + }; + }; + + dai-link@1 { + link-name = "Headphone Jack"; + + cpu { + sound-dai = <&mca 2>; + }; + codec { + sound-dai = <&jack_codec>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/apple/t8103-j456.dts b/arch/arm64/boot/dts/apple/t8103-j456.dts index 9983e11cacdf19..fbecc8bcfad3d7 100644 --- a/arch/arm64/boot/dts/apple/t8103-j456.dts +++ b/arch/arm64/boot/dts/apple/t8103-j456.dts @@ -87,3 +87,34 @@ &pcie0_dart_2 { status = "okay"; }; + +&i2c1 { + jack_codec: codec@48 { + compatible = "cirrus,cs42l83"; + reg = <0x48>; + reset-gpios = <&pinctrl_nub 11 GPIO_ACTIVE_HIGH>; + interrupt-parent = <&pinctrl_ap>; + interrupts = <183 IRQ_TYPE_LEVEL_LOW>; + #sound-dai-cells = <0>; + cirrus,ts-inv = <1>; + sound-name-prefix = "Jack"; + }; +}; + +/ { + sound { + compatible = "apple,j456-macaudio", "apple,macaudio"; + model = "iMac J456 integrated audio"; + + dai-link@1 { + link-name = "Headphone Jack"; + + cpu { + sound-dai = <&mca 2>; + }; + codec { + sound-dai = <&jack_codec>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/apple/t8103-j457.dts b/arch/arm64/boot/dts/apple/t8103-j457.dts index a622ff607d4075..bba32fffd378a6 100644 --- a/arch/arm64/boot/dts/apple/t8103-j457.dts +++ b/arch/arm64/boot/dts/apple/t8103-j457.dts @@ -60,3 +60,34 @@ &pcie0_dart_2 { status = "okay"; }; + +&i2c1 { + jack_codec: codec@48 { + compatible = "cirrus,cs42l83"; + reg = <0x48>; + reset-gpios = <&pinctrl_nub 11 GPIO_ACTIVE_HIGH>; + interrupt-parent = <&pinctrl_ap>; + interrupts = <183 IRQ_TYPE_LEVEL_LOW>; + #sound-dai-cells = <0>; + cirrus,ts-inv = <1>; + sound-name-prefix = "Jack"; + }; +}; + +/ { + sound { + compatible = "apple,j457-macaudio", "apple,macaudio"; + model = "iMac J457 integrated audio"; + + dai-link@1 { + link-name = "Headphone Jack"; + + cpu { + sound-dai = <&mca 2>; + }; + codec { + sound-dai = <&jack_codec>; + }; + }; + }; +}; From a5648b1f022949e2a0fa2cffa7ff6c3f37d1dd80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 11 Mar 2022 22:16:25 +0100 Subject: [PATCH 0028/1020] arm64: dts: apple: t600x-jxxx: Put in audio nodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- arch/arm64/boot/dts/apple/t6000-j314s.dts | 4 + arch/arm64/boot/dts/apple/t6000-j316s.dts | 4 + arch/arm64/boot/dts/apple/t6001-j314c.dts | 4 + arch/arm64/boot/dts/apple/t6001-j316c.dts | 4 + arch/arm64/boot/dts/apple/t6001-j375c.dts | 5 + arch/arm64/boot/dts/apple/t6002-j375d.dts | 5 + .../arm64/boot/dts/apple/t600x-j314-j316.dtsi | 117 ++++++++++++++++++ arch/arm64/boot/dts/apple/t600x-j375.dtsi | 60 +++++++++ 8 files changed, 203 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6000-j314s.dts b/arch/arm64/boot/dts/apple/t6000-j314s.dts index 1430b91ff1b152..6c016b3b0ff11f 100644 --- a/arch/arm64/boot/dts/apple/t6000-j314s.dts +++ b/arch/arm64/boot/dts/apple/t6000-j314s.dts @@ -24,3 +24,7 @@ &bluetooth0 { brcm,board-type = "apple,maldives"; }; + +&sound { + model = "MacBook Pro J314 integrated audio"; +}; diff --git a/arch/arm64/boot/dts/apple/t6000-j316s.dts b/arch/arm64/boot/dts/apple/t6000-j316s.dts index da0cbe7d96736b..d20630560e71c2 100644 --- a/arch/arm64/boot/dts/apple/t6000-j316s.dts +++ b/arch/arm64/boot/dts/apple/t6000-j316s.dts @@ -24,3 +24,7 @@ &bluetooth0 { brcm,board-type = "apple,madagascar"; }; + +&sound { + model = "MacBook Pro J316 integrated audio"; +}; diff --git a/arch/arm64/boot/dts/apple/t6001-j314c.dts b/arch/arm64/boot/dts/apple/t6001-j314c.dts index c37097dcfdb304..ef5846baf7fdd6 100644 --- a/arch/arm64/boot/dts/apple/t6001-j314c.dts +++ b/arch/arm64/boot/dts/apple/t6001-j314c.dts @@ -24,3 +24,7 @@ &bluetooth0 { brcm,board-type = "apple,maldives"; }; + +&sound { + model = "MacBook Pro J314 integrated audio"; +}; diff --git a/arch/arm64/boot/dts/apple/t6001-j316c.dts b/arch/arm64/boot/dts/apple/t6001-j316c.dts index 3bc6e0c3294cf9..df0cee05a708a4 100644 --- a/arch/arm64/boot/dts/apple/t6001-j316c.dts +++ b/arch/arm64/boot/dts/apple/t6001-j316c.dts @@ -24,3 +24,7 @@ &bluetooth0 { brcm,board-type = "apple,madagascar"; }; + +&sound { + model = "MacBook Pro J316 integrated audio"; +}; diff --git a/arch/arm64/boot/dts/apple/t6001-j375c.dts b/arch/arm64/boot/dts/apple/t6001-j375c.dts index 62ea437b58b25c..6e08f48490b380 100644 --- a/arch/arm64/boot/dts/apple/t6001-j375c.dts +++ b/arch/arm64/boot/dts/apple/t6001-j375c.dts @@ -16,3 +16,8 @@ compatible = "apple,j375c", "apple,t6001", "apple,arm-platform"; model = "Apple Mac Studio (M1 Max, 2022)"; }; + +&sound { + compatible = "apple,j375-macaudio", "apple,macaudio"; + model = "Mac Studio J375"; +}; diff --git a/arch/arm64/boot/dts/apple/t6002-j375d.dts b/arch/arm64/boot/dts/apple/t6002-j375d.dts index d3d4f4461deecb..75eb1b2ec934cd 100644 --- a/arch/arm64/boot/dts/apple/t6002-j375d.dts +++ b/arch/arm64/boot/dts/apple/t6002-j375d.dts @@ -17,6 +17,11 @@ model = "Apple Mac Studio (M1 Ultra, 2022)"; }; +&sound { + compatible = "apple,j375-macaudio", "apple,macaudio"; + model = "Mac Studio J375"; +}; + /* USB Type C */ &i2c0 { /* front-right */ diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index 4fd90bf33e1f9e..06f009dfa373b9 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -145,6 +145,81 @@ }; }; +&i2c1 { + status = "okay"; + + speaker_left_tweet: codec@3a { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x3a>; + shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Left Tweeter"; + interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; + }; + + speaker_left_woof1: codec@38 { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x38>; + shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Left Woofer 1"; + interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; + }; + + speaker_left_woof2: codec@39 { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x39>; + shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Left Woofer 2"; + interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; + }; +}; + +&i2c2 { + status = "okay"; + + jack_codec: codec@4b { + compatible = "cirrus,cs42l84"; + reg = <0x4b>; + reset-gpios = <&pinctrl_nub 4 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + interrupts-extended = <&pinctrl_ap 180 IRQ_TYPE_LEVEL_LOW>; + sound-name-prefix = "Jack"; + }; +}; + +&i2c3 { + status = "okay"; + + speaker_right_tweet: codec@3d { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x3d>; + shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Right Tweeter"; + interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; + }; + + speaker_right_woof1: codec@3b { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x3b>; + shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Right Woofer 1"; + interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; + }; + + speaker_right_woof2: codec@3c { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x3c>; + shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Right Woofer 2"; + interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; + }; +}; + &nco_clkref { clock-frequency = <1068000000>; }; @@ -256,3 +331,45 @@ &dwc3_3 { status = "disabled"; }; + +/ { + sound: sound { + compatible = "apple,j314-macaudio", "apple,macaudio"; + + dai-link@0 { + link-name = "Speakers"; + + /* + * DANGER ZONE: You can blow your speakers! + * + * The drivers are not ready, and unless you are careful + * to attenuate the audio stream, you run the risk of + * blowing your speakers. + */ + status = "disabled"; + + cpu { + sound-dai = <&mca 0>, <&mca 1>; + }; + codec { + sound-dai = <&speaker_left_woof1>, + <&speaker_left_tweet>, + <&speaker_left_woof2>, + <&speaker_right_woof1>, + <&speaker_right_tweet>, + <&speaker_right_woof2>; + }; + }; + + dai-link@1 { + link-name = "Headphone Jack"; + + cpu { + sound-dai = <&mca 2>; + }; + codec { + sound-dai = <&jack_codec>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi index e9260a84695abe..6023105f2f5868 100644 --- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi @@ -181,10 +181,70 @@ }; }; +/* Audio */ +&i2c1 { + status = "okay"; + + speaker: codec@38 { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x38>; + shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + }; +}; + +&i2c2 { + status = "okay"; + + jack_codec: codec@4b { + compatible = "cirrus,cs42l84"; + reg = <0x4b>; + reset-gpios = <&pinctrl_nub 4 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + interrupts-extended = <&pinctrl_ap 180 IRQ_TYPE_LEVEL_LOW>; + sound-name-prefix = "Jack"; + }; +}; + &nco_clkref { clock-frequency = <1068000000>; }; +/ { + sound: sound { + /* compatible is set per machine */ + + dai-link@0 { + link-name = "Speaker"; + /* + * DANGER ZONE: You can blow your speakers! + * + * The drivers are not ready, and unless you are careful + * to attenuate the audio stream, you run the risk of + * blowing your speakers. + */ + status = "disabled"; + cpu { + sound-dai = <&mca 0>; + }; + codec { + sound-dai = <&speaker>; + }; + }; + + dai-link@1 { + link-name = "Headphone Jack"; + + cpu { + sound-dai = <&mca 2>; + }; + codec { + sound-dai = <&jack_codec>; + }; + }; + }; +}; + /* PCIe devices */ &port00 { /* WLAN */ From aeafb4a12fe0bd95f1bc7a7b116a01b8d02b5f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 30 Aug 2022 09:59:59 +0200 Subject: [PATCH 0029/1020] arm64: dts: apple: Drop 'integrated audio' from sound models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even though my preference would be to keep it in, the long name crops in a bunch of places and the verbiage at the end needs to go. Signed-off-by: Martin Povišer --- arch/arm64/boot/dts/apple/t6000-j314s.dts | 2 +- arch/arm64/boot/dts/apple/t6000-j316s.dts | 2 +- arch/arm64/boot/dts/apple/t6001-j314c.dts | 2 +- arch/arm64/boot/dts/apple/t6001-j316c.dts | 2 +- arch/arm64/boot/dts/apple/t8103-j274.dts | 2 +- arch/arm64/boot/dts/apple/t8103-j293.dts | 2 +- arch/arm64/boot/dts/apple/t8103-j313.dts | 2 +- arch/arm64/boot/dts/apple/t8103-j456.dts | 2 +- arch/arm64/boot/dts/apple/t8103-j457.dts | 2 +- arch/arm64/boot/dts/apple/t8112-j413.dts | 2 +- arch/arm64/boot/dts/apple/t8112-j493.dts | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t6000-j314s.dts b/arch/arm64/boot/dts/apple/t6000-j314s.dts index 6c016b3b0ff11f..b514b1114c4d24 100644 --- a/arch/arm64/boot/dts/apple/t6000-j314s.dts +++ b/arch/arm64/boot/dts/apple/t6000-j314s.dts @@ -26,5 +26,5 @@ }; &sound { - model = "MacBook Pro J314 integrated audio"; + 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 d20630560e71c2..7f5c91bb5f3210 100644 --- a/arch/arm64/boot/dts/apple/t6000-j316s.dts +++ b/arch/arm64/boot/dts/apple/t6000-j316s.dts @@ -26,5 +26,5 @@ }; &sound { - model = "MacBook Pro J316 integrated audio"; + 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 ef5846baf7fdd6..35ff978b998702 100644 --- a/arch/arm64/boot/dts/apple/t6001-j314c.dts +++ b/arch/arm64/boot/dts/apple/t6001-j314c.dts @@ -26,5 +26,5 @@ }; &sound { - model = "MacBook Pro J314 integrated audio"; + 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 df0cee05a708a4..6a35c87aa7ab02 100644 --- a/arch/arm64/boot/dts/apple/t6001-j316c.dts +++ b/arch/arm64/boot/dts/apple/t6001-j316c.dts @@ -26,5 +26,5 @@ }; &sound { - model = "MacBook Pro J316 integrated audio"; + model = "MacBook Pro J316"; }; diff --git a/arch/arm64/boot/dts/apple/t8103-j274.dts b/arch/arm64/boot/dts/apple/t8103-j274.dts index 6782d4bed42995..b214075000fb8a 100644 --- a/arch/arm64/boot/dts/apple/t8103-j274.dts +++ b/arch/arm64/boot/dts/apple/t8103-j274.dts @@ -97,7 +97,7 @@ / { sound { compatible = "apple,j274-macaudio", "apple,macaudio"; - model = "Mac mini J274 integrated audio"; + model = "Mac mini J274"; dai-link@0 { link-name = "Speaker"; diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts index 2ab533ae753917..3c6ee9132fe61e 100644 --- a/arch/arm64/boot/dts/apple/t8103-j293.dts +++ b/arch/arm64/boot/dts/apple/t8103-j293.dts @@ -132,7 +132,7 @@ / { sound { compatible = "apple,j293-macaudio", "apple,macaudio"; - model = "MacBook Pro J293 integrated audio"; + model = "MacBook Pro J293"; dai-link@0 { link-name = "Speakers"; diff --git a/arch/arm64/boot/dts/apple/t8103-j313.dts b/arch/arm64/boot/dts/apple/t8103-j313.dts index c20d7d109fa32d..6e96f7a4e77ae1 100644 --- a/arch/arm64/boot/dts/apple/t8103-j313.dts +++ b/arch/arm64/boot/dts/apple/t8103-j313.dts @@ -109,7 +109,7 @@ / { sound { compatible = "apple,j313-macaudio", "apple,macaudio"; - model = "MacBook Air J313 integrated audio"; + model = "MacBook Air J313"; dai-link@0 { link-name = "Speakers"; diff --git a/arch/arm64/boot/dts/apple/t8103-j456.dts b/arch/arm64/boot/dts/apple/t8103-j456.dts index fbecc8bcfad3d7..7cf103418455a8 100644 --- a/arch/arm64/boot/dts/apple/t8103-j456.dts +++ b/arch/arm64/boot/dts/apple/t8103-j456.dts @@ -104,7 +104,7 @@ / { sound { compatible = "apple,j456-macaudio", "apple,macaudio"; - model = "iMac J456 integrated audio"; + model = "iMac J456"; dai-link@1 { link-name = "Headphone Jack"; diff --git a/arch/arm64/boot/dts/apple/t8103-j457.dts b/arch/arm64/boot/dts/apple/t8103-j457.dts index bba32fffd378a6..3969d7449f5c13 100644 --- a/arch/arm64/boot/dts/apple/t8103-j457.dts +++ b/arch/arm64/boot/dts/apple/t8103-j457.dts @@ -77,7 +77,7 @@ / { sound { compatible = "apple,j457-macaudio", "apple,macaudio"; - model = "iMac J457 integrated audio"; + model = "iMac J457"; dai-link@1 { link-name = "Headphone Jack"; diff --git a/arch/arm64/boot/dts/apple/t8112-j413.dts b/arch/arm64/boot/dts/apple/t8112-j413.dts index bce2774ff0a406..c4ae6b5a6c7519 100644 --- a/arch/arm64/boot/dts/apple/t8112-j413.dts +++ b/arch/arm64/boot/dts/apple/t8112-j413.dts @@ -140,7 +140,7 @@ / { sound { compatible = "apple,j413-macaudio", "apple,macaudio"; - model = "MacBook Air J413 integrated audio"; + model = "MacBook Air J413"; dai-link@0 { link-name = "Speakers"; diff --git a/arch/arm64/boot/dts/apple/t8112-j493.dts b/arch/arm64/boot/dts/apple/t8112-j493.dts index 919b0762b723b5..45302227cf4be7 100644 --- a/arch/arm64/boot/dts/apple/t8112-j493.dts +++ b/arch/arm64/boot/dts/apple/t8112-j493.dts @@ -129,7 +129,7 @@ / { sound { compatible = "apple,j493-macaudio", "apple,macaudio"; - model = "MacBook Pro J493 integrated audio"; + model = "MacBook Pro J493"; dai-link@0 { link-name = "Speakers"; From d4c8262afe126e7dbb7105c4f4e7347e69c9da9d Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 17 Oct 2022 18:29:28 +0900 Subject: [PATCH 0030/1020] arm64: dts: apple: t6001-j375c: Add USB3 hub GPIO initialization The Mac Studio M1 Max (t6001) model has a built-in USB3 hub. This hub has a firmware flash which is also connected to an AP SPI controller. The hub starts out in reset and the host is expected to bring it out of reset, potentially after upgrading/validating the firmware. We won't be doing anything with the firmware, so just use gpio-hog to flip the two GPIOs needed to bring up the hub chip. Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t6001-j375c.dts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6001-j375c.dts b/arch/arm64/boot/dts/apple/t6001-j375c.dts index 6e08f48490b380..a71b1ebb29d956 100644 --- a/arch/arm64/boot/dts/apple/t6001-j375c.dts +++ b/arch/arm64/boot/dts/apple/t6001-j375c.dts @@ -21,3 +21,19 @@ compatible = "apple,j375-macaudio", "apple,macaudio"; model = "Mac Studio J375"; }; + +&pinctrl_ap { + usb_hub_oe-hog { + gpio-hog; + gpios = <230 0>; + input; + line-name = "usb-hub-oe"; + }; + + usb_hub_rst-hog { + gpio-hog; + gpios = <231 GPIO_ACTIVE_LOW>; + output-low; + line-name = "usb-hub-rst"; + }; +}; From 10da2c60a736e7f6d2c76b0b4678cad7f755d007 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 25 Jan 2022 21:50:59 +0100 Subject: [PATCH 0031/1020] arch: arm64: apple: Add missing power state deps for display The dcp co-processor crashes on HDMI unplug while it apparently tries to notify pmp. Handle "notify_pmp" as a parent dependency for "ps_disp0_fe" and "ps_dispext_fe". Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8103-pmgr.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi index 299b1f51b54179..0966322c5c8e3f 100644 --- a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi @@ -645,7 +645,7 @@ #power-domain-cells = <0>; #reset-cells = <0>; label = "disp0_fe"; - power-domains = <&ps_rmx>; + power-domains = <&ps_rmx>, <&ps_pmp>; apple,always-on; /* TODO: figure out if we can enable PM here */ }; @@ -655,7 +655,7 @@ #power-domain-cells = <0>; #reset-cells = <0>; label = "dispext_fe"; - power-domains = <&ps_rmx>; + power-domains = <&ps_rmx>, <&ps_pmp>; }; ps_dispext_cpu0: power-controller@378 { From 4074d1c7b3b6f73e0e0134707e4606b094ca7f2a Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 24 Apr 2022 11:20:31 +0200 Subject: [PATCH 0032/1020] arch: arm64: apple: t600x: Mark USB and PCIe as "dma-coherent" Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t600x-die0.dtsi | 2 ++ arch/arm64/boot/dts/apple/t600x-dieX.dtsi | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index 152c878ab3e005..40931fe6c93651 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -435,6 +435,8 @@ pinctrl-0 = <&pcie_pins>; pinctrl-names = "default"; + dma-coherent; + port00: pci@0,0 { device_type = "pci"; reg = <0x0 0x0 0x0 0x0 0x0>; diff --git a/arch/arm64/boot/dts/apple/t600x-dieX.dtsi b/arch/arm64/boot/dts/apple/t600x-dieX.dtsi index a5f2ef5aab7929..5c25d843284ca0 100644 --- a/arch/arm64/boot/dts/apple/t600x-dieX.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-dieX.dtsi @@ -149,6 +149,7 @@ iommus = <&DIE_NODE(dwc3_0_dart_0) 0>, <&DIE_NODE(dwc3_0_dart_1) 1>; power-domains = <&DIE_NODE(ps_atc0_usb)>; + dma-coherent; }; DIE_NODE(dwc3_1_dart_0): iommu@b02f00000 { @@ -180,6 +181,7 @@ iommus = <&DIE_NODE(dwc3_1_dart_0) 0>, <&DIE_NODE(dwc3_1_dart_1) 1>; power-domains = <&DIE_NODE(ps_atc1_usb)>; + dma-coherent; }; DIE_NODE(dwc3_2_dart_0): iommu@f02f00000 { @@ -211,6 +213,7 @@ iommus = <&DIE_NODE(dwc3_2_dart_0) 0>, <&DIE_NODE(dwc3_2_dart_1) 1>; power-domains = <&DIE_NODE(ps_atc2_usb)>; + dma-coherent; }; DIE_NODE(dwc3_3_dart_0): iommu@1302f00000 { @@ -242,4 +245,5 @@ iommus = <&DIE_NODE(dwc3_3_dart_0) 0>, <&DIE_NODE(dwc3_3_dart_1) 1>; power-domains = <&DIE_NODE(ps_atc3_usb)>; + dma-coherent; }; From e99fcdc16858ba705158dc757bc211f0885f81ba Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 20 Sep 2021 02:27:09 +0900 Subject: [PATCH 0033/1020] arch: arm64: apple: Add display controller related device tree nodes The display system is initialized by the bootloader to provide a simple framebuffer at startup. Memory for the framebuffer and heap for the display co-processor are alreay mapped through the IOMMU. IOMMU intialization must preserve this mappings to avoid crashing the display co-processor. The exisitng mappings are caried in the devicetree. They are applied during device attach to ensure the IOMMU framework is aware of these mapping. Mappings are filled by m1n1 during boot. Based on https://lore.kernel.org/asahi/20220923123557.866972-1-thierry.reding@gmail.com Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8103-jxxx.dtsi | 10 +++ arch/arm64/boot/dts/apple/t8103.dtsi | 82 +++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi index b70cd5f64b4cc6..727cbd8f206d12 100644 --- a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi @@ -12,6 +12,9 @@ / { aliases { bluetooth0 = &bluetooth0; + dcp = &dcp; + disp0 = &display; + disp0_piodma = &disp0_piodma; serial0 = &serial0; serial2 = &serial2; wifi0 = &wifi0; @@ -32,6 +35,13 @@ }; }; + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + /* To be filled by loader */ + }; + memory@800000000 { device_type = "memory"; reg = <0x8 0 0x2 0>; /* To be filled by loader */ diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index db00ef3de56385..c40a07feaf7b55 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -334,6 +334,14 @@ clock-output-names = "clk_120m"; }; + /* Pixel clock? frequency in Hz (compare: 4K@60 VGA clock 533.250 MHz) */ + clk_disp0: clock-disp0 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <533333328>; + clock-output-names = "clk_disp0"; + }; + /* * This is a fabulated representation of the input clock * to NCO since we don't know the true clock tree. @@ -364,6 +372,72 @@ #performance-domain-cells = <0>; }; + disp0_dart: iommu@231304000 { + compatible = "apple,t8103-dart"; + reg = <0x2 0x31304000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + status = "disabled"; + }; + + dcp_dart: iommu@23130c000 { + compatible = "apple,t8103-dart"; + reg = <0x2 0x3130c000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + }; + + dcp_mbox: mbox@231c08000 { + compatible = "apple,t8103-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x2 0x31c08000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + power-domains = <&ps_disp0_cpu0>; + resets = <&ps_disp0_cpu0>; + }; + + dcp: dcp@231c00000 { + compatible = "apple,t8103-dcp", "apple,dcp"; + mboxes = <&dcp_mbox>; + mbox-names = "mbox"; + iommus = <&dcp_dart 0>; + + reg-names = "coproc", "disp-0", "disp-1", "disp-2", + "disp-3", "disp-4"; + reg = <0x2 0x31c00000 0x0 0x4000>, + <0x2 0x30000000 0x0 0x3e8000>, + <0x2 0x31320000 0x0 0x4000>, + <0x2 0x31344000 0x0 0x4000>, + <0x2 0x31800000 0x0 0x800000>, + <0x2 0x3b3d0000 0x0 0x4000>; + apple,bw-scratch = <&pmgr_dcp 0 5 0x14>; + apple,bw-doorbell = <&pmgr_dcp 1 6>; + power-domains = <&ps_disp0_cpu0>; + clocks = <&clk_disp0>; + apple,asc-dram-mask = <0xf 0x00000000>; + phandle = <&dcp>; + + disp0_piodma: piodma { + iommus = <&disp0_dart 4>; + phandle = <&disp0_piodma>; + }; + }; + + display: display-subsystem { + compatible = "apple,display-subsystem"; + iommus = <&disp0_dart 0>; + /* generate phandle explicitly for use in loader */ + phandle = <&display>; + }; + sio_dart: iommu@235004000 { compatible = "apple,t8103-dart"; reg = <0x2 0x35004000 0x0 0x4000>; @@ -573,6 +647,14 @@ reg = <0x2 0x3b700000 0 0x14000>; }; + pmgr_dcp: power-management@23b738000 { + reg = <0x2 0x3b738000 0x0 0x1000>, + <0x2 0x3bc3c000 0x0 0x1000>; + reg-names = "dcp-bw-scratch", "dcp-bw-doorbell"; + #apple,bw-scratch-cells = <3>; + #apple,bw-doorbell-cells = <2>; + }; + pinctrl_ap: pinctrl@23c100000 { compatible = "apple,t8103-pinctrl", "apple,pinctrl"; reg = <0x2 0x3c100000 0x0 0x100000>; From 70871e718cdde23ba555df51957613ff270bce29 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Fri, 11 Mar 2022 22:14:52 +0100 Subject: [PATCH 0034/1020] arch: arm64: apple: t600x: Add display controller related device tree nodes The display system is initialized by the bootloader to provide a simple framebuffer at startup. Memory for the framebuffer and heap for the display co-processor are alreay mapped through the IOMMU. IOMMU intialization must preserve this mappings to avoid crashing the display co-processor. The exisitng mappings are caried in the devicetree. They are applied during device attach to ensure the IOMMU framework is aware of these mapping. Mappings are filled by m1n1 during boot. Based on https://lore.kernel.org/asahi/20220923123557.866972-1-thierry.reding@gmail.com Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t600x-common.dtsi | 6 ++ arch/arm64/boot/dts/apple/t600x-die0.dtsi | 68 +++++++++++++++++++ .../arm64/boot/dts/apple/t600x-j314-j316.dtsi | 10 +++ arch/arm64/boot/dts/apple/t600x-j375.dtsi | 10 +++ 4 files changed, 94 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-common.dtsi b/arch/arm64/boot/dts/apple/t600x-common.dtsi index 87dfc13d74171f..01385ef831ca91 100644 --- a/arch/arm64/boot/dts/apple/t600x-common.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-common.dtsi @@ -369,6 +369,12 @@ clock-output-names = "clk_200m"; }; + clk_disp0: clock-disp0 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <237333328>; + clock-output-names = "clk_disp0"; + }; /* * This is a fabulated representation of the input clock * to NCO since we don't know the true clock tree. diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index 40931fe6c93651..1b795ddbe552d5 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -24,6 +24,12 @@ power-domains = <&ps_aic>; }; + pmgr_dcp: power-management@28e3d0000 { + reg = <0x2 0x8e3d0000 0x0 0x4000>; + reg-names = "dcp-fw-pmgr"; + #apple,bw-scratch-cells = <3>; + }; + smc_mbox: mbox@290408000 { compatible = "apple,t6000-asc-mailbox", "apple,asc-mailbox-v4"; reg = <0x2 0x90408000 0x0 0x4000>; @@ -166,6 +172,68 @@ }; }; + disp0_dart: iommu@38b304000 { + compatible = "apple,t6000-dart"; + reg = <0x3 0x8b304000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + status = "disabled"; + }; + + dcp_dart: iommu@38b30c000 { + compatible = "apple,t6000-dart"; + reg = <0x3 0x8b30c000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + }; + + dcp_mbox: mbox@38bc08000 { + compatible = "apple,t6000-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x3 0x8bc08000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + power-domains = <&ps_disp0_cpu0>; + }; + + dcp: dcp@38bc00000 { + compatible = "apple,t6000-dcp", "apple,dcp"; + mboxes = <&dcp_mbox>; + mbox-names = "mbox"; + iommus = <&dcp_dart 0>; + + reg-names = "coproc", "disp-0", "disp-1", "disp-2", "disp-3"; + reg = <0x3 0x8bc00000 0x0 0x4000>, + <0x3 0x8a000000 0x0 0x3000000>, + <0x3 0x8b320000 0x0 0x4000>, + <0x3 0x8b344000 0x0 0x4000>, + <0x3 0x8b800000 0x0 0x800000>; + apple,bw-scratch = <&pmgr_dcp 0 4 0x988>; + power-domains = <&ps_disp0_cpu0>; + clocks = <&clk_disp0>; + apple,asc-dram-mask = <0x1f0 0x00000000>; + phandle = <&dcp>; + + disp0_piodma: piodma { + iommus = <&disp0_dart 4>; + phandle = <&disp0_piodma>; + }; + }; + + display: display-subsystem { + compatible = "apple,display-subsystem"; + iommus = <&disp0_dart 0>; + /* generate phandle explicitly for use in loader */ + phandle = <&display>; + }; + sio_dart_0: iommu@39b004000 { compatible = "apple,t6000-dart"; reg = <0x3 0x9b004000 0x0 0x4000>; diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index 06f009dfa373b9..bf6e6fab79e702 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -14,6 +14,9 @@ / { aliases { bluetooth0 = &bluetooth0; + dcp = &dcp; + disp0 = &display; + disp0_piodma = &disp0_piodma; serial0 = &serial0; wifi0 = &wifi0; }; @@ -33,6 +36,13 @@ }; }; + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + /* To be filled by loader */ + }; + memory@10000000000 { device_type = "memory"; reg = <0x100 0 0x2 0>; /* To be filled by loader */ diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi index 6023105f2f5868..1e6ac6e2bbfcc9 100644 --- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi @@ -12,6 +12,9 @@ / { aliases { bluetooth0 = &bluetooth0; + dcp = &dcp; + disp0 = &display; + disp0_piodma = &disp0_piodma; serial0 = &serial0; wifi0 = &wifi0; }; @@ -31,6 +34,13 @@ }; }; + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + /* To be filled by loader */ + }; + memory@10000000000 { device_type = "memory"; reg = <0x100 0 0x2 0>; /* To be filled by loader */ From 70f52a866988290d201c6924ca47bcd199724f8a Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 3 Oct 2022 17:44:37 +0200 Subject: [PATCH 0035/1020] arch: arm64: apple: t8103: Add connector type property for DCP* Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8103-j274.dts | 4 ++++ arch/arm64/boot/dts/apple/t8103-j293.dts | 4 ++++ arch/arm64/boot/dts/apple/t8103-j313.dts | 4 ++++ arch/arm64/boot/dts/apple/t8103-j456.dts | 4 ++++ arch/arm64/boot/dts/apple/t8103-j457.dts | 4 ++++ 5 files changed, 20 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-j274.dts b/arch/arm64/boot/dts/apple/t8103-j274.dts index b214075000fb8a..7169563c1b351a 100644 --- a/arch/arm64/boot/dts/apple/t8103-j274.dts +++ b/arch/arm64/boot/dts/apple/t8103-j274.dts @@ -21,6 +21,10 @@ }; }; +&dcp { + apple,connector-type = "HDMI-A"; +}; + &bluetooth0 { brcm,board-type = "apple,atlantisb"; }; diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts index 3c6ee9132fe61e..1b87dc8fafea34 100644 --- a/arch/arm64/boot/dts/apple/t8103-j293.dts +++ b/arch/arm64/boot/dts/apple/t8103-j293.dts @@ -30,6 +30,10 @@ }; }; +&dcp { + apple,connector-type = "eDP"; +}; + &bluetooth0 { brcm,board-type = "apple,honshu"; }; diff --git a/arch/arm64/boot/dts/apple/t8103-j313.dts b/arch/arm64/boot/dts/apple/t8103-j313.dts index 6e96f7a4e77ae1..5061cc5d09e0da 100644 --- a/arch/arm64/boot/dts/apple/t8103-j313.dts +++ b/arch/arm64/boot/dts/apple/t8103-j313.dts @@ -30,6 +30,10 @@ }; }; +&dcp { + apple,connector-type = "eDP"; +}; + &bluetooth0 { brcm,board-type = "apple,shikoku"; }; diff --git a/arch/arm64/boot/dts/apple/t8103-j456.dts b/arch/arm64/boot/dts/apple/t8103-j456.dts index 7cf103418455a8..ca74b7f87c0c3b 100644 --- a/arch/arm64/boot/dts/apple/t8103-j456.dts +++ b/arch/arm64/boot/dts/apple/t8103-j456.dts @@ -21,6 +21,10 @@ }; }; +&dcp { + apple,connector-type = "eDP"; +}; + &bluetooth0 { brcm,board-type = "apple,capri"; }; diff --git a/arch/arm64/boot/dts/apple/t8103-j457.dts b/arch/arm64/boot/dts/apple/t8103-j457.dts index 3969d7449f5c13..2f248178ce5885 100644 --- a/arch/arm64/boot/dts/apple/t8103-j457.dts +++ b/arch/arm64/boot/dts/apple/t8103-j457.dts @@ -21,6 +21,10 @@ }; }; +&dcp { + apple,connector-type = "eDP"; +}; + &bluetooth0 { brcm,board-type = "apple,santorini"; }; From 00972e226437e99229f99a7b2d8cabb0b681c14f Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 22 Oct 2022 09:33:04 +0200 Subject: [PATCH 0036/1020] arch: arm64: apple: t600x: Add connector type property for DCP* Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi | 4 ++++ arch/arm64/boot/dts/apple/t600x-j375.dtsi | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index bf6e6fab79e702..74f452f3ecd367 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -65,6 +65,10 @@ status = "okay"; }; +&dcp { + apple,connector-type = "eDP"; +}; + /* USB Type C */ &i2c0 { hpm0: usb-pd@38 { diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi index 1e6ac6e2bbfcc9..b86abaa21b546a 100644 --- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi @@ -51,6 +51,10 @@ status = "okay"; }; +&dcp { + apple,connector-type = "HDMI-A"; +}; + /* USB Type C */ &i2c0 { hpm0: usb-pd@38 { From d3ba19cad14f7c8cddb7cbabed8235460cdecc46 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Tue, 15 Nov 2022 12:09:48 +0100 Subject: [PATCH 0037/1020] arm64: dts: apple: t8103: Add eFuses node --- arch/arm64/boot/dts/apple/t8103.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index c40a07feaf7b55..daade68a322a0d 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -941,6 +941,13 @@ resets = <&ps_ans2>; }; + efuse@23d2bc000 { + compatible = "apple,t8103-efuses", "apple,efuses"; + reg = <0x2 0x3d2bc000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + }; + dwc3_0: usb@382280000 { compatible = "apple,t8103-dwc3", "apple,dwc3", "snps,dwc3"; reg = <0x3 0x82280000 0x0 0x100000>; From 7b91b50ad869ca6df3b373d8cf35a34fb4da64a2 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Wed, 30 Nov 2022 22:11:09 +0100 Subject: [PATCH 0038/1020] arm64: dts: apple: t8103: Add ATCPHY node Signed-off-by: Sven Peter --- arch/arm64/boot/dts/apple/t8103-jxxx.dtsi | 31 ++++ arch/arm64/boot/dts/apple/t8103.dtsi | 179 ++++++++++++++++++++++ 2 files changed, 210 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi index 727cbd8f206d12..1ad2a57698afb9 100644 --- a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi @@ -18,6 +18,8 @@ serial0 = &serial0; serial2 = &serial2; wifi0 = &wifi0; + atcphy0 = &atcphy0; + atcphy1 = &atcphy1; }; chosen { @@ -78,6 +80,12 @@ remote-endpoint = <&typec0_usb_hs>; }; }; + port@1 { + reg = <1>; + typec0_con_ss: endpoint { + remote-endpoint = <&typec0_usb_ss>; + }; + }; }; }; }; @@ -103,6 +111,12 @@ remote-endpoint = <&typec1_usb_hs>; }; }; + port@1 { + reg = <1>; + typec1_con_ss: endpoint { + remote-endpoint = <&typec1_usb_ss>; + }; + }; }; }; }; @@ -125,6 +139,23 @@ }; }; +/* Type-C PHYs */ +&atcphy0 { + port { + typec0_usb_ss: endpoint { + remote-endpoint = <&typec0_con_ss>; + }; + }; +}; + +&atcphy1 { + port { + typec1_usb_ss: endpoint { + remote-endpoint = <&typec1_con_ss>; + }; + }; +}; + /* * Force the bus number assignments so that we can declare some of the * on-board devices and properties that are populated by the bootloader diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index daade68a322a0d..4c64d4a3cd9263 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -12,6 +12,7 @@ #include #include #include +#include / { compatible = "apple,t8103", "apple,arm-platform"; @@ -946,6 +947,100 @@ reg = <0x2 0x3d2bc000 0x0 0x1000>; #address-cells = <1>; #size-cells = <1>; + atcphy0_auspll_rodco_bias_adjust: efuse@430,26 { + reg = <0x430 4>; + bits = <26 3>; + }; + + atcphy0_auspll_rodco_encap: efuse@430,29 { + reg = <0x430 4>; + bits = <29 2>; + }; + + atcphy0_auspll_dtc_vreg_adjust: efuse@430,31 { + reg = <0x430 8>; + bits = <31 3>; + }; + + atcphy0_auspll_fracn_dll_start_capcode: efuse@434,2 { + reg = <0x434 4>; + bits = <2 2>; + }; + + atcphy0_aus_cmn_shm_vreg_trim: efuse@434,4 { + reg = <0x434 4>; + bits = <4 5>; + }; + + atcphy0_cio3pll_dco_coarsebin0: efuse@434,9 { + reg = <0x434 4>; + bits = <9 6>; + }; + + atcphy0_cio3pll_dco_coarsebin1: efuse@434,15 { + reg = <0x434 4>; + bits = <15 6>; + }; + + atcphy0_cio3pll_dll_start_capcode: efuse@434,21 { + reg = <0x434 4>; + bits = <21 2>; + }; + + atcphy0_cio3pll_dtc_vreg_adjust: efuse@434,23 { + reg = <0x434 0x4>; + bits = <23 3>; + }; + + atcphy1_auspll_rodco_bias_adjust: efuse@438,4 { + reg = <0x438 4>; + bits = <4 3>; + }; + + atcphy1_auspll_rodco_encap: efuse@438,7 { + reg = <0x438 4>; + bits = <7 2>; + }; + + atcphy1_auspll_dtc_vreg_adjust: efuse@438,9 { + reg = <0x438 4>; + bits = <9 3>; + }; + + atcphy1_auspll_fracn_dll_start_capcode: efuse@438,12 { + reg = <0x438 4>; + bits = <12 2>; + }; + + atcphy1_aus_cmn_shm_vreg_trim: efuse@438,14 { + reg = <0x438 4>; + bits = <14 5>; + }; + + atcphy1_cio3pll_dco_coarsebin0: efuse@438,19 { + reg = <0x438 4>; + bits = <19 6>; + }; + + atcphy1_cio3pll_dco_coarsebin1: efuse@438,25 { + reg = <0x438 4>; + bits = <25 6>; + }; + + atcphy1_cio3pll_dll_start_capcode: efuse@438,31 { + reg = <0x438 4>; + bits = <31 1>; + }; + + atcphy1_cio3pll_dll_start_capcode_workaround: efuse@43c,0 { + reg = <0x43c 0x4>; + bits = <0 1>; + }; + + atcphy1_cio3pll_dtc_vreg_adjust: efuse@43c,1 { + reg = <0x43c 0x4>; + bits = <1 3>; + }; }; dwc3_0: usb@382280000 { @@ -958,6 +1053,9 @@ role-switch-default-mode = "host"; iommus = <&dwc3_0_dart_0 0>, <&dwc3_0_dart_1 1>; power-domains = <&ps_atc0_usb>; + resets = <&atcphy0>; + phys = <&atcphy0 PHY_TYPE_USB2>, <&atcphy0 PHY_TYPE_USB3>; + phy-names = "usb2-phy", "usb3-phy"; }; dwc3_0_dart_0: iommu@382f00000 { @@ -978,6 +1076,44 @@ power-domains = <&ps_atc0_usb>; }; + atcphy0: phy@383000000 { + compatible = "apple,t8103-atcphy"; + reg = <0x3 0x83000000 0x0 0x4c000>, + <0x3 0x83050000 0x0 0x8000>, + <0x3 0x80000000 0x0 0x4000>, + <0x3 0x82a90000 0x0 0x4000>, + <0x3 0x82a84000 0x0 0x4000>; + reg-names = "core", "lpdptx", "axi2af", "usb2phy", + "pipehandler"; + + #phy-cells = <1>; + #reset-cells = <0>; + + nvmem-cells = <&atcphy0_aus_cmn_shm_vreg_trim>, + <&atcphy0_auspll_rodco_encap>, + <&atcphy0_auspll_rodco_bias_adjust>, + <&atcphy0_auspll_fracn_dll_start_capcode>, + <&atcphy0_auspll_dtc_vreg_adjust>, + <&atcphy0_cio3pll_dco_coarsebin0>, + <&atcphy0_cio3pll_dco_coarsebin1>, + <&atcphy0_cio3pll_dll_start_capcode>, + <&atcphy0_cio3pll_dtc_vreg_adjust>; + nvmem-cell-names = "aus_cmn_shm_vreg_trim", + "auspll_rodco_encap", + "auspll_rodco_bias_adjust", + "auspll_fracn_dll_start_capcode", + "auspll_dtc_vreg_adjust", + "cio3pll_dco_coarsebin0", + "cio3pll_dco_coarsebin1", + "cio3pll_dll_start_capcode", + "cio3pll_dtc_vreg_adjust"; + + orientation-switch; + mode-switch; + svid = <0xff01>, <0x8087>; + power-domains = <&ps_atc0_usb>; + }; + dwc3_1: usb@502280000 { compatible = "apple,t8103-dwc3", "apple,dwc3", "snps,dwc3"; reg = <0x5 0x02280000 0x0 0x100000>; @@ -988,6 +1124,9 @@ role-switch-default-mode = "host"; iommus = <&dwc3_1_dart_0 0>, <&dwc3_1_dart_1 1>; power-domains = <&ps_atc1_usb>; + resets = <&atcphy1>; + phys = <&atcphy1 PHY_TYPE_USB2>, <&atcphy1 PHY_TYPE_USB3>; + phy-names = "usb2-phy", "usb3-phy"; }; dwc3_1_dart_0: iommu@502f00000 { @@ -1008,6 +1147,46 @@ power-domains = <&ps_atc1_usb>; }; + atcphy1: phy@503000000 { + compatible = "apple,t8103-atcphy"; + reg = <0x5 0x03000000 0x0 0x4c000>, + <0x5 0x03050000 0x0 0x8000>, + <0x5 0x0 0x0 0x4000>, + <0x5 0x02a90000 0x0 0x4000>, + <0x5 0x02a84000 0x0 0x4000>; + reg-names = "core", "lpdptx", "axi2af", "usb2phy", + "pipehandler"; + + nvmem-cells = <&atcphy1_aus_cmn_shm_vreg_trim>, + <&atcphy1_auspll_rodco_encap>, + <&atcphy1_auspll_rodco_bias_adjust>, + <&atcphy1_auspll_fracn_dll_start_capcode>, + <&atcphy1_auspll_dtc_vreg_adjust>, + <&atcphy1_cio3pll_dco_coarsebin0>, + <&atcphy1_cio3pll_dco_coarsebin1>, + <&atcphy1_cio3pll_dll_start_capcode>, + <&atcphy1_cio3pll_dtc_vreg_adjust>, + <&atcphy1_cio3pll_dll_start_capcode_workaround>; + nvmem-cell-names = "aus_cmn_shm_vreg_trim", + "auspll_rodco_encap", + "auspll_rodco_bias_adjust", + "auspll_fracn_dll_start_capcode", + "auspll_dtc_vreg_adjust", + "cio3pll_dco_coarsebin0", + "cio3pll_dco_coarsebin1", + "cio3pll_dll_start_capcode", + "cio3pll_dtc_vreg_adjust", + "cio3pll_dll_start_capcode_workaround"; + + #phy-cells = <1>; + #reset-cells = <0>; + + orientation-switch; + mode-switch; + svid = <0xff01>, <0x8087>; + power-domains = <&ps_atc1_usb>; + }; + pcie0_dart_0: iommu@681008000 { compatible = "apple,t8103-dart"; reg = <0x6 0x81008000 0x0 0x4000>; From 2f951f3f96665ce7fb0a5c0bd209037529045c33 Mon Sep 17 00:00:00 2001 From: R Date: Tue, 15 Nov 2022 12:09:52 +0100 Subject: [PATCH 0039/1020] arch: arm64: dts: apple: t6000: Add eFuses node Signed-off-by: R --- arch/arm64/boot/dts/apple/t600x-dieX.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-dieX.dtsi b/arch/arm64/boot/dts/apple/t600x-dieX.dtsi index 5c25d843284ca0..d135a437d513ce 100644 --- a/arch/arm64/boot/dts/apple/t600x-dieX.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-dieX.dtsi @@ -74,6 +74,13 @@ reg = <0x2 0x92280000 0 0x4000>; }; + DIE_NODE(efuse): efuse@2922bc000 { + compatible = "apple,t6000-efuses", "apple,efuses"; + reg = <0x2 0x922bc000 0x0 0x2000>; + #address-cells = <1>; + #size-cells = <1>; + }; + DIE_NODE(pinctrl_aop): pinctrl@293820000 { compatible = "apple,t6000-pinctrl", "apple,pinctrl"; reg = <0x2 0x93820000 0x0 0x4000>; From 7003c456270f37a3ccfcee61b2ea4e3f5f975123 Mon Sep 17 00:00:00 2001 From: R Date: Tue, 15 Nov 2022 12:09:54 +0100 Subject: [PATCH 0040/1020] arch: arm64: dts: apple: t600x: Add ATCPHY nodes Signed-off-by: R --- arch/arm64/boot/dts/apple/t6001.dtsi | 1 + arch/arm64/boot/dts/apple/t6002-j375d.dts | 35 ++ arch/arm64/boot/dts/apple/t6002.dtsi | 1 + arch/arm64/boot/dts/apple/t600x-dieX.dtsi | 352 +++++++++++++++++- .../arm64/boot/dts/apple/t600x-j314-j316.dtsi | 47 +++ arch/arm64/boot/dts/apple/t600x-j375.dtsi | 61 +++ 6 files changed, 493 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t6001.dtsi b/arch/arm64/boot/dts/apple/t6001.dtsi index d2cf81926f284c..0bdd1966f5302e 100644 --- a/arch/arm64/boot/dts/apple/t6001.dtsi +++ b/arch/arm64/boot/dts/apple/t6001.dtsi @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "multi-die-cpp.h" diff --git a/arch/arm64/boot/dts/apple/t6002-j375d.dts b/arch/arm64/boot/dts/apple/t6002-j375d.dts index 75eb1b2ec934cd..95a783b9fb144a 100644 --- a/arch/arm64/boot/dts/apple/t6002-j375d.dts +++ b/arch/arm64/boot/dts/apple/t6002-j375d.dts @@ -15,6 +15,10 @@ / { compatible = "apple,j375d", "apple,t6002", "apple,arm-platform"; model = "Apple Mac Studio (M1 Ultra, 2022)"; + aliases { + atcphy4 = &atcphy0_die1; + atcphy5 = &atcphy1_die1; + }; }; &sound { @@ -47,6 +51,12 @@ remote-endpoint = <&typec4_usb_hs>; }; }; + port@1 { + reg = <1>; + typec4_con_ss: endpoint { + remote-endpoint = <&typec4_usb_ss>; + }; + }; }; }; }; @@ -74,6 +84,12 @@ remote-endpoint = <&typec5_usb_hs>; }; }; + port@1 { + reg = <1>; + typec5_con_ss: endpoint { + remote-endpoint = <&typec5_usb_ss>; + }; + }; }; }; }; @@ -96,15 +112,34 @@ }; }; +/* Type-C PHYs */ +&atcphy0_die1 { + port { + typec4_usb_ss: endpoint { + remote-endpoint = <&typec4_con_ss>; + }; + }; +}; + +&atcphy1_die1 { + port { + typec5_usb_ss: endpoint { + remote-endpoint = <&typec5_con_ss>; + }; + }; +}; + /* delete unused USB nodes on die 1 */ /delete-node/ &dwc3_2_dart_0_die1; /delete-node/ &dwc3_2_dart_1_die1; /delete-node/ &dwc3_2_die1; +/delete-node/ &atcphy2_die1; /delete-node/ &dwc3_3_dart_0_die1; /delete-node/ &dwc3_3_dart_1_die1; /delete-node/ &dwc3_3_die1; +/delete-node/ &atcphy3_die1; /* delete unused always-on power-domains on die 1 */ diff --git a/arch/arm64/boot/dts/apple/t6002.dtsi b/arch/arm64/boot/dts/apple/t6002.dtsi index e36f422d257d8f..8fa2d8dd72ff7f 100644 --- a/arch/arm64/boot/dts/apple/t6002.dtsi +++ b/arch/arm64/boot/dts/apple/t6002.dtsi @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "multi-die-cpp.h" diff --git a/arch/arm64/boot/dts/apple/t600x-dieX.dtsi b/arch/arm64/boot/dts/apple/t600x-dieX.dtsi index d135a437d513ce..3fca8efb2dcf17 100644 --- a/arch/arm64/boot/dts/apple/t600x-dieX.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-dieX.dtsi @@ -79,6 +79,186 @@ reg = <0x2 0x922bc000 0x0 0x2000>; #address-cells = <1>; #size-cells = <1>; + + DIE_NODE(atcphy0_auspll_rodco_bias_adjust): efuse@a10,22 { + reg = <0xa10 4>; + bits = <22 3>; + }; + + DIE_NODE(atcphy0_auspll_rodco_encap): efuse@a10,25 { + reg = <0xa10 4>; + bits = <25 2>; + }; + + DIE_NODE(atcphy0_auspll_dtc_vreg_adjust): efuse@a10,27 { + reg = <0xa10 4>; + bits = <27 3>; + }; + + DIE_NODE(atcphy0_auspll_fracn_dll_start_capcode): efuse@a10,30 { + reg = <0xa10 4>; + bits = <30 2>; + }; + + DIE_NODE(atcphy0_aus_cmn_shm_vreg_trim): efuse@a14,0 { + reg = <0xa14 4>; + bits = <0 5>; + }; + + DIE_NODE(atcphy0_cio3pll_dco_coarsebin0): efuse@a14,5 { + reg = <0xa14 4>; + bits = <5 6>; + }; + + DIE_NODE(atcphy0_cio3pll_dco_coarsebin1): efuse@a14,11 { + reg = <0xa14 4>; + bits = <11 6>; + }; + + DIE_NODE(atcphy0_cio3pll_dll_start_capcode): efuse@a14,17 { + reg = <0xa14 4>; + bits = <17 2>; + }; + + DIE_NODE(atcphy0_cio3pll_dtc_vreg_adjust): efuse@a14,19 { + reg = <0xa14 4>; + bits = <19 3>; + }; + + DIE_NODE(atcphy1_auspll_rodco_bias_adjust): efuse@a18,0 { + reg = <0xa18 4>; + bits = <0 3>; + }; + + DIE_NODE(atcphy1_auspll_rodco_encap): efuse@a18,3 { + reg = <0xa18 4>; + bits = <3 2>; + }; + + DIE_NODE(atcphy1_auspll_dtc_vreg_adjust): efuse@a18,5 { + reg = <0xa18 4>; + bits = <5 3>; + }; + + DIE_NODE(atcphy1_auspll_fracn_dll_start_capcode): efuse@a18,8 { + reg = <0xa18 4>; + bits = <8 2>; + }; + + DIE_NODE(atcphy1_aus_cmn_shm_vreg_trim): efuse@a18,10 { + reg = <0xa18 4>; + bits = <10 5>; + }; + + DIE_NODE(atcphy1_cio3pll_dco_coarsebin0): efuse@a18,15 { + reg = <0xa18 4>; + bits = <15 6>; + }; + + DIE_NODE(atcphy1_cio3pll_dco_coarsebin1): efuse@a18,21 { + reg = <0xa18 4>; + bits = <21 6>; + }; + + DIE_NODE(atcphy1_cio3pll_dll_start_capcode): efuse@a18,27 { + reg = <0xa18 4>; + bits = <27 2>; + }; + + DIE_NODE(atcphy1_cio3pll_dtc_vreg_adjust): efuse@a18,29 { + reg = <0xa18 4>; + bits = <29 3>; + }; + + DIE_NODE(atcphy2_auspll_rodco_bias_adjust): efuse@a1c,10 { + reg = <0xa1c 4>; + bits = <10 3>; + }; + + DIE_NODE(atcphy2_auspll_rodco_encap): efuse@a1c,13 { + reg = <0xa1c 4>; + bits = <13 2>; + }; + + DIE_NODE(atcphy2_auspll_dtc_vreg_adjust): efuse@a1c,15 { + reg = <0xa1c 4>; + bits = <15 3>; + }; + + DIE_NODE(atcphy2_auspll_fracn_dll_start_capcode): efuse@a1c,18 { + reg = <0xa1c 4>; + bits = <18 2>; + }; + + DIE_NODE(atcphy2_aus_cmn_shm_vreg_trim): efuse@a1c,20 { + reg = <0xa1c 4>; + bits = <20 5>; + }; + + DIE_NODE(atcphy2_cio3pll_dco_coarsebin0): efuse@a1c,25 { + reg = <0xa1c 4>; + bits = <25 6>; + }; + + DIE_NODE(atcphy2_cio3pll_dco_coarsebin1): efuse@a1c,31 { + reg = <0xa1c 8>; + bits = <31 6>; + }; + + DIE_NODE(atcphy2_cio3pll_dll_start_capcode): efuse@a20,5 { + reg = <0xa20 4>; + bits = <5 2>; + }; + + DIE_NODE(atcphy2_cio3pll_dtc_vreg_adjust): efuse@a20,7 { + reg = <0xa20 4>; + bits = <7 3>; + }; + + DIE_NODE(atcphy3_auspll_rodco_bias_adjust): efuse@a20,20 { + reg = <0xa20 4>; + bits = <20 3>; + }; + + DIE_NODE(atcphy3_auspll_rodco_encap): efuse@a20,23 { + reg = <0xa20 4>; + bits = <23 2>; + }; + + DIE_NODE(atcphy3_auspll_dtc_vreg_adjust): efuse@a20,25 { + reg = <0xa20 4>; + bits = <25 3>; + }; + + DIE_NODE(atcphy3_auspll_fracn_dll_start_capcode): efuse@a20,28 { + reg = <0xa20 4>; + bits = <28 2>; + }; + + DIE_NODE(atcphy3_aus_cmn_shm_vreg_trim): efuse@a20,30 { + reg = <0xa20 8>; + bits = <30 5>; + }; + + DIE_NODE(atcphy3_cio3pll_dco_coarsebin0): efuse@a24,3 { + reg = <0xa24 4>; + bits = <3 6>; + }; + + DIE_NODE(atcphy3_cio3pll_dco_coarsebin1): efuse@a24,9 { + reg = <0xa24 4>; + bits = <9 6>; + }; + + DIE_NODE(atcphy3_cio3pll_dll_start_capcode): efuse@a24,15 { + reg = <0xa24 4>; + bits = <15 2>; + }; + + DIE_NODE(atcphy3_cio3pll_dtc_vreg_adjust): efuse@a24,17 { + reg = <0xa24 4>; + bits = <17 3>; + }; }; DIE_NODE(pinctrl_aop): pinctrl@293820000 { @@ -150,13 +330,54 @@ reg = <0x7 0x02280000 0x0 0x100000>; interrupt-parent = <&aic>; interrupts = ; - /* dr_mode = "otg"; */ + dr_mode = "otg"; usb-role-switch; role-switch-default-mode = "host"; iommus = <&DIE_NODE(dwc3_0_dart_0) 0>, <&DIE_NODE(dwc3_0_dart_1) 1>; power-domains = <&DIE_NODE(ps_atc0_usb)>; dma-coherent; + resets = <&DIE_NODE(atcphy0)>; + phys = <&DIE_NODE(atcphy0) PHY_TYPE_USB2>, <&DIE_NODE(atcphy0) PHY_TYPE_USB3>; + phy-names = "usb2-phy", "usb3-phy"; + }; + + DIE_NODE(atcphy0): phy@703000000 { + compatible = "apple,t6000-atcphy", "apple,t8103-atcphy"; + reg = <0x7 0x03000000 0x0 0x4c000>, + <0x7 0x03050000 0x0 0x8000>, + <0x7 0x00000000 0x0 0x4000>, + <0x7 0x02a90000 0x0 0x4000>, + <0x7 0x02a84000 0x0 0x4000>; + reg-names = "core", "lpdptx", "axi2af", "usb2phy", + "pipehandler"; + + #phy-cells = <1>; + #reset-cells = <0>; + + nvmem-cells = <&DIE_NODE(atcphy0_aus_cmn_shm_vreg_trim)>, + <&DIE_NODE(atcphy0_auspll_rodco_encap)>, + <&DIE_NODE(atcphy0_auspll_rodco_bias_adjust)>, + <&DIE_NODE(atcphy0_auspll_fracn_dll_start_capcode)>, + <&DIE_NODE(atcphy0_auspll_dtc_vreg_adjust)>, + <&DIE_NODE(atcphy0_cio3pll_dco_coarsebin0)>, + <&DIE_NODE(atcphy0_cio3pll_dco_coarsebin1)>, + <&DIE_NODE(atcphy0_cio3pll_dll_start_capcode)>, + <&DIE_NODE(atcphy0_cio3pll_dtc_vreg_adjust)>; + nvmem-cell-names = "aus_cmn_shm_vreg_trim", + "auspll_rodco_encap", + "auspll_rodco_bias_adjust", + "auspll_fracn_dll_start_capcode", + "auspll_dtc_vreg_adjust", + "cio3pll_dco_coarsebin0", + "cio3pll_dco_coarsebin1", + "cio3pll_dll_start_capcode", + "cio3pll_dtc_vreg_adjust"; + + orientation-switch; + mode-switch; + svid = <0xff01>, <0x8087>; + power-domains = <&DIE_NODE(ps_atc0_usb)>; }; DIE_NODE(dwc3_1_dart_0): iommu@b02f00000 { @@ -182,13 +403,54 @@ reg = <0xb 0x02280000 0x0 0x100000>; interrupt-parent = <&aic>; interrupts = ; - /* dr_mode = "otg"; */ + dr_mode = "otg"; usb-role-switch; role-switch-default-mode = "host"; iommus = <&DIE_NODE(dwc3_1_dart_0) 0>, <&DIE_NODE(dwc3_1_dart_1) 1>; power-domains = <&DIE_NODE(ps_atc1_usb)>; dma-coherent; + resets = <&DIE_NODE(atcphy1)>; + phys = <&DIE_NODE(atcphy1) PHY_TYPE_USB2>, <&DIE_NODE(atcphy1) PHY_TYPE_USB3>; + phy-names = "usb2-phy", "usb3-phy"; + }; + + DIE_NODE(atcphy1): phy@b03000000 { + compatible = "apple,t6000-atcphy", "apple,t8103-atcphy"; + reg = <0xb 0x03000000 0x0 0x4c000>, + <0xb 0x03050000 0x0 0x8000>, + <0xb 0x00000000 0x0 0x4000>, + <0xb 0x02a90000 0x0 0x4000>, + <0xb 0x02a84000 0x0 0x4000>; + reg-names = "core", "lpdptx", "axi2af", "usb2phy", + "pipehandler"; + + #phy-cells = <1>; + #reset-cells = <0>; + + nvmem-cells = <&DIE_NODE(atcphy1_aus_cmn_shm_vreg_trim)>, + <&DIE_NODE(atcphy1_auspll_rodco_encap)>, + <&DIE_NODE(atcphy1_auspll_rodco_bias_adjust)>, + <&DIE_NODE(atcphy1_auspll_fracn_dll_start_capcode)>, + <&DIE_NODE(atcphy1_auspll_dtc_vreg_adjust)>, + <&DIE_NODE(atcphy1_cio3pll_dco_coarsebin0)>, + <&DIE_NODE(atcphy1_cio3pll_dco_coarsebin1)>, + <&DIE_NODE(atcphy1_cio3pll_dll_start_capcode)>, + <&DIE_NODE(atcphy1_cio3pll_dtc_vreg_adjust)>; + nvmem-cell-names = "aus_cmn_shm_vreg_trim", + "auspll_rodco_encap", + "auspll_rodco_bias_adjust", + "auspll_fracn_dll_start_capcode", + "auspll_dtc_vreg_adjust", + "cio3pll_dco_coarsebin0", + "cio3pll_dco_coarsebin1", + "cio3pll_dll_start_capcode", + "cio3pll_dtc_vreg_adjust"; + + orientation-switch; + mode-switch; + svid = <0xff01>, <0x8087>; + power-domains = <&DIE_NODE(ps_atc1_usb)>; }; DIE_NODE(dwc3_2_dart_0): iommu@f02f00000 { @@ -214,13 +476,54 @@ reg = <0xf 0x02280000 0x0 0x100000>; interrupt-parent = <&aic>; interrupts = ; - /* dr_mode = "otg"; */ + dr_mode = "otg"; usb-role-switch; role-switch-default-mode = "host"; iommus = <&DIE_NODE(dwc3_2_dart_0) 0>, <&DIE_NODE(dwc3_2_dart_1) 1>; power-domains = <&DIE_NODE(ps_atc2_usb)>; dma-coherent; + resets = <&DIE_NODE(atcphy2)>; + phys = <&DIE_NODE(atcphy2) PHY_TYPE_USB2>, <&DIE_NODE(atcphy2) PHY_TYPE_USB3>; + phy-names = "usb2-phy", "usb3-phy"; + }; + + DIE_NODE(atcphy2): phy@f03000000 { + compatible = "apple,t6000-atcphy", "apple,t8103-atcphy"; + reg = <0xf 0x03000000 0x0 0x4c000>, + <0xf 0x03050000 0x0 0x8000>, + <0xf 0x00000000 0x0 0x4000>, + <0xf 0x02a90000 0x0 0x4000>, + <0xf 0x02a84000 0x0 0x4000>; + reg-names = "core", "lpdptx", "axi2af", "usb2phy", + "pipehandler"; + + #phy-cells = <1>; + #reset-cells = <0>; + + nvmem-cells = <&DIE_NODE(atcphy2_aus_cmn_shm_vreg_trim)>, + <&DIE_NODE(atcphy2_auspll_rodco_encap)>, + <&DIE_NODE(atcphy2_auspll_rodco_bias_adjust)>, + <&DIE_NODE(atcphy2_auspll_fracn_dll_start_capcode)>, + <&DIE_NODE(atcphy2_auspll_dtc_vreg_adjust)>, + <&DIE_NODE(atcphy2_cio3pll_dco_coarsebin0)>, + <&DIE_NODE(atcphy2_cio3pll_dco_coarsebin1)>, + <&DIE_NODE(atcphy2_cio3pll_dll_start_capcode)>, + <&DIE_NODE(atcphy2_cio3pll_dtc_vreg_adjust)>; + nvmem-cell-names = "aus_cmn_shm_vreg_trim", + "auspll_rodco_encap", + "auspll_rodco_bias_adjust", + "auspll_fracn_dll_start_capcode", + "auspll_dtc_vreg_adjust", + "cio3pll_dco_coarsebin0", + "cio3pll_dco_coarsebin1", + "cio3pll_dll_start_capcode", + "cio3pll_dtc_vreg_adjust"; + + orientation-switch; + mode-switch; + svid = <0xff01>, <0x8087>; + power-domains = <&DIE_NODE(ps_atc2_usb)>; }; DIE_NODE(dwc3_3_dart_0): iommu@1302f00000 { @@ -246,11 +549,52 @@ reg = <0x13 0x02280000 0x0 0x100000>; interrupt-parent = <&aic>; interrupts = ; - /* dr_mode = "otg"; */ + dr_mode = "otg"; usb-role-switch; role-switch-default-mode = "host"; iommus = <&DIE_NODE(dwc3_3_dart_0) 0>, <&DIE_NODE(dwc3_3_dart_1) 1>; power-domains = <&DIE_NODE(ps_atc3_usb)>; dma-coherent; + resets = <&DIE_NODE(atcphy3)>; + phys = <&DIE_NODE(atcphy3) PHY_TYPE_USB2>, <&DIE_NODE(atcphy3) PHY_TYPE_USB3>; + phy-names = "usb2-phy", "usb3-phy"; + }; + + DIE_NODE(atcphy3): phy@1303000000 { + compatible = "apple,t6000-atcphy", "apple,t8103-atcphy"; + reg = <0x13 0x03000000 0x0 0x4c000>, + <0x13 0x03050000 0x0 0x8000>, + <0x13 0x00000000 0x0 0x4000>, + <0x13 0x02a90000 0x0 0x4000>, + <0x13 0x02a84000 0x0 0x4000>; + reg-names = "core", "lpdptx", "axi2af", "usb2phy", + "pipehandler"; + + #phy-cells = <1>; + #reset-cells = <0>; + + nvmem-cells = <&DIE_NODE(atcphy3_aus_cmn_shm_vreg_trim)>, + <&DIE_NODE(atcphy3_auspll_rodco_encap)>, + <&DIE_NODE(atcphy3_auspll_rodco_bias_adjust)>, + <&DIE_NODE(atcphy3_auspll_fracn_dll_start_capcode)>, + <&DIE_NODE(atcphy3_auspll_dtc_vreg_adjust)>, + <&DIE_NODE(atcphy3_cio3pll_dco_coarsebin0)>, + <&DIE_NODE(atcphy3_cio3pll_dco_coarsebin1)>, + <&DIE_NODE(atcphy3_cio3pll_dll_start_capcode)>, + <&DIE_NODE(atcphy3_cio3pll_dtc_vreg_adjust)>; + nvmem-cell-names = "aus_cmn_shm_vreg_trim", + "auspll_rodco_encap", + "auspll_rodco_bias_adjust", + "auspll_fracn_dll_start_capcode", + "auspll_dtc_vreg_adjust", + "cio3pll_dco_coarsebin0", + "cio3pll_dco_coarsebin1", + "cio3pll_dll_start_capcode", + "cio3pll_dtc_vreg_adjust"; + + orientation-switch; + mode-switch; + svid = <0xff01>, <0x8087>; + power-domains = <&DIE_NODE(ps_atc3_usb)>; }; diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index 74f452f3ecd367..b4f6496f2b1ace 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -13,6 +13,10 @@ / { aliases { + atcphy0 = &atcphy0; + atcphy1 = &atcphy1; + atcphy2 = &atcphy2; + atcphy3 = &atcphy3; bluetooth0 = &bluetooth0; dcp = &dcp; disp0 = &display; @@ -93,6 +97,12 @@ remote-endpoint = <&typec0_usb_hs>; }; }; + port@1 { + reg = <1>; + typec0_con_ss: endpoint { + remote-endpoint = <&typec0_usb_ss>; + }; + }; }; }; }; @@ -119,6 +129,12 @@ remote-endpoint = <&typec1_usb_hs>; }; }; + port@1 { + reg = <1>; + typec1_con_ss: endpoint { + remote-endpoint = <&typec1_usb_ss>; + }; + }; }; }; }; @@ -145,6 +161,12 @@ remote-endpoint = <&typec2_usb_hs>; }; }; + port@1 { + reg = <1>; + typec2_con_ss: endpoint { + remote-endpoint = <&typec2_usb_ss>; + }; + }; }; }; }; @@ -333,6 +355,31 @@ }; }; +/* Type-C PHYs */ +&atcphy0 { + port { + typec0_usb_ss: endpoint { + remote-endpoint = <&typec0_con_ss>; + }; + }; +}; + +&atcphy1 { + port { + typec1_usb_ss: endpoint { + remote-endpoint = <&typec1_con_ss>; + }; + }; +}; + +&atcphy2 { + port { + typec2_usb_ss: endpoint { + remote-endpoint = <&typec2_con_ss>; + }; + }; +}; + /* ATC3 is used for DisplayPort -> HDMI only */ &dwc3_3_dart_0 { status = "disabled"; diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi index b86abaa21b546a..c5400f298a123e 100644 --- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi @@ -11,6 +11,10 @@ / { aliases { + atcphy0 = &atcphy0; + atcphy1 = &atcphy1; + atcphy2 = &atcphy2; + atcphy3 = &atcphy3; bluetooth0 = &bluetooth0; dcp = &dcp; disp0 = &display; @@ -79,6 +83,12 @@ remote-endpoint = <&typec0_usb_hs>; }; }; + port@1 { + reg = <1>; + typec0_con_ss: endpoint { + remote-endpoint = <&typec0_usb_ss>; + }; + }; }; }; }; @@ -105,6 +115,12 @@ remote-endpoint = <&typec1_usb_hs>; }; }; + port@1 { + reg = <1>; + typec1_con_ss: endpoint { + remote-endpoint = <&typec1_usb_ss>; + }; + }; }; }; }; @@ -131,6 +147,12 @@ remote-endpoint = <&typec2_usb_hs>; }; }; + port@1 { + reg = <1>; + typec2_con_ss: endpoint { + remote-endpoint = <&typec2_usb_ss>; + }; + }; }; }; }; @@ -157,6 +179,12 @@ remote-endpoint = <&typec3_usb_hs>; }; }; + port@1 { + reg = <1>; + typec3_con_ss: endpoint { + remote-endpoint = <&typec3_usb_ss>; + }; + }; }; }; }; @@ -195,6 +223,39 @@ }; }; +/* Type-C PHYs */ +&atcphy0 { + port { + typec0_usb_ss: endpoint { + remote-endpoint = <&typec0_con_ss>; + }; + }; +}; + +&atcphy1 { + port { + typec1_usb_ss: endpoint { + remote-endpoint = <&typec1_con_ss>; + }; + }; +}; + +&atcphy2 { + port { + typec2_usb_ss: endpoint { + remote-endpoint = <&typec2_con_ss>; + }; + }; +}; + +&atcphy3 { + port { + typec3_usb_ss: endpoint { + remote-endpoint = <&typec3_con_ss>; + }; + }; +}; + /* Audio */ &i2c1 { status = "okay"; From 3997aa31375162525680dea69e6fb1d75276900c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 31 Oct 2022 01:19:36 +0100 Subject: [PATCH 0041/1020] arch: arm64: apple: Add dcp panel node for t8103 based laptops and imacs The panel node will contain among other properties backlight control related properties from the "backlight" node in the ADT. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8103-j293.dts | 7 ++++++- arch/arm64/boot/dts/apple/t8103-j313.dts | 7 ++++++- arch/arm64/boot/dts/apple/t8103-j456.dts | 7 ++++++- arch/arm64/boot/dts/apple/t8103-j457.dts | 7 ++++++- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts index 1b87dc8fafea34..a845d92ee10c25 100644 --- a/arch/arm64/boot/dts/apple/t8103-j293.dts +++ b/arch/arm64/boot/dts/apple/t8103-j293.dts @@ -31,7 +31,12 @@ }; &dcp { - apple,connector-type = "eDP"; + panel: panel { + compatible = "apple,panel-j293", "apple,panel"; + width-mm = <286>; + height-mm = <179>; + apple,max-brightness = <525>; + }; }; &bluetooth0 { diff --git a/arch/arm64/boot/dts/apple/t8103-j313.dts b/arch/arm64/boot/dts/apple/t8103-j313.dts index 5061cc5d09e0da..cc05341b24def4 100644 --- a/arch/arm64/boot/dts/apple/t8103-j313.dts +++ b/arch/arm64/boot/dts/apple/t8103-j313.dts @@ -31,7 +31,12 @@ }; &dcp { - apple,connector-type = "eDP"; + panel: panel { + compatible = "apple,panel-j313", "apple,panel"; + width-mm = <286>; + height-mm = <179>; + apple,max-brightness = <420>; + }; }; &bluetooth0 { diff --git a/arch/arm64/boot/dts/apple/t8103-j456.dts b/arch/arm64/boot/dts/apple/t8103-j456.dts index ca74b7f87c0c3b..19f14208a80e16 100644 --- a/arch/arm64/boot/dts/apple/t8103-j456.dts +++ b/arch/arm64/boot/dts/apple/t8103-j456.dts @@ -22,7 +22,12 @@ }; &dcp { - apple,connector-type = "eDP"; + panel: panel { + compatible = "apple,panel-j456", "apple,panel"; + width-mm = <522>; + height-mm = <294>; + apple,max-brightness = <525>; + }; }; &bluetooth0 { diff --git a/arch/arm64/boot/dts/apple/t8103-j457.dts b/arch/arm64/boot/dts/apple/t8103-j457.dts index 2f248178ce5885..72a0a1b9e2db25 100644 --- a/arch/arm64/boot/dts/apple/t8103-j457.dts +++ b/arch/arm64/boot/dts/apple/t8103-j457.dts @@ -22,7 +22,12 @@ }; &dcp { - apple,connector-type = "eDP"; + panel: panel { + compatible = "apple,panel-j457", "apple,panel"; + width-mm = <522>; + height-mm = <294>; + apple,max-brightness = <525>; + }; }; &bluetooth0 { From 0c05a28624e07f57d6edbb06261cbc85d4810620 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 31 Oct 2022 01:21:34 +0100 Subject: [PATCH 0042/1020] arch: arm64: apple: Add dcp panel node for t600x based laptops The panel node will contain among other properties backlight control related properties from the "backlight" node in the ADT. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t6000-j314s.dts | 7 +++++++ arch/arm64/boot/dts/apple/t6000-j316s.dts | 7 +++++++ arch/arm64/boot/dts/apple/t6001-j314c.dts | 7 +++++++ arch/arm64/boot/dts/apple/t6001-j316c.dts | 7 +++++++ arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi | 4 +++- 5 files changed, 31 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/apple/t6000-j314s.dts b/arch/arm64/boot/dts/apple/t6000-j314s.dts index b514b1114c4d24..a2ccc654216164 100644 --- a/arch/arm64/boot/dts/apple/t6000-j314s.dts +++ b/arch/arm64/boot/dts/apple/t6000-j314s.dts @@ -25,6 +25,13 @@ brcm,board-type = "apple,maldives"; }; +&panel { + compatible = "apple,panel-j314", "apple,panel-mini-led", "apple,panel"; + width-mm = <302>; + height-mm = <196>; + adj-height-mm = <189>; +}; + &sound { 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 7f5c91bb5f3210..99a76392b3b791 100644 --- a/arch/arm64/boot/dts/apple/t6000-j316s.dts +++ b/arch/arm64/boot/dts/apple/t6000-j316s.dts @@ -25,6 +25,13 @@ brcm,board-type = "apple,madagascar"; }; +&panel { + compatible = "apple,panel-j316", "apple,panel-mini-led", "apple,panel"; + width-mm = <346>; + height-mm = <223>; + adj-height-mm = <216>; +}; + &sound { 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 35ff978b998702..82d851d4cd3857 100644 --- a/arch/arm64/boot/dts/apple/t6001-j314c.dts +++ b/arch/arm64/boot/dts/apple/t6001-j314c.dts @@ -25,6 +25,13 @@ brcm,board-type = "apple,maldives"; }; +&panel { + compatible = "apple,panel-j314", "apple,panel-mini-led", "apple,panel"; + width-mm = <302>; + height-mm = <196>; + adj-height-mm = <189>; +}; + &sound { 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 6a35c87aa7ab02..a6987c8324dbd7 100644 --- a/arch/arm64/boot/dts/apple/t6001-j316c.dts +++ b/arch/arm64/boot/dts/apple/t6001-j316c.dts @@ -25,6 +25,13 @@ brcm,board-type = "apple,madagascar"; }; +&panel { + compatible = "apple,panel-j316", "apple,panel-mini-led", "apple,panel"; + width-mm = <346>; + height-mm = <223>; + adj-height-mm = <216>; +}; + &sound { 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 b4f6496f2b1ace..39fdc7d3360f42 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -70,7 +70,9 @@ }; &dcp { - apple,connector-type = "eDP"; + panel: panel { + apple,max-brightness = <500>; + }; }; /* USB Type C */ From c470ce26a52cc9ee73d95feecedf89dabdc65a70 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 15 Nov 2022 20:12:33 +0100 Subject: [PATCH 0043/1020] arm64: dts: apple: t8112: Add eFuses node Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112.dtsi | 97 ++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index 4904574aa2f931..e33c464f0ab8e6 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -771,6 +771,103 @@ interrupts = ; }; + efuse@23d2c8000 { + compatible = "apple,t8112-efuses", "apple,efuses"; + reg = <0x2 0x3d2c8000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + + atcphy0_auspll_rodco_bias_adjust: efuse@480,20 { + reg = <0x480 4>; + bits = <20 3>; + }; + + atcphy0_auspll_rodco_encap: efuse@480,23 { + reg = <0x480 4>; + bits = <23 2>; + }; + + atcphy0_auspll_dtc_vreg_adjust: efuse@480,25 { + reg = <0x480 4>; + bits = <25 3>; + }; + + atcphy0_auspll_fracn_dll_start_capcode: efuse@480,28 { + reg = <0x480 4>; + bits = <28 2>; + }; + + atcphy0_aus_cmn_shm_vreg_trim: efuse@480,30 { + reg = <0x480 8>; + bits = <30 5>; + }; + + atcphy0_cio3pll_dco_coarsebin0: efuse@484,3 { + reg = <0x484 4>; + bits = <3 6>; + }; + + atcphy0_cio3pll_dco_coarsebin1: efuse@484,9 { + reg = <0x484 4>; + bits = <9 6>; + }; + + atcphy0_cio3pll_dll_start_capcode: efuse@484,15 { + reg = <0x484 4>; + bits = <15 2>; + }; + + atcphy0_cio3pll_dtc_vreg_adjust: efuse@484,17 { + reg = <0x484 0x4>; + bits = <17 3>; + }; + + atcphy1_auspll_rodco_bias_adjust: efuse@484,30 { + reg = <0x484 8>; + bits = <30 3>; + }; + + atcphy1_auspll_rodco_encap: efuse@488,1 { + reg = <0x488 8>; + bits = <1 2>; + }; + + atcphy1_auspll_dtc_vreg_adjust: efuse@488,3 { + reg = <0x488 4>; + bits = <3 3>; + }; + + atcphy1_auspll_fracn_dll_start_capcode: efuse@488,6 { + reg = <0x488 4>; + bits = <6 2>; + }; + + atcphy1_aus_cmn_shm_vreg_trim: efuse@488,8 { + reg = <0x488 4>; + bits = <8 5>; + }; + + atcphy1_cio3pll_dco_coarsebin0: efuse@488,13 { + reg = <0x488 4>; + bits = <13 6>; + }; + + atcphy1_cio3pll_dco_coarsebin1: efuse@488,19 { + reg = <0x488 4>; + bits = <19 6>; + }; + + atcphy1_cio3pll_dll_start_capcode: efuse@488,25 { + reg = <0x488 4>; + bits = <25 2>; + }; + + atcphy1_cio3pll_dtc_vreg_adjust: efuse@488,27 { + reg = <0x488 0x4>; + bits = <27 3>; + }; + }; + smc_mbox: mbox@23e408000 { compatible = "apple,t8112-asc-mailbox", "apple,asc-mailbox-v4"; reg = <0x2 0x3e408000 0x0 0x4000>; From 691ab4c34d55d1899f124fde3e08279a01cb5134 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 15 Nov 2022 20:14:27 +0100 Subject: [PATCH 0044/1020] arm64: dts: apple: t8112: Add ATCPHY nodes Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112-jxxx.dtsi | 31 +++++++++ arch/arm64/boot/dts/apple/t8112.dtsi | 83 +++++++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi b/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi index 2c14f4479c401f..1d0d1ab4a97cc9 100644 --- a/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi +++ b/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi @@ -11,6 +11,8 @@ / { aliases { + atcphy0 = &atcphy0; + atcphy1 = &atcphy1; serial0 = &serial0; serial2 = &serial2; }; @@ -68,6 +70,12 @@ remote-endpoint = <&typec0_usb_hs>; }; }; + port@1 { + reg = <1>; + typec0_con_ss: endpoint { + remote-endpoint = <&typec0_usb_ss>; + }; + }; }; }; }; @@ -93,6 +101,12 @@ remote-endpoint = <&typec1_usb_hs>; }; }; + port@1 { + reg = <1>; + typec1_con_ss: endpoint { + remote-endpoint = <&typec1_usb_ss>; + }; + }; }; }; }; @@ -115,6 +129,23 @@ }; }; +/* Type-C PHYs */ +&atcphy0 { + port { + typec0_usb_ss: endpoint { + remote-endpoint = <&typec0_con_ss>; + }; + }; +}; + +&atcphy1 { + port { + typec1_usb_ss: endpoint { + remote-endpoint = <&typec1_con_ss>; + }; + }; +}; + &i2c1 { status = "okay"; }; diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index e33c464f0ab8e6..d5edb738cd3d61 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -11,6 +11,7 @@ #include #include #include +#include #include / { @@ -1065,6 +1066,9 @@ role-switch-default-mode = "host"; iommus = <&dwc3_0_dart_0 0>, <&dwc3_0_dart_1 1>; power-domains = <&ps_atc0_usb>; + resets = <&atcphy0>; + phys = <&atcphy0 PHY_TYPE_USB2>, <&atcphy0 PHY_TYPE_USB3>; + phy-names = "usb2-phy", "usb3-phy"; }; dwc3_0_dart_0: iommu@382f00000 { @@ -1085,6 +1089,44 @@ power-domains = <&ps_atc0_usb>; }; + atcphy0: phy@383000000 { + compatible = "apple,t8112-atcphy", "apple,t8103-atcphy"; + reg = <0x3 0x83000000 0x0 0x4c000>, + <0x3 0x83050000 0x0 0x8000>, + <0x3 0x80000000 0x0 0x4000>, + <0x3 0x82a90000 0x0 0x4000>, + <0x3 0x82a84000 0x0 0x4000>; + reg-names = "core", "lpdptx", "axi2af", "usb2phy", + "pipehandler"; + + #phy-cells = <1>; + #reset-cells = <0>; + + nvmem-cells = <&atcphy0_aus_cmn_shm_vreg_trim>, + <&atcphy0_auspll_rodco_encap>, + <&atcphy0_auspll_rodco_bias_adjust>, + <&atcphy0_auspll_fracn_dll_start_capcode>, + <&atcphy0_auspll_dtc_vreg_adjust>, + <&atcphy0_cio3pll_dco_coarsebin0>, + <&atcphy0_cio3pll_dco_coarsebin1>, + <&atcphy0_cio3pll_dll_start_capcode>, + <&atcphy0_cio3pll_dtc_vreg_adjust>; + nvmem-cell-names = "aus_cmn_shm_vreg_trim", + "auspll_rodco_encap", + "auspll_rodco_bias_adjust", + "auspll_fracn_dll_start_capcode", + "auspll_dtc_vreg_adjust", + "cio3pll_dco_coarsebin0", + "cio3pll_dco_coarsebin1", + "cio3pll_dll_start_capcode", + "cio3pll_dtc_vreg_adjust"; + + orientation-switch; + mode-switch; + svid = <0xff01>, <0x8087>; + power-domains = <&ps_atc0_usb>; + }; + dwc3_1: usb@502280000 { compatible = "apple,t8112-dwc3", "apple,dwc3", "snps,dwc3"; reg = <0x5 0x02280000 0x0 0x100000>; @@ -1095,6 +1137,9 @@ role-switch-default-mode = "host"; iommus = <&dwc3_1_dart_0 0>, <&dwc3_1_dart_1 1>; power-domains = <&ps_atc1_usb>; + resets = <&atcphy1>; + phys = <&atcphy1 PHY_TYPE_USB2>, <&atcphy1 PHY_TYPE_USB3>; + phy-names = "usb2-phy", "usb3-phy"; }; dwc3_1_dart_0: iommu@502f00000 { @@ -1115,6 +1160,44 @@ power-domains = <&ps_atc1_usb>; }; + atcphy1: phy@503000000 { + compatible = "apple,t8112-atcphy", "apple,t8103-atcphy"; + reg = <0x5 0x03000000 0x0 0x4c000>, + <0x5 0x03050000 0x0 0x8000>, + <0x5 0x0 0x0 0x4000>, + <0x5 0x02a90000 0x0 0x4000>, + <0x5 0x02a84000 0x0 0x4000>; + reg-names = "core", "lpdptx", "axi2af", "usb2phy", + "pipehandler"; + + nvmem-cells = <&atcphy1_aus_cmn_shm_vreg_trim>, + <&atcphy1_auspll_rodco_encap>, + <&atcphy1_auspll_rodco_bias_adjust>, + <&atcphy1_auspll_fracn_dll_start_capcode>, + <&atcphy1_auspll_dtc_vreg_adjust>, + <&atcphy1_cio3pll_dco_coarsebin0>, + <&atcphy1_cio3pll_dco_coarsebin1>, + <&atcphy1_cio3pll_dll_start_capcode>, + <&atcphy1_cio3pll_dtc_vreg_adjust>; + nvmem-cell-names = "aus_cmn_shm_vreg_trim", + "auspll_rodco_encap", + "auspll_rodco_bias_adjust", + "auspll_fracn_dll_start_capcode", + "auspll_dtc_vreg_adjust", + "cio3pll_dco_coarsebin0", + "cio3pll_dco_coarsebin1", + "cio3pll_dll_start_capcode", + "cio3pll_dtc_vreg_adjust"; + + #phy-cells = <1>; + #reset-cells = <0>; + + orientation-switch; + mode-switch; + svid = <0xff01>, <0x8087>; + power-domains = <&ps_atc1_usb>; + }; + pcie0_dart: iommu@681008000 { compatible = "apple,t8110-dart"; reg = <0x6 0x81008000 0x0 0x4000>; From 1a9848aa69affedb61730c33b72a26196f8efad1 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 20 Nov 2022 20:22:57 +0100 Subject: [PATCH 0045/1020] arm64: dts: apple: t8112: Add dcp/disp0 nodes Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112-j413.dts | 10 +++ arch/arm64/boot/dts/apple/t8112-j493.dts | 9 +++ arch/arm64/boot/dts/apple/t8112-jxxx.dtsi | 10 +++ arch/arm64/boot/dts/apple/t8112.dtsi | 79 +++++++++++++++++++++++ 4 files changed, 108 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j413.dts b/arch/arm64/boot/dts/apple/t8112-j413.dts index c4ae6b5a6c7519..de9c98c6fac0a2 100644 --- a/arch/arm64/boot/dts/apple/t8112-j413.dts +++ b/arch/arm64/boot/dts/apple/t8112-j413.dts @@ -35,6 +35,16 @@ }; }; +&dcp { + panel: panel { + compatible = "apple,panel-j413", "apple,panel"; + width-mm = <290>; + height-mm = <189>; + adj-height-mm = <181>; + apple,max-brightness = <525>; + }; +}; + /* * Force the bus number assignments so that we can declare some of the * on-board devices and properties that are populated by the bootloader diff --git a/arch/arm64/boot/dts/apple/t8112-j493.dts b/arch/arm64/boot/dts/apple/t8112-j493.dts index 45302227cf4be7..42705711c6928d 100644 --- a/arch/arm64/boot/dts/apple/t8112-j493.dts +++ b/arch/arm64/boot/dts/apple/t8112-j493.dts @@ -35,6 +35,15 @@ }; }; +&dcp { + panel: panel { + compatible = "apple,panel-j493", "apple,panel"; + width-mm = <286>; + height-mm = <179>; + apple,max-brightness = <525>; + }; +}; + /* * Force the bus number assignments so that we can declare some of the * on-board devices and properties that are populated by the bootloader diff --git a/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi b/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi index 1d0d1ab4a97cc9..7edabecc862e46 100644 --- a/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi +++ b/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi @@ -13,6 +13,9 @@ aliases { atcphy0 = &atcphy0; atcphy1 = &atcphy1; + dcp = &dcp; + disp0 = &display; + disp0_piodma = &disp0_piodma; serial0 = &serial0; serial2 = &serial2; }; @@ -32,6 +35,13 @@ }; }; + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + /* To be filled by loader */ + }; + memory@800000000 { device_type = "memory"; reg = <0x8 0 0x2 0>; /* To be filled by loader */ diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index d5edb738cd3d61..ec99763b11cc35 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -360,6 +360,14 @@ clock-output-names = "nco_ref"; }; + /* Pixel clock? frequency in Hz (compare: 4K@60 VGA clock 533.250 MHz) */ + clk_disp0: clock-disp0 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <533333328>; + clock-output-names = "clk_disp0"; + }; + soc { compatible = "simple-bus"; #address-cells = <2>; @@ -380,6 +388,71 @@ #performance-domain-cells = <0>; }; + disp0_dart: iommu@231304000 { + compatible = "apple,t8112-dart", "apple,t8110-dart"; + reg = <0x2 0x31304000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + status = "disabled"; + }; + + dcp_dart: iommu@23130c000 { + compatible = "apple,t8112-dart", "apple,t8110-dart"; + reg = <0x2 0x3130c000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + }; + + dcp_mbox: mbox@231c08000 { + compatible = "apple,t8112-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x2 0x31c08000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + power-domains = <&ps_disp0_cpu0>; + resets = <&ps_disp0_cpu0>; + }; + + dcp: dcp@231c00000 { + compatible = "apple,t8112-dcp", "apple,dcp"; + mboxes = <&dcp_mbox>; + mbox-names = "mbox"; + iommus = <&dcp_dart 5>; + + /* the ADT has 2 additional regs which seems to be unused */ + reg-names = "coproc", "disp-0", "disp-1", "disp-2", "disp-3"; + reg = <0x2 0x31c00000 0x0 0x4000>, + <0x2 0x30000000 0x0 0x61c000>, + <0x2 0x31320000 0x0 0x4000>, + <0x2 0x31344000 0x0 0x4000>, + <0x2 0x31800000 0x0 0x800000>; + apple,bw-scratch = <&pmgr_dcp 0 4 0x5d8>; + power-domains = <&ps_disp0_cpu0>; + clocks = <&clk_disp0>; + apple,asc-dram-mask = <0x0 0x0>; + phandle = <&dcp>; + + disp0_piodma: piodma { + iommus = <&disp0_dart 4>; + phandle = <&disp0_piodma>; + }; + }; + + display: display-subsystem { + compatible = "apple,display-subsystem"; + /* disp_dart0 must be 1st since it is locked */ + iommus = <&disp0_dart 0>; + /* generate phandle explicitly for use in loader */ + phandle = <&display>; + }; + sio_dart: iommu@235004000 { compatible = "apple,t8110-dart"; reg = <0x2 0x35004000 0x0 0x4000>; @@ -595,6 +668,12 @@ /* child nodes are added in t8103-pmgr.dtsi */ }; + pmgr_dcp: power-management@23b3d0000 { + reg = <0x2 0x3b3d0000 0x0 0x4000>; + reg-names = "dcp-bw-scratch"; + #apple,bw-scratch-cells = <3>; + }; + pinctrl_ap: pinctrl@23c100000 { compatible = "apple,t8112-pinctrl", "apple,pinctrl"; reg = <0x2 0x3c100000 0x0 0x100000>; From 03ad61abd065d38ec4023a874b3e8006f0e6942e Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Wed, 2 Nov 2022 15:58:07 +0900 Subject: [PATCH 0046/1020] scripts/dtc: Add support for floating-point literals Signed-off-by: Asahi Lina --- scripts/dtc/data.c | 27 +++++++++++++++++++++++++++ scripts/dtc/dtc-lexer.l | 22 ++++++++++++++++++++++ scripts/dtc/dtc-parser.y | 16 ++++++++++++++++ scripts/dtc/dtc.h | 1 + 4 files changed, 66 insertions(+) diff --git a/scripts/dtc/data.c b/scripts/dtc/data.c index 14734233ad8b7e..d12c1f0146bedf 100644 --- a/scripts/dtc/data.c +++ b/scripts/dtc/data.c @@ -184,6 +184,33 @@ struct data data_append_integer(struct data d, uint64_t value, int bits) } } +struct data data_append_float(struct data d, double value, int bits) +{ + float f32; + uint32_t u32; + double f64; + uint64_t u64; + fdt32_t value_32; + fdt64_t value_64; + + switch (bits) { + case 32: + f32 = value; + memcpy(&u32, &f32, sizeof(u32)); + value_32 = cpu_to_fdt32(u32); + return data_append_data(d, &value_32, 4); + + case 64: + f64 = value; + memcpy(&u64, &f64, sizeof(u64)); + value_64 = cpu_to_fdt64(u64); + return data_append_data(d, &value_64, 8); + + default: + die("Invalid literal size (%d)\n", bits); + } +} + struct data data_append_re(struct data d, uint64_t address, uint64_t size) { struct fdt_reserve_entry re; diff --git a/scripts/dtc/dtc-lexer.l b/scripts/dtc/dtc-lexer.l index de60a70b6bdbcb..ac0fadff20802d 100644 --- a/scripts/dtc/dtc-lexer.l +++ b/scripts/dtc/dtc-lexer.l @@ -151,6 +151,28 @@ static void PRINTF(1, 2) lexical_error(const char *fmt, ...); return DT_LABEL; } +[-+]?(([0-9]+\.[0-9]*)|([0-9]*\.[0-9]+))(e[-+]?[0-9]+)?f? { + char *e; + DPRINT("Floating-point Literal: '%s'\n", yytext); + + errno = 0; + yylval.floating = strtod(yytext, &e); + + if (*e && (*e != 'f' || e[1])) { + lexical_error("Bad floating-point literal '%s'", + yytext); + } + + if (errno == ERANGE) + lexical_error("Floating-point literal '%s' out of range", + yytext); + else + /* ERANGE is the only strtod error triggerable + * by strings matching the pattern */ + assert(errno == 0); + return DT_FP_LITERAL; + } + ([0-9]+|0[xX][0-9a-fA-F]+)(U|L|UL|LL|ULL)? { char *e; DPRINT("Integer Literal: '%s'\n", yytext); diff --git a/scripts/dtc/dtc-parser.y b/scripts/dtc/dtc-parser.y index bff1337ec2665a..398451f2fab54b 100644 --- a/scripts/dtc/dtc-parser.y +++ b/scripts/dtc/dtc-parser.y @@ -48,6 +48,7 @@ static bool is_ref_relative(const char *ref) struct node *nodelist; struct reserve_info *re; uint64_t integer; + double floating; unsigned int flags; } @@ -61,6 +62,7 @@ static bool is_ref_relative(const char *ref) %token DT_OMIT_NO_REF %token DT_PROPNODENAME %token DT_LITERAL +%token DT_FP_LITERAL %token DT_CHAR_LITERAL %token DT_BYTE %token DT_STRING @@ -86,6 +88,7 @@ static bool is_ref_relative(const char *ref) %type subnode %type subnodes +%type floating_prim %type integer_prim %type integer_unary %type integer_mul @@ -392,6 +395,15 @@ arrayprefix: $$.data = data_add_marker(empty_data, TYPE_UINT32, NULL); $$.bits = 32; } + | arrayprefix floating_prim + { + if ($1.bits < 32) { + ERROR(&@2, "Floating-point values must be" + " 32-bit or 64-bit"); + } + + $$.data = data_append_float($1.data, $2, $1.bits); + } | arrayprefix integer_prim { if ($1.bits < 64) { @@ -436,6 +448,10 @@ arrayprefix: } ; +floating_prim: + DT_FP_LITERAL + ; + integer_prim: DT_LITERAL | DT_CHAR_LITERAL diff --git a/scripts/dtc/dtc.h b/scripts/dtc/dtc.h index 0a1f54991026bd..1054d922207cc5 100644 --- a/scripts/dtc/dtc.h +++ b/scripts/dtc/dtc.h @@ -177,6 +177,7 @@ struct data data_insert_at_marker(struct data d, struct marker *m, struct data data_merge(struct data d1, struct data d2); struct data data_append_cell(struct data d, cell_t word); struct data data_append_integer(struct data d, uint64_t word, int bits); +struct data data_append_float(struct data d, double value, int bits); struct data data_append_re(struct data d, uint64_t address, uint64_t size); struct data data_append_addr(struct data d, uint64_t addr); struct data data_append_byte(struct data d, uint8_t byte); From d1f7a02ac80bdab2617a58f1a167c7fc15591b15 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Thu, 18 Aug 2022 02:15:43 +0900 Subject: [PATCH 0047/1020] arm64: dts: apple: t8103*: Add GPU nodes Signed-off-by: Asahi Lina --- arch/arm64/boot/dts/apple/t8103.dtsi | 51 ++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index 4c64d4a3cd9263..8839cace6d154c 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -353,6 +353,27 @@ clock-output-names = "nco_ref"; }; + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + uat_handoff: uat-handoff { + reg = <0 0 0 0>; + no-map; + }; + + uat_pagetables: uat-pagetables { + reg = <0 0 0 0>; + no-map; + }; + + uat_ttbs: uat-ttbs { + reg = <0 0 0 0>; + no-map; + }; + }; + soc { compatible = "simple-bus"; #address-cells = <2>; @@ -361,6 +382,36 @@ ranges; nonposted-mmio; + agx: gpu@206400000 { + compatible = "apple,agx-t8103", "apple,agx-g13g"; + reg = <0x2 0x6400000 0 0x40000>, + <0x2 0x4000000 0 0x1000000>; + reg-names = "asc", "sgx"; + interrupt-parent = <&aic>; + interrupts = , + , + , + , + ; + mboxes = <&agx_mbox>; + power-domains = <&ps_gfx>; + memory-region = <&uat_ttbs>, <&uat_pagetables>, <&uat_handoff>; + memory-region-names = "ttbs", "pagetables", "handoff"; + }; + + agx_mbox: mbox@206408000 { + compatible = "apple,t8103-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x2 0x6408000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + }; + cpufreq_e: performance-controller@210e20000 { compatible = "apple,t8103-cluster-cpufreq", "apple,cluster-cpufreq"; reg = <0x2 0x10e20000 0 0x1000>; From f180cb1b29307663d5a1ab24bd4f2ed443428f9d Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Sat, 22 Oct 2022 00:15:22 +0900 Subject: [PATCH 0048/1020] arm64: dts: Add GPU performance data to t8103.dts Signed-off-by: Asahi Lina --- arch/arm64/boot/dts/apple/t8103.dtsi | 52 +++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index 8839cace6d154c..81a7d9513c91c0 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -20,6 +20,10 @@ #address-cells = <2>; #size-cells = <2>; + aliases { + gpu = &gpu; + }; + cpus { #address-cells = <2>; #size-cells = <0>; @@ -299,6 +303,50 @@ #endif }; + gpu_opp: opp-table-gpu { + compatible = "operating-points-v2"; + + /* + * NOTE: The voltage and power values are device-specific and + * must be filled in by the bootloader. + */ + opp00 { + opp-hz = /bits/ 64 <0>; + opp-microvolt = <400000>; + apple,opp-rel-power = <0>; + }; + opp01 { + opp-hz = /bits/ 64 <396000000>; + opp-microvolt = <603000>; + apple,opp-rel-power = <19>; + }; + opp02 { + opp-hz = /bits/ 64 <528000000>; + opp-microvolt = <640000>; + apple,opp-rel-power = <26>; + }; + opp03 { + opp-hz = /bits/ 64 <720000000>; + opp-microvolt = <690000>; + apple,opp-rel-power = <38>; + }; + opp04 { + opp-hz = /bits/ 64 <924000000>; + opp-microvolt = <784000>; + apple,opp-rel-power = <60>; + }; + opp05 { + opp-hz = /bits/ 64 <1128000000>; + opp-microvolt = <862000>; + apple,opp-rel-power = <87>; + }; + opp06 { + opp-hz = /bits/ 64 <1278000000>; + opp-microvolt = <931000>; + apple,opp-rel-power = <100>; + }; + }; + timer { compatible = "arm,armv8-timer"; interrupt-parent = <&aic>; @@ -382,7 +430,7 @@ ranges; nonposted-mmio; - agx: gpu@206400000 { + gpu: gpu@206400000 { compatible = "apple,agx-t8103", "apple,agx-g13g"; reg = <0x2 0x6400000 0 0x40000>, <0x2 0x4000000 0 0x1000000>; @@ -397,6 +445,8 @@ power-domains = <&ps_gfx>; memory-region = <&uat_ttbs>, <&uat_pagetables>, <&uat_handoff>; memory-region-names = "ttbs", "pagetables", "handoff"; + + operating-points-v2 = <&gpu_opp>; }; agx_mbox: mbox@206408000 { From 15d35f7f067994edc5b4e1fbd978f278cb21bd9a Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Wed, 2 Nov 2022 23:32:46 +0900 Subject: [PATCH 0049/1020] arm64: dts: Add power data for t8103 Signed-off-by: Asahi Lina --- arch/arm64/boot/dts/apple/t8103-j274.dts | 4 +++ arch/arm64/boot/dts/apple/t8103-j456.dts | 4 +++ arch/arm64/boot/dts/apple/t8103-j457.dts | 4 +++ arch/arm64/boot/dts/apple/t8103.dtsi | 40 +++++++++++++++++++----- 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8103-j274.dts b/arch/arm64/boot/dts/apple/t8103-j274.dts index 7169563c1b351a..0a69e2962c7c7f 100644 --- a/arch/arm64/boot/dts/apple/t8103-j274.dts +++ b/arch/arm64/boot/dts/apple/t8103-j274.dts @@ -127,3 +127,7 @@ }; }; + +&gpu { + apple,perf-base-pstate = <3>; +}; diff --git a/arch/arm64/boot/dts/apple/t8103-j456.dts b/arch/arm64/boot/dts/apple/t8103-j456.dts index 19f14208a80e16..c16a0594b1d2ca 100644 --- a/arch/arm64/boot/dts/apple/t8103-j456.dts +++ b/arch/arm64/boot/dts/apple/t8103-j456.dts @@ -127,3 +127,7 @@ }; }; }; + +&gpu { + apple,perf-base-pstate = <3>; +}; diff --git a/arch/arm64/boot/dts/apple/t8103-j457.dts b/arch/arm64/boot/dts/apple/t8103-j457.dts index 72a0a1b9e2db25..c1d1201ecbe2cc 100644 --- a/arch/arm64/boot/dts/apple/t8103-j457.dts +++ b/arch/arm64/boot/dts/apple/t8103-j457.dts @@ -100,3 +100,7 @@ }; }; }; + +&gpu { + apple,perf-base-pstate = <3>; +}; diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index 81a7d9513c91c0..7a3c553f863f12 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -313,37 +313,37 @@ opp00 { opp-hz = /bits/ 64 <0>; opp-microvolt = <400000>; - apple,opp-rel-power = <0>; + opp-microwatt = <0>; }; opp01 { opp-hz = /bits/ 64 <396000000>; opp-microvolt = <603000>; - apple,opp-rel-power = <19>; + opp-microwatt = <3714690>; }; opp02 { opp-hz = /bits/ 64 <528000000>; opp-microvolt = <640000>; - apple,opp-rel-power = <26>; + opp-microwatt = <5083260>; }; opp03 { opp-hz = /bits/ 64 <720000000>; opp-microvolt = <690000>; - apple,opp-rel-power = <38>; + opp-microwatt = <7429380>; }; opp04 { opp-hz = /bits/ 64 <924000000>; opp-microvolt = <784000>; - apple,opp-rel-power = <60>; + opp-microwatt = <11730600>; }; opp05 { opp-hz = /bits/ 64 <1128000000>; opp-microvolt = <862000>; - apple,opp-rel-power = <87>; + opp-microwatt = <17009370>; }; opp06 { opp-hz = /bits/ 64 <1278000000>; opp-microvolt = <931000>; - apple,opp-rel-power = <100>; + opp-microwatt = <19551000>; }; }; @@ -447,6 +447,32 @@ memory-region-names = "ttbs", "pagetables", "handoff"; operating-points-v2 = <&gpu_opp>; + apple,perf-base-pstate = <1>; + apple,min-sram-microvolt = <850000>; + apple,avg-power-filter-tc-ms = <1000>; + apple,avg-power-ki-only = <7.5>; + apple,avg-power-kp = <4.0>; + apple,avg-power-min-duty-cycle = <40>; + apple,avg-power-target-filter-tc = <125>; + apple,fast-die0-integral-gain = <200.0>; + apple,fast-die0-proportional-gain = <5.0>; + apple,perf-filter-drop-threshold = <0>; + apple,perf-filter-time-constant = <5>; + apple,perf-filter-time-constant2 = <50>; + apple,perf-integral-gain2 = <0.197392>; + apple,perf-integral-min-clamp = <0>; + apple,perf-proportional-gain2 = <6.853981>; + apple,perf-tgt-utilization = <85>; + apple,power-sample-period = <8>; + apple,power-zones = <30000 100 6875>; + apple,ppm-filter-time-constant-ms = <100>; + apple,ppm-ki = <91.5>; + apple,ppm-kp = <6.9>; + apple,pwr-filter-time-constant = <313>; + apple,pwr-integral-gain = <0.0202129>; + apple,pwr-integral-min-clamp = <0>; + apple,pwr-min-duty-cycle = <40>; + apple,pwr-proportional-gain = <5.2831855>; }; agx_mbox: mbox@206408000 { From 2098d4e83cedbd943c43ad1546a430aef6077b14 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Thu, 3 Nov 2022 01:03:44 +0900 Subject: [PATCH 0050/1020] arm64: dts: Add t600x GPU nodes Signed-off-by: Asahi Lina --- arch/arm64/boot/dts/apple/t6000.dtsi | 6 ++ arch/arm64/boot/dts/apple/t6001.dtsi | 26 ++++++++ arch/arm64/boot/dts/apple/t6002.dtsi | 8 +++ arch/arm64/boot/dts/apple/t600x-common.dtsi | 66 +++++++++++++++++++++ arch/arm64/boot/dts/apple/t600x-die0.dtsi | 64 ++++++++++++++++++++ arch/arm64/boot/dts/apple/t600x-j375.dtsi | 9 +++ 6 files changed, 179 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6000.dtsi b/arch/arm64/boot/dts/apple/t6000.dtsi index 89c3b211b116e9..c9e4e52d9aac92 100644 --- a/arch/arm64/boot/dts/apple/t6000.dtsi +++ b/arch/arm64/boot/dts/apple/t6000.dtsi @@ -9,6 +9,8 @@ /* This chip is just a cut down version of t6001, so include it and disable the missing parts */ +#define GPU_REPEAT(x) + #include "t6001.dtsi" / { @@ -16,3 +18,7 @@ }; /delete-node/ &pmgr_south; + +&gpu { + compatible = "apple,agx-t6000", "apple,agx-g13x"; +}; diff --git a/arch/arm64/boot/dts/apple/t6001.dtsi b/arch/arm64/boot/dts/apple/t6001.dtsi index 0bdd1966f5302e..6e7e7cdeacf943 100644 --- a/arch/arm64/boot/dts/apple/t6001.dtsi +++ b/arch/arm64/boot/dts/apple/t6001.dtsi @@ -16,11 +16,33 @@ #include "multi-die-cpp.h" +#ifndef GPU_REPEAT +# define GPU_REPEAT(x) +#endif + #include "t600x-common.dtsi" / { compatible = "apple,t6001", "apple,arm-platform"; + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + uat_handoff: uat-handoff { + reg = <0 0 0 0>; + }; + + uat_pagetables: uat-pagetables { + reg = <0 0 0 0>; + }; + + uat_ttbs: uat-ttbs { + reg = <0 0 0 0>; + }; + }; + soc { compatible = "simple-bus"; #address-cells = <2>; @@ -63,3 +85,7 @@ }; }; }; + +&gpu { + compatible = "apple,agx-t6001", "apple,agx-g13x"; +}; diff --git a/arch/arm64/boot/dts/apple/t6002.dtsi b/arch/arm64/boot/dts/apple/t6002.dtsi index 8fa2d8dd72ff7f..f1164315be755a 100644 --- a/arch/arm64/boot/dts/apple/t6002.dtsi +++ b/arch/arm64/boot/dts/apple/t6002.dtsi @@ -16,6 +16,10 @@ #include "multi-die-cpp.h" +#ifndef GPU_REPEAT +# define GPU_REPEAT(x) +#endif + #include "t600x-common.dtsi" / { @@ -301,3 +305,7 @@ // On t6002, the die0 GPU power domain needs both AFR power domains power-domains = <&ps_afr>, <&ps_afr_die1>; }; + +&gpu { + compatible = "apple,agx-t6002", "apple,agx-g13x"; +}; diff --git a/arch/arm64/boot/dts/apple/t600x-common.dtsi b/arch/arm64/boot/dts/apple/t600x-common.dtsi index 01385ef831ca91..c76ab2fc8f96dd 100644 --- a/arch/arm64/boot/dts/apple/t600x-common.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-common.dtsi @@ -11,6 +11,10 @@ #address-cells = <2>; #size-cells = <2>; + aliases { + gpu = &gpu; + }; + cpus { #address-cells = <2>; #size-cells = <0>; @@ -333,6 +337,50 @@ */ }; + gpu_opp: opp-table-gpu { + compatible = "operating-points-v2"; + + /* + * NOTE: The voltage and power values are device-specific and + * must be filled in by the bootloader. + */ + opp00 { + opp-hz = /bits/ 64 <0>; + opp-microvolt = GPU_REPEAT(400000); + opp-microwatt = <0>; + }; + opp01 { + opp-hz = /bits/ 64 <388800000>; + opp-microvolt = GPU_REPEAT(634000); + opp-microwatt = <25011450>; + }; + opp02 { + opp-hz = /bits/ 64 <486000000>; + opp-microvolt = GPU_REPEAT(650000); + opp-microwatt = <31681170>; + }; + opp03 { + opp-hz = /bits/ 64 <648000000>; + opp-microvolt = GPU_REPEAT(668000); + opp-microwatt = <41685750>; + }; + opp04 { + opp-hz = /bits/ 64 <777600000>; + opp-microvolt = GPU_REPEAT(715000); + opp-microwatt = <56692620>; + }; + opp05 { + opp-hz = /bits/ 64 <972000000>; + opp-microvolt = GPU_REPEAT(778000); + opp-microwatt = <83371500>; + }; + opp06 { + opp-hz = /bits/ 64 <1296000000>; + opp-microvolt = GPU_REPEAT(903000); + opp-microwatt = <166743000>; + }; + }; + pmu-e { compatible = "apple,icestorm-pmu"; interrupt-parent = <&aic>; @@ -384,4 +432,22 @@ #clock-cells = <0>; clock-output-names = "nco_ref"; }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + uat_handoff: uat-handoff { + reg = <0 0 0 0>; + }; + + uat_pagetables: uat-pagetables { + reg = <0 0 0 0>; + }; + + uat_ttbs: uat-ttbs { + reg = <0 0 0 0>; + }; + }; }; diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index 1b795ddbe552d5..a313f8e3058645 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -426,6 +426,70 @@ #sound-dai-cells = <1>; }; + gpu: gpu@406400000 { + compatible = "apple,agx-g13x"; + reg = <0x4 0x6400000 0 0x40000>, + <0x4 0x4000000 0 0x1000000>; + reg-names = "asc", "sgx"; + interrupt-parent = <&aic>; + interrupts = , + , + , + , + ; + mboxes = <&agx_mbox>; + power-domains = <&ps_gfx>; + memory-region = <&uat_ttbs>, <&uat_pagetables>, <&uat_handoff>; + memory-region-names = "ttbs", "pagetables", "handoff"; + + operating-points-v2 = <&gpu_opp>; + apple,perf-base-pstate = <1>; + apple,min-sram-microvolt = <790000>; + apple,avg-power-filter-tc-ms = <1000>; + apple,avg-power-ki-only = <2.4>; + apple,avg-power-kp = <1.5>; + apple,avg-power-min-duty-cycle = <40>; + apple,avg-power-target-filter-tc = <125>; + apple,fast-die0-integral-gain = <500.0>; + apple,fast-die0-proportional-gain = <72.0>; + apple,perf-boost-ce-step = <50>; + apple,perf-boost-min-util = <90>; + apple,perf-filter-drop-threshold = <0>; + apple,perf-filter-time-constant = <5>; + apple,perf-filter-time-constant2 = <50>; + apple,perf-integral-gain = <6.3>; + apple,perf-integral-gain2 = <0.197392>; + apple,perf-integral-min-clamp = <0>; + apple,perf-proportional-gain = <15.75>; + apple,perf-proportional-gain2 = <6.853981>; + apple,perf-tgt-utilization = <85>; + apple,power-sample-period = <8>; + apple,ppm-filter-time-constant-ms = <100>; + apple,ppm-ki = <30>; + apple,ppm-kp = <1.5>; + apple,pwr-filter-time-constant = <313>; + apple,pwr-integral-gain = <0.0202129>; + apple,pwr-integral-min-clamp = <0>; + apple,pwr-min-duty-cycle = <40>; + apple,pwr-proportional-gain = <5.2831855>; + + apple,core-leak-coef = GPU_REPEAT(1200.0); + apple,sram-leak-coef = GPU_REPEAT(20.0); + }; + + agx_mbox: mbox@406408000 { + compatible = "apple,t6000-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x4 0x6408000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + }; + pcie0_dart_0: iommu@581008000 { compatible = "apple,t6000-dart"; reg = <0x5 0x81008000 0x0 0x4000>; diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi index c5400f298a123e..dad19cad7a99a0 100644 --- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi @@ -381,3 +381,12 @@ &pcie0_dart_3 { status = "okay"; }; + +&gpu { + apple,avg-power-ki-only = <0.6375>; + apple,avg-power-kp = <0.58>; + apple,avg-power-target-filter-tc = <1>; + apple,perf-base-pstate = <3>; + apple,ppm-ki = <5.8>; + apple,ppm-kp = <0.355>; +}; From 4b13a896a4117aec7d68f2823cf082f49eb6d62e Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Fri, 4 Nov 2022 21:56:26 +0900 Subject: [PATCH 0051/1020] arm64: dts: t8103: Add GPU leak coefficients Signed-off-by: Asahi Lina --- arch/arm64/boot/dts/apple/t8103.dtsi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index 7a3c553f863f12..c77ae26879090f 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -473,6 +473,9 @@ apple,pwr-integral-min-clamp = <0>; apple,pwr-min-duty-cycle = <40>; apple,pwr-proportional-gain = <5.2831855>; + + apple,core-leak-coef = <1000.0>; + apple,sram-leak-coef = <45.0>; }; agx_mbox: mbox@206408000 { From 38014470697650221147d04b3b53f03b1b9d8e99 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Wed, 9 Nov 2022 10:44:43 +0900 Subject: [PATCH 0052/1020] arm64: dts: apple: Add no-map to GPU reserved-memory nodes Signed-off-by: Asahi Lina --- arch/arm64/boot/dts/apple/t600x-common.dtsi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-common.dtsi b/arch/arm64/boot/dts/apple/t600x-common.dtsi index c76ab2fc8f96dd..279ba91d8abacd 100644 --- a/arch/arm64/boot/dts/apple/t600x-common.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-common.dtsi @@ -440,14 +440,17 @@ uat_handoff: uat-handoff { reg = <0 0 0 0>; + no-map; }; uat_pagetables: uat-pagetables { reg = <0 0 0 0>; + no-map; }; uat_ttbs: uat-ttbs { reg = <0 0 0 0>; + no-map; }; }; }; From 127154a419bf499b9fe741bbdf9bde2881c85bb5 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Fri, 25 Nov 2022 23:06:59 +0900 Subject: [PATCH 0053/1020] arm64: dts: apple: Add GPU nodes to T8112 Signed-off-by: Asahi Lina --- arch/arm64/boot/dts/apple/t8112.dtsi | 141 +++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index ec99763b11cc35..019d9f42250e45 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -20,6 +20,10 @@ #address-cells = <2>; #size-cells = <2>; + aliases { + gpu = &gpu; + }; + cpus { #address-cells = <2>; #size-cells = <0>; @@ -321,6 +325,60 @@ #endif }; + gpu_opp: opp-table-gpu { + compatible = "operating-points-v2"; + + /* + * NOTE: The voltage and power values are device-specific and + * must be filled in by the bootloader. + */ + opp00 { + opp-hz = /bits/ 64 <0>; + opp-microvolt = <400000>; + opp-microwatt = <0>; + }; + opp01 { + opp-hz = /bits/ 64 <444000000>; + opp-microvolt = <603000>; + opp-microwatt = <4295000>; + }; + opp02 { + opp-hz = /bits/ 64 <612000000>; + opp-microvolt = <675000>; + opp-microwatt = <6251000>; + }; + opp03 { + opp-hz = /bits/ 64 <808000000>; + opp-microvolt = <710000>; + opp-microwatt = <8625000>; + }; + opp04 { + opp-hz = /bits/ 64 <968000000>; + opp-microvolt = <775000>; + opp-microwatt = <11948000>; + }; + opp05 { + opp-hz = /bits/ 64 <1110000000>; + opp-microvolt = <820000>; + opp-microwatt = <15071000>; + }; + opp06 { + opp-hz = /bits/ 64 <1236000000>; + opp-microvolt = <875000>; + opp-microwatt = <18891000>; + }; + opp07 { + opp-hz = /bits/ 64 <1338000000>; + opp-microvolt = <915000>; + opp-microwatt = <21960000>; + }; + opp08 { + opp-hz = /bits/ 64 <1398000000>; + opp-microvolt = <950000>; + opp-microwatt = <22800000>; + }; + }; + timer { compatible = "arm,armv8-timer"; interrupt-parent = <&aic>; @@ -368,6 +426,27 @@ clock-output-names = "clk_disp0"; }; + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + uat_handoff: uat-handoff { + reg = <0x0 0 0 0>; + no-map; + }; + + uat_pagetables: uat-pagetables { + reg = <0x0 0 0 0>; + no-map; + }; + + uat_ttbs: uat-ttbs { + reg = <0x0 0 0 0>; + no-map; + }; + }; + soc { compatible = "simple-bus"; #address-cells = <2>; @@ -376,6 +455,68 @@ ranges; nonposted-mmio; + gpu: gpu@206400000 { + compatible = "apple,agx-t8112", "apple,agx-g14g"; + reg = <0x2 0x6400000 0 0x40000>, + <0x2 0x4000000 0 0x1000000>; + reg-names = "asc", "sgx"; + interrupt-parent = <&aic>; + interrupts = , + , + , + , + ; + mboxes = <&agx_mbox>; + power-domains = <&ps_gfx>; + memory-region = <&uat_ttbs>, <&uat_pagetables>, <&uat_handoff>; + memory-region-names = "ttbs", "pagetables", "handoff"; + + apple,firmware-version = <12 4 0>; + apple,firmware-compat = <12 4 0>; + + operating-points-v2 = <&gpu_opp>; + apple,perf-base-pstate = <1>; + apple,min-sram-microvolt = <780000>; + apple,avg-power-filter-tc-ms = <300>; + apple,avg-power-ki-only = <9.375>; + apple,avg-power-kp = <3.22>; + apple,avg-power-min-duty-cycle = <40>; + apple,avg-power-target-filter-tc = <1>; + apple,fast-die0-integral-gain = <200.0>; + apple,fast-die0-proportional-gain = <5.0>; + apple,perf-boost-ce-step = <50>; + apple,perf-boost-min-util = <90>; + apple,perf-filter-drop-threshold = <0>; + apple,perf-filter-time-constant = <5>; + apple,perf-filter-time-constant2 = <200>; + apple,perf-integral-gain = <5.94>; + apple,perf-integral-gain2 = <5.94>; + apple,perf-integral-min-clamp = <0>; + apple,perf-proportional-gain = <14.85>; + apple,perf-proportional-gain2 = <14.85>; + apple,perf-tgt-utilization = <85>; + apple,power-sample-period = <8>; + apple,ppm-filter-time-constant-ms = <34>; + apple,ppm-ki = <205.0>; + apple,ppm-kp = <0.75>; + apple,pwr-min-duty-cycle = <40>; + apple,core-leak-coef = <1920.0>; + apple,sram-leak-coef = <74.0>; + }; + + agx_mbox: mbox@206408000 { + compatible = "apple,t8103-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x2 0x6408000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + }; + cpufreq_e: cpufreq@210e20000 { compatible = "apple,t8112-cluster-cpufreq", "apple,cluster-cpufreq"; reg = <0x2 0x10e20000 0 0x1000>; From 18f776c191500177471cdb024c3ddbe06c55dc77 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Fri, 25 Nov 2022 23:07:22 +0900 Subject: [PATCH 0054/1020] arm64: dts: apple: Add GPU firmware versions to t8113/t600x Signed-off-by: Asahi Lina --- arch/arm64/boot/dts/apple/t600x-die0.dtsi | 3 +++ arch/arm64/boot/dts/apple/t8103.dtsi | 3 +++ 2 files changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index a313f8e3058645..65df566d4baf1d 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -442,6 +442,9 @@ memory-region = <&uat_ttbs>, <&uat_pagetables>, <&uat_handoff>; memory-region-names = "ttbs", "pagetables", "handoff"; + apple,firmware-version = <12 3 0>; + apple,firmware-compat = <12 3 0>; + operating-points-v2 = <&gpu_opp>; apple,perf-base-pstate = <1>; apple,min-sram-microvolt = <790000>; diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index c77ae26879090f..4abc94bdd2888c 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -446,6 +446,9 @@ memory-region = <&uat_ttbs>, <&uat_pagetables>, <&uat_handoff>; memory-region-names = "ttbs", "pagetables", "handoff"; + apple,firmware-version = <12 3 0>; + apple,firmware-compat = <12 3 0>; + operating-points-v2 = <&gpu_opp>; apple,perf-base-pstate = <1>; apple,min-sram-microvolt = <850000>; From fca3237f156c4d6b9affe3cf7dea9d7012d39e85 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 30 Sep 2023 21:38:03 +0200 Subject: [PATCH 0055/1020] arch: arm64: apple: Add spi1-nvram.dtsi All devices use an identical SPI nor/nvram setup so move that to dtsi file they all can include. Since the nvram partition size depends on the iboot version disable the nvram partition and have m1n1 enable it after it has filled the correct values from the ADT. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/spi1-nvram.dtsi | 32 +++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 arch/arm64/boot/dts/apple/spi1-nvram.dtsi diff --git a/arch/arm64/boot/dts/apple/spi1-nvram.dtsi b/arch/arm64/boot/dts/apple/spi1-nvram.dtsi new file mode 100644 index 00000000000000..36bfef5cf81d43 --- /dev/null +++ b/arch/arm64/boot/dts/apple/spi1-nvram.dtsi @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * Common config for Apple's nvram using a SPI nor flash. This is common on all + * M1 and M2 devices. identically set up identically on all M1 and M2 devicesspi1, spinor and nvram config identical on all devices + * + * Copyright The Asahi Linux Contributors + */ + +&spi1 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x0>; + spi-max-frequency = <25000000>; + #address-cells = <1>; + #size-cells = <1>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + nvram: partition@700000 { + label = "nvram"; + /* To be filled by the loader */ + reg = <0x0 0x0>; + status = "disabled"; + }; + }; + }; +}; From 62705d72427f4348848674b2402fe3d3b6695eec Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Sat, 29 Oct 2022 23:18:27 +0300 Subject: [PATCH 0056/1020] arm64: dts: apple: t600x: Add the NVRAM bindings Add the SPI controller and the nvram partition bindings for M1 Pro/Max/Ultra Macs Signed-off-by: Sasha Finkelstein --- arch/arm64/boot/dts/apple/t600x-die0.dtsi | 1 - arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi | 15 +++------------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index 65df566d4baf1d..4a5a275b706f5a 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -355,7 +355,6 @@ pinctrl-0 = <&spi1_pins>; pinctrl-names = "default"; power-domains = <&ps_spi1>; - status = "disabled"; }; spi3: spi@39b10c000 { diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index 39fdc7d3360f42..58d017253f4aa2 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -21,6 +21,7 @@ dcp = &dcp; disp0 = &display; disp0_piodma = &disp0_piodma; + nvram = &nvram; serial0 = &serial0; wifi0 = &wifi0; }; @@ -262,18 +263,6 @@ clock-frequency = <1068000000>; }; -&spi1 { - status = "disabled"; - - flash@0 { - compatible = "jedec,spi-nor"; - reg = <0x0>; - spi-max-frequency = <25000000>; - #address-cells = <1>; - #size-cells = <1>; - }; -}; - &spi3 { status = "okay"; @@ -436,3 +425,5 @@ }; }; }; + +#include "spi1-nvram.dtsi" From cac666ed5f7ce7c3131998fb6d7ec933b4ff4ae0 Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Sat, 29 Oct 2022 23:17:40 +0300 Subject: [PATCH 0057/1020] arm64: dts: apple: t8112: Add the NVRAM bindings Add the SPI controller and the nvram partition bindings for M2 Macs Signed-off-by: Sasha Finkelstein --- arch/arm64/boot/dts/apple/t8112-jxxx.dtsi | 2 ++ arch/arm64/boot/dts/apple/t8112.dtsi | 28 +++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi b/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi index 7edabecc862e46..16daa5c5cde57d 100644 --- a/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi +++ b/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi @@ -171,3 +171,5 @@ &nco_clkref { clock-frequency = <900000000>; }; + +#include "spi1-nvram.dtsi" diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index 019d9f42250e45..754d06f868b4b8 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -447,6 +447,13 @@ }; }; + clk_200m: clock-200m { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <200000000>; + clock-output-names = "clk_200m"; + }; + soc { compatible = "simple-bus"; #address-cells = <2>; @@ -682,6 +689,20 @@ status = "disabled"; }; + spi1: spi@235104000 { + compatible = "apple,t8112-spi", "apple,spi"; + reg = <0x2 0x35104000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + clocks = <&clk_200m>; + pinctrl-0 = <&spi1_pins>; + pinctrl-names = "default"; + power-domains = <&ps_spi1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + spi3: spi@23510c000 { compatible = "apple,t8112-spi", "apple,spi"; reg = <0x2 0x3510c000 0x0 0x4000>; @@ -861,6 +882,13 @@ ; }; + spi1_pins: spi1-pins { + pinmux = , + , + , + ; + }; + spi3_pins: spi3-pins { pinmux = , , From 06c461a914e6a09294f0f586e159a019b9cce83a Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Sat, 29 Oct 2022 23:14:39 +0300 Subject: [PATCH 0058/1020] arm64: dts: apple: t8103: Add the NVRAM bindings Add the SPI controller and the nvram partition bindings for M1 Macs Signed-off-by: Sasha Finkelstein --- arch/arm64/boot/dts/apple/t8103-jxxx.dtsi | 2 ++ arch/arm64/boot/dts/apple/t8103.dtsi | 28 +++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi index 1ad2a57698afb9..6b6332b5652732 100644 --- a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi @@ -183,3 +183,5 @@ &nco_clkref { clock-frequency = <900000000>; }; + +#include "spi1-nvram.dtsi" diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index 4abc94bdd2888c..3d547b5cf4465a 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -383,6 +383,13 @@ clock-output-names = "clk_120m"; }; + clk_200m: clock-200m { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <200000000>; + clock-output-names = "clk_200m"; + }; + /* Pixel clock? frequency in Hz (compare: 4K@60 VGA clock 533.250 MHz) */ clk_disp0: clock-disp0 { compatible = "fixed-clock"; @@ -657,6 +664,20 @@ status = "disabled"; }; + spi1: spi@235104000 { + compatible = "apple,t8103-spi", "apple,spi"; + reg = <0x2 0x35104000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + clocks = <&clk_200m>; + pinctrl-0 = <&spi1_pins>; + pinctrl-names = "default"; + power-domains = <&ps_spi1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + spi3: spi@23510c000 { compatible = "apple,t8103-spi", "apple,spi"; reg = <0x2 0x3510c000 0x0 0x4000>; @@ -835,6 +856,13 @@ ; }; + spi1_pins: spi1-pins { + pinmux = , + , + , + ; + }; + spi3_pins: spi3-pins { pinmux = , , From fe34c5d6e6ea633cd7c916bb4e6dcd6df6219864 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 13 Dec 2022 00:17:35 +0900 Subject: [PATCH 0059/1020] arm64: dts: apple: t600x: Add DCP power domain to missing devices Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t600x-die0.dtsi | 2 ++ arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi | 1 + arch/arm64/boot/dts/apple/t600x-j375.dtsi | 1 + arch/arm64/boot/dts/apple/t600x-pmgr.dtsi | 2 -- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index 4a5a275b706f5a..f34c5f5d315a8a 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -179,6 +179,7 @@ interrupt-parent = <&aic>; interrupts = ; status = "disabled"; + power-domains = <&ps_disp0_cpu0>; }; dcp_dart: iommu@38b30c000 { @@ -187,6 +188,7 @@ #iommu-cells = <1>; interrupt-parent = <&aic>; interrupts = ; + power-domains = <&ps_disp0_cpu0>; }; dcp_mbox: mbox@38bc08000 { diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index 58d017253f4aa2..04c418aead3a4e 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -38,6 +38,7 @@ reg = <0 0 0 0>; /* To be filled by loader */ /* Format properties will be added by loader */ status = "disabled"; + power-domains = <&ps_disp0_cpu0>; }; }; diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi index dad19cad7a99a0..50dd882a7ad2e8 100644 --- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi @@ -35,6 +35,7 @@ reg = <0 0 0 0>; /* To be filled by loader */ /* Format properties will be added by loader */ status = "disabled"; + power-domains = <&ps_disp0_cpu0>; }; }; diff --git a/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi b/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi index 0bd44753b76a0c..00b317c2355b8c 100644 --- a/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi @@ -1293,7 +1293,6 @@ #reset-cells = <0>; label = DIE_LABEL(disp0_fe); power-domains = <&DIE_NODE(ps_afnc2_lw0)>; - apple,always-on; /* TODO: figure out if we can enable PM here */ }; DIE_NODE(ps_disp0_cpu0): power-controller@350 { @@ -1303,7 +1302,6 @@ #reset-cells = <0>; label = DIE_LABEL(disp0_cpu0); power-domains = <&DIE_NODE(ps_disp0_fe)>; - apple,always-on; /* TODO: figure out if we can enable PM here */ apple,min-state = <4>; }; From 8f71764062088e1e023a6b21fd6482c850f2bb3e Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 13 Dec 2022 00:17:35 +0900 Subject: [PATCH 0060/1020] arm64: dts: apple: t8103: Add DCP power domain to missing devices Removes the "apple,always-on" property from ps_disp0_fe/cpu0. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8103-jxxx.dtsi | 1 + arch/arm64/boot/dts/apple/t8103-pmgr.dtsi | 2 -- arch/arm64/boot/dts/apple/t8103.dtsi | 2 ++ 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi index 6b6332b5652732..2b4136d6f77ee4 100644 --- a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi @@ -32,6 +32,7 @@ framebuffer0: framebuffer@0 { compatible = "apple,simple-framebuffer", "simple-framebuffer"; reg = <0 0 0 0>; /* To be filled by loader */ + power-domains = <&ps_disp0_cpu0>; /* Format properties will be added by loader */ status = "disabled"; }; diff --git a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi index 0966322c5c8e3f..ea0ee0224b1cb1 100644 --- a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi @@ -646,7 +646,6 @@ #reset-cells = <0>; label = "disp0_fe"; power-domains = <&ps_rmx>, <&ps_pmp>; - apple,always-on; /* TODO: figure out if we can enable PM here */ }; ps_dispext_fe: power-controller@368 { @@ -1001,7 +1000,6 @@ #reset-cells = <0>; label = "disp0_cpu0"; power-domains = <&ps_disp0_fe>; - apple,always-on; /* TODO: figure out if we can enable PM here */ apple,min-state = <4>; }; }; diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index 3d547b5cf4465a..d1cef7837e1ff9 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -519,6 +519,7 @@ #iommu-cells = <1>; interrupt-parent = <&aic>; interrupts = ; + power-domains = <&ps_disp0_cpu0>; status = "disabled"; }; @@ -528,6 +529,7 @@ #iommu-cells = <1>; interrupt-parent = <&aic>; interrupts = ; + power-domains = <&ps_disp0_cpu0>; }; dcp_mbox: mbox@231c08000 { From 3117ffc0491c8f41376001c8a60fccbd0de0f980 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 13 Dec 2022 00:17:35 +0900 Subject: [PATCH 0061/1020] arm64: dts: apple: t8112: Add DCP power domain to missing devices Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112-jxxx.dtsi | 1 + arch/arm64/boot/dts/apple/t8112-pmgr.dtsi | 2 -- arch/arm64/boot/dts/apple/t8112.dtsi | 2 ++ 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi b/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi index 16daa5c5cde57d..5fec625bf5c2a6 100644 --- a/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi +++ b/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi @@ -30,6 +30,7 @@ framebuffer0: framebuffer@0 { compatible = "apple,simple-framebuffer", "simple-framebuffer"; reg = <0 0 0 0>; /* To be filled by loader */ + power-domains = <&ps_disp0_cpu0>; /* Format properties will be added by loader */ status = "disabled"; }; diff --git a/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi index 118694dd9b5f06..d1711155f84686 100644 --- a/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi @@ -663,7 +663,6 @@ #reset-cells = <0>; label = "disp0_sys"; power-domains = <&ps_rmx1>; - apple,always-on; /* TODO: figure out if we can enable PM here */ }; ps_disp0_fe: power-controller@378 { @@ -673,7 +672,6 @@ #reset-cells = <0>; label = "disp0_fe"; power-domains = <&ps_disp0_sys>, <&ps_pmp>; - apple,always-on; /* TODO: figure out if we can enable PM here */ }; ps_dispext_sys: power-controller@380 { diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index 754d06f868b4b8..199214ce73db73 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -542,6 +542,7 @@ #iommu-cells = <1>; interrupt-parent = <&aic>; interrupts = ; + power-domains = <&ps_disp0_cpu0>; status = "disabled"; }; @@ -551,6 +552,7 @@ #iommu-cells = <1>; interrupt-parent = <&aic>; interrupts = ; + power-domains = <&ps_disp0_cpu0>; }; dcp_mbox: mbox@231c08000 { From 261778dc8b8dd53cf43147df63a8b02d62c30908 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Fri, 30 Dec 2022 01:15:57 +0100 Subject: [PATCH 0062/1020] arm64: dts: apple: t8103: Add missing ps_pmp dependency to ps_gfx AGX' ASC crashes shortly after ps_pmp is powered down due to dcp runtime PM suspend. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8103-pmgr.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi index ea0ee0224b1cb1..724e7fd559e7a1 100644 --- a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi @@ -733,6 +733,7 @@ #power-domain-cells = <0>; #reset-cells = <0>; label = "gfx"; + power-domains = <&ps_pmp>; }; ps_dcs4: power-controller@320 { From 554124effa228f5cd860d900f898fabb1334b075 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 9 Jan 2023 00:03:49 +0100 Subject: [PATCH 0063/1020] arm64: dts: apple: t600x: Add "ps_disp0_cpu0" as resets for dcp Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t600x-die0.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index f34c5f5d315a8a..6421f085465060 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -219,6 +219,7 @@ <0x3 0x8b800000 0x0 0x800000>; apple,bw-scratch = <&pmgr_dcp 0 4 0x988>; power-domains = <&ps_disp0_cpu0>; + resets = <&ps_disp0_cpu0>; clocks = <&clk_disp0>; apple,asc-dram-mask = <0x1f0 0x00000000>; phandle = <&dcp>; From 665f0fd1fb0746f128ed73bd2a393d35b8c55f3c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 9 Jan 2023 00:05:06 +0100 Subject: [PATCH 0064/1020] arm64: dts: apple: t8103: Add "ps_disp0_cpu0" as resets for dcp Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8103.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index d1cef7837e1ff9..65e532c8198841 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -564,6 +564,7 @@ apple,bw-scratch = <&pmgr_dcp 0 5 0x14>; apple,bw-doorbell = <&pmgr_dcp 1 6>; power-domains = <&ps_disp0_cpu0>; + resets = <&ps_disp0_cpu0>; clocks = <&clk_disp0>; apple,asc-dram-mask = <0xf 0x00000000>; phandle = <&dcp>; From c6bcfe44abcf47341e657347b5541dd0caa6e168 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 9 Jan 2023 00:05:30 +0100 Subject: [PATCH 0065/1020] arm64: dts: apple: t8112: Add "ps_disp0_cpu0" as resets for dcp Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index 199214ce73db73..9ef423b5fde4c5 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -585,6 +585,7 @@ <0x2 0x31800000 0x0 0x800000>; apple,bw-scratch = <&pmgr_dcp 0 4 0x5d8>; power-domains = <&ps_disp0_cpu0>; + resets = <&ps_disp0_cpu0>; clocks = <&clk_disp0>; apple,asc-dram-mask = <0x0 0x0>; phandle = <&dcp>; From 6eefe4c0bd70ebe906ea561514ce1379e5799327 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 11 Mar 2023 21:45:11 +0900 Subject: [PATCH 0066/1020] arm64: dts: apple: j314/j316: Disable ATC3_USB_AON power domain These power domains are normally always on for real Thunderbolt ports (or else dwc3 breaks), but not for the port that's hardwired to the HDMI bridge. Fixes some dmesg spam: apple-pmgr-pwrstate 292280000.power-management:power-controller@a0: always-on domain atc3_usb_aon is not on at boot Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index 04c418aead3a4e..7c5bbbed9a183d 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -385,6 +385,10 @@ status = "disabled"; }; +&ps_atc3_usb_aon { + /delete-property/ apple,always-on; +}; + / { sound: sound { compatible = "apple,j314-macaudio", "apple,macaudio"; From 7ccaf80bde4458a699116d3292cc9ede9b9a7351 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 19 Mar 2023 19:12:52 +0900 Subject: [PATCH 0067/1020] arm64: dts: apple: Add keyboard alias & layout props for t8112 laptops Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8112-j413.dts | 5 ++++- arch/arm64/boot/dts/apple/t8112-j493.dts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8112-j413.dts b/arch/arm64/boot/dts/apple/t8112-j413.dts index de9c98c6fac0a2..ceb93965cb4d44 100644 --- a/arch/arm64/boot/dts/apple/t8112-j413.dts +++ b/arch/arm64/boot/dts/apple/t8112-j413.dts @@ -20,6 +20,7 @@ aliases { bluetooth0 = &bluetooth0; wifi0 = &wifi0; + keyboard = &keyboard; }; led-controller { @@ -206,7 +207,9 @@ firmware-name = "apple/tpmtfw-j413.bin"; }; - keyboard { + keyboard: keyboard { + hid-country-code = <0>; + apple,keyboard-layout-id = <0>; }; stm { diff --git a/arch/arm64/boot/dts/apple/t8112-j493.dts b/arch/arm64/boot/dts/apple/t8112-j493.dts index 42705711c6928d..d34acd0ee2f203 100644 --- a/arch/arm64/boot/dts/apple/t8112-j493.dts +++ b/arch/arm64/boot/dts/apple/t8112-j493.dts @@ -20,6 +20,7 @@ aliases { bluetooth0 = &bluetooth0; wifi0 = &wifi0; + keyboard = &keyboard; }; led-controller { @@ -194,7 +195,9 @@ firmware-name = "apple/tpmtfw-j493.bin"; }; - keyboard { + keyboard: keyboard { + hid-country-code = <0>; + apple,keyboard-layout-id = <0>; }; stm { From 71a6f78547d8bc92767089cb9ddc54e8f3bf372e Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 9 Apr 2023 23:49:01 +0900 Subject: [PATCH 0068/1020] arm64: dts: apple: Fix t600x mca IRQs Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t600x-die0.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index 6421f085465060..d5f83226dda558 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -418,10 +418,10 @@ "tx2a", "rx2a", "tx2b", "rx2b", "tx3a", "rx3a", "tx3b", "rx3b"; interrupt-parent = <&aic>; - interrupts = , + interrupts = , + , , - , - ; + ; power-domains = <&ps_audio_p>, <&ps_mca0>, <&ps_mca1>, <&ps_mca2>, <&ps_mca3>; resets = <&ps_audio_p>; From 2f825871616a85decee6aa9fa964f1f07ca5426b Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 9 Apr 2023 23:48:38 +0900 Subject: [PATCH 0069/1020] arm64: dts: apple: Add initial t602x device trees Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/Makefile | 5 + arch/arm64/boot/dts/apple/t600x-die0.dtsi | 2 +- .../arm64/boot/dts/apple/t600x-j314-j316.dtsi | 7 + arch/arm64/boot/dts/apple/t600x-j375.dtsi | 4 + arch/arm64/boot/dts/apple/t6020-j414s.dts | 38 + arch/arm64/boot/dts/apple/t6020-j416s.dts | 38 + arch/arm64/boot/dts/apple/t6020-j474s.dts | 98 + arch/arm64/boot/dts/apple/t6020.dtsi | 24 + arch/arm64/boot/dts/apple/t6021-j414c.dts | 38 + arch/arm64/boot/dts/apple/t6021-j416c.dts | 54 + arch/arm64/boot/dts/apple/t6021.dtsi | 91 + arch/arm64/boot/dts/apple/t602x-common.dtsi | 489 ++++ arch/arm64/boot/dts/apple/t602x-die0.dtsi | 702 +++++ arch/arm64/boot/dts/apple/t602x-dieX.dtsi | 344 +++ .../arm64/boot/dts/apple/t602x-gpio-pins.dtsi | 81 + .../arm64/boot/dts/apple/t602x-j414-j416.dtsi | 88 + arch/arm64/boot/dts/apple/t602x-nvme.dtsi | 42 + arch/arm64/boot/dts/apple/t602x-pmgr.dtsi | 2262 +++++++++++++++++ 18 files changed, 4406 insertions(+), 1 deletion(-) create mode 100644 arch/arm64/boot/dts/apple/t6020-j414s.dts create mode 100644 arch/arm64/boot/dts/apple/t6020-j416s.dts create mode 100644 arch/arm64/boot/dts/apple/t6020-j474s.dts create mode 100644 arch/arm64/boot/dts/apple/t6020.dtsi create mode 100644 arch/arm64/boot/dts/apple/t6021-j414c.dts create mode 100644 arch/arm64/boot/dts/apple/t6021-j416c.dts create mode 100644 arch/arm64/boot/dts/apple/t6021.dtsi create mode 100644 arch/arm64/boot/dts/apple/t602x-common.dtsi create mode 100644 arch/arm64/boot/dts/apple/t602x-die0.dtsi create mode 100644 arch/arm64/boot/dts/apple/t602x-dieX.dtsi create mode 100644 arch/arm64/boot/dts/apple/t602x-gpio-pins.dtsi create mode 100644 arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi create mode 100644 arch/arm64/boot/dts/apple/t602x-nvme.dtsi create mode 100644 arch/arm64/boot/dts/apple/t602x-pmgr.dtsi diff --git a/arch/arm64/boot/dts/apple/Makefile b/arch/arm64/boot/dts/apple/Makefile index aec5e29cdfb737..e3e62c672d53b7 100644 --- a/arch/arm64/boot/dts/apple/Makefile +++ b/arch/arm64/boot/dts/apple/Makefile @@ -13,3 +13,8 @@ dtb-$(CONFIG_ARCH_APPLE) += t6002-j375d.dtb dtb-$(CONFIG_ARCH_APPLE) += t8112-j413.dtb dtb-$(CONFIG_ARCH_APPLE) += t8112-j473.dtb dtb-$(CONFIG_ARCH_APPLE) += t8112-j493.dtb +dtb-$(CONFIG_ARCH_APPLE) += t6020-j414s.dtb +dtb-$(CONFIG_ARCH_APPLE) += t6021-j414c.dtb +dtb-$(CONFIG_ARCH_APPLE) += t6020-j416s.dtb +dtb-$(CONFIG_ARCH_APPLE) += t6021-j416c.dtb +dtb-$(CONFIG_ARCH_APPLE) += t6020-j474s.dtb diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index d5f83226dda558..3c991c9bdc529a 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -221,7 +221,7 @@ power-domains = <&ps_disp0_cpu0>; resets = <&ps_disp0_cpu0>; clocks = <&clk_disp0>; - apple,asc-dram-mask = <0x1f0 0x00000000>; + apple,asc-dram-mask = <0>; phandle = <&dcp>; disp0_piodma: piodma { diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index 7c5bbbed9a183d..9475dbd2f2047e 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -264,6 +264,7 @@ clock-frequency = <1068000000>; }; +#ifndef NO_SPI_TRACKPAD &spi3 { status = "okay"; @@ -284,6 +285,7 @@ interrupts-extended = <&pinctrl_nub 6 IRQ_TYPE_LEVEL_LOW>; }; }; +#endif /* PCIe devices */ &port00 { @@ -310,6 +312,7 @@ /* SD card reader */ bus-range = <2 2>; pwren-gpios = <&smc_gpio 26 GPIO_ACTIVE_HIGH>; + status = "okay"; sdhci0: mmc@0,0 { compatible = "pci17a0,9755"; reg = <0x20000 0x0 0x0 0x0 0x0>; @@ -322,6 +325,10 @@ status = "okay"; }; +&pcie0_dart_1 { + status = "okay"; +}; + /* USB controllers */ &dwc3_0 { port { diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi index 50dd882a7ad2e8..0564c8cae687ab 100644 --- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi @@ -344,6 +344,7 @@ }; }; +#ifndef NO_PCIE_SDHC &port01 { /* SD card reader */ bus-range = <2 2>; @@ -355,6 +356,7 @@ wp-inverted; }; }; +#endif &port02 { /* 10 Gbit Ethernet */ @@ -383,6 +385,7 @@ status = "okay"; }; +#ifndef NO_GPU &gpu { apple,avg-power-ki-only = <0.6375>; apple,avg-power-kp = <0.58>; @@ -391,3 +394,4 @@ apple,ppm-ki = <5.8>; apple,ppm-kp = <0.355>; }; +#endif diff --git a/arch/arm64/boot/dts/apple/t6020-j414s.dts b/arch/arm64/boot/dts/apple/t6020-j414s.dts new file mode 100644 index 00000000000000..18cc67a3076def --- /dev/null +++ b/arch/arm64/boot/dts/apple/t6020-j414s.dts @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * MacBook Pro (14-inch, M2 Pro, 2023) + * + * target-type: J414s + * + * Copyright The Asahi Linux Contributors + */ + +/dts-v1/; + +#include "t6020.dtsi" +#include "t602x-j414-j416.dtsi" + +/ { + compatible = "apple,j414s", "apple,t6020", "apple,arm-platform"; + model = "Apple MacBook Pro (14-inch, M2 Pro, 2023)"; +}; + +&wifi0 { + brcm,board-type = "apple,tokara"; +}; + +&bluetooth0 { + brcm,board-type = "apple,tokara"; +}; + +&panel { + compatible = "apple,panel-j414", "apple,panel-mini-led", "apple,panel"; + width-mm = <302>; + height-mm = <196>; + adj-height-mm = <189>; +}; + +&sound { + compatible = "apple,j414-macaudio", "apple,j314-macaudio", "apple,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 new file mode 100644 index 00000000000000..b9e0973ba37c30 --- /dev/null +++ b/arch/arm64/boot/dts/apple/t6020-j416s.dts @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * MacBook Pro (16-inch, M2 Pro, 2023) + * + * target-type: J416s + * + * Copyright The Asahi Linux Contributors + */ + +/dts-v1/; + +#include "t6020.dtsi" +#include "t602x-j414-j416.dtsi" + +/ { + compatible = "apple,j416s", "apple,t6020", "apple,arm-platform"; + model = "Apple MacBook Pro (16-inch, M2 Pro, 2023)"; +}; + +&wifi0 { + brcm,board-type = "apple,amami"; +}; + +&bluetooth0 { + brcm,board-type = "apple,amami"; +}; + +&panel { + compatible = "apple,panel-j416", "apple,panel-mini-led", "apple,panel"; + width-mm = <346>; + height-mm = <223>; + adj-height-mm = <216>; +}; + +&sound { + compatible = "apple,j416-macaudio", "apple,j316-macaudio", "apple,macaudio"; + model = "MacBook Pro J416"; +}; diff --git a/arch/arm64/boot/dts/apple/t6020-j474s.dts b/arch/arm64/boot/dts/apple/t6020-j474s.dts new file mode 100644 index 00000000000000..9b61a7bb9d6ce4 --- /dev/null +++ b/arch/arm64/boot/dts/apple/t6020-j474s.dts @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * Mac Mini (M2 Pro, 2023) + * + * target-type: J474s + * + * Copyright The Asahi Linux Contributors + */ + +/dts-v1/; + +#include "t6020.dtsi" + +/* + * These model is very similar to the previous generation Mac Studio, other than + * the GPIO indices. + */ + +#define NO_PCIE_SDHC +#define NO_GPU +#include "t600x-j375.dtsi" + +/ { + compatible = "apple,j474s", "apple,t6020", "apple,arm-platform"; + model = "Apple Mac Mini (M2 Pro, 2023)"; + + aliases { + ethernet0 = ðernet0; + }; +}; + +&hpm0 { + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; +}; + +&hpm1 { + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; +}; + +&hpm2 { + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; +}; + +&hpm3 { + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; +}; + +&wifi0 { + compatible = "pci14e4,4434"; + brcm,board-type = "apple,tasmania"; +}; + +&bluetooth0 { + compatible = "pci14e4,5f72"; + brcm,board-type = "apple,tasmania"; +}; + +/* PCIe devices */ +&port00 { + pwren-gpios = <&smc_gpio 13 GPIO_ACTIVE_HIGH>; +}; + +&port02 { + /* 10 Gbit Ethernet */ + bus-range = <3 3>; + status = "okay"; + ethernet0: ethernet@0,0 { + reg = <0x30000 0x0 0x0 0x0 0x0>; + /* To be filled by the loader */ + local-mac-address = [00 10 18 00 00 00]; + }; +}; + +&port03 { + /* USB xHCI */ + pwren-gpios = <&smc_gpio 19 GPIO_ACTIVE_HIGH>; +}; + + +&speaker { + shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>; + interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>; +}; + +&jack_codec { + reset-gpios = <&pinctrl_nub 8 GPIO_ACTIVE_HIGH>; + interrupts-extended = <&pinctrl_ap 59 IRQ_TYPE_LEVEL_LOW>; +}; + +&sound { + compatible = "apple,j474-macaudio", "apple,j473-macaudio", "apple,macaudio"; + model = "Mac mini J474"; +}; + +&gpu { + /* Apple does not do this, but they probably should */ + apple,perf-base-pstate = <3>; +}; diff --git a/arch/arm64/boot/dts/apple/t6020.dtsi b/arch/arm64/boot/dts/apple/t6020.dtsi new file mode 100644 index 00000000000000..3a864ebd91bb2f --- /dev/null +++ b/arch/arm64/boot/dts/apple/t6020.dtsi @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * Apple T6020 "M2 Pro" SoC + * + * Other names: H14J, "Rhodes Chop" + * + * Copyright The Asahi Linux Contributors + */ + +/* This chip is just a cut down version of t6021, so include it and disable the missing parts */ + +#define GPU_REPEAT(x) + +#include "t6021.dtsi" + +/ { + compatible = "apple,t6020", "apple,arm-platform"; +}; + +/delete-node/ &pmgr_south; + +&gpu { + compatible = "apple,agx-t6020", "apple,agx-g14x"; +}; diff --git a/arch/arm64/boot/dts/apple/t6021-j414c.dts b/arch/arm64/boot/dts/apple/t6021-j414c.dts new file mode 100644 index 00000000000000..b173caf0df0fce --- /dev/null +++ b/arch/arm64/boot/dts/apple/t6021-j414c.dts @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * MacBook Pro (14-inch, M2 Max, 2023) + * + * target-type: J414c + * + * Copyright The Asahi Linux Contributors + */ + +/dts-v1/; + +#include "t6021.dtsi" +#include "t602x-j414-j416.dtsi" + +/ { + compatible = "apple,j414c", "apple,t6021", "apple,arm-platform"; + model = "Apple MacBook Pro (14-inch, M2 Max, 2023)"; +}; + +&wifi0 { + brcm,board-type = "apple,tokara"; +}; + +&bluetooth0 { + brcm,board-type = "apple,tokara"; +}; + +&panel { + compatible = "apple,panel-j414", "apple,panel-mini-led", "apple,panel"; + width-mm = <302>; + height-mm = <196>; + adj-height-mm = <189>; +}; + +&sound { + compatible = "apple,j414-macaudio", "apple,j314-macaudio", "apple,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 new file mode 100644 index 00000000000000..36a57890c25d72 --- /dev/null +++ b/arch/arm64/boot/dts/apple/t6021-j416c.dts @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * MacBook Pro (16-inch, M2 Max, 2022) + * + * target-type: J416c + * + * Copyright The Asahi Linux Contributors + */ + +/dts-v1/; + +#include "t6021.dtsi" +#include "t602x-j414-j416.dtsi" + +/ { + compatible = "apple,j416c", "apple,t6021", "apple,arm-platform"; + model = "Apple MacBook Pro (16-inch, M2 Max, 2023)"; +}; + +/* This machine model (only) has two extra boost CPU P-states */ +&avalanche_opp { + opp18 { + opp-hz = /bits/ 64 <3528000000>; + opp-level = <18>; + clock-latency-ns = <67000>; + turbo-mode; + }; + opp19 { + opp-hz = /bits/ 64 <3696000000>; + opp-level = <19>; + clock-latency-ns = <67000>; + turbo-mode; + }; +}; + +&wifi0 { + brcm,board-type = "apple,amami"; +}; + +&bluetooth0 { + brcm,board-type = "apple,amami"; +}; + +&panel { + compatible = "apple,panel-j416", "apple,panel-mini-led", "apple,panel"; + width-mm = <346>; + height-mm = <223>; + adj-height-mm = <216>; +}; + +&sound { + compatible = "apple,j416-macaudio", "apple,j316-macaudio", "apple,macaudio"; + model = "MacBook Pro J416"; +}; diff --git a/arch/arm64/boot/dts/apple/t6021.dtsi b/arch/arm64/boot/dts/apple/t6021.dtsi new file mode 100644 index 00000000000000..d907c4753f67dd --- /dev/null +++ b/arch/arm64/boot/dts/apple/t6021.dtsi @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * Apple T6021 "M2 Max" SoC + * + * Other names: H14J, "Rhodes" + * + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include + +#include "multi-die-cpp.h" + +#ifndef GPU_REPEAT +# define GPU_REPEAT(x) +#endif + +#include "t602x-common.dtsi" + +/ { + compatible = "apple,t6001", "apple,arm-platform"; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + uat_handoff: uat-handoff { + reg = <0 0 0 0>; + }; + + uat_pagetables: uat-pagetables { + reg = <0 0 0 0>; + }; + + uat_ttbs: uat-ttbs { + reg = <0 0 0 0>; + }; + }; + + soc { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + + ranges; + nonposted-mmio; + + // filled via templated includes at the end of the file + }; +}; + +#define DIE +#define DIE_NO 0 + +&{/soc} { + #include "t602x-die0.dtsi" + #include "t602x-dieX.dtsi" + #include "t602x-nvme.dtsi" +}; + +#include "t602x-gpio-pins.dtsi" +#include "t602x-pmgr.dtsi" + +#undef DIE +#undef DIE_NO + + +&aic { + affinities { + e-core-pmu-affinity { + apple,fiq-index = ; + cpus = <&cpu_e00 &cpu_e01 &cpu_e02 &cpu_e03>; + }; + + p-core-pmu-affinity { + apple,fiq-index = ; + cpus = <&cpu_p00 &cpu_p01 &cpu_p02 &cpu_p03 + &cpu_p10 &cpu_p11 &cpu_p12 &cpu_p13>; + }; + }; +}; + +&gpu { + compatible = "apple,agx-t6021", "apple,agx-g14x"; +}; diff --git a/arch/arm64/boot/dts/apple/t602x-common.dtsi b/arch/arm64/boot/dts/apple/t602x-common.dtsi new file mode 100644 index 00000000000000..1224e4f12a5347 --- /dev/null +++ b/arch/arm64/boot/dts/apple/t602x-common.dtsi @@ -0,0 +1,489 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * Nodes common to all T602x family SoCs (M2 Pro/Max/Ultra) + * + * Copyright The Asahi Linux Contributors + */ + + / { + #address-cells = <2>; + #size-cells = <2>; + + cpus { + #address-cells = <2>; + #size-cells = <0>; + + cpu-map { + cluster0 { + core0 { + cpu = <&cpu_e00>; + }; + core1 { + cpu = <&cpu_e01>; + }; + core2 { + cpu = <&cpu_e02>; + }; + core3 { + cpu = <&cpu_e03>; + }; + }; + cluster1 { + core0 { + cpu = <&cpu_p00>; + }; + core1 { + cpu = <&cpu_p01>; + }; + core2 { + cpu = <&cpu_p02>; + }; + core3 { + cpu = <&cpu_p03>; + }; + }; + + cluster2 { + core0 { + cpu = <&cpu_p10>; + }; + core1 { + cpu = <&cpu_p11>; + }; + core2 { + cpu = <&cpu_p12>; + }; + core3 { + cpu = <&cpu_p13>; + }; + }; + }; + + cpu_e00: cpu@0 { + compatible = "apple,blizzard"; + device_type = "cpu"; + reg = <0x0 0x0>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* to be filled by loader */ + next-level-cache = <&l2_cache_0>; + i-cache-size = <0x20000>; + d-cache-size = <0x10000>; + operating-points-v2 = <&blizzard_opp>; + capacity-dmips-mhz = <756>; + performance-domains = <&cpufreq_e>; + }; + + cpu_e01: cpu@1 { + compatible = "apple,blizzard"; + device_type = "cpu"; + reg = <0x0 0x1>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* to be filled by loader */ + next-level-cache = <&l2_cache_0>; + i-cache-size = <0x20000>; + d-cache-size = <0x10000>; + operating-points-v2 = <&blizzard_opp>; + capacity-dmips-mhz = <756>; + performance-domains = <&cpufreq_e>; + }; + + cpu_e02: cpu@2 { + compatible = "apple,blizzard"; + device_type = "cpu"; + reg = <0x0 0x2>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* to be filled by loader */ + next-level-cache = <&l2_cache_0>; + i-cache-size = <0x20000>; + d-cache-size = <0x10000>; + operating-points-v2 = <&blizzard_opp>; + capacity-dmips-mhz = <756>; + performance-domains = <&cpufreq_e>; + }; + + cpu_e03: cpu@3 { + compatible = "apple,blizzard"; + device_type = "cpu"; + reg = <0x0 0x3>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* to be filled by loader */ + next-level-cache = <&l2_cache_0>; + i-cache-size = <0x20000>; + d-cache-size = <0x10000>; + operating-points-v2 = <&blizzard_opp>; + capacity-dmips-mhz = <756>; + performance-domains = <&cpufreq_e>; + }; + + cpu_p00: cpu@10100 { + compatible = "apple,avalanche"; + device_type = "cpu"; + reg = <0x0 0x10100>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* To be filled by loader */ + next-level-cache = <&l2_cache_1>; + i-cache-size = <0x30000>; + d-cache-size = <0x20000>; + operating-points-v2 = <&avalanche_opp>; + capacity-dmips-mhz = <1024>; + performance-domains = <&cpufreq_p0>; + }; + + cpu_p01: cpu@10101 { + compatible = "apple,avalanche"; + device_type = "cpu"; + reg = <0x0 0x10101>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* To be filled by loader */ + next-level-cache = <&l2_cache_1>; + i-cache-size = <0x30000>; + d-cache-size = <0x20000>; + operating-points-v2 = <&avalanche_opp>; + capacity-dmips-mhz = <1024>; + performance-domains = <&cpufreq_p0>; + }; + + cpu_p02: cpu@10102 { + compatible = "apple,avalanche"; + device_type = "cpu"; + reg = <0x0 0x10102>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* To be filled by loader */ + next-level-cache = <&l2_cache_1>; + i-cache-size = <0x30000>; + d-cache-size = <0x20000>; + operating-points-v2 = <&avalanche_opp>; + capacity-dmips-mhz = <1024>; + performance-domains = <&cpufreq_p0>; + }; + + cpu_p03: cpu@10103 { + compatible = "apple,avalanche"; + device_type = "cpu"; + reg = <0x0 0x10103>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* To be filled by loader */ + next-level-cache = <&l2_cache_1>; + i-cache-size = <0x30000>; + d-cache-size = <0x20000>; + operating-points-v2 = <&avalanche_opp>; + capacity-dmips-mhz = <1024>; + performance-domains = <&cpufreq_p0>; + }; + + cpu_p10: cpu@10200 { + compatible = "apple,avalanche"; + device_type = "cpu"; + reg = <0x0 0x10200>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* To be filled by loader */ + next-level-cache = <&l2_cache_2>; + i-cache-size = <0x30000>; + d-cache-size = <0x20000>; + operating-points-v2 = <&avalanche_opp>; + capacity-dmips-mhz = <1024>; + performance-domains = <&cpufreq_p1>; + }; + + cpu_p11: cpu@10201 { + compatible = "apple,avalanche"; + device_type = "cpu"; + reg = <0x0 0x10201>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* To be filled by loader */ + next-level-cache = <&l2_cache_2>; + i-cache-size = <0x30000>; + d-cache-size = <0x20000>; + operating-points-v2 = <&avalanche_opp>; + capacity-dmips-mhz = <1024>; + performance-domains = <&cpufreq_p1>; + }; + + cpu_p12: cpu@10202 { + compatible = "apple,avalanche"; + device_type = "cpu"; + reg = <0x0 0x10202>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* To be filled by loader */ + next-level-cache = <&l2_cache_2>; + i-cache-size = <0x30000>; + d-cache-size = <0x20000>; + operating-points-v2 = <&avalanche_opp>; + capacity-dmips-mhz = <1024>; + performance-domains = <&cpufreq_p1>; + }; + + cpu_p13: cpu@10203 { + compatible = "apple,avalanche"; + device_type = "cpu"; + reg = <0x0 0x10203>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* To be filled by loader */ + next-level-cache = <&l2_cache_2>; + i-cache-size = <0x30000>; + d-cache-size = <0x20000>; + operating-points-v2 = <&avalanche_opp>; + capacity-dmips-mhz = <1024>; + performance-domains = <&cpufreq_p1>; + }; + + l2_cache_0: l2-cache-0 { + compatible = "cache"; + cache-level = <2>; + cache-unified; + cache-size = <0x400000>; + }; + + l2_cache_1: l2-cache-1 { + compatible = "cache"; + cache-level = <2>; + cache-unified; + cache-size = <0x1000000>; + }; + + l2_cache_2: l2-cache-2 { + compatible = "cache"; + cache-level = <2>; + cache-unified; + cache-size = <0x1000000>; + }; + }; + + blizzard_opp: opp-table-0 { + compatible = "operating-points-v2"; + opp-shared; + + /* pstate #1 is a dummy clone of #2 */ + opp02 { + opp-hz = /bits/ 64 <912000000>; + opp-level = <2>; + clock-latency-ns = <7700>; + }; + opp03 { + opp-hz = /bits/ 64 <1284000000>; + opp-level = <3>; + clock-latency-ns = <25000>; + }; + opp04 { + opp-hz = /bits/ 64 <1752000000>; + opp-level = <4>; + clock-latency-ns = <33000>; + }; + opp05 { + opp-hz = /bits/ 64 <2004000000>; + opp-level = <5>; + clock-latency-ns = <38000>; + }; + opp06 { + opp-hz = /bits/ 64 <2256000000>; + opp-level = <6>; + clock-latency-ns = <44000>; + }; + opp07 { + opp-hz = /bits/ 64 <2424000000>; + opp-level = <7>; + clock-latency-ns = <48000>; + }; + }; + + avalanche_opp: opp-table-1 { + compatible = "operating-points-v2"; + opp-shared; + + opp01 { + opp-hz = /bits/ 64 <702000000>; + opp-level = <1>; + clock-latency-ns = <7400>; + }; + opp02 { + opp-hz = /bits/ 64 <948000000>; + opp-level = <2>; + clock-latency-ns = <18000>; + }; + opp03 { + opp-hz = /bits/ 64 <1188000000>; + opp-level = <3>; + clock-latency-ns = <21000>; + }; + opp04 { + opp-hz = /bits/ 64 <1452000000>; + opp-level = <4>; + clock-latency-ns = <24000>; + }; + opp05 { + opp-hz = /bits/ 64 <1704000000>; + opp-level = <5>; + clock-latency-ns = <28000>; + }; + opp06 { + opp-hz = /bits/ 64 <1968000000>; + opp-level = <6>; + clock-latency-ns = <31000>; + }; + opp07 { + opp-hz = /bits/ 64 <2208000000>; + opp-level = <7>; + clock-latency-ns = <33000>; + }; + opp08 { + opp-hz = /bits/ 64 <2400000000>; + opp-level = <8>; + clock-latency-ns = <45000>; + }; + opp09 { + opp-hz = /bits/ 64 <2568000000>; + opp-level = <9>; + clock-latency-ns = <47000>; + }; + opp10 { + opp-hz = /bits/ 64 <2724000000>; + opp-level = <10>; + clock-latency-ns = <50000>; + }; + opp11 { + opp-hz = /bits/ 64 <2868000000>; + opp-level = <11>; + clock-latency-ns = <52000>; + }; + opp12 { + opp-hz = /bits/ 64 <3000000000>; + opp-level = <12>; + clock-latency-ns = <57000>; + }; + opp13 { + opp-hz = /bits/ 64 <3132000000>; + opp-level = <13>; + clock-latency-ns = <60000>; + }; + opp14 { + opp-hz = /bits/ 64 <3264000000>; + opp-level = <14>; + clock-latency-ns = <64000>; + }; + opp15 { + opp-hz = /bits/ 64 <3360000000>; + opp-level = <15>; + clock-latency-ns = <64000>; + turbo-mode; + }; + opp16 { + opp-hz = /bits/ 64 <3408000000>; + opp-level = <16>; + clock-latency-ns = <64000>; + turbo-mode; + }; + opp17 { + opp-hz = /bits/ 64 <3504000000>; + opp-level = <17>; + clock-latency-ns = <64000>; + turbo-mode; + }; + }; + + gpu_opp: opp-table-gpu { + compatible = "operating-points-v2"; + + /* + * NOTE: The voltage and power values are device-specific and + * must be filled in by the bootloader. + */ + opp00 { + opp-hz = /bits/ 64 <0>; + opp-microvolt = GPU_REPEAT(400000); + opp-microwatt = <0>; + }; + opp01 { + opp-hz = /bits/ 64 <444000000>; + opp-microvolt = GPU_REPEAT(637000); + opp-microwatt = <4295000>; + }; + opp02 { + opp-hz = /bits/ 64 <612000000>; + opp-microvolt = GPU_REPEAT(656000); + opp-microwatt = <6251000>; + }; + opp03 { + opp-hz = /bits/ 64 <808000000>; + opp-microvolt = GPU_REPEAT(687000); + opp-microwatt = <8625000>; + }; + opp04 { + opp-hz = /bits/ 64 <968000000>; + opp-microvolt = GPU_REPEAT(725000); + opp-microwatt = <11948000>; + }; + opp05 { + opp-hz = /bits/ 64 <1110000000>; + opp-microvolt = GPU_REPEAT(790000); + opp-microwatt = <15071000>; + }; + opp06 { + opp-hz = /bits/ 64 <1236000000>; + opp-microvolt = GPU_REPEAT(843000); + opp-microwatt = <18891000>; + }; + opp07 { + opp-hz = /bits/ 64 <1338000000>; + opp-microvolt = GPU_REPEAT(887000); + opp-microwatt = <21960000>; + }; + opp08 { + opp-hz = /bits/ 64 <1398000000>; + opp-microvolt = GPU_REPEAT(918000); + opp-microwatt = <22800000>; + }; + }; + + pmu-e { + compatible = "apple,blizzard-pmu"; + interrupt-parent = <&aic>; + interrupts = ; + }; + + pmu-p { + compatible = "apple,avalanche-pmu"; + interrupt-parent = <&aic>; + interrupts = ; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupt-parent = <&aic>; + interrupt-names = "phys", "virt", "hyp-phys", "hyp-virt"; + interrupts = , + , + , + ; + }; + + clkref: clock-ref { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24000000>; + clock-output-names = "clkref"; + }; + + clk_200m: clock-200m { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <200000000>; + clock-output-names = "clk_200m"; + }; + + clk_disp0: clock-disp0 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <257142848>; /* TODO: check */ + clock-output-names = "clk_disp0"; + }; + + /* + * This is a fabulated representation of the input clock + * to NCO since we don't know the true clock tree. + */ + nco_clkref: clock-ref-nco { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-output-names = "nco_ref"; + }; +}; diff --git a/arch/arm64/boot/dts/apple/t602x-die0.dtsi b/arch/arm64/boot/dts/apple/t602x-die0.dtsi new file mode 100644 index 00000000000000..794abdacfb01dc --- /dev/null +++ b/arch/arm64/boot/dts/apple/t602x-die0.dtsi @@ -0,0 +1,702 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * In anticipation of an M2 Ultra. Inspired by T600x. + * + * Obviously needs filling out, just the bare bones required + * to boot to a console in the HV. + * + * Copyright The Asahi Linux Contributors + */ + + nco: clock-controller@28e03c000 { + compatible = "apple,t6020-nco", "apple,nco"; + reg = <0x2 0x8e03c000 0x0 0x14000>; + clocks = <&nco_clkref>; + #clock-cells = <1>; + }; + + aic: interrupt-controller@28e100000 { + compatible = "apple,t6020-aic", "apple,aic2"; + #interrupt-cells = <4>; + interrupt-controller; + reg = <0x2 0x8e100000 0x0 0xc000>, + <0x2 0x8e10c000 0x0 0x1000>; + reg-names = "core", "event"; + power-domains = <&ps_aic>; + }; + + pmgr_dcp: power-management@28e3d0000 { + reg = <0x2 0x8e3d0000 0x0 0x4000>; + reg-names = "dcp-fw-pmgr"; + #apple,bw-scratch-cells = <3>; + }; + + wdt: watchdog@29e2c4000 { + compatible = "apple,t6020-wdt", "apple,wdt"; + reg = <0x2 0x9e2c4000 0x0 0x4000>; + clocks = <&clkref>; + interrupt-parent = <&aic>; + interrupts = ; + }; + + nub_spmi0: spmi@29e114000 { + compatible = "apple,t6020-spmi", "apple,spmi"; + reg = <0x2 0x9e114000 0x0 0x100>; + #address-cells = <2>; + #size-cells = <0>; + interrupt-parent = <&aic>; + interrupts = , + ; + + pmu1: pmu@f { + compatible = "apple,maverick-pmu", "apple,spmi-pmu"; + reg = <0xb SPMI_USID>; + #address-cells = <1>; + #size-cells = <1>; + + rtc_nvmem@1400 { + compatible = "apple,spmi-pmu-nvmem"; + reg = <0x1400 0x20>; + #address-cells = <1>; + #size-cells = <1>; + + pm_setting: pm-setting@5 { + reg = <0x5 0x1>; + }; + + rtc_offset: rtc-offset@11 { + reg = <0x11 0x6>; + }; + }; + + legacy_nvmem@6000 { + compatible = "apple,spmi-pmu-nvmem"; + reg = <0x6000 0x20>; + #address-cells = <1>; + #size-cells = <1>; + + boot_stage: boot-stage@1 { + reg = <0x1 0x1>; + }; + + boot_error_count: boot-error-count@2 { + reg = <0x2 0x1>; + bits = <0 4>; + }; + + panic_count: panic-count@2 { + reg = <0x2 0x1>; + bits = <4 4>; + }; + + boot_error_stage: boot-error-stage@3 { + reg = <0x3 0x1>; + }; + + shutdown_flag: shutdown-flag@f { + reg = <0xf 0x1>; + bits = <3 1>; + }; + }; + + scrpad_nvmem@8000 { + compatible = "apple,spmi-pmu-nvmem"; + reg = <0x8000 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + + fault_shadow: fault-shadow@67b { + reg = <0x67b 0x10>; + }; + + socd: socd@b00 { + reg = <0xb00 0x400>; + }; + }; + + }; + }; + + smc_mbox: mbox@2a2408000 { + compatible = "apple,t6020-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x2 0xa2408000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + }; + + smc: smc@2a2400000 { + compatible = "apple,t6020-smc", "apple,smc"; + reg = <0x2 0xa2400000 0x0 0x4000>, + <0x2 0xa3e00000 0x0 0x100000>; + reg-names = "smc", "sram"; + mboxes = <&smc_mbox>; + + smc_gpio: gpio { + gpio-controller; + #gpio-cells = <2>; + }; + + smc_rtc: rtc { + nvmem-cells = <&rtc_offset>; + nvmem-cell-names = "rtc_offset"; + }; + + smc_reboot: reboot { + nvmem-cells = <&shutdown_flag>, <&boot_stage>, + <&boot_error_count>, <&panic_count>, <&pm_setting>; + nvmem-cell-names = "shutdown_flag", "boot_stage", + "boot_error_count", "panic_count", "pm_setting"; + }; + }; + + pinctrl_smc: pinctrl@2a2820000 { + compatible = "apple,t6020-pinctrl", "apple,pinctrl"; + reg = <0x2 0xa2820000 0x0 0x4000>; + + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pinctrl_smc 0 0 30>; + apple,npins = <30>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&aic>; + interrupts = , + , + , + , + , + , + ; + }; + + disp0_dart: iommu@389304000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x3 0x89304000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + status = "disabled"; + power-domains = <&ps_disp0_cpu0>; + apple,dma-range = <0x100 0x0 0x10 0x0>; + }; + + dcp_dart: iommu@38930c000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x3 0x8930c000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_disp0_cpu0>; + apple,dma-range = <0x100 0x0 0x10 0x0>; + }; + + dcp_mbox: mbox@389c08000 { + compatible = "apple,t6020-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x3 0x89c08000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + power-domains = <&ps_disp0_cpu0>; + }; + + dcp: dcp@389c00000 { + compatible = "apple,t6020-dcp", "apple,dcp"; + mboxes = <&dcp_mbox>; + mbox-names = "mbox"; + iommus = <&dcp_dart 5>; + + reg-names = "coproc", "disp-0", "disp-1", "disp-2", "disp-3"; + reg = <0x3 0x89c00000 0x0 0x4000>, // check? + <0x3 0x88000000 0x0 0x61c000>, + <0x3 0x89320000 0x0 0x4000>, + <0x3 0x89344000 0x0 0x4000>, + <0x3 0x89800000 0x0 0x800000>; + apple,bw-scratch = <&pmgr_dcp 0 4 0x1208>; + power-domains = <&ps_disp0_cpu0>; + resets = <&ps_disp0_cpu0>; + clocks = <&clk_disp0>; + phandle = <&dcp>; + + disp0_piodma: piodma { + iommus = <&disp0_dart 4>; + phandle = <&disp0_piodma>; + }; + }; + + display: display-subsystem { + compatible = "apple,display-subsystem"; + iommus = <&disp0_dart 0>; + /* generate phandle explicitly for use in loader */ + phandle = <&display>; + }; + + sio_dart: iommu@39b008000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x3 0x9b008000 0x0 0x8000>; + interrupt-parent = <&aic>; + interrupts = ; + #iommu-cells = <1>; + power-domains = <&ps_sio_cpu>; + }; + + fpwm0: pwm@39b030000 { + compatible = "apple,t6020-fpwm", "apple,s5l-fpwm"; + reg = <0x3 0x9b030000 0x0 0x4000>; + power-domains = <&ps_fpwm0>; + clocks = <&clkref>; + #pwm-cells = <2>; + status = "disabled"; + }; + + i2c0: i2c@39b040000 { + compatible = "apple,t6020-i2c", "apple,i2c"; + reg = <0x3 0x9b040000 0x0 0x4000>; + clocks = <&clkref>; + interrupt-parent = <&aic>; + interrupts = ; + pinctrl-0 = <&i2c0_pins>; + pinctrl-names = "default"; + power-domains = <&ps_i2c0>; + #address-cells = <0x1>; + #size-cells = <0x0>; + }; + + i2c1: i2c@39b044000 { + compatible = "apple,t6020-i2c", "apple,i2c"; + reg = <0x3 0x9b044000 0x0 0x4000>; + clocks = <&clkref>; + interrupt-parent = <&aic>; + interrupts = ; + pinctrl-0 = <&i2c1_pins>; + pinctrl-names = "default"; + power-domains = <&ps_i2c1>; + #address-cells = <0x1>; + #size-cells = <0x0>; + status = "disabled"; + }; + + i2c2: i2c@39b048000 { + compatible = "apple,t6020-i2c", "apple,i2c"; + reg = <0x3 0x9b048000 0x0 0x4000>; + clocks = <&clkref>; + interrupt-parent = <&aic>; + interrupts = ; + pinctrl-0 = <&i2c2_pins>; + pinctrl-names = "default"; + power-domains = <&ps_i2c2>; + #address-cells = <0x1>; + #size-cells = <0x0>; + status = "disabled"; + }; + + i2c3: i2c@39b04c000 { + compatible = "apple,t6020-i2c", "apple,i2c"; + reg = <0x3 0x9b04c000 0x0 0x4000>; + clocks = <&clkref>; + interrupt-parent = <&aic>; + interrupts = ; + pinctrl-0 = <&i2c3_pins>; + pinctrl-names = "default"; + power-domains = <&ps_i2c3>; + #address-cells = <0x1>; + #size-cells = <0x0>; + status = "disabled"; + }; + + i2c4: i2c@39b050000 { + compatible = "apple,t6020-i2c", "apple,i2c"; + reg = <0x3 0x9b050000 0x0 0x4000>; + clocks = <&clkref>; + interrupt-parent = <&aic>; + interrupts = ; + pinctrl-0 = <&i2c4_pins>; + pinctrl-names = "default"; + power-domains = <&ps_i2c4>; + #address-cells = <0x1>; + #size-cells = <0x0>; + status = "disabled"; + }; + + i2c5: i2c@39b054000 { + compatible = "apple,t6020-i2c", "apple,i2c"; + reg = <0x3 0x9b054000 0x0 0x4000>; + clocks = <&clkref>; + interrupt-parent = <&aic>; + interrupts = ; + pinctrl-0 = <&i2c5_pins>; + pinctrl-names = "default"; + power-domains = <&ps_i2c5>; + #address-cells = <0x1>; + #size-cells = <0x0>; + status = "disabled"; + }; + + i2c6: i2c@39b054000 { + compatible = "apple,t6020-i2c", "apple,i2c"; + reg = <0x3 0x9b054000 0x0 0x4000>; + clocks = <&clkref>; + interrupt-parent = <&aic>; + interrupts = ; + pinctrl-0 = <&i2c6_pins>; + pinctrl-names = "default"; + power-domains = <&ps_i2c6>; + #address-cells = <0x1>; + #size-cells = <0x0>; + status = "disabled"; + }; + + i2c7: i2c@39b054000 { + compatible = "apple,t6020-i2c", "apple,i2c"; + reg = <0x3 0x9b054000 0x0 0x4000>; + clocks = <&clkref>; + interrupt-parent = <&aic>; + interrupts = ; + pinctrl-0 = <&i2c7_pins>; + pinctrl-names = "default"; + power-domains = <&ps_i2c7>; + #address-cells = <0x1>; + #size-cells = <0x0>; + status = "disabled"; + }; + + i2c8: i2c@39b054000 { + compatible = "apple,t6020-i2c", "apple,i2c"; + reg = <0x3 0x9b054000 0x0 0x4000>; + clocks = <&clkref>; + interrupt-parent = <&aic>; + interrupts = ; + pinctrl-0 = <&i2c8_pins>; + pinctrl-names = "default"; + power-domains = <&ps_i2c8>; + #address-cells = <0x1>; + #size-cells = <0x0>; + status = "disabled"; + }; + + spi1: spi@39b104000 { + compatible = "apple,t6020-spi", "apple,spi"; + reg = <0x3 0x9b104000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clk_200m>; + pinctrl-0 = <&spi1_pins>; + pinctrl-names = "default"; + power-domains = <&ps_spi1>; + status = "disabled"; + }; + + spi2: spi@39b108000 { + compatible = "apple,t6020-spi", "apple,spi"; + reg = <0x3 0x9b108000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clkref>; + pinctrl-0 = <&spi2_pins>; + pinctrl-names = "default"; + power-domains = <&ps_spi2>; + status = "disabled"; + }; + + spi4: spi@39b110000 { + compatible = "apple,t6020-spi", "apple,spi"; + reg = <0x3 0x9b110000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clkref>; + pinctrl-0 = <&spi4_pins>; + pinctrl-names = "default"; + power-domains = <&ps_spi4>; + status = "disabled"; + }; + + serial0: serial@39b200000 { + compatible = "apple,s5l-uart"; + reg = <0x3 0x9b200000 0x0 0x4000>; + reg-io-width = <4>; + interrupt-parent = <&aic>; + interrupts = ; + /* + * TODO: figure out the clocking properly, there may + * be a third selectable clock. + */ + clocks = <&clkref>, <&clkref>; + clock-names = "uart", "clk_uart_baud0"; + power-domains = <&ps_uart0>; + status = "disabled"; + }; + + admac: dma-controller@39b400000 { + compatible = "apple,t6020-admac", "apple,admac"; + reg = <0x3 0x9b400000 0x0 0x34000>; + #dma-cells = <1>; + dma-channels = <16>; + interrupts-extended = <0>, + <&aic AIC_IRQ 0 1218 IRQ_TYPE_LEVEL_HIGH>, + <0>, + <0>; + iommus = <&sio_dart 2>; + power-domains = <&ps_sio_adma>; + resets = <&ps_audio_p>; + }; + + mca: mca@39b600000 { + compatible = "apple,t6020-mca", "apple,mca"; + reg = <0x3 0x9b600000 0x0 0x10000>, + <0x3 0x9b500000 0x0 0x20000>; + clocks = <&nco 0>, <&nco 1>, <&nco 2>, <&nco 3>; + dmas = <&admac 0>, <&admac 1>, <&admac 2>, <&admac 3>, + <&admac 4>, <&admac 5>, <&admac 6>, <&admac 7>, + <&admac 8>, <&admac 9>, <&admac 10>, <&admac 11>, + <&admac 12>, <&admac 13>, <&admac 14>, <&admac 15>; + dma-names = "tx0a", "rx0a", "tx0b", "rx0b", + "tx1a", "rx1a", "tx1b", "rx1b", + "tx2a", "rx2a", "tx2b", "rx2b", + "tx3a", "rx3a", "tx3b", "rx3b"; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + power-domains = <&ps_audio_p>, <&ps_mca0>, <&ps_mca1>, + <&ps_mca2>, <&ps_mca3>; + resets = <&ps_audio_p>; + #sound-dai-cells = <1>; + }; + + pmgr_gfx: power-management@404e80000 { + compatible = "apple,t6021-pmgr", "apple,pmgr", "syscon", "simple-mfd"; + #address-cells = <1>; + #size-cells = <1>; + + reg = <0x4 0x4e80000 0 0x4000>; + }; + + gpu: gpu@406400000 { + compatible = "apple,agx-g14x"; + reg = <0x4 0x6400000 0 0x40000>, + <0x4 0x4000000 0 0x1000000>; + reg-names = "asc", "sgx"; + interrupt-parent = <&aic>; + interrupts = , + , + , + , + , + , + ; + mboxes = <&agx_mbox>; + power-domains = <&ps_gfx>; + memory-region = <&uat_ttbs>, <&uat_pagetables>, <&uat_handoff>; + memory-region-names = "ttbs", "pagetables", "handoff"; + + apple,firmware-version = <0 0 0>; + apple,firmware-compat = <0 0 0>; + + operating-points-v2 = <&gpu_opp>; + /* TODO perf stuff */ + apple,perf-base-pstate = <1>; + }; + + agx_mbox: mbox@406408000 { + compatible = "apple,t6020-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x4 0x6408000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + }; + + pcie0: pcie@580000000 { + compatible = "apple,t6020-pcie"; + device_type = "pci"; + + reg = <0x5 0x80000000 0x0 0x1000000>, /* config */ + <0x5 0x91000000 0x0 0x4000>, /* rc */ + <0x5 0x94008000 0x0 0x4000>, /* port0 */ + <0x5 0x95008000 0x0 0x4000>, /* port1 */ + <0x5 0x96008000 0x0 0x4000>, /* port2 */ + <0x5 0x97008000 0x0 0x4000>, /* port3 */ + <0x5 0x9e00c000 0x0 0x4000>, /* phy0 */ + <0x5 0x9e010000 0x0 0x4000>, /* phy1 */ + <0x5 0x9e014000 0x0 0x4000>, /* phy2 */ + <0x5 0x9e018000 0x0 0x4000>, /* phy3 */ + <0x5 0x9401c000 0x0 0x1000>, /* ltssm0 */ + <0x5 0x9501c000 0x0 0x1000>, /* ltssm1 */ + <0x5 0x9601c000 0x0 0x1000>, /* ltssm2 */ + <0x5 0x9701c000 0x0 0x1000>; /* ltssm3 */ + reg-names = "config", "rc", + "port0", "port1", "port2", "port3", + "phy0", "phy1", "phy2", "phy3", + "ltssm0", "ltssm1", "ltssm2", "ltssm3"; + + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + + msi-controller; + msi-parent = <&pcie0>; + msi-ranges = <&aic AIC_IRQ 0 1672 IRQ_TYPE_EDGE_RISING 32>; + + + iommu-map = <0x100 &pcie0_dart_0 1 1>, + <0x200 &pcie0_dart_1 1 1>, + <0x300 &pcie0_dart_2 1 1>, + <0x400 &pcie0_dart_3 1 1>; + iommu-map-mask = <0xff00>; + + bus-range = <0 4>; + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x43000000 0x5 0xa0000000 0x5 0xa0000000 0x0 0x20000000>, + <0x02000000 0x0 0xc0000000 0x5 0xc0000000 0x0 0x40000000>; + + power-domains = <&ps_apcie_gp_sys>; + pinctrl-0 = <&pcie_pins>; + pinctrl-names = "default"; + + dma-coherent; + + port00: pci@0,0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + reset-gpios = <&pinctrl_ap 4 GPIO_ACTIVE_LOW>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + interrupt-controller; + #interrupt-cells = <1>; + + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &port00 0 0 0 0>, + <0 0 0 2 &port00 0 0 0 1>, + <0 0 0 3 &port00 0 0 0 2>, + <0 0 0 4 &port00 0 0 0 3>; + }; + + port01: pci@1,0 { + device_type = "pci"; + reg = <0x800 0x0 0x0 0x0 0x0>; + reset-gpios = <&pinctrl_ap 5 GPIO_ACTIVE_LOW>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + interrupt-controller; + #interrupt-cells = <1>; + + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &port01 0 0 0 0>, + <0 0 0 2 &port01 0 0 0 1>, + <0 0 0 3 &port01 0 0 0 2>, + <0 0 0 4 &port01 0 0 0 3>; + status = "disabled"; + }; + + port02: pci@2,0 { + device_type = "pci"; + reg = <0x1000 0x0 0x0 0x0 0x0>; + reset-gpios = <&pinctrl_ap 6 GPIO_ACTIVE_LOW>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + interrupt-controller; + #interrupt-cells = <1>; + + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &port02 0 0 0 0>, + <0 0 0 2 &port02 0 0 0 1>, + <0 0 0 3 &port02 0 0 0 2>, + <0 0 0 4 &port02 0 0 0 3>; + status = "disabled"; + }; + + port03: pci@3,0 { + device_type = "pci"; + reg = <0x1800 0x0 0x0 0x0 0x0>; + reset-gpios = <&pinctrl_ap 7 GPIO_ACTIVE_LOW>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + interrupt-controller; + #interrupt-cells = <1>; + + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &port03 0 0 0 0>, + <0 0 0 2 &port03 0 0 0 1>, + <0 0 0 3 &port03 0 0 0 2>, + <0 0 0 4 &port03 0 0 0 3>; + status = "disabled"; + }; + }; + + pcie0_dart_0: iommu@594000000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x5 0x94000000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_apcie_gp_sys>; + }; + + pcie0_dart_1: iommu@595000000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x5 0x95000000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_apcie_gp_sys>; + status = "disabled"; + }; + + pcie0_dart_2: iommu@596000000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x5 0x96000000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_apcie_gp_sys>; + status = "disabled"; + }; + + pcie0_dart_3: iommu@597000000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x5 0x97000000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_apcie_gp_sys>; + status = "disabled"; + }; + + diff --git a/arch/arm64/boot/dts/apple/t602x-dieX.dtsi b/arch/arm64/boot/dts/apple/t602x-dieX.dtsi new file mode 100644 index 00000000000000..203316df0d06f0 --- /dev/null +++ b/arch/arm64/boot/dts/apple/t602x-dieX.dtsi @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * Nodes present on both dies of a hypothetical T6022 (M2 Ultra) + * and present on M2 Pro/Max. + * + * Copyright The Asahi Linux Contributors + */ + + DIE_NODE(cpufreq_e): cpufreq@210e20000 { + compatible = "apple,t6020-cluster-cpufreq", "apple,t8112-cluster-cpufreq", "apple,cluster-cpufreq"; + reg = <0x2 0x10e20000 0 0x1000>; + #performance-domain-cells = <0>; + }; + + DIE_NODE(cpufreq_p0): cpufreq@211e20000 { + compatible = "apple,t6020-cluster-cpufreq", "apple,t8112-cluster-cpufreq", "apple,cluster-cpufreq"; + reg = <0x2 0x11e20000 0 0x1000>; + #performance-domain-cells = <0>; + }; + + DIE_NODE(cpufreq_p1): cpufreq@212e20000 { + compatible = "apple,t6020-cluster-cpufreq", "apple,t8112-cluster-cpufreq", "apple,cluster-cpufreq"; + reg = <0x2 0x12e20000 0 0x1000>; + #performance-domain-cells = <0>; + }; + + DIE_NODE(pmgr): power-management@28e080000 { + compatible = "apple,t6020-pmgr", "apple,pmgr", "syscon", "simple-mfd"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x2 0x8e080000 0 0x8000>; + }; + + DIE_NODE(pmgr_south): power-management@28e680000 { + compatible = "apple,t6020-pmgr", "apple,pmgr", "syscon", "simple-mfd"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x2 0x8e680000 0 0x8000>; + }; + + DIE_NODE(pmgr_east): power-management@290280000 { + compatible = "apple,t6020-pmgr", "apple,pmgr", "syscon", "simple-mfd"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x2 0x90280000 0 0xc000>; + }; + + DIE_NODE(pinctrl_nub): pinctrl@29e1f0000 { + compatible = "apple,t6000-pinctrl", "apple,pinctrl"; + reg = <0x2 0x9e1f0000 0x0 0x4000>; + power-domains = <&DIE_NODE(ps_nub_gpio)>; + + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&DIE_NODE(pinctrl_nub) 0 0 30>; + apple,npins = <30>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&aic>; + interrupts = , + , + , + , + , + , + ; + }; + + DIE_NODE(pmgr_mini): power-management@29e280000 { + compatible = "apple,t6000-pmgr", "apple,pmgr", "syscon", "simple-mfd"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x2 0x9e280000 0 0x4000>; + }; + + DIE_NODE(efuse): efuse@29e2cc000 { + compatible = "apple,t6020-efuses", "apple,efuses"; + reg = <0x2 0x9e2cc000 0x0 0x2000>; + #address-cells = <1>; + #size-cells = <1>; + }; + + DIE_NODE(pinctrl_aop): pinctrl@2a6820000 { + compatible = "apple,t6020-pinctrl", "apple,pinctrl"; + reg = <0x2 0xa6820000 0x0 0x4000>; + + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&DIE_NODE(pinctrl_aop) 0 0 72>; + apple,npins = <72>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&aic>; + interrupts = , + , + , + , + , + , + ; + }; + + DIE_NODE(pinctrl_ap): pinctrl@39b028000 { + compatible = "apple,t6020-pinctrl", "apple,pinctrl"; + reg = <0x3 0x9b028000 0x0 0x4000>; + + interrupt-parent = <&aic>; + interrupts = , + , + , + , + , + , + ; + + clocks = <&clkref>; + power-domains = <&DIE_NODE(ps_gpio)>; + + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&DIE_NODE(pinctrl_ap) 0 0 255>; + apple,npins = <255>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + DIE_NODE(dwc3_0_dart_0): iommu@702f00000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x7 0x02f00000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_atc0_usb)>; + #iommu-cells = <1>; + }; + + DIE_NODE(dwc3_0_dart_1): iommu@702f80000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x7 0x02f80000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_atc0_usb)>; + #iommu-cells = <1>; + }; + + DIE_NODE(dwc3_0): usb@702280000 { + compatible = "apple,t6020-dwc3", "apple,dwc3", "snps,dwc3"; + reg = <0x7 0x02280000 0x0 0x100000>; + interrupt-parent = <&aic>; + interrupts = ; + dr_mode = "otg"; + usb-role-switch; + role-switch-default-mode = "host"; + iommus = <&DIE_NODE(dwc3_0_dart_0) 0>, + <&DIE_NODE(dwc3_0_dart_1) 1>; + power-domains = <&DIE_NODE(ps_atc0_usb)>; + dma-coherent; + resets = <&DIE_NODE(atcphy0)>; + phys = <&DIE_NODE(atcphy0) PHY_TYPE_USB2>, <&DIE_NODE(atcphy0) PHY_TYPE_USB3>; + phy-names = "usb2-phy", "usb3-phy"; + }; + + DIE_NODE(atcphy0): phy@703000000 { + compatible = "apple,t6020-atcphy", "apple,t8103-atcphy"; + reg = <0x7 0x03000000 0x0 0x4c000>, + <0x7 0x03050000 0x0 0x8000>, + <0x7 0x00000000 0x0 0x4000>, + <0x7 0x02a90000 0x0 0x4000>, + <0x7 0x02a84000 0x0 0x4000>; + reg-names = "core", "lpdptx", "axi2af", "usb2phy", + "pipehandler"; + + #phy-cells = <1>; + #reset-cells = <0>; + + orientation-switch; + mode-switch; + svid = <0xff01>, <0x8087>; + power-domains = <&DIE_NODE(ps_atc0_usb)>; + }; + + DIE_NODE(dwc3_1_dart_0): iommu@b02f00000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0xb 0x02f00000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_atc1_usb)>; + #iommu-cells = <1>; + }; + + DIE_NODE(dwc3_1_dart_1): iommu@b02f80000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0xb 0x02f80000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_atc1_usb)>; + #iommu-cells = <1>; + }; + + DIE_NODE(dwc3_1): usb@b02280000 { + compatible = "apple,t6020-dwc3", "apple,dwc3", "snps,dwc3"; + reg = <0xb 0x02280000 0x0 0x100000>; + interrupt-parent = <&aic>; + interrupts = ; + dr_mode = "otg"; + usb-role-switch; + role-switch-default-mode = "host"; + iommus = <&DIE_NODE(dwc3_1_dart_0) 0>, + <&DIE_NODE(dwc3_1_dart_1) 1>; + power-domains = <&DIE_NODE(ps_atc1_usb)>; + dma-coherent; + resets = <&DIE_NODE(atcphy1)>; + phys = <&DIE_NODE(atcphy1) PHY_TYPE_USB2>, <&DIE_NODE(atcphy1) PHY_TYPE_USB3>; + phy-names = "usb2-phy", "usb3-phy"; + }; + + DIE_NODE(atcphy1): phy@b03000000 { + compatible = "apple,t6020-atcphy", "apple,t8103-atcphy"; + reg = <0xb 0x03000000 0x0 0x4c000>, + <0xb 0x03050000 0x0 0x8000>, + <0xb 0x00000000 0x0 0x4000>, + <0xb 0x02a90000 0x0 0x4000>, + <0xb 0x02a84000 0x0 0x4000>; + reg-names = "core", "lpdptx", "axi2af", "usb2phy", + "pipehandler"; + + #phy-cells = <1>; + #reset-cells = <0>; + + orientation-switch; + mode-switch; + svid = <0xff01>, <0x8087>; + power-domains = <&DIE_NODE(ps_atc1_usb)>; + }; + + DIE_NODE(dwc3_2_dart_0): iommu@f02f00000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0xf 0x02f00000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_atc2_usb)>; + #iommu-cells = <1>; + }; + + DIE_NODE(dwc3_2_dart_1): iommu@f02f80000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0xf 0x02f80000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_atc2_usb)>; + #iommu-cells = <1>; + }; + + DIE_NODE(dwc3_2): usb@f02280000 { + compatible = "apple,t6020-dwc3", "apple,dwc3", "snps,dwc3"; + reg = <0xf 0x02280000 0x0 0x100000>; + interrupt-parent = <&aic>; + interrupts = ; + dr_mode = "otg"; + usb-role-switch; + role-switch-default-mode = "host"; + iommus = <&DIE_NODE(dwc3_2_dart_0) 0>, + <&DIE_NODE(dwc3_2_dart_1) 1>; + power-domains = <&DIE_NODE(ps_atc2_usb)>; + dma-coherent; + resets = <&DIE_NODE(atcphy2)>; + phys = <&DIE_NODE(atcphy2) PHY_TYPE_USB2>, <&DIE_NODE(atcphy2) PHY_TYPE_USB3>; + phy-names = "usb2-phy", "usb3-phy"; + }; + + DIE_NODE(atcphy2): phy@f03000000 { + compatible = "apple,t6020-atcphy", "apple,t8103-atcphy"; + reg = <0xf 0x03000000 0x0 0x4c000>, + <0xf 0x03050000 0x0 0x8000>, + <0xf 0x00000000 0x0 0x4000>, + <0xf 0x02a90000 0x0 0x4000>, + <0xf 0x02a84000 0x0 0x4000>; + reg-names = "core", "lpdptx", "axi2af", "usb2phy", + "pipehandler"; + + #phy-cells = <1>; + #reset-cells = <0>; + + orientation-switch; + mode-switch; + svid = <0xff01>, <0x8087>; + power-domains = <&DIE_NODE(ps_atc2_usb)>; + }; + + DIE_NODE(dwc3_3_dart_0): iommu@1302f00000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x13 0x02f00000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_atc3_usb)>; + #iommu-cells = <1>; + }; + + DIE_NODE(dwc3_3_dart_1): iommu@1302f80000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x13 0x02f80000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_atc3_usb)>; + #iommu-cells = <1>; + }; + + DIE_NODE(dwc3_3): usb@1302280000 { + compatible = "apple,t6020-dwc3", "apple,dwc3", "snps,dwc3"; + reg = <0x13 0x02280000 0x0 0x100000>; + interrupt-parent = <&aic>; + interrupts = ; + dr_mode = "otg"; + usb-role-switch; + role-switch-default-mode = "host"; + iommus = <&DIE_NODE(dwc3_3_dart_0) 0>, + <&DIE_NODE(dwc3_3_dart_1) 1>; + power-domains = <&DIE_NODE(ps_atc3_usb)>; + dma-coherent; + resets = <&DIE_NODE(atcphy3)>; + phys = <&DIE_NODE(atcphy3) PHY_TYPE_USB2>, <&DIE_NODE(atcphy3) PHY_TYPE_USB3>; + phy-names = "usb2-phy", "usb3-phy"; + }; + + DIE_NODE(atcphy3): phy@1303000000 { + compatible = "apple,t6020-atcphy", "apple,t8103-atcphy"; + reg = <0x13 0x03000000 0x0 0x4c000>, + <0x13 0x03050000 0x0 0x8000>, + <0x13 0x00000000 0x0 0x4000>, + <0x13 0x02a90000 0x0 0x4000>, + <0x13 0x02a84000 0x0 0x4000>; + reg-names = "core", "lpdptx", "axi2af", "usb2phy", + "pipehandler"; + + #phy-cells = <1>; + #reset-cells = <0>; + + orientation-switch; + mode-switch; + svid = <0xff01>, <0x8087>; + power-domains = <&DIE_NODE(ps_atc3_usb)>; + }; diff --git a/arch/arm64/boot/dts/apple/t602x-gpio-pins.dtsi b/arch/arm64/boot/dts/apple/t602x-gpio-pins.dtsi new file mode 100644 index 00000000000000..acb133d1723d03 --- /dev/null +++ b/arch/arm64/boot/dts/apple/t602x-gpio-pins.dtsi @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * GPIO pin mappings for Apple T600x SoCs. + * + * Copyright The Asahi Linux Contributors + */ + +&pinctrl_ap { + i2c0_pins: i2c0-pins { + pinmux = , + ; + }; + + i2c1_pins: i2c1-pins { + pinmux = , + ; + }; + + i2c2_pins: i2c2-pins { + pinmux = , + ; + }; + + i2c3_pins: i2c3-pins { + pinmux = , + ; + }; + + i2c4_pins: i2c4-pins { + pinmux = , + ; + }; + + i2c5_pins: i2c5-pins { + pinmux = , + ; + }; + + i2c6_pins: i2c6-pins { + pinmux = , + ; + }; + + i2c7_pins: i2c7-pins { + pinmux = , + ; + }; + + i2c8_pins: i2c8-pins { + pinmux = , + ; + }; + + spi1_pins: spi1-pins { + pinmux = , /* SDI */ + , /* SDO */ + , /* SCK */ + ; /* CS */ + }; + + spi2_pins: spi2-pins { + pinmux = , /* SDI */ + , /* SDO */ + , /* SCK */ + ; /* CS */ + }; + + spi4_pins: spi4-pins { + pinmux = , /* SDI */ + , /* SDO */ + , /* SCK */ + ; /* CS */ + }; + + pcie_pins: pcie-pins { + pinmux = , + , + , + ; + }; +}; diff --git a/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi b/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi new file mode 100644 index 00000000000000..c1f45b8114c92d --- /dev/null +++ b/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * MacBook Pro (14/16-inch, 2022) + * + * This file contains the parts common to J414 and J416 devices with both t6020 and t6021. + * + * target-type: J414s / J414c / J416s / J416c + * + * Copyright The Asahi Linux Contributors + */ + +/* + * These models are essentially identical to the previous generation, other than + * the GPIO indices. + */ + +#define NO_SPI_TRACKPAD +#include "t600x-j314-j316.dtsi" + +/ { + aliases { + keyboard = &keyboard; + }; +}; + +&hpm0 { + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; +}; + +&hpm1 { + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; +}; + +&hpm2 { + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; +}; + +&hpm5 { + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; +}; + +&speaker_left_tweet { + shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>; + interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>; +}; + +&speaker_left_woof1 { + shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>; + interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>; +}; + +&speaker_left_woof2 { + shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>; + interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>; +}; + +&speaker_right_tweet { + shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>; + interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>; +}; + +&speaker_right_woof1 { + shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>; + interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>; +}; + +&speaker_right_woof2 { + shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>; + interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>; +}; + +&jack_codec { + reset-gpios = <&pinctrl_nub 8 GPIO_ACTIVE_HIGH>; + interrupts-extended = <&pinctrl_ap 59 IRQ_TYPE_LEVEL_LOW>; +}; + +&wifi0 { + compatible = "pci14e4,4434"; +}; + +&bluetooth0 { + compatible = "pci14e4,5f72"; +}; + +&port01 { + pwren-gpios = <&smc_gpio 22 GPIO_ACTIVE_HIGH>; +}; + diff --git a/arch/arm64/boot/dts/apple/t602x-nvme.dtsi b/arch/arm64/boot/dts/apple/t602x-nvme.dtsi new file mode 100644 index 00000000000000..756a971bde48ae --- /dev/null +++ b/arch/arm64/boot/dts/apple/t602x-nvme.dtsi @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * NVMe related devices for Apple T602x SoCs. + * + * Copyright The Asahi Linux Contributors + */ + + DIE_NODE(ans_mbox): mbox@347408000 { + compatible = "apple,t6020-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x3 0x47408000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + power-domains = <&DIE_NODE(ps_ans2)>; + #mbox-cells = <0>; + }; + + DIE_NODE(sart): sart@34bc50000 { + compatible = "apple,t6020-sart", "apple,t6000-sart"; + reg = <0x3 0x4bc50000 0x0 0x10000>; + power-domains = <&DIE_NODE(ps_ans2)>; + }; + + DIE_NODE(nvme): nvme@34bcc0000 { + compatible = "apple,t6020-nvme-ans2", "apple,nvme-ans2"; + reg = <0x3 0x4bcc0000 0x0 0x40000>, <0x3 0x47400000 0x0 0x4000>; + reg-names = "nvme", "ans"; + interrupt-parent = <&aic>; + /* The NVME interrupt is always routed to die 0 */ + interrupts = ; + mboxes = <&DIE_NODE(ans_mbox)>; + apple,sart = <&DIE_NODE(sart)>; + power-domains = <&DIE_NODE(ps_ans2)>, + <&DIE_NODE(ps_apcie_st_sys)>, + <&DIE_NODE(ps_apcie_st1_sys)>; + power-domain-names = "ans", "apcie0", "apcie1"; + resets = <&DIE_NODE(ps_ans2)>; + }; diff --git a/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi b/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi new file mode 100644 index 00000000000000..50d79ab1ed1298 --- /dev/null +++ b/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi @@ -0,0 +1,2262 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * PMGR Power domains for the Apple T6001 "M1 Max" SoC + * + * Copyright The Asahi Linux Contributors + */ + +&pmgr { + DIE_NODE(ps_afi): power-controller@100 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x100 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afi); + apple,always-on; /* Apple Fabric, CPU interface is here */ + }; + + DIE_NODE(ps_aic): power-controller@108 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x108 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(aic); + apple,always-on; /* Core device */ + }; + + DIE_NODE(ps_dwi): power-controller@110 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x110 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dwi); + }; + + DIE_NODE(ps_pms): power-controller@118 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x118 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(pms); + apple,always-on; /* Core device */ + }; + + DIE_NODE(ps_gpio): power-controller@120 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x120 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(gpio); + power-domains = <&DIE_NODE(ps_sio)>, <&DIE_NODE(ps_pms)>; + }; + + DIE_NODE(ps_soc_dpe): power-controller@128 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x128 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(soc_dpe); + apple,always-on; /* Core device */ + }; + + DIE_NODE(ps_pms_c1ppt): power-controller@130 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x130 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(pms_c1ppt); + apple,always-on; /* Core device */ + }; + + DIE_NODE(ps_pmgr_soc_ocla): power-controller@138 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x138 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(pmgr_soc_ocla); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_amcc0): power-controller@168 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x168 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(amcc0); + apple,always-on; /* Memory controller */ + }; + + DIE_NODE(ps_amcc2): power-controller@170 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x170 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(amcc2); + apple,always-on; /* Memory controller */ + }; + + DIE_NODE(ps_dcs_00): power-controller@178 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x178 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_00); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_01): power-controller@180 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x180 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_01); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_02): power-controller@188 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x188 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_02); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_03): power-controller@190 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x190 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_03); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_08): power-controller@198 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x198 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_08); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_09): power-controller@1a0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1a0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_09); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_10): power-controller@1a8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1a8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_10); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_11): power-controller@1b0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1b0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_11); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_afnc1_ioa): power-controller@1b8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1b8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc1_ioa); + apple,always-on; /* Apple Fabric */ + power-domains = <&DIE_NODE(ps_afi)>; + }; + + DIE_NODE(ps_afc): power-controller@1d0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1d0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afc); + apple,always-on; /* Apple Fabric, CPU interface is here */ + }; + + DIE_NODE(ps_afnc0_ioa): power-controller@1e8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1e8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc0_ioa); + apple,always-on; /* Apple Fabric */ + power-domains = <&DIE_NODE(ps_afi)>; + }; + + DIE_NODE(ps_afnc1_ls): power-controller@1f0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1f0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc1_ls); + apple,always-on; /* Apple Fabric */ + power-domains = <&DIE_NODE(ps_afnc1_ioa)>; + }; + + DIE_NODE(ps_afnc0_ls): power-controller@1f8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1f8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc0_ls); + apple,always-on; /* Apple Fabric */ + power-domains = <&DIE_NODE(ps_afnc0_ioa)>; + }; + + DIE_NODE(ps_afnc1_lw0): power-controller@200 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x200 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc1_lw0); + apple,always-on; /* Apple Fabric */ + power-domains = <&DIE_NODE(ps_afnc1_ls)>; + }; + + DIE_NODE(ps_afnc1_lw1): power-controller@208 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x208 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc1_lw1); + apple,always-on; /* Apple Fabric */ + power-domains = <&DIE_NODE(ps_afnc1_ls)>; + }; + + DIE_NODE(ps_afnc1_lw2): power-controller@210 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x210 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc1_lw2); + apple,always-on; /* Apple Fabric */ + power-domains = <&DIE_NODE(ps_afnc1_ls)>; + }; + + DIE_NODE(ps_afnc0_lw0): power-controller@218 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x218 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc0_lw0); + apple,always-on; /* Apple Fabric */ + power-domains = <&DIE_NODE(ps_afnc0_ls)>; + }; + + DIE_NODE(ps_scodec): power-controller@220 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x220 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(scodec); + power-domains = <&DIE_NODE(ps_afnc1_lw0)>; + }; + + DIE_NODE(ps_atc0_common): power-controller@228 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x228 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc0_common); + power-domains = <&DIE_NODE(ps_afnc1_lw1)>; + }; + + DIE_NODE(ps_atc1_common): power-controller@230 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x230 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc1_common); + power-domains = <&DIE_NODE(ps_afnc1_lw1)>; + }; + + DIE_NODE(ps_atc2_common): power-controller@238 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x238 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc2_common); + power-domains = <&DIE_NODE(ps_afnc1_lw1)>; + }; + + DIE_NODE(ps_atc3_common): power-controller@240 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x240 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc3_common); + power-domains = <&DIE_NODE(ps_afnc1_lw1)>; + }; + + DIE_NODE(ps_dispext1_sys): power-controller@248 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x248 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dispext1_sys); + power-domains = <&DIE_NODE(ps_afnc1_lw2)>; + }; + + DIE_NODE(ps_pms_bridge): power-controller@250 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x250 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(pms_bridge); + apple,always-on; /* Core device */ + power-domains = <&DIE_NODE(ps_afnc0_lw0)>; + }; + + DIE_NODE(ps_dispext0_sys): power-controller@258 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x258 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dispext0_sys); + power-domains = <&DIE_NODE(ps_afnc0_lw0)>, <&DIE_NODE(ps_afr)>; + }; + + DIE_NODE(ps_ane_sys): power-controller@260 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x260 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(ane_sys); + power-domains = <&DIE_NODE(ps_afnc0_lw0)>; + }; + + DIE_NODE(ps_avd_sys): power-controller@268 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x268 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(avd_sys); + power-domains = <&DIE_NODE(ps_afnc0_lw0)>; + }; + + DIE_NODE(ps_atc0_cio): power-controller@270 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x270 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc0_cio); + power-domains = <&DIE_NODE(ps_atc0_common)>; + }; + + DIE_NODE(ps_atc0_pcie): power-controller@278 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x278 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc0_pcie); + power-domains = <&DIE_NODE(ps_atc0_common)>; + }; + + DIE_NODE(ps_atc1_cio): power-controller@280 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x280 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc1_cio); + power-domains = <&DIE_NODE(ps_atc1_common)>; + }; + + DIE_NODE(ps_atc1_pcie): power-controller@288 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x288 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc1_pcie); + power-domains = <&DIE_NODE(ps_atc1_common)>; + }; + + DIE_NODE(ps_atc2_cio): power-controller@290 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x290 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc2_cio); + power-domains = <&DIE_NODE(ps_atc2_common)>; + }; + + DIE_NODE(ps_atc2_pcie): power-controller@298 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x298 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc2_pcie); + power-domains = <&DIE_NODE(ps_atc2_common)>; + }; + + DIE_NODE(ps_atc3_cio): power-controller@2a0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x2a0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc3_cio); + power-domains = <&DIE_NODE(ps_atc3_common)>; + }; + + DIE_NODE(ps_atc3_pcie): power-controller@2a8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x2a8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc3_pcie); + power-domains = <&DIE_NODE(ps_atc3_common)>; + }; + + DIE_NODE(ps_dispext1_fe): power-controller@2b0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x2b0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dispext1_fe); + power-domains = <&DIE_NODE(ps_dispext1_sys)>; + }; + + DIE_NODE(ps_dispext1_cpu0): power-controller@2b8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x2b8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dispext1_cpu0); + power-domains = <&DIE_NODE(ps_dispext1_fe)>; + }; + + DIE_NODE(ps_dispext0_fe): power-controller@2c0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x2c0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dispext0_fe); + power-domains = <&DIE_NODE(ps_dispext0_sys)>; + }; + +#if DIE_NO == 0 + /* PMP is only present on die 0 of the M1 Ultra */ + DIE_NODE(ps_pmp): power-controller@2c8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x2c8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(pmp); + }; +#endif + + DIE_NODE(ps_pms_sram): power-controller@2d0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x2d0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(pms_sram); + }; + + DIE_NODE(ps_dispext0_cpu0): power-controller@2d8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x2d8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dispext0_cpu0); + power-domains = <&DIE_NODE(ps_dispext0_fe)>; + }; + + DIE_NODE(ps_ane_cpu): power-controller@2e0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x2e0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(ane_cpu); + power-domains = <&DIE_NODE(ps_ane_sys)>; + }; + + DIE_NODE(ps_atc0_cio_pcie): power-controller@2e8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x2e8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc0_cio_pcie); + power-domains = <&DIE_NODE(ps_atc0_cio)>; + }; + + DIE_NODE(ps_atc0_cio_usb): power-controller@2f0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x2f0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc0_cio_usb); + power-domains = <&DIE_NODE(ps_atc0_cio)>; + }; + + DIE_NODE(ps_atc1_cio_pcie): power-controller@2f8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x2f8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc1_cio_pcie); + power-domains = <&DIE_NODE(ps_atc1_cio)>; + }; + + DIE_NODE(ps_atc1_cio_usb): power-controller@300 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x300 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc1_cio_usb); + power-domains = <&DIE_NODE(ps_atc1_cio)>; + }; + + DIE_NODE(ps_atc2_cio_pcie): power-controller@308 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x308 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc2_cio_pcie); + power-domains = <&DIE_NODE(ps_atc2_cio)>; + }; + + DIE_NODE(ps_atc2_cio_usb): power-controller@310 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x310 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc2_cio_usb); + power-domains = <&DIE_NODE(ps_atc2_cio)>; + }; + + DIE_NODE(ps_atc3_cio_pcie): power-controller@318 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x318 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc3_cio_pcie); + power-domains = <&DIE_NODE(ps_atc3_cio)>; + }; + + DIE_NODE(ps_atc3_cio_usb): power-controller@320 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x320 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc3_cio_usb); + power-domains = <&DIE_NODE(ps_atc3_cio)>; + }; + + DIE_NODE(ps_trace_fab): power-controller@390 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x390 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(trace_fab); + }; + + DIE_NODE(ps_ane_sys_mpm): power-controller@4000 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4000 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(ane_sys_mpm); + power-domains = <&DIE_NODE(ps_ane_sys)>; + }; + + DIE_NODE(ps_ane_td): power-controller@4008 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4008 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(ane_td); + power-domains = <&DIE_NODE(ps_ane_sys)>; + }; + + DIE_NODE(ps_ane_base): power-controller@4010 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4010 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(ane_base); + power-domains = <&DIE_NODE(ps_ane_td)>; + }; + + DIE_NODE(ps_ane_set1): power-controller@4018 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4018 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(ane_set1); + power-domains = <&DIE_NODE(ps_ane_base)>; + }; + + DIE_NODE(ps_ane_set2): power-controller@4020 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4020 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(ane_set2); + power-domains = <&DIE_NODE(ps_ane_set1)>; + }; + + DIE_NODE(ps_ane_set3): power-controller@4028 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4028 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(ane_set3); + power-domains = <&DIE_NODE(ps_ane_set2)>; + }; + + DIE_NODE(ps_ane_set4): power-controller@4030 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4030 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(ane_set4); + power-domains = <&DIE_NODE(ps_ane_set3)>; + }; +}; + +&pmgr_south { + DIE_NODE(ps_amcc4): power-controller@100 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x100 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(amcc4); + apple,always-on; + }; + + DIE_NODE(ps_amcc5): power-controller@108 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x108 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(amcc5); + apple,always-on; + }; + + DIE_NODE(ps_amcc6): power-controller@110 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x110 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(amcc6); + apple,always-on; + }; + + DIE_NODE(ps_amcc7): power-controller@118 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x118 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(amcc7); + apple,always-on; + }; + + DIE_NODE(ps_dcs_16): power-controller@120 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x120 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_16); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_17): power-controller@128 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x128 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_17); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_18): power-controller@130 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x130 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_18); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_19): power-controller@138 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x138 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_19); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_20): power-controller@140 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x140 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_20); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_21): power-controller@148 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x148 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_21); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_22): power-controller@150 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x150 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_22); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_23): power-controller@158 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x158 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_23); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_24): power-controller@160 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x160 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_24); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_25): power-controller@168 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x168 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_25); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_26): power-controller@170 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x170 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_26); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_27): power-controller@178 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x178 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_27); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_28): power-controller@180 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x180 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_28); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_29): power-controller@188 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x188 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_29); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_30): power-controller@190 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x190 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_30); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_31): power-controller@198 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x198 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_31); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_afnc4_ioa): power-controller@1a0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1a0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc4_ioa); + apple,always-on; /* Apple Fabric */ + power-domains = <&DIE_NODE(ps_afi)>; + }; + + DIE_NODE(ps_afnc4_ls): power-controller@1a8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1a8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc4_ls); + apple,always-on; /* Apple Fabric */ + power-domains = <&DIE_NODE(ps_afnc4_ioa)>; + }; + + DIE_NODE(ps_afnc4_lw0): power-controller@1b0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1b0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc4_lw0); + apple,always-on; /* Apple Fabric */ + power-domains = <&DIE_NODE(ps_afnc4_ls)>; + }; + + DIE_NODE(ps_afnc5_ioa): power-controller@1b8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1b8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc5_ioa); + apple,always-on; /* Apple Fabric */ + power-domains = <&DIE_NODE(ps_afi)>; + }; + + DIE_NODE(ps_afnc5_ls): power-controller@1c0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1c0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc5_ls); + apple,always-on; /* Apple Fabric */ + power-domains = <&DIE_NODE(ps_afnc5_ioa)>; + }; + + DIE_NODE(ps_afnc5_lw0): power-controller@1c8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1c8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc5_lw0); + apple,always-on; /* Apple Fabric */ + power-domains = <&DIE_NODE(ps_afnc5_ls)>; + }; + + DIE_NODE(ps_dispext2_sys): power-controller@1d0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1d0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dispext2_sys); + }; + + DIE_NODE(ps_msr1): power-controller@1d8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1d8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(msr1); + }; + + DIE_NODE(ps_dispext2_fe): power-controller@1e0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1e0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dispext2_fe); + power-domains = <&DIE_NODE(ps_dispext2_sys)>; + }; + + DIE_NODE(ps_dispext2_cpu0): power-controller@1e8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1e8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dispext2_cpu0); + power-domains = <&DIE_NODE(ps_dispext2_fe)>; + }; + + DIE_NODE(ps_msr1_ase_core): power-controller@1f0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1f0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(msr1_ase_core); + power-domains = <&DIE_NODE(ps_msr1)>; + }; + + DIE_NODE(ps_dispext3_sys): power-controller@220 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x220 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dispext3_sys); + }; + + DIE_NODE(ps_venc1_sys): power-controller@228 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x228 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(venc1_sys); + }; + + DIE_NODE(ps_dispext3_fe): power-controller@230 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x230 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dispext3_fe); + power-domains = <&DIE_NODE(ps_dispext3_sys)>; + }; + + DIE_NODE(ps_dispext3_cpu0): power-controller@238 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x238 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dispext3_cpu0); + power-domains = <&DIE_NODE(ps_dispext3_fe)>; + }; + + DIE_NODE(ps_venc1_dma): power-controller@4000 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4000 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(venc1_dma); + power-domains = <&DIE_NODE(ps_venc1_sys)>; + }; + + DIE_NODE(ps_venc1_pipe4): power-controller@4008 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4008 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(venc1_pipe4); + power-domains = <&DIE_NODE(ps_venc1_dma)>; + }; + + DIE_NODE(ps_venc1_pipe5): power-controller@4010 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4010 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(venc1_pipe5); + power-domains = <&DIE_NODE(ps_venc1_dma)>; + }; + + DIE_NODE(ps_venc1_me0): power-controller@4018 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4018 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(venc1_me0); + power-domains = <&DIE_NODE(ps_venc1_pipe5)>, <&DIE_NODE(ps_venc1_pipe4)>; + }; + + DIE_NODE(ps_venc1_me1): power-controller@4020 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4020 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(venc1_me1); + power-domains = <&DIE_NODE(ps_venc1_me0)>; + }; +}; + +&pmgr_east { + DIE_NODE(ps_clvr_spmi0): power-controller@100 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x100 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(clvr_spmi0); + apple,always-on; /* PCPU voltage regulator interface (used by SMC) */ + }; + + DIE_NODE(ps_clvr_spmi1): power-controller@108 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x108 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(clvr_spmi1); + apple,always-on; /* GPU voltage regulator interface (used by SMC) */ + }; + + DIE_NODE(ps_clvr_spmi2): power-controller@110 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x110 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(clvr_spmi2); + apple,always-on; /* ANE, fabric, AFR voltage regulator interface (used by SMC) */ + }; + + DIE_NODE(ps_clvr_spmi3): power-controller@118 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x118 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(clvr_spmi3); + apple,always-on; /* Additional voltage regulator, probably used on T6021 (SMC) */ + }; + + DIE_NODE(ps_clvr_spmi4): power-controller@120 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x120 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(clvr_spmi4); + apple,always-on; /* Additional voltage regulator, probably used on T6021 (SMC) */ + }; + + DIE_NODE(ps_ispsens0): power-controller@128 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x128 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(ispsens0); + }; + + DIE_NODE(ps_ispsens1): power-controller@130 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x130 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(ispsens1); + }; + + DIE_NODE(ps_ispsens2): power-controller@138 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x138 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(ispsens2); + }; + + DIE_NODE(ps_ispsens3): power-controller@140 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x140 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(ispsens3); + }; + + DIE_NODE(ps_afnc6_ioa): power-controller@148 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x148 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc6_ioa); + apple,always-on; + power-domains = <&DIE_NODE(ps_afi)>; + }; + + DIE_NODE(ps_afnc6_ls): power-controller@150 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x150 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc6_ls); + apple,always-on; + power-domains = <&DIE_NODE(ps_afnc6_ioa)>; + }; + + DIE_NODE(ps_afnc6_lw0): power-controller@158 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x158 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc6_lw0); + apple,always-on; + power-domains = <&DIE_NODE(ps_afnc6_ls)>; + }; + + DIE_NODE(ps_afnc2_ioa): power-controller@160 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x160 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc2_ioa); + apple,always-on; + power-domains = <&DIE_NODE(ps_dcs_10)>; + }; + + DIE_NODE(ps_afnc2_ls): power-controller@168 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x168 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc2_ls); + apple,always-on; + power-domains = <&DIE_NODE(ps_afnc2_ioa)>; + }; + + DIE_NODE(ps_afnc2_lw0): power-controller@170 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x170 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc2_lw0); + apple,always-on; + power-domains = <&DIE_NODE(ps_afnc2_ls)>; + }; + + DIE_NODE(ps_afnc2_lw1): power-controller@178 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x178 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc2_lw1); + apple,always-on; + power-domains = <&DIE_NODE(ps_afnc2_ls)>; + }; + + DIE_NODE(ps_afnc3_ioa): power-controller@180 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x180 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc3_ioa); + apple,always-on; + power-domains = <&DIE_NODE(ps_afi)>; + }; + + DIE_NODE(ps_afnc3_ls): power-controller@188 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x188 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc3_ls); + apple,always-on; + power-domains = <&DIE_NODE(ps_afnc3_ioa)>; + }; + + DIE_NODE(ps_afnc3_lw0): power-controller@190 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x190 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afnc3_lw0); + apple,always-on; + power-domains = <&DIE_NODE(ps_afnc3_ls)>; + }; + + DIE_NODE(ps_apcie_gp): power-controller@198 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x198 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(apcie_gp); + power-domains = <&DIE_NODE(ps_afnc6_lw0)>; + }; + + DIE_NODE(ps_apcie_st): power-controller@1a0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1a0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(apcie_st); + power-domains = <&DIE_NODE(ps_afnc6_lw0)>; + }; + + DIE_NODE(ps_ans2): power-controller@1a8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1a8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(ans2); + power-domains = <&DIE_NODE(ps_afnc6_lw0)>; + }; + + DIE_NODE(ps_disp0_sys): power-controller@1b0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1b0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(disp0_sys); + power-domains = <&DIE_NODE(ps_afnc2_lw0)>; + }; + + DIE_NODE(ps_jpg): power-controller@1b8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1b8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(jpg); + power-domains = <&DIE_NODE(ps_afnc2_lw0)>; + }; + + DIE_NODE(ps_sio): power-controller@1c0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1c0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(sio); + power-domains = <&DIE_NODE(ps_afnc2_lw1)>; + }; + + DIE_NODE(ps_isp_sys): power-controller@1c8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1c8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(isp_sys); + power-domains = <&DIE_NODE(ps_afnc2_lw1)>; + }; + + DIE_NODE(ps_disp0_fe): power-controller@1d0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1d0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(disp0_fe); + power-domains = <&DIE_NODE(ps_disp0_sys)>; + }; + + DIE_NODE(ps_disp0_cpu0): power-controller@1d8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1d8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(disp0_cpu0); + power-domains = <&DIE_NODE(ps_disp0_fe)>; + apple,min-state = <4>; + }; + + DIE_NODE(ps_sio_cpu): power-controller@1e0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1e0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(sio_cpu); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_fpwm0): power-controller@1e8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1e8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(fpwm0); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_fpwm1): power-controller@1f0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1f0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(fpwm1); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_fpwm2): power-controller@1f8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x1f8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(fpwm2); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_i2c0): power-controller@200 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x200 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(i2c0); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_i2c1): power-controller@208 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x208 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(i2c1); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_i2c2): power-controller@210 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x210 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(i2c2); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_i2c3): power-controller@218 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x218 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(i2c3); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_i2c4): power-controller@220 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x220 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(i2c4); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_i2c5): power-controller@228 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x228 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(i2c5); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_i2c6): power-controller@230 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x230 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(i2c6); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_i2c7): power-controller@238 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x238 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(i2c7); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_i2c8): power-controller@240 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x240 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(i2c8); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_spi_p): power-controller@248 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x248 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(spi_p); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_sio_spmi0): power-controller@250 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x250 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(sio_spmi0); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_sio_spmi1): power-controller@258 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x258 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(sio_spmi1); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_sio_spmi2): power-controller@260 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x260 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(sio_spmi2); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_uart_p): power-controller@268 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x268 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(uart_p); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_audio_p): power-controller@270 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x270 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(audio_p); + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_sio_adma): power-controller@278 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x278 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(sio_adma); + power-domains = <&DIE_NODE(ps_audio_p)>, <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_aes): power-controller@280 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x280 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(aes); + apple,always-on; + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_dptx_phy_ps): power-controller@288 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x288 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dptx_phy_ps); + apple,always-on; + power-domains = <&DIE_NODE(ps_sio)>; + }; + + DIE_NODE(ps_spi0): power-controller@2d8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x2d8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(spi0); + power-domains = <&DIE_NODE(ps_spi_p)>; + }; + + DIE_NODE(ps_spi1): power-controller@2e0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x2e0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(spi1); + power-domains = <&DIE_NODE(ps_spi_p)>; + }; + + DIE_NODE(ps_spi2): power-controller@2e8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x2e8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(spi2); + power-domains = <&DIE_NODE(ps_spi_p)>; + }; + + DIE_NODE(ps_spi3): power-controller@2f0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x2f0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(spi3); + power-domains = <&DIE_NODE(ps_spi_p)>; + }; + + DIE_NODE(ps_spi4): power-controller@2f8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x2f8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(spi4); + power-domains = <&DIE_NODE(ps_spi_p)>; + }; + + DIE_NODE(ps_spi5): power-controller@300 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x300 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(spi5); + power-domains = <&DIE_NODE(ps_spi_p)>; + }; + + DIE_NODE(ps_uart_n): power-controller@308 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x308 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(uart_n); + power-domains = <&DIE_NODE(ps_uart_p)>; + }; + + DIE_NODE(ps_uart0): power-controller@310 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x310 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(uart0); + power-domains = <&DIE_NODE(ps_uart_p)>; + }; + + DIE_NODE(ps_amcc1): power-controller@318 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x318 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(amcc1); + apple,always-on; + }; + + DIE_NODE(ps_amcc3): power-controller@320 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x320 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(amcc3); + apple,always-on; + }; + + DIE_NODE(ps_dcs_04): power-controller@328 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x328 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_04); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_05): power-controller@330 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x330 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_05); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_06): power-controller@338 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x338 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_06); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_07): power-controller@340 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x340 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_07); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_12): power-controller@348 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x348 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_12); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_13): power-controller@350 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x350 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_13); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_14): power-controller@358 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x358 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_14); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_dcs_15): power-controller@360 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x360 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dcs_15); + apple,always-on; /* LPDDR5 interface */ + }; + + DIE_NODE(ps_uart1): power-controller@368 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x368 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(uart1); + power-domains = <&DIE_NODE(ps_uart_p)>; + }; + + DIE_NODE(ps_uart2): power-controller@370 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x370 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(uart2); + power-domains = <&DIE_NODE(ps_uart_p)>; + }; + + DIE_NODE(ps_uart3): power-controller@378 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x378 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(uart3); + power-domains = <&DIE_NODE(ps_uart_p)>; + }; + + DIE_NODE(ps_uart4): power-controller@380 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x380 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(uart4); + power-domains = <&DIE_NODE(ps_uart_p)>; + }; + + DIE_NODE(ps_uart5): power-controller@388 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x388 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(uart5); + power-domains = <&DIE_NODE(ps_uart_p)>; + }; + + DIE_NODE(ps_uart6): power-controller@390 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x390 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(uart6); + power-domains = <&DIE_NODE(ps_uart_p)>; + }; + + DIE_NODE(ps_mca0): power-controller@398 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x398 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(mca0); + power-domains = <&DIE_NODE(ps_audio_p)>, <&DIE_NODE(ps_sio_adma)>; + }; + + DIE_NODE(ps_mca1): power-controller@3a0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x3a0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(mca1); + power-domains = <&DIE_NODE(ps_audio_p)>, <&DIE_NODE(ps_sio_adma)>; + }; + + DIE_NODE(ps_mca2): power-controller@3a8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x3a8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(mca2); + power-domains = <&DIE_NODE(ps_audio_p)>, <&DIE_NODE(ps_sio_adma)>; + }; + + DIE_NODE(ps_mca3): power-controller@3b0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x3b0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(mca3); + power-domains = <&DIE_NODE(ps_audio_p)>, <&DIE_NODE(ps_sio_adma)>; + }; + + DIE_NODE(ps_dpa0): power-controller@3b8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x3b8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dpa0); + power-domains = <&DIE_NODE(ps_audio_p)>; + }; + + DIE_NODE(ps_dpa1): power-controller@3c0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x3c0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dpa1); + power-domains = <&DIE_NODE(ps_audio_p)>; + }; + + DIE_NODE(ps_dpa2): power-controller@3c8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x3c8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dpa2); + power-domains = <&DIE_NODE(ps_audio_p)>; + }; + + DIE_NODE(ps_dpa3): power-controller@3d0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x3d0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dpa3); + power-domains = <&DIE_NODE(ps_audio_p)>; + }; + + DIE_NODE(ps_msr0): power-controller@3d8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x3d8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(msr0); + }; + + DIE_NODE(ps_venc_sys): power-controller@3e0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x3e0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(venc_sys); + }; + + DIE_NODE(ps_dpa4): power-controller@3e8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x3e8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dpa4); + power-domains = <&DIE_NODE(ps_audio_p)>; + }; + + DIE_NODE(ps_msr0_ase_core): power-controller@3f0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x3f0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(msr0_ase_core); + power-domains = <&DIE_NODE(ps_msr0)>; + }; + + DIE_NODE(ps_apcie_gpshr_sys): power-controller@3f8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x3f8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(apcie_gpshr_sys); + power-domains = <&DIE_NODE(ps_apcie_gp)>; + }; + + DIE_NODE(ps_apcie_st_sys): power-controller@408 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x408 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(apcie_st_sys); + power-domains = <&DIE_NODE(ps_apcie_st)>, <&DIE_NODE(ps_ans2)>; + }; + + DIE_NODE(ps_apcie_st1_sys): power-controller@410 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x410 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(apcie_st1_sys); + power-domains = <&DIE_NODE(ps_apcie_st_sys)>; + }; + + DIE_NODE(ps_apcie_gp_sys): power-controller@418 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x418 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(apcie_gp_sys); + power-domains = <&DIE_NODE(ps_apcie_gpshr_sys)>; + apple,always-on; /* Breaks things if shut down */ + }; + + DIE_NODE(ps_apcie_ge_sys): power-controller@420 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x420 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(apcie_ge_sys); + power-domains = <&DIE_NODE(ps_apcie_gpshr_sys)>; + }; + + DIE_NODE(ps_apcie_phy_sw): power-controller@428 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x428 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(apcie_phy_sw); + apple,always-on; /* macOS does not turn this off */ + }; + + DIE_NODE(ps_sep): power-controller@c00 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0xc00 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(sep); + apple,always-on; /* Locked on */ + }; + + /* There is a dependency tree involved with these PDs, + * but we do not express it here since the ISP driver + * is supposed to sequence them in the right order anyway. + * + * This also works around spurious parent PD activation + * on machines with ISP disabled (desktops), so we don't + * have to enable/disable everything in the per-model DTs. + */ + DIE_NODE(ps_isp_cpu): power-controller@4000 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4000 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(isp_cpu); + /* power-domains = <&DIE_NODE(ps_isp_sys)>; */ + apple,force-disable; + }; + + DIE_NODE(ps_isp_fe): power-controller@4008 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4008 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(isp_fe); + /* power-domains = <&DIE_NODE(ps_isp_sys)>; */ + }; + + DIE_NODE(ps_dprx): power-controller@4010 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4010 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(dprx); + /* power-domains = <&DIE_NODE(ps_isp_sys)>; */ + }; + + DIE_NODE(ps_isp_vis): power-controller@4018 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4018 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(isp_vis); + /* power-domains = <&DIE_NODE(ps_isp_fe)>; */ + }; + + DIE_NODE(ps_isp_be): power-controller@4020 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4020 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(isp_be); + /* power-domains = <&DIE_NODE(ps_isp_fe)>; */ + }; + + DIE_NODE(ps_isp_raw): power-controller@4028 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4028 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(isp_raw); + /* power-domains = <&DIE_NODE(ps_isp_fe)>; */ + }; + + DIE_NODE(ps_isp_clr): power-controller@4030 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4030 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(isp_clr); + /* power-domains = <&DIE_NODE(ps_isp_be)>; */ + }; + + DIE_NODE(ps_venc_dma): power-controller@8000 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x8000 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(venc_dma); + power-domains = <&DIE_NODE(ps_venc_sys)>; + }; + + DIE_NODE(ps_venc_pipe4): power-controller@8008 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x8008 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(venc_pipe4); + power-domains = <&DIE_NODE(ps_venc_dma)>; + }; + + DIE_NODE(ps_venc_pipe5): power-controller@8010 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x8010 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(venc_pipe5); + power-domains = <&DIE_NODE(ps_venc_dma)>; + }; + + DIE_NODE(ps_venc_me0): power-controller@8018 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x8018 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(venc_me0); + power-domains = <&DIE_NODE(ps_venc_pipe5)>, <&DIE_NODE(ps_venc_pipe4)>; + }; + + DIE_NODE(ps_venc_me1): power-controller@8020 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x8020 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(venc_me1); + power-domains = <&DIE_NODE(ps_venc_me0)>; + }; + + DIE_NODE(ps_prores): power-controller@c000 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0xc000 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(prores); + power-domains = <&DIE_NODE(ps_afnc3_lw0)>; + }; +}; + +&pmgr_mini { + DIE_NODE(ps_debug): power-controller@58 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x58 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(debug); + apple,always-on; /* Core AON device */ + }; + + DIE_NODE(ps_nub_spmi0): power-controller@60 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x60 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(nub_spmi0); + apple,always-on; /* Core AON device */ + }; + + DIE_NODE(ps_nub_spmi1): power-controller@68 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x68 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(nub_spmi1); + apple,always-on; /* Core AON device */ + }; + + DIE_NODE(ps_nub_aon): power-controller@70 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x70 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(nub_aon); + apple,always-on; /* Core AON device */ + }; + + DIE_NODE(ps_msg): power-controller@78 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x78 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(msg); + }; + + DIE_NODE(ps_nub_gpio): power-controller@80 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x80 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(nub_gpio); + apple,always-on; /* Core AON device */ + }; + + DIE_NODE(ps_nub_fabric): power-controller@88 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x88 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(nub_fabric); + apple,always-on; /* Core AON device */ + }; + + DIE_NODE(ps_atc0_usb_aon): power-controller@90 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x90 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc0_usb_aon); + apple,always-on; /* Needs to stay on for dwc3 to work */ + }; + + DIE_NODE(ps_atc1_usb_aon): power-controller@98 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x98 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc1_usb_aon); + apple,always-on; /* Needs to stay on for dwc3 to work */ + }; + + DIE_NODE(ps_atc2_usb_aon): power-controller@a0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0xa0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc2_usb_aon); + apple,always-on; /* Needs to stay on for dwc3 to work */ + }; + + DIE_NODE(ps_atc3_usb_aon): power-controller@a8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0xa8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc3_usb_aon); + apple,always-on; /* Needs to stay on for dwc3 to work */ + }; + + DIE_NODE(ps_mtp_fabric): power-controller@b0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0xb0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(mtp_fabric); + apple,always-on; + power-domains = <&DIE_NODE(ps_nub_fabric)>; + }; + + DIE_NODE(ps_nub_sram): power-controller@b8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0xb8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(nub_sram); + apple,always-on; /* Core AON device */ + }; + + DIE_NODE(ps_debug_switch): power-controller@c0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0xc0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(debug_switch); + apple,always-on; /* Core AON device */ + }; + + DIE_NODE(ps_atc0_usb): power-controller@c8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0xc8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc0_usb); + power-domains = <&DIE_NODE(ps_atc0_common)>; + }; + + DIE_NODE(ps_atc1_usb): power-controller@d0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0xd0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc1_usb); + power-domains = <&DIE_NODE(ps_atc1_common)>; + }; + + DIE_NODE(ps_atc2_usb): power-controller@d8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0xd8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc2_usb); + power-domains = <&DIE_NODE(ps_atc2_common)>; + }; + + DIE_NODE(ps_atc3_usb): power-controller@e0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0xe0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(atc3_usb); + power-domains = <&DIE_NODE(ps_atc3_common)>; + }; + +#if 0 + /* MTP stuff is self-managed */ + DIE_NODE(ps_mtp_gpio): power-controller@e8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0xe8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(mtp_gpio); + apple,always-on; /* MTP always stays on */ + power-domains = <&DIE_NODE(ps_mtp_fabric)>; + }; + + DIE_NODE(ps_mtp_base): power-controller@f0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0xf0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(mtp_base); + apple,always-on; /* MTP always stays on */ + power-domains = <&DIE_NODE(ps_mtp_fabric)>; + }; + + DIE_NODE(ps_mtp_periph): power-controller@f8 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0xf8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(mtp_periph); + apple,always-on; /* MTP always stays on */ + power-domains = <&DIE_NODE(ps_mtp_fabric)>; + }; + + DIE_NODE(ps_mtp_spi0): power-controller@100 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x100 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(mtp_spi0); + apple,always-on; /* MTP always stays on */ + power-domains = <&DIE_NODE(ps_mtp_fabric)>; + }; + + DIE_NODE(ps_mtp_i2cm0): power-controller@108 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x108 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(mtp_i2cm0); + apple,always-on; /* MTP always stays on */ + power-domains = <&DIE_NODE(ps_mtp_fabric)>; + }; + + DIE_NODE(ps_mtp_uart0): power-controller@110 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x110 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(mtp_uart0); + apple,always-on; /* MTP always stays on */ + power-domains = <&DIE_NODE(ps_mtp_fabric)>; + }; + + DIE_NODE(ps_mtp_cpu): power-controller@118 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x118 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(mtp_cpu); + apple,always-on; /* MTP always stays on */ + power-domains = <&DIE_NODE(ps_mtp_fabric)>; + }; + + DIE_NODE(ps_mtp_scm_fabric): power-controller@120 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x120 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(mtp_scm_fabric); + apple,always-on; /* MTP always stays on */ + power-domains = <&DIE_NODE(ps_mtp_periph)>; + }; + + DIE_NODE(ps_mtp_sram): power-controller@128 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x128 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(mtp_sram); + apple,always-on; /* MTP always stays on */ + power-domains = <&DIE_NODE(ps_mtp_scm_fabric)>, <&DIE_NODE(ps_mtp_cpu)>; + }; + + DIE_NODE(ps_mtp_dma): power-controller@130 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x130 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(mtp_dma); + apple,always-on; /* MTP always stays on */ + power-domains = <&DIE_NODE(ps_mtp_sram)>; + }; +#endif +}; + +&pmgr_gfx { + DIE_NODE(ps_gpx): power-controller@0 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(gpx); + apple,always-on; + }; + + DIE_NODE(ps_afr): power-controller@100 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x100 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(afr); + /* Apple Fabric, media stuff: this can power down */ + }; + + DIE_NODE(ps_gfx): power-controller@108 { + compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x108 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = DIE_LABEL(gfx); + power-domains = <&DIE_NODE(ps_afr)>, <&DIE_NODE(ps_gpx)>; + }; +}; + From 25b478bb2d180dcc27249891b40f074b68db4f04 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 10 Apr 2023 18:15:33 +0900 Subject: [PATCH 0070/1020] arm64: dts: apple: Add MTP nodes to t6020x Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t6020-j414s.dts | 4 + arch/arm64/boot/dts/apple/t6020-j416s.dts | 4 + arch/arm64/boot/dts/apple/t6021-j414c.dts | 4 + arch/arm64/boot/dts/apple/t6021-j416c.dts | 4 + arch/arm64/boot/dts/apple/t602x-die0.dtsi | 76 +++++++++++++++++++ .../arm64/boot/dts/apple/t602x-j414-j416.dtsi | 41 ++++++++++ arch/arm64/boot/dts/apple/t602x-pmgr.dtsi | 1 + arch/arm64/boot/dts/apple/t8112.dtsi | 1 + 8 files changed, 135 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6020-j414s.dts b/arch/arm64/boot/dts/apple/t6020-j414s.dts index 18cc67a3076def..5dd97df71efc4b 100644 --- a/arch/arm64/boot/dts/apple/t6020-j414s.dts +++ b/arch/arm64/boot/dts/apple/t6020-j414s.dts @@ -36,3 +36,7 @@ compatible = "apple,j414-macaudio", "apple,j314-macaudio", "apple,macaudio"; model = "MacBook Pro J414"; }; + +&mtp_mt { + firmware-name = "apple/tpmtfw-j414s.bin"; +}; diff --git a/arch/arm64/boot/dts/apple/t6020-j416s.dts b/arch/arm64/boot/dts/apple/t6020-j416s.dts index b9e0973ba37c30..56ddf7c61de634 100644 --- a/arch/arm64/boot/dts/apple/t6020-j416s.dts +++ b/arch/arm64/boot/dts/apple/t6020-j416s.dts @@ -36,3 +36,7 @@ compatible = "apple,j416-macaudio", "apple,j316-macaudio", "apple,macaudio"; model = "MacBook Pro J416"; }; + +&mtp_mt { + firmware-name = "apple/tpmtfw-j416s.bin"; +}; diff --git a/arch/arm64/boot/dts/apple/t6021-j414c.dts b/arch/arm64/boot/dts/apple/t6021-j414c.dts index b173caf0df0fce..6905c7d39db0ce 100644 --- a/arch/arm64/boot/dts/apple/t6021-j414c.dts +++ b/arch/arm64/boot/dts/apple/t6021-j414c.dts @@ -36,3 +36,7 @@ compatible = "apple,j414-macaudio", "apple,j314-macaudio", "apple,macaudio"; model = "MacBook Pro J414"; }; + +&mtp_mt { + firmware-name = "apple/tpmtfw-j414c.bin"; +}; diff --git a/arch/arm64/boot/dts/apple/t6021-j416c.dts b/arch/arm64/boot/dts/apple/t6021-j416c.dts index 36a57890c25d72..331a1e93e7f352 100644 --- a/arch/arm64/boot/dts/apple/t6021-j416c.dts +++ b/arch/arm64/boot/dts/apple/t6021-j416c.dts @@ -52,3 +52,7 @@ compatible = "apple,j416-macaudio", "apple,j316-macaudio", "apple,macaudio"; model = "MacBook Pro J416"; }; + +&mtp_mt { + firmware-name = "apple/tpmtfw-j416c.bin"; +}; diff --git a/arch/arm64/boot/dts/apple/t602x-die0.dtsi b/arch/arm64/boot/dts/apple/t602x-die0.dtsi index 794abdacfb01dc..443326b4f3ffec 100644 --- a/arch/arm64/boot/dts/apple/t602x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-die0.dtsi @@ -176,6 +176,82 @@ ; }; + mtp: mtp@2a9400000 { + compatible = "apple,t6020-mtp", "apple,t6020-rtk-helper-asc4", "apple,mtp", "apple,rtk-helper-asc4"; + reg = <0x2 0xa9400000 0x0 0x4000>, + <0x2 0xa9c00000 0x0 0x100000>; + reg-names = "asc", "sram"; + mboxes = <&mtp_mbox>; + iommus = <&mtp_dart 1>; + #helper-cells = <0>; + + status = "disabled"; + }; + + mtp_mbox: mbox@2a9408000 { + compatible = "apple,t8112-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x2 0xa9408000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + + status = "disabled"; + }; + + mtp_dart: iommu@2a9808000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x2 0xa9808000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #iommu-cells = <1>; + + apple,dma-range = <0x100 0x0 0x1 0x0>; + + status = "disabled"; + }; + + mtp_dockchannel: fifo@2a9b14000 { + compatible = "apple,t6020-dockchannel", "apple,dockchannel"; + reg = <0x2 0xa9b14000 0x0 0x4000>; + reg-names = "irq"; + interrupt-parent = <&aic>; + interrupts = ; + + ranges = <0 0x2 0xa9b28000 0x20000>; + nonposted-mmio; + #address-cells = <1>; + #size-cells = <1>; + + interrupt-controller; + #interrupt-cells = <2>; + + status = "disabled"; + + mtp_hid: input@8000 { + compatible = "apple,dockchannel-hid"; + reg = <0x8000 0x4000>, + <0xc000 0x4000>, + <0x0000 0x4000>, + <0x4000 0x4000>; + reg-names = "config", "data", + "rmt-config", "rmt-data"; + iommus = <&mtp_dart 1>; + interrupt-parent = <&mtp_dockchannel>; + interrupts = <2 IRQ_TYPE_LEVEL_HIGH>, + <3 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "tx", "rx"; + + apple,fifo-size = <0x800>; + apple,helper-cpu = <&mtp>; + }; + + }; + disp0_dart: iommu@389304000 { compatible = "apple,t6020-dart", "apple,t8110-dart"; reg = <0x3 0x89304000 0x0 0x4000>; diff --git a/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi b/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi index c1f45b8114c92d..dd1ea2b5dd95a8 100644 --- a/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi @@ -86,3 +86,44 @@ pwren-gpios = <&smc_gpio 22 GPIO_ACTIVE_HIGH>; }; +&ps_mtp_fabric { + status = "okay"; +}; + +&mtp { + status = "okay"; +}; + +&mtp_mbox { + status = "okay"; +}; + +&mtp_dart { + status = "okay"; +}; + +&mtp_dockchannel { + status = "okay"; +}; + +&mtp_hid { + apple,afe-reset-gpios = <&smc_gpio 25 GPIO_ACTIVE_LOW>; + apple,stm-reset-gpios = <&smc_gpio 26 GPIO_ACTIVE_LOW>; + + mtp_mt: multi-touch { + }; + + keyboard: keyboard { + hid-country-code = <0>; + apple,keyboard-layout-id = <0>; + }; + + stm { + }; + + actuator { + }; + + tp_accel { + }; +}; diff --git a/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi b/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi index 50d79ab1ed1298..facbcc1260f4f9 100644 --- a/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi @@ -2071,6 +2071,7 @@ label = DIE_LABEL(mtp_fabric); apple,always-on; power-domains = <&DIE_NODE(ps_nub_fabric)>; + status = "disabled"; }; DIE_NODE(ps_nub_sram): power-controller@b8 { diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index 9ef423b5fde4c5..e09e35ee96337b 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -1245,6 +1245,7 @@ interrupts = ; ranges = <0 0x2 0x4eb28000 0x20000>; + nonposted-mmio; #address-cells = <1>; #size-cells = <1>; From 961fca5c061ebd8645913b024e08a70a0deb959e Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 11 Apr 2023 02:34:01 +0900 Subject: [PATCH 0071/1020] arm64: dts: apple: Add identity dma-ranges mapping Without this, the OF core ends up limiting all DMA masks to the default 32-bit, since that runs before drivers set up the proper DMA mask. Skipping the highest page because it is impossible to express a full 64-bit range in the DT. Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t6001.dtsi | 2 ++ arch/arm64/boot/dts/apple/t6002.dtsi | 4 ++++ arch/arm64/boot/dts/apple/t6021.dtsi | 2 ++ arch/arm64/boot/dts/apple/t8103.dtsi | 2 ++ arch/arm64/boot/dts/apple/t8112.dtsi | 2 ++ 5 files changed, 12 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6001.dtsi b/arch/arm64/boot/dts/apple/t6001.dtsi index 6e7e7cdeacf943..316deb8a95be63 100644 --- a/arch/arm64/boot/dts/apple/t6001.dtsi +++ b/arch/arm64/boot/dts/apple/t6001.dtsi @@ -50,6 +50,8 @@ ranges; nonposted-mmio; + /* Required to get >32-bit DMA via DARTs */ + dma-ranges = <0 0 0 0 0xffffffff 0xffffc000>; // filled via templated includes at the end of the file }; diff --git a/arch/arm64/boot/dts/apple/t6002.dtsi b/arch/arm64/boot/dts/apple/t6002.dtsi index f1164315be755a..a7dfc6196fa724 100644 --- a/arch/arm64/boot/dts/apple/t6002.dtsi +++ b/arch/arm64/boot/dts/apple/t6002.dtsi @@ -240,6 +240,8 @@ <0x5 0x80000000 0x5 0x80000000 0x1 0x80000000>, <0x7 0x0 0x7 0x0 0xf 0x80000000>; nonposted-mmio; + /* Required to get >32-bit DMA via DARTs */ + dma-ranges = <0 0 0 0 0xffffffff 0xffffc000>; // filled via templated includes at the end of the file }; @@ -251,6 +253,8 @@ ranges = <0x2 0x0 0x22 0x0 0x4 0x0>, <0x7 0x0 0x27 0x0 0xf 0x80000000>; nonposted-mmio; + /* Required to get >32-bit DMA via DARTs */ + dma-ranges = <0 0 0 0 0xffffffff 0xffffc000>; // filled via templated includes at the end of the file }; diff --git a/arch/arm64/boot/dts/apple/t6021.dtsi b/arch/arm64/boot/dts/apple/t6021.dtsi index d907c4753f67dd..ec3cfde14722a6 100644 --- a/arch/arm64/boot/dts/apple/t6021.dtsi +++ b/arch/arm64/boot/dts/apple/t6021.dtsi @@ -50,6 +50,8 @@ ranges; nonposted-mmio; + /* Required to get >32-bit DMA via DARTs */ + dma-ranges = <0 0 0 0 0xffffffff 0xffffc000>; // filled via templated includes at the end of the file }; diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index 65e532c8198841..a7cdb3ae1ad11d 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -436,6 +436,8 @@ ranges; nonposted-mmio; + /* Required to get >32-bit DMA via DARTs */ + dma-ranges = <0 0 0 0 0xffffffff 0xffffc000>; gpu: gpu@206400000 { compatible = "apple,agx-t8103", "apple,agx-g13g"; diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index e09e35ee96337b..41eafdeddd9007 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -461,6 +461,8 @@ ranges; nonposted-mmio; + /* Required to get >32-bit DMA via DARTs */ + dma-ranges = <0 0 0 0 0xffffffff 0xffffc000>; gpu: gpu@206400000 { compatible = "apple,agx-t8112", "apple,agx-g14g"; From b237f3c652938054ad03774cde9780fa35efc32a Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 11 Apr 2023 21:39:50 +0200 Subject: [PATCH 0072/1020] DO NOT SUBMIT: arm64: dts: apple: t6020-j474s: Add unused PCIe port01 This works around a Linux bug which results in mismatched iommus on gaps in PCI(e) ports / bus numbers. Remove as soon as the bug is identified. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t6020-j474s.dts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6020-j474s.dts b/arch/arm64/boot/dts/apple/t6020-j474s.dts index 9b61a7bb9d6ce4..653af803551c76 100644 --- a/arch/arm64/boot/dts/apple/t6020-j474s.dts +++ b/arch/arm64/boot/dts/apple/t6020-j474s.dts @@ -60,6 +60,16 @@ pwren-gpios = <&smc_gpio 13 GPIO_ACTIVE_HIGH>; }; +&port01 { + /* + * TODO: do not enable port without device. This works around a Linux + * bug which results in mismatched iommus on gaps in PCI(e) ports / bus + * numbers. + */ + bus-range = <2 2>; + status = "okay"; +}; + &port02 { /* 10 Gbit Ethernet */ bus-range = <3 3>; From 5100f127f2a28068a3a61119b5f0d0e01b35b2c7 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 18 Apr 2023 05:05:57 +0900 Subject: [PATCH 0073/1020] arm64: dts: apple: Add pmgr-misc nodes to t60xx --- arch/arm64/boot/dts/apple/t600x-die0.dtsi | 10 ++++++++++ arch/arm64/boot/dts/apple/t602x-die0.dtsi | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index 3c991c9bdc529a..6a43db684ed461 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -24,6 +24,16 @@ power-domains = <&ps_aic>; }; + pmgr_misc: power-management@28e20c000 { + compatible = "apple,t6000-pmgr-misc"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x2 0x8e20c000 0 0x400>, + <0x2 0x8e20c800 0 0x400>; + reg-names = "fabric-ps", "dcs-ps"; + apple,dcs-min-ps = <7>; + }; + pmgr_dcp: power-management@28e3d0000 { reg = <0x2 0x8e3d0000 0x0 0x4000>; reg-names = "dcp-fw-pmgr"; diff --git a/arch/arm64/boot/dts/apple/t602x-die0.dtsi b/arch/arm64/boot/dts/apple/t602x-die0.dtsi index 443326b4f3ffec..71dd21c5136f54 100644 --- a/arch/arm64/boot/dts/apple/t602x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-die0.dtsi @@ -25,6 +25,15 @@ power-domains = <&ps_aic>; }; + pmgr_misc: power-management@28e20c000 { + compatible = "apple,t6020-pmgr-misc", "apple,t6000-pmgr-misc"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x2 0x8e20c000 0 0x400>, + <0x2 0x8e20c400 0 0x400>; + reg-names = "fabric-ps", "dcs-ps"; + }; + pmgr_dcp: power-management@28e3d0000 { reg = <0x2 0x8e3d0000 0x0 0x4000>; reg-names = "dcp-fw-pmgr"; From bd3a34ab98c57a41b0881bbcddc25938f1bbc47a Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 24 Apr 2023 23:27:52 +0900 Subject: [PATCH 0074/1020] arm64: dts: apple: t600x: Remove obsolete comment in ans2 power domain Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t600x-pmgr.dtsi | 6 ------ 1 file changed, 6 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi b/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi index 00b317c2355b8c..0555ba2eb3918c 100644 --- a/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi @@ -1383,12 +1383,6 @@ #power-domain-cells = <0>; #reset-cells = <0>; label = DIE_LABEL(ans2); - /* - * The ADT makes ps_apcie_st[1]_sys depend on ps_ans2 instead, - * but we'd rather have a single power domain for the downstream - * device to depend on, so use this node as the child. - * This makes more sense anyway (since ANS2 uses APCIE_ST). - */ power-domains = <&DIE_NODE(ps_afnc2_lw0)>; }; From 3980ced1fa2b791ad13bef9e35b49384ddfeed9a Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Wed, 26 Apr 2023 02:17:26 +0900 Subject: [PATCH 0075/1020] arm64: dts: apple: Make ps_msg always-on Apple has it that way, and it might be important. Let's not risk it. Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t600x-pmgr.dtsi | 1 + arch/arm64/boot/dts/apple/t602x-pmgr.dtsi | 1 + arch/arm64/boot/dts/apple/t8103-pmgr.dtsi | 1 + arch/arm64/boot/dts/apple/t8112-pmgr.dtsi | 1 + 4 files changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi b/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi index 0555ba2eb3918c..af3baf871b22ee 100644 --- a/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi @@ -1873,6 +1873,7 @@ #power-domain-cells = <0>; #reset-cells = <0>; label = DIE_LABEL(msg); + apple,always-on; /* Core AON device? */ }; DIE_NODE(ps_nub_gpio): power-controller@80 { diff --git a/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi b/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi index facbcc1260f4f9..33641648f2ae02 100644 --- a/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi @@ -2007,6 +2007,7 @@ #power-domain-cells = <0>; #reset-cells = <0>; label = DIE_LABEL(msg); + apple,always-on; /* Core AON device? */ }; DIE_NODE(ps_nub_gpio): power-controller@80 { diff --git a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi index 724e7fd559e7a1..5fb8c8601a9dcb 100644 --- a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi @@ -1095,6 +1095,7 @@ #power-domain-cells = <0>; #reset-cells = <0>; label = "msg"; + apple,always-on; /* Core AON device? */ }; ps_atc0_usb_aon: power-controller@88 { diff --git a/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi index d1711155f84686..3828a1333dacae 100644 --- a/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi @@ -1061,6 +1061,7 @@ #power-domain-cells = <0>; #reset-cells = <0>; label = "msg"; + apple,always-on; /* Core AON device? */ }; ps_nub_gpio: power-controller@80 { From 5b7141a51dd7aea06d0e78c2d9843454bb9ca3a7 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 27 Apr 2023 13:53:35 +0900 Subject: [PATCH 0076/1020] arm64: dts: apple: t600x: Enable turbo CPU p-states These should work now that we have cpuidle. Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t600x-common.dtsi | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t600x-common.dtsi b/arch/arm64/boot/dts/apple/t600x-common.dtsi index 279ba91d8abacd..667c02724b8646 100644 --- a/arch/arm64/boot/dts/apple/t600x-common.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-common.dtsi @@ -315,7 +315,6 @@ opp-level = <12>; clock-latency-ns = <56000>; }; - /* Not available until CPU deep sleep is implemented opp13 { opp-hz = /bits/ 64 <3132000000>; opp-level = <13>; @@ -334,7 +333,6 @@ clock-latency-ns = <56000>; turbo-mode; }; - */ }; gpu_opp: opp-table-gpu { From e73f327bbc94013d315f13ad3376c2cd961ea021 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 27 Apr 2023 13:54:17 +0900 Subject: [PATCH 0077/1020] arm64: dts: apple: t8103: Enable turbo CPU p-states These should work now that we have cpuidle. Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8103.dtsi | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index a7cdb3ae1ad11d..130e240f59a08c 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -280,7 +280,6 @@ opp-level = <12>; clock-latency-ns = <55000>; }; -#if 0 /* Not available until CPU deep sleep is implemented */ opp13 { opp-hz = /bits/ 64 <3096000000>; @@ -300,7 +299,6 @@ clock-latency-ns = <56000>; turbo-mode; }; -#endif }; gpu_opp: opp-table-gpu { From fbdadc9db3410f1f5991552ce39f2a98c18dd59e Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 27 Apr 2023 13:54:30 +0900 Subject: [PATCH 0078/1020] arm64: dts: apple: t8112: Enable turbo CPU p-states These should work now that we have cpuidle. Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8112.dtsi | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index 41eafdeddd9007..cabf3a23104afc 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -302,8 +302,6 @@ opp-level = <14>; clock-latency-ns = <46000>; }; - /* Not available until CPU deep sleep is implemented */ -#if 0 opp15 { opp-hz = /bits/ 64 <3324000000>; opp-level = <15>; @@ -322,7 +320,6 @@ clock-latency-ns = <62000>; turbo-mode; }; -#endif }; gpu_opp: opp-table-gpu { From 97cc0bb0bcbdf384e0f90be97181515dc23fb71f Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Wed, 19 Apr 2023 21:28:29 +0900 Subject: [PATCH 0079/1020] arm64: dts: apple: Add T602x GPU node Signed-off-by: Asahi Lina --- arch/arm64/boot/dts/apple/t6020.dtsi | 7 ++ arch/arm64/boot/dts/apple/t6021.dtsi | 10 +++ arch/arm64/boot/dts/apple/t602x-common.dtsi | 78 +++++++++++++++++++++ arch/arm64/boot/dts/apple/t602x-die0.dtsi | 45 +++++++++++- 4 files changed, 139 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/apple/t6020.dtsi b/arch/arm64/boot/dts/apple/t6020.dtsi index 3a864ebd91bb2f..77affcd3aa0d1c 100644 --- a/arch/arm64/boot/dts/apple/t6020.dtsi +++ b/arch/arm64/boot/dts/apple/t6020.dtsi @@ -21,4 +21,11 @@ &gpu { compatible = "apple,agx-t6020", "apple,agx-g14x"; + + apple,avg-power-filter-tc-ms = <302>; + apple,avg-power-ki-only = <2.6375>; + apple,avg-power-kp = <0.18>; + apple,fast-die0-integral-gain = <1350.0>; + apple,ppm-filter-time-constant-ms = <32>; + apple,ppm-ki = <28.0>; }; diff --git a/arch/arm64/boot/dts/apple/t6021.dtsi b/arch/arm64/boot/dts/apple/t6021.dtsi index ec3cfde14722a6..102f2915b9e8ad 100644 --- a/arch/arm64/boot/dts/apple/t6021.dtsi +++ b/arch/arm64/boot/dts/apple/t6021.dtsi @@ -19,6 +19,9 @@ #ifndef GPU_REPEAT # define GPU_REPEAT(x) #endif +#ifndef GPU_DIE_REPEAT +# define GPU_DIE_REPEAT(x) +#endif #include "t602x-common.dtsi" @@ -90,4 +93,11 @@ &gpu { compatible = "apple,agx-t6021", "apple,agx-g14x"; + + apple,avg-power-filter-tc-ms = <300>; + apple,avg-power-ki-only = <1.5125>; + apple,avg-power-kp = <0.38>; + apple,fast-die0-integral-gain = <700.0>; + apple,ppm-filter-time-constant-ms = <34>; + apple,ppm-ki = <18.0>; }; diff --git a/arch/arm64/boot/dts/apple/t602x-common.dtsi b/arch/arm64/boot/dts/apple/t602x-common.dtsi index 1224e4f12a5347..80ba81c47a3424 100644 --- a/arch/arm64/boot/dts/apple/t602x-common.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-common.dtsi @@ -9,6 +9,10 @@ #address-cells = <2>; #size-cells = <2>; + aliases { + gpu = &gpu; + }; + cpus { #address-cells = <2>; #size-cells = <0>; @@ -434,6 +438,80 @@ }; }; + gpu_cs_opp: opp-table-gpu-cs { + compatible = "operating-points-v2"; + + /* + * NOTE: The voltage and power values are device-specific and + * must be filled in by the bootloader. + */ + opp00 { + opp-hz = /bits/ 64 <24>; + opp-microvolt = GPU_DIE_REPEAT(668000); + }; + opp01 { + opp-hz = /bits/ 64 <444000000>; + opp-microvolt = GPU_DIE_REPEAT(668000); + }; + opp02 { + opp-hz = /bits/ 64 <612000000>; + opp-microvolt = GPU_DIE_REPEAT(678000); + }; + opp03 { + opp-hz = /bits/ 64 <808000000>; + opp-microvolt = GPU_DIE_REPEAT(737000); + }; + opp04 { + opp-hz = /bits/ 64 <1024000000>; + opp-microvolt = GPU_DIE_REPEAT(815000); + }; + opp05 { + opp-hz = /bits/ 64 <1140000000>; + opp-microvolt = GPU_DIE_REPEAT(862000); + }; + opp06 { + opp-hz = /bits/ 64 <1236000000>; + opp-microvolt = GPU_DIE_REPEAT(893000); + }; + }; + + gpu_afr_opp: opp-table-gpu-afr { + compatible = "operating-points-v2"; + + /* + * NOTE: The voltage and power values are device-specific and + * must be filled in by the bootloader. + */ + opp00 { + opp-hz = /bits/ 64 <24>; + opp-microvolt = GPU_DIE_REPEAT(668000); + }; + opp01 { + opp-hz = /bits/ 64 <400000000>; + opp-microvolt = GPU_DIE_REPEAT(668000); + }; + opp02 { + opp-hz = /bits/ 64 <552000000>; + opp-microvolt = GPU_DIE_REPEAT(678000); + }; + opp03 { + opp-hz = /bits/ 64 <760000000>; + opp-microvolt = GPU_DIE_REPEAT(737000); + }; + opp04 { + opp-hz = /bits/ 64 <980000000>; + opp-microvolt = GPU_DIE_REPEAT(815000); + }; + opp05 { + opp-hz = /bits/ 64 <1098000000>; + opp-microvolt = GPU_DIE_REPEAT(862000); + }; + opp06 { + opp-hz = /bits/ 64 <1200000000>; + opp-microvolt = GPU_DIE_REPEAT(893000); + }; + }; + pmu-e { compatible = "apple,blizzard-pmu"; interrupt-parent = <&aic>; diff --git a/arch/arm64/boot/dts/apple/t602x-die0.dtsi b/arch/arm64/boot/dts/apple/t602x-die0.dtsi index 71dd21c5136f54..6bccfb60e50b3b 100644 --- a/arch/arm64/boot/dts/apple/t602x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-die0.dtsi @@ -596,8 +596,51 @@ apple,firmware-compat = <0 0 0>; operating-points-v2 = <&gpu_opp>; - /* TODO perf stuff */ + apple,cs-opp = <&gpu_cs_opp>; + apple,afr-opp = <&gpu_afr_opp>; + + apple,min-sram-microvolt = <790000>; + apple,csafr-min-sram-microvolt = <812000>; apple,perf-base-pstate = <1>; + + apple,avg-power-min-duty-cycle = <40>; + apple,avg-power-target-filter-tc = <1>; + apple,fast-die0-proportional-gain = <34.0>; + apple,perf-boost-ce-step = <50>; + apple,perf-boost-min-util = <90>; + apple,perf-filter-drop-threshold = <0>; + apple,perf-filter-time-constant = <5>; + apple,perf-filter-time-constant2 = <200>; + apple,perf-integral-gain = <1.62>; + apple,perf-integral-gain2 = <1.62>; + apple,perf-integral-min-clamp = <0>; + apple,perf-proportional-gain2 = <5.4>; + apple,perf-proportional-gain = <5.4>; + apple,perf-tgt-utilization = <85>; + apple,power-sample-period = <8>; + apple,ppm-filter-time-constant-ms = <34>; + apple,ppm-ki = <18.0>; + apple,ppm-kp = <0.1>; + apple,pwr-filter-time-constant = <313>; + apple,pwr-integral-gain = <0.0202129>; + apple,pwr-integral-min-clamp = <0>; + apple,pwr-min-duty-cycle = <40>; + apple,pwr-proportional-gain = <5.2831855>; + apple,pwr-sample-period-aic-clks = <200000>; + apple,se-engagement-criteria = <700>; + apple,se-filter-time-constant = <9>; + apple,se-filter-time-constant-1 = <3>; + apple,se-inactive-threshold = <2500>; + apple,se-ki = <-50.0>; + apple,se-ki-1 = <-100.0>; + apple,se-kp = <-5.0>; + apple,se-kp-1 = <-10.0>; + apple,se-reset-criteria = <50>; + + apple,core-leak-coef = GPU_REPEAT(1200.0); + apple,sram-leak-coef = GPU_REPEAT(20.0); + apple,cs-leak-coef = GPU_DIE_REPEAT(400.0); + apple,afr-leak-coef = GPU_DIE_REPEAT(200.0); }; agx_mbox: mbox@406408000 { From 6acf017d4fd3a9ef13db66b6d1bd309bbb0ec013 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 11 Jul 2023 13:47:13 +0900 Subject: [PATCH 0080/1020] arm64: dts: apple: t600x-j375.dtsi: Add missing etherhet0 alias Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t600x-j375.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi index 0564c8cae687ab..c31b9798d2617c 100644 --- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi @@ -19,6 +19,7 @@ dcp = &dcp; disp0 = &display; disp0_piodma = &disp0_piodma; + ethernet0 = ðernet0; serial0 = &serial0; wifi0 = &wifi0; }; From 1778f8c71337f53ab04d7bb799ec3c299a9e6565 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 7 Aug 2023 18:08:35 +0900 Subject: [PATCH 0081/1020] arm64: dts: apple: Add initial t6022 support Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/Makefile | 1 + arch/arm64/boot/dts/apple/t6021.dtsi | 18 - arch/arm64/boot/dts/apple/t6022.dtsi | 360 ++++++++++++++++++++ arch/arm64/boot/dts/apple/t602x-common.dtsi | 21 ++ arch/arm64/boot/dts/apple/t602x-die0.dtsi | 8 - arch/arm64/boot/dts/apple/t602x-dieX.dtsi | 8 + arch/arm64/boot/dts/apple/t602x-pmgr.dtsi | 12 +- 7 files changed, 397 insertions(+), 31 deletions(-) create mode 100644 arch/arm64/boot/dts/apple/t6022.dtsi diff --git a/arch/arm64/boot/dts/apple/Makefile b/arch/arm64/boot/dts/apple/Makefile index e3e62c672d53b7..a55d7097b7fbbb 100644 --- a/arch/arm64/boot/dts/apple/Makefile +++ b/arch/arm64/boot/dts/apple/Makefile @@ -18,3 +18,4 @@ dtb-$(CONFIG_ARCH_APPLE) += t6021-j414c.dtb dtb-$(CONFIG_ARCH_APPLE) += t6020-j416s.dtb dtb-$(CONFIG_ARCH_APPLE) += t6021-j416c.dtb dtb-$(CONFIG_ARCH_APPLE) += t6020-j474s.dtb +dtb-$(CONFIG_ARCH_APPLE) += t6022-j180d.dtb diff --git a/arch/arm64/boot/dts/apple/t6021.dtsi b/arch/arm64/boot/dts/apple/t6021.dtsi index 102f2915b9e8ad..95298973624f1d 100644 --- a/arch/arm64/boot/dts/apple/t6021.dtsi +++ b/arch/arm64/boot/dts/apple/t6021.dtsi @@ -28,24 +28,6 @@ / { compatible = "apple,t6001", "apple,arm-platform"; - reserved-memory { - #address-cells = <2>; - #size-cells = <2>; - ranges; - - uat_handoff: uat-handoff { - reg = <0 0 0 0>; - }; - - uat_pagetables: uat-pagetables { - reg = <0 0 0 0>; - }; - - uat_ttbs: uat-ttbs { - reg = <0 0 0 0>; - }; - }; - soc { compatible = "simple-bus"; #address-cells = <2>; diff --git a/arch/arm64/boot/dts/apple/t6022.dtsi b/arch/arm64/boot/dts/apple/t6022.dtsi new file mode 100644 index 00000000000000..b7c19be04c72a3 --- /dev/null +++ b/arch/arm64/boot/dts/apple/t6022.dtsi @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * Apple T6022 "M2 Ultra" SoC + * + * Other names: H14J, "Rhodes 2C" + * + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include + +#include "multi-die-cpp.h" + +#ifndef GPU_REPEAT +# define GPU_REPEAT(x) +#endif +#ifndef GPU_DIE_REPEAT +# define GPU_DIE_REPEAT(x) +#endif + +#include "t602x-common.dtsi" + +/ { + compatible = "apple,t6022", "apple,arm-platform"; + + #address-cells = <2>; + #size-cells = <2>; + + cpus { + cpu-map { + cluster3 { + core0 { + cpu = <&cpu_e10>; + }; + core1 { + cpu = <&cpu_e11>; + }; + core2 { + cpu = <&cpu_e12>; + }; + core3 { + cpu = <&cpu_e13>; + }; + }; + + cluster4 { + core0 { + cpu = <&cpu_p20>; + }; + core1 { + cpu = <&cpu_p21>; + }; + core2 { + cpu = <&cpu_p22>; + }; + core3 { + cpu = <&cpu_p23>; + }; + }; + + cluster5 { + core0 { + cpu = <&cpu_p30>; + }; + core1 { + cpu = <&cpu_p31>; + }; + core2 { + cpu = <&cpu_p32>; + }; + core3 { + cpu = <&cpu_p33>; + }; + }; + }; + + cpu_e10: cpu@800 { + compatible = "apple,blizzard"; + device_type = "cpu"; + reg = <0x0 0x800>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* to be filled by loader */ + next-level-cache = <&l2_cache_3>; + i-cache-size = <0x20000>; + d-cache-size = <0x10000>; + operating-points-v2 = <&blizzard_opp>; + capacity-dmips-mhz = <756>; + performance-domains = <&cpufreq_e_die1>; + }; + + cpu_e11: cpu@801 { + compatible = "apple,blizzard"; + device_type = "cpu"; + reg = <0x0 0x801>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* to be filled by loader */ + next-level-cache = <&l2_cache_3>; + i-cache-size = <0x20000>; + d-cache-size = <0x10000>; + operating-points-v2 = <&blizzard_opp>; + capacity-dmips-mhz = <756>; + performance-domains = <&cpufreq_e_die1>; + }; + + cpu_e12: cpu@802 { + compatible = "apple,blizzard"; + device_type = "cpu"; + reg = <0x0 0x802>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* to be filled by loader */ + next-level-cache = <&l2_cache_3>; + i-cache-size = <0x20000>; + d-cache-size = <0x10000>; + operating-points-v2 = <&blizzard_opp>; + capacity-dmips-mhz = <756>; + performance-domains = <&cpufreq_e_die1>; + }; + + cpu_e13: cpu@803 { + compatible = "apple,blizzard"; + device_type = "cpu"; + reg = <0x0 0x803>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* to be filled by loader */ + next-level-cache = <&l2_cache_3>; + i-cache-size = <0x20000>; + d-cache-size = <0x10000>; + operating-points-v2 = <&blizzard_opp>; + capacity-dmips-mhz = <756>; + performance-domains = <&cpufreq_e_die1>; + }; + + cpu_p20: cpu@10900 { + compatible = "apple,avalanche"; + device_type = "cpu"; + reg = <0x0 0x10900>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* To be filled by loader */ + next-level-cache = <&l2_cache_4>; + i-cache-size = <0x30000>; + d-cache-size = <0x20000>; + operating-points-v2 = <&avalanche_opp>; + capacity-dmips-mhz = <1024>; + performance-domains = <&cpufreq_p0_die1>; + }; + + cpu_p21: cpu@10901 { + compatible = "apple,avalanche"; + device_type = "cpu"; + reg = <0x0 0x10901>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* To be filled by loader */ + next-level-cache = <&l2_cache_4>; + i-cache-size = <0x30000>; + d-cache-size = <0x20000>; + operating-points-v2 = <&avalanche_opp>; + capacity-dmips-mhz = <1024>; + performance-domains = <&cpufreq_p0_die1>; + }; + + cpu_p22: cpu@10902 { + compatible = "apple,avalanche"; + device_type = "cpu"; + reg = <0x0 0x10902>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* To be filled by loader */ + next-level-cache = <&l2_cache_4>; + i-cache-size = <0x30000>; + d-cache-size = <0x20000>; + operating-points-v2 = <&avalanche_opp>; + capacity-dmips-mhz = <1024>; + performance-domains = <&cpufreq_p0_die1>; + }; + + cpu_p23: cpu@10903 { + compatible = "apple,avalanche"; + device_type = "cpu"; + reg = <0x0 0x10903>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* To be filled by loader */ + next-level-cache = <&l2_cache_4>; + i-cache-size = <0x30000>; + d-cache-size = <0x20000>; + operating-points-v2 = <&avalanche_opp>; + capacity-dmips-mhz = <1024>; + performance-domains = <&cpufreq_p0_die1>; + }; + + cpu_p30: cpu@10a00 { + compatible = "apple,avalanche"; + device_type = "cpu"; + reg = <0x0 0x10a00>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* To be filled by loader */ + next-level-cache = <&l2_cache_5>; + i-cache-size = <0x30000>; + d-cache-size = <0x20000>; + operating-points-v2 = <&avalanche_opp>; + capacity-dmips-mhz = <1024>; + performance-domains = <&cpufreq_p1_die1>; + }; + + cpu_p31: cpu@10a01 { + compatible = "apple,avalanche"; + device_type = "cpu"; + reg = <0x0 0x10a01>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* To be filled by loader */ + next-level-cache = <&l2_cache_5>; + i-cache-size = <0x30000>; + d-cache-size = <0x20000>; + operating-points-v2 = <&avalanche_opp>; + capacity-dmips-mhz = <1024>; + performance-domains = <&cpufreq_p1_die1>; + }; + + cpu_p32: cpu@10a02 { + compatible = "apple,avalanche"; + device_type = "cpu"; + reg = <0x0 0x10a02>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* To be filled by loader */ + next-level-cache = <&l2_cache_5>; + i-cache-size = <0x30000>; + d-cache-size = <0x20000>; + operating-points-v2 = <&avalanche_opp>; + capacity-dmips-mhz = <1024>; + performance-domains = <&cpufreq_p1_die1>; + }; + + cpu_p33: cpu@10a03 { + compatible = "apple,avalanche"; + device_type = "cpu"; + reg = <0x0 0x10a03>; + enable-method = "spin-table"; + cpu-release-addr = <0 0>; /* To be filled by loader */ + next-level-cache = <&l2_cache_5>; + i-cache-size = <0x30000>; + d-cache-size = <0x20000>; + operating-points-v2 = <&avalanche_opp>; + capacity-dmips-mhz = <1024>; + performance-domains = <&cpufreq_p1_die1>; + }; + + l2_cache_3: l2-cache-3 { + compatible = "cache"; + cache-level = <2>; + cache-unified; + cache-size = <0x400000>; + }; + + l2_cache_4: l2-cache-4 { + compatible = "cache"; + cache-level = <2>; + cache-unified; + cache-size = <0x1000000>; + }; + + l2_cache_5: l2-cache-5 { + compatible = "cache"; + cache-level = <2>; + cache-unified; + cache-size = <0x1000000>; + }; + }; + + die0: soc@200000000 { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x2 0x0 0x2 0x0 0x4 0x0>, + <0x5 0x80000000 0x5 0x80000000 0x1 0x80000000>, + <0x7 0x0 0x7 0x0 0xf 0x80000000>; + nonposted-mmio; + /* Required to get >32-bit DMA via DARTs */ + dma-ranges = <0 0 0 0 0xffffffff 0xffffc000>; + + // filled via templated includes at the end of the file + }; + + die1: soc@2200000000 { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x2 0x0 0x22 0x0 0x4 0x0>, + <0x7 0x0 0x27 0x0 0xf 0x80000000>; + nonposted-mmio; + /* Required to get >32-bit DMA via DARTs */ + dma-ranges = <0 0 0 0 0xffffffff 0xffffc000>; + + // filled via templated includes at the end of the file + }; +}; + +#define DIE +#define DIE_NO 0 + +&die0 { + #include "t602x-die0.dtsi" + #include "t602x-dieX.dtsi" +}; + +#include "t602x-pmgr.dtsi" +#include "t602x-gpio-pins.dtsi" + +#undef DIE +#undef DIE_NO + +#define DIE _die1 +#define DIE_NO 1 + +&die1 { + #include "t602x-dieX.dtsi" + #include "t602x-nvme.dtsi" +}; + +#include "t602x-pmgr.dtsi" + +#undef DIE +#undef DIE_NO + +&aic { + affinities { + e-core-pmu-affinity { + apple,fiq-index = ; + cpus = <&cpu_e00 &cpu_e01 &cpu_e02 &cpu_e03 + &cpu_e10 &cpu_e11 &cpu_e12 &cpu_e13>; + }; + + p-core-pmu-affinity { + apple,fiq-index = ; + cpus = <&cpu_p00 &cpu_p01 &cpu_p02 &cpu_p03 + &cpu_p10 &cpu_p11 &cpu_p12 &cpu_p13 + &cpu_p20 &cpu_p21 &cpu_p22 &cpu_p23 + &cpu_p30 &cpu_p31 &cpu_p32 &cpu_p33>; + }; + }; +}; + +&ps_gfx { + // On t6022, the die0 GPU power domain needs both AFR power domains + power-domains = <&ps_afr>, <&ps_afr_die1>; +}; + +&gpu { + compatible = "apple,agx-t6022", "apple,agx-g14x"; + + apple,avg-power-filter-tc-ms = <302>; + apple,avg-power-ki-only = <1.0125>; + apple,avg-power-kp = <0.15>; + apple,fast-die0-integral-gain = <9.6>; + apple,fast-die0-proportional-gain = <24.0>; + apple,ppm-ki = <11.0>; + apple,ppm-kp = <0.15>; +}; diff --git a/arch/arm64/boot/dts/apple/t602x-common.dtsi b/arch/arm64/boot/dts/apple/t602x-common.dtsi index 80ba81c47a3424..79a2afc1b39268 100644 --- a/arch/arm64/boot/dts/apple/t602x-common.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-common.dtsi @@ -564,4 +564,25 @@ #clock-cells = <0>; clock-output-names = "nco_ref"; }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + uat_handoff: uat-handoff { + reg = <0 0 0 0>; + no-map; + }; + + uat_pagetables: uat-pagetables { + reg = <0 0 0 0>; + no-map; + }; + + uat_ttbs: uat-ttbs { + reg = <0 0 0 0>; + no-map; + }; + }; }; diff --git a/arch/arm64/boot/dts/apple/t602x-die0.dtsi b/arch/arm64/boot/dts/apple/t602x-die0.dtsi index 6bccfb60e50b3b..be873359b11677 100644 --- a/arch/arm64/boot/dts/apple/t602x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-die0.dtsi @@ -566,14 +566,6 @@ #sound-dai-cells = <1>; }; - pmgr_gfx: power-management@404e80000 { - compatible = "apple,t6021-pmgr", "apple,pmgr", "syscon", "simple-mfd"; - #address-cells = <1>; - #size-cells = <1>; - - reg = <0x4 0x4e80000 0 0x4000>; - }; - gpu: gpu@406400000 { compatible = "apple,agx-g14x"; reg = <0x4 0x6400000 0 0x40000>, diff --git a/arch/arm64/boot/dts/apple/t602x-dieX.dtsi b/arch/arm64/boot/dts/apple/t602x-dieX.dtsi index 203316df0d06f0..0ac6c258884187 100644 --- a/arch/arm64/boot/dts/apple/t602x-dieX.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-dieX.dtsi @@ -127,6 +127,14 @@ #interrupt-cells = <2>; }; + DIE_NODE(pmgr_gfx): power-management@404e80000 { + compatible = "apple,t6020-pmgr", "apple,pmgr", "syscon", "simple-mfd"; + #address-cells = <1>; + #size-cells = <1>; + + reg = <0x4 0x4e80000 0 0x4000>; + }; + DIE_NODE(dwc3_0_dart_0): iommu@702f00000 { compatible = "apple,t6020-dart", "apple,t8110-dart"; reg = <0x7 0x02f00000 0x0 0x4000>; diff --git a/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi b/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi index 33641648f2ae02..ea4372739e4fff 100644 --- a/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi @@ -5,7 +5,7 @@ * Copyright The Asahi Linux Contributors */ -&pmgr { +&DIE_NODE(pmgr) { DIE_NODE(ps_afi): power-controller@100 { compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; reg = <0x100 4>; @@ -627,7 +627,7 @@ }; }; -&pmgr_south { +&DIE_NODE(pmgr_south) { DIE_NODE(ps_amcc4): power-controller@100 { compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; reg = <0x100 4>; @@ -991,7 +991,7 @@ }; }; -&pmgr_east { +&DIE_NODE(pmgr_east) { DIE_NODE(ps_clvr_spmi0): power-controller@100 { compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; reg = <0x100 4>; @@ -1964,7 +1964,7 @@ }; }; -&pmgr_mini { +&DIE_NODE(pmgr_mini) { DIE_NODE(ps_debug): power-controller@58 { compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; reg = <0x58 4>; @@ -2233,13 +2233,14 @@ #endif }; -&pmgr_gfx { +&DIE_NODE(pmgr_gfx) { DIE_NODE(ps_gpx): power-controller@0 { compatible = "apple,t6020-pmgr-pwrstate", "apple,pmgr-pwrstate"; reg = <0x0 4>; #power-domain-cells = <0>; #reset-cells = <0>; label = DIE_LABEL(gpx); + apple,min-state = <4>; apple,always-on; }; @@ -2250,6 +2251,7 @@ #reset-cells = <0>; label = DIE_LABEL(afr); /* Apple Fabric, media stuff: this can power down */ + apple,min-state = <4>; }; DIE_NODE(ps_gfx): power-controller@108 { From 521888111990907edc2a5abc2a4f372902389620 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 7 Aug 2023 18:09:41 +0900 Subject: [PATCH 0082/1020] arm64: dts: apple: Add j180d (Mac Pro 2023) device tree Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t6022-j180d.dts | 554 ++++++++++++++++++++++ 1 file changed, 554 insertions(+) create mode 100644 arch/arm64/boot/dts/apple/t6022-j180d.dts diff --git a/arch/arm64/boot/dts/apple/t6022-j180d.dts b/arch/arm64/boot/dts/apple/t6022-j180d.dts new file mode 100644 index 00000000000000..62882f6bfd5eb5 --- /dev/null +++ b/arch/arm64/boot/dts/apple/t6022-j180d.dts @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * Mac Pro (M2 Ultra, 2023) + * + * target-type: J180d + * + * Copyright The Asahi Linux Contributors + */ + +/dts-v1/; + +#include "t6022.dtsi" + +/ { + compatible = "apple,j180d", "apple,t6022", "apple,arm-platform"; + model = "Apple Mac Pro (M2 Ultra, 2023)"; + aliases { + atcphy0 = &atcphy0; + atcphy1 = &atcphy1; + atcphy2 = &atcphy2; + atcphy3 = &atcphy3; + atcphy4 = &atcphy0_die1; + atcphy5 = &atcphy1_die1; + atcphy6 = &atcphy2_die1; + atcphy7 = &atcphy3_die1; + //bluetooth0 = &bluetooth0; + //ethernet0 = ðernet0; + //ethernet1 = ðernet1; + serial0 = &serial0; + //wifi0 = &wifi0; + }; + + chosen { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + stdout-path = "serial0"; + + framebuffer0: framebuffer@0 { + compatible = "apple,simple-framebuffer", "simple-framebuffer"; + reg = <0 0 0 0>; /* To be filled by loader */ + /* Format properties will be added by loader */ + status = "disabled"; + power-domains = <&ps_dispext0_cpu0_die1>, <&ps_dptx_phy_ps_die1>; + }; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + /* To be filled by loader */ + }; + + memory@10000000000 { + device_type = "memory"; + reg = <0x100 0 0x2 0>; /* To be filled by loader */ + }; +}; + +&serial0 { + status = "okay"; +}; + +/* USB Type C Rear */ +&i2c0 { + hpm2: usb-pd@3b { + compatible = "apple,cd321x"; + reg = <0x3b>; + interrupt-parent = <&pinctrl_ap>; + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "irq"; + + typec2: connector { + compatible = "usb-c-connector"; + label = "USB-C Back 1"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec2_con_hs: endpoint { + remote-endpoint = <&typec2_usb_hs>; + }; + }; + port@1 { + reg = <1>; + typec2_con_ss: endpoint { + remote-endpoint = <&typec2_usb_ss>; + }; + }; + }; + }; + }; + + hpm3: usb-pd@3c { + compatible = "apple,cd321x"; + reg = <0x3c>; + interrupt-parent = <&pinctrl_ap>; + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "irq"; + + typec3: connector { + compatible = "usb-c-connector"; + label = "USB-C Back 2"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec3_con_hs: endpoint { + remote-endpoint = <&typec3_usb_hs>; + }; + }; + port@1 { + reg = <1>; + typec3_con_ss: endpoint { + remote-endpoint = <&typec3_usb_ss>; + }; + }; + }; + }; + }; + + hpm4: usb-pd@39 { + compatible = "apple,cd321x"; + reg = <0x39>; + interrupt-parent = <&pinctrl_ap>; + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "irq"; + + typec4: connector { + compatible = "usb-c-connector"; + label = "USB-C Back 3"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec4_con_hs: endpoint { + remote-endpoint = <&typec4_usb_hs>; + }; + }; + port@1 { + reg = <1>; + typec4_con_ss: endpoint { + remote-endpoint = <&typec4_usb_ss>; + }; + }; + }; + }; + }; + + hpm5: usb-pd@3a { + compatible = "apple,cd321x"; + reg = <0x3a>; + interrupt-parent = <&pinctrl_ap>; + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "irq"; + + typec5: connector { + compatible = "usb-c-connector"; + label = "USB-C Back 4"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec5_con_hs: endpoint { + remote-endpoint = <&typec5_usb_hs>; + }; + }; + port@1 { + reg = <1>; + typec5_con_ss: endpoint { + remote-endpoint = <&typec5_usb_ss>; + }; + }; + }; + }; + }; + + hpm6: usb-pd@3d { + compatible = "apple,cd321x"; + reg = <0x3d>; + interrupt-parent = <&pinctrl_ap>; + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "irq"; + + typec6: connector { + compatible = "usb-c-connector"; + label = "USB-C Back 5"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec6_con_hs: endpoint { + remote-endpoint = <&typec6_usb_hs>; + }; + }; + port@1 { + reg = <1>; + typec6_con_ss: endpoint { + remote-endpoint = <&typec6_usb_ss>; + }; + }; + }; + }; + }; + + hpm7: usb-pd@3e { + compatible = "apple,cd321x"; + reg = <0x3e>; + interrupt-parent = <&pinctrl_ap>; + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "irq"; + + typec7: connector { + compatible = "usb-c-connector"; + label = "USB-C Back 6"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec7_con_hs: endpoint { + remote-endpoint = <&typec7_usb_hs>; + }; + }; + port@1 { + reg = <1>; + typec7_con_ss: endpoint { + remote-endpoint = <&typec7_usb_ss>; + }; + }; + }; + }; + }; +}; + +/* USB Type C Front */ +&i2c3 { + status = "okay"; + + hpm0: usb-pd@38 { + compatible = "apple,cd321x"; + reg = <0x38>; + interrupt-parent = <&pinctrl_ap>; + interrupts = <60 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "irq"; + + typec0: connector { + compatible = "usb-c-connector"; + label = "USB-C Top Right"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec0_con_hs: endpoint { + remote-endpoint = <&typec0_usb_hs>; + }; + }; + port@1 { + reg = <1>; + typec0_con_ss: endpoint { + remote-endpoint = <&typec0_usb_ss>; + }; + }; + }; + }; + }; + + hpm1: usb-pd@3f { + compatible = "apple,cd321x"; + reg = <0x3f>; + interrupt-parent = <&pinctrl_ap>; + interrupts = <60 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "irq"; + + typec1: connector { + compatible = "usb-c-connector"; + label = "USB-C Top Left"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec1_con_hs: endpoint { + remote-endpoint = <&typec1_usb_hs>; + }; + }; + port@1 { + reg = <1>; + typec1_con_ss: endpoint { + remote-endpoint = <&typec1_usb_ss>; + }; + }; + }; + }; + }; +}; + +/* USB controllers */ +&dwc3_0 { + port { + typec0_usb_hs: endpoint { + remote-endpoint = <&typec0_con_hs>; + }; + }; +}; + +&dwc3_1 { + port { + typec1_usb_hs: endpoint { + remote-endpoint = <&typec1_con_hs>; + }; + }; +}; + +&dwc3_2 { + port { + typec2_usb_hs: endpoint { + remote-endpoint = <&typec2_con_hs>; + }; + }; +}; + +&dwc3_3 { + port { + typec3_usb_hs: endpoint { + remote-endpoint = <&typec3_con_hs>; + }; + }; +}; + +&dwc3_0_die1 { + port { + typec4_usb_hs: endpoint { + remote-endpoint = <&typec4_con_hs>; + }; + }; +}; + +&dwc3_1_die1 { + port { + typec5_usb_hs: endpoint { + remote-endpoint = <&typec5_con_hs>; + }; + }; +}; + +&dwc3_2_die1 { + port { + typec6_usb_hs: endpoint { + remote-endpoint = <&typec6_con_hs>; + }; + }; +}; + +&dwc3_3_die1 { + port { + typec7_usb_hs: endpoint { + remote-endpoint = <&typec7_con_hs>; + }; + }; +}; + +/* Type-C PHYs */ +&atcphy0 { + port { + typec0_usb_ss: endpoint { + remote-endpoint = <&typec0_con_ss>; + }; + }; +}; + +&atcphy1 { + port { + typec1_usb_ss: endpoint { + remote-endpoint = <&typec1_con_ss>; + }; + }; +}; + +&atcphy2 { + port { + typec2_usb_ss: endpoint { + remote-endpoint = <&typec2_con_ss>; + }; + }; +}; + +&atcphy3 { + port { + typec3_usb_ss: endpoint { + remote-endpoint = <&typec3_con_ss>; + }; + }; +}; + +&atcphy0_die1 { + port { + typec4_usb_ss: endpoint { + remote-endpoint = <&typec4_con_ss>; + }; + }; +}; + +&atcphy1_die1 { + port { + typec5_usb_ss: endpoint { + remote-endpoint = <&typec5_con_ss>; + }; + }; +}; + +&atcphy2_die1 { + port { + typec6_usb_ss: endpoint { + remote-endpoint = <&typec6_con_ss>; + }; + }; +}; + +&atcphy3_die1 { + port { + typec7_usb_ss: endpoint { + remote-endpoint = <&typec7_con_ss>; + }; + }; +}; + +/* Audio */ +&i2c1 { + status = "okay"; + + speaker_tweeter: codec@38 { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x38>; + shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Tweeter"; + interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>; + }; + + speaker_woofer: codec@39 { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x39>; + shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Woofer"; + interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>; + }; +}; + +&i2c2 { + status = "okay"; + + jack_codec: codec@4b { + compatible = "cirrus,cs42l84"; + reg = <0x4b>; + reset-gpios = <&pinctrl_nub 8 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + interrupts-extended = <&pinctrl_ap 59 IRQ_TYPE_LEVEL_LOW>; + sound-name-prefix = "Jack"; + }; +}; + +&nco_clkref { + clock-frequency = <1068000000>; +}; + +/ { + sound: sound { + compatible = "apple,j180-macaudio", "apple,macaudio"; + model = "Mac Pro J180"; + + dai-link@0 { + link-name = "Speakers"; + /* + * DANGER ZONE: You can blow your speakers! + * + * The drivers are not ready, and unless you are careful + * to attenuate the audio stream, you run the risk of + * blowing your speakers. + */ + status = "disabled"; + cpu { + sound-dai = <&mca 0>; + }; + codec { + sound-dai = <&speaker_woofer>, <&speaker_tweeter>; + }; + }; + + dai-link@1 { + link-name = "Headphone Jack"; + + cpu { + sound-dai = <&mca 2>; + }; + codec { + sound-dai = <&jack_codec>; + }; + }; + }; +}; + +&pcie0 { + status = "disabled"; +}; + +&pcie0_dart_0 { + status = "disabled"; +}; + +/* delete unused always-on power-domains on die 1 */ +/delete-node/ &ps_disp0_cpu0_die1; +/delete-node/ &ps_disp0_fe_die1; + +&gpu { + apple,idleoff-standby-timer = <3000>; + apple,perf-base-pstate = <5>; + apple,perf-boost-ce-step = <100>; + apple,perf-boost-min-util = <75>; + apple,perf-tgt-utilization = <70>; +}; From 48d63463ef708085d214b7b1e82af4873b3c5de2 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 7 Aug 2023 19:53:50 +0900 Subject: [PATCH 0083/1020] arm64: dts: apple: t6022: Add APCIE-GE nodes Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t6022-j180d.dts | 16 +++++ arch/arm64/boot/dts/apple/t6022.dtsi | 12 +++- arch/arm64/boot/dts/apple/t602x-die0.dtsi | 2 - arch/arm64/boot/dts/apple/t602x-dieX.dtsi | 64 +++++++++++++++++++ .../arm64/boot/dts/apple/t602x-gpio-pins.dtsi | 4 ++ 5 files changed, 94 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t6022-j180d.dts b/arch/arm64/boot/dts/apple/t6022-j180d.dts index 62882f6bfd5eb5..7775cbf8698d06 100644 --- a/arch/arm64/boot/dts/apple/t6022-j180d.dts +++ b/arch/arm64/boot/dts/apple/t6022-j180d.dts @@ -541,6 +541,22 @@ status = "disabled"; }; +&pcie_ge { + status = "ok"; +}; + +&pcie_ge_dart { + status = "ok"; +}; + +&pcie_ge_die1 { + status = "ok"; +}; + +&pcie_ge_dart_die1 { + status = "ok"; +}; + /* delete unused always-on power-domains on die 1 */ /delete-node/ &ps_disp0_cpu0_die1; /delete-node/ &ps_disp0_fe_die1; diff --git a/arch/arm64/boot/dts/apple/t6022.dtsi b/arch/arm64/boot/dts/apple/t6022.dtsi index b7c19be04c72a3..ebf8e5bf53e86c 100644 --- a/arch/arm64/boot/dts/apple/t6022.dtsi +++ b/arch/arm64/boot/dts/apple/t6022.dtsi @@ -275,7 +275,8 @@ #size-cells = <2>; ranges = <0x2 0x0 0x2 0x0 0x4 0x0>, <0x5 0x80000000 0x5 0x80000000 0x1 0x80000000>, - <0x7 0x0 0x7 0x0 0xf 0x80000000>; + <0x7 0x0 0x7 0x0 0xf 0x80000000>, + <0x16 0x80000000 0x16 0x80000000 0x5 0x80000000>; nonposted-mmio; /* Required to get >32-bit DMA via DARTs */ dma-ranges = <0 0 0 0 0xffffffff 0xffffc000>; @@ -288,7 +289,8 @@ #address-cells = <2>; #size-cells = <2>; ranges = <0x2 0x0 0x22 0x0 0x4 0x0>, - <0x7 0x0 0x27 0x0 0xf 0x80000000>; + <0x7 0x0 0x27 0x0 0xf 0x80000000>, + <0x16 0x80000000 0x36 0x80000000 0x5 0x80000000>; nonposted-mmio; /* Required to get >32-bit DMA via DARTs */ dma-ranges = <0 0 0 0 0xffffffff 0xffffc000>; @@ -358,3 +360,9 @@ apple,ppm-ki = <11.0>; apple,ppm-kp = <0.15>; }; + +&pinctrl_ap_die1 { + pcie_ge_pins_die1: pcie-ge1-pins { + pinmux = ; + }; +}; diff --git a/arch/arm64/boot/dts/apple/t602x-die0.dtsi b/arch/arm64/boot/dts/apple/t602x-die0.dtsi index be873359b11677..332df752013e10 100644 --- a/arch/arm64/boot/dts/apple/t602x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-die0.dtsi @@ -818,5 +818,3 @@ power-domains = <&ps_apcie_gp_sys>; status = "disabled"; }; - - diff --git a/arch/arm64/boot/dts/apple/t602x-dieX.dtsi b/arch/arm64/boot/dts/apple/t602x-dieX.dtsi index 0ac6c258884187..da891047c5db7a 100644 --- a/arch/arm64/boot/dts/apple/t602x-dieX.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-dieX.dtsi @@ -350,3 +350,67 @@ svid = <0xff01>, <0x8087>; power-domains = <&DIE_NODE(ps_atc3_usb)>; }; + + DIE_NODE(pcie_ge): pcie@1680000000 { + compatible = "apple,t6020-pcie-ge", "apple,t6020-pcie"; + device_type = "pci"; + + reg = <0x16 0x80000000 0x0 0x1000000>, /* config */ + <0x16 0x91000000 0x0 0x4000>, /* rc */ + <0x16 0x94008000 0x0 0x4000>, /* port0 */ + <0x16 0x9e01c000 0x0 0x4000>, /* phy0 */ + <0x16 0x9401c000 0x0 0x1000>; /* ltssm0 */ + reg-names = "config", "rc", "port0", "phy0", "ltssm0"; + + interrupt-parent = <&aic>; + interrupts = ; + + msi-controller; + msi-parent = <&pcie0>; + msi-ranges = <&aic AIC_IRQ DIE_NO 1672 IRQ_TYPE_EDGE_RISING 32>; + + + iommu-map = <0x100 &pcie_ge_dart 1 1>; + iommu-map-mask = <0xff00>; + + bus-range = <0 1>; + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x43000000 0x18 0x00000000 0x18 0x00000000 0x4 0x00000000>, + <0x02000000 0x0 0x80000000 0x17 0x80000000 0x0 0x80000000>; + + power-domains = <&ps_apcie_ge_sys>; + pinctrl-0 = <&DIE_NODE(pcie_ge_pins)>; + pinctrl-names = "default"; + + dma-coherent; + + status = "disabled"; + + DIE_NODE(port_ge00): pci@0,0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + reset-gpios = <&DIE_NODE(pinctrl_ap) 9 GPIO_ACTIVE_LOW>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + interrupt-controller; + #interrupt-cells = <1>; + + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &port00 0 0 0 0>; + }; + }; + + DIE_NODE(pcie_ge_dart): iommu@1694000000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x16 0x94000000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_apcie_ge_sys>; + status = "disabled"; + }; + diff --git a/arch/arm64/boot/dts/apple/t602x-gpio-pins.dtsi b/arch/arm64/boot/dts/apple/t602x-gpio-pins.dtsi index acb133d1723d03..9b24832ba26abe 100644 --- a/arch/arm64/boot/dts/apple/t602x-gpio-pins.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-gpio-pins.dtsi @@ -78,4 +78,8 @@ , ; }; + + pcie_ge_pins: pcie-ge-pins { + pinmux = ; + }; }; From f5429b3549eb1735f265bcdcc929bedf010d0a6e Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Tue, 18 Apr 2023 23:06:49 +0300 Subject: [PATCH 0084/1020] arm64: dts: apple: t8103: Add touchbar screen bindings Adds device tree entries for the touchbar screen Signed-off-by: Sasha Finkelstein --- arch/arm64/boot/dts/apple/t8103-j293.dts | 9 ++++++++ arch/arm64/boot/dts/apple/t8103.dtsi | 26 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts index a845d92ee10c25..3351cf4228b052 100644 --- a/arch/arm64/boot/dts/apple/t8103-j293.dts +++ b/arch/arm64/boot/dts/apple/t8103-j293.dts @@ -138,6 +138,15 @@ &fpwm1 { status = "okay"; }; + +&display_dfr { + status = "okay"; + dfr_panel: panel@0 { + compatible = "apple,summit"; + reg = <0>; + }; +}; + / { sound { compatible = "apple,j293-macaudio", "apple,macaudio"; diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index 130e240f59a08c..29bc5c61150124 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -513,6 +513,32 @@ #performance-domain-cells = <0>; }; + display_dfr: display-pipe@228200000 { + compatible = "apple,t8103-display-pipe", "apple,h7-display-pipe"; + reg-names = "be", "fe", "mipi"; + reg = <0x2 0x28200000 0x0 0xc000>, + <0x2 0x28400000 0x0 0x4000>, + <0x2 0x28600000 0x0 0x100000>; + power-domains = <&ps_dispdfr_fe>, <&ps_dispdfr_be>, <&ps_mipi_dsi>; + interrupt-parent = <&aic>; + interrupts = , + ; + interrupt-names = "be", "fe"; + status = "disabled"; + iommus = <&displaydfr_dart 0>; + #address-cells = <1>; + #size-cells = <0>; + }; + + displaydfr_dart: iommu@228304000 { + compatible = "apple,t8103-dart"; + reg = <0x2 0x28304000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #iommu-cells = <1>; + power-domains = <&ps_dispdfr_fe>; + }; + disp0_dart: iommu@231304000 { compatible = "apple,t8103-dart"; reg = <0x2 0x31304000 0x0 0x4000>; From beb3714e742b2574e5200f810ccd8fafdb471d7c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 15 Apr 2023 16:42:41 +0200 Subject: [PATCH 0085/1020] arm64: dts: apple: Add touchbar display nodes for t8112-j493 Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112-j493.dts | 15 ++++++++++++++ arch/arm64/boot/dts/apple/t8112.dtsi | 25 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j493.dts b/arch/arm64/boot/dts/apple/t8112-j493.dts index d34acd0ee2f203..0234102dbe8462 100644 --- a/arch/arm64/boot/dts/apple/t8112-j493.dts +++ b/arch/arm64/boot/dts/apple/t8112-j493.dts @@ -45,6 +45,21 @@ }; }; +&display_dfr { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + dfr_panel: panel@0 { + compatible = "apple,summit"; + reg = <0>; + }; +}; + +&displaydfr_dart { + status = "okay"; +}; + /* * Force the bus number assignments so that we can declare some of the * on-board devices and properties that are populated by the bootloader diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index cabf3a23104afc..4f92fb33b77110 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -535,6 +535,31 @@ #performance-domain-cells = <0>; }; + display_dfr: display-pipe@228200000 { + compatible = "apple,t8112-display-pipe", "apple,h7-display-pipe"; + reg-names = "be", "fe", "mipi"; + reg = <0x2 0x28200000 0x0 0xc000>, + <0x2 0x28400000 0x0 0x4000>, + <0x2 0x28600000 0x0 0x100000>; + power-domains = <&ps_dispdfr_fe>, <&ps_dispdfr_be>, <&ps_mipi_dsi>; + interrupt-parent = <&aic>; + interrupts = , + ; + interrupt-names = "be", "fe"; + status = "disabled"; + iommus = <&displaydfr_dart 0>; + }; + + displaydfr_dart: iommu@228304000 { + compatible = "apple,t8110-dart"; + reg = <0x2 0x28304000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #iommu-cells = <1>; + power-domains = <&ps_dispdfr_fe>; + status = "disabled"; + }; + disp0_dart: iommu@231304000 { compatible = "apple,t8112-dart", "apple,t8110-dart"; reg = <0x2 0x31304000 0x0 0x4000>; From bb0e54f9797170809b137398819e1b42f783ff81 Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Sat, 21 Jan 2023 19:47:32 +0300 Subject: [PATCH 0086/1020] arm64: dts: apple: t8103: Add touchbar bindings Adds device tree entries for the touchbar digitizer Signed-off-by: Sasha Finkelstein --- arch/arm64/boot/dts/apple/t8103-j293.dts | 24 ++++++++++++++++++++++++ arch/arm64/boot/dts/apple/t8103.dtsi | 12 ++++++++++++ 2 files changed, 36 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts index 3351cf4228b052..c519a8975d95fa 100644 --- a/arch/arm64/boot/dts/apple/t8103-j293.dts +++ b/arch/arm64/boot/dts/apple/t8103-j293.dts @@ -28,6 +28,10 @@ default-state = "keep"; }; }; + + aliases { + touchbar0 = &touchbar0; + }; }; &dcp { @@ -59,6 +63,26 @@ label = "USB-C Left-front"; }; +&spi0 { + status = "okay"; + + touchbar0: touchbar@0 { + compatible = "apple,j293-touchbar", + "apple,z2-touchbar", "apple,z2-multitouch"; + reg = <0>; + spi-max-frequency = <11500000>; + spi-cs-setup-delay-ns = <2000>; + spi-cs-hold-delay-ns = <2000>; + reset-gpios = <&pinctrl_ap 139 GPIO_ACTIVE_LOW>; + cs-gpios = <&pinctrl_ap 109 0>; + interrupts-extended = <&pinctrl_ap 194 IRQ_TYPE_EDGE_FALLING>; + firmware-name = "apple/dfrmtfw-j293.bin"; + touchscreen-size-x = <23045>; + touchscreen-size-y = <640>; + label = "MacBookPro17,1 Touch Bar"; + }; +}; + &spi3 { status = "okay"; diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index 29bc5c61150124..762aaa2661dd5a 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -693,6 +693,18 @@ status = "disabled"; }; + spi0: spi@235100000 { + compatible = "apple,t8103-spi", "apple,spi"; + reg = <0x2 0x35100000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + clocks = <&clk_200m>; + power-domains = <&ps_spi0>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; /* only used in J293 */ + }; + spi1: spi@235104000 { compatible = "apple,t8103-spi", "apple,spi"; reg = <0x2 0x35104000 0x0 0x4000>; From b151dc26722188801c430fcf91fdfc369e5fc749 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 21 Jan 2023 19:47:32 +0300 Subject: [PATCH 0087/1020] arm64: dts: apple: t8112: Add touchbar digitizer node Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112-j493.dts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j493.dts b/arch/arm64/boot/dts/apple/t8112-j493.dts index 0234102dbe8462..8b629e564c5019 100644 --- a/arch/arm64/boot/dts/apple/t8112-j493.dts +++ b/arch/arm64/boot/dts/apple/t8112-j493.dts @@ -21,6 +21,7 @@ bluetooth0 = &bluetooth0; wifi0 = &wifi0; keyboard = &keyboard; + touchbar0 = &touchbar0; }; led-controller { @@ -190,6 +191,25 @@ }; }; +&spi3 { + status = "okay"; + + touchbar0: touchbar@0 { + compatible = "apple,j493-touchbar", "apple,z2-touchbar", "apple,z2-multitouch"; + reg = <0>; + label = "Mac14,7 Touch Bar"; + spi-max-frequency = <8000000>; + spi-cs-setup-delay-ns = <2000>; + spi-cs-hold-delay-ns = <2000>; + + reset-gpios = <&pinctrl_ap 170 GPIO_ACTIVE_LOW>; + interrupts-extended = <&pinctrl_ap 174 IRQ_TYPE_EDGE_FALLING>; + firmware-name = "apple/dfrmtfw-j493.bin"; + touchscreen-size-x = <23045>; + touchscreen-size-y = <640>; + }; +}; + &mtp { status = "okay"; }; From ab1d752e387eda936613b0cae1ceec0edacef5be Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 29 Jul 2023 16:14:11 +0200 Subject: [PATCH 0088/1020] arm64: dts: apple: Add devicetree for Macbook Air (15-inch, M2, 2023) Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/Makefile | 1 + arch/arm64/boot/dts/apple/t8112-j415.dts | 239 +++++++++++++++++++++++ 2 files changed, 240 insertions(+) create mode 100644 arch/arm64/boot/dts/apple/t8112-j415.dts diff --git a/arch/arm64/boot/dts/apple/Makefile b/arch/arm64/boot/dts/apple/Makefile index a55d7097b7fbbb..02fab071d0acd4 100644 --- a/arch/arm64/boot/dts/apple/Makefile +++ b/arch/arm64/boot/dts/apple/Makefile @@ -11,6 +11,7 @@ dtb-$(CONFIG_ARCH_APPLE) += t6001-j316c.dtb dtb-$(CONFIG_ARCH_APPLE) += t6001-j375c.dtb dtb-$(CONFIG_ARCH_APPLE) += t6002-j375d.dtb dtb-$(CONFIG_ARCH_APPLE) += t8112-j413.dtb +dtb-$(CONFIG_ARCH_APPLE) += t8112-j415.dtb dtb-$(CONFIG_ARCH_APPLE) += t8112-j473.dtb dtb-$(CONFIG_ARCH_APPLE) += t8112-j493.dtb dtb-$(CONFIG_ARCH_APPLE) += t6020-j414s.dtb diff --git a/arch/arm64/boot/dts/apple/t8112-j415.dts b/arch/arm64/boot/dts/apple/t8112-j415.dts new file mode 100644 index 00000000000000..c502bed5f96224 --- /dev/null +++ b/arch/arm64/boot/dts/apple/t8112-j415.dts @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * Apple MacBook Air (15-inchl, M2, 2023) + * + * target-type: J415 + * + * Copyright The Asahi Linux Contributors + */ + +/dts-v1/; + +#include "t8112.dtsi" +#include "t8112-jxxx.dtsi" +#include + +/ { + compatible = "apple,j415", "apple,t8112", "apple,arm-platform"; + model = "Apple MacBook Air (15-inch, M2, 2023)"; + + aliases { + bluetooth0 = &bluetooth0; + wifi0 = &wifi0; + keyboard = &keyboard; + }; + + led-controller { + compatible = "pwm-leds"; + led-0 { + pwms = <&fpwm1 0 40000>; + label = "kbd_backlight"; + function = LED_FUNCTION_KBD_BACKLIGHT; + color = ; + max-brightness = <255>; + default-state = "keep"; + }; + }; +}; + +&dcp { + panel: panel { + compatible = "apple,panel-j415", "apple,panel"; + width-mm = <327>; + height-mm = <211>; + adj-height-mm = <204>; + apple,max-brightness = <500>; + }; +}; + +/* + * Force the bus number assignments so that we can declare some of the + * on-board devices and properties that are populated by the bootloader + * (such as MAC addresses). + */ +&port00 { + bus-range = <1 1>; + pwren-gpios = <&smc_gpio 13 GPIO_ACTIVE_HIGH>; + wifi0: wifi@0,0 { + compatible = "pci14e4,4433"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + /* To be filled by the loader */ + local-mac-address = [00 10 18 00 00 10]; + apple,antenna-sku = "XX"; + brcm,board-type = "apple,snake"; + }; + + bluetooth0: bluetooth@0,1 { + compatible = "pci14e4,5f71"; + reg = <0x10100 0x0 0x0 0x0 0x0>; + /* To be filled by the loader */ + local-bd-address = [00 00 00 00 00 00]; + brcm,board-type = "apple,snake"; + }; +}; + +/* + * Provide labels for the USB type C ports. + */ + +&typec0 { + label = "USB-C Left-back"; +}; + +&typec1 { + label = "USB-C Left-front"; +}; + +&i2c0 { + /* MagSafe port */ + hpm5: usb-pd@3a { + compatible = "apple,cd321x"; + reg = <0x3a>; + interrupt-parent = <&pinctrl_ap>; + interrupts = <8 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "irq"; + }; +}; + +&i2c1 { + speaker_left_woof1: codec@38 { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x38>; + shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Left Woofer 1"; + }; + + speaker_left_tweet: codec@39 { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x39>; + shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Left Tweeter"; + }; + + speaker_left_woof2: codec@3a { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x3a>; + shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Left Woofer 2"; + }; +}; + +&i2c3 { + speaker_right_woof1: codec@3b { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x3b>; + shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Right Woofer 1"; + }; + + speaker_right_tweet: codec@3c { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x3c>; + shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Right Tweeter"; + }; + + speaker_right_woof2: codec@3d { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x3d>; + shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + sound-name-prefix = "Right Woofer 2"; + }; + + jack_codec: codec@4b { + compatible = "cirrus,cs42l84"; + reg = <0x4b>; + reset-gpios = <&pinctrl_nub 12 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + interrupts-extended = <&pinctrl_ap 149 IRQ_TYPE_LEVEL_LOW>; + sound-name-prefix = "Jack"; + }; +}; + +&fpwm1 { + status = "okay"; +}; + +/ { + sound { + compatible = "apple,j415-macaudio", "apple,macaudio"; + model = "MacBook Air J415"; + + dai-link@0 { + link-name = "Speakers"; + + /* + * DANGER ZONE: You can blow your speakers! + * + * The drivers are not ready, and unless you are careful + * to attenuate the audio stream, you run the risk of + * blowing your speakers. + */ + status = "disabled"; + + cpu { + sound-dai = <&mca 0>, <&mca 1>; + }; + codec { + sound-dai = <&speaker_left_woof1>, + <&speaker_left_tweet>, + <&speaker_left_woof2>, + <&speaker_right_woof1>, + <&speaker_right_tweet>, + <&speaker_right_woof2>; + }; + }; + + dai-link@1 { + link-name = "Headphone Jack"; + + cpu { + sound-dai = <&mca 2>; + }; + codec { + sound-dai = <&jack_codec>; + }; + }; + }; +}; + +&mtp { + status = "okay"; +}; +&mtp_mbox { + status = "okay"; +}; +&mtp_dart { + status = "okay"; +}; +&mtp_dockchannel { + status = "okay"; +}; +&mtp_hid { + apple,afe-reset-gpios = <&smc_gpio 8 GPIO_ACTIVE_LOW>; + apple,stm-reset-gpios = <&smc_gpio 24 GPIO_ACTIVE_LOW>; + + multi-touch { + firmware-name = "apple/tpmtfw-j415.bin"; + }; + + keyboard: keyboard { + hid-country-code = <0>; + apple,keyboard-layout-id = <0>; + }; + + stm { + }; + + actuator { + }; + + tp_accel { + }; +}; From 52b85609c79a0870d7acd8f1892deee0cf190637 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Wed, 9 Aug 2023 14:08:43 +0900 Subject: [PATCH 0089/1020] arm64: dts: apple: t8112-j473: Set GPU base pstate This should help performance/responsiveness (used on most desktops). Signed-off-by: Asahi Lina --- arch/arm64/boot/dts/apple/t8112-j473.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j473.dts b/arch/arm64/boot/dts/apple/t8112-j473.dts index 4705ec980211c4..92efe72b18461b 100644 --- a/arch/arm64/boot/dts/apple/t8112-j473.dts +++ b/arch/arm64/boot/dts/apple/t8112-j473.dts @@ -136,3 +136,7 @@ }; }; + +&gpu { + apple,perf-base-pstate = <3>; +}; From a962ccfb08662816944dae3bab9bf1c842e1f21a Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 4 Sep 2023 20:57:11 +0200 Subject: [PATCH 0090/1020] arm64: dts: apple: Share USB-C port node on t6022 devices Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t6022-j180d.dts | 105 ++----------------- arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi | 111 +++++++++++++++++++++ 2 files changed, 122 insertions(+), 94 deletions(-) create mode 100644 arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi diff --git a/arch/arm64/boot/dts/apple/t6022-j180d.dts b/arch/arm64/boot/dts/apple/t6022-j180d.dts index 7775cbf8698d06..499e5ad7e8c658 100644 --- a/arch/arm64/boot/dts/apple/t6022-j180d.dts +++ b/arch/arm64/boot/dts/apple/t6022-j180d.dts @@ -10,6 +10,7 @@ /dts-v1/; #include "t6022.dtsi" +#include "t6022-jxxxd.dtsi" / { compatible = "apple,j180d", "apple,t6022", "apple,arm-platform"; @@ -129,69 +130,9 @@ }; }; - hpm4: usb-pd@39 { - compatible = "apple,cd321x"; - reg = <0x39>; - interrupt-parent = <&pinctrl_ap>; - interrupts = <44 IRQ_TYPE_LEVEL_LOW>; - interrupt-names = "irq"; - - typec4: connector { - compatible = "usb-c-connector"; - label = "USB-C Back 3"; - power-role = "dual"; - data-role = "dual"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - typec4_con_hs: endpoint { - remote-endpoint = <&typec4_usb_hs>; - }; - }; - port@1 { - reg = <1>; - typec4_con_ss: endpoint { - remote-endpoint = <&typec4_usb_ss>; - }; - }; - }; - }; - }; - - hpm5: usb-pd@3a { - compatible = "apple,cd321x"; - reg = <0x3a>; - interrupt-parent = <&pinctrl_ap>; - interrupts = <44 IRQ_TYPE_LEVEL_LOW>; - interrupt-names = "irq"; + /* hpm4 included from t6022-jxxxd.dtsi */ - typec5: connector { - compatible = "usb-c-connector"; - label = "USB-C Back 4"; - power-role = "dual"; - data-role = "dual"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - typec5_con_hs: endpoint { - remote-endpoint = <&typec5_usb_hs>; - }; - }; - port@1 { - reg = <1>; - typec5_con_ss: endpoint { - remote-endpoint = <&typec5_usb_ss>; - }; - }; - }; - }; - }; + /* hpm5 included from t6022-jxxxd.dtsi */ hpm6: usb-pd@3d { compatible = "apple,cd321x"; @@ -258,6 +199,14 @@ }; }; +&hpm4 { + label = "USB-C Back 3"; +}; + +&hpm5 { + label = "USB-C Back 4"; +}; + /* USB Type C Front */ &i2c3 { status = "okay"; @@ -360,22 +309,6 @@ }; }; -&dwc3_0_die1 { - port { - typec4_usb_hs: endpoint { - remote-endpoint = <&typec4_con_hs>; - }; - }; -}; - -&dwc3_1_die1 { - port { - typec5_usb_hs: endpoint { - remote-endpoint = <&typec5_con_hs>; - }; - }; -}; - &dwc3_2_die1 { port { typec6_usb_hs: endpoint { @@ -425,22 +358,6 @@ }; }; -&atcphy0_die1 { - port { - typec4_usb_ss: endpoint { - remote-endpoint = <&typec4_con_ss>; - }; - }; -}; - -&atcphy1_die1 { - port { - typec5_usb_ss: endpoint { - remote-endpoint = <&typec5_con_ss>; - }; - }; -}; - &atcphy2_die1 { port { typec6_usb_ss: endpoint { diff --git a/arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi b/arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi new file mode 100644 index 00000000000000..f8fbdca4105fb7 --- /dev/null +++ b/arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * Mac Pro (M2 Ultra, 2023) and Mac Studio (M2 Ultra, 2023) + * + * This file contains the parts common to J180 and J475 devices with t6022. + * + * target-type: J180d / J475d + * + * Copyright The Asahi Linux Contributors + */ + +/* USB Type C */ +&i2c0 { + /* front-right */ + hpm4: usb-pd@39 { + compatible = "apple,cd321x"; + reg = <0x39>; + interrupt-parent = <&pinctrl_ap>; + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "irq"; + + typec4: connector { + compatible = "usb-c-connector"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec4_con_hs: endpoint { + remote-endpoint = <&typec4_usb_hs>; + }; + }; + port@1 { + reg = <1>; + typec4_con_ss: endpoint { + remote-endpoint = <&typec4_usb_ss>; + }; + }; + }; + }; + }; + + /* front-left */ + hpm5: usb-pd@3a { + compatible = "apple,cd321x"; + reg = <0x3a>; + interrupt-parent = <&pinctrl_ap>; + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "irq"; + + typec5: connector { + compatible = "usb-c-connector"; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec5_con_hs: endpoint { + remote-endpoint = <&typec5_usb_hs>; + }; + }; + port@1 { + reg = <1>; + typec5_con_ss: endpoint { + remote-endpoint = <&typec5_usb_ss>; + }; + }; + }; + }; + }; +}; + +/* USB controllers on die 1 */ +&dwc3_0_die1 { + port { + typec4_usb_hs: endpoint { + remote-endpoint = <&typec4_con_hs>; + }; + }; +}; + +&dwc3_1_die1 { + port { + typec5_usb_hs: endpoint { + remote-endpoint = <&typec5_con_hs>; + }; + }; +}; + +/* Type-C PHYs */ +&atcphy0_die1 { + port { + typec4_usb_ss: endpoint { + remote-endpoint = <&typec4_con_ss>; + }; + }; +}; + +&atcphy1_die1 { + port { + typec5_usb_ss: endpoint { + remote-endpoint = <&typec5_con_ss>; + }; + }; +}; From 4f2196708be2c94187e9a852820243f6dd12b64b Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 4 Sep 2023 21:48:34 +0200 Subject: [PATCH 0091/1020] arm64: dts: apple: t6022: Disable dcp thouroughly Also disables "display" until it can be supported via dispext*.\ Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t6022-j180d.dts | 4 ---- arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi | 22 ++++++++++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t6022-j180d.dts b/arch/arm64/boot/dts/apple/t6022-j180d.dts index 499e5ad7e8c658..a2cb2c5b86bc10 100644 --- a/arch/arm64/boot/dts/apple/t6022-j180d.dts +++ b/arch/arm64/boot/dts/apple/t6022-j180d.dts @@ -474,10 +474,6 @@ status = "ok"; }; -/* delete unused always-on power-domains on die 1 */ -/delete-node/ &ps_disp0_cpu0_die1; -/delete-node/ &ps_disp0_fe_die1; - &gpu { apple,idleoff-standby-timer = <3000>; apple,perf-base-pstate = <5>; diff --git a/arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi b/arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi index f8fbdca4105fb7..4f552c2530aa7a 100644 --- a/arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi +++ b/arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi @@ -9,6 +9,28 @@ * Copyright The Asahi Linux Contributors */ +/* disable unused display node */ + +&display { + status = "disabled"; + iommus = <>; /* <&dispext0_dart_die1 0>; */ +}; + +/* delete missing dcp0/disp0 */ + +/delete-node/ &disp0_dart; +/delete-node/ &dcp_dart; +/delete-node/ &dcp_mbox; +/delete-node/ &dcp; + +/* delete unused always-on power-domains */ +/delete-node/ &ps_disp0_cpu0; +/delete-node/ &ps_disp0_fe; + +/delete-node/ &ps_disp0_cpu0_die1; +/delete-node/ &ps_disp0_fe_die1; + + /* USB Type C */ &i2c0 { /* front-right */ From 446c9d481a9ebf228b7aedfd29346fda2a5fd014 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 17 Aug 2023 23:25:58 +0200 Subject: [PATCH 0092/1020] arm64: dts: apple: t6020-j474s: Disable dcp until lpdpphy is supported This emulates the M2 Ultra Mac Studio and Pro. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t6020-j474s.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6020-j474s.dts b/arch/arm64/boot/dts/apple/t6020-j474s.dts index 653af803551c76..557226c04deed9 100644 --- a/arch/arm64/boot/dts/apple/t6020-j474s.dts +++ b/arch/arm64/boot/dts/apple/t6020-j474s.dts @@ -29,6 +29,10 @@ }; }; +&dcp { + status = "disabled"; +}; + &hpm0 { interrupts = <44 IRQ_TYPE_LEVEL_LOW>; }; From 1b3766cb8102fc2444b6e520c650ae3d946c5a47 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 21 Aug 2023 00:50:07 +0200 Subject: [PATCH 0093/1020] arm64: dts: apple: t602x: Add initial Mac Studio (2023) device trees They use the same GPIO pins and interrupts as the Mac Mini (M2 Pro, 2023) so use a common .dtsi for those definitions. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/Makefile | 2 + arch/arm64/boot/dts/apple/t600x-j375.dtsi | 2 + arch/arm64/boot/dts/apple/t6020-j474s.dts | 63 +--------------- arch/arm64/boot/dts/apple/t6021-j475c.dts | 49 ++++++++++++ arch/arm64/boot/dts/apple/t6022-j475d.dts | 73 ++++++++++++++++++ .../arm64/boot/dts/apple/t602x-j474-j475.dtsi | 74 +++++++++++++++++++ 6 files changed, 201 insertions(+), 62 deletions(-) create mode 100644 arch/arm64/boot/dts/apple/t6021-j475c.dts create mode 100644 arch/arm64/boot/dts/apple/t6022-j475d.dts create mode 100644 arch/arm64/boot/dts/apple/t602x-j474-j475.dtsi diff --git a/arch/arm64/boot/dts/apple/Makefile b/arch/arm64/boot/dts/apple/Makefile index 02fab071d0acd4..b974842cb1500b 100644 --- a/arch/arm64/boot/dts/apple/Makefile +++ b/arch/arm64/boot/dts/apple/Makefile @@ -19,4 +19,6 @@ dtb-$(CONFIG_ARCH_APPLE) += t6021-j414c.dtb dtb-$(CONFIG_ARCH_APPLE) += t6020-j416s.dtb dtb-$(CONFIG_ARCH_APPLE) += t6021-j416c.dtb dtb-$(CONFIG_ARCH_APPLE) += t6020-j474s.dtb +dtb-$(CONFIG_ARCH_APPLE) += t6021-j475c.dtb +dtb-$(CONFIG_ARCH_APPLE) += t6022-j475d.dtb dtb-$(CONFIG_ARCH_APPLE) += t6022-j180d.dtb diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi index c31b9798d2617c..a41ca38a476814 100644 --- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi @@ -16,9 +16,11 @@ atcphy2 = &atcphy2; atcphy3 = &atcphy3; bluetooth0 = &bluetooth0; + #ifndef NO_DCP dcp = &dcp; disp0 = &display; disp0_piodma = &disp0_piodma; + #endif ethernet0 = ðernet0; serial0 = &serial0; wifi0 = &wifi0; diff --git a/arch/arm64/boot/dts/apple/t6020-j474s.dts b/arch/arm64/boot/dts/apple/t6020-j474s.dts index 557226c04deed9..ab0e50bbd49dd0 100644 --- a/arch/arm64/boot/dts/apple/t6020-j474s.dts +++ b/arch/arm64/boot/dts/apple/t6020-j474s.dts @@ -11,42 +11,12 @@ #include "t6020.dtsi" -/* - * These model is very similar to the previous generation Mac Studio, other than - * the GPIO indices. - */ - #define NO_PCIE_SDHC -#define NO_GPU -#include "t600x-j375.dtsi" +#include "t602x-j474-j475.dtsi" / { compatible = "apple,j474s", "apple,t6020", "apple,arm-platform"; model = "Apple Mac Mini (M2 Pro, 2023)"; - - aliases { - ethernet0 = ðernet0; - }; -}; - -&dcp { - status = "disabled"; -}; - -&hpm0 { - interrupts = <44 IRQ_TYPE_LEVEL_LOW>; -}; - -&hpm1 { - interrupts = <44 IRQ_TYPE_LEVEL_LOW>; -}; - -&hpm2 { - interrupts = <44 IRQ_TYPE_LEVEL_LOW>; -}; - -&hpm3 { - interrupts = <44 IRQ_TYPE_LEVEL_LOW>; }; &wifi0 { @@ -60,10 +30,6 @@ }; /* PCIe devices */ -&port00 { - pwren-gpios = <&smc_gpio 13 GPIO_ACTIVE_HIGH>; -}; - &port01 { /* * TODO: do not enable port without device. This works around a Linux @@ -74,33 +40,6 @@ status = "okay"; }; -&port02 { - /* 10 Gbit Ethernet */ - bus-range = <3 3>; - status = "okay"; - ethernet0: ethernet@0,0 { - reg = <0x30000 0x0 0x0 0x0 0x0>; - /* To be filled by the loader */ - local-mac-address = [00 10 18 00 00 00]; - }; -}; - -&port03 { - /* USB xHCI */ - pwren-gpios = <&smc_gpio 19 GPIO_ACTIVE_HIGH>; -}; - - -&speaker { - shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>; - interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>; -}; - -&jack_codec { - reset-gpios = <&pinctrl_nub 8 GPIO_ACTIVE_HIGH>; - interrupts-extended = <&pinctrl_ap 59 IRQ_TYPE_LEVEL_LOW>; -}; - &sound { compatible = "apple,j474-macaudio", "apple,j473-macaudio", "apple,macaudio"; model = "Mac mini J474"; diff --git a/arch/arm64/boot/dts/apple/t6021-j475c.dts b/arch/arm64/boot/dts/apple/t6021-j475c.dts new file mode 100644 index 00000000000000..591f637c4a6a98 --- /dev/null +++ b/arch/arm64/boot/dts/apple/t6021-j475c.dts @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * Mac Studio (M2 Max, 2023) + * + * target-type: J475c + * + * Copyright The Asahi Linux Contributors + */ + +/dts-v1/; + +#include "t6021.dtsi" +#include "t602x-j474-j475.dtsi" + +/ { + compatible = "apple,j475c", "apple,t6021", "apple,arm-platform"; + model = "Apple Mac Studio (M2 Max, 2023)"; +}; + +&wifi0 { + compatible = "pci14e4,4434"; + brcm,board-type = "apple,canary"; +}; + +&bluetooth0 { + compatible = "pci14e4,5f72"; + brcm,board-type = "apple,canary"; +}; + +&pinctrl_ap { + usb_hub_oe-hog { + gpio-hog; + gpios = <231 0>; + input; + line-name = "usb-hub-oe"; + }; + + usb_hub_rst-hog { + gpio-hog; + gpios = <232 GPIO_ACTIVE_LOW>; + output-low; + line-name = "usb-hub-rst"; + }; +}; + +&sound { + compatible = "apple,j475-macaudio", "apple,j375-macaudio", "apple,macaudio"; + model = "Mac Studio J475"; +}; diff --git a/arch/arm64/boot/dts/apple/t6022-j475d.dts b/arch/arm64/boot/dts/apple/t6022-j475d.dts new file mode 100644 index 00000000000000..43dba036456159 --- /dev/null +++ b/arch/arm64/boot/dts/apple/t6022-j475d.dts @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * Mac Studio (M2 Ultra, 2023) + * + * target-type: J475d + * + * Copyright The Asahi Linux Contributors + */ + +/dts-v1/; + +#define NO_DCP + +#include "t6022.dtsi" +#include "t602x-j474-j475.dtsi" +#include "t6022-jxxxd.dtsi" + +/ { + compatible = "apple,j475d", "apple,t6022", "apple,arm-platform"; + model = "Apple Mac Studio (M2 Ultra, 2023)"; + aliases { + atcphy4 = &atcphy0_die1; + atcphy5 = &atcphy1_die1; + }; +}; + +&framebuffer0 { + power-domains = <&ps_dispext0_cpu0_die1>, <&ps_dptx_phy_ps_die1>; +}; + +&typec4 { + label = "USB-C Front Right"; +}; + +&typec5 { + label = "USB-C Front Left"; +}; + +/* delete unused USB nodes on die 1 */ + +/delete-node/ &dwc3_2_dart_0_die1; +/delete-node/ &dwc3_2_dart_1_die1; +/delete-node/ &dwc3_2_die1; +/delete-node/ &atcphy2_die1; + +/delete-node/ &dwc3_3_dart_0_die1; +/delete-node/ &dwc3_3_dart_1_die1; +/delete-node/ &dwc3_3_die1; +/delete-node/ &atcphy3_die1; + + +/* delete unused always-on power-domains on die 1 */ + +/delete-node/ &ps_atc2_usb_aon_die1; +/delete-node/ &ps_atc2_usb_die1; + +/delete-node/ &ps_atc3_usb_aon_die1; +/delete-node/ &ps_atc3_usb_die1; + +&wifi0 { + compatible = "pci14e4,4434"; + brcm,board-type = "apple,canary"; +}; + +&bluetooth0 { + compatible = "pci14e4,5f72"; + brcm,board-type = "apple,canary"; +}; + +&sound { + compatible = "apple,j475-macaudio", "apple,j375-macaudio", "apple,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 new file mode 100644 index 00000000000000..0553e557d8becb --- /dev/null +++ b/arch/arm64/boot/dts/apple/t602x-j474-j475.dtsi @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * Mac Mini (M2 Pro, 2023) and Mac Studio (2023) + * + * This file contains the parts common to J474 and J475 devices with t6020, + * t6021 and t6022. + * + * target-type: J474s / J375c / J375d + * + * Copyright The Asahi Linux Contributors + */ + +/* + * These model is very similar to the previous generation Mac Studio, other than + * the GPIO indices. + */ + +#include "t600x-j375.dtsi" + +&framebuffer0 { + power-domains = <&ps_disp0_cpu0>, <&ps_dptx_phy_ps>; +}; + +/* disable dcp until it is supported */ +&dcp { + status = "disabled"; +}; + +&hpm0 { + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; +}; + +&hpm1 { + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; +}; + +&hpm2 { + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; +}; + +&hpm3 { + interrupts = <44 IRQ_TYPE_LEVEL_LOW>; +}; + +/* PCIe devices */ +&port00 { + pwren-gpios = <&smc_gpio 13 GPIO_ACTIVE_HIGH>; +}; + +#ifndef NO_PCIE_SDHC +&port01 { + pwren-gpios = <&smc_gpio 22 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + +&pcie0_dart_1 { + status = "okay"; +}; +#endif + +&port03 { + /* USB xHCI */ + pwren-gpios = <&smc_gpio 19 GPIO_ACTIVE_HIGH>; +}; + +&speaker { + shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>; + interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>; +}; + +&jack_codec { + reset-gpios = <&pinctrl_nub 8 GPIO_ACTIVE_HIGH>; + interrupts-extended = <&pinctrl_ap 59 IRQ_TYPE_LEVEL_LOW>; +}; From 3e852d66fcb01044c5755664d603603622436ce8 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 6 Sep 2023 12:47:36 +0200 Subject: [PATCH 0094/1020] arm64: dts: apple: t8112-j473: Add dptx-phy power-domain The HDMI output used by framebuffer0 requires the display controller and external DP phy power-domains to remain active. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112-j473.dts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j473.dts b/arch/arm64/boot/dts/apple/t8112-j473.dts index 92efe72b18461b..fa26d1d4be7ded 100644 --- a/arch/arm64/boot/dts/apple/t8112-j473.dts +++ b/arch/arm64/boot/dts/apple/t8112-j473.dts @@ -23,6 +23,15 @@ }; }; +&framebuffer0 { + power-domains = <&ps_disp0_cpu0>, <&ps_dptx_ext_phy>; +}; + +/* disable dcp until it is supported */ +&dcp { + status = "disabled"; +}; + /* * Provide labels for the USB type C ports. */ From e5bf6d1b53c15ffdea6ec1e1ac289abed66d55ea Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 25 Sep 2023 19:55:58 +0200 Subject: [PATCH 0095/1020] arm64: dts: apple: t6020x: Mark dptx_phy_ps only on laptops always-on The desktops will need to handle this on their own. On laptops it is a little weird since dcp seems to handle the programming of the phy which is apparently used for the internal display. It might be possible to move this to the panel node once dcp is upstream ready. The chosen.framebuffer node should reference the panel then. In the meantime keep it always-on on notebooks. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi | 7 +++++++ arch/arm64/boot/dts/apple/t602x-pmgr.dtsi | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi b/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi index dd1ea2b5dd95a8..280dc15f5a3b6c 100644 --- a/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi @@ -23,6 +23,13 @@ }; }; +/* HACK: keep dptx_phy_ps power-domain always-on + * it is unclear how to sequence with dcp for the integrated display + */ +&ps_dptx_phy_ps { + apple,always-on; +}; + &hpm0 { interrupts = <44 IRQ_TYPE_LEVEL_LOW>; }; diff --git a/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi b/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi index ea4372739e4fff..47b02b76bb1523 100644 --- a/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi @@ -1447,7 +1447,6 @@ #power-domain-cells = <0>; #reset-cells = <0>; label = DIE_LABEL(dptx_phy_ps); - apple,always-on; power-domains = <&DIE_NODE(ps_sio)>; }; From d2dd5966a4d1a284e2dfbee2273cd542512c85b3 Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sun, 3 Sep 2023 16:41:27 +1000 Subject: [PATCH 0096/1020] arm64: dts: apple: t8112: add opp-microwatt props to avalanche/blizzard Enable energy-aware scheduling on devices with the Apple M2 SoC (T8112) by adding experimentally measured opp-microwatt values to the application core OPP tables. Values are an approximation calculated by the System Management Controller, and collected using freqbench. Signed-off-by: James Calligeros --- arch/arm64/boot/dts/apple/t8112.dtsi | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index 4f92fb33b77110..4d3f10fe0e02e2 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -195,36 +195,43 @@ opp-hz = /bits/ 64 <600000000>; opp-level = <1>; clock-latency-ns = <7500>; + opp-microwatt = <26000>; }; opp02 { opp-hz = /bits/ 64 <912000000>; opp-level = <2>; clock-latency-ns = <20000>; + opp-microwatt = <56000>; }; opp03 { opp-hz = /bits/ 64 <1284000000>; opp-level = <3>; clock-latency-ns = <22000>; + opp-microwatt = <88000>; }; opp04 { opp-hz = /bits/ 64 <1752000000>; opp-level = <4>; clock-latency-ns = <30000>; + opp-microwatt = <155000>; }; opp05 { opp-hz = /bits/ 64 <2004000000>; opp-level = <5>; clock-latency-ns = <35000>; + opp-microwatt = <231000>; }; opp06 { opp-hz = /bits/ 64 <2256000000>; opp-level = <6>; clock-latency-ns = <39000>; + opp-microwatt = <254000>; }; opp07 { opp-hz = /bits/ 64 <2424000000>; opp-level = <7>; clock-latency-ns = <53000>; + opp-microwatt = <351000>; }; }; @@ -236,88 +243,105 @@ opp-hz = /bits/ 64 <660000000>; opp-level = <1>; clock-latency-ns = <9000>; + opp-microwatt = <133000>; }; opp02 { opp-hz = /bits/ 64 <924000000>; opp-level = <2>; clock-latency-ns = <19000>; + opp-microwatt = <212000>; }; opp03 { opp-hz = /bits/ 64 <1188000000>; opp-level = <3>; clock-latency-ns = <22000>; + opp-microwatt = <261000>; }; opp04 { opp-hz = /bits/ 64 <1452000000>; opp-level = <4>; clock-latency-ns = <24000>; + opp-microwatt = <345000>; }; opp05 { opp-hz = /bits/ 64 <1704000000>; opp-level = <5>; clock-latency-ns = <26000>; + opp-microwatt = <441000>; }; opp06 { opp-hz = /bits/ 64 <1968000000>; opp-level = <6>; clock-latency-ns = <28000>; + opp-microwatt = <619000>; }; opp07 { opp-hz = /bits/ 64 <2208000000>; opp-level = <7>; clock-latency-ns = <30000>; + opp-microwatt = <740000>; }; opp08 { opp-hz = /bits/ 64 <2400000000>; opp-level = <8>; clock-latency-ns = <33000>; + opp-microwatt = <855000>; }; opp09 { opp-hz = /bits/ 64 <2568000000>; opp-level = <9>; clock-latency-ns = <34000>; + opp-microwatt = <1006000>; }; opp10 { opp-hz = /bits/ 64 <2724000000>; opp-level = <10>; clock-latency-ns = <36000>; + opp-microwatt = <1217000>; }; opp11 { opp-hz = /bits/ 64 <2868000000>; opp-level = <11>; clock-latency-ns = <41000>; + opp-microwatt = <1534000>; }; opp12 { opp-hz = /bits/ 64 <2988000000>; opp-level = <12>; clock-latency-ns = <42000>; + opp-microwatt = <1714000>; }; opp13 { opp-hz = /bits/ 64 <3096000000>; opp-level = <13>; clock-latency-ns = <44000>; + opp-microwatt = <1877000>; }; opp14 { opp-hz = /bits/ 64 <3204000000>; opp-level = <14>; clock-latency-ns = <46000>; + opp-microwatt = <2159000>; }; opp15 { opp-hz = /bits/ 64 <3324000000>; opp-level = <15>; clock-latency-ns = <62000>; + opp-microwatt = <2393000>; turbo-mode; }; opp16 { opp-hz = /bits/ 64 <3408000000>; opp-level = <16>; clock-latency-ns = <62000>; + opp-microwatt = <2497000>; turbo-mode; }; opp17 { opp-hz = /bits/ 64 <3504000000>; opp-level = <17>; clock-latency-ns = <62000>; + opp-microwatt = <2648000>; turbo-mode; }; }; From 8ca740707dc632ac01509543e9955a7b6c4b00f9 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 17 Aug 2023 19:21:26 +0200 Subject: [PATCH 0097/1020] arm64: dts: apple: t600x-j375.dtsi: Add spi nor flash and nvram partition Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t600x-j375.dtsi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi index a41ca38a476814..fcacd7c74af110 100644 --- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi @@ -22,6 +22,7 @@ disp0_piodma = &disp0_piodma; #endif ethernet0 = ðernet0; + nvram = &nvram; serial0 = &serial0; wifi0 = &wifi0; }; @@ -398,3 +399,5 @@ apple,ppm-kp = <0.355>; }; #endif + +#include "spi1-nvram.dtsi" From 4006f9411682c978ed5f3c29a09d870bee0751f4 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 17 Aug 2023 19:21:26 +0200 Subject: [PATCH 0098/1020] arm64: dts: apple: t6022-j180.dtsi: Add spi nor flash and nvram partition Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t6022-j180d.dts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6022-j180d.dts b/arch/arm64/boot/dts/apple/t6022-j180d.dts index a2cb2c5b86bc10..be93805ee0417a 100644 --- a/arch/arm64/boot/dts/apple/t6022-j180d.dts +++ b/arch/arm64/boot/dts/apple/t6022-j180d.dts @@ -27,6 +27,7 @@ //bluetooth0 = &bluetooth0; //ethernet0 = ðernet0; //ethernet1 = ðernet1; + nvram = &nvram; serial0 = &serial0; //wifi0 = &wifi0; }; @@ -481,3 +482,5 @@ apple,perf-boost-min-util = <75>; apple,perf-tgt-utilization = <70>; }; + +#include "spi1-nvram.dtsi" From d00c7128db651ef6bf721e93576598a46fc09940 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 5 Oct 2023 12:36:01 +0900 Subject: [PATCH 0099/1020] arm64: dts: apple: t8103: Add nvram alias This is used by m1n1 to populate the nvram size automatically, since that turned out to be firmware-dependent. Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8103-jxxx.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi index 2b4136d6f77ee4..09ae9aa4f1e550 100644 --- a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi @@ -15,6 +15,7 @@ dcp = &dcp; disp0 = &display; disp0_piodma = &disp0_piodma; + nvram = &nvram; serial0 = &serial0; serial2 = &serial2; wifi0 = &wifi0; From 032eeee1b14833676562e75a0abd658b7e0d2955 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 5 Oct 2023 12:36:01 +0900 Subject: [PATCH 0100/1020] arm64: dts: apple: t8112: Add nvram alias This is used by m1n1 to populate the nvram size automatically, since that turned out to be firmware-dependent. Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8112-jxxx.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi b/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi index 5fec625bf5c2a6..fb93cedeb24a44 100644 --- a/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi +++ b/arch/arm64/boot/dts/apple/t8112-jxxx.dtsi @@ -16,6 +16,7 @@ dcp = &dcp; disp0 = &display; disp0_piodma = &disp0_piodma; + nvram = &nvram; serial0 = &serial0; serial2 = &serial2; }; From 013a910c4a5e83c7d3b7ac2905b8c97040587cb7 Mon Sep 17 00:00:00 2001 From: Eileen Yoon Date: Thu, 31 Aug 2023 19:10:27 +0900 Subject: [PATCH 0101/1020] arm64: dts: apple: t8103: Add ISP nodes Signed-off-by: Eileen Yoon --- arch/arm64/boot/dts/apple/t8103-pmgr.dtsi | 117 ++++++++++++++++++++++ arch/arm64/boot/dts/apple/t8103.dtsi | 55 ++++++++++ 2 files changed, 172 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi index 5fb8c8601a9dcb..fa989987866837 100644 --- a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi @@ -1003,6 +1003,123 @@ power-domains = <&ps_disp0_fe>; apple,min-state = <4>; }; + + /* There is a dependency tree involved with these PDs, + * but we do not express it here since the ISP driver + * is supposed to sequence them in the right order anyway + * (and we do not know the exact tree structure). + * + * This also works around spurious parent PD activation + * on machines with ISP disabled (desktops). + */ + ps_isp_set0: power-controller@4000 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4000 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set0"; + apple,force-disable; + }; + + ps_isp_set1: power-controller@4008 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4008 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set1"; + apple,force-disable; + apple,force-reset; + }; + + ps_isp_set2: power-controller@4010 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4010 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set2"; + apple,force-disable; + apple,force-reset; + }; + + ps_isp_fe: power-controller@4018 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4018 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_fe"; + }; + + ps_isp_set4: power-controller@4020 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4020 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set4"; + }; + + ps_isp_set5: power-controller@4028 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4028 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set5"; + }; + + ps_isp_set6: power-controller@4030 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4030 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set6"; + }; + + ps_isp_set7: power-controller@4038 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4038 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set7"; + }; + + ps_isp_set8: power-controller@4040 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4040 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set8"; + }; + + ps_isp_set9: power-controller@4048 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4048 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set9"; + }; + + ps_isp_set10: power-controller@4050 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4050 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set10"; + }; + + ps_isp_set11: power-controller@4058 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4058 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set11"; + }; + + ps_isp_set12: power-controller@4060 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4060 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set12"; + }; }; &pmgr_mini { diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index 762aaa2661dd5a..932b3fca3e1f72 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -608,6 +608,61 @@ phandle = <&display>; }; + isp_dart0: iommu@22c0e8000 { + compatible = "apple,t8103-dart"; + reg = <0x2 0x2c0e8000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_isp_sys>; + + status = "disabled"; + }; + + isp_dart1: iommu@22c0f4000 { + compatible = "apple,t8103-dart"; + reg = <0x2 0x2c0f4000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_isp_sys>; + + status = "disabled"; + }; + + isp_dart2: iommu@22c0fc000 { + compatible = "apple,t8103-dart"; + reg = <0x2 0x2c0fc000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_isp_sys>; + + status = "disabled"; + }; + + isp: isp@22a000000 { + compatible = "apple,t8103-isp", "apple,isp"; + iommus = <&isp_dart0 0>, <&isp_dart1 0>, <&isp_dart2 0>; + reg-names = "coproc", "mbox", "gpio", "mbox2"; + reg = <0x2 0x2a000000 0x0 0x2000000>, + <0x2 0x2c104000 0x0 0x100>, + <0x2 0x2c104170 0x0 0x100>, + <0x2 0x2c1043f0 0x0 0x100>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_isp_sys>, <&ps_isp_set0>, + <&ps_isp_set1>, <&ps_isp_set2>, <&ps_isp_fe>, + <&ps_isp_set4>, <&ps_isp_set5>, <&ps_isp_set6>, + <&ps_isp_set7>, <&ps_isp_set8>, <&ps_isp_set9>, + <&ps_isp_set10>, <&ps_isp_set11>, + <&ps_isp_set12>; + + apple,dart-vm-size = <0x0 0xa0000000>; + + status = "disabled"; + }; + sio_dart: iommu@235004000 { compatible = "apple,t8103-dart"; reg = <0x2 0x35004000 0x0 0x4000>; From 74f8985f846a6d72a2b12ab14a63a47df8d06741 Mon Sep 17 00:00:00 2001 From: Eileen Yoon Date: Sat, 2 Sep 2023 01:39:10 +0900 Subject: [PATCH 0102/1020] arm64: dts: apple: t6000: Add ISP nodes Signed-off-by: Eileen Yoon --- arch/arm64/boot/dts/apple/t600x-die0.dtsi | 49 ++++++++++++++ arch/arm64/boot/dts/apple/t600x-pmgr.dtsi | 80 +++++++++++++++++++++++ 2 files changed, 129 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index 6a43db684ed461..94b832b68b9226 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -505,6 +505,55 @@ #mbox-cells = <0>; }; + isp_dart0: iommu@3860e8000 { + compatible = "apple,t6000-dart"; + reg = <0x3 0x860e8000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_isp_sys>; + status = "disabled"; + }; + + isp_dart1: iommu@3860f4000 { + compatible = "apple,t6000-dart"; + reg = <0x3 0x860f4000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_isp_sys>; + status = "disabled"; + }; + + isp_dart2: iommu@3860fc000 { + compatible = "apple,t6000-dart"; + reg = <0x3 0x860fc000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_isp_sys>; + status = "disabled"; + }; + + isp: isp@384000000 { + compatible = "apple,t6000-isp", "apple,isp"; + iommus = <&isp_dart0 0>, <&isp_dart1 0>, <&isp_dart2 0>; + reg-names = "coproc", "mbox", "gpio", "mbox2"; + reg = <0x3 0x84000000 0x0 0x2000000>, + <0x3 0x86104000 0x0 0x100>, + <0x3 0x86104170 0x0 0x100>, + <0x3 0x861043f0 0x0 0x100>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_isp_sys>, <&ps_isp_set0>, + <&ps_isp_set1>, <&ps_isp_fe>, <&ps_isp_set3>, + <&ps_isp_set4>, <&ps_isp_set5>, <&ps_isp_set6>, + <&ps_isp_set7>, <&ps_isp_set8>; + apple,dart-vm-size = <0x0 0xa0000000>; + + status = "disabled"; + }; + pcie0_dart_0: iommu@581008000 { compatible = "apple,t6000-dart"; reg = <0x5 0x81008000 0x0 0x4000>; diff --git a/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi b/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi index af3baf871b22ee..429ab2969d048e 100644 --- a/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi @@ -1448,6 +1448,86 @@ label = DIE_LABEL(venc_me1); power-domains = <&DIE_NODE(ps_venc_me0)>; }; + + /* There is a dependency tree involved with these PDs, + * but we do not express it here since the ISP driver + * is supposed to sequence them in the right order anyway + * (and we do not know the exact tree structure). + * + * This also works around spurious parent PD activation + * on machines with ISP disabled (desktops). + */ + DIE_NODE(ps_isp_set0): power-controller@4000 { + compatible = "apple,t6000-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4000 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set0"; + }; + + DIE_NODE(ps_isp_set1): power-controller@4010 { + compatible = "apple,t6000-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4010 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set1"; + }; + + DIE_NODE(ps_isp_fe): power-controller@4008 { + compatible = "apple,t6000-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4008 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set2"; + }; + + DIE_NODE(ps_isp_set3): power-controller@4028 { + compatible = "apple,t6000-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4028 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set3"; + }; + + DIE_NODE(ps_isp_set4): power-controller@4020 { + compatible = "apple,t6000-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4020 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set4"; + }; + + DIE_NODE(ps_isp_set5): power-controller@4030 { + compatible = "apple,t6000-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4030 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set5"; + }; + + DIE_NODE(ps_isp_set6): power-controller@4018 { + compatible = "apple,t6000-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4018 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set6"; + }; + + DIE_NODE(ps_isp_set7): power-controller@4038 { + compatible = "apple,t6000-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4038 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set7"; + }; + + DIE_NODE(ps_isp_set8): power-controller@4040 { + compatible = "apple,t6000-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4040 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set8"; + }; }; &DIE_NODE(pmgr_south) { From 21413c9fd5afe28b80cb840b274bf7a5c9e3b418 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 8 Sep 2023 00:46:11 +0900 Subject: [PATCH 0103/1020] arm64: dts: apple: t8112: Add ISP nodes Co-developed-by: Janne Grunau Signed-off-by: Janne Grunau Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8112-pmgr.dtsi | 117 ++++++++++++++++++++++ arch/arm64/boot/dts/apple/t8112.dtsi | 51 ++++++++++ 2 files changed, 168 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi index 3828a1333dacae..c82436067c3c56 100644 --- a/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi @@ -961,6 +961,123 @@ apple,always-on; }; + /* There is a dependency tree involved with these PDs, + * but we do not express it here since the ISP driver + * is supposed to sequence them in the right order anyway + * (and we do not know the exact tree structure). + * + * This also works around spurious parent PD activation + * on machines with ISP disabled (desktops). + */ + ps_isp_set0: power-controller@4000 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4000 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set0"; + apple,force-disable; + }; + + ps_isp_set1: power-controller@4008 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4008 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set1"; + apple,force-disable; + apple,force-reset; + }; + + ps_isp_set2: power-controller@4010 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4010 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set2"; + apple,force-disable; + apple,force-reset; + }; + + ps_isp_fe: power-controller@4018 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4018 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_fe"; + }; + + ps_isp_set4: power-controller@4020 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4020 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set4"; + }; + + ps_isp_set5: power-controller@4028 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4028 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set5"; + }; + + ps_isp_set6: power-controller@4030 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4030 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set6"; + }; + + ps_isp_set7: power-controller@4038 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4038 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set7"; + }; + + ps_isp_set8: power-controller@4040 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4040 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set8"; + }; + + ps_isp_set9: power-controller@4048 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4048 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set9"; + }; + + ps_isp_set12: power-controller@4050 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4050 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set10"; + }; + + ps_isp_set10: power-controller@4058 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4058 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set11"; + }; + + ps_isp_set11: power-controller@4060 { + compatible = "apple,t8103-pmgr-pwrstate", "apple,pmgr-pwrstate"; + reg = <0x4060 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "isp_set12"; + }; + ps_venc_dma: power-controller@8000 { compatible = "apple,t8112-pmgr-pwrstate", "apple,pmgr-pwrstate"; reg = <0x8000 4>; diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index 4d3f10fe0e02e2..62128ef1f966ad 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -584,6 +584,57 @@ status = "disabled"; }; + isp_dart0: iommu@22c4a8000 { + compatible = "apple,t8110-dart"; + reg = <0x2 0x2c4a8000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_isp_sys>; + status = "disabled"; + }; + + isp_dart1: iommu@22c4b4000 { + compatible = "apple,t8110-dart"; + reg = <0x2 0x2c4b4000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_isp_sys>; + status = "disabled"; + }; + + isp_dart2: iommu@22c4bc000 { + compatible = "apple,t8110-dart"; + reg = <0x2 0x2c4bc000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_isp_sys>; + status = "disabled"; + }; + + isp: isp@22a000000 { + compatible = "apple,t8112-isp", "apple,isp"; + iommus = <&isp_dart0 0>, <&isp_dart1 0>, <&isp_dart2 0>; + reg-names = "coproc", "mbox", "gpio", "mbox2"; + reg = <0x2 0x2a000000 0x0 0x2000000>, + <0x2 0x2c4c4000 0x0 0x100>, + <0x2 0x2c4c41b0 0x0 0x100>, + <0x2 0x2c4c4430 0x0 0x100>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_isp_sys>, <&ps_isp_set0>, + <&ps_isp_set1>, <&ps_isp_set2>, <&ps_isp_fe>, + <&ps_isp_set4>, <&ps_isp_set5>, <&ps_isp_set6>, + <&ps_isp_set7>, <&ps_isp_set8>, <&ps_isp_set9>, + <&ps_isp_set10>, <&ps_isp_set11>, + <&ps_isp_set12>; + + apple,dart-vm-size = <0x0 0xa0000000>; + status = "disabled"; + }; + disp0_dart: iommu@231304000 { compatible = "apple,t8112-dart", "apple,t8110-dart"; reg = <0x2 0x31304000 0x0 0x4000>; From 957ad850ea5673d9e20ee51f4358e49e57daf0f0 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Sun, 24 Sep 2023 01:01:10 +0900 Subject: [PATCH 0104/1020] arm64: dts: apple: t602x: Add ISP nodes Signed-off-by: Asahi Lina --- arch/arm64/boot/dts/apple/t602x-die0.dtsi | 54 +++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t602x-die0.dtsi b/arch/arm64/boot/dts/apple/t602x-die0.dtsi index 332df752013e10..63040483d9e2c6 100644 --- a/arch/arm64/boot/dts/apple/t602x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-die0.dtsi @@ -261,6 +261,60 @@ }; + isp_dart0: iommu@3860e8000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x3 0x860e8000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_isp_sys>; + + apple,dma-range = <0x100 0x0 0x1 0x0>; + status = "disabled"; + }; + + isp_dart1: iommu@3860f4000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x3 0x860f4000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_isp_sys>; + + apple,dma-range = <0x100 0x0 0x1 0x0>; + status = "disabled"; + }; + + isp_dart2: iommu@3860fc000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x3 0x860fc000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_isp_sys>; + + apple,dma-range = <0x100 0x0 0x1 0x0>; + status = "disabled"; + }; + + isp: isp@384000000 { + compatible = "apple,t6020-isp", "apple,isp"; + iommus = <&isp_dart0 0>, <&isp_dart1 0>, <&isp_dart2 0>; + reg-names = "coproc", "mbox", "gpio", "mbox2"; + reg = <0x3 0x84000000 0x0 0x2000000>, + <0x3 0x86104000 0x0 0x100>, + <0x3 0x86104170 0x0 0x100>, + <0x3 0x861043f0 0x0 0x100>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_isp_cpu>, <&ps_isp_fe>, + <&ps_dprx>, <&ps_isp_vis>, <&ps_isp_be>, + <&ps_isp_clr>, <&ps_isp_raw>; + apple,dart-vm-size = <0x0 0xa0000000>; + + status = "disabled"; + }; + disp0_dart: iommu@389304000 { compatible = "apple,t6020-dart", "apple,t8110-dart"; reg = <0x3 0x89304000 0x0 0x4000>; From 26b21bb2a8cde684f0bb8e8f9b09fa300d5cd31f Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Thu, 28 Sep 2023 02:02:43 +0900 Subject: [PATCH 0105/1020] arm64: dts: ISP platform configs Signed-off-by: Asahi Lina --- arch/arm64/boot/dts/apple/isp-common.dtsi | 43 +++++++++ arch/arm64/boot/dts/apple/isp-imx248.dtsi | 22 +++++ arch/arm64/boot/dts/apple/isp-imx364.dtsi | 71 ++++++++++++++ .../arm64/boot/dts/apple/isp-imx558-cfg0.dtsi | 92 +++++++++++++++++++ arch/arm64/boot/dts/apple/isp-imx558.dtsi | 50 ++++++++++ .../arm64/boot/dts/apple/t600x-j314-j316.dtsi | 6 ++ .../arm64/boot/dts/apple/t602x-j414-j416.dtsi | 7 ++ arch/arm64/boot/dts/apple/t8103-j293.dts | 6 ++ arch/arm64/boot/dts/apple/t8103-j313.dts | 6 ++ arch/arm64/boot/dts/apple/t8103-j456.dts | 6 ++ arch/arm64/boot/dts/apple/t8103-j457.dts | 6 ++ arch/arm64/boot/dts/apple/t8112-j413.dts | 7 ++ arch/arm64/boot/dts/apple/t8112-j415.dts | 7 ++ arch/arm64/boot/dts/apple/t8112-j493.dts | 6 ++ 14 files changed, 335 insertions(+) create mode 100644 arch/arm64/boot/dts/apple/isp-common.dtsi create mode 100644 arch/arm64/boot/dts/apple/isp-imx248.dtsi create mode 100644 arch/arm64/boot/dts/apple/isp-imx364.dtsi create mode 100644 arch/arm64/boot/dts/apple/isp-imx558-cfg0.dtsi create mode 100644 arch/arm64/boot/dts/apple/isp-imx558.dtsi diff --git a/arch/arm64/boot/dts/apple/isp-common.dtsi b/arch/arm64/boot/dts/apple/isp-common.dtsi new file mode 100644 index 00000000000000..bf406772469b67 --- /dev/null +++ b/arch/arm64/boot/dts/apple/isp-common.dtsi @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * Common ISP configuration for Apple silicon platforms. + * + * Copyright The Asahi Linux Contributors + */ + +/ { + aliases { + isp = &isp; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + isp_heap: isp-heap { + compatible = "apple,asc-mem"; + /* Filled in by bootloder */ + reg = <0 0 0 0>; + no-map; + }; + }; +}; + +&isp { + memory-region = <&isp_heap>; + memory-region-names = "heap"; + status = "okay"; +}; + +&isp_dart0 { + status = "okay"; +}; + +&isp_dart1 { + status = "okay"; +}; + +&isp_dart2 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/apple/isp-imx248.dtsi b/arch/arm64/boot/dts/apple/isp-imx248.dtsi new file mode 100644 index 00000000000000..acad3ecf0331ef --- /dev/null +++ b/arch/arm64/boot/dts/apple/isp-imx248.dtsi @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * ISP configuration for platforms with IMX248 sensor. + * + * Copyright The Asahi Linux Contributors + */ + +#include "isp-common.dtsi" + +&isp { + apple,temporal-filter = <0>; + + sensor-presets { + /* 1280x720 */ + preset0 { + apple,config-index = <0>; + apple,input-size = <1296 736>; + apple,output-size = <1280 720>; + apple,crop = <8 8 1280 720>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/apple/isp-imx364.dtsi b/arch/arm64/boot/dts/apple/isp-imx364.dtsi new file mode 100644 index 00000000000000..55484d86523657 --- /dev/null +++ b/arch/arm64/boot/dts/apple/isp-imx364.dtsi @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * ISP configuration for platforms with IMX364 sensor. + * + * Copyright The Asahi Linux Contributors + */ + +#include "isp-common.dtsi" + +&isp { + apple,temporal-filter = <0>; + + sensor-presets { + /* 1920x1080 */ + preset0 { + apple,config-index = <0>; + apple,input-size = <1920 1080>; + apple,output-size = <1920 1080>; + apple,crop = <0 0 1920 1080>; + }; + /* 1440x720 (4:3) */ + preset1 { + apple,config-index = <0>; + apple,input-size = <1920 1080>; + apple,output-size = <1440 1080>; + apple,crop = <240 0 1440 1080>; + }; + /* 1280x720 (16:9) */ + preset2 { + apple,config-index = <0>; + apple,input-size = <1920 1080>; + apple,output-size = <1280 720>; + apple,crop = <0 0 1920 1080>; + }; + /* 960x720 (4:3) */ + preset3{ + apple,config-index = <0>; + apple,input-size = <1920 1080>; + apple,output-size = <960 720>; + apple,crop = <240 0 1440 1080>; + }; + /* 960x540 (16:9) */ + preset4 { + apple,config-index = <0>; + apple,input-size = <1920 1080>; + apple,output-size = <960 540>; + apple,crop = <0 0 1920 1080>; + }; + /* 640x480 (4:3) */ + preset5 { + apple,config-index = <0>; + apple,input-size = <1920 1080>; + apple,output-size = <640 480>; + apple,crop = <240 0 1440 1080>; + }; + /* 640x360 (16:9) */ + preset6 { + apple,config-index = <0>; + apple,input-size = <1920 1080>; + apple,output-size = <640 360>; + apple,crop = <0 0 1920 1080>; + }; + /* 320x180 (16:9) */ + preset7 { + apple,config-index = <0>; + apple,input-size = <1920 1080>; + apple,output-size = <320 180>; + apple,crop = <0 0 1920 1080>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/apple/isp-imx558-cfg0.dtsi b/arch/arm64/boot/dts/apple/isp-imx558-cfg0.dtsi new file mode 100644 index 00000000000000..729b97829cbb7e --- /dev/null +++ b/arch/arm64/boot/dts/apple/isp-imx558-cfg0.dtsi @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * ISP configuration for platforms with IMX558 sensor in + * config #0 mode. + * + * These platforms enable MLVNR for all configs except + * #0, which we don't support. Config #0 is an uncropped + * square 1920x1920 sensor, with dark corners. + * Therefore, we synthesize common resolutions by using + * crop/scale while always choosing config #0. + * + * Copyright The Asahi Linux Contributors + */ + +#include "isp-common.dtsi" + +&isp { + apple,temporal-filter = <0>; + + sensor-presets { + /* 1920x1080 */ + preset0 { + apple,config-index = <0>; + apple,input-size = <1920 1920>; + apple,output-size = <1920 1080>; + apple,crop = <0 420 1920 1080>; + }; + /* 1080x1920 */ + preset1 { + apple,config-index = <0>; + apple,input-size = <1920 1920>; + apple,output-size = <1080 1920>; + apple,crop = <420 0 1080 1920>; + }; + /* 1920x1440 */ + preset2 { + apple,config-index = <0>; + apple,input-size = <1920 1920>; + apple,output-size = <1920 1440>; + apple,crop = <0 240 1920 1440>; + }; + /* 1440x1920 */ + preset3 { + apple,config-index = <0>; + apple,input-size = <1920 1920>; + apple,output-size = <1440 1920>; + apple,crop = <240 0 1440 1920>; + }; + /* 1280x720 */ + preset4 { + apple,config-index = <0>; + apple,input-size = <1920 1920>; + apple,output-size = <1280 720>; + apple,crop = <0 420 1920 1080>; + }; + /* 720x1280 */ + preset5 { + apple,config-index = <0>; + apple,input-size = <1920 1920>; + apple,output-size = <720 1280>; + apple,crop = <420 0 1080 1920>; + }; + /* 1280x960 */ + preset6 { + apple,config-index = <0>; + apple,input-size = <1920 1920>; + apple,output-size = <1280 960>; + apple,crop = <0 240 1920 1440>; + }; + /* 960x1280 */ + preset7 { + apple,config-index = <0>; + apple,input-size = <1920 1920>; + apple,output-size = <960 1280>; + apple,crop = <240 0 1440 1920>; + }; + /* 640x480 */ + preset8 { + apple,config-index = <0>; + apple,input-size = <1920 1920>; + apple,output-size = <640 480>; + apple,crop = <0 240 1920 1440>; + }; + /* 480x640 */ + preset9 { + apple,config-index = <0>; + apple,input-size = <1920 1920>; + apple,output-size = <480 640>; + apple,crop = <240 0 1440 1920>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/apple/isp-imx558.dtsi b/arch/arm64/boot/dts/apple/isp-imx558.dtsi new file mode 100644 index 00000000000000..a23785b7d5e65a --- /dev/null +++ b/arch/arm64/boot/dts/apple/isp-imx558.dtsi @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * ISP configuration for platforms with IMX558 sensor. + * + * Copyright The Asahi Linux Contributors + */ + +#include "isp-common.dtsi" + +&isp { + apple,temporal-filter = <0>; + + sensor-presets { + /* 1920x1080 */ + preset0 { + apple,config-index = <1>; + apple,input-size = <1920 1080>; + apple,output-size = <1920 1080>; + apple,crop = <0 0 1920 1080>; + }; + /* 1080x1920 */ + preset1 { + apple,config-index = <2>; + apple,input-size = <1080 1920>; + apple,output-size = <1080 1920>; + apple,crop = <0 0 1080 1920>; + }; + /* 1760x1328 */ + preset2 { + apple,config-index = <3>; + apple,input-size = <1760 1328>; + apple,output-size = <1760 1328>; + apple,crop = <0 0 1760 1328>; + }; + /* 1328x1760 */ + preset3 { + apple,config-index = <4>; + apple,input-size = <1328 1760>; + apple,output-size = < 1328 1760>; + apple,crop = <0 0 1328 1760>; + }; + /* 1152x1152 */ + preset4 { + apple,config-index = <5>; + apple,input-size = <1152 1152>; + apple,output-size = <1152 1152>; + apple,crop = <0 0 1152 1152>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index 9475dbd2f2047e..ad282b663e3ac0 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -439,3 +439,9 @@ }; #include "spi1-nvram.dtsi" + +#include "isp-imx558.dtsi" + +&isp { + apple,platform-id = <3>; +}; diff --git a/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi b/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi index 280dc15f5a3b6c..9c2b5b7ce2beb3 100644 --- a/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi @@ -134,3 +134,10 @@ tp_accel { }; }; + +&isp { + apple,platform-id = <7>; + /delete-node/ sensor-presets; /* Override j31[46] below */ +}; + +#include "isp-imx558-cfg0.dtsi" diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts index c519a8975d95fa..e7dd28737e001c 100644 --- a/arch/arm64/boot/dts/apple/t8103-j293.dts +++ b/arch/arm64/boot/dts/apple/t8103-j293.dts @@ -209,3 +209,9 @@ }; }; }; + +#include "isp-imx248.dtsi" + +&isp { + apple,platform-id = <1>; +}; diff --git a/arch/arm64/boot/dts/apple/t8103-j313.dts b/arch/arm64/boot/dts/apple/t8103-j313.dts index cc05341b24def4..6e1478f5827ca0 100644 --- a/arch/arm64/boot/dts/apple/t8103-j313.dts +++ b/arch/arm64/boot/dts/apple/t8103-j313.dts @@ -151,3 +151,9 @@ }; }; }; + +#include "isp-imx248.dtsi" + +&isp { + apple,platform-id = <1>; +}; diff --git a/arch/arm64/boot/dts/apple/t8103-j456.dts b/arch/arm64/boot/dts/apple/t8103-j456.dts index c16a0594b1d2ca..b7cc5cb8a60af1 100644 --- a/arch/arm64/boot/dts/apple/t8103-j456.dts +++ b/arch/arm64/boot/dts/apple/t8103-j456.dts @@ -131,3 +131,9 @@ &gpu { apple,perf-base-pstate = <3>; }; + +#include "isp-imx364.dtsi" + +&isp { + apple,platform-id = <2>; +}; diff --git a/arch/arm64/boot/dts/apple/t8103-j457.dts b/arch/arm64/boot/dts/apple/t8103-j457.dts index c1d1201ecbe2cc..10742637efc2f3 100644 --- a/arch/arm64/boot/dts/apple/t8103-j457.dts +++ b/arch/arm64/boot/dts/apple/t8103-j457.dts @@ -104,3 +104,9 @@ &gpu { apple,perf-base-pstate = <3>; }; + +#include "isp-imx364.dtsi" + +&isp { + apple,platform-id = <2>; +}; diff --git a/arch/arm64/boot/dts/apple/t8112-j413.dts b/arch/arm64/boot/dts/apple/t8112-j413.dts index ceb93965cb4d44..920602617ade05 100644 --- a/arch/arm64/boot/dts/apple/t8112-j413.dts +++ b/arch/arm64/boot/dts/apple/t8112-j413.dts @@ -221,3 +221,10 @@ tp_accel { }; }; + +#include "isp-imx558-cfg0.dtsi" + +&isp { + apple,platform-id = <14>; + apple,temporal-filter = <1>; +}; diff --git a/arch/arm64/boot/dts/apple/t8112-j415.dts b/arch/arm64/boot/dts/apple/t8112-j415.dts index c502bed5f96224..8d7b700ce62607 100644 --- a/arch/arm64/boot/dts/apple/t8112-j415.dts +++ b/arch/arm64/boot/dts/apple/t8112-j415.dts @@ -237,3 +237,10 @@ tp_accel { }; }; + +#include "isp-imx558-cfg0.dtsi" + +&isp { + apple,platform-id = <15>; + apple,temporal-filter = <1>; +}; diff --git a/arch/arm64/boot/dts/apple/t8112-j493.dts b/arch/arm64/boot/dts/apple/t8112-j493.dts index 8b629e564c5019..dcf11e6ddb1ff5 100644 --- a/arch/arm64/boot/dts/apple/t8112-j493.dts +++ b/arch/arm64/boot/dts/apple/t8112-j493.dts @@ -244,3 +244,9 @@ tp_accel { }; }; + +#include "isp-imx248.dtsi" + +&isp { + apple,platform-id = <6>; +}; From 02724da443abaf5f22f7045442de1d4beb53ba23 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 7 Oct 2023 00:38:24 +0200 Subject: [PATCH 0106/1020] arm64: dts: apple: imx248: Add scaled and cropped presets Adds following resolution presets: - 960x720 (4:3) - 960x540 (16:9) - 640x480 (4:3) - 640x360 (16:9) - 320x180 (16:9) Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/isp-imx248.dtsi | 35 +++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/arch/arm64/boot/dts/apple/isp-imx248.dtsi b/arch/arm64/boot/dts/apple/isp-imx248.dtsi index acad3ecf0331ef..0a4ac1a0152c2c 100644 --- a/arch/arm64/boot/dts/apple/isp-imx248.dtsi +++ b/arch/arm64/boot/dts/apple/isp-imx248.dtsi @@ -18,5 +18,40 @@ apple,output-size = <1280 720>; apple,crop = <8 8 1280 720>; }; + /* 960x720 (4:3) */ + preset1 { + apple,config-index = <0>; + apple,input-size = <1296 736>; + apple,output-size = <960 720>; + apple,crop = <168 8 960 720>; + }; + /* 960x540 (16:9) */ + preset2 { + apple,config-index = <0>; + apple,input-size = <1296 736>; + apple,output-size = <960 540>; + apple,crop = <8 8 1280 720>; + }; + /* 640x480 (4:3) */ + preset3 { + apple,config-index = <0>; + apple,input-size = <1296 736>; + apple,output-size = <640 480>; + apple,crop = <168 8 960 720>; + }; + /* 640x360 (16:9) */ + preset4 { + apple,config-index = <0>; + apple,input-size = <1296 736>; + apple,output-size = <640 360>; + apple,crop = <8 8 1280 720>; + }; + /* 320x180 (16:9) */ + preset5 { + apple,config-index = <0>; + apple,input-size = <1296 736>; + apple,output-size = <320 180>; + apple,crop = <8 8 1280 720>; + }; }; }; From 0fd64b4b63d27947c4f5e48496ae246e7652ce53 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 8 Oct 2023 19:53:27 +0900 Subject: [PATCH 0107/1020] arm64: dts: apple: imx558: Add downscaled resolution presets To match those from cfg0. The 4:3 crops are different and this also has a 1:1 config, so we might want to unify things at some point... Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/isp-imx558.dtsi | 42 +++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/arch/arm64/boot/dts/apple/isp-imx558.dtsi b/arch/arm64/boot/dts/apple/isp-imx558.dtsi index a23785b7d5e65a..d55854c883f5b6 100644 --- a/arch/arm64/boot/dts/apple/isp-imx558.dtsi +++ b/arch/arm64/boot/dts/apple/isp-imx558.dtsi @@ -46,5 +46,47 @@ apple,output-size = <1152 1152>; apple,crop = <0 0 1152 1152>; }; + /* 1280x720 */ + preset5 { + apple,config-index = <1>; + apple,input-size = <1920 1080>; + apple,output-size = <1280 720>; + apple,crop = <0 0 1920 1080>; + }; + /* 720x1280 */ + preset6 { + apple,config-index = <2>; + apple,input-size = <1080 1920>; + apple,output-size = <720 1280>; + apple,crop = <0 0 1080 1920>; + }; + /* 1280x960 */ + preset7 { + apple,config-index = <3>; + apple,input-size = <1760 1328>; + apple,output-size = <1280 960>; + apple,crop = <0 4 1760 1320>; + }; + /* 960x1280 */ + preset8 { + apple,config-index = <4>; + apple,input-size = <1328 1760>; + apple,output-size = <960 1280>; + apple,crop = <4 0 1320 1760>; + }; + /* 640x480 */ + preset9 { + apple,config-index = <3>; + apple,input-size = <1760 1328>; + apple,output-size = <640 480>; + apple,crop = <0 4 1760 1320>; + }; + /* 480x640 */ + preset10 { + apple,config-index = <4>; + apple,input-size = <1328 1760>; + apple,output-size = <480 640>; + apple,crop = <4 0 1320 1760>; + }; }; }; From cd8e8fe54193c919b3ea866f6d113482abd27fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 20 Jan 2023 11:40:29 +0100 Subject: [PATCH 0108/1020] arm64: dts: apple: t8103-j274: Add speaker I/V sense slots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Specify TDM slots for the speaker amp IC to transmit I/V sense measurements in. Signed-off-by: Martin Povišer --- arch/arm64/boot/dts/apple/t8103-j274.dts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-j274.dts b/arch/arm64/boot/dts/apple/t8103-j274.dts index 0a69e2962c7c7f..b20300a5b18306 100644 --- a/arch/arm64/boot/dts/apple/t8103-j274.dts +++ b/arch/arm64/boot/dts/apple/t8103-j274.dts @@ -80,6 +80,9 @@ reg = <0x31>; shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; + ti,imon-slot-no = <0>; + ti,vmon-slot-no = <2>; + ti,sdout-zero-fill; }; }; From d0146eb21b8339335c7f75ba86f77d482ae3ba2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 20 Jan 2023 11:43:08 +0100 Subject: [PATCH 0109/1020] arm64: dts: apple: t600x-j314-j316: Add speaker I/V sense slots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Specify TDM slots for the speaker amp IC to transmit I/V sense measurements in. Make sure the channel order mirrors that of the playback PCM. Signed-off-by: Martin Povišer --- arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index ad282b663e3ac0..fb1649062a2b8c 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -195,6 +195,8 @@ #sound-dai-cells = <0>; sound-name-prefix = "Left Tweeter"; interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; + ti,imon-slot-no = <8>; + ti,vmon-slot-no = <10>; }; speaker_left_woof1: codec@38 { @@ -204,6 +206,8 @@ #sound-dai-cells = <0>; sound-name-prefix = "Left Woofer 1"; interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; + ti,imon-slot-no = <0>; + ti,vmon-slot-no = <2>; }; speaker_left_woof2: codec@39 { @@ -213,6 +217,8 @@ #sound-dai-cells = <0>; sound-name-prefix = "Left Woofer 2"; interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; + ti,imon-slot-no = <16>; + ti,vmon-slot-no = <18>; }; }; @@ -239,6 +245,8 @@ #sound-dai-cells = <0>; sound-name-prefix = "Right Tweeter"; interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; + ti,imon-slot-no = <12>; + ti,vmon-slot-no = <14>; }; speaker_right_woof1: codec@3b { @@ -248,6 +256,8 @@ #sound-dai-cells = <0>; sound-name-prefix = "Right Woofer 1"; interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; + ti,imon-slot-no = <4>; + ti,vmon-slot-no = <6>; }; speaker_right_woof2: codec@3c { @@ -257,6 +267,8 @@ #sound-dai-cells = <0>; sound-name-prefix = "Right Woofer 2"; interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; + ti,imon-slot-no = <20>; + ti,vmon-slot-no = <22>; }; }; From 1e6c9c038aa6441b2a2e1012eaa6967b14274e43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 24 Jan 2023 15:30:36 +0100 Subject: [PATCH 0110/1020] arm64: dts: apple: t600x-j314-j316: Zero out unused speaker sense slots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make one left codec and one right codec zero out the unused slots on their respective speaker sense buses. Internally, inside the SoC, the left and right sense buses are ORed, and zeroing-out the unused slots on one bus is required so as not to corrupt the data on the other. Signed-off-by: Martin Povišer --- arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index fb1649062a2b8c..8d67b6d5b2bfd6 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -208,6 +208,7 @@ interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <0>; ti,vmon-slot-no = <2>; + ti,sdout-force-zero-mask = <0xf0f0f0>; }; speaker_left_woof2: codec@39 { @@ -258,6 +259,7 @@ interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <4>; ti,vmon-slot-no = <6>; + ti,sdout-force-zero-mask = <0x0f0f0f>; }; speaker_right_woof2: codec@3c { From 930833b3a5e9c4e42770342057c73079e38d54c9 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 12 Oct 2023 23:20:26 +0900 Subject: [PATCH 0111/1020] arm64: dts: apple: t600x: Mark MCA power states as externally-clocked Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t600x-pmgr.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi b/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi index 429ab2969d048e..b8957a4c6359f1 100644 --- a/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi @@ -1113,6 +1113,7 @@ #reset-cells = <0>; label = DIE_LABEL(mca0); power-domains = <&DIE_NODE(ps_audio_p)>, <&DIE_NODE(ps_sio_adma)>; + apple,externally-clocked; }; DIE_NODE(ps_mca1): power-controller@290 { @@ -1122,6 +1123,7 @@ #reset-cells = <0>; label = DIE_LABEL(mca1); power-domains = <&DIE_NODE(ps_audio_p)>, <&DIE_NODE(ps_sio_adma)>; + apple,externally-clocked; }; DIE_NODE(ps_mca2): power-controller@298 { @@ -1131,6 +1133,7 @@ #reset-cells = <0>; label = DIE_LABEL(mca2); power-domains = <&DIE_NODE(ps_audio_p)>, <&DIE_NODE(ps_sio_adma)>; + apple,externally-clocked; }; DIE_NODE(ps_mca3): power-controller@2a0 { @@ -1140,6 +1143,7 @@ #reset-cells = <0>; label = DIE_LABEL(mca3); power-domains = <&DIE_NODE(ps_audio_p)>, <&DIE_NODE(ps_sio_adma)>; + apple,externally-clocked; }; DIE_NODE(ps_dpa0): power-controller@2a8 { From 813263874fd535ebe8ae583008c8509ef9a45327 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 12 Oct 2023 23:20:39 +0900 Subject: [PATCH 0112/1020] arm64: dts: apple: t602x: Mark MCA power states as externally-clocked Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t602x-pmgr.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi b/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi index 47b02b76bb1523..c3b1a1f50fff84 100644 --- a/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi @@ -1673,6 +1673,7 @@ #reset-cells = <0>; label = DIE_LABEL(mca0); power-domains = <&DIE_NODE(ps_audio_p)>, <&DIE_NODE(ps_sio_adma)>; + apple,externally-clocked; }; DIE_NODE(ps_mca1): power-controller@3a0 { @@ -1682,6 +1683,7 @@ #reset-cells = <0>; label = DIE_LABEL(mca1); power-domains = <&DIE_NODE(ps_audio_p)>, <&DIE_NODE(ps_sio_adma)>; + apple,externally-clocked; }; DIE_NODE(ps_mca2): power-controller@3a8 { @@ -1691,6 +1693,7 @@ #reset-cells = <0>; label = DIE_LABEL(mca2); power-domains = <&DIE_NODE(ps_audio_p)>, <&DIE_NODE(ps_sio_adma)>; + apple,externally-clocked; }; DIE_NODE(ps_mca3): power-controller@3b0 { @@ -1700,6 +1703,7 @@ #reset-cells = <0>; label = DIE_LABEL(mca3); power-domains = <&DIE_NODE(ps_audio_p)>, <&DIE_NODE(ps_sio_adma)>; + apple,externally-clocked; }; DIE_NODE(ps_dpa0): power-controller@3b8 { From 9d2057d68a9d9eab454afd646e22dd9926451ca4 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 12 Oct 2023 23:20:47 +0900 Subject: [PATCH 0113/1020] arm64: dts: apple: t8103: Mark MCA power states as externally-clocked Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8103-pmgr.dtsi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi index fa989987866837..4d1422d7e8b5b4 100644 --- a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi @@ -493,6 +493,7 @@ #reset-cells = <0>; label = "mca0"; power-domains = <&ps_audio_p>, <&ps_sio_adma>; + apple,externally-clocked; }; ps_mca1: power-controller@2c0 { @@ -502,6 +503,7 @@ #reset-cells = <0>; label = "mca1"; power-domains = <&ps_audio_p>, <&ps_sio_adma>; + apple,externally-clocked; }; ps_mca2: power-controller@2c8 { @@ -511,6 +513,7 @@ #reset-cells = <0>; label = "mca2"; power-domains = <&ps_audio_p>, <&ps_sio_adma>; + apple,externally-clocked; }; ps_mca3: power-controller@2d0 { @@ -520,6 +523,7 @@ #reset-cells = <0>; label = "mca3"; power-domains = <&ps_audio_p>, <&ps_sio_adma>; + apple,externally-clocked; }; ps_mca4: power-controller@2d8 { @@ -529,6 +533,7 @@ #reset-cells = <0>; label = "mca4"; power-domains = <&ps_audio_p>, <&ps_sio_adma>; + apple,externally-clocked; }; ps_mca5: power-controller@2e0 { @@ -538,6 +543,7 @@ #reset-cells = <0>; label = "mca5"; power-domains = <&ps_audio_p>, <&ps_sio_adma>; + apple,externally-clocked; }; ps_dpa0: power-controller@2e8 { From d3b4c0a4f20b263f0db947c45d660225df3bcd02 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 12 Oct 2023 23:20:56 +0900 Subject: [PATCH 0114/1020] arm64: dts: apple: t8112: Mark MCA power states as externally-clocked Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8112-pmgr.dtsi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi index c82436067c3c56..9ed831031ae6f0 100644 --- a/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi @@ -465,6 +465,7 @@ #reset-cells = <0>; label = "mca0"; power-domains = <&ps_sio_adma>, <&ps_audio_p>; + apple,externally-clocked; }; ps_mca1: power-controller@2c8 { @@ -474,6 +475,7 @@ #reset-cells = <0>; label = "mca1"; power-domains = <&ps_sio_adma>, <&ps_audio_p>; + apple,externally-clocked; }; ps_mca2: power-controller@2d0 { @@ -483,6 +485,7 @@ #reset-cells = <0>; label = "mca2"; power-domains = <&ps_sio_adma>, <&ps_audio_p>; + apple,externally-clocked; }; ps_mca3: power-controller@2d8 { @@ -492,6 +495,7 @@ #reset-cells = <0>; label = "mca3"; power-domains = <&ps_sio_adma>, <&ps_audio_p>; + apple,externally-clocked; }; ps_mca4: power-controller@2e0 { @@ -501,6 +505,7 @@ #reset-cells = <0>; label = "mca4"; power-domains = <&ps_sio_adma>, <&ps_audio_p>; + apple,externally-clocked; }; ps_mca5: power-controller@2e8 { @@ -510,6 +515,7 @@ #reset-cells = <0>; label = "mca5"; power-domains = <&ps_sio_adma>, <&ps_audio_p>; + apple,externally-clocked; }; ps_mcc: power-controller@2f0 { From c0ed6cda22ad8e4cac9f11bd585b4b5fd54a98ff Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 13 Oct 2023 01:36:54 +0900 Subject: [PATCH 0115/1020] arm64: dts: apple: t600x-j375: Add I/VMON slots to amp Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t600x-j375.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi index fcacd7c74af110..86bb83aa8b6c2b 100644 --- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi @@ -270,6 +270,8 @@ reg = <0x38>; shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; + ti,imon-slot-no = <0>; + ti,vmon-slot-no = <2>; }; }; From cb423d214b01422f514f549ce1a1d12cf34a7a46 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 13 Oct 2023 01:37:23 +0900 Subject: [PATCH 0116/1020] arm64: dts: apple: t600x-j180d: Add I/VMON slots to amps Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t6022-j180d.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6022-j180d.dts b/arch/arm64/boot/dts/apple/t6022-j180d.dts index be93805ee0417a..9afc14ae04a290 100644 --- a/arch/arm64/boot/dts/apple/t6022-j180d.dts +++ b/arch/arm64/boot/dts/apple/t6022-j180d.dts @@ -386,6 +386,8 @@ #sound-dai-cells = <0>; sound-name-prefix = "Tweeter"; interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>; + ti,imon-slot-no = <4>; + ti,vmon-slot-no = <6>; }; speaker_woofer: codec@39 { @@ -395,6 +397,8 @@ #sound-dai-cells = <0>; sound-name-prefix = "Woofer"; interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>; + ti,imon-slot-no = <0>; + ti,vmon-slot-no = <2>; }; }; From ee277b8934198edf867df143b711cf15e0cfaded Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 13 Oct 2023 01:38:24 +0900 Subject: [PATCH 0117/1020] arm64: dts: apple: t8112-j413: Add I/VMON slots to amps Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8112-j413.dts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j413.dts b/arch/arm64/boot/dts/apple/t8112-j413.dts index 920602617ade05..c056956d508c2e 100644 --- a/arch/arm64/boot/dts/apple/t8112-j413.dts +++ b/arch/arm64/boot/dts/apple/t8112-j413.dts @@ -102,6 +102,9 @@ 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>; + ti,sdout-force-zero-mask = <0xf0f0>; }; speaker_left_tweet: codec@39 { @@ -110,6 +113,8 @@ 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>; }; }; @@ -120,6 +125,9 @@ 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>; + ti,sdout-force-zero-mask = <0x0f0f>; }; speaker_right_tweet: codec@3c { @@ -128,6 +136,8 @@ 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 { From 3cbcff4821e0dbf40a89382d0903916fd407896f Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 13 Oct 2023 01:39:10 +0900 Subject: [PATCH 0118/1020] arm64: dts: apple: t8112-j415: Add I/VMON slots to amps Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8112-j415.dts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j415.dts b/arch/arm64/boot/dts/apple/t8112-j415.dts index 8d7b700ce62607..296c8075846f86 100644 --- a/arch/arm64/boot/dts/apple/t8112-j415.dts +++ b/arch/arm64/boot/dts/apple/t8112-j415.dts @@ -102,6 +102,9 @@ 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>; + ti,sdout-force-zero-mask = <0xf0f0f0>; }; speaker_left_tweet: codec@39 { @@ -110,6 +113,8 @@ 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 { @@ -118,6 +123,8 @@ 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>; }; }; @@ -128,6 +135,9 @@ 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>; + ti,sdout-force-zero-mask = <0x0f0f0f>; }; speaker_right_tweet: codec@3c { @@ -136,6 +146,8 @@ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Right Tweeter"; + ti,imon-slot-no = <12>; + ti,vmon-slot-no = <14>; }; speaker_right_woof2: codec@3d { @@ -144,6 +156,8 @@ 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 { From bd7ce91caeccbd6718287a473dd69d804e0bf65c Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 13 Oct 2023 01:39:19 +0900 Subject: [PATCH 0119/1020] arm64: dts: apple: t8112-j473: Add I/VMON slots to amp Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8112-j473.dts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j473.dts b/arch/arm64/boot/dts/apple/t8112-j473.dts index fa26d1d4be7ded..686426f9468d18 100644 --- a/arch/arm64/boot/dts/apple/t8112-j473.dts +++ b/arch/arm64/boot/dts/apple/t8112-j473.dts @@ -102,6 +102,8 @@ #sound-dai-cells = <0>; interrupt-parent = <&pinctrl_ap>; interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + ti,imon-slot-no = <0>; + ti,vmon-slot-no = <2>; }; jack_codec: codec@4b { From df1bb542ca4892bbeebfcb477337a8124d47181d Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 13 Oct 2023 01:42:30 +0900 Subject: [PATCH 0120/1020] arm64: dts: apple: t8112-j493: Add I/VMON slots to amps Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8112-j493.dts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j493.dts b/arch/arm64/boot/dts/apple/t8112-j493.dts index dcf11e6ddb1ff5..0f8a17439075a0 100644 --- a/arch/arm64/boot/dts/apple/t8112-j493.dts +++ b/arch/arm64/boot/dts/apple/t8112-j493.dts @@ -106,6 +106,8 @@ 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 { @@ -114,6 +116,9 @@ 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>; + ti,sdout-force-zero-mask = <0xf0f0>; }; }; @@ -124,6 +129,8 @@ 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 { @@ -132,6 +139,9 @@ 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>; + ti,sdout-force-zero-mask = <0x0f0f>; }; jack_codec: codec@4b { From 4314da04f584dff199bac11b71841521f31c7bbb Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 13 Oct 2023 01:43:09 +0900 Subject: [PATCH 0121/1020] arm64: dts: apple: t8103-j293: Add I/VMON slots to amps Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8103-j293.dts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts index e7dd28737e001c..42014a5763b384 100644 --- a/arch/arm64/boot/dts/apple/t8103-j293.dts +++ b/arch/arm64/boot/dts/apple/t8103-j293.dts @@ -111,6 +111,9 @@ 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>; + ti,pdm-slot-no = <12>; }; speaker_left_front: codec@32 { @@ -119,6 +122,10 @@ 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>; + ti,pdm-slot-no = <4>; + ti,sdout-pull-down; }; }; @@ -144,6 +151,9 @@ 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>; + ti,pdm-slot-no = <16>; }; speaker_right_front: codec@35 { @@ -152,6 +162,10 @@ 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>; + ti,pdm-slot-no = <8>; + ti,sdout-pull-down; }; }; From c0cdb879bccfac08265b0a0cd45f71cb0458c627 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 13 Oct 2023 01:43:20 +0900 Subject: [PATCH 0122/1020] arm64: dts: apple: t8103-j313: Add I/VMON slots to amps Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8103-j313.dts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-j313.dts b/arch/arm64/boot/dts/apple/t8103-j313.dts index 6e1478f5827ca0..a35fa16186fc09 100644 --- a/arch/arm64/boot/dts/apple/t8103-j313.dts +++ b/arch/arm64/boot/dts/apple/t8103-j313.dts @@ -91,6 +91,9 @@ 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>; + ti,sdout-zero-fill; }; }; @@ -101,6 +104,9 @@ 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>; + ti,sdout-zero-fill; }; jack_codec: codec@48 { From 60d5c6443b71cba0f42d3210d690950ba4a8c3b7 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 13 Oct 2023 02:44:48 +0900 Subject: [PATCH 0123/1020] arm64: dts: apple: j314/j316: Enable speakers Still gated in macaudio by the command line argument Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi | 9 --------- 1 file changed, 9 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index 8d67b6d5b2bfd6..f258e4a6e39baf 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -417,15 +417,6 @@ dai-link@0 { link-name = "Speakers"; - /* - * DANGER ZONE: You can blow your speakers! - * - * The drivers are not ready, and unless you are careful - * to attenuate the audio stream, you run the risk of - * blowing your speakers. - */ - status = "disabled"; - cpu { sound-dai = <&mca 0>, <&mca 1>; }; From be8d3e9956e1c4cfa87f0ea706ef23c9bb1a1b74 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 13 Oct 2023 02:48:03 +0900 Subject: [PATCH 0124/1020] arm64: dts: apple: j413: Enable speakers Still gated in macaudio by the command line argument Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8112-j413.dts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8112-j413.dts b/arch/arm64/boot/dts/apple/t8112-j413.dts index c056956d508c2e..ec5dbb69b3e850 100644 --- a/arch/arm64/boot/dts/apple/t8112-j413.dts +++ b/arch/arm64/boot/dts/apple/t8112-j413.dts @@ -166,15 +166,6 @@ dai-link@0 { link-name = "Speakers"; - /* - * DANGER ZONE: You can blow your speakers! - * - * The drivers are not ready, and unless you are careful - * to attenuate the audio stream, you run the risk of - * blowing your speakers. - */ - status = "disabled"; - cpu { sound-dai = <&mca 0>, <&mca 1>; }; From cc6edf6f5e1aa1099f20956ef8b74be9f589c8e7 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 13 Oct 2023 02:48:19 +0900 Subject: [PATCH 0125/1020] arm64: dts: apple: j415: Enable speakers Still gated in macaudio by the command line argument Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8112-j415.dts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8112-j415.dts b/arch/arm64/boot/dts/apple/t8112-j415.dts index 296c8075846f86..6f25131813a445 100644 --- a/arch/arm64/boot/dts/apple/t8112-j415.dts +++ b/arch/arm64/boot/dts/apple/t8112-j415.dts @@ -182,15 +182,6 @@ dai-link@0 { link-name = "Speakers"; - /* - * DANGER ZONE: You can blow your speakers! - * - * The drivers are not ready, and unless you are careful - * to attenuate the audio stream, you run the risk of - * blowing your speakers. - */ - status = "disabled"; - cpu { sound-dai = <&mca 0>, <&mca 1>; }; From de96721e092bc5ac51d99ff54d048495f77ee8de Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 14 Oct 2023 23:40:34 +0900 Subject: [PATCH 0126/1020] arm64: dts: apple: j375: Add missing speaker amp IRQs Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t600x-j375.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi index 86bb83aa8b6c2b..db3b5fe0b4f32a 100644 --- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi @@ -270,6 +270,7 @@ reg = <0x38>; shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; + interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <0>; ti,vmon-slot-no = <2>; }; From c2f87bc4b92d74f50c55a40ead360f526ad6bc6d Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 14 Oct 2023 23:42:00 +0900 Subject: [PATCH 0127/1020] arm64: dts: apple: j293: Add missing speaker amp IRQs Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8103-j293.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts index 42014a5763b384..865d7e56aba6bb 100644 --- a/arch/arm64/boot/dts/apple/t8103-j293.dts +++ b/arch/arm64/boot/dts/apple/t8103-j293.dts @@ -111,6 +111,7 @@ shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Left Rear"; + interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <8>; ti,vmon-slot-no = <10>; ti,pdm-slot-no = <12>; @@ -122,6 +123,7 @@ shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Left Front"; + interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <0>; ti,vmon-slot-no = <2>; ti,pdm-slot-no = <4>; @@ -151,6 +153,7 @@ shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Right Rear"; + interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <12>; ti,vmon-slot-no = <14>; ti,pdm-slot-no = <16>; @@ -162,6 +165,7 @@ shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Right Front"; + interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <4>; ti,vmon-slot-no = <6>; ti,pdm-slot-no = <8>; From d24c67833b184b15cdcd2376a2ef7b7eb6649a79 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 14 Oct 2023 23:42:13 +0900 Subject: [PATCH 0128/1020] arm64: dts: apple: j313: Add missing speaker amp IRQs Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8103-j313.dts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-j313.dts b/arch/arm64/boot/dts/apple/t8103-j313.dts index a35fa16186fc09..94a621666767aa 100644 --- a/arch/arm64/boot/dts/apple/t8103-j313.dts +++ b/arch/arm64/boot/dts/apple/t8103-j313.dts @@ -91,6 +91,7 @@ shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Left"; + interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <0>; ti,vmon-slot-no = <2>; ti,sdout-zero-fill; @@ -104,6 +105,7 @@ shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Right"; + interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <4>; ti,vmon-slot-no = <6>; ti,sdout-zero-fill; From 5a92303f93310b74361655f301223ac299258db7 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 14 Oct 2023 23:42:38 +0900 Subject: [PATCH 0129/1020] arm64: dts: apple: j413: Add missing speaker amp IRQs Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8112-j413.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j413.dts b/arch/arm64/boot/dts/apple/t8112-j413.dts index ec5dbb69b3e850..b3ac6c8c3f6261 100644 --- a/arch/arm64/boot/dts/apple/t8112-j413.dts +++ b/arch/arm64/boot/dts/apple/t8112-j413.dts @@ -102,6 +102,7 @@ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Left Woofer"; + interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <0>; ti,vmon-slot-no = <2>; ti,sdout-force-zero-mask = <0xf0f0>; @@ -113,6 +114,7 @@ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Left Tweeter"; + interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <8>; ti,vmon-slot-no = <10>; }; @@ -125,6 +127,7 @@ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Right Woofer"; + interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <4>; ti,vmon-slot-no = <6>; ti,sdout-force-zero-mask = <0x0f0f>; @@ -136,6 +139,7 @@ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Right Tweeter"; + interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <12>; ti,vmon-slot-no = <14>; }; From feb62ae781da40cdcfcaf76b9b5981a83d21c4dd Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 14 Oct 2023 23:43:13 +0900 Subject: [PATCH 0130/1020] arm64: dts: apple: j415: Add missing speaker amp IRQs Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8112-j415.dts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j415.dts b/arch/arm64/boot/dts/apple/t8112-j415.dts index 6f25131813a445..be75466a2b5788 100644 --- a/arch/arm64/boot/dts/apple/t8112-j415.dts +++ b/arch/arm64/boot/dts/apple/t8112-j415.dts @@ -102,6 +102,7 @@ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Left Woofer 1"; + interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <0>; ti,vmon-slot-no = <2>; ti,sdout-force-zero-mask = <0xf0f0f0>; @@ -113,6 +114,7 @@ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Left Tweeter"; + interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <8>; ti,vmon-slot-no = <10>; }; @@ -123,6 +125,7 @@ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Left Woofer 2"; + interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <16>; ti,vmon-slot-no = <18>; }; @@ -135,6 +138,7 @@ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Right Woofer 1"; + interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <4>; ti,vmon-slot-no = <6>; ti,sdout-force-zero-mask = <0x0f0f0f>; @@ -146,6 +150,7 @@ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Right Tweeter"; + interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <12>; ti,vmon-slot-no = <14>; }; @@ -156,6 +161,7 @@ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Right Woofer 2"; + interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <20>; ti,vmon-slot-no = <22>; }; From 46685ad3802793a99f8984810a0be39921fbeb2f Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 14 Oct 2023 23:43:37 +0900 Subject: [PATCH 0131/1020] arm64: dts: apple: j493: Add missing speaker amp IRQs Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8112-j493.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j493.dts b/arch/arm64/boot/dts/apple/t8112-j493.dts index 0f8a17439075a0..81fd99cc9b452b 100644 --- a/arch/arm64/boot/dts/apple/t8112-j493.dts +++ b/arch/arm64/boot/dts/apple/t8112-j493.dts @@ -106,6 +106,7 @@ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Left Rear"; + interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <8>; ti,vmon-slot-no = <10>; }; @@ -116,6 +117,7 @@ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Left Front"; + interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <0>; ti,vmon-slot-no = <2>; ti,sdout-force-zero-mask = <0xf0f0>; @@ -129,6 +131,7 @@ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Right Rear"; + interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <12>; ti,vmon-slot-no = <14>; }; @@ -139,6 +142,7 @@ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; #sound-dai-cells = <0>; sound-name-prefix = "Right Front"; + interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; ti,imon-slot-no = <4>; ti,vmon-slot-no = <6>; ti,sdout-force-zero-mask = <0x0f0f>; From e6650fda8b14a550039d3d39fe8ecd87dd736116 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 21 Oct 2023 22:18:06 +0900 Subject: [PATCH 0132/1020] arm64: dts: apple: j413: Model SDZ GPIO as a regulator Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8112-j413.dts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8112-j413.dts b/arch/arm64/boot/dts/apple/t8112-j413.dts index b3ac6c8c3f6261..d5104c24d2e4e3 100644 --- a/arch/arm64/boot/dts/apple/t8112-j413.dts +++ b/arch/arm64/boot/dts/apple/t8112-j413.dts @@ -95,11 +95,22 @@ }; }; +/* Virtual regulator representing the shared shutdown GPIO */ +/ { + speaker_sdz: fixed-regulator-sn012776-sdz { + compatible = "regulator-fixed"; + regulator-name = "sn012776-sdz"; + startup-delay-us = <5000>; + gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; +}; + &i2c1 { speaker_left_woof: codec@38 { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x38>; - shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Left Woofer"; interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; @@ -111,7 +122,7 @@ speaker_left_tweet: codec@39 { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x39>; - shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Left Tweeter"; interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; @@ -124,7 +135,7 @@ speaker_right_woof: codec@3b { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x3b>; - shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Right Woofer"; interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; @@ -136,7 +147,7 @@ speaker_right_tweet: codec@3c { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x3c>; - shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Right Tweeter"; interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; From c8f124483e6c79a0303c05460d9f91e2428e15f3 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 28 Oct 2023 22:52:20 +0900 Subject: [PATCH 0133/1020] arm64: dts: apple: t600x-j314-j316: Set sound compatibles per device j314 and j316 have different speakers, so should have different compatibles. In addition, j414 and j416 will include this config but should have their own compatibles. Move the compatibles to the individual device files. Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t6000-j314s.dts | 1 + arch/arm64/boot/dts/apple/t6000-j316s.dts | 1 + arch/arm64/boot/dts/apple/t6001-j314c.dts | 1 + arch/arm64/boot/dts/apple/t6001-j316c.dts | 1 + arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi | 2 +- 5 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/apple/t6000-j314s.dts b/arch/arm64/boot/dts/apple/t6000-j314s.dts index a2ccc654216164..ae79e3236614be 100644 --- a/arch/arm64/boot/dts/apple/t6000-j314s.dts +++ b/arch/arm64/boot/dts/apple/t6000-j314s.dts @@ -33,5 +33,6 @@ }; &sound { + compatible = "apple,j314-macaudio", "apple,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 99a76392b3b791..272fa1c1712479 100644 --- a/arch/arm64/boot/dts/apple/t6000-j316s.dts +++ b/arch/arm64/boot/dts/apple/t6000-j316s.dts @@ -33,5 +33,6 @@ }; &sound { + compatible = "apple,j316-macaudio", "apple,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 82d851d4cd3857..81d34507ed81ff 100644 --- a/arch/arm64/boot/dts/apple/t6001-j314c.dts +++ b/arch/arm64/boot/dts/apple/t6001-j314c.dts @@ -33,5 +33,6 @@ }; &sound { + compatible = "apple,j314-macaudio", "apple,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 a6987c8324dbd7..564d927f2fecbd 100644 --- a/arch/arm64/boot/dts/apple/t6001-j316c.dts +++ b/arch/arm64/boot/dts/apple/t6001-j316c.dts @@ -33,5 +33,6 @@ }; &sound { + compatible = "apple,j316-macaudio", "apple,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 f258e4a6e39baf..ac49a38917432b 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -412,7 +412,7 @@ / { sound: sound { - compatible = "apple,j314-macaudio", "apple,macaudio"; + /* compatible is set per machine */ dai-link@0 { link-name = "Speakers"; From c40dad89cf0bc64ac46ca05a593448ee12fcfcb9 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 28 Oct 2023 22:58:36 +0900 Subject: [PATCH 0134/1020] arm64: dts: apple: j375 & friends: Enable speakers Still gated in macaudio by the command line argument Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t600x-j375.dtsi | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi index db3b5fe0b4f32a..d8db13fc02e3f6 100644 --- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi @@ -299,14 +299,7 @@ dai-link@0 { link-name = "Speaker"; - /* - * DANGER ZONE: You can blow your speakers! - * - * The drivers are not ready, and unless you are careful - * to attenuate the audio stream, you run the risk of - * blowing your speakers. - */ - status = "disabled"; + cpu { sound-dai = <&mca 0>; }; From 0eaf9efc2dc915c14c467fe7dbc68c8931e21307 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 28 Oct 2023 22:58:49 +0900 Subject: [PATCH 0135/1020] arm64: dts: apple: j293: Enable speakers Still gated in macaudio by the command line argument Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8103-j293.dts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts index 865d7e56aba6bb..c6ef45fad387c2 100644 --- a/arch/arm64/boot/dts/apple/t8103-j293.dts +++ b/arch/arm64/boot/dts/apple/t8103-j293.dts @@ -197,15 +197,6 @@ dai-link@0 { link-name = "Speakers"; - /* - * DANGER ZONE: You can blow your speakers! - * - * The drivers are not ready, and unless you are careful - * to attenuate the audio stream, you run the risk of - * blowing your speakers. - */ - status = "disabled"; - cpu { sound-dai = <&mca 0>, <&mca 1>; }; From 99b3094726687fec3d3a5129e406ba5be87bea00 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 28 Oct 2023 22:58:58 +0900 Subject: [PATCH 0136/1020] arm64: dts: apple: j313: Enable speakers Still gated in macaudio by the command line argument Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8103-j313.dts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8103-j313.dts b/arch/arm64/boot/dts/apple/t8103-j313.dts index 94a621666767aa..bc2ec8c7cb6862 100644 --- a/arch/arm64/boot/dts/apple/t8103-j313.dts +++ b/arch/arm64/boot/dts/apple/t8103-j313.dts @@ -130,14 +130,6 @@ dai-link@0 { link-name = "Speakers"; - /* - * DANGER ZONE: You can blow your speakers! - * - * The drivers are not ready, and unless you are careful - * to attenuate the audio stream, you run the risk of - * blowing your speakers. - */ - status = "disabled"; cpu { sound-dai = <&mca 0>, <&mca 1>; From 315444ea5190a5e6c7526e9d78816fb7ac6e1468 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 28 Oct 2023 22:59:05 +0900 Subject: [PATCH 0137/1020] arm64: dts: apple: j493: Enable speakers Still gated in macaudio by the command line argument Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8112-j493.dts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8112-j493.dts b/arch/arm64/boot/dts/apple/t8112-j493.dts index 81fd99cc9b452b..f1589366d48631 100644 --- a/arch/arm64/boot/dts/apple/t8112-j493.dts +++ b/arch/arm64/boot/dts/apple/t8112-j493.dts @@ -174,15 +174,6 @@ dai-link@0 { link-name = "Speakers"; - /* - * DANGER ZONE: You can blow your speakers! - * - * The drivers are not ready, and unless you are careful - * to attenuate the audio stream, you run the risk of - * blowing your speakers. - */ - status = "disabled"; - cpu { sound-dai = <&mca 0>, <&mca 1>; }; From c40308be533a638e7b79e2ca0a7abebb8eb5ee1f Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sun, 29 Oct 2023 08:55:40 +1000 Subject: [PATCH 0138/1020] arm64: dts: apple: describe shared SDZ GPIO for tas2764 machines with the tas2764 amp codec share a GPIO line for asserting/deasserting the SDZ pin on the chips. describe this as a regulator to facilitate chip reset on suspend/resume Signed-off-by: James Calligeros --- .../arm64/boot/dts/apple/t600x-j314-j316.dtsi | 23 ++++++++++++++----- .../arm64/boot/dts/apple/t602x-j414-j416.dtsi | 11 ++++----- arch/arm64/boot/dts/apple/t8112-j415.dts | 23 ++++++++++++++----- arch/arm64/boot/dts/apple/t8112-j493.dts | 19 +++++++++++---- 4 files changed, 54 insertions(+), 22 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index ac49a38917432b..2631daf667e14e 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -185,13 +185,24 @@ }; }; +/* Virtual regulator representing the shared shutdown GPIO */ +/ { + speaker_sdz: fixed-regulator-sn012776-sdz { + compatible = "regulator-fixed"; + regulator-name = "sn012776-sdz"; + startup-delay-us = <5000>; + gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; +}; + &i2c1 { status = "okay"; speaker_left_tweet: codec@3a { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x3a>; - shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Left Tweeter"; interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; @@ -202,7 +213,7 @@ speaker_left_woof1: codec@38 { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x38>; - shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Left Woofer 1"; interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; @@ -214,7 +225,7 @@ speaker_left_woof2: codec@39 { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x39>; - shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Left Woofer 2"; interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; @@ -242,7 +253,7 @@ speaker_right_tweet: codec@3d { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x3d>; - shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Right Tweeter"; interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; @@ -253,7 +264,7 @@ speaker_right_woof1: codec@3b { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x3b>; - shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Right Woofer 1"; interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; @@ -265,7 +276,7 @@ speaker_right_woof2: codec@3c { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x3c>; - shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Right Woofer 2"; interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>; diff --git a/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi b/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi index 9c2b5b7ce2beb3..9eb19bfef4171a 100644 --- a/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi @@ -46,33 +46,32 @@ interrupts = <44 IRQ_TYPE_LEVEL_LOW>; }; +/* Redefine GPIO for SDZ */ +&speaker_sdz { + gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>; +}; + &speaker_left_tweet { - shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>; interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>; }; &speaker_left_woof1 { - shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>; interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>; }; &speaker_left_woof2 { - shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>; interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>; }; &speaker_right_tweet { - shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>; interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>; }; &speaker_right_woof1 { - shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>; interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>; }; &speaker_right_woof2 { - shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>; interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>; }; diff --git a/arch/arm64/boot/dts/apple/t8112-j415.dts b/arch/arm64/boot/dts/apple/t8112-j415.dts index be75466a2b5788..82f86859efdbea 100644 --- a/arch/arm64/boot/dts/apple/t8112-j415.dts +++ b/arch/arm64/boot/dts/apple/t8112-j415.dts @@ -95,11 +95,22 @@ }; }; +/* Virtual regulator representing the shared shutdown GPIO */ +/ { + speaker_sdz: fixed-regulator-sn012776-sdz { + compatible = "regulator-fixed"; + regulator-name = "sn012776-sdz"; + startup-delay-us = <5000>; + gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; +}; + &i2c1 { speaker_left_woof1: codec@38 { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x38>; - shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Left Woofer 1"; interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; @@ -111,7 +122,7 @@ speaker_left_tweet: codec@39 { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x39>; - shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Left Tweeter"; interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; @@ -122,7 +133,7 @@ speaker_left_woof2: codec@3a { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x3a>; - shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Left Woofer 2"; interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; @@ -135,7 +146,7 @@ speaker_right_woof1: codec@3b { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x3b>; - shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Right Woofer 1"; interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; @@ -147,7 +158,7 @@ speaker_right_tweet: codec@3c { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x3c>; - shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Right Tweeter"; interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; @@ -158,7 +169,7 @@ speaker_right_woof2: codec@3d { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x3d>; - shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Right Woofer 2"; interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; diff --git a/arch/arm64/boot/dts/apple/t8112-j493.dts b/arch/arm64/boot/dts/apple/t8112-j493.dts index f1589366d48631..0624d854b5542e 100644 --- a/arch/arm64/boot/dts/apple/t8112-j493.dts +++ b/arch/arm64/boot/dts/apple/t8112-j493.dts @@ -99,11 +99,22 @@ label = "USB-C Left-front"; }; +/* Virtual regulator representing the shared shutdown GPIO */ +/ { + speaker_sdz: fixed-regulator-sn012776-sdz { + compatible = "regulator-fixed"; + regulator-name = "sn012776-sdz"; + startup-delay-us = <5000>; + gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; +}; + &i2c1 { speaker_left_rear: codec@38 { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x38>; - shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Left Rear"; interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; @@ -114,7 +125,7 @@ speaker_left_front: codec@39 { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x39>; - shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Left Front"; interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; @@ -128,7 +139,7 @@ speaker_right_rear: codec@3b { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x3b>; - shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Right Rear"; interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; @@ -139,7 +150,7 @@ speaker_right_front: codec@3c { compatible = "ti,sn012776", "ti,tas2764"; reg = <0x3c>; - shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Right Front"; interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>; From 93efee59313f37779f22067e6cfa7ad69170ee24 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 29 Oct 2023 17:35:00 +0900 Subject: [PATCH 0139/1020] arm64: dts: apple: j293: Model SDZ GPIO as a regulator Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8103-j293.dts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts index c6ef45fad387c2..bb8b878630bac1 100644 --- a/arch/arm64/boot/dts/apple/t8103-j293.dts +++ b/arch/arm64/boot/dts/apple/t8103-j293.dts @@ -104,11 +104,22 @@ }; }; +/* Virtual regulator representing the shared shutdown GPIO */ +/ { + speaker_sdz: fixed-regulator-tas5770-sdz { + compatible = "regulator-fixed"; + regulator-name = "tas5770-sdz"; + startup-delay-us = <5000>; + gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; +}; + &i2c1 { speaker_left_rear: codec@31 { compatible = "ti,tas5770l", "ti,tas2770"; reg = <0x31>; - shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Left Rear"; interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>; @@ -120,7 +131,7 @@ speaker_left_front: codec@32 { compatible = "ti,tas5770l", "ti,tas2770"; reg = <0x32>; - shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Left Front"; interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>; @@ -150,7 +161,7 @@ speaker_right_rear: codec@34 { compatible = "ti,tas5770l", "ti,tas2770"; reg = <0x34>; - shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Right Rear"; interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>; @@ -162,7 +173,7 @@ speaker_right_front: codec@35 { compatible = "ti,tas5770l", "ti,tas2770"; reg = <0x35>; - shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Right Front"; interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>; From ea605381505ae86d87b148231b14f8a416c21197 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 29 Oct 2023 17:35:16 +0900 Subject: [PATCH 0140/1020] arm64: dts: apple: j313: Model SDZ GPIO as a regulator Signed-off-by: Hector Martin --- arch/arm64/boot/dts/apple/t8103-j313.dts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8103-j313.dts b/arch/arm64/boot/dts/apple/t8103-j313.dts index bc2ec8c7cb6862..dc96f17aea2fed 100644 --- a/arch/arm64/boot/dts/apple/t8103-j313.dts +++ b/arch/arm64/boot/dts/apple/t8103-j313.dts @@ -84,11 +84,22 @@ }; }; +/* Virtual regulator representing the shared shutdown GPIO */ +/ { + speaker_sdz: fixed-regulator-tas5770-sdz { + compatible = "regulator-fixed"; + regulator-name = "tas5770-sdz"; + startup-delay-us = <5000>; + gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; +}; + &i2c1 { speaker_left: codec@31 { compatible = "ti,tas5770l", "ti,tas2770"; reg = <0x31>; - shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Left"; interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>; @@ -102,7 +113,7 @@ speaker_right: codec@34 { compatible = "ti,tas5770l", "ti,tas2770"; reg = <0x34>; - shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>; + SDZ-supply = <&speaker_sdz>; #sound-dai-cells = <0>; sound-name-prefix = "Right"; interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>; From be1a6331dbe0bc0dc2dd6fad1d9edeb6cf6bde1a Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Mon, 6 Nov 2023 20:33:51 +1000 Subject: [PATCH 0141/1020] arm64: dts: apple: add opp-microwatt to t8103/t600x This patch adds measured opp-microwatt values for the Firestorm and Icestorm application cores found in Apple's T8103 (M1), T6000 (M1 Pro), T6001 (M1 Max) and T6002 (M1 Ultra) SoCs. Values were measured from the System Management Controller's core cluster power meter. A version of freqbench modified to read this power meter was used to orchestrate testing, running 1,000,000 iterations of coremark on a single core from each cluster at each operating point. The bulk of the testing was done on a T6000. Note that Apple calibrates voltage regulator settings for each SoC as they come off the assembly line, introducing some natural variance between machines. Testing across multiple machines with identical SoCs reveals no measurable impact on the accuracy of the EM subsystem's cost calculations. Signed-off-by: James Calligeros --- arch/arm64/boot/dts/apple/t600x-common.dtsi | 20 ++++++++++++++++++++ arch/arm64/boot/dts/apple/t8103.dtsi | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-common.dtsi b/arch/arm64/boot/dts/apple/t600x-common.dtsi index 667c02724b8646..5f62329b628a39 100644 --- a/arch/arm64/boot/dts/apple/t600x-common.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-common.dtsi @@ -229,26 +229,31 @@ opp-hz = /bits/ 64 <600000000>; opp-level = <1>; clock-latency-ns = <7500>; + opp-microwatt = <47296>; }; opp02 { opp-hz = /bits/ 64 <972000000>; opp-level = <2>; clock-latency-ns = <23000>; + opp-microwatt = <99715>; }; opp03 { opp-hz = /bits/ 64 <1332000000>; opp-level = <3>; clock-latency-ns = <29000>; + opp-microwatt = <188860>; }; opp04 { opp-hz = /bits/ 64 <1704000000>; opp-level = <4>; clock-latency-ns = <40000>; + opp-microwatt = <288891>; }; opp05 { opp-hz = /bits/ 64 <2064000000>; opp-level = <5>; clock-latency-ns = <50000>; + opp-microwatt = <412979>; }; }; @@ -259,78 +264,93 @@ opp-hz = /bits/ 64 <600000000>; opp-level = <1>; clock-latency-ns = <8000>; + opp-microwatt = <290230>; }; opp02 { opp-hz = /bits/ 64 <828000000>; opp-level = <2>; clock-latency-ns = <18000>; + opp-microwatt = <449013>; }; opp03 { opp-hz = /bits/ 64 <1056000000>; opp-level = <3>; clock-latency-ns = <19000>; + opp-microwatt = <647097>; }; opp04 { opp-hz = /bits/ 64 <1296000000>; opp-level = <4>; clock-latency-ns = <23000>; + opp-microwatt = <865620>; }; opp05 { opp-hz = /bits/ 64 <1524000000>; opp-level = <5>; clock-latency-ns = <24000>; + opp-microwatt = <1112838>; }; opp06 { opp-hz = /bits/ 64 <1752000000>; opp-level = <6>; clock-latency-ns = <28000>; + opp-microwatt = <1453271>; }; opp07 { opp-hz = /bits/ 64 <1980000000>; opp-level = <7>; clock-latency-ns = <31000>; + opp-microwatt = <1776667>; }; opp08 { opp-hz = /bits/ 64 <2208000000>; opp-level = <8>; clock-latency-ns = <45000>; + opp-microwatt = <2366690>; }; opp09 { opp-hz = /bits/ 64 <2448000000>; opp-level = <9>; clock-latency-ns = <49000>; + opp-microwatt = <2892193>; }; opp10 { opp-hz = /bits/ 64 <2676000000>; opp-level = <10>; clock-latency-ns = <53000>; + opp-microwatt = <3475417>; }; opp11 { opp-hz = /bits/ 64 <2904000000>; opp-level = <11>; clock-latency-ns = <56000>; + opp-microwatt = <3959410>; }; opp12 { opp-hz = /bits/ 64 <3036000000>; opp-level = <12>; clock-latency-ns = <56000>; + opp-microwatt = <4540620>; }; opp13 { opp-hz = /bits/ 64 <3132000000>; opp-level = <13>; clock-latency-ns = <56000>; + opp-microwatt = <4745031>; turbo-mode; }; opp14 { opp-hz = /bits/ 64 <3168000000>; opp-level = <14>; clock-latency-ns = <56000>; + opp-microwatt = <4822390>; turbo-mode; }; opp15 { opp-hz = /bits/ 64 <3228000000>; opp-level = <15>; clock-latency-ns = <56000>; + opp-microwatt = <4951324>; turbo-mode; }; }; diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index 932b3fca3e1f72..30fe535b4b308c 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -194,26 +194,31 @@ opp-hz = /bits/ 64 <600000000>; opp-level = <1>; clock-latency-ns = <7500>; + opp-microwatt = <47296>; }; opp02 { opp-hz = /bits/ 64 <972000000>; opp-level = <2>; clock-latency-ns = <22000>; + opp-microwatt = <99715>; }; opp03 { opp-hz = /bits/ 64 <1332000000>; opp-level = <3>; clock-latency-ns = <27000>; + opp-microwatt = <188860>; }; opp04 { opp-hz = /bits/ 64 <1704000000>; opp-level = <4>; clock-latency-ns = <33000>; + opp-microwatt = <288891>; }; opp05 { opp-hz = /bits/ 64 <2064000000>; opp-level = <5>; clock-latency-ns = <50000>; + opp-microwatt = <412979>; }; }; @@ -224,79 +229,94 @@ opp-hz = /bits/ 64 <600000000>; opp-level = <1>; clock-latency-ns = <8000>; + opp-microwatt = <290230>; }; opp02 { opp-hz = /bits/ 64 <828000000>; opp-level = <2>; clock-latency-ns = <19000>; + opp-microwatt = <449013>; }; opp03 { opp-hz = /bits/ 64 <1056000000>; opp-level = <3>; clock-latency-ns = <21000>; + opp-microwatt = <647097>; }; opp04 { opp-hz = /bits/ 64 <1284000000>; opp-level = <4>; clock-latency-ns = <23000>; + opp-microwatt = <865620>; }; opp05 { opp-hz = /bits/ 64 <1500000000>; opp-level = <5>; clock-latency-ns = <24000>; + opp-microwatt = <1112838>; }; opp06 { opp-hz = /bits/ 64 <1728000000>; opp-level = <6>; clock-latency-ns = <29000>; + opp-microwatt = <1453271>; }; opp07 { opp-hz = /bits/ 64 <1956000000>; opp-level = <7>; clock-latency-ns = <31000>; + opp-microwatt = <1776667>; }; opp08 { opp-hz = /bits/ 64 <2184000000>; opp-level = <8>; clock-latency-ns = <34000>; + opp-microwatt = <2366690>; }; opp09 { opp-hz = /bits/ 64 <2388000000>; opp-level = <9>; clock-latency-ns = <36000>; + opp-microwatt = <2892193>; }; opp10 { opp-hz = /bits/ 64 <2592000000>; opp-level = <10>; clock-latency-ns = <51000>; + opp-microwatt = <3475417>; }; opp11 { opp-hz = /bits/ 64 <2772000000>; opp-level = <11>; clock-latency-ns = <54000>; + opp-microwatt = <3959410>; }; opp12 { opp-hz = /bits/ 64 <2988000000>; opp-level = <12>; clock-latency-ns = <55000>; + opp-microwatt = <4540620>; }; /* Not available until CPU deep sleep is implemented */ opp13 { opp-hz = /bits/ 64 <3096000000>; opp-level = <13>; clock-latency-ns = <55000>; + opp-microwatt = <4745031>; turbo-mode; }; opp14 { opp-hz = /bits/ 64 <3144000000>; opp-level = <14>; clock-latency-ns = <56000>; + opp-microwatt = <4822390>; turbo-mode; }; opp15 { opp-hz = /bits/ 64 <3204000000>; opp-level = <15>; clock-latency-ns = <56000>; + opp-microwatt = <4951324>; turbo-mode; }; }; From 536e1197bf3436fd029b83ebb9f151609b91e042 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 4 Nov 2023 22:44:17 +0100 Subject: [PATCH 0142/1020] arm64: dts: apple: Disable ps_isp_sys unless it is used Seems to be fuxed off on t602x devices without camera and causes annoying kernel log splat. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/isp-common.dtsi | 4 ++++ arch/arm64/boot/dts/apple/t600x-pmgr.dtsi | 1 + arch/arm64/boot/dts/apple/t602x-pmgr.dtsi | 1 + arch/arm64/boot/dts/apple/t8103-pmgr.dtsi | 1 + arch/arm64/boot/dts/apple/t8112-pmgr.dtsi | 1 + 5 files changed, 8 insertions(+) diff --git a/arch/arm64/boot/dts/apple/isp-common.dtsi b/arch/arm64/boot/dts/apple/isp-common.dtsi index bf406772469b67..739e6e9e66e740 100644 --- a/arch/arm64/boot/dts/apple/isp-common.dtsi +++ b/arch/arm64/boot/dts/apple/isp-common.dtsi @@ -41,3 +41,7 @@ &isp_dart2 { status = "okay"; }; + +&ps_isp_sys { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi b/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi index b8957a4c6359f1..607ae7697973c3 100644 --- a/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi @@ -1370,6 +1370,7 @@ #reset-cells = <0>; label = DIE_LABEL(isp_sys); power-domains = <&DIE_NODE(ps_afnc2_lw1)>; + status = "disabled"; }; DIE_NODE(ps_venc_sys): power-controller@3b0 { diff --git a/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi b/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi index c3b1a1f50fff84..07c50156149562 100644 --- a/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi @@ -1230,6 +1230,7 @@ #reset-cells = <0>; label = DIE_LABEL(isp_sys); power-domains = <&DIE_NODE(ps_afnc2_lw1)>; + status = "disabled"; }; DIE_NODE(ps_disp0_fe): power-controller@1d0 { diff --git a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi index 4d1422d7e8b5b4..10facd0c01e420 100644 --- a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi @@ -812,6 +812,7 @@ #reset-cells = <0>; label = "isp_sys"; power-domains = <&ps_rmx>; + status = "disabled"; }; ps_venc_sys: power-controller@408 { diff --git a/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi index 9ed831031ae6f0..102ff3ad0535d0 100644 --- a/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi @@ -821,6 +821,7 @@ #reset-cells = <0>; label = "isp_sys"; power-domains = <&ps_rmx1>; + status = "disabled"; }; ps_venc_sys: power-controller@440 { From ed5e5935882fab12a915ed22a2ee56f0a841c9f6 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 6 Nov 2023 00:51:53 +0100 Subject: [PATCH 0143/1020] arm64: dts: apple: t600x: Switch to apple,dma-range Obsoletes the use of "apple,asc-dram-mask" in the device tree and the dcp driver. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t600x-die0.dtsi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index 94b832b68b9226..89357b619363c2 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -190,6 +190,7 @@ interrupts = ; status = "disabled"; power-domains = <&ps_disp0_cpu0>; + apple,dma-range = <0x0 0x0 0x0 0xfc000000>; }; dcp_dart: iommu@38b30c000 { @@ -199,6 +200,7 @@ interrupt-parent = <&aic>; interrupts = ; power-domains = <&ps_disp0_cpu0>; + apple,dma-range = <0x1f0 0x0 0x0 0xfc000000>; }; dcp_mbox: mbox@38bc08000 { @@ -231,7 +233,6 @@ power-domains = <&ps_disp0_cpu0>; resets = <&ps_disp0_cpu0>; clocks = <&clk_disp0>; - apple,asc-dram-mask = <0>; phandle = <&dcp>; disp0_piodma: piodma { From 75e7850a2098d890be9ac941233ded508ec512c5 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 6 Nov 2023 00:51:53 +0100 Subject: [PATCH 0144/1020] arm64: dts: apple: t8103: Switch to apple,dma-range Obsoletes the use of "apple,asc-dram-mask" in the device tree and the dcp driver. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8103.dtsi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index 30fe535b4b308c..b6ef5c71cb02a1 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -566,6 +566,7 @@ interrupt-parent = <&aic>; interrupts = ; power-domains = <&ps_disp0_cpu0>; + apple,dma-range = <0x0 0x0 0x0 0xfc000000>; status = "disabled"; }; @@ -575,6 +576,7 @@ #iommu-cells = <1>; interrupt-parent = <&aic>; interrupts = ; + apple,dma-range = <0xf 0x00000000 0x0 0xfc000000>; power-domains = <&ps_disp0_cpu0>; }; @@ -612,7 +614,6 @@ power-domains = <&ps_disp0_cpu0>; resets = <&ps_disp0_cpu0>; clocks = <&clk_disp0>; - apple,asc-dram-mask = <0xf 0x00000000>; phandle = <&dcp>; disp0_piodma: piodma { From d8f59ff14e7c51e9b8dea12e2f139d7b7a66096a Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 6 Nov 2023 20:04:09 +0100 Subject: [PATCH 0145/1020] arm64: dts: apple: t8112: Switch to apple,dma-range Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112.dtsi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index 62128ef1f966ad..9c78063f3a2338 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -642,6 +642,7 @@ interrupt-parent = <&aic>; interrupts = ; power-domains = <&ps_disp0_cpu0>; + apple,dma-range = <0x0 0x0 0xf 0xffff0000>; status = "disabled"; }; @@ -652,6 +653,7 @@ interrupt-parent = <&aic>; interrupts = ; power-domains = <&ps_disp0_cpu0>; + apple,dma-range = <0x8 0x00000000 0x7 0xffff0000>; }; dcp_mbox: mbox@231c08000 { @@ -686,7 +688,6 @@ power-domains = <&ps_disp0_cpu0>; resets = <&ps_disp0_cpu0>; clocks = <&clk_disp0>; - apple,asc-dram-mask = <0x0 0x0>; phandle = <&dcp>; disp0_piodma: piodma { From 715a32291f34cab630526b2ae66c6a50ed807f23 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 6 Nov 2023 00:46:53 +0100 Subject: [PATCH 0146/1020] arm64: dts: apple: t600x: Add "apple,min-state" to ps_dispextN_cpu0 DCP ASC co-processors do not come back up from lower power states. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t600x-pmgr.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi b/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi index 607ae7697973c3..88bd7a760f370f 100644 --- a/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi @@ -396,6 +396,7 @@ #reset-cells = <0>; label = DIE_LABEL(dispext0_cpu0); power-domains = <&DIE_NODE(ps_dispext0_fe)>; + apple,min-state = <4>; }; DIE_NODE(ps_dispext1_cpu0): power-controller@2a8 { @@ -405,6 +406,7 @@ #reset-cells = <0>; label = DIE_LABEL(dispext1_cpu0); power-domains = <&DIE_NODE(ps_dispext1_fe)>; + apple,min-state = <4>; }; DIE_NODE(ps_ane_sys_cpu): power-controller@2c8 { @@ -1792,6 +1794,7 @@ #reset-cells = <0>; label = DIE_LABEL(dispext2_cpu0); power-domains = <&DIE_NODE(ps_dispext2_fe)>; + apple,min-state = <4>; }; DIE_NODE(ps_dispext3_fe): power-controller@210 { @@ -1810,6 +1813,7 @@ #reset-cells = <0>; label = DIE_LABEL(dispext3_cpu0); power-domains = <&DIE_NODE(ps_dispext3_fe)>; + apple,min-state = <4>; }; DIE_NODE(ps_msr1): power-controller@250 { From 8ef94d6d60ff10abff78df9d78f0ce87829fa4b8 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 6 Nov 2023 00:46:53 +0100 Subject: [PATCH 0147/1020] arm64: dts: apple: t602x: Add "apple,min-state" to ps_dispextN_cpu0 DCP ASC co-processors do not come back up from lower power states. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t602x-pmgr.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi b/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi index 07c50156149562..063181e44412b5 100644 --- a/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi @@ -435,6 +435,7 @@ #reset-cells = <0>; label = DIE_LABEL(dispext1_cpu0); power-domains = <&DIE_NODE(ps_dispext1_fe)>; + apple,min-state = <4>; }; DIE_NODE(ps_dispext0_fe): power-controller@2c0 { @@ -472,6 +473,7 @@ #reset-cells = <0>; label = DIE_LABEL(dispext0_cpu0); power-domains = <&DIE_NODE(ps_dispext0_fe)>; + apple,min-state = <4>; }; DIE_NODE(ps_ane_cpu): power-controller@2e0 { @@ -900,6 +902,7 @@ #reset-cells = <0>; label = DIE_LABEL(dispext2_cpu0); power-domains = <&DIE_NODE(ps_dispext2_fe)>; + apple,min-state = <4>; }; DIE_NODE(ps_msr1_ase_core): power-controller@1f0 { @@ -943,6 +946,7 @@ #reset-cells = <0>; label = DIE_LABEL(dispext3_cpu0); power-domains = <&DIE_NODE(ps_dispext3_fe)>; + apple,min-state = <4>; }; DIE_NODE(ps_venc1_dma): power-controller@4000 { From 27cbf4636b732fccefc5c578a45508c5ca52ad9e Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Fri, 30 Sep 2022 22:30:13 +0200 Subject: [PATCH 0148/1020] arm64: dts: apple: t8103: Add dcpext/dispext0 nodes Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8103.dtsi | 74 ++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index b6ef5c71cb02a1..654ebb7174de5d 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -416,6 +416,14 @@ clock-output-names = "clk_disp0"; }; + /* Pixel clock? frequency in Hz (compare: 4K@60 VGA clock 533.250 MHz) */ + clk_dispext0: clock-dispext0 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "clk_dispext0"; + }; + /* * This is a fabulated representation of the input clock * to NCO since we don't know the true clock tree. @@ -624,6 +632,7 @@ display: display-subsystem { compatible = "apple,display-subsystem"; + /* disp_dart0 must be 1st since it is locked */ iommus = <&disp0_dart 0>; /* generate phandle explicitly for use in loader */ phandle = <&display>; @@ -1186,6 +1195,71 @@ ; }; + dispext0_dart: iommu@271304000 { + compatible = "apple,t8103-dart"; + reg = <0x2 0x71304000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_dispext_cpu0>; + status = "disabled"; + }; + + dcpext_dart: iommu@27130c000 { + compatible = "apple,t8103-dart"; + reg = <0x2 0x7130c000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_dispext_cpu0>; + status = "disabled"; + }; + + dcpext_mbox: mbox@271c08000 { + compatible = "apple,t8103-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x2 0x71c08000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + power-domains = <&ps_dispext_cpu0>; + resets = <&ps_dispext_cpu0>; + status = "disabled"; + }; + + dcpext: dcp@271c00000 { + compatible = "apple,t8103-dcpext", "apple,dcpext"; + mboxes = <&dcpext_mbox>; + mbox-names = "mbox"; + iommus = <&dcpext_dart 0>; + phandle = <&dcpext>; + + reg-names = "coproc", "disp-0", "disp-1", "disp-2", + "disp-3", "disp-4"; + reg = <0x2 0x71c00000 0x0 0x4000>, + <0x2 0x70000000 0x0 0x118000>, + <0x2 0x71320000 0x0 0x4000>, + <0x2 0x71344000 0x0 0x4000>, + <0x2 0x71800000 0x0 0x800000>, + <0x2 0x3b3d0000 0x0 0x4000>; + apple,bw-scratch = <&pmgr_dcp 0 5 0x18>; + apple,bw-doorbell = <&pmgr_dcp 1 6>; + power-domains = <&ps_dispext_cpu0>; + resets = <&ps_dispext_cpu0>; + clocks = <&clk_dispext0>; + apple,asc-dram-mask = <0xf 0x00000000>; + status = "disabled"; + + dispext0_piodma: piodma { + iommus = <&dispext0_dart 4>; + phandle = <&dispext0_piodma>; + }; + }; + ans_mbox: mbox@277408000 { compatible = "apple,t8103-asc-mailbox", "apple,asc-mailbox-v4"; reg = <0x2 0x77408000 0x0 0x4000>; From c4e367f4fb68e10aa73711a9050f3cca280e3535 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 3 Dec 2022 22:12:25 +0100 Subject: [PATCH 0149/1020] arm64: dts: apple: t8112: Add dcpext/dispext0 nodes Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112.dtsi | 74 ++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index 9c78063f3a2338..16c3395aee13ef 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -447,6 +447,14 @@ clock-output-names = "clk_disp0"; }; + /* Pixel clock? frequency in Hz (compare: 4K@60 VGA clock 533.250 MHz) */ + clk_dispext0: clock-dispext0 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "clk_dispext0"; + }; + reserved-memory { #address-cells = <2>; #size-cells = <2>; @@ -1374,6 +1382,72 @@ }; + dispext0_dart: iommu@271304000 { + compatible = "apple,t8112-dart", "apple,t8110-dart"; + reg = <0x2 0x71304000 0x0 0x4000>; + #iommu-cells = <1>; + apple,dma-range = <0x0 0x0 0xf 0xffff0000>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_dispext_cpu0>; + status = "disabled"; + }; + + dcpext_dart: iommu@27130c000 { + compatible = "apple,t8112-dart", "apple,t8110-dart"; + reg = <0x2 0x7130c000 0x0 0x4000>; + #iommu-cells = <1>; + apple,dma-range = <0x8 0x0 0x7 0xffff0000>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_dispext_cpu0>; + status = "disabled"; + }; + + dcpext_mbox: mbox@271c08000 { + compatible = "apple,t8112-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x2 0x71c08000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + power-domains = <&ps_dispext_cpu0>; + resets = <&ps_dispext_cpu0>; + status = "disabled"; + }; + + dcpext: dcp@271c00000 { + compatible = "apple,t8112-dcpext", "apple,dcpext"; + mboxes = <&dcpext_mbox>; + mbox-names = "mbox"; + iommus = <&dcpext_dart 5>; + phandle = <&dcpext>; + + /* the ADT has 2 additional regs which seems to be unused */ + reg-names = "coproc", "disp-0", "disp-1", "disp-2", "disp-3"; + reg = <0x2 0x71c00000 0x0 0x4000>, + <0x2 0x70000000 0x0 0x61C000>, + <0x2 0x71320000 0x0 0x4000>, + <0x2 0x71344000 0x0 0x4000>, + <0x2 0x71800000 0x0 0x800000>; + apple,bw-scratch = <&pmgr_dcp 0 4 0x5e0>; + power-domains = <&ps_dispext_cpu0>; + resets = <&ps_dispext_cpu0>; + clocks = <&clk_dispext0>; + apple,asc-dram-mask = <0x0 0x0>; + apple,dcp-index = <1>; + status = "disabled"; + + dispext0_piodma: piodma { + iommus = <&dispext0_dart 4>; + phandle = <&dispext0_piodma>; + }; + }; + ans_mbox: mbox@277408000 { compatible = "apple,t8112-asc-mailbox", "apple,asc-mailbox-v4"; reg = <0x2 0x77408000 0x0 0x4000>; From 2928d6c526d35e6ee3bbb98effc798c2d7c149ac Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 20 Oct 2022 20:44:02 +0200 Subject: [PATCH 0150/1020] arm64: dts: apple: t600x: Add t6000 dispext device nodes While thunderbolt and DP-altmode are not working 2 dispext/dcpext devices are enough. "dispext0" will be used for the HDMI output and dispext1 can be used for DP-altmopde experiments. All nodes are disabled and have be enabled explicitly in device .dts or .dtsi. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t6002.dtsi | 10 ++ arch/arm64/boot/dts/apple/t600x-common.dtsi | 28 +++++ arch/arm64/boot/dts/apple/t600x-dieX.dtsi | 126 ++++++++++++++++++++ 3 files changed, 164 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6002.dtsi b/arch/arm64/boot/dts/apple/t6002.dtsi index a7dfc6196fa724..331cc49b42994d 100644 --- a/arch/arm64/boot/dts/apple/t6002.dtsi +++ b/arch/arm64/boot/dts/apple/t6002.dtsi @@ -305,6 +305,16 @@ }; }; +&dcpext0_die1 { + // TODO: verify + apple,bw-scratch = <&pmgr_dcp 0 4 0x9c0>; +}; + +&dcpext1_die1 { + // TODO: verify + apple,bw-scratch = <&pmgr_dcp 0 4 0x9c8>; +}; + &ps_gfx { // On t6002, the die0 GPU power domain needs both AFR power domains power-domains = <&ps_afr>, <&ps_afr_die1>; diff --git a/arch/arm64/boot/dts/apple/t600x-common.dtsi b/arch/arm64/boot/dts/apple/t600x-common.dtsi index 5f62329b628a39..e244ded374da18 100644 --- a/arch/arm64/boot/dts/apple/t600x-common.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-common.dtsi @@ -441,6 +441,34 @@ clock-frequency = <237333328>; clock-output-names = "clk_disp0"; }; + + clk_dispext0: clock-dispext0 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "clk_dispext0"; + }; + + clk_dispext0_die1: clock-dispext0_die1 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "clk_dispext0_die1"; + }; + + clk_dispext1: clock-dispext1 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "clk_dispext0"; + }; + + clk_dispext1_die1: clock-dispext1_die1 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "clk_dispext1_die1"; + }; /* * This is a fabulated representation of the input clock * to NCO since we don't know the true clock tree. diff --git a/arch/arm64/boot/dts/apple/t600x-dieX.dtsi b/arch/arm64/boot/dts/apple/t600x-dieX.dtsi index 3fca8efb2dcf17..6a84ebd0946cd3 100644 --- a/arch/arm64/boot/dts/apple/t600x-dieX.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-dieX.dtsi @@ -24,6 +24,69 @@ #performance-domain-cells = <0>; }; + DIE_NODE(dispext0_dart): iommu@289304000 { + compatible = "apple,t6000-dart"; + reg = <0x2 0x89304000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_dispext0_cpu0)>; + apple,dma-range = <0x0 0x0 0x0 0xfc000000>; + status = "disabled"; + }; + + DIE_NODE(dcpext0_dart): iommu@28930c000 { + compatible = "apple,t6000-dart"; + reg = <0x2 0x8930c000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_dispext0_cpu0)>; + apple,dma-range = <0x1f0 0x0 0x0 0xfc000000>; + status = "disabled"; + }; + + DIE_NODE(dcpext0_mbox): mbox@289c08000 { + compatible = "apple,t6000-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x2 0x89c08000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + power-domains = <&DIE_NODE(ps_dispext0_cpu0)>; + resets = <&DIE_NODE(ps_dispext0_cpu0)>; + status = "disabled"; + }; + + DIE_NODE(dcpext0): dcp@289c00000 { + compatible = "apple,t6000-dcpext", "apple,dcpext"; + mboxes = <&DIE_NODE(dcpext0_mbox)>; + mbox-names = "mbox"; + iommus = <&DIE_NODE(dcpext0_dart) 0>; + + reg-names = "coproc", "disp-0", "disp-1", "disp-2", "disp-3"; + reg = <0x2 0x89c00000 0x0 0x4000>, + <0x2 0x88000000 0x0 0x3000000>, + <0x2 0x89320000 0x0 0x4000>, + <0x2 0x89344000 0x0 0x4000>, + <0x2 0x89800000 0x0 0x800000>; + apple,bw-scratch = <&pmgr_dcp 0 4 0x990>; + power-domains = <&DIE_NODE(ps_dispext0_cpu0)>; + resets = <&DIE_NODE(ps_dispext0_cpu0)>; + clocks = <&DIE_NODE(clk_dispext0)>; + phandle = <&DIE_NODE(dcpext0)>; + apple,dcp-index = <1>; + status = "disabled"; + + piodma { + iommus = <&DIE_NODE(dispext0_dart) 4>; + }; + }; + DIE_NODE(pmgr): power-management@28e080000 { compatible = "apple,t6000-pmgr", "apple,pmgr", "syscon", "simple-mfd"; #address-cells = <1>; @@ -282,6 +345,69 @@ ; }; + DIE_NODE(dispext1_dart): iommu@315304000 { + compatible = "apple,t6000-dart", "apple,t8110-dart"; + reg = <0x3 0x15304000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_dispext1_cpu0)>; + apple,dma-range = <0x100 0x0 0x10 0x0>; + status = "disabled"; + }; + + DIE_NODE(dcpext1_dart): iommu@31530c000 { + compatible = "apple,t6000-dart", "apple,t8110-dart"; + reg = <0x3 0x1530c000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_dispext1_cpu0)>; + apple,dma-range = <0x100 0x0 0x10 0x0>; + status = "disabled"; + }; + + DIE_NODE(dcpext1_mbox): mbox@315c08000 { + compatible = "apple,t6000-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x3 0x15c08000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + power-domains = <&DIE_NODE(ps_dispext1_cpu0)>; + resets = <&DIE_NODE(ps_dispext1_cpu0)>; + status = "disabled"; + }; + + DIE_NODE(dcpext1): dcp@315c00000 { + compatible = "apple,t6000-dcpext", "apple,dcpext"; + mboxes = <&DIE_NODE(dcpext1_mbox)>; + mbox-names = "mbox"; + iommus = <&DIE_NODE(dcpext1_dart) 5>; + + reg-names = "coproc", "disp-0", "disp-1", "disp-2", "disp-3"; + reg = <0x3 0x15c00000 0x0 0x4000>, + <0x3 0x14000000 0x0 0x4000000>, + <0x3 0x15320000 0x0 0x4000>, + <0x3 0x15344000 0x0 0x4000>, + <0x3 0x15800000 0x0 0x800000>; + apple,bw-scratch = <&pmgr_dcp 0 4 0x998>; + power-domains = <&DIE_NODE(ps_dispext1_cpu0)>; + resets = <&DIE_NODE(ps_dispext1_cpu0)>; + clocks = <&DIE_NODE(clk_dispext1)>; + phandle = <&DIE_NODE(dcpext1)>; + apple,dcp-index = <2>; + status = "disabled"; + + piodma { + iommus = <&DIE_NODE(dispext1_dart) 4>; + }; + }; + DIE_NODE(pinctrl_ap): pinctrl@39b028000 { compatible = "apple,t6000-pinctrl", "apple,pinctrl"; reg = <0x3 0x9b028000 0x0 0x4000>; From 28ed9b57a101dee9754ff1cefd30ee5e84eefe49 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 17 Aug 2023 19:45:46 +0200 Subject: [PATCH 0151/1020] arm64: dts: apple: t602x: Add t6020 dispext device nodes While thunderbolt and DP-altmode are not working 2 dispext/dcpext devices are enough. "dispext0" will be used for the HDMI output and dispext1 can be used for DP-altmopde experiments. All nodes are disabled and have be enabled explicitly in device .dts or .dtsi. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t6022.dtsi | 8 ++ arch/arm64/boot/dts/apple/t602x-common.dtsi | 28 +++++ arch/arm64/boot/dts/apple/t602x-dieX.dtsi | 126 ++++++++++++++++++++ 3 files changed, 162 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6022.dtsi b/arch/arm64/boot/dts/apple/t6022.dtsi index ebf8e5bf53e86c..e9140440fb65e4 100644 --- a/arch/arm64/boot/dts/apple/t6022.dtsi +++ b/arch/arm64/boot/dts/apple/t6022.dtsi @@ -344,6 +344,14 @@ }; }; +&dcpext0_die1 { + apple,bw-scratch = <&pmgr_dcp 0 4 0x1240>; +}; + +&dcpext1_die1 { + apple,bw-scratch = <&pmgr_dcp 0 4 0x1248>; +}; + &ps_gfx { // On t6022, the die0 GPU power domain needs both AFR power domains power-domains = <&ps_afr>, <&ps_afr_die1>; diff --git a/arch/arm64/boot/dts/apple/t602x-common.dtsi b/arch/arm64/boot/dts/apple/t602x-common.dtsi index 79a2afc1b39268..bc8f439b523a0d 100644 --- a/arch/arm64/boot/dts/apple/t602x-common.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-common.dtsi @@ -555,6 +555,34 @@ clock-output-names = "clk_disp0"; }; + clk_dispext0: clock-dispext0 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "clk_dispext0"; + }; + + clk_dispext0_die1: clock-dispext0_die1 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "clk_dispext0_die1"; + }; + + clk_dispext1: clock-dispext1 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "clk_dispext1"; + }; + + clk_dispext1_die1: clock-dispext1_die1 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "clk_dispext1_die1"; + }; + /* * This is a fabulated representation of the input clock * to NCO since we don't know the true clock tree. diff --git a/arch/arm64/boot/dts/apple/t602x-dieX.dtsi b/arch/arm64/boot/dts/apple/t602x-dieX.dtsi index da891047c5db7a..987208b24e2a52 100644 --- a/arch/arm64/boot/dts/apple/t602x-dieX.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-dieX.dtsi @@ -24,6 +24,69 @@ #performance-domain-cells = <0>; }; + DIE_NODE(dispext0_dart): iommu@289304000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x2 0x89304000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_dispext0_cpu0)>; + apple,dma-range = <0x100 0x0 0x10 0x0>; + status = "disabled"; + }; + + DIE_NODE(dcpext0_dart): iommu@28930c000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x2 0x8930c000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_dispext0_cpu0)>; + apple,dma-range = <0x100 0x0 0x10 0x0>; + status = "disabled"; + }; + + DIE_NODE(dcpext0_mbox): mbox@289c08000 { + compatible = "apple,t6020-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x2 0x89c08000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + power-domains = <&DIE_NODE(ps_dispext0_cpu0)>; + resets = <&DIE_NODE(ps_dispext0_cpu0)>; + status = "disabled"; + }; + + DIE_NODE(dcpext0): dcp@289c00000 { + compatible = "apple,t6020-dcpext", "apple,dcpext"; + mboxes = <&DIE_NODE(dcpext0_mbox)>; + mbox-names = "mbox"; + iommus = <&DIE_NODE(dcpext0_dart) 5>; + + reg-names = "coproc", "disp-0", "disp-1", "disp-2", "disp-3"; + reg = <0x2 0x89c00000 0x0 0x4000>, + <0x2 0x88000000 0x0 0x4000000>, + <0x2 0x89320000 0x0 0x4000>, + <0x2 0x89344000 0x0 0x4000>, + <0x2 0x89800000 0x0 0x800000>; + apple,bw-scratch = <&pmgr_dcp 0 4 0x1210>; + power-domains = <&DIE_NODE(ps_dispext0_cpu0)>; + resets = <&DIE_NODE(ps_dispext0_cpu0)>; + clocks = <&DIE_NODE(clk_dispext0)>; + phandle = <&DIE_NODE(dcpext0)>; + apple,dcp-index = <1>; + status = "disabled"; + + piodma { + iommus = <&DIE_NODE(dispext0_dart) 4>; + }; + }; + DIE_NODE(pmgr): power-management@28e080000 { compatible = "apple,t6020-pmgr", "apple,pmgr", "syscon", "simple-mfd"; #address-cells = <1>; @@ -102,6 +165,69 @@ ; }; + DIE_NODE(dispext1_dart): iommu@315304000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x3 0x15304000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_dispext1_cpu0)>; + apple,dma-range = <0x100 0x0 0x10 0x0>; + status = "disabled"; + }; + + DIE_NODE(dcpext1_dart): iommu@31530c000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x3 0x1530c000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_dispext1_cpu0)>; + apple,dma-range = <0x100 0x0 0x10 0x0>; + status = "disabled"; + }; + + DIE_NODE(dcpext1_mbox): mbox@315c08000 { + compatible = "apple,t6020-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x3 0x15c08000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + power-domains = <&DIE_NODE(ps_dispext1_cpu0)>; + resets = <&DIE_NODE(ps_dispext1_cpu0)>; + status = "disabled"; + }; + + DIE_NODE(dcpext1): dcp@315c00000 { + compatible = "apple,t6020-dcpext", "apple,dcpext"; + mboxes = <&DIE_NODE(dcpext1_mbox)>; + mbox-names = "mbox"; + iommus = <&DIE_NODE(dcpext1_dart) 5>; + + reg-names = "coproc", "disp-0", "disp-1", "disp-2", "disp-3"; + reg = <0x3 0x15c00000 0x0 0x4000>, + <0x3 0x14000000 0x0 0x4000000>, + <0x3 0x15320000 0x0 0x4000>, + <0x3 0x15344000 0x0 0x4000>, + <0x3 0x15800000 0x0 0x800000>; + apple,bw-scratch = <&pmgr_dcp 0 4 0x1218>; + power-domains = <&DIE_NODE(ps_dispext1_cpu0)>; + resets = <&DIE_NODE(ps_dispext1_cpu0)>; + clocks = <&DIE_NODE(clk_dispext1)>; + phandle = <&DIE_NODE(dcpext1)>; + apple,dcp-index = <2>; + status = "disabled"; + + piodma { + iommus = <&DIE_NODE(dispext1_dart) 4>; + }; + }; + DIE_NODE(pinctrl_ap): pinctrl@39b028000 { compatible = "apple,t6020-pinctrl", "apple,pinctrl"; reg = <0x3 0x9b028000 0x0 0x4000>; From 7295ff2c799ca6a2f657107d103843ff648180bb Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 28 Oct 2023 23:12:33 +0200 Subject: [PATCH 0152/1020] arm64: dts: apple: t8112: Add dptx-phy node On M2 desktop devices more parts of the HDMI output pipeline are under the OS' control. One of this parts is the primary DPTX phy which drives the the HDMI port through an integrated MCDP29XX DP to HDMI converter. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112.dtsi | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index 16c3395aee13ef..b8a1e433268274 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -1015,6 +1015,17 @@ }; }; + dptxphy: phy@23c500000 { + compatible = "apple,t8112-dptx-phy", "apple,dptx-phy"; + reg = <0x2 0x3c500000 0x0 0x4000>, + <0x2 0x3c540000 0x0 0xc000>; + reg-names = "core", "dptx"; + power-domain = <&ps_dptx_ext_phy>; + #phy-cells = <0>; + #reset-cells = <0>; + status = "disabled"; /* only used on j473 */ + }; + nub_spmi: spmi@23d0d9300 { compatible = "apple,t8112-spmi", "apple,spmi"; reg = <0x2 0x3d714000 0x0 0x100>; From 264e69714bffe38e70f85c0e336ed081021b365b Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 29 Oct 2023 10:39:17 +0100 Subject: [PATCH 0153/1020] arm64: dts: apple: t602x: Add lpdptx-phy node On M2 desktop devices more parts of the HDMI output pipeline are under the OS' control. One of this parts is the primary DPTX phy which drives the the HDMI port through an integrated MCDP29XX DP to HDMI converter. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t602x-dieX.dtsi | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t602x-dieX.dtsi b/arch/arm64/boot/dts/apple/t602x-dieX.dtsi index 987208b24e2a52..54de205835cdcf 100644 --- a/arch/arm64/boot/dts/apple/t602x-dieX.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-dieX.dtsi @@ -253,6 +253,17 @@ #interrupt-cells = <2>; }; + DIE_NODE(lpdptxphy): phy@39c000000 { + compatible = "apple,t6020-dptx-phy", "apple,dptx-phy"; + reg = <0x3 0x9c000000 0x0 0x4000>, + <0x3 0x9c040000 0x0 0xc000>; + reg-names = "core", "dptx"; + power-domain = <&DIE_NODE(ps_dptx_phy_ps)>; + #phy-cells = <0>; + #reset-cells = <0>; + status = "disabled"; /* only exposed on desktop devices */ + }; + DIE_NODE(pmgr_gfx): power-management@404e80000 { compatible = "apple,t6020-pmgr", "apple,pmgr", "syscon", "simple-mfd"; #address-cells = <1>; From 987631a22bbd8d294643f856118833ceec6d25ac Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 6 Nov 2023 00:35:54 +0100 Subject: [PATCH 0154/1020] arm64: dts: apple: t600x: Add device nodes for atc DP crossbar Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t6002-j375d.dts | 2 ++ arch/arm64/boot/dts/apple/t600x-dieX.dtsi | 32 +++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6002-j375d.dts b/arch/arm64/boot/dts/apple/t6002-j375d.dts index 95a783b9fb144a..062bfc72575ebf 100644 --- a/arch/arm64/boot/dts/apple/t6002-j375d.dts +++ b/arch/arm64/boot/dts/apple/t6002-j375d.dts @@ -135,11 +135,13 @@ /delete-node/ &dwc3_2_dart_1_die1; /delete-node/ &dwc3_2_die1; /delete-node/ &atcphy2_die1; +/delete-node/ &atcphy2_xbar_die1; /delete-node/ &dwc3_3_dart_0_die1; /delete-node/ &dwc3_3_dart_1_die1; /delete-node/ &dwc3_3_die1; /delete-node/ &atcphy3_die1; +/delete-node/ &atcphy3_xbar_die1; /* delete unused always-on power-domains on die 1 */ diff --git a/arch/arm64/boot/dts/apple/t600x-dieX.dtsi b/arch/arm64/boot/dts/apple/t600x-dieX.dtsi index 6a84ebd0946cd3..99c1e8a774d685 100644 --- a/arch/arm64/boot/dts/apple/t600x-dieX.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-dieX.dtsi @@ -506,6 +506,14 @@ power-domains = <&DIE_NODE(ps_atc0_usb)>; }; + DIE_NODE(atcphy0_xbar): mux@70304c000 { + compatible = "apple,t6000-display-crossbar"; + reg = <0x7 0x0304c000 0x0 0x4000>; + #mux-control-cells = <1>; + power-domains = <&DIE_NODE(ps_atc0_usb)>; + status = "disabled"; + }; + DIE_NODE(dwc3_1_dart_0): iommu@b02f00000 { compatible = "apple,t6000-dart"; reg = <0xb 0x02f00000 0x0 0x4000>; @@ -579,6 +587,14 @@ power-domains = <&DIE_NODE(ps_atc1_usb)>; }; + DIE_NODE(atcphy1_xbar): mux@b0304c000 { + compatible = "apple,t6000-display-crossbar"; + reg = <0xb 0x0304c000 0x0 0x4000>; + #mux-control-cells = <1>; + power-domains = <&DIE_NODE(ps_atc1_usb)>; + status = "disabled"; + }; + DIE_NODE(dwc3_2_dart_0): iommu@f02f00000 { compatible = "apple,t6000-dart"; reg = <0xf 0x02f00000 0x0 0x4000>; @@ -652,6 +668,14 @@ power-domains = <&DIE_NODE(ps_atc2_usb)>; }; + DIE_NODE(atcphy2_xbar): mux@f0304c000 { + compatible = "apple,t6000-display-crossbar"; + reg = <0xf 0x0304c000 0x0 0x4000>; + #mux-control-cells = <1>; + power-domains = <&DIE_NODE(ps_atc2_usb)>; + status = "disabled"; + }; + DIE_NODE(dwc3_3_dart_0): iommu@1302f00000 { compatible = "apple,t6000-dart"; reg = <0x13 0x02f00000 0x0 0x4000>; @@ -724,3 +748,11 @@ svid = <0xff01>, <0x8087>; power-domains = <&DIE_NODE(ps_atc3_usb)>; }; + + DIE_NODE(atcphy3_xbar): mux@130304c000 { + compatible = "apple,t6000-display-crossbar"; + reg = <0x13 0x0304c000 0x0 0x4000>; + #mux-control-cells = <1>; + power-domains = <&DIE_NODE(ps_atc3_usb)>; + status = "disabled"; + }; From 10744222ca1768abf3f468d4b0c13608abb09dc5 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 17 Aug 2023 23:17:13 +0200 Subject: [PATCH 0155/1020] arm64: dts: apple: t602x: Add device nodes for atc DP crossbar Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t6022-j475d.dts | 2 ++ arch/arm64/boot/dts/apple/t602x-dieX.dtsi | 32 +++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6022-j475d.dts b/arch/arm64/boot/dts/apple/t6022-j475d.dts index 43dba036456159..1ee076f229f916 100644 --- a/arch/arm64/boot/dts/apple/t6022-j475d.dts +++ b/arch/arm64/boot/dts/apple/t6022-j475d.dts @@ -42,11 +42,13 @@ /delete-node/ &dwc3_2_dart_1_die1; /delete-node/ &dwc3_2_die1; /delete-node/ &atcphy2_die1; +/delete-node/ &atcphy2_xbar_die1; /delete-node/ &dwc3_3_dart_0_die1; /delete-node/ &dwc3_3_dart_1_die1; /delete-node/ &dwc3_3_die1; /delete-node/ &atcphy3_die1; +/delete-node/ &atcphy3_xbar_die1; /* delete unused always-on power-domains on die 1 */ diff --git a/arch/arm64/boot/dts/apple/t602x-dieX.dtsi b/arch/arm64/boot/dts/apple/t602x-dieX.dtsi index 54de205835cdcf..dda942326a6cb8 100644 --- a/arch/arm64/boot/dts/apple/t602x-dieX.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-dieX.dtsi @@ -326,6 +326,14 @@ power-domains = <&DIE_NODE(ps_atc0_usb)>; }; + DIE_NODE(atcphy0_xbar): mux@70304c000 { + compatible = "apple,t6020-display-crossbar"; + reg = <0x7 0x0304c000 0x0 0x4000>; + #mux-control-cells = <1>; + power-domains = <&DIE_NODE(ps_atc0_usb)>; + status = "disabled"; + }; + DIE_NODE(dwc3_1_dart_0): iommu@b02f00000 { compatible = "apple,t6020-dart", "apple,t8110-dart"; reg = <0xb 0x02f00000 0x0 0x4000>; @@ -380,6 +388,14 @@ power-domains = <&DIE_NODE(ps_atc1_usb)>; }; + DIE_NODE(atcphy1_xbar): mux@b0304c000 { + compatible = "apple,t6020-display-crossbar"; + reg = <0xb 0x0304c000 0x0 0x4000>; + #mux-control-cells = <1>; + power-domains = <&DIE_NODE(ps_atc1_usb)>; + status = "disabled"; + }; + DIE_NODE(dwc3_2_dart_0): iommu@f02f00000 { compatible = "apple,t6020-dart", "apple,t8110-dart"; reg = <0xf 0x02f00000 0x0 0x4000>; @@ -434,6 +450,14 @@ power-domains = <&DIE_NODE(ps_atc2_usb)>; }; + DIE_NODE(atcphy2_xbar): mux@f0304c000 { + compatible = "apple,t6020-display-crossbar"; + reg = <0xf 0x0304c000 0x0 0x4000>; + #mux-control-cells = <1>; + power-domains = <&DIE_NODE(ps_atc2_usb)>; + status = "disabled"; + }; + DIE_NODE(dwc3_3_dart_0): iommu@1302f00000 { compatible = "apple,t6020-dart", "apple,t8110-dart"; reg = <0x13 0x02f00000 0x0 0x4000>; @@ -488,6 +512,14 @@ power-domains = <&DIE_NODE(ps_atc3_usb)>; }; + DIE_NODE(atcphy3_xbar): mux@130304c000 { + compatible = "apple,t6020-display-crossbar"; + reg = <0x13 0x0304c000 0x0 0x4000>; + #mux-control-cells = <1>; + power-domains = <&DIE_NODE(ps_atc3_usb)>; + status = "disabled"; + }; + DIE_NODE(pcie_ge): pcie@1680000000 { compatible = "apple,t6020-pcie-ge", "apple,t6020-pcie"; device_type = "pci"; From cf35c1b222910a61ae056595c3fe8eec3927f018 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 28 Oct 2023 23:40:47 +0200 Subject: [PATCH 0156/1020] arm64: dts: apple: t8112-j473: Enable dcp/dptx-phy/dp2hdmi After all parts are in place enable DCP on the M2 Mac Mini for HDMI output. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112-j473.dts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8112-j473.dts b/arch/arm64/boot/dts/apple/t8112-j473.dts index 686426f9468d18..dbd66483f2f56e 100644 --- a/arch/arm64/boot/dts/apple/t8112-j473.dts +++ b/arch/arm64/boot/dts/apple/t8112-j473.dts @@ -27,9 +27,23 @@ power-domains = <&ps_disp0_cpu0>, <&ps_dptx_ext_phy>; }; -/* disable dcp until it is supported */ +&dptxphy { + status = "okay"; +}; + &dcp { - status = "disabled"; + status = "okay"; + apple,connector-type = "HDMI-A"; + + /* HDMI HPD gpio, used as interrupt*/ + hdmi-hpd-gpios = <&pinctrl_aop 49 GPIO_ACTIVE_HIGH>; + + hdmi-pwren-gpios = <&smc_gpio 21 GPIO_ACTIVE_HIGH>; + dp2hdmi-pwren-gpios = <&smc_gpio 22 GPIO_ACTIVE_HIGH>; + + phys = <&dptxphy>; + phy-names = "dp-phy"; + apple,dptx-phy = <5>; }; /* From 210a5ca6b7c9ce4f9010148aa1918c668a709621 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 6 Nov 2023 21:47:19 +0100 Subject: [PATCH 0157/1020] arm64: dts: apple: t6020-j474,t6021-j475: Enable dcp/dptx-phy/dp2hdmi After all parts are in place enable the DCP on the M2 Pro Mac Mini and the M2 Max Mac Studio for HDMI output. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t6020-j474s.dts | 45 +++++++++++++++++++ arch/arm64/boot/dts/apple/t6021-j475c.dts | 19 ++++++++ .../arm64/boot/dts/apple/t602x-j474-j475.dtsi | 5 --- 3 files changed, 64 insertions(+), 5 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t6020-j474s.dts b/arch/arm64/boot/dts/apple/t6020-j474s.dts index ab0e50bbd49dd0..ea763d25b2e874 100644 --- a/arch/arm64/boot/dts/apple/t6020-j474s.dts +++ b/arch/arm64/boot/dts/apple/t6020-j474s.dts @@ -45,6 +45,51 @@ model = "Mac mini J474"; }; +&lpdptxphy { + status = "okay"; +}; + +#define USE_DCPEXT0 0 + +#if USE_DCPEXT0 +/ { + aliases { + dcpext0 = &dcpext0; + }; +}; +&dcp { + status = "disabled"; +}; +&display { + iommus = <&dispext0_dart 0>; +}; +&dispext0_dart { + status = "okay"; +}; +&dcpext0_dart { + status = "okay"; +}; +&dcpext0_mbox { + status = "okay"; +}; +&dcpext0 { +#else +&dcp { +#endif + status = "okay"; + apple,connector-type = "HDMI-A"; + + /* HDMI HPD gpio, used as interrupt*/ + hdmi-hpd-gpios = <&pinctrl_aop 25 GPIO_ACTIVE_HIGH>; + + hdmi-pwren-gpios = <&smc_gpio 23 GPIO_ACTIVE_HIGH>; + dp2hdmi-pwren-gpios = <&smc_gpio 25 GPIO_ACTIVE_HIGH>; + + phys = <&lpdptxphy>; + phy-names = "dp-phy"; + apple,dptx-phy = <4>; +}; + &gpu { /* Apple does not do this, but they probably should */ apple,perf-base-pstate = <3>; diff --git a/arch/arm64/boot/dts/apple/t6021-j475c.dts b/arch/arm64/boot/dts/apple/t6021-j475c.dts index 591f637c4a6a98..46708998e86f96 100644 --- a/arch/arm64/boot/dts/apple/t6021-j475c.dts +++ b/arch/arm64/boot/dts/apple/t6021-j475c.dts @@ -47,3 +47,22 @@ compatible = "apple,j475-macaudio", "apple,j375-macaudio", "apple,macaudio"; model = "Mac Studio J475"; }; + +&lpdptxphy { + status = "okay"; +}; + +&dcp { + status = "okay"; + apple,connector-type = "HDMI-A"; + + /* HDMI HPD gpio, used as interrupt*/ + hdmi-hpd-gpios = <&pinctrl_aop 25 GPIO_ACTIVE_HIGH>; + + hdmi-pwren-gpios = <&smc_gpio 23 GPIO_ACTIVE_HIGH>; + dp2hdmi-pwren-gpios = <&smc_gpio 25 GPIO_ACTIVE_HIGH>; + + phys = <&lpdptxphy>; + phy-names = "dp-phy"; + apple,dptx-phy = <4>; +}; diff --git a/arch/arm64/boot/dts/apple/t602x-j474-j475.dtsi b/arch/arm64/boot/dts/apple/t602x-j474-j475.dtsi index 0553e557d8becb..b1390aefdbd7c1 100644 --- a/arch/arm64/boot/dts/apple/t602x-j474-j475.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-j474-j475.dtsi @@ -21,11 +21,6 @@ power-domains = <&ps_disp0_cpu0>, <&ps_dptx_phy_ps>; }; -/* disable dcp until it is supported */ -&dcp { - status = "disabled"; -}; - &hpm0 { interrupts = <44 IRQ_TYPE_LEVEL_LOW>; }; From cd6a2378f75e76f8d25d9c0523611cff10238536 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 4 Nov 2023 22:33:29 +0100 Subject: [PATCH 0158/1020] arm64: dts: apple: t6022-{j180,j475}: Enable dcpext0/dptx-phy/dp2hdmi After all parts are in place enable dcpext on M2 Ultra Mac Pro and Studio. On the Mac Pro only the HDMI output connected to die1 is enabled. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t6022-j475d.dts | 5 +++ arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi | 43 ++++++++++++++++++++-- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t6022-j475d.dts b/arch/arm64/boot/dts/apple/t6022-j475d.dts index 1ee076f229f916..4b139d07545925 100644 --- a/arch/arm64/boot/dts/apple/t6022-j475d.dts +++ b/arch/arm64/boot/dts/apple/t6022-j475d.dts @@ -28,6 +28,11 @@ power-domains = <&ps_dispext0_cpu0_die1>, <&ps_dptx_phy_ps_die1>; }; +&dcpext0_die1 { + // J180 misses "function-dp2hdmi_pwr_en" + dp2hdmi-pwren-gpios = <&smc_gpio 25 GPIO_ACTIVE_HIGH>; +}; + &typec4 { label = "USB-C Front Right"; }; diff --git a/arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi b/arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi index 4f552c2530aa7a..9b7391b922db54 100644 --- a/arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi +++ b/arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi @@ -9,11 +9,48 @@ * Copyright The Asahi Linux Contributors */ -/* disable unused display node */ +/ { + aliases { + dcpext4 = &dcpext0_die1; + disp0 = &display; + }; +}; + +&lpdptxphy_die1 { + status = "okay"; +}; &display { - status = "disabled"; - iommus = <>; /* <&dispext0_dart_die1 0>; */ + iommus = <&dispext0_dart_die1 0>; +}; + +&dispext0_dart_die1 { + status = "okay"; +}; + +&dcpext0_dart_die1 { + status = "okay"; +}; + +&dcpext0_mbox_die1 { + status = "okay"; +}; + +&dcpext0_die1 { + status = "okay"; + apple,connector-type = "HDMI-A"; + + /* HDMI HPD gpio, used as interrupt*/ + hdmi-hpd-gpios = <&pinctrl_aop 41 GPIO_ACTIVE_HIGH>; + + hdmi-pwren-gpios = <&smc_gpio 23 GPIO_ACTIVE_HIGH>; + // J180 misses "function-dp2hdmi_pwr_en" + // dp2hdmi-pwren-gpios = <&smc_gpio 25 GPIO_ACTIVE_HIGH>; + + phys = <&lpdptxphy_die1>; + phy-names = "dp-phy"; + apple,dptx-phy = <4>; + apple,dptx-die = <1>; }; /* delete missing dcp0/disp0 */ From 5bec91a7a41f54d3701c52396b44bb9e85eee9f0 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 6 Nov 2023 20:39:42 +0100 Subject: [PATCH 0159/1020] arm64: dts: apple: Fill device node for dp2hdmi on Macbook Pros The HDMI output on the 14 and 16 inch Macbook Pros with M1/M2 Pro/Max is driven by an unused ATC port using the phy and crossbar. The DP output from any dcpext display controller is routed to a Kinetic DP2HDMI converter (MCDP2920 and a unknown HDMI 2.1 capable variant). Signed-off-by: Janne Grunau --- .../arm64/boot/dts/apple/t600x-j314-j316.dtsi | 48 +++++++++++++++++++ .../arm64/boot/dts/apple/t602x-j414-j416.dtsi | 5 ++ 2 files changed, 53 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index 2631daf667e14e..3f1ade2f9066ab 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -19,6 +19,7 @@ atcphy3 = &atcphy3; bluetooth0 = &bluetooth0; dcp = &dcp; + dcpext0 = &dcpext0; disp0 = &display; disp0_piodma = &disp0_piodma; nvram = &nvram; @@ -77,6 +78,49 @@ }; }; +&display { + iommus = <&disp0_dart 0>, <&dispext0_dart 0>; +}; + +&dispext0_dart { + status = "okay"; +}; + +&dcpext0_dart { + status = "okay"; +}; + +&dcpext0_mbox { + status = "okay"; +}; + +&dcpext0 { + /* enabled by the loader */ + apple,connector-type = "HDMI-A"; + + /* HDMI HPD gpio, used as interrupt*/ + hdmi-hpd-gpios = <&pinctrl_nub 15 GPIO_ACTIVE_HIGH>; + + hdmi-pwren-gpios = <&smc_gpio 23 GPIO_ACTIVE_HIGH>; + dp2hdmi-pwren-gpios = <&smc_gpio 6 GPIO_ACTIVE_HIGH>; + + phy-names = "dp-phy"; + phys = <&atcphy3 PHY_TYPE_DP>; + phy-names = "dp-phy"; + mux-controls = <&atcphy3_xbar 0>; + mux-control-names = "dp-xbar"; + mux-index = <0>; + apple,dptx-phy = <3>; +}; + +&atcphy3 { + apple,mode-fixed-dp; +}; + +&atcphy3_xbar { + status = "okay"; +}; + /* USB Type C */ &i2c0 { hpm0: usb-pd@38 { @@ -416,6 +460,10 @@ &dwc3_3 { status = "disabled"; }; +/* Delete unused dwc3_3 to prevent dt_disable_missing_devs() from disabling + * atcphy3 via phandle references from a disablecd device. + */ +/delete-node/ &dwc3_3; &ps_atc3_usb_aon { /delete-property/ apple,always-on; diff --git a/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi b/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi index 9eb19bfef4171a..6e8df7750d2a43 100644 --- a/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi @@ -30,6 +30,11 @@ apple,always-on; }; +&dcpext0 { + /* HDMI HPD gpio, used as interrupt*/ + hdmi-hpd-gpios = <&pinctrl_aop 25 GPIO_ACTIVE_HIGH>; +}; + &hpm0 { interrupts = <44 IRQ_TYPE_LEVEL_LOW>; }; From ebc81ddc11ebe14f068ad981bf3222dbe67aaf1b Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 27 Nov 2023 00:35:55 +0100 Subject: [PATCH 0160/1020] arm64: dts: apple: j474s/j475c: Use dcpext0 for HDMI out dcp on t8112 and t602x does not wake up after sleep + reset but dcpext* does. Use dcpext0 for sharing the code with M1* devices. My interpretation of the tea leaves from Apple's marketing department suggests that dcpext is more capable (6k 60Hz vs 5k 60Hz) so use dcpext as long as only one is used. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t6020-j474s.dts | 8 +++++- arch/arm64/boot/dts/apple/t6021-j475c.dts | 33 +++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/apple/t6020-j474s.dts b/arch/arm64/boot/dts/apple/t6020-j474s.dts index ea763d25b2e874..e4d9b580f2ef4d 100644 --- a/arch/arm64/boot/dts/apple/t6020-j474s.dts +++ b/arch/arm64/boot/dts/apple/t6020-j474s.dts @@ -49,14 +49,20 @@ status = "okay"; }; -#define USE_DCPEXT0 0 +#define USE_DCPEXT0 1 #if USE_DCPEXT0 / { aliases { dcpext0 = &dcpext0; + /delete-property/ dcp; }; }; + +&framebuffer0 { + power-domains = <&ps_dispext0_cpu0>, <&ps_dptx_phy_ps>; +}; + &dcp { status = "disabled"; }; diff --git a/arch/arm64/boot/dts/apple/t6021-j475c.dts b/arch/arm64/boot/dts/apple/t6021-j475c.dts index 46708998e86f96..c954d02a038d9f 100644 --- a/arch/arm64/boot/dts/apple/t6021-j475c.dts +++ b/arch/arm64/boot/dts/apple/t6021-j475c.dts @@ -52,7 +52,40 @@ status = "okay"; }; + +#define USE_DCPEXT0 1 + +#if USE_DCPEXT0 +/ { + aliases { + dcpext0 = &dcpext0; + /delete-property/ dcp; + }; +}; + +&framebuffer0 { + power-domains = <&ps_dispext0_cpu0>, <&ps_dptx_phy_ps>; +}; + +&dcp { + status = "disabled"; +}; +&display { + iommus = <&dispext0_dart 0>; +}; +&dispext0_dart { + status = "okay"; +}; +&dcpext0_dart { + status = "okay"; +}; +&dcpext0_mbox { + status = "okay"; +}; +&dcpext0 { +#else &dcp { +#endif status = "okay"; apple,connector-type = "HDMI-A"; From f313d23d5cecea625b7597437c9f6abdb039b576 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 27 Nov 2023 00:44:04 +0100 Subject: [PATCH 0161/1020] fixup! arm64: dts: apple: t6022-{j180,j475}: Enable dcpext0/dptx-phy/dp2hdmi Prevent m1n1 from enabling the absent dcp node. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t6022-j475d.dts | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/apple/t6022-j475d.dts b/arch/arm64/boot/dts/apple/t6022-j475d.dts index 4b139d07545925..0d53f26b6f5352 100644 --- a/arch/arm64/boot/dts/apple/t6022-j475d.dts +++ b/arch/arm64/boot/dts/apple/t6022-j475d.dts @@ -21,6 +21,7 @@ aliases { atcphy4 = &atcphy0_die1; atcphy5 = &atcphy1_die1; + /delete-property/ dcp; }; }; From 37da75f1963e1416b838bd0eefaabe7a4067aed2 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 27 Nov 2023 00:45:27 +0100 Subject: [PATCH 0162/1020] arm64: dts: apple: t8112-j473: Use dcpext for HDMI out dcp on t8112 and t602x does not wake up after sleep + reset but dcpext* does. Use dcpext0 for sharing the code with M1* devices. My interpretation of the tea leaves from Apple's marketing department suggests that dcpext is more capable (6k 60Hz vs 5k 60Hz) so use dcpext as long as only one is used. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112-j473.dts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/apple/t8112-j473.dts b/arch/arm64/boot/dts/apple/t8112-j473.dts index dbd66483f2f56e..8697eef5c7a3fc 100644 --- a/arch/arm64/boot/dts/apple/t8112-j473.dts +++ b/arch/arm64/boot/dts/apple/t8112-j473.dts @@ -18,13 +18,15 @@ aliases { bluetooth0 = &bluetooth0; + /delete-property/ dcp; + dcpext = &dcpext; ethernet0 = ðernet0; wifi0 = &wifi0; }; }; &framebuffer0 { - power-domains = <&ps_disp0_cpu0>, <&ps_dptx_ext_phy>; + power-domains = <&ps_dispext_cpu0>, <&ps_dptx_ext_phy>; }; &dptxphy { @@ -32,6 +34,22 @@ }; &dcp { + status = "disabled"; +}; + +&display { + iommus = <&dispext0_dart 0>; +}; +&dispext0_dart { + status = "okay"; +}; +&dcpext_dart { + status = "okay"; +}; +&dcpext_mbox { + status = "okay"; +}; +&dcpext { status = "okay"; apple,connector-type = "HDMI-A"; From 525d4ca8de7524477542460740454e52b61fea3c Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 30 Nov 2023 22:16:15 +0900 Subject: [PATCH 0163/1020] fixup! arm64: dts: apple: Add initial t602x device trees --- arch/arm64/boot/dts/apple/t6021-j416c.dts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/apple/t6021-j416c.dts b/arch/arm64/boot/dts/apple/t6021-j416c.dts index 331a1e93e7f352..786ac2393d7535 100644 --- a/arch/arm64/boot/dts/apple/t6021-j416c.dts +++ b/arch/arm64/boot/dts/apple/t6021-j416c.dts @@ -17,7 +17,10 @@ model = "Apple MacBook Pro (16-inch, M2 Max, 2023)"; }; -/* This machine model (only) has two extra boost CPU P-states */ +/* This machine model (only) has two extra boost CPU P-states * + * Disabled: Only the highest CPU bin (38 GPU cores) has this. + * Keep this disabled until m1n1 learns how to remove these OPPs + * for unsupported machines, otherwise it breaks cpufreq. &avalanche_opp { opp18 { opp-hz = /bits/ 64 <3528000000>; @@ -32,6 +35,7 @@ turbo-mode; }; }; +*/ &wifi0 { brcm,board-type = "apple,amami"; From a0fce157c466570e10a82432fa36a32d29d65f16 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 5 Mar 2024 20:54:53 +0100 Subject: [PATCH 0164/1020] fixup! arch: arm64: apple: Add dcp panel node for t600x based laptops Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index 3f1ade2f9066ab..19c270019c26d7 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -40,6 +40,7 @@ /* Format properties will be added by loader */ status = "disabled"; power-domains = <&ps_disp0_cpu0>; + panel = &panel; }; }; From 39f9b55c4c89d7a875353cc9415364e2c15cddba Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 5 Mar 2024 20:59:32 +0100 Subject: [PATCH 0165/1020] fixup! arch: arm64: apple: Add dcp panel node for t8103 based laptops and imacs Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8103-j293.dts | 4 ++++ arch/arm64/boot/dts/apple/t8103-j313.dts | 4 ++++ arch/arm64/boot/dts/apple/t8103-j456.dts | 4 ++++ arch/arm64/boot/dts/apple/t8103-j457.dts | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts index bb8b878630bac1..74f205e6662697 100644 --- a/arch/arm64/boot/dts/apple/t8103-j293.dts +++ b/arch/arm64/boot/dts/apple/t8103-j293.dts @@ -43,6 +43,10 @@ }; }; +&framebuffer0 { + panel = &panel; +}; + &bluetooth0 { brcm,board-type = "apple,honshu"; }; diff --git a/arch/arm64/boot/dts/apple/t8103-j313.dts b/arch/arm64/boot/dts/apple/t8103-j313.dts index dc96f17aea2fed..97facb88428fe6 100644 --- a/arch/arm64/boot/dts/apple/t8103-j313.dts +++ b/arch/arm64/boot/dts/apple/t8103-j313.dts @@ -39,6 +39,10 @@ }; }; +&framebuffer0 { + panel = &panel; +}; + &bluetooth0 { brcm,board-type = "apple,shikoku"; }; diff --git a/arch/arm64/boot/dts/apple/t8103-j456.dts b/arch/arm64/boot/dts/apple/t8103-j456.dts index b7cc5cb8a60af1..b77f998009fae3 100644 --- a/arch/arm64/boot/dts/apple/t8103-j456.dts +++ b/arch/arm64/boot/dts/apple/t8103-j456.dts @@ -30,6 +30,10 @@ }; }; +&framebuffer0 { + panel = &panel; +}; + &bluetooth0 { brcm,board-type = "apple,capri"; }; diff --git a/arch/arm64/boot/dts/apple/t8103-j457.dts b/arch/arm64/boot/dts/apple/t8103-j457.dts index 10742637efc2f3..282dc912a49444 100644 --- a/arch/arm64/boot/dts/apple/t8103-j457.dts +++ b/arch/arm64/boot/dts/apple/t8103-j457.dts @@ -30,6 +30,10 @@ }; }; +&framebuffer0 { + panel = &panel; +}; + &bluetooth0 { brcm,board-type = "apple,santorini"; }; From 91adce0a4c40199df8ad1a576b7b2dcaec798e02 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 5 Mar 2024 21:00:32 +0100 Subject: [PATCH 0166/1020] fixup! arm64: dts: apple: t8112: Add dcp/disp0 nodes Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112-j413.dts | 4 ++++ arch/arm64/boot/dts/apple/t8112-j493.dts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j413.dts b/arch/arm64/boot/dts/apple/t8112-j413.dts index d5104c24d2e4e3..1f75df87a12525 100644 --- a/arch/arm64/boot/dts/apple/t8112-j413.dts +++ b/arch/arm64/boot/dts/apple/t8112-j413.dts @@ -46,6 +46,10 @@ }; }; +&framebuffer0 { + panel = &panel; +}; + /* * Force the bus number assignments so that we can declare some of the * on-board devices and properties that are populated by the bootloader diff --git a/arch/arm64/boot/dts/apple/t8112-j493.dts b/arch/arm64/boot/dts/apple/t8112-j493.dts index 0624d854b5542e..059edbdca3ec40 100644 --- a/arch/arm64/boot/dts/apple/t8112-j493.dts +++ b/arch/arm64/boot/dts/apple/t8112-j493.dts @@ -46,6 +46,10 @@ }; }; +&framebuffer0 { + panel = &panel; +}; + &display_dfr { status = "okay"; #address-cells = <1>; From d7d48709cc51749e35313afa72b8e834a5897672 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 5 Mar 2024 21:01:13 +0100 Subject: [PATCH 0167/1020] fixup! arm64: dts: apple: Add devicetree for Macbook Air (15-inch, M2, 2023) Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112-j415.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j415.dts b/arch/arm64/boot/dts/apple/t8112-j415.dts index 82f86859efdbea..8aa576477837d8 100644 --- a/arch/arm64/boot/dts/apple/t8112-j415.dts +++ b/arch/arm64/boot/dts/apple/t8112-j415.dts @@ -46,6 +46,10 @@ }; }; +&framebuffer0 { + panel = &panel; +}; + /* * Force the bus number assignments so that we can declare some of the * on-board devices and properties that are populated by the bootloader From 4d3bec36cd5c21d81e6dc356b0b7d78167631ed2 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 24 Mar 2024 09:50:07 +0100 Subject: [PATCH 0168/1020] fixup! arm64: dts: apple: t602x: Add lpdptx-phy node --- arch/arm64/boot/dts/apple/t602x-dieX.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/apple/t602x-dieX.dtsi b/arch/arm64/boot/dts/apple/t602x-dieX.dtsi index dda942326a6cb8..108f06ea34525d 100644 --- a/arch/arm64/boot/dts/apple/t602x-dieX.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-dieX.dtsi @@ -258,7 +258,7 @@ reg = <0x3 0x9c000000 0x0 0x4000>, <0x3 0x9c040000 0x0 0xc000>; reg-names = "core", "dptx"; - power-domain = <&DIE_NODE(ps_dptx_phy_ps)>; + power-domains = <&DIE_NODE(ps_dptx_phy_ps)>; #phy-cells = <0>; #reset-cells = <0>; status = "disabled"; /* only exposed on desktop devices */ From 95faea5a692cfa32ecfd18447d2c492e78ee91bd Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 24 Mar 2024 09:54:18 +0100 Subject: [PATCH 0169/1020] fixup! arm64: dts: apple: t8112: Add dptx-phy node --- arch/arm64/boot/dts/apple/t8112.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index b8a1e433268274..0761cfe976c4b7 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -1020,7 +1020,7 @@ reg = <0x2 0x3c500000 0x0 0x4000>, <0x2 0x3c540000 0x0 0xc000>; reg-names = "core", "dptx"; - power-domain = <&ps_dptx_ext_phy>; + power-domains = <&ps_dptx_ext_phy>; #phy-cells = <0>; #reset-cells = <0>; status = "disabled"; /* only used on j473 */ From 5275c05ac59e528701bf40a9c2bbf97a28ebdaec Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 25 Dec 2023 21:34:28 +0100 Subject: [PATCH 0170/1020] fixup! arm64: dts: apple: Add MTP nodes to t6020x --- arch/arm64/boot/dts/apple/t602x-die0.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/apple/t602x-die0.dtsi b/arch/arm64/boot/dts/apple/t602x-die0.dtsi index 63040483d9e2c6..9375e5777e7126 100644 --- a/arch/arm64/boot/dts/apple/t602x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-die0.dtsi @@ -198,7 +198,7 @@ }; mtp_mbox: mbox@2a9408000 { - compatible = "apple,t8112-asc-mailbox", "apple,asc-mailbox-v4"; + compatible = "apple,t6020-asc-mailbox", "apple,asc-mailbox-v4"; reg = <0x2 0xa9408000 0x0 0x4000>; interrupt-parent = <&aic>; interrupts = , From 490a57bb86e83c0e1cc1ca7d43207d7886c2c3ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sun, 12 Feb 2023 15:26:30 +0100 Subject: [PATCH 0171/1020] arm64: apple: t8103-pmgr: SIO: Add audio, spi and uart power-domains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The power-domains AUDIO_P, SPI_P and UART_P are necessary for SIO's ASC firmware to run. This is not explicitly expressed in the ADT (probably since the power-domains are implicitly turned on when macOS uses SIO). Since we plan to use SIO only for DP/HDMI audio add the power-domains explicitly as dependency of ps_sio_cpu. They might be better placed directly into the SIO node but the SIO driver doesn't support multiple power-domains. Signed-off-by: Janne Grunau Signed-off-by: Martin Povišer --- arch/arm64/boot/dts/apple/t8103-pmgr.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi index 10facd0c01e420..5d3846d44e3578 100644 --- a/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-pmgr.dtsi @@ -234,7 +234,7 @@ #power-domain-cells = <0>; #reset-cells = <0>; label = "sio_cpu"; - power-domains = <&ps_sio>; + power-domains = <&ps_sio &ps_uart_p &ps_spi_p &ps_dpa0>; }; ps_fpwm0: power-controller@1d8 { From 5da1bb43ee177f2f031a04ace5cc3c8dca49acce Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 13 Nov 2023 23:58:10 +0100 Subject: [PATCH 0172/1020] arm64: apple: t8112-pmgr: SIO: Add audio, spi and uart power-domains The power-domains AUDIO_P, SPI_P and UART_P are necessary for SIO's ASC firmware to run. This is not explicitly expressed in the ADT (probably since the power-domains are implicitly turned on when macOS uses SIO). Since we plan to use SIO only for DP/HDMI audio add the power-domains explicitly as dependency of ps_sio_cpu. They might be better placed directly into the SIO node but the SIO driver doesn't support multiple power-domains. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112-pmgr.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi index 102ff3ad0535d0..ab8ec9bd4e4401 100644 --- a/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8112-pmgr.dtsi @@ -176,7 +176,7 @@ #power-domain-cells = <0>; #reset-cells = <0>; label = "sio_cpu"; - power-domains = <&ps_sio>; + power-domains = <&ps_sio &ps_uart_p &ps_spi_p &ps_dpa0>; }; ps_fpwm0: power-controller@1c8 { From 007180a6a07b49cbe6dcc04736e87112e2c43bea Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 21 Apr 2024 18:01:37 +0200 Subject: [PATCH 0173/1020] arm64: apple: t600x: pmgr: SIO: Add audio, spi and uart power-domains The power-domains AUDIO_P, SPI_P and UART_P are necessary for SIO's ASC firmware to run. This is not explicitly expressed in the ADT (probably since the power-domains are implicitly turned on when macOS uses SIO). Since we plan to use SIO only for DP/HDMI audio add the power-domains explicitly as dependency of ps_sio_cpu. They might be better placed directly into the SIO node butr the SIO driver doesn't support multiple power-domains. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t600x-pmgr.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi b/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi index 88bd7a760f370f..3517b2aeb5f61f 100644 --- a/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-pmgr.dtsi @@ -826,7 +826,7 @@ #power-domain-cells = <0>; #reset-cells = <0>; label = DIE_LABEL(sio_cpu); - power-domains = <&DIE_NODE(ps_sio)>; + power-domains = <&DIE_NODE(ps_sio) &DIE_NODE(ps_uart_p) &DIE_NODE(ps_spi_p) &DIE_NODE(ps_audio_p)>; }; DIE_NODE(ps_fpwm0): power-controller@190 { From ac5cd20fefa3cab83592c2b6147bb0f9212a2049 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 21 Apr 2024 18:01:37 +0200 Subject: [PATCH 0174/1020] arm64: apple: t602x: pmgr: SIO: Add audio, spi and uart power-domains The power-domains AUDIO_P, SPI_P and UART_P are necessary for SIO's ASC firmware to run. This is not explicitly expressed in the ADT (probably since the power-domains are implicitly turned on when macOS uses SIO). Since we plan to use SIO only for DP/HDMI audio add the power-domains explicitly as dependency of ps_sio_cpu. They might be better placed directly into the SIO node butr the SIO driver doesn't support multiple power-domains. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t602x-pmgr.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi b/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi index 063181e44412b5..d97287833f1bf3 100644 --- a/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-pmgr.dtsi @@ -1262,7 +1262,7 @@ #power-domain-cells = <0>; #reset-cells = <0>; label = DIE_LABEL(sio_cpu); - power-domains = <&DIE_NODE(ps_sio)>; + power-domains = <&DIE_NODE(ps_sio) &DIE_NODE(ps_uart_p) &DIE_NODE(ps_spi_p) &DIE_NODE(ps_audio_p)>; }; DIE_NODE(ps_fpwm0): power-controller@1e8 { From 1212770ff131d327ecfb1e919626df600582e26a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 28 Nov 2022 17:10:01 +0100 Subject: [PATCH 0175/1020] arm64: apple: t8103: Add SIO, DPA nodes; hook up to DCP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- arch/arm64/boot/dts/apple/t8103-j274.dts | 5 ++ arch/arm64/boot/dts/apple/t8103.dtsi | 90 ++++++++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-j274.dts b/arch/arm64/boot/dts/apple/t8103-j274.dts index b20300a5b18306..b12dbecb5f0cd7 100644 --- a/arch/arm64/boot/dts/apple/t8103-j274.dts +++ b/arch/arm64/boot/dts/apple/t8103-j274.dts @@ -18,6 +18,7 @@ aliases { ethernet0 = ðernet0; + sio = &sio; }; }; @@ -25,6 +26,10 @@ apple,connector-type = "HDMI-A"; }; +&dpaudio0 { + status = "okay"; +}; + &bluetooth0 { brcm,board-type = "apple,atlantisb"; }; diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index 654ebb7174de5d..00f11a3b76cda2 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -628,6 +628,17 @@ iommus = <&disp0_dart 4>; phandle = <&disp0_piodma>; }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + dcp_audio: endpoint { + remote-endpoint = <&dpaudio0_dcp>; + }; + }; + }; }; display: display-subsystem { @@ -846,6 +857,32 @@ status = "disabled"; }; + sio_mbox: mbox@236408000 { + compatible = "apple,t8103-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x2 0x36408000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + power-domains = <&ps_sio>; + }; + + sio: sio@236400000 { + compatible = "apple,t8103-sio", "apple,sio"; + reg = <0x2 0x36400000 0x0 0x8000>; + dma-channels = <128>; + #dma-cells = <1>; + mboxes = <&sio_mbox>; + iommus = <&sio_dart 0>; + power-domains = <&ps_sio_cpu>; + resets = <&ps_sio>; /* TODO: verify reset does something */ + status = "disabled"; + }; + admac: dma-controller@238200000 { compatible = "apple,t8103-admac", "apple,admac"; reg = <0x2 0x38200000 0x0 0x34000>; @@ -860,6 +897,48 @@ resets = <&ps_audio_p>; }; + dpaudio0: audio-controller@238330000 { + compatible = "apple,t8103-dpaudio", "apple,dpaudio"; + reg = <0x2 0x38330000 0x0 0x4000>; + dmas = <&sio 0x64>; + dma-names = "tx"; + power-domains = <&ps_dpa0>; + reset-domains = <&ps_dpa0>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + dpaudio0_dcp: endpoint { + remote-endpoint = <&dcp_audio>; + }; + }; + }; + }; + + dpaudio1: audio-controller@238334000 { + compatible = "apple,t8103-dpaudio", "apple,dpaudio"; + reg = <0x2 0x38334000 0x0 0x4000>; + dmas = <&sio 0x66>; + dma-names = "tx"; + power-domains = <&ps_dpa1>; + reset-domains = <&ps_dpa1>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + dpaudio1_dcp: endpoint { + remote-endpoint = <&dcpext_audio>; + }; + }; + }; + }; + mca: i2s@238400000 { compatible = "apple,t8103-mca", "apple,mca"; reg = <0x2 0x38400000 0x0 0x18000>, @@ -1258,6 +1337,17 @@ iommus = <&dispext0_dart 4>; phandle = <&dispext0_piodma>; }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + dcpext_audio: endpoint { + remote-endpoint = <&dpaudio1_dcp>; + }; + }; + }; }; ans_mbox: mbox@277408000 { From 525b850e8a6e81258c27d1c413334f0c98de9ae7 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 13 Nov 2023 23:35:07 +0100 Subject: [PATCH 0176/1020] arm64: apple: t8112: Add SIO, DPA nodes; hook up to DCP Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112-j473.dts | 5 ++ arch/arm64/boot/dts/apple/t8112.dtsi | 90 ++++++++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j473.dts b/arch/arm64/boot/dts/apple/t8112-j473.dts index 8697eef5c7a3fc..5ad461092fbd7e 100644 --- a/arch/arm64/boot/dts/apple/t8112-j473.dts +++ b/arch/arm64/boot/dts/apple/t8112-j473.dts @@ -21,6 +21,7 @@ /delete-property/ dcp; dcpext = &dcpext; ethernet0 = ðernet0; + sio = &sio; wifi0 = &wifi0; }; }; @@ -64,6 +65,10 @@ apple,dptx-phy = <5>; }; +&dpaudio1 { + status = "okay"; +}; + /* * Provide labels for the USB type C ports. */ diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index 0761cfe976c4b7..474379085b80dc 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -702,6 +702,17 @@ iommus = <&disp0_dart 4>; phandle = <&disp0_piodma>; }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + dcp_audio: endpoint { + remote-endpoint = <&dpaudio0_dcp>; + }; + }; + }; }; display: display-subsystem { @@ -856,6 +867,32 @@ status = "disabled"; }; + sio_mbox: mbox@236408000 { + compatible = "apple,t8112-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x2 0x36408000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + power-domains = <&ps_sio_cpu>; + }; + + sio: sio@236400000 { + compatible = "apple,t8112-sio", "apple,sio"; + reg = <0x2 0x36400000 0x0 0x8000>; + dma-channels = <128>; + #dma-cells = <1>; + mboxes = <&sio_mbox>; + iommus = <&sio_dart 0>; + power-domains = <&ps_sio_cpu>; + resets = <&ps_sio>; /* TODO: verify reset does something */ + status = "disabled"; + }; + admac: dma-controller@238200000 { compatible = "apple,t8112-admac", "apple,admac"; reg = <0x2 0x38200000 0x0 0x34000>; @@ -870,6 +907,48 @@ resets = <&ps_audio_p>; }; + dpaudio0: audio-controller@238330000 { + compatible = "apple,t8112-dpaudio", "apple,dpaudio"; + reg = <0x2 0x38330000 0x0 0x4000>; + dmas = <&sio 0x64>; + dma-names = "tx"; + power-domains = <&ps_dpa0>; + reset-domains = <&ps_dpa0>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + dpaudio0_dcp: endpoint { + remote-endpoint = <&dcp_audio>; + }; + }; + }; + }; + + dpaudio1: audio-controller@238334000 { + compatible = "apple,t8112-dpaudio", "apple,dpaudio"; + reg = <0x2 0x38334000 0x0 0x4000>; + dmas = <&sio 0x66>; + dma-names = "tx"; + power-domains = <&ps_dpa1>; + reset-domains = <&ps_dpa1>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + dpaudio1_dcp: endpoint { + remote-endpoint = <&dcpext_audio>; + }; + }; + }; + }; + mca: i2s@238400000 { compatible = "apple,t8112-mca", "apple,mca"; reg = <0x2 0x38400000 0x0 0x18000>, @@ -1457,6 +1536,17 @@ iommus = <&dispext0_dart 4>; phandle = <&dispext0_piodma>; }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + dcpext_audio: endpoint { + remote-endpoint = <&dpaudio1_dcp>; + }; + }; + }; }; ans_mbox: mbox@277408000 { From c22ba2ebd81338ef67b18be751437c1711ec4312 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 19 Nov 2023 14:55:00 +0100 Subject: [PATCH 0177/1020] arm64: apple: t600x: Move dart_sio* to dieX j375d uses SIO on the second die for DP audio for its dcpexts. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t600x-die0.dtsi | 18 ------------------ arch/arm64/boot/dts/apple/t600x-dieX.dtsi | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index 89357b619363c2..252c05316d7186 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -248,24 +248,6 @@ phandle = <&display>; }; - sio_dart_0: iommu@39b004000 { - compatible = "apple,t6000-dart"; - reg = <0x3 0x9b004000 0x0 0x4000>; - interrupt-parent = <&aic>; - interrupts = ; - #iommu-cells = <1>; - power-domains = <&ps_sio_cpu>; - }; - - sio_dart_1: iommu@39b008000 { - compatible = "apple,t6000-dart"; - reg = <0x3 0x9b008000 0x0 0x8000>; - interrupt-parent = <&aic>; - interrupts = ; - #iommu-cells = <1>; - power-domains = <&ps_sio_cpu>; - }; - fpwm0: pwm@39b030000 { compatible = "apple,t6000-fpwm", "apple,s5l-fpwm"; reg = <0x3 0x9b030000 0x0 0x4000>; diff --git a/arch/arm64/boot/dts/apple/t600x-dieX.dtsi b/arch/arm64/boot/dts/apple/t600x-dieX.dtsi index 99c1e8a774d685..8f862798d9d2fd 100644 --- a/arch/arm64/boot/dts/apple/t600x-dieX.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-dieX.dtsi @@ -408,6 +408,24 @@ }; }; + DIE_NODE(sio_dart_0): iommu@39b004000 { + compatible = "apple,t6000-dart"; + reg = <0x3 0x9b004000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #iommu-cells = <1>; + power-domains = <&DIE_NODE(ps_sio_cpu)>; + }; + + DIE_NODE(sio_dart_1): iommu@39b008000 { + compatible = "apple,t6000-dart"; + reg = <0x3 0x9b008000 0x0 0x8000>; + interrupt-parent = <&aic>; + interrupts = ; + #iommu-cells = <1>; + power-domains = <&DIE_NODE(ps_sio_cpu)>; + }; + DIE_NODE(pinctrl_ap): pinctrl@39b028000 { compatible = "apple,t6000-pinctrl", "apple,pinctrl"; reg = <0x3 0x9b028000 0x0 0x4000>; From 5bacf6bc5717cf232718e5b3d37a9ba06ea7c58e Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 19 Nov 2023 14:57:09 +0100 Subject: [PATCH 0178/1020] arm64: apple: t600x: Add sio and dpaudio device nodes Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t600x-die0.dtsi | 32 +++++ arch/arm64/boot/dts/apple/t600x-dieX.dtsi | 136 ++++++++++++++++++++++ 2 files changed, 168 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index 252c05316d7186..a378f65f9126d9 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -203,6 +203,27 @@ apple,dma-range = <0x1f0 0x0 0x0 0xfc000000>; }; + dpaudio0: audio-controller@39b500000 { + compatible = "apple,t6000-dpaudio", "apple,dpaudio"; + reg = <0x3 0x9b500000 0x0 0x4000>; + dmas = <&sio 0x64>; + dma-names = "tx"; + power-domains = <&ps_dpa0>; + reset-domains = <&ps_dpa0>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + dpaudio0_dcp: endpoint { + remote-endpoint = <&dcp_audio>; + }; + }; + }; + }; + dcp_mbox: mbox@38bc08000 { compatible = "apple,t6000-asc-mailbox", "apple,asc-mailbox-v4"; reg = <0x3 0x8bc08000 0x0 0x4000>; @@ -239,6 +260,17 @@ iommus = <&disp0_dart 4>; phandle = <&disp0_piodma>; }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + dcp_audio: endpoint { + remote-endpoint = <&dpaudio0_dcp>; + }; + }; + }; }; display: display-subsystem { diff --git a/arch/arm64/boot/dts/apple/t600x-dieX.dtsi b/arch/arm64/boot/dts/apple/t600x-dieX.dtsi index 8f862798d9d2fd..6473dc3118218d 100644 --- a/arch/arm64/boot/dts/apple/t600x-dieX.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-dieX.dtsi @@ -85,6 +85,17 @@ piodma { iommus = <&DIE_NODE(dispext0_dart) 4>; }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + DIE_NODE(dcpext0_audio): endpoint { + remote-endpoint = <&DIE_NODE(dpaudio1_dcp)>; + }; + }; + }; }; DIE_NODE(pmgr): power-management@28e080000 { @@ -406,6 +417,17 @@ piodma { iommus = <&DIE_NODE(dispext1_dart) 4>; }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + DIE_NODE(dcpext1_audio): endpoint { + remote-endpoint = <&DIE_NODE(dpaudio2_dcp)>; + }; + }; + }; }; DIE_NODE(sio_dart_0): iommu@39b004000 { @@ -451,6 +473,120 @@ #interrupt-cells = <2>; }; + DIE_NODE(sio_mbox): mbox@39bc08000 { + compatible = "apple,t6000-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x3 0x9bc08000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + power-domains = <&DIE_NODE(ps_sio_cpu)>; + }; + + DIE_NODE(sio): sio@39bc00000 { + compatible = "apple,t6000-sio", "apple,sio"; + reg = <0x3 0x9bc00000 0x0 0x8000>; + dma-channels = <128>; + #dma-cells = <1>; + mboxes = <&DIE_NODE(sio_mbox)>; + iommus = <&DIE_NODE(sio_dart_0) 0>, <&DIE_NODE(sio_dart_1) 0>; + power-domains = <&DIE_NODE(ps_sio_cpu)>; + resets = <&DIE_NODE(ps_sio)>; /* TODO: verify reset does something */ + status = "disabled"; + }; + + DIE_NODE(dpaudio1): audio-controller@39b504000 { + compatible = "apple,t6000-dpaudio", "apple,dpaudio"; + reg = <0x3 0x9b540000 0x0 0x4000>; + dmas = <&DIE_NODE(sio) 0x66>; + dma-names = "tx"; + power-domains = <&DIE_NODE(ps_dpa1)>; + reset-domains = <&DIE_NODE(ps_dpa1)>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + DIE_NODE(dpaudio1_dcp): endpoint { + remote-endpoint = <&DIE_NODE(dcpext0_audio)>; + }; + }; + }; + }; + + DIE_NODE(dpaudio2): audio-controller@39b508000 { + compatible = "apple,t6000-dpaudio", "apple,dpaudio"; + reg = <0x3 0x9b580000 0x0 0x4000>; + dmas = <&DIE_NODE(sio) 0x68>; + dma-names = "tx"; + power-domains = <&DIE_NODE(ps_dpa2)>; + reset-domains = <&DIE_NODE(ps_dpa2)>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + DIE_NODE(dpaudio2_dcp): endpoint { + remote-endpoint = <&DIE_NODE(dcpext1_audio)>; + }; + }; + }; + }; + + /* + * omit dpaudio3 / 4 as long as the linked dcpext nodes don't exist + * + DIE_NODE(dpaudio3): audio-controller@39b50c000 { + compatible = "apple,t6000-dpaudio", "apple,dpaudio"; + reg = <0x3 0x9b5c0000 0x0 0x4000>; + dmas = <&DIE_NODE(sio) 0x6a>; + dma-names = "tx"; + power-domains = <&DIE_NODE(ps_dpa3)>; + reset-domains = <&DIE_NODE(ps_dpa3)>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + DIE_NODE(dpaudio3_dcp): endpoint { + remote-endpoint = <&DIE_NODE(dcpext2_audio)>; + }; + }; + }; + }; + + DIE_NODE(dpaudio4): audio-controller@39b510000 { + compatible = "apple,t6000-dpaudio", "apple,dpaudio"; + reg = <0x3 0x9b500000 0x0 0x4000>; + dmas = <&DIE_NODE(sio) 0x6c>; + dma-names = "tx"; + power-domains = <&DIE_NODE(ps_dpa4)>; + reset-domains = <&DIE_NODE(ps_dpa4)>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + DIE_NODE(dpaudio4_dcp): endpoint { + remote-endpoint = <&DIE_NODE(dcpext3_audio)>; + }; + }; + }; + }; + */ + DIE_NODE(dwc3_0_dart_0): iommu@702f00000 { compatible = "apple,t6000-dart"; reg = <0x7 0x02f00000 0x0 0x4000>; From 4136082e07135f46d14b888f121fcffa08276b21 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 21 Jan 2024 14:45:49 +0100 Subject: [PATCH 0179/1020] arm64: apple: t602x: Add sio and dpaudio device nodes Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi | 1 + arch/arm64/boot/dts/apple/t602x-die0.dtsi | 41 ++++-- arch/arm64/boot/dts/apple/t602x-dieX.dtsi | 146 +++++++++++++++++++++ 3 files changed, 179 insertions(+), 9 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi b/arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi index 9b7391b922db54..33e439903080ad 100644 --- a/arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi +++ b/arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi @@ -59,6 +59,7 @@ /delete-node/ &dcp_dart; /delete-node/ &dcp_mbox; /delete-node/ &dcp; +/delete-node/ &dpaudio0; /* delete unused always-on power-domains */ /delete-node/ &ps_disp0_cpu0; diff --git a/arch/arm64/boot/dts/apple/t602x-die0.dtsi b/arch/arm64/boot/dts/apple/t602x-die0.dtsi index 9375e5777e7126..fb50c02d6c39bf 100644 --- a/arch/arm64/boot/dts/apple/t602x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-die0.dtsi @@ -372,6 +372,17 @@ iommus = <&disp0_dart 4>; phandle = <&disp0_piodma>; }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + dcp_audio: endpoint { + remote-endpoint = <&dpaudio0_dcp>; + }; + }; + }; }; display: display-subsystem { @@ -381,15 +392,6 @@ phandle = <&display>; }; - sio_dart: iommu@39b008000 { - compatible = "apple,t6020-dart", "apple,t8110-dart"; - reg = <0x3 0x9b008000 0x0 0x8000>; - interrupt-parent = <&aic>; - interrupts = ; - #iommu-cells = <1>; - power-domains = <&ps_sio_cpu>; - }; - fpwm0: pwm@39b030000 { compatible = "apple,t6020-fpwm", "apple,s5l-fpwm"; reg = <0x3 0x9b030000 0x0 0x4000>; @@ -596,6 +598,27 @@ resets = <&ps_audio_p>; }; + dpaudio0: audio-controller@39b500000 { + compatible = "apple,t6020-dpaudio", "apple,dpaudio"; + reg = <0x3 0x9b500000 0x0 0x4000>; + dmas = <&sio 0x64>; + dma-names = "tx"; + power-domains = <&ps_dpa0>; + reset-domains = <&ps_dpa0>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + dpaudio0_dcp: endpoint { + remote-endpoint = <&dcp_audio>; + }; + }; + }; + }; + mca: mca@39b600000 { compatible = "apple,t6020-mca", "apple,mca"; reg = <0x3 0x9b600000 0x0 0x10000>, diff --git a/arch/arm64/boot/dts/apple/t602x-dieX.dtsi b/arch/arm64/boot/dts/apple/t602x-dieX.dtsi index 108f06ea34525d..86cd3af1ab80a3 100644 --- a/arch/arm64/boot/dts/apple/t602x-dieX.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-dieX.dtsi @@ -85,6 +85,17 @@ piodma { iommus = <&DIE_NODE(dispext0_dart) 4>; }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + DIE_NODE(dcpext0_audio): endpoint { + remote-endpoint = <&DIE_NODE(dpaudio1_dcp)>; + }; + }; + }; }; DIE_NODE(pmgr): power-management@28e080000 { @@ -226,6 +237,27 @@ piodma { iommus = <&DIE_NODE(dispext1_dart) 4>; }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + DIE_NODE(dcpext1_audio): endpoint { + remote-endpoint = <&DIE_NODE(dpaudio2_dcp)>; + }; + }; + }; + }; + + DIE_NODE(sio_dart): iommu@39b008000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x3 0x9b008000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #iommu-cells = <1>; + power-domains = <&DIE_NODE(ps_sio)>; + //apple,dma-range = <0x100 0x0001c000 0x2ff 0xfffe4000>; }; DIE_NODE(pinctrl_ap): pinctrl@39b028000 { @@ -253,6 +285,120 @@ #interrupt-cells = <2>; }; + DIE_NODE(sio_mbox): mbox@39bc08000 { + compatible = "apple,t6020-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x3 0x9bc08000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + power-domains = <&DIE_NODE(ps_sio_cpu)>; + }; + + DIE_NODE(sio): sio@39bc00000 { + compatible = "apple,t6020-sio", "apple,sio"; + reg = <0x3 0x9bc00000 0x0 0x8000>; + dma-channels = <128>; + #dma-cells = <1>; + mboxes = <&DIE_NODE(sio_mbox)>; + iommus = <&DIE_NODE(sio_dart) 0>; + power-domains = <&DIE_NODE(ps_sio_cpu)>; + resets = <&DIE_NODE(ps_sio_cpu)>; + status = "disabled"; + }; + + DIE_NODE(dpaudio1): audio-controller@39b504000 { + compatible = "apple,t6020-dpaudio", "apple,dpaudio"; + reg = <0x3 0x9b540000 0x0 0x4000>; + dmas = <&DIE_NODE(sio) 0x66>; + dma-names = "tx"; + power-domains = <&DIE_NODE(ps_dpa1)>; + reset-domains = <&DIE_NODE(ps_dpa1)>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + DIE_NODE(dpaudio1_dcp): endpoint { + remote-endpoint = <&DIE_NODE(dcpext0_audio)>; + }; + }; + }; + }; + + DIE_NODE(dpaudio2): audio-controller@39b508000 { + compatible = "apple,t6020-dpaudio", "apple,dpaudio"; + reg = <0x3 0x9b580000 0x0 0x4000>; + dmas = <&DIE_NODE(sio) 0x68>; + dma-names = "tx"; + power-domains = <&DIE_NODE(ps_dpa2)>; + reset-domains = <&DIE_NODE(ps_dpa2)>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + DIE_NODE(dpaudio2_dcp): endpoint { + remote-endpoint = <&DIE_NODE(dcpext1_audio)>; + }; + }; + }; + }; + + /* + * omit dpaudio3 / 4 as long as the linked dcpext nodes don't exist + * + DIE_NODE(dpaudio3): audio-controller@39b50c000 { + compatible = "apple,t6020-dpaudio", "apple,dpaudio"; + reg = <0x3 0x9b5c0000 0x0 0x4000>; + dmas = <&DIE_NODE(sio) 0x6a>; + dma-names = "tx"; + power-domains = <&DIE_NODE(ps_dpa3)>; + reset-domains = <&DIE_NODE(ps_dpa3)>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + DIE_NODE(dpaudio3_dcp): endpoint { + remote-endpoint = <&DIE_NODE(dcpext2_audio)>; + }; + }; + }; + }; + + DIE_NODE(dpaudio4): audio-controller@39b510000 { + compatible = "apple,t6020-dpaudio", "apple,dpaudio"; + reg = <0x3 0x9b500000 0x0 0x4000>; + dmas = <&DIE_NODE(sio) 0x6c>; + dma-names = "tx"; + power-domains = <&DIE_NODE(ps_dpa4)>; + reset-domains = <&DIE_NODE(ps_dpa4)>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + DIE_NODE(dpaudio4_dcp): endpoint { + remote-endpoint = <&DIE_NODE(dcpext3_audio)>; + }; + }; + }; + }; + */ + DIE_NODE(lpdptxphy): phy@39c000000 { compatible = "apple,t6020-dptx-phy", "apple,dptx-phy"; reg = <0x3 0x9c000000 0x0 0x4000>, From 3d7a55e0a4c450226c0092ec3014b0ba42ffafc9 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 7 Apr 2024 23:10:55 +0200 Subject: [PATCH 0180/1020] arm64: apple: t60xx: Enable DP/HMI audio nodes on all devices Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t6001-j375c.dts | 4 ++++ arch/arm64/boot/dts/apple/t6002-j375d.dts | 4 ++++ arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi | 5 +++++ arch/arm64/boot/dts/apple/t600x-j375.dtsi | 1 + arch/arm64/boot/dts/apple/t6020-j474s.dts | 6 ++++++ arch/arm64/boot/dts/apple/t6021-j475c.dts | 6 ++++++ arch/arm64/boot/dts/apple/t6022-j475d.dts | 1 + arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi | 5 +++++ 8 files changed, 32 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6001-j375c.dts b/arch/arm64/boot/dts/apple/t6001-j375c.dts index a71b1ebb29d956..f0c31f301dfef0 100644 --- a/arch/arm64/boot/dts/apple/t6001-j375c.dts +++ b/arch/arm64/boot/dts/apple/t6001-j375c.dts @@ -17,6 +17,10 @@ model = "Apple Mac Studio (M1 Max, 2022)"; }; +&dpaudio0 { + status = "okay"; +}; + &sound { compatible = "apple,j375-macaudio", "apple,macaudio"; model = "Mac Studio J375"; diff --git a/arch/arm64/boot/dts/apple/t6002-j375d.dts b/arch/arm64/boot/dts/apple/t6002-j375d.dts index 062bfc72575ebf..e16254b1d6819b 100644 --- a/arch/arm64/boot/dts/apple/t6002-j375d.dts +++ b/arch/arm64/boot/dts/apple/t6002-j375d.dts @@ -21,6 +21,10 @@ }; }; +&dpaudio0 { + status = "okay"; +}; + &sound { compatible = "apple,j375-macaudio", "apple,macaudio"; model = "Mac Studio J375"; diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index 19c270019c26d7..dc78f503bc245a 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -24,6 +24,7 @@ disp0_piodma = &disp0_piodma; nvram = &nvram; serial0 = &serial0; + sio = &sio; wifi0 = &wifi0; }; @@ -114,6 +115,10 @@ apple,dptx-phy = <3>; }; +&dpaudio1 { + status = "okay"; +}; + &atcphy3 { apple,mode-fixed-dp; }; diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi index d8db13fc02e3f6..d740a06a995984 100644 --- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi @@ -24,6 +24,7 @@ ethernet0 = ðernet0; nvram = &nvram; serial0 = &serial0; + sio = &sio; wifi0 = &wifi0; }; diff --git a/arch/arm64/boot/dts/apple/t6020-j474s.dts b/arch/arm64/boot/dts/apple/t6020-j474s.dts index e4d9b580f2ef4d..180ea8aa7646bb 100644 --- a/arch/arm64/boot/dts/apple/t6020-j474s.dts +++ b/arch/arm64/boot/dts/apple/t6020-j474s.dts @@ -78,8 +78,14 @@ &dcpext0_mbox { status = "okay"; }; +&dpaudio1 { + status = "okay"; +}; &dcpext0 { #else +&dpaudio0 { + status = "okay"; +}; &dcp { #endif status = "okay"; diff --git a/arch/arm64/boot/dts/apple/t6021-j475c.dts b/arch/arm64/boot/dts/apple/t6021-j475c.dts index c954d02a038d9f..aeca677cccaa7c 100644 --- a/arch/arm64/boot/dts/apple/t6021-j475c.dts +++ b/arch/arm64/boot/dts/apple/t6021-j475c.dts @@ -82,8 +82,14 @@ &dcpext0_mbox { status = "okay"; }; +&dpaudio1 { + status = "okay"; +}; &dcpext0 { #else +&dpaudio0 { + status = "okay"; +}; &dcp { #endif status = "okay"; diff --git a/arch/arm64/boot/dts/apple/t6022-j475d.dts b/arch/arm64/boot/dts/apple/t6022-j475d.dts index 0d53f26b6f5352..57521f1bb7b423 100644 --- a/arch/arm64/boot/dts/apple/t6022-j475d.dts +++ b/arch/arm64/boot/dts/apple/t6022-j475d.dts @@ -22,6 +22,7 @@ atcphy4 = &atcphy0_die1; atcphy5 = &atcphy1_die1; /delete-property/ dcp; + /delete-property/ sio; }; }; diff --git a/arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi b/arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi index 33e439903080ad..f8d2fcd485d1fc 100644 --- a/arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi +++ b/arch/arm64/boot/dts/apple/t6022-jxxxd.dtsi @@ -13,6 +13,7 @@ aliases { dcpext4 = &dcpext0_die1; disp0 = &display; + sio1 = &sio_die1; }; }; @@ -53,6 +54,10 @@ apple,dptx-die = <1>; }; +&dpaudio1_die1 { + status = "okay"; +}; + /* delete missing dcp0/disp0 */ /delete-node/ &disp0_dart; From f69a81f4bc24d239d8f3bc5de47b360a2201d987 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 27 Apr 2024 12:57:52 +0200 Subject: [PATCH 0181/1020] arm64: apple: t60x0/t60x1: Enable sio explicitly To be removed after m1n1 does this after proper setup. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi | 5 +++++ arch/arm64/boot/dts/apple/t600x-j375.dtsi | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index dc78f503bc245a..9723c6c1fb0138 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -115,6 +115,11 @@ apple,dptx-phy = <3>; }; +/* remove once m1n1 enables sio nodes after setup */ +&sio { + status = "okay"; +}; + &dpaudio1 { status = "okay"; }; diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi index d740a06a995984..e8c355ca0f21c5 100644 --- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi @@ -65,6 +65,11 @@ apple,connector-type = "HDMI-A"; }; +/* remove once m1n1 enables sio nodes after setup */ +&sio { + status = "okay"; +}; + /* USB Type C */ &i2c0 { hpm0: usb-pd@38 { From d29c24eeffe431b90e115f1e19158ac0613a41d7 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 27 Apr 2024 12:57:52 +0200 Subject: [PATCH 0182/1020] arm64: apple: t8103-j274: Enable sio explicitly To be removed after m1n1 does this after proper setup. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8103-j274.dts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-j274.dts b/arch/arm64/boot/dts/apple/t8103-j274.dts index b12dbecb5f0cd7..9f8712b5b6accc 100644 --- a/arch/arm64/boot/dts/apple/t8103-j274.dts +++ b/arch/arm64/boot/dts/apple/t8103-j274.dts @@ -26,6 +26,11 @@ apple,connector-type = "HDMI-A"; }; +/* remove once m1n1 enables sio nodes after setup */ +&sio { + status = "okay"; +}; + &dpaudio0 { status = "okay"; }; From 54d52f5d5ed35e6fb9b341133c63126f442622a2 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 27 Apr 2024 12:57:52 +0200 Subject: [PATCH 0183/1020] arm64: apple: t8112-j473: Enable sio explicitly To be removed after m1n1 does this after proper setup. Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8112-j473.dts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8112-j473.dts b/arch/arm64/boot/dts/apple/t8112-j473.dts index 5ad461092fbd7e..aa6be5e55e4ce9 100644 --- a/arch/arm64/boot/dts/apple/t8112-j473.dts +++ b/arch/arm64/boot/dts/apple/t8112-j473.dts @@ -65,6 +65,11 @@ apple,dptx-phy = <5>; }; +/* remove once m1n1 enables sio nodes after setup */ +&sio { + status = "okay"; +}; + &dpaudio1 { status = "okay"; }; From f5ebb40d8ef5b574bedcdf949aadfdce5a590d7d Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 30 Apr 2024 20:34:17 +0200 Subject: [PATCH 0184/1020] fixup! arm64: apple: t60xx: Enable DP/HMI audio nodes on all devices Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t6022-j475d.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6022-j475d.dts b/arch/arm64/boot/dts/apple/t6022-j475d.dts index 57521f1bb7b423..b084ee8be12390 100644 --- a/arch/arm64/boot/dts/apple/t6022-j475d.dts +++ b/arch/arm64/boot/dts/apple/t6022-j475d.dts @@ -26,6 +26,10 @@ }; }; +&sio { + status = "disabled"; +}; + &framebuffer0 { power-domains = <&ps_dispext0_cpu0_die1>, <&ps_dptx_phy_ps_die1>; }; From 9ed81552390ec5a5aa83bd075095f66a47c6e21d Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 12 Dec 2021 12:28:41 +0900 Subject: [PATCH 0185/1020] MAINTAINERS: Add Apple dwc3 bindings to ARM/APPLE This Apple dwc3 controller variance is present on Apple ARM SoCs (t8103/t600x). Splitting this change from the binding/driver commits to avoid merge conflicts with other things touching this section, as usual. Signed-off-by: Hector Martin --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 28e20975c26f5c..392474b5cb1645 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1966,6 +1966,7 @@ F: Documentation/devicetree/bindings/pci/apple,pcie.yaml F: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml F: Documentation/devicetree/bindings/power/apple* F: Documentation/devicetree/bindings/pwm/apple,s5l-fpwm.yaml +F: Documentation/devicetree/bindings/usb/apple,dwc3.yaml F: Documentation/devicetree/bindings/watchdog/apple,wdt.yaml F: arch/arm64/boot/dts/apple/ F: drivers/bluetooth/hci_bcm4377.c From df29fedfcb5563b1209ac22e803eb6320ab08d61 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 12 Dec 2021 12:28:41 +0900 Subject: [PATCH 0186/1020] MAINTAINERS: Add apple-spi driver & binding files This Apple SPI controller is present on Apple ARM SoCs (t8103/t6000). Splitting this change from the binding/driver commits to avoid merge conflicts with other things touching this section, as usual. Signed-off-by: Hector Martin --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 392474b5cb1645..10ec4c39a35158 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1966,6 +1966,7 @@ F: Documentation/devicetree/bindings/pci/apple,pcie.yaml F: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml F: Documentation/devicetree/bindings/power/apple* F: Documentation/devicetree/bindings/pwm/apple,s5l-fpwm.yaml +F: Documentation/devicetree/bindings/spi/apple,spi.yaml F: Documentation/devicetree/bindings/usb/apple,dwc3.yaml F: Documentation/devicetree/bindings/watchdog/apple,wdt.yaml F: arch/arm64/boot/dts/apple/ @@ -1984,6 +1985,7 @@ F: drivers/nvmem/apple-efuses.c F: drivers/pinctrl/pinctrl-apple-gpio.c F: drivers/pwm/pwm-apple.c F: drivers/soc/apple/* +F: drivers/spi/spi-apple.c F: drivers/watchdog/apple_wdt.c F: include/dt-bindings/interrupt-controller/apple-aic.h F: include/dt-bindings/pinctrl/apple.h From c50103432a550958ed384f576e6f0270d90f9f91 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Sat, 15 Oct 2022 21:24:57 +0900 Subject: [PATCH 0187/1020] soc: apple: rtkit: Check for failure to send mgmt messages & log Signed-off-by: Asahi Lina --- drivers/soc/apple/rtkit.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/soc/apple/rtkit.c b/drivers/soc/apple/rtkit.c index e6d940292c9fbd..54f0905789f9a1 100644 --- a/drivers/soc/apple/rtkit.c +++ b/drivers/soc/apple/rtkit.c @@ -97,12 +97,20 @@ bool apple_rtkit_is_crashed(struct apple_rtkit *rtk) } EXPORT_SYMBOL_GPL(apple_rtkit_is_crashed); -static void apple_rtkit_management_send(struct apple_rtkit *rtk, u8 type, +static int apple_rtkit_management_send(struct apple_rtkit *rtk, u8 type, u64 msg) { + int ret; + msg &= ~APPLE_RTKIT_MGMT_TYPE; msg |= FIELD_PREP(APPLE_RTKIT_MGMT_TYPE, type); - apple_rtkit_send_message(rtk, APPLE_RTKIT_EP_MGMT, msg, NULL, false); + ret = apple_rtkit_send_message(rtk, APPLE_RTKIT_EP_MGMT, msg, NULL, false); + + if (ret) { + dev_err(rtk->dev, "RTKit: Failed to send management message: %d\n", ret); + } + + return ret; } static void apple_rtkit_management_rx_hello(struct apple_rtkit *rtk, u64 msg) @@ -742,8 +750,10 @@ static int apple_rtkit_set_ap_power_state(struct apple_rtkit *rtk, reinit_completion(&rtk->ap_pwr_ack_completion); msg = FIELD_PREP(APPLE_RTKIT_MGMT_PWR_STATE, state); - apple_rtkit_management_send(rtk, APPLE_RTKIT_MGMT_SET_AP_PWR_STATE, - msg); + ret = apple_rtkit_management_send(rtk, APPLE_RTKIT_MGMT_SET_AP_PWR_STATE, + msg); + if (ret) + return ret; ret = apple_rtkit_wait_for_completion(&rtk->ap_pwr_ack_completion); if (ret) @@ -763,8 +773,10 @@ static int apple_rtkit_set_iop_power_state(struct apple_rtkit *rtk, reinit_completion(&rtk->iop_pwr_ack_completion); msg = FIELD_PREP(APPLE_RTKIT_MGMT_PWR_STATE, state); - apple_rtkit_management_send(rtk, APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE, - msg); + ret = apple_rtkit_management_send(rtk, APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE, + msg); + if (ret) + return ret; ret = apple_rtkit_wait_for_completion(&rtk->iop_pwr_ack_completion); if (ret) @@ -865,6 +877,7 @@ EXPORT_SYMBOL_GPL(apple_rtkit_quiesce); int apple_rtkit_wake(struct apple_rtkit *rtk) { u64 msg; + int ret; if (apple_rtkit_is_running(rtk)) return -EINVAL; @@ -876,8 +889,10 @@ int apple_rtkit_wake(struct apple_rtkit *rtk) * will wait for the completion anyway. */ msg = FIELD_PREP(APPLE_RTKIT_MGMT_PWR_STATE, APPLE_RTKIT_PWR_STATE_ON); - apple_rtkit_management_send(rtk, APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE, - msg); + ret = apple_rtkit_management_send(rtk, APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE, + msg); + if (ret) + return ret; return apple_rtkit_boot(rtk); } From c2c65f1f3d558b74b141569e7438ac3aaf056efd Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Tue, 27 Sep 2022 05:10:17 +0900 Subject: [PATCH 0188/1020] soc: apple: rtkit: Log failure to send messages Signed-off-by: Asahi Lina --- drivers/soc/apple/rtkit.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/soc/apple/rtkit.c b/drivers/soc/apple/rtkit.c index 54f0905789f9a1..6ee256d112602c 100644 --- a/drivers/soc/apple/rtkit.c +++ b/drivers/soc/apple/rtkit.c @@ -596,11 +596,18 @@ int apple_rtkit_send_message(struct apple_rtkit *rtk, u8 ep, u64 message, .msg1 = ep, }; - if (rtk->crashed) + if (rtk->crashed) { + dev_warn(rtk->dev, + "RTKit: Device is crashed, cannot send message\n"); return -EINVAL; + } + if (ep >= APPLE_RTKIT_APP_ENDPOINT_START && - !apple_rtkit_is_running(rtk)) + !apple_rtkit_is_running(rtk)) { + dev_warn(rtk->dev, + "RTKit: Endpoint 0x%02x is not running, cannot send message\n", ep); return -EINVAL; + } /* * The message will be sent with a MMIO write. We need the barrier From 9ff7926b41280200369e2de487b74452860de511 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Thu, 18 Aug 2022 02:13:03 +0900 Subject: [PATCH 0189/1020] soc: apple: rtkit: Log failed buffer requests Signed-off-by: Asahi Lina --- drivers/soc/apple/rtkit.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soc/apple/rtkit.c b/drivers/soc/apple/rtkit.c index 6ee256d112602c..87d124b8739d92 100644 --- a/drivers/soc/apple/rtkit.c +++ b/drivers/soc/apple/rtkit.c @@ -303,6 +303,9 @@ static int apple_rtkit_common_rx_get_buffer(struct apple_rtkit *rtk, return 0; error: + dev_err(rtk->dev, "RTKit: failed buffer request for 0x%zx bytes (%d)\n", + buffer->size, err); + buffer->buffer = NULL; buffer->iomem = NULL; buffer->iova = 0; From 5b0f739268f47ef3168479dba48e40662a839901 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 25 Jul 2022 21:38:56 +0200 Subject: [PATCH 0190/1020] soc: apple: rtkit: Add APPLE_RTKIT_PWR_STATE_INIT This state is needed to wake the dcp IOP after m1n1 shut it down. Signed-off-by: Janne Grunau --- drivers/soc/apple/rtkit.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/soc/apple/rtkit.c b/drivers/soc/apple/rtkit.c index 87d124b8739d92..7c28a9344b01b5 100644 --- a/drivers/soc/apple/rtkit.c +++ b/drivers/soc/apple/rtkit.c @@ -12,6 +12,7 @@ enum { APPLE_RTKIT_PWR_STATE_IDLE = 0x201, /* sleeping, retain state */ APPLE_RTKIT_PWR_STATE_QUIESCED = 0x10, /* running but no communication */ APPLE_RTKIT_PWR_STATE_ON = 0x20, /* normal operating state */ + APPLE_RTKIT_PWR_STATE_INIT = 0x220, /* init after starting the coproc */ }; enum { @@ -898,7 +899,7 @@ int apple_rtkit_wake(struct apple_rtkit *rtk) * Use open-coded apple_rtkit_set_iop_power_state since apple_rtkit_boot * will wait for the completion anyway. */ - msg = FIELD_PREP(APPLE_RTKIT_MGMT_PWR_STATE, APPLE_RTKIT_PWR_STATE_ON); + msg = FIELD_PREP(APPLE_RTKIT_MGMT_PWR_STATE, APPLE_RTKIT_PWR_STATE_INIT); ret = apple_rtkit_management_send(rtk, APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE, msg); if (ret) From c3a02ad325dd4857e57bcb0b7c044ed8c51b2030 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 16 Apr 2023 21:21:17 +0900 Subject: [PATCH 0191/1020] soc: apple: rtkit: Implement OSLog buffers properly Apparently nobody can figure out where the old logic came from, but it seems like it has never been actually used on any supported firmware to this day. OSLog buffers were apparently never requested. But starting with 13.3, we actually need this implemented properly for MTP (and later AOP) to work, so let's actually do that. Signed-off-by: Hector Martin --- drivers/soc/apple/rtkit-internal.h | 1 + drivers/soc/apple/rtkit.c | 55 ++++++++++++++++++------------ 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/drivers/soc/apple/rtkit-internal.h b/drivers/soc/apple/rtkit-internal.h index 27c9fa745fd528..b8d5244678f010 100644 --- a/drivers/soc/apple/rtkit-internal.h +++ b/drivers/soc/apple/rtkit-internal.h @@ -44,6 +44,7 @@ struct apple_rtkit { struct apple_rtkit_shmem ioreport_buffer; struct apple_rtkit_shmem crashlog_buffer; + struct apple_rtkit_shmem oslog_buffer; struct apple_rtkit_shmem syslog_buffer; char *syslog_msg_buffer; diff --git a/drivers/soc/apple/rtkit.c b/drivers/soc/apple/rtkit.c index 7c28a9344b01b5..ae1fd86a617ead 100644 --- a/drivers/soc/apple/rtkit.c +++ b/drivers/soc/apple/rtkit.c @@ -67,8 +67,9 @@ enum { #define APPLE_RTKIT_SYSLOG_MSG_SIZE GENMASK_ULL(31, 24) #define APPLE_RTKIT_OSLOG_TYPE GENMASK_ULL(63, 56) -#define APPLE_RTKIT_OSLOG_INIT 1 -#define APPLE_RTKIT_OSLOG_ACK 3 +#define APPLE_RTKIT_OSLOG_BUFFER_REQUEST 1 +#define APPLE_RTKIT_OSLOG_SIZE GENMASK_ULL(55, 36) +#define APPLE_RTKIT_OSLOG_IOVA GENMASK_ULL(35, 0) #define APPLE_RTKIT_MIN_SUPPORTED_VERSION 11 #define APPLE_RTKIT_MAX_SUPPORTED_VERSION 12 @@ -260,15 +261,20 @@ static int apple_rtkit_common_rx_get_buffer(struct apple_rtkit *rtk, struct apple_rtkit_shmem *buffer, u8 ep, u64 msg) { - size_t n_4kpages = FIELD_GET(APPLE_RTKIT_BUFFER_REQUEST_SIZE, msg); u64 reply; int err; + if (ep == APPLE_RTKIT_EP_OSLOG) { + buffer->size = FIELD_GET(APPLE_RTKIT_OSLOG_SIZE, msg); + buffer->iova = FIELD_GET(APPLE_RTKIT_OSLOG_IOVA, msg) << 12; + } else { + buffer->size = FIELD_GET(APPLE_RTKIT_BUFFER_REQUEST_SIZE, msg) << 12; + buffer->iova = FIELD_GET(APPLE_RTKIT_BUFFER_REQUEST_IOVA, msg); + } + buffer->buffer = NULL; buffer->iomem = NULL; buffer->is_mapped = false; - buffer->iova = FIELD_GET(APPLE_RTKIT_BUFFER_REQUEST_IOVA, msg); - buffer->size = n_4kpages << 12; dev_dbg(rtk->dev, "RTKit: buffer request for 0x%zx bytes at %pad\n", buffer->size, &buffer->iova); @@ -293,11 +299,21 @@ static int apple_rtkit_common_rx_get_buffer(struct apple_rtkit *rtk, } if (!buffer->is_mapped) { - reply = FIELD_PREP(APPLE_RTKIT_SYSLOG_TYPE, - APPLE_RTKIT_BUFFER_REQUEST); - reply |= FIELD_PREP(APPLE_RTKIT_BUFFER_REQUEST_SIZE, n_4kpages); - reply |= FIELD_PREP(APPLE_RTKIT_BUFFER_REQUEST_IOVA, - buffer->iova); + /* oslog uses different fields */ + if (ep == APPLE_RTKIT_EP_OSLOG) { + reply = FIELD_PREP(APPLE_RTKIT_OSLOG_TYPE, + APPLE_RTKIT_OSLOG_BUFFER_REQUEST); + reply |= FIELD_PREP(APPLE_RTKIT_OSLOG_SIZE, buffer->size); + reply |= FIELD_PREP(APPLE_RTKIT_OSLOG_IOVA, + buffer->iova >> 12); + } else { + reply = FIELD_PREP(APPLE_RTKIT_SYSLOG_TYPE, + APPLE_RTKIT_BUFFER_REQUEST); + reply |= FIELD_PREP(APPLE_RTKIT_BUFFER_REQUEST_SIZE, + buffer->size >> 12); + reply |= FIELD_PREP(APPLE_RTKIT_BUFFER_REQUEST_IOVA, + buffer->iova); + } apple_rtkit_send_message(rtk, ep, reply, NULL, false); } @@ -494,25 +510,18 @@ static void apple_rtkit_syslog_rx(struct apple_rtkit *rtk, u64 msg) } } -static void apple_rtkit_oslog_rx_init(struct apple_rtkit *rtk, u64 msg) -{ - u64 ack; - - dev_dbg(rtk->dev, "RTKit: oslog init: msg: 0x%llx\n", msg); - ack = FIELD_PREP(APPLE_RTKIT_OSLOG_TYPE, APPLE_RTKIT_OSLOG_ACK); - apple_rtkit_send_message(rtk, APPLE_RTKIT_EP_OSLOG, ack, NULL, false); -} - static void apple_rtkit_oslog_rx(struct apple_rtkit *rtk, u64 msg) { u8 type = FIELD_GET(APPLE_RTKIT_OSLOG_TYPE, msg); switch (type) { - case APPLE_RTKIT_OSLOG_INIT: - apple_rtkit_oslog_rx_init(rtk, msg); + case APPLE_RTKIT_OSLOG_BUFFER_REQUEST: + apple_rtkit_common_rx_get_buffer(rtk, &rtk->oslog_buffer, + APPLE_RTKIT_EP_OSLOG, msg); break; default: - dev_warn(rtk->dev, "RTKit: Unknown oslog message: %llx\n", msg); + dev_warn(rtk->dev, "RTKit: Unknown oslog message: %llx\n", + msg); } } @@ -729,6 +738,7 @@ int apple_rtkit_reinit(struct apple_rtkit *rtk) apple_rtkit_free_buffer(rtk, &rtk->ioreport_buffer); apple_rtkit_free_buffer(rtk, &rtk->crashlog_buffer); + apple_rtkit_free_buffer(rtk, &rtk->oslog_buffer); apple_rtkit_free_buffer(rtk, &rtk->syslog_buffer); kfree(rtk->syslog_msg_buffer); @@ -916,6 +926,7 @@ void apple_rtkit_free(struct apple_rtkit *rtk) apple_rtkit_free_buffer(rtk, &rtk->ioreport_buffer); apple_rtkit_free_buffer(rtk, &rtk->crashlog_buffer); + apple_rtkit_free_buffer(rtk, &rtk->oslog_buffer); apple_rtkit_free_buffer(rtk, &rtk->syslog_buffer); kfree(rtk->syslog_msg_buffer); From 7a03f5e60fdc6cff0266be37d4e09d6462cbdf06 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 18 Apr 2023 04:19:44 +0900 Subject: [PATCH 0192/1020] soc: apple: Add driver for Apple PMGR misc controls Apple SoCs have PMGR blocks that control a bunch of power-related features. Besides the existing device power state controls (which are very uniform and handled by apple-pmgr-pwrstate), we also need to manage more random registers such as SoC-wide fabric and memory controller power states, which have a different interface. Add a driver for these kitchen sink controls. Right now it implements fabric and memory controller power state switching on system standby/s2idle, which saves about 1W of power or so on t60xx platforms. Signed-off-by: Hector Martin --- drivers/soc/apple/Kconfig | 9 ++ drivers/soc/apple/Makefile | 2 + drivers/soc/apple/apple-pmgr-misc.c | 158 ++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 drivers/soc/apple/apple-pmgr-misc.c diff --git a/drivers/soc/apple/Kconfig b/drivers/soc/apple/Kconfig index 6388cbe1e56b5a..651319e31bbad2 100644 --- a/drivers/soc/apple/Kconfig +++ b/drivers/soc/apple/Kconfig @@ -4,6 +4,15 @@ if ARCH_APPLE || COMPILE_TEST menu "Apple SoC drivers" +config APPLE_PMGR_MISC + bool "Apple SoC PMGR miscellaneous support" + depends on PM + default ARCH_APPLE + help + The PMGR block in Apple SoCs provides high-level power state + controls for SoC devices. This driver manages miscellaneous + power controls. + config APPLE_MAILBOX tristate "Apple SoC mailboxes" depends on PM diff --git a/drivers/soc/apple/Makefile b/drivers/soc/apple/Makefile index 4d9ab8f3037b71..31fb20888523ee 100644 --- a/drivers/soc/apple/Makefile +++ b/drivers/soc/apple/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_APPLE_PMGR_MISC) += apple-pmgr-misc.o + obj-$(CONFIG_APPLE_MAILBOX) += apple-mailbox.o apple-mailbox-y = mailbox.o diff --git a/drivers/soc/apple/apple-pmgr-misc.c b/drivers/soc/apple/apple-pmgr-misc.c new file mode 100644 index 00000000000000..e768f34aacc586 --- /dev/null +++ b/drivers/soc/apple/apple-pmgr-misc.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SoC PMGR device power state driver + * + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include +#include + +#define APPLE_CLKGEN_PSTATE 0 +#define APPLE_CLKGEN_PSTATE_DESIRED GENMASK(3, 0) + +#define SYS_DEV_PSTATE_SUSPEND 1 + +enum sys_device { + DEV_FABRIC, + DEV_DCS, + DEV_MAX, +}; + +struct apple_pmgr_sys_device { + void __iomem *base; + u32 active_state; + u32 suspend_state; +}; + +struct apple_pmgr_misc { + struct device *dev; + struct apple_pmgr_sys_device devices[DEV_MAX]; +}; + +static void apple_pmgr_sys_dev_set_pstate(struct apple_pmgr_misc *misc, + enum sys_device dev, bool active) +{ + u32 pstate; + u32 val; + + if (!misc->devices[dev].base) + return; + + if (active) + pstate = misc->devices[dev].active_state; + else + pstate = misc->devices[dev].suspend_state; + + printk("set %d ps to pstate %d\n", dev, pstate); + + val = readl_relaxed(misc->devices[dev].base + APPLE_CLKGEN_PSTATE); + val &= ~APPLE_CLKGEN_PSTATE_DESIRED; + val |= FIELD_PREP(APPLE_CLKGEN_PSTATE_DESIRED, pstate); + writel_relaxed(val, misc->devices[dev].base); +} + +static int __maybe_unused apple_pmgr_misc_suspend_noirq(struct device *dev) +{ + struct apple_pmgr_misc *misc = dev_get_drvdata(dev); + int i; + + for (i = 0; i < DEV_MAX; i++) + apple_pmgr_sys_dev_set_pstate(misc, i, false); + + return 0; +} + +static int __maybe_unused apple_pmgr_misc_resume_noirq(struct device *dev) +{ + struct apple_pmgr_misc *misc = dev_get_drvdata(dev); + int i; + + for (i = 0; i < DEV_MAX; i++) + apple_pmgr_sys_dev_set_pstate(misc, i, true); + + return 0; +} + +static bool apple_pmgr_init_device(struct apple_pmgr_misc *misc, + enum sys_device dev, const char *device_name) +{ + void __iomem *base; + char name[32]; + u32 val; + + snprintf(name, sizeof(name), "%s-ps", device_name); + + base = devm_platform_ioremap_resource_byname( + to_platform_device(misc->dev), name); + if (!base) + return false; + + val = readl_relaxed(base + APPLE_CLKGEN_PSTATE); + + misc->devices[dev].base = base; + misc->devices[dev].active_state = + FIELD_GET(APPLE_CLKGEN_PSTATE_DESIRED, val); + misc->devices[dev].suspend_state = SYS_DEV_PSTATE_SUSPEND; + + snprintf(name, sizeof(name), "apple,%s-min-ps", device_name); + of_property_read_u32(misc->dev->of_node, name, + &misc->devices[dev].suspend_state); + + return true; +} + +static int apple_pmgr_misc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct apple_pmgr_misc *misc; + int ret = -ENODEV; + + misc = devm_kzalloc(dev, sizeof(*misc), GFP_KERNEL); + if (!misc) + return -ENOMEM; + + misc->dev = dev; + + if (apple_pmgr_init_device(misc, DEV_FABRIC, "fabric")) + ret = 0; + + if (apple_pmgr_init_device(misc, DEV_DCS, "dcs")) + ret = 0; + + platform_set_drvdata(pdev, misc); + + return ret; +} + +static const struct of_device_id apple_pmgr_misc_of_match[] = { + { .compatible = "apple,t6000-pmgr-misc" }, + {} +}; + +MODULE_DEVICE_TABLE(of, apple_pmgr_misc_of_match); + +static const struct dev_pm_ops apple_pmgr_misc_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(apple_pmgr_misc_suspend_noirq, + apple_pmgr_misc_resume_noirq) +}; + +static struct platform_driver apple_pmgr_misc_driver = { + .probe = apple_pmgr_misc_probe, + .driver = { + .name = "apple-pmgr-misc", + .of_match_table = apple_pmgr_misc_of_match, + .pm = pm_ptr(&apple_pmgr_misc_pm_ops), + }, +}; + +MODULE_AUTHOR("Hector Martin "); +MODULE_DESCRIPTION("PMGR misc driver for Apple SoCs"); +MODULE_LICENSE("GPL v2"); + +module_platform_driver(apple_pmgr_misc_driver); From 2d253d2001775e47687c0ea0c4cdf6cb77a13709 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 17 Apr 2023 20:41:13 +0900 Subject: [PATCH 0193/1020] cpuidle: apple: Add Apple SoC cpuidle driver May the PSCI conversation happen some day. Until it does, this will make the user experience a lot less painful in downstream kernels. Signed-off-by: Hector Martin --- drivers/cpuidle/Kconfig.arm | 8 ++ drivers/cpuidle/Makefile | 1 + drivers/cpuidle/cpuidle-apple.c | 157 ++++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 drivers/cpuidle/cpuidle-apple.c diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index a1ee475d180dac..c6870f08457632 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm @@ -130,3 +130,11 @@ config ARM_QCOM_SPM_CPUIDLE The Subsystem Power Manager (SPM) controls low power modes for the CPU and L2 cores. It interface with various system drivers to put the cores in low power modes. + +config ARM_APPLE_CPUIDLE + bool "Apple SoC CPU idle driver" + depends on ARM64 + default ARCH_APPLE + select CPU_IDLE_MULTIPLE_DRIVERS + help + Select this to enable cpuidle on Apple SoCs. diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index d103342b7cfc21..972b49aec88903 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_ARM_PSCI_CPUIDLE) += cpuidle-psci.o obj-$(CONFIG_ARM_PSCI_CPUIDLE_DOMAIN) += cpuidle-psci-domain.o obj-$(CONFIG_ARM_TEGRA_CPUIDLE) += cpuidle-tegra.o obj-$(CONFIG_ARM_QCOM_SPM_CPUIDLE) += cpuidle-qcom-spm.o +obj-$(CONFIG_ARM_APPLE_CPUIDLE) += cpuidle-apple.o ############################################################################### # MIPS drivers diff --git a/drivers/cpuidle/cpuidle-apple.c b/drivers/cpuidle/cpuidle-apple.c new file mode 100644 index 00000000000000..1dfb10cdb5e4d6 --- /dev/null +++ b/drivers/cpuidle/cpuidle-apple.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Copyright The Asahi Linux Contributors + * + * CPU idle support for Apple SoCs + */ + +#include +#include +#include +#include +#include +#include + +enum idle_state { + STATE_WFI, + STATE_PWRDOWN, + STATE_COUNT +}; + +asm( + ".pushsection .cpuidle.text, \"ax\"\n" + ".type apple_cpu_deep_wfi, @function\n" + "apple_cpu_deep_wfi:\n" + "str x30, [sp, #-16]!\n" + "stp x28, x29, [sp, #-16]!\n" + "stp x26, x27, [sp, #-16]!\n" + "stp x24, x25, [sp, #-16]!\n" + "stp x22, x23, [sp, #-16]!\n" + "stp x20, x21, [sp, #-16]!\n" + "stp x18, x19, [sp, #-16]!\n" + + "mrs x0, s3_5_c15_c5_0\n" + "orr x0, x0, #(3L << 24)\n" + "msr s3_5_c15_c5_0, x0\n" + + "1:\n" + "dsb sy\n" + "wfi\n" + + "mrs x0, ISR_EL1\n" + "cbz x0, 1b\n" + + "mrs x0, s3_5_c15_c5_0\n" + "bic x0, x0, #(1L << 24)\n" + "msr s3_5_c15_c5_0, x0\n" + + "ldp x18, x19, [sp], #16\n" + "ldp x20, x21, [sp], #16\n" + "ldp x22, x23, [sp], #16\n" + "ldp x24, x25, [sp], #16\n" + "ldp x26, x27, [sp], #16\n" + "ldp x28, x29, [sp], #16\n" + "ldr x30, [sp], #16\n" + + "ret\n" + ".popsection\n" +); + +void apple_cpu_deep_wfi(void); + +static __cpuidle int apple_enter_wfi(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) +{ + cpu_do_idle(); + return index; +} + +static __cpuidle int apple_enter_idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) +{ + /* + * Deep WFI will clobber FP state, among other things. + * The CPU PM notifier will take care of saving that and anything else + * that needs to be notified of the CPU powering down. + */ + if (cpu_pm_enter()) + return -1; + + ct_cpuidle_enter(); + + switch(index) { + case STATE_PWRDOWN: + apple_cpu_deep_wfi(); + break; + default: + WARN_ON(1); + break; + } + + ct_cpuidle_exit(); + + cpu_pm_exit(); + + return index; +} + +static struct cpuidle_driver apple_idle_driver = { + .name = "apple_idle", + .owner = THIS_MODULE, + .states = { + [STATE_WFI] = { + .enter = apple_enter_wfi, + .enter_s2idle = apple_enter_wfi, + .exit_latency = 1, + .target_residency = 1, + .power_usage = UINT_MAX, + .name = "WFI", + .desc = "CPU clock-gated", + .flags = 0, + }, + [STATE_PWRDOWN] = { + .enter = apple_enter_idle, + .enter_s2idle = apple_enter_idle, + .exit_latency = 10, + .target_residency = 10000, + .power_usage = 0, + .name = "CPU PD", + .desc = "CPU/cluster powered down", + .flags = CPUIDLE_FLAG_RCU_IDLE, + }, + }, + .safe_state_index = STATE_WFI, + .state_count = STATE_COUNT, +}; + +static int apple_cpuidle_probe(struct platform_device *pdev) +{ + return cpuidle_register(&apple_idle_driver, NULL); +} + +static struct platform_driver apple_cpuidle_driver = { + .driver = { + .name = "cpuidle-apple", + }, + .probe = apple_cpuidle_probe, +}; + +static int __init apple_cpuidle_init(void) +{ + struct platform_device *pdev; + int ret; + + ret = platform_driver_register(&apple_cpuidle_driver); + if (ret) + return ret; + + if (!of_machine_is_compatible("apple,arm-platform")) + return 0; + + pdev = platform_device_register_simple("cpuidle-apple", -1, NULL, 0); + if (IS_ERR(pdev)) { + platform_driver_unregister(&apple_cpuidle_driver); + return PTR_ERR(pdev); + } + + return 0; +} +device_initcall(apple_cpuidle_init); From 86afaf81a7f236f2f8ecc7bce4da32c7f34a07ec Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 2 Jan 2023 19:20:55 +0100 Subject: [PATCH 0194/1020] soc: apple: rtkit: Add devm_apple_rtkit_free() To be used to free a RTKit interface while the associated device remains alive. Probably useless since it's unknown how or if RTKit based co-processors can be restarted. Signed-off-by: Janne Grunau --- drivers/soc/apple/rtkit.c | 6 ++++++ include/linux/soc/apple/rtkit.h | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/soc/apple/rtkit.c b/drivers/soc/apple/rtkit.c index ae1fd86a617ead..daed4b512ec480 100644 --- a/drivers/soc/apple/rtkit.c +++ b/drivers/soc/apple/rtkit.c @@ -958,6 +958,12 @@ struct apple_rtkit *devm_apple_rtkit_init(struct device *dev, void *cookie, } EXPORT_SYMBOL_GPL(devm_apple_rtkit_init); +void devm_apple_rtkit_free(struct device *dev, struct apple_rtkit *rtk) +{ + devm_release_action(dev, apple_rtkit_free_wrapper, rtk); +} +EXPORT_SYMBOL_GPL(devm_apple_rtkit_free); + MODULE_LICENSE("Dual MIT/GPL"); MODULE_AUTHOR("Sven Peter "); MODULE_DESCRIPTION("Apple RTKit driver"); diff --git a/include/linux/soc/apple/rtkit.h b/include/linux/soc/apple/rtkit.h index 8c9ca857ccf6a3..c265ea441fc5d9 100644 --- a/include/linux/soc/apple/rtkit.h +++ b/include/linux/soc/apple/rtkit.h @@ -78,6 +78,13 @@ struct apple_rtkit; struct apple_rtkit *devm_apple_rtkit_init(struct device *dev, void *cookie, const char *mbox_name, int mbox_idx, const struct apple_rtkit_ops *ops); +/* + * Frees internal RTKit state allocated by devm_apple_rtkit_init(). + * + * @dev: Pointer to the device node this coprocessor is assocated with + * @rtk: Internal RTKit state initialized by devm_apple_rtkit_init() + */ +void devm_apple_rtkit_free(struct device *dev, struct apple_rtkit *rtk); /* * Non-devm version of devm_apple_rtkit_init. Must be freed with From 5fe23876322f04db9646a8f805ebcbc0dd052163 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Fri, 29 Sep 2023 22:11:47 +0900 Subject: [PATCH 0195/1020] dt-bindings: power: apple,pmgr-pwrstate: Add force-{disable,reset} These flags are used for some ISP power domains, that apparently require more aggressive behavior on power down. Signed-off-by: Asahi Lina --- .../bindings/power/apple,pmgr-pwrstate.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Documentation/devicetree/bindings/power/apple,pmgr-pwrstate.yaml b/Documentation/devicetree/bindings/power/apple,pmgr-pwrstate.yaml index 59a6af735a2167..1531e26549f209 100644 --- a/Documentation/devicetree/bindings/power/apple,pmgr-pwrstate.yaml +++ b/Documentation/devicetree/bindings/power/apple,pmgr-pwrstate.yaml @@ -70,6 +70,18 @@ properties: minimum: 0 maximum: 15 + apple,force-disable: + description: + Forces this device to be disabled (bus access blocked) when the power + domain is powered down. + type: boolean + + apple,force-reset: + description: + Forces a reset/error recovery of the power control logic when the power + domain is powered down. + type: boolean + required: - compatible - reg From 4d367de673c0e78ead934107e3011fd4a4f7ef68 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Fri, 29 Sep 2023 22:07:30 +0900 Subject: [PATCH 0196/1020] soc: apple: pmgr: Add force-disable/force-reset It seems some ISP power states should have their force disable device access flag set when powered down (which may avoid this problem, but we're still figuring that out), and on some bit 12 is also explicitly set before shutdown. Add two properties to handle this case. Signed-off-by: Asahi Lina --- drivers/pmdomain/apple/pmgr-pwrstate.c | 43 ++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/drivers/pmdomain/apple/pmgr-pwrstate.c b/drivers/pmdomain/apple/pmgr-pwrstate.c index d62a776c89a121..367b1b243f9a69 100644 --- a/drivers/pmdomain/apple/pmgr-pwrstate.c +++ b/drivers/pmdomain/apple/pmgr-pwrstate.c @@ -21,7 +21,8 @@ #define APPLE_PMGR_AUTO_ENABLE BIT(28) #define APPLE_PMGR_PS_AUTO GENMASK(27, 24) #define APPLE_PMGR_PS_MIN GENMASK(19, 16) -#define APPLE_PMGR_PARENT_OFF BIT(11) +#define APPLE_PMGR_PS_RESET BIT(12) +#define APPLE_PMGR_BUSY BIT(11) #define APPLE_PMGR_DEV_DISABLE BIT(10) #define APPLE_PMGR_WAS_CLKGATED BIT(9) #define APPLE_PMGR_WAS_PWRGATED BIT(8) @@ -44,6 +45,8 @@ struct apple_pmgr_ps { struct regmap *regmap; u32 offset; u32 min_state; + bool force_disable; + bool force_reset; }; #define genpd_to_apple_pmgr_ps(_genpd) container_of(_genpd, struct apple_pmgr_ps, genpd) @@ -53,7 +56,7 @@ static int apple_pmgr_ps_set(struct generic_pm_domain *genpd, u32 pstate, bool a { int ret; struct apple_pmgr_ps *ps = genpd_to_apple_pmgr_ps(genpd); - u32 reg; + u32 reg, cur; ret = regmap_read(ps->regmap, ps->offset, ®); if (ret < 0) @@ -64,7 +67,29 @@ static int apple_pmgr_ps_set(struct generic_pm_domain *genpd, u32 pstate, bool a dev_err(ps->dev, "PS %s: powering off with RESET active\n", genpd->name); - reg &= ~(APPLE_PMGR_AUTO_ENABLE | APPLE_PMGR_FLAGS | APPLE_PMGR_PS_TARGET); + if (pstate != APPLE_PMGR_PS_ACTIVE && (ps->force_disable || ps->force_reset)) { + u32 reg_pre = reg & ~(APPLE_PMGR_AUTO_ENABLE | APPLE_PMGR_FLAGS); + + if (ps->force_disable) + reg_pre |= APPLE_PMGR_DEV_DISABLE; + if (ps->force_reset) + reg_pre |= APPLE_PMGR_PS_RESET; + + regmap_write(ps->regmap, ps->offset, reg_pre); + + ret = regmap_read_poll_timeout_atomic( + ps->regmap, ps->offset, cur, + (cur & (APPLE_PMGR_DEV_DISABLE | APPLE_PMGR_PS_RESET)) == + (reg_pre & (APPLE_PMGR_DEV_DISABLE | APPLE_PMGR_PS_RESET)), 1, + APPLE_PMGR_PS_SET_TIMEOUT); + + if (ret < 0) + dev_err(ps->dev, "PS %s: Failed to set reset/disable bits (now: 0x%x)\n", + genpd->name, reg); + } + + reg &= ~(APPLE_PMGR_DEV_DISABLE | APPLE_PMGR_PS_RESET | + APPLE_PMGR_AUTO_ENABLE | APPLE_PMGR_FLAGS | APPLE_PMGR_PS_TARGET); reg |= FIELD_PREP(APPLE_PMGR_PS_TARGET, pstate); dev_dbg(ps->dev, "PS %s: pwrstate = 0x%x: 0x%x\n", genpd->name, pstate, reg); @@ -72,16 +97,16 @@ static int apple_pmgr_ps_set(struct generic_pm_domain *genpd, u32 pstate, bool a regmap_write(ps->regmap, ps->offset, reg); ret = regmap_read_poll_timeout_atomic( - ps->regmap, ps->offset, reg, - (FIELD_GET(APPLE_PMGR_PS_ACTUAL, reg) == pstate), 1, + ps->regmap, ps->offset, cur, + FIELD_GET(APPLE_PMGR_PS_ACTUAL, cur) == pstate, 1, APPLE_PMGR_PS_SET_TIMEOUT); + if (ret < 0) dev_err(ps->dev, "PS %s: Failed to reach power state 0x%x (now: 0x%x)\n", genpd->name, pstate, reg); if (auto_enable) { /* Not all devices implement this; this is a no-op where not implemented. */ - reg &= ~APPLE_PMGR_FLAGS; reg |= APPLE_PMGR_AUTO_ENABLE; regmap_write(ps->regmap, ps->offset, reg); } @@ -244,6 +269,12 @@ static int apple_pmgr_ps_probe(struct platform_device *pdev) } } + if (of_property_read_bool(node, "apple,force-disable")) + ps->force_disable = true; + + if (of_property_read_bool(node, "apple,force-reset")) + ps->force_reset = true; + /* Turn on auto-PM if the domain is already on */ if (active) regmap_update_bits(regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_AUTO_ENABLE, From 43a9277e98a0498bc47e11144b017bf3aebb7d85 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 12 Oct 2023 23:18:14 +0900 Subject: [PATCH 0197/1020] soc: apple: pmgr: Add externally-clocked property MCA power states require an external clock to be provided. If they are powered on while this clock is not active, the power state will only go into the "clock gated" state. This is effectively working as intended, so add a property that instructs the pwrstate driver to consider the PS to be successfully powered on when it reaches the clock gated state. Signed-off-by: Hector Martin --- drivers/pmdomain/apple/pmgr-pwrstate.c | 35 ++++++++++++++++++-------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/drivers/pmdomain/apple/pmgr-pwrstate.c b/drivers/pmdomain/apple/pmgr-pwrstate.c index 367b1b243f9a69..a272cba58e2f1f 100644 --- a/drivers/pmdomain/apple/pmgr-pwrstate.c +++ b/drivers/pmdomain/apple/pmgr-pwrstate.c @@ -47,6 +47,7 @@ struct apple_pmgr_ps { u32 min_state; bool force_disable; bool force_reset; + bool externally_clocked; }; #define genpd_to_apple_pmgr_ps(_genpd) container_of(_genpd, struct apple_pmgr_ps, genpd) @@ -96,10 +97,21 @@ static int apple_pmgr_ps_set(struct generic_pm_domain *genpd, u32 pstate, bool a regmap_write(ps->regmap, ps->offset, reg); - ret = regmap_read_poll_timeout_atomic( - ps->regmap, ps->offset, cur, - FIELD_GET(APPLE_PMGR_PS_ACTUAL, cur) == pstate, 1, - APPLE_PMGR_PS_SET_TIMEOUT); + if (ps->externally_clocked && pstate == APPLE_PMGR_PS_ACTIVE) { + /* + * If this clock domain requires an external clock, then + * consider the "clock gated" state to be good enough. + */ + ret = regmap_read_poll_timeout_atomic( + ps->regmap, ps->offset, cur, + FIELD_GET(APPLE_PMGR_PS_ACTUAL, cur) >= APPLE_PMGR_PS_CLKGATE, 1, + APPLE_PMGR_PS_SET_TIMEOUT); + } else { + ret = regmap_read_poll_timeout_atomic( + ps->regmap, ps->offset, cur, + FIELD_GET(APPLE_PMGR_PS_ACTUAL, cur) == pstate, 1, + APPLE_PMGR_PS_SET_TIMEOUT); + } if (ret < 0) dev_err(ps->dev, "PS %s: Failed to reach power state 0x%x (now: 0x%x)\n", @@ -259,6 +271,15 @@ static int apple_pmgr_ps_probe(struct platform_device *pdev) regmap_update_bits(regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_PS_MIN, FIELD_PREP(APPLE_PMGR_PS_MIN, ps->min_state)); + if (of_property_read_bool(node, "apple,force-disable")) + ps->force_disable = true; + + if (of_property_read_bool(node, "apple,force-reset")) + ps->force_reset = true; + + if (of_property_read_bool(node, "apple,externally-clocked")) + ps->externally_clocked = true; + active = apple_pmgr_ps_is_active(ps); if (of_property_read_bool(node, "apple,always-on")) { ps->genpd.flags |= GENPD_FLAG_ALWAYS_ON; @@ -269,12 +290,6 @@ static int apple_pmgr_ps_probe(struct platform_device *pdev) } } - if (of_property_read_bool(node, "apple,force-disable")) - ps->force_disable = true; - - if (of_property_read_bool(node, "apple,force-reset")) - ps->force_reset = true; - /* Turn on auto-PM if the domain is already on */ if (active) regmap_update_bits(regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_AUTO_ENABLE, From 4578c31a674b8b5fcbdf83327a23c86f37ed2648 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 4 May 2024 21:42:57 +0200 Subject: [PATCH 0198/1020] soc: apple: rtkit: Use high prio work queue rtkit messages as communication with the DCP firmware for framebuffer swaps or input events are time critical so use WQ_HIGHPRI to prevent user space CPU load to increase latency. With kwin_wayland 6's explicit sync mode user space load was able to delay the IOMFB rtkit communication enough to miss vsync for surface swaps. Minimal test scenario is constantly resizing a glxgears Xwayland window. Signed-off-by: Janne Grunau --- drivers/soc/apple/rtkit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/apple/rtkit.c b/drivers/soc/apple/rtkit.c index daed4b512ec480..79b80e8f38c163 100644 --- a/drivers/soc/apple/rtkit.c +++ b/drivers/soc/apple/rtkit.c @@ -695,7 +695,7 @@ struct apple_rtkit *apple_rtkit_init(struct device *dev, void *cookie, rtk->mbox->rx = apple_rtkit_rx; rtk->mbox->cookie = rtk; - rtk->wq = alloc_ordered_workqueue("rtkit-%s", WQ_MEM_RECLAIM, + rtk->wq = alloc_ordered_workqueue("rtkit-%s", WQ_HIGHPRI | WQ_MEM_RECLAIM, dev_name(rtk->dev)); if (!rtk->wq) { ret = -ENOMEM; From 6e9cf523799357fdef7af0ab612ed62b3f593c59 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 22 Oct 2022 16:01:01 +0200 Subject: [PATCH 0199/1020] iommu/io-pgtable: dart: Remove unused __dart_alloc_pages() paramater The parameter became unused during refactoring but was missed. Fixes: 745ef1092bcf ("iommu/io-pgtable: Move Apple DART support to its own file") Signed-off-by: Janne Grunau --- drivers/iommu/io-pgtable-dart.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/io-pgtable-dart.c b/drivers/iommu/io-pgtable-dart.c index 74b1ef2b96bee1..a8a81b55b4d26b 100644 --- a/drivers/iommu/io-pgtable-dart.c +++ b/drivers/iommu/io-pgtable-dart.c @@ -106,8 +106,7 @@ static phys_addr_t iopte_to_paddr(dart_iopte pte, return paddr; } -static void *__dart_alloc_pages(size_t size, gfp_t gfp, - struct io_pgtable_cfg *cfg) +static void *__dart_alloc_pages(size_t size, gfp_t gfp) { int order = get_order(size); struct page *p; @@ -262,7 +261,7 @@ static int dart_map_pages(struct io_pgtable_ops *ops, unsigned long iova, /* no L2 table present */ if (!pte) { - cptep = __dart_alloc_pages(tblsz, gfp, cfg); + cptep = __dart_alloc_pages(tblsz, gfp); if (!cptep) return -ENOMEM; @@ -419,8 +418,7 @@ apple_dart_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie) cfg->apple_dart_cfg.n_ttbrs = 1 << data->tbl_bits; for (i = 0; i < cfg->apple_dart_cfg.n_ttbrs; ++i) { - data->pgd[i] = __dart_alloc_pages(DART_GRANULE(data), GFP_KERNEL, - cfg); + data->pgd[i] = __dart_alloc_pages(DART_GRANULE(data), GFP_KERNEL); if (!data->pgd[i]) goto out_free_data; cfg->apple_dart_cfg.ttbr[i] = virt_to_phys(data->pgd[i]); From 6ef426247db9716db7b17acf77e2d367cdf0619b Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 22 Oct 2022 12:00:21 +0200 Subject: [PATCH 0200/1020] iommu: Add IOMMU_RESV_TRANSLATED for non 1:1 mapped reserved regions The display controller in Apple silicon SoCs uses bootloader mappings which require IOMMU translation. Signed-off-by: Janne Grunau --- drivers/iommu/iommu.c | 24 ++++++++++++++++++++---- include/linux/iommu.h | 10 ++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index a95a483def2d2a..e81ae293b8c5cf 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -85,6 +85,7 @@ static const char * const iommu_group_resv_type_string[] = { [IOMMU_RESV_RESERVED] = "reserved", [IOMMU_RESV_MSI] = "msi", [IOMMU_RESV_SW_MSI] = "msi", + [IOMMU_RESV_TRANSLATED] = "translated", }; #define IOMMU_CMD_LINE_DMA_API BIT(0) @@ -2764,10 +2765,11 @@ void iommu_put_resv_regions(struct device *dev, struct list_head *list) } EXPORT_SYMBOL(iommu_put_resv_regions); -struct iommu_resv_region *iommu_alloc_resv_region(phys_addr_t start, - size_t length, int prot, - enum iommu_resv_type type, - gfp_t gfp) +struct iommu_resv_region *iommu_alloc_resv_region_tr(phys_addr_t start, + dma_addr_t dva_start, + size_t length, int prot, + enum iommu_resv_type type, + gfp_t gfp) { struct iommu_resv_region *region; @@ -2777,11 +2779,25 @@ struct iommu_resv_region *iommu_alloc_resv_region(phys_addr_t start, INIT_LIST_HEAD(®ion->list); region->start = start; + if (type == IOMMU_RESV_TRANSLATED) + region->dva = dva_start; region->length = length; region->prot = prot; region->type = type; return region; } +EXPORT_SYMBOL_GPL(iommu_alloc_resv_region_tr); + +struct iommu_resv_region *iommu_alloc_resv_region(phys_addr_t start, + size_t length, int prot, + enum iommu_resv_type type, + gfp_t gfp) +{ + if (type == IOMMU_RESV_TRANSLATED) + return NULL; + + return iommu_alloc_resv_region_tr(start, 0, length, prot, type, gfp); +} EXPORT_SYMBOL_GPL(iommu_alloc_resv_region); void iommu_set_default_passthrough(bool cmd_line) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 2e925b5eba534c..cd315f5c046a49 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -268,12 +268,18 @@ enum iommu_resv_type { IOMMU_RESV_MSI, /* Software-managed MSI translation window */ IOMMU_RESV_SW_MSI, + /* + * Memory regions which must be mapped with the specified mapping + * at all times. + */ + IOMMU_RESV_TRANSLATED, }; /** * struct iommu_resv_region - descriptor for a reserved memory region * @list: Linked list pointers * @start: System physical start address of the region + * @start: Device virtual start address of the region for IOMMU_RESV_TRANSLATED * @length: Length of the region in bytes * @prot: IOMMU Protection flags (READ/WRITE/...) * @type: Type of the reserved region @@ -282,6 +288,7 @@ enum iommu_resv_type { struct iommu_resv_region { struct list_head list; phys_addr_t start; + dma_addr_t dva; size_t length; int prot; enum iommu_resv_type type; @@ -808,6 +815,9 @@ extern bool iommu_default_passthrough(void); extern struct iommu_resv_region * iommu_alloc_resv_region(phys_addr_t start, size_t length, int prot, enum iommu_resv_type type, gfp_t gfp); +extern struct iommu_resv_region * +iommu_alloc_resv_region_tr(phys_addr_t start, dma_addr_t dva_start, size_t length, + int prot, enum iommu_resv_type type, gfp_t gfp); extern int iommu_get_group_resv_regions(struct iommu_group *group, struct list_head *head); From f8dfce88ef6b38d6e062fd04670ddcce4f0c340c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 22 Oct 2022 12:24:54 +0200 Subject: [PATCH 0201/1020] iommu: Parse translated reserved regions These regions are setup by the boot loader and require an iommu to translate arbitray physical to device VA mappings. Signed-off-by: Janne Grunau --- drivers/iommu/dma-iommu.c | 9 +++++++-- drivers/iommu/of_iommu.c | 11 +++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index e4cb26f6a9434e..d9fb9b565e87d5 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -581,8 +581,13 @@ static int iova_reserve_iommu_regions(struct device *dev, if (region->type == IOMMU_RESV_SW_MSI) continue; - lo = iova_pfn(iovad, region->start); - hi = iova_pfn(iovad, region->start + region->length - 1); + if (region->type == IOMMU_RESV_TRANSLATED) { + lo = iova_pfn(iovad, region->dva); + hi = iova_pfn(iovad, region->dva + region->length - 1); + } else { + lo = iova_pfn(iovad, region->start); + hi = iova_pfn(iovad, region->start + region->length - 1); + } reserve_iova(iovad, lo, hi); if (region->type == IOMMU_RESV_MSI) diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index 3afe0b48a48db9..73601b35b7f7d3 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -185,9 +185,7 @@ iommu_resv_region_get_type(struct device *dev, if (start == phys->start && end == phys->end) return IOMMU_RESV_DIRECT; - dev_warn(dev, "treating non-direct mapping [%pr] -> [%pap-%pap] as reservation\n", phys, - &start, &end); - return IOMMU_RESV_RESERVED; + return IOMMU_RESV_TRANSLATED; } /** @@ -258,8 +256,13 @@ void of_iommu_get_resv_regions(struct device *dev, struct list_head *list) } type = iommu_resv_region_get_type(dev, &phys, iova, length); - region = iommu_alloc_resv_region(iova, length, prot, type, + if (type == IOMMU_RESV_TRANSLATED) + region = iommu_alloc_resv_region_tr(phys.start, iova, length, prot, type, + GFP_KERNEL); + else + region = iommu_alloc_resv_region(iova, length, prot, type, GFP_KERNEL); + if (region) list_add_tail(®ion->list, list); } From 113e9f29d11ba164b6311b302898d41af860dd00 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Thu, 26 Aug 2021 12:16:52 -0400 Subject: [PATCH 0202/1020] iommu/dart: Track if the DART is locked Locked DARTs require special handling. This can be detected with the configuration register. Check this when probing and save the result. Signed-off-by: Alyssa Rosenzweig --- drivers/iommu/apple-dart.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index eb1e62cd499a58..265a3d4328a58f 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -197,6 +197,7 @@ struct apple_dart_hw { * @lock: lock for hardware operations involving this dart * @pgsize: pagesize supported by this DART * @supports_bypass: indicates if this DART supports bypass mode + * @locked: indicates if this DART is locked * @sid2group: maps stream ids to iommu_groups * @iommu: iommu core device */ @@ -217,6 +218,7 @@ struct apple_dart { u32 pgsize; u32 num_streams; u32 supports_bypass : 1; + u32 locked : 1; struct iommu_group *sid2group[DART_MAX_STREAMS]; struct iommu_device iommu; @@ -1076,6 +1078,11 @@ static irqreturn_t apple_dart_t8110_irq(int irq, void *dev) return IRQ_HANDLED; } +static bool apple_dart_is_locked(struct apple_dart *dart) +{ + return !!(readl(dart->regs + dart->hw->lock) & dart->hw->lock_bit); +} + static int apple_dart_probe(struct platform_device *pdev) { int ret; @@ -1143,6 +1150,7 @@ static int apple_dart_probe(struct platform_device *pdev) goto err_clk_disable; } + dart->locked = apple_dart_is_locked(dart); ret = apple_dart_hw_reset(dart); if (ret) goto err_clk_disable; @@ -1165,9 +1173,9 @@ static int apple_dart_probe(struct platform_device *pdev) dev_info( &pdev->dev, - "DART [pagesize %x, %d streams, bypass support: %d, bypass forced: %d] initialized\n", + "DART [pagesize %x, %d streams, bypass support: %d, bypass forced: %d, locked: %d] initialized\n", dart->pgsize, dart->num_streams, dart->supports_bypass, - dart->pgsize > PAGE_SIZE); + dart->pgsize > PAGE_SIZE, dart->locked); return 0; err_sysfs_remove: From 2b59c6a552f2f8984739cb90d7d098297dfac9da Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Thu, 26 Aug 2021 12:18:28 -0400 Subject: [PATCH 0203/1020] iommu/dart: Allow locked DARTs to probe Instead of bailing from reset() if the DART is locked, simply skip the reset for locked DARTs. Signed-off-by: Alyssa Rosenzweig --- drivers/iommu/apple-dart.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index 265a3d4328a58f..40206e54ccd75d 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -452,17 +452,9 @@ apple_dart_t8110_hw_invalidate_tlb(struct apple_dart_stream_map *stream_map) static int apple_dart_hw_reset(struct apple_dart *dart) { - u32 config; struct apple_dart_stream_map stream_map; int i; - config = readl(dart->regs + dart->hw->lock); - if (config & dart->hw->lock_bit) { - dev_err(dart->dev, "DART is locked down until reboot: %08x\n", - config); - return -EINVAL; - } - stream_map.dart = dart; bitmap_zero(stream_map.sidmap, DART_MAX_STREAMS); bitmap_set(stream_map.sidmap, 0, dart->num_streams); @@ -1151,9 +1143,11 @@ static int apple_dart_probe(struct platform_device *pdev) } dart->locked = apple_dart_is_locked(dart); - ret = apple_dart_hw_reset(dart); - if (ret) - goto err_clk_disable; + if (!dart->locked) { + ret = apple_dart_hw_reset(dart); + if (ret) + goto err_clk_disable; + } ret = request_irq(dart->irq, dart->hw->irq_handler, IRQF_SHARED, "apple-dart fault handler", dart); From b9b52002ff94c7086dfcd03c34637ed60add8baf Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 17 Nov 2022 21:31:23 +0900 Subject: [PATCH 0204/1020] iommu: apple-dart: Don't attempt to reset/restore locked DARTs This can't work, and should not be needed in these cases. Signed-off-by: Hector Martin --- drivers/iommu/apple-dart.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index 40206e54ccd75d..2a11d84ae92316 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -1186,7 +1186,9 @@ static void apple_dart_remove(struct platform_device *pdev) { struct apple_dart *dart = platform_get_drvdata(pdev); - apple_dart_hw_reset(dart); + if (!dart->locked) + apple_dart_hw_reset(dart); + free_irq(dart->irq, dart); iommu_device_unregister(&dart->iommu); @@ -1319,6 +1321,10 @@ static __maybe_unused int apple_dart_resume(struct device *dev) unsigned int sid, idx; int ret; + /* Locked DARTs can't be restored, and they should not need it */ + if (dart->locked) + return 0; + ret = apple_dart_hw_reset(dart); if (ret) { dev_err(dev, "Failed to reset DART on resume\n"); From 06432a5f62bfd4892ab4402cf6953b42443b0241 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Thu, 26 Aug 2021 12:48:59 -0400 Subject: [PATCH 0205/1020] iommu/dart: Set DMA domain for locked DARTs This is required. Signed-off-by: Alyssa Rosenzweig --- drivers/iommu/apple-dart.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index 2a11d84ae92316..fcbfe0ea077afc 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -941,6 +941,8 @@ static int apple_dart_def_domain_type(struct device *dev) return IOMMU_DOMAIN_IDENTITY; if (!cfg->stream_maps[0].dart->supports_bypass) return IOMMU_DOMAIN_DMA; + if (cfg->stream_maps[0].dart->locked) + return IOMMU_DOMAIN_DMA; return 0; } From 6a8518fc7c8c10d0645e789cb50bf10c720493ca Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Thu, 26 Aug 2021 12:50:07 -0400 Subject: [PATCH 0206/1020] iommu/dart: Reject identity domain for locked DARTs Signed-off-by: Alyssa Rosenzweig Signed-off-by: Janbne Grunau --- drivers/iommu/apple-dart.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index fcbfe0ea077afc..21e1620ca40b2d 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -681,6 +681,9 @@ static int apple_dart_attach_dev_identity(struct iommu_domain *domain, if (!cfg->stream_maps[0].dart->supports_bypass) return -EINVAL; + if (cfg->stream_maps[0].dart->locked) + return -EINVAL; + for_each_stream_map(i, cfg, stream_map) apple_dart_hw_enable_bypass(stream_map); return 0; From 0e1ac0b86e3ef647fea55669425353eaa071e5ce Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Thu, 26 Aug 2021 13:19:51 -0400 Subject: [PATCH 0207/1020] iommu/dart: Assert !locked when reconfiguring Signed-off-by: Alyssa Rosenzweig --- drivers/iommu/apple-dart.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index 21e1620ca40b2d..a856fb2b1637b6 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -309,6 +309,7 @@ apple_dart_hw_enable_translation(struct apple_dart_stream_map *stream_map) struct apple_dart *dart = stream_map->dart; int sid; + WARN_ON(stream_map->dart->locked); for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) writel(dart->hw->tcr_enabled, dart->regs + DART_TCR(dart, sid)); } @@ -318,6 +319,7 @@ static void apple_dart_hw_disable_dma(struct apple_dart_stream_map *stream_map) struct apple_dart *dart = stream_map->dart; int sid; + WARN_ON(stream_map->dart->locked); for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) writel(dart->hw->tcr_disabled, dart->regs + DART_TCR(dart, sid)); } @@ -328,6 +330,7 @@ apple_dart_hw_enable_bypass(struct apple_dart_stream_map *stream_map) struct apple_dart *dart = stream_map->dart; int sid; + WARN_ON(stream_map->dart->locked); WARN_ON(!stream_map->dart->supports_bypass); for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) writel(dart->hw->tcr_bypass, @@ -340,6 +343,7 @@ static void apple_dart_hw_set_ttbr(struct apple_dart_stream_map *stream_map, struct apple_dart *dart = stream_map->dart; int sid; + WARN_ON(stream_map->dart->locked); WARN_ON(paddr & ((1 << dart->hw->ttbr_shift) - 1)); for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) writel(dart->hw->ttbr_valid | @@ -353,6 +357,7 @@ static void apple_dart_hw_clear_ttbr(struct apple_dart_stream_map *stream_map, struct apple_dart *dart = stream_map->dart; int sid; + WARN_ON(stream_map->dart->locked); for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) writel(0, dart->regs + DART_TTBR(dart, sid, idx)); } From 003e5116a2c0c80a70a22bf73fb70fceec48bccf Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 20 Nov 2022 14:23:57 +0100 Subject: [PATCH 0208/1020] iommu: apple-dart: Install IOMMU_RESV_TRANSLATED mappings The iommus for the display processors on Apple silicon machines have locked TTBR registers. To support iommu domain switching use a shadow L1 page table and sync it on flush to the HW L1 table. TODO: investigate if it's possible / necessary to optimize the syncing Signed-off-by: Janne Grunau --- drivers/iommu/Kconfig | 1 + drivers/iommu/apple-dart.c | 175 +++++++++++++++++++++++++++++++++++-- 2 files changed, 168 insertions(+), 8 deletions(-) diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index 0af39bbbe3a30e..8fc6075d3b623f 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -305,6 +305,7 @@ config APPLE_DART depends on !GENERIC_ATOMIC64 # for IOMMU_IO_PGTABLE_DART select IOMMU_API select IOMMU_IO_PGTABLE_DART + select OF_IOMMU default ARCH_APPLE help Support for Apple DART (Device Address Resolution Table) IOMMUs diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index a856fb2b1637b6..010b6fe5257543 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -225,6 +225,9 @@ struct apple_dart { u32 save_tcr[DART_MAX_STREAMS]; u32 save_ttbr[DART_MAX_STREAMS][DART_MAX_TTBR]; + + u64 *locked_ttbr[DART_MAX_STREAMS][DART_MAX_TTBR]; + u64 *shadow_ttbr[DART_MAX_STREAMS][DART_MAX_TTBR]; }; /* @@ -371,6 +374,89 @@ apple_dart_hw_clear_all_ttbrs(struct apple_dart_stream_map *stream_map) apple_dart_hw_clear_ttbr(stream_map, i); } +static int +apple_dart_hw_set_locked_ttbr(struct apple_dart_stream_map *stream_map, u8 idx, + phys_addr_t paddr) +{ + struct apple_dart *dart = stream_map->dart; + int sid; + + WARN_ON(!dart->locked); + WARN_ON(paddr & ((1 << dart->hw->ttbr_shift) - 1)); + for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) { + u32 ttbr; + phys_addr_t phys; + u64 *l1_tbl, *l1_shadow; + + ttbr = readl(dart->regs + DART_TTBR(dart, sid, idx)); + + WARN_ON(!(ttbr & dart->hw->ttbr_valid)); + ttbr &= ~dart->hw->ttbr_valid; + + if (dart->hw->ttbr_addr_field_shift) + ttbr >>= dart->hw->ttbr_addr_field_shift; + phys = ((phys_addr_t) ttbr) << dart->hw->ttbr_shift; + + l1_tbl = devm_memremap(dart->dev, phys, dart->pgsize, + MEMREMAP_WB); + if (!l1_tbl) + return -ENOMEM; + l1_shadow = devm_memremap(dart->dev, paddr, dart->pgsize, + MEMREMAP_WB); + if (!l1_shadow) + return -ENOMEM; + + dart->locked_ttbr[sid][idx] = l1_tbl; + dart->shadow_ttbr[sid][idx] = l1_shadow; + } + + return 0; +} + +static int +apple_dart_hw_clear_locked_ttbr(struct apple_dart_stream_map *stream_map, + u8 idx) +{ + struct apple_dart *dart = stream_map->dart; + int sid; + + WARN_ON(!dart->locked); + for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) { + /* TODO: locked L1 table might need to be restored to boot state */ + if (dart->locked_ttbr[sid][idx]) { + memset(dart->locked_ttbr[sid][idx], 0, dart->pgsize); + devm_memunmap(dart->dev, dart->locked_ttbr[sid][idx]); + } + dart->locked_ttbr[sid][idx] = NULL; + if (dart->shadow_ttbr[sid][idx]) + devm_memunmap(dart->dev, dart->shadow_ttbr[sid][idx]); + dart->shadow_ttbr[sid][idx] = NULL; + } + + return 0; +} + +static int +apple_dart_hw_sync_locked(struct apple_dart_stream_map *stream_map) +{ + struct apple_dart *dart = stream_map->dart; + int sid; + + WARN_ON(!dart->locked); + for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) { + for (int idx = 0; idx < dart->hw->ttbr_count; idx++) { + u64 *ttbrep = dart->locked_ttbr[sid][idx]; + u64 *ptep = dart->shadow_ttbr[sid][idx]; + if (!ttbrep || !ptep) + continue; + for (int entry = 0; entry < dart->pgsize / sizeof(*ptep); entry++) + ttbrep[entry] = ptep[entry]; + } + } + + return 0; +} + static int apple_dart_t8020_hw_stream_command(struct apple_dart_stream_map *stream_map, u32 command) @@ -491,6 +577,10 @@ static void apple_dart_domain_flush_tlb(struct apple_dart_domain *domain) for (j = 0; j < BITS_TO_LONGS(stream_map.dart->num_streams); j++) stream_map.sidmap[j] = atomic_long_read(&domain_stream_map->sidmap[j]); + + if (stream_map.dart->locked) + apple_dart_hw_sync_locked(&stream_map); + stream_map.dart->hw->invalidate_tlb(&stream_map); } } @@ -559,17 +649,62 @@ apple_dart_setup_translation(struct apple_dart_domain *domain, struct io_pgtable_cfg *pgtbl_cfg = &io_pgtable_ops_to_pgtable(domain->pgtbl_ops)->cfg; - for (i = 0; i < pgtbl_cfg->apple_dart_cfg.n_ttbrs; ++i) - apple_dart_hw_set_ttbr(stream_map, i, - pgtbl_cfg->apple_dart_cfg.ttbr[i]); - for (; i < stream_map->dart->hw->ttbr_count; ++i) - apple_dart_hw_clear_ttbr(stream_map, i); + /* Locked DARTs are set up by the bootloader. */ + if (stream_map->dart->locked) { + for (i = 0; i < pgtbl_cfg->apple_dart_cfg.n_ttbrs; ++i) + apple_dart_hw_set_locked_ttbr(stream_map, i, + pgtbl_cfg->apple_dart_cfg.ttbr[i]); + for (; i < stream_map->dart->hw->ttbr_count; ++i) + apple_dart_hw_clear_locked_ttbr(stream_map, i); + apple_dart_hw_sync_locked(stream_map); + } else { + for (i = 0; i < pgtbl_cfg->apple_dart_cfg.n_ttbrs; ++i) + apple_dart_hw_set_ttbr(stream_map, i, + pgtbl_cfg->apple_dart_cfg.ttbr[i]); + for (; i < stream_map->dart->hw->ttbr_count; ++i) + apple_dart_hw_clear_ttbr(stream_map, i); - apple_dart_hw_enable_translation(stream_map); + apple_dart_hw_enable_translation(stream_map); + } stream_map->dart->hw->invalidate_tlb(stream_map); } +static int apple_dart_setup_resv_locked(struct iommu_domain *domain, + struct device *dev, size_t pgsize) +{ + struct iommu_resv_region *region; + LIST_HEAD(resv_regions); + int ret = 0; + + of_iommu_get_resv_regions(dev, &resv_regions); + list_for_each_entry(region, &resv_regions, list) { + size_t mapped = 0; + + /* Only map translated reserved regions */ + if (region->type != IOMMU_RESV_TRANSLATED) + continue; + + while (mapped < region->length) { + phys_addr_t paddr = region->start + mapped; + unsigned long iova = region->dva + mapped; + size_t length = region->length - mapped; + size_t pgcount = length / pgsize; + + ret = apple_dart_map_pages(domain, iova, + paddr, pgsize, pgcount, + region->prot, GFP_KERNEL, &mapped); + + if (ret) + goto end_put; + } + } +end_put: + iommu_put_resv_regions(dev, &resv_regions); + return ret; +} + static int apple_dart_finalize_domain(struct apple_dart_domain *dart_domain, + struct device *dev, struct apple_dart_master_cfg *cfg) { struct apple_dart *dart = cfg->stream_maps[0].dart; @@ -600,6 +735,21 @@ static int apple_dart_finalize_domain(struct apple_dart_domain *dart_domain, .iommu_dev = dart->dev, }; + if (dart->locked) { + unsigned long *sidmap; + int sid; + u32 ttbr; + + /* Locked DARTs can only have a single stream bound */ + sidmap = cfg->stream_maps[0].sidmap; + sid = find_first_bit(sidmap, dart->num_streams); + + WARN_ON((sid < 0) || bitmap_weight(sidmap, dart->num_streams) > 1); + ttbr = readl(dart->regs + DART_TTBR(dart, sid, 0)); + + WARN_ON(!(ttbr & dart->hw->ttbr_valid)); + } + dart_domain->pgtbl_ops = alloc_io_pgtable_ops(dart->hw->fmt, &pgtbl_cfg, &dart_domain->domain); if (!dart_domain->pgtbl_ops) { @@ -615,6 +765,7 @@ static int apple_dart_finalize_domain(struct apple_dart_domain *dart_domain, dart_domain->finalized = true; + ret = apple_dart_setup_resv_locked(&dart_domain->domain, dev, dart->pgsize); done: mutex_unlock(&dart_domain->init_lock); return ret; @@ -663,7 +814,7 @@ static int apple_dart_attach_dev_paging(struct iommu_domain *domain, struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev); struct apple_dart_domain *dart_domain = to_dart_domain(domain); - ret = apple_dart_finalize_domain(dart_domain, cfg); + ret = apple_dart_finalize_domain(dart_domain, dev, cfg); if (ret) return ret; @@ -743,8 +894,16 @@ static struct iommu_device *apple_dart_probe_device(struct device *dev) static void apple_dart_release_device(struct device *dev) { + int i, j; + struct apple_dart_stream_map *stream_map; struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev); + for_each_stream_map(j, cfg, stream_map) { + if (stream_map->dart->locked) + for (i = 0; i < stream_map->dart->hw->ttbr_count; ++i) + apple_dart_hw_clear_locked_ttbr(stream_map, i); + } + kfree(cfg); } @@ -762,7 +921,7 @@ static struct iommu_domain *apple_dart_domain_alloc_paging(struct device *dev) struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev); int ret; - ret = apple_dart_finalize_domain(dart_domain, cfg); + ret = apple_dart_finalize_domain(dart_domain, dev, cfg); if (ret) { kfree(dart_domain); return ERR_PTR(ret); From 5023c1a562bf4425b488ed57fbf3364d47303764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 28 Apr 2023 19:10:56 +0200 Subject: [PATCH 0209/1020] iommu: apple-dart: Link to consumers with blanket RPM_ACTIVE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without the RPM_ACTIVE flag, runtime PM core only seems to consider the link insofar as it prevents the DART from suspending in case of consumers *considered active by runtime PM*. Other devices, like those on which runtime PM has yet to be enabled, or which lack any runtime PM support, are not considered in preventing the DART from suspending. DART going through suspend/resume cycle with active consumers can break the consumers' operation by the DART being reset in its resume path, among other things. Add RPM_ACTIVE flag to the link to have the consumer in the link prevent the DART from being suspended, unless the consumer itself is runtime PM suspended. This supersedes an earlier PCIe-only workaround. (TODO: Does this mean devices without bound drivers will keep their DARTs up indefinitely? This depends on the timing of the iommu probe_device/release_device calls. Investigate.) Signed-off-by: Martin Povišer --- drivers/iommu/apple-dart.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index 010b6fe5257543..03c42a49b91826 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -885,9 +885,9 @@ static struct iommu_device *apple_dart_probe_device(struct device *dev) return ERR_PTR(-ENODEV); for_each_stream_map(i, cfg, stream_map) - device_link_add( - dev, stream_map->dart->dev, - DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER); + device_link_add(dev, stream_map->dart->dev, + DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER | + DL_FLAG_RPM_ACTIVE); return &cfg->stream_maps[0].dart->iommu; } From 630e03c8fc7c8c3a6dff5987d12ff900a8479c46 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 12 Dec 2022 23:53:23 +0900 Subject: [PATCH 0210/1020] iommu: apple-dart: Enable runtime PM Signed-off-by: Hector Martin --- drivers/iommu/apple-dart.c | 42 +++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index 03c42a49b91826..62b011874d30e5 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -577,11 +578,13 @@ static void apple_dart_domain_flush_tlb(struct apple_dart_domain *domain) for (j = 0; j < BITS_TO_LONGS(stream_map.dart->num_streams); j++) stream_map.sidmap[j] = atomic_long_read(&domain_stream_map->sidmap[j]); + WARN_ON(pm_runtime_get_sync(stream_map.dart->dev) < 0); if (stream_map.dart->locked) apple_dart_hw_sync_locked(&stream_map); stream_map.dart->hw->invalidate_tlb(&stream_map); + pm_runtime_put(stream_map.dart->dev); } } @@ -814,17 +817,23 @@ static int apple_dart_attach_dev_paging(struct iommu_domain *domain, struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev); struct apple_dart_domain *dart_domain = to_dart_domain(domain); + for_each_stream_map(i, cfg, stream_map) + WARN_ON(pm_runtime_get_sync(stream_map->dart->dev) < 0); + ret = apple_dart_finalize_domain(dart_domain, dev, cfg); if (ret) - return ret; + goto err; ret = apple_dart_domain_add_streams(dart_domain, cfg); if (ret) - return ret; + goto err; for_each_stream_map(i, cfg, stream_map) apple_dart_setup_translation(dart_domain, stream_map); - return 0; +err: + for_each_stream_map(i, cfg, stream_map) + pm_runtime_put(stream_map->dart->dev); + return ret; } static int apple_dart_attach_dev_identity(struct iommu_domain *domain, @@ -840,8 +849,14 @@ static int apple_dart_attach_dev_identity(struct iommu_domain *domain, if (cfg->stream_maps[0].dart->locked) return -EINVAL; + for_each_stream_map(i, cfg, stream_map) + WARN_ON(pm_runtime_get_sync(stream_map->dart->dev) < 0); + for_each_stream_map(i, cfg, stream_map) apple_dart_hw_enable_bypass(stream_map); + + for_each_stream_map(i, cfg, stream_map) + pm_runtime_put(stream_map->dart->dev); return 0; } @@ -861,8 +876,14 @@ static int apple_dart_attach_dev_blocked(struct iommu_domain *domain, struct apple_dart_stream_map *stream_map; int i; + for_each_stream_map(i, cfg, stream_map) + WARN_ON(pm_runtime_get_sync(stream_map->dart->dev) < 0); + for_each_stream_map(i, cfg, stream_map) apple_dart_hw_disable_dma(stream_map); + + for_each_stream_map(i, cfg, stream_map) + pm_runtime_put(stream_map->dart->dev); return 0; } @@ -1282,6 +1303,14 @@ static int apple_dart_probe(struct platform_device *pdev) if (ret) return ret; + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_irq_safe(dev); + + ret = devm_pm_runtime_enable(dev); + if (ret) + goto err_clk_disable; + dart_params[0] = readl(dart->regs + DART_PARAMS1); dart_params[1] = readl(dart->regs + DART_PARAMS2); dart->pgsize = 1 << FIELD_GET(DART_PARAMS1_PAGE_SHIFT, dart_params[0]); @@ -1334,6 +1363,8 @@ static int apple_dart_probe(struct platform_device *pdev) if (ret) goto err_sysfs_remove; + pm_runtime_put(dev); + dev_info( &pdev->dev, "DART [pagesize %x, %d streams, bypass support: %d, bypass forced: %d, locked: %d] initialized\n", @@ -1346,6 +1377,7 @@ static int apple_dart_probe(struct platform_device *pdev) err_free_irq: free_irq(dart->irq, dart); err_clk_disable: + pm_runtime_put(dev); clk_bulk_disable_unprepare(dart->num_clks, dart->clks); return ret; @@ -1510,7 +1542,7 @@ static __maybe_unused int apple_dart_resume(struct device *dev) return 0; } -static DEFINE_SIMPLE_DEV_PM_OPS(apple_dart_pm_ops, apple_dart_suspend, apple_dart_resume); +static DEFINE_RUNTIME_DEV_PM_OPS(apple_dart_pm_ops, apple_dart_suspend, apple_dart_resume, NULL); static const struct of_device_id apple_dart_of_match[] = { { .compatible = "apple,t8103-dart", .data = &apple_dart_hw_t8103 }, @@ -1526,7 +1558,7 @@ static struct platform_driver apple_dart_driver = { .name = "apple-dart", .of_match_table = apple_dart_of_match, .suppress_bind_attrs = true, - .pm = pm_sleep_ptr(&apple_dart_pm_ops), + .pm = pm_ptr(&apple_dart_pm_ops), }, .probe = apple_dart_probe, .remove_new = apple_dart_remove, From 338591d780f67058fc9f265c31556c059319fab0 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 10 Apr 2023 20:02:26 +0900 Subject: [PATCH 0211/1020] iommu: io-pgtable: Add 4-level page table support DARTs on t602x SoCs are of the t8110 variant but have an IAS of 42, which means optional support for an extra page table level. Refactor the PTE management to support an arbitrary level count, and then calculate how many levels we need for any given configuration. Signed-off-by: Hector Martin --- drivers/iommu/io-pgtable-dart.c | 148 +++++++++++++++++++------------- include/linux/io-pgtable.h | 1 + 2 files changed, 90 insertions(+), 59 deletions(-) diff --git a/drivers/iommu/io-pgtable-dart.c b/drivers/iommu/io-pgtable-dart.c index a8a81b55b4d26b..8998705e4c28f0 100644 --- a/drivers/iommu/io-pgtable-dart.c +++ b/drivers/iommu/io-pgtable-dart.c @@ -26,8 +26,9 @@ #define DART1_MAX_ADDR_BITS 36 -#define DART_MAX_TABLES 4 -#define DART_LEVELS 2 +#define DART_MAX_TABLE_BITS 2 +#define DART_MAX_TABLES BIT(DART_MAX_TABLE_BITS) +#define DART_MAX_LEVELS 4 /* Includes TTBR level */ /* Struct accessors */ #define io_pgtable_to_data(x) \ @@ -67,6 +68,7 @@ struct dart_io_pgtable { struct io_pgtable iop; + int levels; int tbl_bits; int bits_per_level; @@ -169,44 +171,45 @@ static dart_iopte dart_install_table(dart_iopte *table, return old; } -static int dart_get_table(struct dart_io_pgtable *data, unsigned long iova) +static int dart_get_index(struct dart_io_pgtable *data, unsigned long iova, int level) { - return (iova >> (3 * data->bits_per_level + ilog2(sizeof(dart_iopte)))) & - ((1 << data->tbl_bits) - 1); + return (iova >> (level * data->bits_per_level + ilog2(sizeof(dart_iopte)))) & + ((1 << data->bits_per_level) - 1); } -static int dart_get_l1_index(struct dart_io_pgtable *data, unsigned long iova) -{ - - return (iova >> (2 * data->bits_per_level + ilog2(sizeof(dart_iopte)))) & - ((1 << data->bits_per_level) - 1); -} - -static int dart_get_l2_index(struct dart_io_pgtable *data, unsigned long iova) +static int dart_get_last_index(struct dart_io_pgtable *data, unsigned long iova) { return (iova >> (data->bits_per_level + ilog2(sizeof(dart_iopte)))) & ((1 << data->bits_per_level) - 1); } -static dart_iopte *dart_get_l2(struct dart_io_pgtable *data, unsigned long iova) +static dart_iopte *dart_get_last(struct dart_io_pgtable *data, unsigned long iova) { dart_iopte pte, *ptep; - int tbl = dart_get_table(data, iova); + int level = data->levels; + int tbl = dart_get_index(data, iova, level); + + if (tbl > (1 << data->tbl_bits)) + return NULL; ptep = data->pgd[tbl]; if (!ptep) return NULL; - ptep += dart_get_l1_index(data, iova); - pte = READ_ONCE(*ptep); + while (--level > 1) { + ptep += dart_get_index(data, iova, level); + pte = READ_ONCE(*ptep); - /* Valid entry? */ - if (!pte) - return NULL; + /* Valid entry? */ + if (!pte) + return NULL; + + /* Deref to get next level table */ + ptep = iopte_deref(pte, data); + } - /* Deref to get level 2 table */ - return iopte_deref(pte, data); + return ptep; } static dart_iopte dart_prot_to_pte(struct dart_io_pgtable *data, @@ -242,6 +245,7 @@ static int dart_map_pages(struct io_pgtable_ops *ops, unsigned long iova, int ret = 0, tbl, num_entries, max_entries, map_idx_start; dart_iopte pte, *cptep, *ptep; dart_iopte prot; + int level = data->levels; if (WARN_ON(pgsize != cfg->pgsize_bitmap)) return -EINVAL; @@ -253,31 +257,36 @@ static int dart_map_pages(struct io_pgtable_ops *ops, unsigned long iova, if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE))) return 0; - tbl = dart_get_table(data, iova); + tbl = dart_get_index(data, iova, level); + + if (tbl > (1 << data->tbl_bits)) + return -ENOMEM; ptep = data->pgd[tbl]; - ptep += dart_get_l1_index(data, iova); - pte = READ_ONCE(*ptep); + while (--level > 1) { + ptep += dart_get_index(data, iova, level); + pte = READ_ONCE(*ptep); - /* no L2 table present */ - if (!pte) { - cptep = __dart_alloc_pages(tblsz, gfp); - if (!cptep) - return -ENOMEM; + /* no table present */ + if (!pte) { + cptep = __dart_alloc_pages(tblsz, gfp); + if (!cptep) + return -ENOMEM; - pte = dart_install_table(cptep, ptep, 0, data); - if (pte) - free_pages((unsigned long)cptep, get_order(tblsz)); + pte = dart_install_table(cptep, ptep, 0, data); + if (pte) + free_pages((unsigned long)cptep, get_order(tblsz)); - /* L2 table is present (now) */ - pte = READ_ONCE(*ptep); - } + /* L2 table is present (now) */ + pte = READ_ONCE(*ptep); + } - ptep = iopte_deref(pte, data); + ptep = iopte_deref(pte, data); + } /* install a leaf entries into L2 table */ prot = dart_prot_to_pte(data, iommu_prot); - map_idx_start = dart_get_l2_index(data, iova); + map_idx_start = dart_get_last_index(data, iova); max_entries = DART_PTES_PER_TABLE(data) - map_idx_start; num_entries = min_t(int, pgcount, max_entries); ptep += map_idx_start; @@ -306,13 +315,13 @@ static size_t dart_unmap_pages(struct io_pgtable_ops *ops, unsigned long iova, if (WARN_ON(pgsize != cfg->pgsize_bitmap || !pgcount)) return 0; - ptep = dart_get_l2(data, iova); + ptep = dart_get_last(data, iova); /* Valid L2 IOPTE pointer? */ if (WARN_ON(!ptep)) return 0; - unmap_idx_start = dart_get_l2_index(data, iova); + unmap_idx_start = dart_get_last_index(data, iova); ptep += unmap_idx_start; max_entries = DART_PTES_PER_TABLE(data) - unmap_idx_start; @@ -343,13 +352,13 @@ static phys_addr_t dart_iova_to_phys(struct io_pgtable_ops *ops, struct dart_io_pgtable *data = io_pgtable_ops_to_data(ops); dart_iopte pte, *ptep; - ptep = dart_get_l2(data, iova); + ptep = dart_get_last(data, iova); /* Valid L2 IOPTE pointer? */ if (!ptep) return 0; - ptep += dart_get_l2_index(data, iova); + ptep += dart_get_last_index(data, iova); pte = READ_ONCE(*ptep); /* Found translation */ @@ -366,21 +375,37 @@ static struct dart_io_pgtable * dart_alloc_pgtable(struct io_pgtable_cfg *cfg) { struct dart_io_pgtable *data; - int tbl_bits, bits_per_level, va_bits, pg_shift; + int levels, max_tbl_bits, tbl_bits, bits_per_level, va_bits, pg_shift; + + /* + * Old 4K page DARTs can use up to 4 top-level tables. + * Newer ones only ever use a maximum of 1. + */ + if (cfg->pgsize_bitmap == SZ_4K) + max_tbl_bits = DART_MAX_TABLE_BITS; + else + max_tbl_bits = 0; pg_shift = __ffs(cfg->pgsize_bitmap); bits_per_level = pg_shift - ilog2(sizeof(dart_iopte)); va_bits = cfg->ias - pg_shift; - tbl_bits = max_t(int, 0, va_bits - (bits_per_level * DART_LEVELS)); - if ((1 << tbl_bits) > DART_MAX_TABLES) + levels = max_t(int, 2, (va_bits - max_tbl_bits + bits_per_level - 1) / bits_per_level); + + if (levels > (DART_MAX_LEVELS - 1)) + return NULL; + + tbl_bits = max_t(int, 0, va_bits - (bits_per_level * levels)); + + if (tbl_bits > max_tbl_bits) return NULL; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return NULL; + data->levels = levels + 1; /* Table level counts as one level */ data->tbl_bits = tbl_bits; data->bits_per_level = bits_per_level; @@ -416,6 +441,7 @@ apple_dart_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie) return NULL; cfg->apple_dart_cfg.n_ttbrs = 1 << data->tbl_bits; + cfg->apple_dart_cfg.n_levels = data->levels; for (i = 0; i < cfg->apple_dart_cfg.n_ttbrs; ++i) { data->pgd[i] = __dart_alloc_pages(DART_GRANULE(data), GFP_KERNEL); @@ -434,28 +460,32 @@ apple_dart_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie) return NULL; } -static void apple_dart_free_pgtable(struct io_pgtable *iop) +static void apple_dart_free_pgtables(struct dart_io_pgtable *data, dart_iopte *ptep, int level) { - struct dart_io_pgtable *data = io_pgtable_to_data(iop); - dart_iopte *ptep, *end; - int i; + dart_iopte *end; - for (i = 0; i < (1 << data->tbl_bits) && data->pgd[i]; ++i) { - ptep = data->pgd[i]; + if (level > 1) { end = (void *)ptep + DART_GRANULE(data); while (ptep != end) { dart_iopte pte = *ptep++; - if (pte) { - unsigned long page = - (unsigned long)iopte_deref(pte, data); - - free_pages(page, get_order(DART_GRANULE(data))); - } + if (pte) + apple_dart_free_pgtables(data, iopte_deref(pte, data), level - 1); } - free_pages((unsigned long)data->pgd[i], - get_order(DART_GRANULE(data))); + } + free_pages((unsigned long)ptep, get_order(DART_GRANULE(data))); +} + +static void apple_dart_free_pgtable(struct io_pgtable *iop) +{ + struct dart_io_pgtable *data = io_pgtable_to_data(iop); + dart_iopte *ptep; + int i; + + for (i = 0; i < (1 << data->tbl_bits) && data->pgd[i]; ++i) { + ptep = data->pgd[i]; + apple_dart_free_pgtables(data, data->pgd[i], data->levels - 1); } kfree(data); diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h index 86cf1f7ae389a4..0e0ec0dd06d205 100644 --- a/include/linux/io-pgtable.h +++ b/include/linux/io-pgtable.h @@ -167,6 +167,7 @@ struct io_pgtable_cfg { struct { u64 ttbr[4]; u32 n_ttbrs; + u32 n_levels; } apple_dart_cfg; }; }; From 204255fe8d04a4b30ee8406b4e323fea2ed29973 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 10 Apr 2023 20:04:05 +0900 Subject: [PATCH 0212/1020] iommu: apple-dart: Clear stream error indicator bits for T8110 DARTs These registers exist at least on the t602x variant, and if not cleared the IRQ will never clear. Signed-off-by: Hector Martin --- drivers/iommu/apple-dart.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index 62b011874d30e5..eadc00e69e860c 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -123,6 +123,8 @@ #define DART_T8110_ERROR_ADDR_LO 0x170 #define DART_T8110_ERROR_ADDR_HI 0x174 +#define DART_T8110_ERROR_STREAMS 0x1c0 + #define DART_T8110_PROTECT 0x200 #define DART_T8110_UNPROTECT 0x204 #define DART_T8110_PROTECT_LOCK 0x208 @@ -1231,6 +1233,7 @@ static irqreturn_t apple_dart_t8110_irq(int irq, void *dev) u32 addr_hi = readl(dart->regs + DART_T8110_ERROR_ADDR_HI); u64 addr = addr_lo | (((u64)addr_hi) << 32); u8 stream_idx = FIELD_GET(DART_T8110_ERROR_STREAM, error); + int i; if (!(error & DART_T8110_ERROR_FLAG)) return IRQ_NONE; @@ -1257,6 +1260,9 @@ static irqreturn_t apple_dart_t8110_irq(int irq, void *dev) error, stream_idx, error_code, fault_name, addr); writel(error, dart->regs + DART_T8110_ERROR); + for (i = 0; i < BITS_TO_U32(dart->num_streams); i++) + writel(U32_MAX, dart->regs + DART_T8110_ERROR_STREAMS + 4 * i); + return IRQ_HANDLED; } From 10e9c8cc901c22ba21129d257de34f810c7478fe Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 10 Apr 2023 20:05:20 +0900 Subject: [PATCH 0213/1020] iommu: apple-dart: Make the hw register fields u32s The registers are 32-bit and the offsets definitely don't need 64 bits either, these should've been u32s. Signed-off-by: Hector Martin --- drivers/iommu/apple-dart.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index eadc00e69e860c..73a04c5e5708aa 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -169,22 +169,22 @@ struct apple_dart_hw { int max_sid_count; - u64 lock; - u64 lock_bit; + u32 lock; + u32 lock_bit; - u64 error; + u32 error; - u64 enable_streams; + u32 enable_streams; - u64 tcr; - u64 tcr_enabled; - u64 tcr_disabled; - u64 tcr_bypass; + u32 tcr; + u32 tcr_enabled; + u32 tcr_disabled; + u32 tcr_bypass; - u64 ttbr; - u64 ttbr_valid; - u64 ttbr_addr_field_shift; - u64 ttbr_shift; + u32 ttbr; + u32 ttbr_valid; + u32 ttbr_addr_field_shift; + u32 ttbr_shift; int ttbr_count; }; From ea620e9634aeb0d2bfb50c903ade8457218f3dc1 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 10 Apr 2023 20:06:44 +0900 Subject: [PATCH 0214/1020] iommu: apple-dart: Add 4-level page table support The T8110 variant DART implementation on T602x SoCs indicates an IAS of 42, which requires an extra page table level. The extra level is optional, but let's implement it. Later it might be useful to restrict this based on the actual attached devices, since most won't need that much address space anyway. Signed-off-by: Hector Martin --- drivers/iommu/apple-dart.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index 73a04c5e5708aa..52b125bbc6daa2 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -136,6 +136,7 @@ #define DART_T8110_TCR 0x1000 #define DART_T8110_TCR_REMAP GENMASK(11, 8) #define DART_T8110_TCR_REMAP_EN BIT(7) +#define DART_T8110_TCR_FOUR_LEVEL BIT(3) #define DART_T8110_TCR_BYPASS_DAPF BIT(2) #define DART_T8110_TCR_BYPASS_DART BIT(1) #define DART_T8110_TCR_TRANSLATE_ENABLE BIT(0) @@ -180,6 +181,7 @@ struct apple_dart_hw { u32 tcr_enabled; u32 tcr_disabled; u32 tcr_bypass; + u32 tcr_4level; u32 ttbr; u32 ttbr_valid; @@ -222,6 +224,7 @@ struct apple_dart { u32 num_streams; u32 supports_bypass : 1; u32 locked : 1; + u32 four_level : 1; struct iommu_group *sid2group[DART_MAX_STREAMS]; struct iommu_device iommu; @@ -310,14 +313,17 @@ static struct apple_dart_domain *to_dart_domain(struct iommu_domain *dom) } static void -apple_dart_hw_enable_translation(struct apple_dart_stream_map *stream_map) +apple_dart_hw_enable_translation(struct apple_dart_stream_map *stream_map, int levels) { struct apple_dart *dart = stream_map->dart; int sid; + WARN_ON(levels != 3 && levels != 4); + WARN_ON(levels == 4 && !dart->four_level); WARN_ON(stream_map->dart->locked); for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) - writel(dart->hw->tcr_enabled, dart->regs + DART_TCR(dart, sid)); + writel(dart->hw->tcr_enabled | (levels == 4 ? dart->hw->tcr_4level : 0), + dart->regs + DART_TCR(dart, sid)); } static void apple_dart_hw_disable_dma(struct apple_dart_stream_map *stream_map) @@ -669,7 +675,8 @@ apple_dart_setup_translation(struct apple_dart_domain *domain, for (; i < stream_map->dart->hw->ttbr_count; ++i) apple_dart_hw_clear_ttbr(stream_map, i); - apple_dart_hw_enable_translation(stream_map); + apple_dart_hw_enable_translation(stream_map, + pgtbl_cfg->apple_dart_cfg.n_levels); } stream_map->dart->hw->invalidate_tlb(stream_map); } @@ -753,6 +760,19 @@ static int apple_dart_finalize_domain(struct apple_dart_domain *dart_domain, ttbr = readl(dart->regs + DART_TTBR(dart, sid, 0)); WARN_ON(!(ttbr & dart->hw->ttbr_valid)); + + /* If the DART is locked, we need to keep the translation level count. */ + if (dart->hw->tcr_4level && dart->ias > 36) { + if (readl(dart->regs + DART_TCR(dart, sid)) & dart->hw->tcr_4level) { + if (dart->ias < 37) { + dev_info(dart->dev, "Expanded to ias=37 due to lock\n"); + pgtbl_cfg.ias = 37; + } + } else if (dart->ias > 36) { + dev_info(dart->dev, "Limited to ias=36 due to lock\n"); + pgtbl_cfg.ias = 36; + } + } } dart_domain->pgtbl_ops = alloc_io_pgtable_ops(dart->hw->fmt, &pgtbl_cfg, @@ -765,7 +785,7 @@ static int apple_dart_finalize_domain(struct apple_dart_domain *dart_domain, dart_domain->domain.pgsize_bitmap = pgtbl_cfg.pgsize_bitmap; dart_domain->domain.geometry.aperture_start = 0; dart_domain->domain.geometry.aperture_end = - (dma_addr_t)DMA_BIT_MASK(dart->ias); + (dma_addr_t)DMA_BIT_MASK(pgtbl_cfg.ias); dart_domain->domain.geometry.force_aperture = true; dart_domain->finalized = true; @@ -1336,6 +1356,7 @@ static int apple_dart_probe(struct platform_device *pdev) dart->ias = FIELD_GET(DART_T8110_PARAMS3_VA_WIDTH, dart_params[2]); dart->oas = FIELD_GET(DART_T8110_PARAMS3_PA_WIDTH, dart_params[2]); dart->num_streams = FIELD_GET(DART_T8110_PARAMS4_NUM_SIDS, dart_params[3]); + dart->four_level = dart->ias > 36; break; } @@ -1373,9 +1394,9 @@ static int apple_dart_probe(struct platform_device *pdev) dev_info( &pdev->dev, - "DART [pagesize %x, %d streams, bypass support: %d, bypass forced: %d, locked: %d] initialized\n", + "DART [pagesize %x, %d streams, bypass support: %d, bypass forced: %d, locked: %d, AS %d -> %d] initialized\n", dart->pgsize, dart->num_streams, dart->supports_bypass, - dart->pgsize > PAGE_SIZE, dart->locked); + dart->pgsize > PAGE_SIZE, dart->locked, dart->ias, dart->oas); return 0; err_sysfs_remove: @@ -1499,6 +1520,7 @@ static const struct apple_dart_hw apple_dart_hw_t8110 = { .tcr_enabled = DART_T8110_TCR_TRANSLATE_ENABLE, .tcr_disabled = 0, .tcr_bypass = DART_T8110_TCR_BYPASS_DAPF | DART_T8110_TCR_BYPASS_DART, + .tcr_4level = DART_T8110_TCR_FOUR_LEVEL, .ttbr = DART_T8110_TTBR, .ttbr_valid = DART_T8110_TTBR_VALID, From 7c870521918e62c4e5447c6a64a0425730c67ed7 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 11 Apr 2023 01:32:06 +0900 Subject: [PATCH 0215/1020] iommu: apple-dart: Support specifying the DMA aperture in the DT Apple DARTs are often connected directly to devices that expect only a portion of their address space to be used for DMA (for example, because other ranges are mapped directly to something else). Add an apple,dma-range property to allow specifying this range. This range *can* be outside of the DART's IAS. In that case, it is assumed that the hardware truncates addresses and the page tables will only map the lower bits of the address. However, the specified range cannot straddle an IAS boundary (you cannot cover more than IAS worth of address space nor wrap). This corresponds to the vm-base and vm-size properties on the Apple device tree side of things. Signed-off-by: Hector Martin --- drivers/iommu/apple-dart.c | 63 ++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index 52b125bbc6daa2..a2eb799cdb70a3 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -226,6 +227,9 @@ struct apple_dart { u32 locked : 1; u32 four_level : 1; + dma_addr_t dma_min; + dma_addr_t dma_max; + struct iommu_group *sid2group[DART_MAX_STREAMS]; struct iommu_device iommu; @@ -273,6 +277,7 @@ struct apple_dart_domain { struct io_pgtable_ops *pgtbl_ops; bool finalized; + u64 mask; struct mutex init_lock; struct apple_dart_atomic_stream_map stream_maps[MAX_DARTS_PER_DEVICE]; @@ -623,7 +628,7 @@ static phys_addr_t apple_dart_iova_to_phys(struct iommu_domain *domain, if (!ops) return 0; - return ops->iova_to_phys(ops, iova); + return ops->iova_to_phys(ops, iova & dart_domain->mask); } static int apple_dart_map_pages(struct iommu_domain *domain, unsigned long iova, @@ -637,8 +642,8 @@ static int apple_dart_map_pages(struct iommu_domain *domain, unsigned long iova, if (!ops) return -ENODEV; - return ops->map_pages(ops, iova, paddr, pgsize, pgcount, prot, gfp, - mapped); + return ops->map_pages(ops, iova & dart_domain->mask, paddr, pgsize, + pgcount, prot, gfp, mapped); } static size_t apple_dart_unmap_pages(struct iommu_domain *domain, @@ -649,7 +654,8 @@ static size_t apple_dart_unmap_pages(struct iommu_domain *domain, struct apple_dart_domain *dart_domain = to_dart_domain(domain); struct io_pgtable_ops *ops = dart_domain->pgtbl_ops; - return ops->unmap_pages(ops, iova, pgsize, pgcount, gather); + return ops->unmap_pages(ops, iova & dart_domain->mask, pgsize, pgcount, + gather); } static void @@ -721,6 +727,8 @@ static int apple_dart_finalize_domain(struct apple_dart_domain *dart_domain, { struct apple_dart *dart = cfg->stream_maps[0].dart; struct io_pgtable_cfg pgtbl_cfg; + dma_addr_t dma_max = dart->dma_max; + u32 ias = min_t(u32, dart->ias, fls64(dma_max)); int ret = 0; int i, j; @@ -741,7 +749,7 @@ static int apple_dart_finalize_domain(struct apple_dart_domain *dart_domain, pgtbl_cfg = (struct io_pgtable_cfg){ .pgsize_bitmap = dart->pgsize, - .ias = dart->ias, + .ias = ias, .oas = dart->oas, .coherent_walk = 1, .iommu_dev = dart->dev, @@ -764,13 +772,21 @@ static int apple_dart_finalize_domain(struct apple_dart_domain *dart_domain, /* If the DART is locked, we need to keep the translation level count. */ if (dart->hw->tcr_4level && dart->ias > 36) { if (readl(dart->regs + DART_TCR(dart, sid)) & dart->hw->tcr_4level) { - if (dart->ias < 37) { + if (ias < 37) { dev_info(dart->dev, "Expanded to ias=37 due to lock\n"); pgtbl_cfg.ias = 37; } - } else if (dart->ias > 36) { + } else if (ias > 36) { dev_info(dart->dev, "Limited to ias=36 due to lock\n"); pgtbl_cfg.ias = 36; + if (dart->dma_min == 0 && dma_max == DMA_BIT_MASK(dart->ias)) { + dma_max = DMA_BIT_MASK(pgtbl_cfg.ias); + } else if ((dart->dma_min ^ dma_max) & ~DMA_BIT_MASK(36)) { + dev_err(dart->dev, + "Invalid DMA range for locked 3-level PT\n"); + ret = -ENOMEM; + goto done; + } } } } @@ -782,10 +798,16 @@ static int apple_dart_finalize_domain(struct apple_dart_domain *dart_domain, goto done; } + if (pgtbl_cfg.pgsize_bitmap == SZ_4K) + dart_domain->mask = DMA_BIT_MASK(min_t(u32, dart->ias, 32)); + else if (pgtbl_cfg.apple_dart_cfg.n_levels == 3) + dart_domain->mask = DMA_BIT_MASK(min_t(u32, dart->ias, 36)); + else if (pgtbl_cfg.apple_dart_cfg.n_levels == 4) + dart_domain->mask = DMA_BIT_MASK(min_t(u32, dart->ias, 47)); + dart_domain->domain.pgsize_bitmap = pgtbl_cfg.pgsize_bitmap; - dart_domain->domain.geometry.aperture_start = 0; - dart_domain->domain.geometry.aperture_end = - (dma_addr_t)DMA_BIT_MASK(pgtbl_cfg.ias); + dart_domain->domain.geometry.aperture_start = dart->dma_min; + dart_domain->domain.geometry.aperture_end = dma_max; dart_domain->domain.geometry.force_aperture = true; dart_domain->finalized = true; @@ -1298,6 +1320,7 @@ static int apple_dart_probe(struct platform_device *pdev) struct resource *res; struct apple_dart *dart; struct device *dev = &pdev->dev; + u64 dma_range[2]; dart = devm_kzalloc(dev, sizeof(*dart), GFP_KERNEL); if (!dart) @@ -1360,6 +1383,26 @@ static int apple_dart_probe(struct platform_device *pdev) break; } + dart->dma_min = 0; + dart->dma_max = DMA_BIT_MASK(dart->ias); + + ret = of_property_read_u64_array(dev->of_node, "apple,dma-range", dma_range, 2); + if (ret == -EINVAL) { + ret = 0; + } else if (ret) { + goto err_clk_disable; + } else { + dart->dma_min = dma_range[0]; + dart->dma_max = dma_range[0] + dma_range[1] - 1; + if ((dart->dma_min ^ dart->dma_max) & ~DMA_BIT_MASK(dart->ias)) { + dev_err(&pdev->dev, "Invalid DMA range for ias=%d\n", + dart->ias); + goto err_clk_disable; + } + dev_info(&pdev->dev, "Limiting DMA range to %pad..%pad\n", + &dart->dma_min, &dart->dma_max); + } + if (dart->num_streams > DART_MAX_STREAMS) { dev_err(&pdev->dev, "Too many streams (%d > %d)\n", dart->num_streams, DART_MAX_STREAMS); From 798da5d3c422d8410ee4b5556e33f266395356ea Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 10 Sep 2023 23:36:59 +0900 Subject: [PATCH 0216/1020] iommu: apple-dart: Increase MAX_DARTS_PER_DEVICE to 3 ISP needs this. Signed-off-by: Hector Martin --- drivers/iommu/apple-dart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index a2eb799cdb70a3..41c7fd346e1edf 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -38,7 +38,7 @@ #define DART_MAX_STREAMS 256 #define DART_MAX_TTBR 4 -#define MAX_DARTS_PER_DEVICE 2 +#define MAX_DARTS_PER_DEVICE 3 /* Common registers */ From 08766f7d86a1d973e2a6c82850344b0282d97032 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 11 Sep 2023 00:06:48 +0900 Subject: [PATCH 0217/1020] iommu: apple-dart: Allow mismatched bypass support This is needed by ISP, which has DART0 with bypass and DART1/2 without. Signed-off-by: Hector Martin --- drivers/iommu/apple-dart.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index 41c7fd346e1edf..605668c7190976 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -293,6 +293,9 @@ struct apple_dart_domain { * @streams: streams for this device */ struct apple_dart_master_cfg { + /* Union of DART capabilitles */ + u32 supports_bypass : 1; + struct apple_dart_stream_map stream_maps[MAX_DARTS_PER_DEVICE]; }; @@ -887,7 +890,7 @@ static int apple_dart_attach_dev_identity(struct iommu_domain *domain, struct apple_dart_stream_map *stream_map; int i; - if (!cfg->stream_maps[0].dart->supports_bypass) + if (!cfg->supports_bypass) return -EINVAL; if (cfg->stream_maps[0].dart->locked) @@ -1018,20 +1021,25 @@ static int apple_dart_of_xlate(struct device *dev, return -EINVAL; sid = args->args[0]; - if (!cfg) + if (!cfg) { cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + + /* Will be ANDed with DART capabilities */ + cfg->supports_bypass = true; + } if (!cfg) return -ENOMEM; dev_iommu_priv_set(dev, cfg); cfg_dart = cfg->stream_maps[0].dart; if (cfg_dart) { - if (cfg_dart->supports_bypass != dart->supports_bypass) - return -EINVAL; if (cfg_dart->pgsize != dart->pgsize) return -EINVAL; } + if (!dart->supports_bypass) + cfg->supports_bypass = false; + for (i = 0; i < MAX_DARTS_PER_DEVICE; ++i) { if (cfg->stream_maps[i].dart == dart) { set_bit(sid, cfg->stream_maps[i].sidmap); @@ -1171,7 +1179,7 @@ static int apple_dart_def_domain_type(struct device *dev) if (cfg->stream_maps[0].dart->pgsize > PAGE_SIZE) return IOMMU_DOMAIN_IDENTITY; - if (!cfg->stream_maps[0].dart->supports_bypass) + if (!cfg->supports_bypass) return IOMMU_DOMAIN_DMA; if (cfg->stream_maps[0].dart->locked) return IOMMU_DOMAIN_DMA; From dd56e82fd7be0619b68788ca57e02d84a701f9da Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Fri, 29 Sep 2023 19:46:53 +0900 Subject: [PATCH 0218/1020] iommu: apple-dart: Power on device when handling IRQs It's possible for an IRQ to fire and the device to be RPM suspended before we can handle it, which then causes device register accesses to fail in the IRQ handler. Since RPM is IRQ-safe for this device, just make sure we power on the DART in the IRQ handler too. Signed-off-by: Asahi Lina --- drivers/iommu/apple-dart.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index 605668c7190976..4f7c104e23eb93 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -1316,6 +1316,17 @@ static irqreturn_t apple_dart_t8110_irq(int irq, void *dev) return IRQ_HANDLED; } +static irqreturn_t apple_dart_irq(int irq, void *dev) +{ + irqreturn_t ret; + struct apple_dart *dart = dev; + + WARN_ON(pm_runtime_get_sync(dart->dev) < 0); + ret = dart->hw->irq_handler(irq, dev); + pm_runtime_put(dart->dev); + return ret; +} + static bool apple_dart_is_locked(struct apple_dart *dart) { return !!(readl(dart->regs + dart->hw->lock) & dart->hw->lock_bit); @@ -1425,7 +1436,7 @@ static int apple_dart_probe(struct platform_device *pdev) goto err_clk_disable; } - ret = request_irq(dart->irq, dart->hw->irq_handler, IRQF_SHARED, + ret = request_irq(dart->irq, apple_dart_irq, IRQF_SHARED, "apple-dart fault handler", dart); if (ret) goto err_clk_disable; From a4a5e5be51f3078f504f6d6b7a5e2a40101acd3d Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 23 Nov 2023 18:08:50 +0900 Subject: [PATCH 0219/1020] iommu: apple-dart: Check for fwspec in the device probe path We need to check for a fwspec in the probe path, to ensure that the driver does not probe as a bus iommu driver. This, along with related fixes to the IOMMU core code, fixes races and issues when multiple IOMMUs assigned to the same device probe at different times. Suggested-by: Jason Gunthorpe Signed-off-by: Hector Martin iommu: apple-dart: --- drivers/iommu/apple-dart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index 4f7c104e23eb93..f9ad281975c59a 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -949,7 +949,7 @@ static struct iommu_device *apple_dart_probe_device(struct device *dev) struct apple_dart_stream_map *stream_map; int i; - if (!cfg) + if (!dev_iommu_fwspec_get(dev) || !cfg) return ERR_PTR(-ENODEV); for_each_stream_map(i, cfg, stream_map) From efce9497381c7e27b015772847fdc1e0d81a38c0 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 24 Mar 2024 18:06:46 +0100 Subject: [PATCH 0220/1020] iommu/of: Free fwspec on probe deferrel For devices with multiple iommus of_iommu_configure_device() potentially inits the fwspec for one of the iommus but another iommu device might have not yet been probe resulting in -EPROBE_DEFER. Clear the fwspec in such cases to ensure the next of_iommu_configure() call retries to configure all iommus. Signed-off-by: Janne Grunau --- drivers/iommu/of_iommu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index 73601b35b7f7d3..6cbf91906fe8d5 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -149,6 +149,8 @@ int of_iommu_configure(struct device *dev, struct device_node *master_np, of_pci_iommu_init, &info); } else { err = of_iommu_configure_device(master_np, dev, id); + if (err == -EPROBE_DEFER) + iommu_fwspec_free(dev); } mutex_unlock(&iommu_probe_device_lock); From db90a7eced92efd2468876221e093076f9ac11de Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 24 Mar 2024 18:26:53 +0100 Subject: [PATCH 0221/1020] iommu/of: Handle missing iommu_ops as probe deferral in of_iommu_xlate Signed-off-by: Janne Grunau --- drivers/iommu/of_iommu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index 6cbf91906fe8d5..bba9a776ef50d3 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -28,6 +28,8 @@ static int of_iommu_xlate(struct device *dev, if ((ops && !ops->of_xlate) || !of_device_is_available(iommu_spec->np)) return -ENODEV; + if (!ops) + return driver_deferred_probe_check_state(dev); ret = iommu_fwspec_init(dev, fwnode, ops); if (ret) From 05c68699aca4de1b69a49a8a6b2b80ff2956cf12 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 20 Sep 2021 02:23:11 +0900 Subject: [PATCH 0222/1020] tty: serial: samsung_tty: Support runtime PM This allows idle UART devices to be suspended using the standard runtime-PM framework. The logic is modeled after stm32-usart. Signed-off-by: Hector Martin --- drivers/tty/serial/samsung_tty.c | 89 ++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 33 deletions(-) diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c index a2d07e05c50264..b2d995e820355a 100644 --- a/drivers/tty/serial/samsung_tty.c +++ b/drivers/tty/serial/samsung_tty.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -1294,30 +1295,49 @@ static int apple_s5l_serial_startup(struct uart_port *port) return ret; } +static int __maybe_unused s3c24xx_serial_runtime_suspend(struct device *dev) +{ + struct uart_port *port = dev_get_drvdata(dev); + struct s3c24xx_uart_port *ourport = to_ourport(port); + int timeout = 10000; + + while (--timeout && !s3c24xx_serial_txempty_nofifo(port)) + udelay(100); + + if (!IS_ERR(ourport->baudclk)) + clk_disable_unprepare(ourport->baudclk); + + clk_disable_unprepare(ourport->clk); + return 0; +}; + +static int __maybe_unused s3c24xx_serial_runtime_resume(struct device *dev) +{ + struct uart_port *port = dev_get_drvdata(dev); + struct s3c24xx_uart_port *ourport = to_ourport(port); + + clk_prepare_enable(ourport->clk); + + if (!IS_ERR(ourport->baudclk)) + clk_prepare_enable(ourport->baudclk); + return 0; +}; + static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, unsigned int old) { struct s3c24xx_uart_port *ourport = to_ourport(port); - int timeout = 10000; ourport->pm_level = level; switch (level) { - case 3: - while (--timeout && !s3c24xx_serial_txempty_nofifo(port)) - udelay(100); - - if (!IS_ERR(ourport->baudclk)) - clk_disable_unprepare(ourport->baudclk); - - clk_disable_unprepare(ourport->clk); + case UART_PM_STATE_OFF: + pm_runtime_mark_last_busy(port->dev); + pm_runtime_put_sync(port->dev); break; - case 0: - clk_prepare_enable(ourport->clk); - - if (!IS_ERR(ourport->baudclk)) - clk_prepare_enable(ourport->baudclk); + case UART_PM_STATE_ON: + pm_runtime_get_sync(port->dev); break; default: dev_err(port->dev, "s3c24xx_serial: unknown pm %d\n", level); @@ -2040,18 +2060,15 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) } } + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + dev_dbg(&pdev->dev, "%s: adding port\n", __func__); uart_add_one_port(&s3c24xx_uart_drv, &ourport->port); platform_set_drvdata(pdev, &ourport->port); - /* - * Deactivate the clock enabled in s3c24xx_serial_init_port here, - * so that a potential re-enablement through the pm-callback overlaps - * and keeps the clock enabled in this case. - */ - clk_disable_unprepare(ourport->clk); - if (!IS_ERR(ourport->baudclk)) - clk_disable_unprepare(ourport->baudclk); + pm_runtime_put_sync(&pdev->dev); probe_index++; @@ -2061,16 +2078,26 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) static void s3c24xx_serial_remove(struct platform_device *dev) { struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); + struct s3c24xx_uart_port *ourport = to_ourport(port); if (port) + pm_runtime_get_sync(&dev->dev); uart_remove_one_port(&s3c24xx_uart_drv, port); + clk_disable_unprepare(ourport->clk); + if (!IS_ERR(ourport->baudclk)) + clk_disable_unprepare(ourport->baudclk); + + pm_runtime_disable(&dev->dev); + pm_runtime_set_suspended(&dev->dev); + pm_runtime_put_noidle(&dev->dev); + uart_unregister_driver(&s3c24xx_uart_drv); } /* UART power management code */ -#ifdef CONFIG_PM_SLEEP -static int s3c24xx_serial_suspend(struct device *dev) + +static int __maybe_unused s3c24xx_serial_suspend(struct device *dev) { struct uart_port *port = s3c24xx_dev_to_port(dev); @@ -2080,7 +2107,7 @@ static int s3c24xx_serial_suspend(struct device *dev) return 0; } -static int s3c24xx_serial_resume(struct device *dev) +static int __maybe_unused s3c24xx_serial_resume(struct device *dev) { struct uart_port *port = s3c24xx_dev_to_port(dev); struct s3c24xx_uart_port *ourport = to_ourport(port); @@ -2100,7 +2127,7 @@ static int s3c24xx_serial_resume(struct device *dev) return 0; } -static int s3c24xx_serial_resume_noirq(struct device *dev) +static int __maybe_unused s3c24xx_serial_resume_noirq(struct device *dev) { struct uart_port *port = s3c24xx_dev_to_port(dev); struct s3c24xx_uart_port *ourport = to_ourport(port); @@ -2172,13 +2199,9 @@ static int s3c24xx_serial_resume_noirq(struct device *dev) static const struct dev_pm_ops s3c24xx_serial_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(s3c24xx_serial_suspend, s3c24xx_serial_resume) SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, s3c24xx_serial_resume_noirq) + SET_RUNTIME_PM_OPS(s3c24xx_serial_runtime_suspend, + s3c24xx_serial_runtime_resume, NULL) }; -#define SERIAL_SAMSUNG_PM_OPS (&s3c24xx_serial_pm_ops) - -#else /* !CONFIG_PM_SLEEP */ - -#define SERIAL_SAMSUNG_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ /* Console code */ @@ -2653,7 +2676,7 @@ static struct platform_driver samsung_serial_driver = { .id_table = s3c24xx_serial_driver_ids, .driver = { .name = "samsung-uart", - .pm = SERIAL_SAMSUNG_PM_OPS, + .pm = &s3c24xx_serial_pm_ops, .of_match_table = of_match_ptr(s3c24xx_uart_dt_match), }, }; From 1564007681af6ad6809ec2c288f5b3666f54faa9 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Sun, 7 Nov 2021 11:21:19 +0100 Subject: [PATCH 0223/1020] dt-bindings: usb: Add Apple dwc3 bindings Apple Silicon SoCs such as the M1 have multiple USB controllers based on the Synopsys DesignWare USB3 controller. References to the ATC PHY required for SuperSpeed are left out for now until support has been upstreamed as well. Signed-off-by: Sven Peter --- .../devicetree/bindings/usb/apple,dwc3.yaml | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/apple,dwc3.yaml diff --git a/Documentation/devicetree/bindings/usb/apple,dwc3.yaml b/Documentation/devicetree/bindings/usb/apple,dwc3.yaml new file mode 100644 index 00000000000000..fb3b3489e6b263 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/apple,dwc3.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/apple,dwc3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple Silicon DWC3 USB controller + +maintainers: + - Sven Peter + +description: + On Apple Silicon SoCs such as the M1 each Type-C port has a corresponding + USB controller based on the Synopsys DesignWare USB3 controller. + + The common content of this binding is defined in snps,dwc3.yaml. + +allOf: + - $ref: snps,dwc3.yaml# + +select: + properties: + compatible: + contains: + const: apple,dwc3 + required: + - compatible + +properties: + compatible: + items: + - enum: + - apple,t8103-dwc3 + - apple,t6000-dwc3 + - const: apple,dwc3 + - const: snps,dwc3 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + +unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + +examples: + - | + #include + #include + + usb@82280000 { + compatible = "apple,t8103-dwc3", "apple,dwc3", "snps,dwc3"; + reg = <0x82280000 0x10000>; + interrupts = ; + + dr_mode = "otg"; + usb-role-switch; + role-switch-default-mode = "host"; + }; From a520c6df42e10eef1ddd984c831e5bbd3e36eec0 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Wed, 30 Nov 2022 22:10:59 +0100 Subject: [PATCH 0224/1020] usb: dwc3: Add support for Apple DWC3 As mad as it sounds, the dwc3 controller present on the Apple M1 must be reset and reinitialized whenever a device is unplugged from the root port or when the PHY mode is changed. This is required for at least the following reasons: - The USB2 D+/D- lines are connected through a stateful eUSB2 repeater which in turn is controlled by a variant of the TI TPS6598x USB PD chip. When the USB PD controller detects a hotplug event it resets the eUSB2 repeater. Afterwards, no new device is recognized before the DWC3 core and PHY are reset as well because the eUSB2 repeater and the PHY/dwc3 block disagree about the current state. - It's possible to completely break the dwc3 controller by switching it to device mode and unplugging the cable at just the wrong time. If this happens dwc3 behaves as if no device is connected. CORESOFTRESET will also never clear after it has been set. The only workaround is to trigger a hard reset of the entire dwc3 core with its external reset line. - Whenever the PHY mode is changed (to e.g. transition to DisplayPort alternate mode or USB4) dwc3 has to be shutdown and reinitialized. Otherwise the Type-C port will not be useable until the entire SoC has been reset. All of this can be easily worked around by respecting transitions to USB_ROLE_NONE and making sure the external reset line is asserted when switching roles. Signed-off-by: Sven Peter --- drivers/usb/dwc3/core.c | 58 ++++++++++++++++++++++++++++++++++++++--- drivers/usb/dwc3/core.h | 3 +++ drivers/usb/dwc3/drd.c | 11 +++++++- 3 files changed, 67 insertions(+), 5 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 100041320e8dd2..614ffa391f8ca8 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -137,6 +137,9 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode) dwc->current_dr_role = mode; } +static void dwc3_core_exit(struct dwc3 *dwc); +static int dwc3_core_init_for_resume(struct dwc3 *dwc); + static void __dwc3_set_mode(struct work_struct *work) { struct dwc3 *dwc = work_to_dwc(work); @@ -155,7 +158,7 @@ static void __dwc3_set_mode(struct work_struct *work) if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG) dwc3_otg_update(dwc, 0); - if (!desired_dr_role) + if (!desired_dr_role && !dwc->role_switch_reset_quirk) goto out; if (desired_dr_role == dwc->current_dr_role) @@ -183,13 +186,32 @@ static void __dwc3_set_mode(struct work_struct *work) break; } + if (dwc->role_switch_reset_quirk) { + if (dwc->current_dr_role) { + dwc->current_dr_role = 0; + dwc3_core_exit(dwc); + } + + if (desired_dr_role) { + ret = dwc3_core_init_for_resume(dwc); + if (ret) { + dev_err(dwc->dev, + "failed to reinitialize core\n"); + goto out; + } + } else { + goto out; + } + } + /* * When current_dr_role is not set, there's no role switching. * Only perform GCTL.CoreSoftReset when there's DRD role switching. */ - if (dwc->current_dr_role && ((DWC3_IP_IS(DWC3) || + if (dwc->role_switch_reset_quirk || + (dwc->current_dr_role && ((DWC3_IP_IS(DWC3) || DWC3_VER_IS_PRIOR(DWC31, 190A)) && - desired_dr_role != DWC3_GCTL_PRTCAP_OTG)) { + desired_dr_role != DWC3_GCTL_PRTCAP_OTG))) { reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg |= DWC3_GCTL_CORESOFTRESET; dwc3_writel(dwc->regs, DWC3_GCTL, reg); @@ -1426,6 +1448,18 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) ret = dwc3_drd_init(dwc); if (ret) return dev_err_probe(dev, ret, "failed to initialize dual-role\n"); + + /* + * If the role switch reset quirk is required the first role + * switch notification will initialize the core such that we + * have to shut it down here. Make sure that the __dwc3_set_mode + * queued by dwc3_drd_init has completed before since it + * may still try to access MMIO. + */ + if (dwc->role_switch_reset_quirk) { + flush_work(&dwc->drd_work); + dwc3_core_exit(dwc); + } break; default: dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode); @@ -1930,6 +1964,22 @@ static int dwc3_probe(struct platform_device *pdev) if (ret) goto err_put_psy; + if (dev->of_node) { + if (of_device_is_compatible(dev->of_node, "apple,dwc3")) { + if (!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) || + !IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) { + dev_err(dev, + "Apple DWC3 requires role switch support.\n" + ); + ret = -EINVAL; + goto err_put_psy; + } + + dwc->dr_mode = USB_DR_MODE_OTG; + dwc->role_switch_reset_quirk = true; + } + } + ret = reset_control_deassert(dwc->reset); if (ret) goto err_put_psy; @@ -2055,7 +2105,6 @@ static void dwc3_remove(struct platform_device *pdev) power_supply_put(dwc->usb_psy); } -#ifdef CONFIG_PM static int dwc3_core_init_for_resume(struct dwc3 *dwc) { int ret; @@ -2082,6 +2131,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc) return ret; } +#ifdef CONFIG_PM static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) { unsigned long flags; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 180dd8d29287c6..c1ef5f546dc597 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1136,6 +1136,7 @@ struct dwc3_scratchpad_array { * @sys_wakeup: set if the device may do system wakeup. * @wakeup_configured: set if the device is configured for remote wakeup. * @suspended: set to track suspend event due to U3/L2. + * @role_switch_reset_quirk: set to force reinitialization after any role switch * @imod_interval: set the interrupt moderation interval in 250ns * increments or 0 to disable. * @max_cfg_eps: current max number of IN eps used across all USB configs. @@ -1362,6 +1363,8 @@ struct dwc3 { unsigned wakeup_configured:1; unsigned suspended:1; + unsigned role_switch_reset_quirk:1; + u16 imod_interval; int max_cfg_eps; diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index 57ddd2e43022eb..1d3ee1bea189e6 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -461,6 +461,9 @@ static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, break; } + if (dwc->role_switch_reset_quirk && role == USB_ROLE_NONE) + mode = 0; + dwc3_set_mode(dwc, mode); return 0; } @@ -489,6 +492,10 @@ static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw) role = USB_ROLE_DEVICE; break; } + + if (dwc->role_switch_reset_quirk && !dwc->current_dr_role) + role = USB_ROLE_NONE; + spin_unlock_irqrestore(&dwc->lock, flags); return role; } @@ -499,7 +506,9 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc) u32 mode; dwc->role_switch_default_mode = usb_get_role_switch_default_mode(dwc->dev); - if (dwc->role_switch_default_mode == USB_DR_MODE_HOST) { + if (dwc->role_switch_reset_quirk) { + mode = 0; + } else if (dwc->role_switch_default_mode == USB_DR_MODE_HOST) { mode = DWC3_GCTL_PRTCAP_HOST; } else { dwc->role_switch_default_mode = USB_DR_MODE_PERIPHERAL; From 3a74161518039cf058bcf13248c4353d72ce7cc7 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 16 Feb 2022 12:17:58 -0700 Subject: [PATCH 0225/1020] apple-nvme: defer cache flushes by a specified amount Cache flushes on the M1 nvme are really slow, taking 17-18 msec to complete. This can slow down workloads considerably, pure random writes end up being bound by the flush latency and hence run at 55-60 IOPS. Add a deferred flush work around to provide better performance, at a minimal risk. By default, flushes are delayed at most 1 second, but this is configurable. With this work-around, a pure random write workload runs at ~12K IOPS rather than 56 IOPS. Signed-off-by: Jens Axboe --- drivers/nvme/host/apple.c | 69 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c index dd6ec0865141a9..6a9c5d06f38bfc 100644 --- a/drivers/nvme/host/apple.c +++ b/drivers/nvme/host/apple.c @@ -195,8 +195,20 @@ struct apple_nvme { int irq; spinlock_t lock; + + /* + * Delayed cache flush handling state + */ + struct nvme_ns *flush_ns; + unsigned long flush_interval; + unsigned long last_flush; + struct delayed_work flush_dwork; }; +unsigned int flush_interval = 1000; +module_param(flush_interval, uint, 0644); +MODULE_PARM_DESC(flush_interval, "Grace period in msecs between flushes"); + static_assert(sizeof(struct nvme_command) == 64); static_assert(sizeof(struct apple_nvmmu_tcb) == 128); @@ -729,6 +741,26 @@ static int apple_nvme_remove_sq(struct apple_nvme *anv) return nvme_submit_sync_cmd(anv->ctrl.admin_q, &c, NULL, 0); } +static bool apple_nvme_delayed_flush(struct apple_nvme *anv, struct nvme_ns *ns, + struct request *req) +{ + if (!anv->flush_interval || req_op(req) != REQ_OP_FLUSH) + return false; + if (delayed_work_pending(&anv->flush_dwork)) + return true; + if (time_before(jiffies, anv->last_flush + anv->flush_interval)) { + kblockd_mod_delayed_work_on(WORK_CPU_UNBOUND, &anv->flush_dwork, + anv->flush_interval); + if (WARN_ON_ONCE(anv->flush_ns && anv->flush_ns != ns)) + goto out; + anv->flush_ns = ns; + return true; + } +out: + anv->last_flush = jiffies; + return false; +} + static blk_status_t apple_nvme_queue_rq(struct blk_mq_hw_ctx *hctx, const struct blk_mq_queue_data *bd) { @@ -764,6 +796,12 @@ static blk_status_t apple_nvme_queue_rq(struct blk_mq_hw_ctx *hctx, } nvme_start_request(req); + + if (apple_nvme_delayed_flush(anv, ns, req)) { + blk_mq_complete_request(req); + return BLK_STS_OK; + } + apple_nvme_submit_cmd(q, cmnd); return BLK_STS_OK; @@ -1388,6 +1426,28 @@ static void devm_apple_nvme_mempool_destroy(void *data) mempool_destroy(data); } +static void apple_nvme_flush_work(struct work_struct *work) +{ + struct nvme_command c = { }; + struct apple_nvme *anv; + struct nvme_ns *ns; + int err; + + anv = container_of(work, struct apple_nvme, flush_dwork.work); + ns = anv->flush_ns; + if (WARN_ON_ONCE(!ns)) + return; + + c.common.opcode = nvme_cmd_flush; + c.common.nsid = cpu_to_le32(anv->flush_ns->head->ns_id); + err = nvme_submit_sync_cmd(ns->queue, &c, NULL, 0); + if (err) { + dev_err(anv->dev, "Deferred flush failed: %d\n", err); + } else { + anv->last_flush = jiffies; + } +} + static int apple_nvme_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1522,6 +1582,14 @@ static int apple_nvme_probe(struct platform_device *pdev) goto put_dev; } + if (flush_interval) { + anv->flush_interval = msecs_to_jiffies(flush_interval); + anv->flush_ns = NULL; + anv->last_flush = jiffies - anv->flush_interval; + } + + INIT_DELAYED_WORK(&anv->flush_dwork, apple_nvme_flush_work); + nvme_reset_ctrl(&anv->ctrl); async_schedule(apple_nvme_async_probe, anv); @@ -1553,6 +1621,7 @@ static void apple_nvme_shutdown(struct platform_device *pdev) { struct apple_nvme *anv = platform_get_drvdata(pdev); + flush_delayed_work(&anv->flush_dwork); apple_nvme_disable(anv, true); if (apple_rtkit_is_running(anv->rtk)) apple_rtkit_shutdown(anv->rtk); From f59e51668fe732d6d93b6c2dceda810e175de1d4 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 27 Jun 2022 21:47:43 +0900 Subject: [PATCH 0226/1020] apple-nvme: Release power domains when probe fails Signed-off-by: Hector Martin --- drivers/nvme/host/apple.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c index 6a9c5d06f38bfc..f3da937a61b169 100644 --- a/drivers/nvme/host/apple.c +++ b/drivers/nvme/host/apple.c @@ -1596,6 +1596,7 @@ static int apple_nvme_probe(struct platform_device *pdev) return 0; put_dev: + apple_nvme_detach_genpd(anv); put_device(anv->dev); return ret; } From f7c1c5e82593d1ba62beba288963fd85f82f8e46 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 10 Apr 2023 02:56:59 +0900 Subject: [PATCH 0227/1020] apple-nvme: Support coprocessors left idle iBoot on at least some firmwares/machines leaves ANS2 running, requiring a wake command instead of a CPU boot (and if we reset ANS2 in that state, everything breaks). Only stop the CPU if RTKit was running, and only do the reset dance if the CPU is stopped. Normal shutdown handoff: - RTKit not yet running - CPU detected not running - Reset - CPU powerup - RTKit boot wait ANS2 left running/idle: - RTKit not yet running - CPU detected running - RTKit wake message Sleep/resume cycle: - RTKit shutdown - CPU stopped - (sleep here) - CPU detected not running - Reset - CPU powerup - RTKit boot wait Shutdown or device removal: - RTKit shutdown - CPU stopped Therefore, the CPU running bit serves as a consistent flag of whether the coprocessor is fully stopped or just idle. Signed-off-by: Hector Martin --- drivers/nvme/host/apple.c | 53 ++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c index f3da937a61b169..e2d8b3a3f21ce1 100644 --- a/drivers/nvme/host/apple.c +++ b/drivers/nvme/host/apple.c @@ -1049,25 +1049,37 @@ static void apple_nvme_reset_work(struct work_struct *work) ret = apple_rtkit_shutdown(anv->rtk); if (ret) goto out; + + writel(0, anv->mmio_coproc + APPLE_ANS_COPROC_CPU_CONTROL); } - writel(0, anv->mmio_coproc + APPLE_ANS_COPROC_CPU_CONTROL); + /* + * Only do the soft-reset if the CPU is not running, which means either we + * or the previous stage shut it down cleanly. + */ + if (!(readl(anv->mmio_coproc + APPLE_ANS_COPROC_CPU_CONTROL) & + APPLE_ANS_COPROC_CPU_CONTROL_RUN)) { - ret = reset_control_assert(anv->reset); - if (ret) - goto out; + ret = reset_control_assert(anv->reset); + if (ret) + goto out; - ret = apple_rtkit_reinit(anv->rtk); - if (ret) - goto out; + ret = apple_rtkit_reinit(anv->rtk); + if (ret) + goto out; - ret = reset_control_deassert(anv->reset); - if (ret) - goto out; + ret = reset_control_deassert(anv->reset); + if (ret) + goto out; + + writel(APPLE_ANS_COPROC_CPU_CONTROL_RUN, + anv->mmio_coproc + APPLE_ANS_COPROC_CPU_CONTROL); + + ret = apple_rtkit_boot(anv->rtk); + } else { + ret = apple_rtkit_wake(anv->rtk); + } - writel(APPLE_ANS_COPROC_CPU_CONTROL_RUN, - anv->mmio_coproc + APPLE_ANS_COPROC_CPU_CONTROL); - ret = apple_rtkit_boot(anv->rtk); if (ret) { dev_err(anv->dev, "ANS did not boot"); goto out; @@ -1612,9 +1624,12 @@ static void apple_nvme_remove(struct platform_device *pdev) apple_nvme_disable(anv, true); nvme_uninit_ctrl(&anv->ctrl); - if (apple_rtkit_is_running(anv->rtk)) + if (apple_rtkit_is_running(anv->rtk)) { apple_rtkit_shutdown(anv->rtk); + writel(0, anv->mmio_coproc + APPLE_ANS_COPROC_CPU_CONTROL); + } + apple_nvme_detach_genpd(anv); } @@ -1624,8 +1639,11 @@ static void apple_nvme_shutdown(struct platform_device *pdev) flush_delayed_work(&anv->flush_dwork); apple_nvme_disable(anv, true); - if (apple_rtkit_is_running(anv->rtk)) + if (apple_rtkit_is_running(anv->rtk)) { apple_rtkit_shutdown(anv->rtk); + + writel(0, anv->mmio_coproc + APPLE_ANS_COPROC_CPU_CONTROL); + } } static int apple_nvme_resume(struct device *dev) @@ -1642,10 +1660,11 @@ static int apple_nvme_suspend(struct device *dev) apple_nvme_disable(anv, true); - if (apple_rtkit_is_running(anv->rtk)) + if (apple_rtkit_is_running(anv->rtk)) { ret = apple_rtkit_shutdown(anv->rtk); - writel(0, anv->mmio_coproc + APPLE_ANS_COPROC_CPU_CONTROL); + writel(0, anv->mmio_coproc + APPLE_ANS_COPROC_CPU_CONTROL); + } return ret; } From e0544d6d2d4c771980d54c4077aea7b467f485bd Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 12 Dec 2021 11:46:33 +0900 Subject: [PATCH 0228/1020] dt-bindings: spi: apple,spi: Add binding for Apple SPI controllers The Apple SPI controller is present in SoCs such as the M1 (t8103) and M1 Pro/Max (t600x). This controller uses one IRQ and one clock, and doesn't need any special properties, so the binding is trivial. Signed-off-by: Hector Martin --- .../devicetree/bindings/spi/apple,spi.yaml | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/apple,spi.yaml diff --git a/Documentation/devicetree/bindings/spi/apple,spi.yaml b/Documentation/devicetree/bindings/spi/apple,spi.yaml new file mode 100644 index 00000000000000..bcbdc8943e92a3 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/apple,spi.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/apple,spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple ARM SoC SPI controller + +allOf: + - $ref: "spi-controller.yaml#" + +maintainers: + - Hector Martin + +properties: + compatible: + items: + - enum: + - apple,t8103-spi + - apple,t6000-spi + - const: apple,spi + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 1 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - interrupts + - '#address-cells' + - '#size-cells' + +unevaluatedProperties: false + +examples: + - | + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + spi: spi@39b104000 { + compatible = "apple,t6000-spi", "apple,spi"; + reg = <0x3 0x9b104000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clk>; + }; + }; From 976a2db15d615792a1354ce33ce58f24abbbe431 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 9 Dec 2021 21:55:49 +0900 Subject: [PATCH 0229/1020] spi: apple: Add driver for Apple SPI controller This SPI controller is present in Apple SoCs such as the M1 (t8103) and M1 Pro/Max (t600x). It is a relatively straightforward design with two 16-entry FIFOs, arbitrary transfer sizes (up to 2**32 - 1) and fully configurable word size up to 32 bits. It supports one hardware CS line which can also be driven via the pinctrl/GPIO driver instead, if desired. TX and RX can be independently enabled. There are a surprising number of knobs for tweaking details of the transfer, most of which we do not use right now. Hardware CS control is available, but we haven't found a way to make it stay low across multiple logical transfers, so we just use software CS control for now. There is also a shared DMA offload coprocessor that can be used to handle larger transfers without requiring an IRQ every 8-16 words, but that feature depends on a bunch of scaffolding that isn't ready to be upstreamed yet, so leave it for later. The hardware shares some register bit definitions with spi-s3c24xx which suggests it has a shared legacy with Samsung SoCs, but it is too different to warrant sharing a driver. Signed-off-by: Hector Martin --- drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/spi-apple.c | 544 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 553 insertions(+) create mode 100644 drivers/spi/spi-apple.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index bc7021da2fe91c..6e49a4173dcbdf 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -86,6 +86,14 @@ config SPI_AMLOGIC_SPIFC_A1 This enables master mode support for the SPIFC (SPI flash controller) available in Amlogic A1 (A113L SoC). +config SPI_APPLE + tristate "Apple SoC SPI Controller platform driver" + depends on ARCH_APPLE || COMPILE_TEST + help + This enables support for the SPI controller present on + many Apple SoCs, including the t8103 (M1) and t600x + (M1 Pro/Max). + config SPI_AR934X tristate "Qualcomm Atheros AR934X/QCA95XX SPI controller driver" depends on ATH79 || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 4ff8d725ba5e6f..5785da75aaa433 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_SPI_ALTERA) += spi-altera-platform.o obj-$(CONFIG_SPI_ALTERA_CORE) += spi-altera-core.o obj-$(CONFIG_SPI_ALTERA_DFL) += spi-altera-dfl.o obj-$(CONFIG_SPI_AMLOGIC_SPIFC_A1) += spi-amlogic-spifc-a1.o +obj-$(CONFIG_SPI_APPLE) += spi-apple.o obj-$(CONFIG_SPI_AR934X) += spi-ar934x.o obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o obj-$(CONFIG_SPI_ASPEED_SMC) += spi-aspeed-smc.o diff --git a/drivers/spi/spi-apple.c b/drivers/spi/spi-apple.c new file mode 100644 index 00000000000000..c483ad3f69ef5e --- /dev/null +++ b/drivers/spi/spi-apple.c @@ -0,0 +1,544 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Apple SoC SPI device driver + * + * Copyright The Asahi Linux Contributors + * + * Based on spi-sifive.c, Copyright 2018 SiFive, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define APPLE_SPI_CTRL 0x000 +#define APPLE_SPI_CTRL_RUN BIT(0) +#define APPLE_SPI_CTRL_TX_RESET BIT(2) +#define APPLE_SPI_CTRL_RX_RESET BIT(3) + +#define APPLE_SPI_CFG 0x004 +#define APPLE_SPI_CFG_CPHA BIT(1) +#define APPLE_SPI_CFG_CPOL BIT(2) +#define APPLE_SPI_CFG_MODE GENMASK(6, 5) +#define APPLE_SPI_CFG_MODE_POLLED 0 +#define APPLE_SPI_CFG_MODE_IRQ 1 +#define APPLE_SPI_CFG_MODE_DMA 2 +#define APPLE_SPI_CFG_IE_RXCOMPLETE BIT(7) +#define APPLE_SPI_CFG_IE_TXRXTHRESH BIT(8) +#define APPLE_SPI_CFG_LSB_FIRST BIT(13) +#define APPLE_SPI_CFG_WORD_SIZE GENMASK(16, 15) +#define APPLE_SPI_CFG_WORD_SIZE_8B 0 +#define APPLE_SPI_CFG_WORD_SIZE_16B 1 +#define APPLE_SPI_CFG_WORD_SIZE_32B 2 +#define APPLE_SPI_CFG_FIFO_THRESH GENMASK(18, 17) +#define APPLE_SPI_CFG_FIFO_THRESH_8B 0 +#define APPLE_SPI_CFG_FIFO_THRESH_4B 1 +#define APPLE_SPI_CFG_FIFO_THRESH_1B 2 +#define APPLE_SPI_CFG_IE_TXCOMPLETE BIT(21) + +#define APPLE_SPI_STATUS 0x008 +#define APPLE_SPI_STATUS_RXCOMPLETE BIT(0) +#define APPLE_SPI_STATUS_TXRXTHRESH BIT(1) +#define APPLE_SPI_STATUS_TXCOMPLETE BIT(2) + +#define APPLE_SPI_PIN 0x00c +#define APPLE_SPI_PIN_KEEP_MOSI BIT(0) +#define APPLE_SPI_PIN_CS BIT(1) + +#define APPLE_SPI_TXDATA 0x010 +#define APPLE_SPI_RXDATA 0x020 +#define APPLE_SPI_CLKDIV 0x030 +#define APPLE_SPI_CLKDIV_MAX 0x7ff +#define APPLE_SPI_RXCNT 0x034 +#define APPLE_SPI_WORD_DELAY 0x038 +#define APPLE_SPI_TXCNT 0x04c + +#define APPLE_SPI_FIFOSTAT 0x10c +#define APPLE_SPI_FIFOSTAT_TXFULL BIT(4) +#define APPLE_SPI_FIFOSTAT_LEVEL_TX GENMASK(15, 8) +#define APPLE_SPI_FIFOSTAT_RXEMPTY BIT(20) +#define APPLE_SPI_FIFOSTAT_LEVEL_RX GENMASK(31, 24) + +#define APPLE_SPI_IE_XFER 0x130 +#define APPLE_SPI_IF_XFER 0x134 +#define APPLE_SPI_XFER_RXCOMPLETE BIT(0) +#define APPLE_SPI_XFER_TXCOMPLETE BIT(1) + +#define APPLE_SPI_IE_FIFO 0x138 +#define APPLE_SPI_IF_FIFO 0x13c +#define APPLE_SPI_FIFO_RXTHRESH BIT(4) +#define APPLE_SPI_FIFO_TXTHRESH BIT(5) +#define APPLE_SPI_FIFO_RXFULL BIT(8) +#define APPLE_SPI_FIFO_TXEMPTY BIT(9) +#define APPLE_SPI_FIFO_RXUNDERRUN BIT(16) +#define APPLE_SPI_FIFO_TXOVERFLOW BIT(17) + +#define APPLE_SPI_SHIFTCFG 0x150 +#define APPLE_SPI_SHIFTCFG_CLK_ENABLE BIT(0) +#define APPLE_SPI_SHIFTCFG_CS_ENABLE BIT(1) +#define APPLE_SPI_SHIFTCFG_AND_CLK_DATA BIT(8) +#define APPLE_SPI_SHIFTCFG_CS_AS_DATA BIT(9) +#define APPLE_SPI_SHIFTCFG_TX_ENABLE BIT(10) +#define APPLE_SPI_SHIFTCFG_RX_ENABLE BIT(11) +#define APPLE_SPI_SHIFTCFG_BITS GENMASK(21, 16) +#define APPLE_SPI_SHIFTCFG_OVERRIDE_CS BIT(24) + +#define APPLE_SPI_PINCFG 0x154 +#define APPLE_SPI_PINCFG_KEEP_CLK BIT(0) +#define APPLE_SPI_PINCFG_KEEP_CS BIT(1) +#define APPLE_SPI_PINCFG_KEEP_MOSI BIT(2) +#define APPLE_SPI_PINCFG_CLK_IDLE_VAL BIT(8) +#define APPLE_SPI_PINCFG_CS_IDLE_VAL BIT(9) +#define APPLE_SPI_PINCFG_MOSI_IDLE_VAL BIT(10) + +#define APPLE_SPI_DELAY_PRE 0x160 +#define APPLE_SPI_DELAY_POST 0x168 +#define APPLE_SPI_DELAY_ENABLE BIT(0) +#define APPLE_SPI_DELAY_NO_INTERBYTE BIT(1) +#define APPLE_SPI_DELAY_SET_SCK BIT(4) +#define APPLE_SPI_DELAY_SET_MOSI BIT(6) +#define APPLE_SPI_DELAY_SCK_VAL BIT(8) +#define APPLE_SPI_DELAY_MOSI_VAL BIT(12) + +#define APPLE_SPI_FIFO_DEPTH 16 + +/* + * The slowest refclock available is 24MHz, the highest divider is 0x7ff, + * the largest word size is 32 bits, the FIFO depth is 16, the maximum + * intra-word delay is 0xffff refclocks. So the maximum time a transfer + * cycle can take is: + * + * (0x7ff * 32 + 0xffff) * 16 / 24e6 Hz ~= 87ms + * + * Double it and round it up to 200ms for good measure. + */ +#define APPLE_SPI_TIMEOUT_MS 200 + +struct apple_spi { + void __iomem *regs; /* MMIO register address */ + struct clk *clk; /* bus clock */ + struct completion done; /* wake-up from interrupt */ +}; + +static inline void reg_write(struct apple_spi *spi, int offset, u32 value) +{ + writel_relaxed(value, spi->regs + offset); +} + +static inline u32 reg_read(struct apple_spi *spi, int offset) +{ + return readl_relaxed(spi->regs + offset); +} + +static inline void reg_mask(struct apple_spi *spi, int offset, u32 clear, u32 set) +{ + u32 val = reg_read(spi, offset); + + val &= ~clear; + val |= set; + reg_write(spi, offset, val); +} + +static void apple_spi_init(struct apple_spi *spi) +{ + /* Set CS high (inactive) and disable override and auto-CS */ + reg_write(spi, APPLE_SPI_PIN, APPLE_SPI_PIN_CS); + reg_mask(spi, APPLE_SPI_SHIFTCFG, APPLE_SPI_SHIFTCFG_OVERRIDE_CS, 0); + reg_mask(spi, APPLE_SPI_PINCFG, APPLE_SPI_PINCFG_CS_IDLE_VAL, APPLE_SPI_PINCFG_KEEP_CS); + + /* Reset FIFOs */ + reg_write(spi, APPLE_SPI_CTRL, APPLE_SPI_CTRL_RX_RESET | APPLE_SPI_CTRL_TX_RESET); + + /* Configure defaults */ + reg_write(spi, APPLE_SPI_CFG, + FIELD_PREP(APPLE_SPI_CFG_FIFO_THRESH, APPLE_SPI_CFG_FIFO_THRESH_8B) | + FIELD_PREP(APPLE_SPI_CFG_MODE, APPLE_SPI_CFG_MODE_IRQ) | + FIELD_PREP(APPLE_SPI_CFG_WORD_SIZE, APPLE_SPI_CFG_WORD_SIZE_8B)); + + /* Disable IRQs */ + reg_write(spi, APPLE_SPI_IE_FIFO, 0); + reg_write(spi, APPLE_SPI_IE_XFER, 0); + + /* Disable delays */ + reg_write(spi, APPLE_SPI_DELAY_PRE, 0); + reg_write(spi, APPLE_SPI_DELAY_POST, 0); +} + +static int apple_spi_prepare_message(struct spi_controller *ctlr, struct spi_message *msg) +{ + struct apple_spi *spi = spi_controller_get_devdata(ctlr); + struct spi_device *device = msg->spi; + + u32 cfg = ((device->mode & SPI_CPHA ? APPLE_SPI_CFG_CPHA : 0) | + (device->mode & SPI_CPOL ? APPLE_SPI_CFG_CPOL : 0) | + (device->mode & SPI_LSB_FIRST ? APPLE_SPI_CFG_LSB_FIRST : 0)); + + /* Update core config */ + reg_mask(spi, APPLE_SPI_CFG, + APPLE_SPI_CFG_CPHA | APPLE_SPI_CFG_CPOL | APPLE_SPI_CFG_LSB_FIRST, cfg); + + return 0; +} + +static void apple_spi_set_cs(struct spi_device *device, bool is_high) +{ + struct apple_spi *spi = spi_controller_get_devdata(device->controller); + + reg_mask(spi, APPLE_SPI_PIN, APPLE_SPI_PIN_CS, is_high ? APPLE_SPI_PIN_CS : 0); +} + +static bool apple_spi_prep_transfer(struct apple_spi *spi, struct spi_transfer *t) +{ + u32 cr, fifo_threshold; + + /* Calculate and program the clock rate */ + cr = DIV_ROUND_UP(clk_get_rate(spi->clk), t->speed_hz); + reg_write(spi, APPLE_SPI_CLKDIV, min_t(u32, cr, APPLE_SPI_CLKDIV_MAX)); + + /* Update bits per word */ + reg_mask(spi, APPLE_SPI_SHIFTCFG, APPLE_SPI_SHIFTCFG_BITS, + FIELD_PREP(APPLE_SPI_SHIFTCFG_BITS, t->bits_per_word)); + + /* We will want to poll if the time we need to wait is + * less than the context switching time. + * Let's call that threshold 5us. The operation will take: + * bits_per_word * fifo_threshold / hz <= 5 * 10^-6 + * 200000 * bits_per_word * fifo_threshold <= hz + */ + fifo_threshold = APPLE_SPI_FIFO_DEPTH / 2; + return (200000 * t->bits_per_word * fifo_threshold) <= t->speed_hz; +} + +static irqreturn_t apple_spi_irq(int irq, void *dev_id) +{ + struct apple_spi *spi = dev_id; + u32 fifo = reg_read(spi, APPLE_SPI_IF_FIFO) & reg_read(spi, APPLE_SPI_IE_FIFO); + u32 xfer = reg_read(spi, APPLE_SPI_IF_XFER) & reg_read(spi, APPLE_SPI_IE_XFER); + + if (fifo || xfer) { + /* Disable interrupts until next transfer */ + reg_write(spi, APPLE_SPI_IE_XFER, 0); + reg_write(spi, APPLE_SPI_IE_FIFO, 0); + complete(&spi->done); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int apple_spi_wait(struct apple_spi *spi, u32 fifo_bit, u32 xfer_bit, int poll) +{ + int ret = 0; + + if (poll) { + u32 fifo, xfer; + unsigned long timeout = jiffies + APPLE_SPI_TIMEOUT_MS * HZ / 1000; + + do { + fifo = reg_read(spi, APPLE_SPI_IF_FIFO); + xfer = reg_read(spi, APPLE_SPI_IF_XFER); + if (time_after(jiffies, timeout)) { + ret = -ETIMEDOUT; + break; + } + } while (!((fifo & fifo_bit) || (xfer & xfer_bit))); + } else { + reinit_completion(&spi->done); + reg_write(spi, APPLE_SPI_IE_XFER, xfer_bit); + reg_write(spi, APPLE_SPI_IE_FIFO, fifo_bit); + + if (!wait_for_completion_timeout(&spi->done, + msecs_to_jiffies(APPLE_SPI_TIMEOUT_MS))) + ret = -ETIMEDOUT; + + reg_write(spi, APPLE_SPI_IE_XFER, 0); + reg_write(spi, APPLE_SPI_IE_FIFO, 0); + } + + return ret; +} + +static void apple_spi_tx(struct apple_spi *spi, const void **tx_ptr, u32 *left, + unsigned int bytes_per_word) +{ + u32 inuse, words, wrote; + + if (!*tx_ptr) + return; + + inuse = FIELD_GET(APPLE_SPI_FIFOSTAT_LEVEL_TX, reg_read(spi, APPLE_SPI_FIFOSTAT)); + words = wrote = min_t(u32, *left, APPLE_SPI_FIFO_DEPTH - inuse); + + if (!words) + return; + + *left -= words; + + switch (bytes_per_word) { + case 1: { + const u8 *p = *tx_ptr; + + while (words--) + reg_write(spi, APPLE_SPI_TXDATA, *p++); + break; + } + case 2: { + const u16 *p = *tx_ptr; + + while (words--) + reg_write(spi, APPLE_SPI_TXDATA, *p++); + break; + } + case 4: { + const u32 *p = *tx_ptr; + + while (words--) + reg_write(spi, APPLE_SPI_TXDATA, *p++); + break; + } + default: + WARN_ON(1); + } + + *tx_ptr = ((u8 *)*tx_ptr) + bytes_per_word * wrote; +} + +static void apple_spi_rx(struct apple_spi *spi, void **rx_ptr, u32 *left, + unsigned int bytes_per_word) +{ + u32 words, read; + + if (!*rx_ptr) + return; + + words = read = FIELD_GET(APPLE_SPI_FIFOSTAT_LEVEL_RX, reg_read(spi, APPLE_SPI_FIFOSTAT)); + WARN_ON(words > *left); + + if (!words) + return; + + *left -= min_t(u32, *left, words); + + switch (bytes_per_word) { + case 1: { + u8 *p = *rx_ptr; + + while (words--) + *p++ = reg_read(spi, APPLE_SPI_RXDATA); + break; + } + case 2: { + u16 *p = *rx_ptr; + + while (words--) + *p++ = reg_read(spi, APPLE_SPI_RXDATA); + break; + } + case 4: { + u32 *p = *rx_ptr; + + while (words--) + *p++ = reg_read(spi, APPLE_SPI_RXDATA); + break; + } + default: + WARN_ON(1); + } + + *rx_ptr = ((u8 *)*rx_ptr) + bytes_per_word * read; +} + +static int apple_spi_transfer_one(struct spi_controller *ctlr, struct spi_device *device, + struct spi_transfer *t) +{ + struct apple_spi *spi = spi_controller_get_devdata(ctlr); + bool poll = apple_spi_prep_transfer(spi, t); + const void *tx_ptr = t->tx_buf; + void *rx_ptr = t->rx_buf; + unsigned int bytes_per_word; + u32 words, remaining_tx, remaining_rx; + u32 xfer_flags = 0; + u32 fifo_flags; + int retries = 100; + int ret = 0; + + if (t->bits_per_word > 16) + bytes_per_word = 4; + else if (t->bits_per_word > 8) + bytes_per_word = 2; + else + bytes_per_word = 1; + + words = t->len / bytes_per_word; + remaining_tx = tx_ptr ? words : 0; + remaining_rx = rx_ptr ? words : 0; + + /* Reset FIFOs */ + reg_write(spi, APPLE_SPI_CTRL, APPLE_SPI_CTRL_RX_RESET | APPLE_SPI_CTRL_TX_RESET); + + /* Clear IRQ flags */ + reg_write(spi, APPLE_SPI_IF_XFER, ~0); + reg_write(spi, APPLE_SPI_IF_FIFO, ~0); + + /* Determine transfer completion flags we wait for */ + if (tx_ptr) + xfer_flags |= APPLE_SPI_XFER_TXCOMPLETE; + if (rx_ptr) + xfer_flags |= APPLE_SPI_XFER_RXCOMPLETE; + + /* Set transfer length */ + reg_write(spi, APPLE_SPI_TXCNT, remaining_tx); + reg_write(spi, APPLE_SPI_RXCNT, remaining_rx); + + /* Prime transmit FIFO */ + apple_spi_tx(spi, &tx_ptr, &remaining_tx, bytes_per_word); + + /* Start transfer */ + reg_write(spi, APPLE_SPI_CTRL, APPLE_SPI_CTRL_RUN); + + /* TX again since a few words get popped off immediately */ + apple_spi_tx(spi, &tx_ptr, &remaining_tx, bytes_per_word); + + while (xfer_flags) { + fifo_flags = 0; + + if (remaining_tx) + fifo_flags |= APPLE_SPI_FIFO_TXTHRESH; + if (remaining_rx) + fifo_flags |= APPLE_SPI_FIFO_RXTHRESH; + + /* Wait for anything to happen */ + ret = apple_spi_wait(spi, fifo_flags, xfer_flags, poll); + if (ret) { + dev_err(&ctlr->dev, "transfer timed out (remaining %d tx, %d rx)\n", + remaining_tx, remaining_rx); + goto err; + } + + /* Stop waiting on transfer halves once they complete */ + xfer_flags &= ~reg_read(spi, APPLE_SPI_IF_XFER); + + /* Transmit and receive everything we can */ + apple_spi_tx(spi, &tx_ptr, &remaining_tx, bytes_per_word); + apple_spi_rx(spi, &rx_ptr, &remaining_rx, bytes_per_word); + } + + /* + * Sometimes the transfer completes before the last word is in the RX FIFO. + * Normally one retry is all it takes to get the last word out. + */ + while (remaining_rx && retries--) + apple_spi_rx(spi, &rx_ptr, &remaining_rx, bytes_per_word); + + if (remaining_tx) + dev_err(&ctlr->dev, "transfer completed with %d words left to transmit\n", + remaining_tx); + if (remaining_rx) + dev_err(&ctlr->dev, "transfer completed with %d words left to receive\n", + remaining_rx); + +err: + fifo_flags = reg_read(spi, APPLE_SPI_IF_FIFO); + WARN_ON(fifo_flags & APPLE_SPI_FIFO_TXOVERFLOW); + WARN_ON(fifo_flags & APPLE_SPI_FIFO_RXUNDERRUN); + + /* Stop transfer */ + reg_write(spi, APPLE_SPI_CTRL, 0); + + return ret; +} + +static void apple_spi_clk_disable_unprepare(void *data) +{ + clk_disable_unprepare(data); +} + +static int apple_spi_probe(struct platform_device *pdev) +{ + struct apple_spi *spi; + int ret, irq; + struct spi_controller *ctlr; + + ctlr = devm_spi_alloc_master(&pdev->dev, sizeof(struct apple_spi)); + if (!ctlr) + return dev_err_probe(&pdev->dev, -ENOMEM, "out of memory\n"); + + spi = spi_controller_get_devdata(ctlr); + init_completion(&spi->done); + platform_set_drvdata(pdev, ctlr); + + spi->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(spi->regs)) + return PTR_ERR(spi->regs); + + spi->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(spi->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(spi->clk), "Unable to find bus clock\n"); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, apple_spi_irq, 0, + dev_name(&pdev->dev), spi); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Unable to bind to interrupt\n"); + + ret = clk_prepare_enable(spi->clk); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Unable to enable bus clock\n"); + + ret = devm_add_action_or_reset(&pdev->dev, apple_spi_clk_disable_unprepare, spi->clk); + if (ret) + return ret; + + ctlr->dev.of_node = pdev->dev.of_node; + ctlr->bus_num = pdev->id; + ctlr->num_chipselect = 1; + ctlr->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST; + ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); + ctlr->flags = 0; + ctlr->prepare_message = apple_spi_prepare_message; + ctlr->set_cs = apple_spi_set_cs; + ctlr->transfer_one = apple_spi_transfer_one; + ctlr->auto_runtime_pm = true; + + pm_runtime_set_active(&pdev->dev); + devm_pm_runtime_enable(&pdev->dev); + + apple_spi_init(spi); + + ret = devm_spi_register_controller(&pdev->dev, ctlr); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "devm_spi_register_controller failed\n"); + + return 0; +} + +static const struct of_device_id apple_spi_of_match[] = { + { .compatible = "apple,spi", }, + {} +}; +MODULE_DEVICE_TABLE(of, apple_spi_of_match); + +static struct platform_driver apple_spi_driver = { + .probe = apple_spi_probe, + .driver = { + .name = "apple-spi", + .owner = THIS_MODULE, + .of_match_table = apple_spi_of_match, + }, +}; +module_platform_driver(apple_spi_driver); + +MODULE_AUTHOR("Hector Martin "); +MODULE_DESCRIPTION("Apple SoC SPI driver"); +MODULE_LICENSE("GPL"); From 5c07cc445fcd2f455c718be8dd613063e83ab256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 19 Aug 2022 18:09:39 +0200 Subject: [PATCH 0230/1020] Revert "ASoC: ops: Don't modify the driver's plaform_max when reading state" This reverts commit 30ac49841386f933339817771ec315a34a4c0edd. --- sound/soc/soc-ops.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index b27e89ff6a1673..48193c7cbe5baa 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -176,28 +176,20 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - const char *vol_string = NULL; - int max; + int platform_max; - max = uinfo->value.integer.max = mc->max - mc->min; - if (mc->platform_max && mc->platform_max < max) - max = mc->platform_max; + if (!mc->platform_max) + mc->platform_max = mc->max; + platform_max = mc->platform_max; - if (max == 1) { - /* Even two value controls ending in Volume should always be integer */ - vol_string = strstr(kcontrol->id.name, " Volume"); - if (vol_string && !strcmp(vol_string, " Volume")) - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - else - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - } else { + if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume")) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - } uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1; uinfo->value.integer.min = 0; - uinfo->value.integer.max = max; - + uinfo->value.integer.max = platform_max - mc->min; return 0; } EXPORT_SYMBOL_GPL(snd_soc_info_volsw); From 4884adc2efee99064115abe62b3aa61c93c15f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 11 Mar 2022 11:55:44 +0100 Subject: [PATCH 0231/1020] ASoC: tas2764: Extend driver to SN012776 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SN012776 is a speaker amp chip found in Apple's 2021 laptops. It appears similar and more-or-less compatible to TAS2764. Extend the TAS2764 driver with some SN012776 specifics and configure the chip assuming it's in one of the Apple machines. Signed-off-by: Martin Povišer --- sound/soc/codecs/tas2764.c | 50 ++++++++++++++++++++++++++++++++++---- sound/soc/codecs/tas2764.h | 3 +++ 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index a9838e0738cc1b..0a89dc193e38e8 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,11 @@ #include "tas2764.h" +enum tas2764_devid { + DEVID_TAS2764 = 0, + DEVID_SN012776 = 1 +}; + struct tas2764_priv { struct snd_soc_component *component; struct gpio_desc *reset_gpio; @@ -32,7 +38,8 @@ struct tas2764_priv { struct regmap *regmap; struct device *dev; int irq; - + enum tas2764_devid devid; + int v_sense_slot; int i_sense_slot; @@ -528,10 +535,16 @@ static struct snd_soc_dai_driver tas2764_dai_driver[] = { }, }; +static uint8_t sn012776_bop_presets[] = { + 0x01, 0x32, 0x02, 0x22, 0x83, 0x2d, 0x80, 0x02, 0x06, + 0x32, 0x46, 0x30, 0x02, 0x06, 0x38, 0x40, 0x30, 0x02, + 0x06, 0x3e, 0x37, 0x30, 0xff, 0xe6 +}; + static int tas2764_codec_probe(struct snd_soc_component *component) { struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); - int ret; + int ret, i; tas2764->component = component; @@ -580,6 +593,23 @@ static int tas2764_codec_probe(struct snd_soc_component *component) if (ret < 0) return ret; + if (tas2764->devid == DEVID_SN012776) { + ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, + TAS2764_PWR_CTRL_BOP_SRC, + TAS2764_PWR_CTRL_BOP_SRC); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(sn012776_bop_presets); i++) { + ret = snd_soc_component_write(component, + TAS2764_BOP_CFG0 + i, + sn012776_bop_presets[i]); + + if (ret < 0) + return ret; + } + } + return 0; } @@ -699,9 +729,12 @@ static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764) return 0; } +static const struct of_device_id tas2764_of_match[]; + static int tas2764_i2c_probe(struct i2c_client *client) { struct tas2764_priv *tas2764; + const struct of_device_id *of_id = NULL; int result; tas2764 = devm_kzalloc(&client->dev, sizeof(struct tas2764_priv), @@ -709,6 +742,14 @@ static int tas2764_i2c_probe(struct i2c_client *client) if (!tas2764) return -ENOMEM; + if (client->dev.of_node) + of_id = of_match_device(tas2764_of_match, &client->dev); + + if (of_id) + tas2764->devid = (enum tas2764_devid) of_id->data; + else + tas2764->devid = DEVID_TAS2764; + tas2764->dev = &client->dev; tas2764->irq = client->irq; i2c_set_clientdata(client, tas2764); @@ -743,13 +784,12 @@ static const struct i2c_device_id tas2764_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, tas2764_i2c_id); -#if defined(CONFIG_OF) static const struct of_device_id tas2764_of_match[] = { - { .compatible = "ti,tas2764" }, + { .compatible = "ti,tas2764", .data = (void*) DEVID_TAS2764 }, + { .compatible = "ti,sn012776", .data = (void*) DEVID_SN012776 }, {}, }; MODULE_DEVICE_TABLE(of, tas2764_of_match); -#endif static struct i2c_driver tas2764_i2c_driver = { .driver = { diff --git a/sound/soc/codecs/tas2764.h b/sound/soc/codecs/tas2764.h index 168af772a898ff..0a40166040e7e8 100644 --- a/sound/soc/codecs/tas2764.h +++ b/sound/soc/codecs/tas2764.h @@ -29,6 +29,7 @@ #define TAS2764_PWR_CTRL_ACTIVE 0x0 #define TAS2764_PWR_CTRL_MUTE BIT(0) #define TAS2764_PWR_CTRL_SHUTDOWN BIT(1) +#define TAS2764_PWR_CTRL_BOP_SRC BIT(7) #define TAS2764_VSENSE_POWER_EN 3 #define TAS2764_ISENSE_POWER_EN 4 @@ -110,4 +111,6 @@ #define TAS2764_INT_CLK_CFG TAS2764_REG(0x0, 0x5c) #define TAS2764_INT_CLK_CFG_IRQZ_CLR BIT(2) +#define TAS2764_BOP_CFG0 TAS2764_REG(0X0, 0x1d) + #endif /* __TAS2764__ */ From d198d1a89a67c8f9ee3382238e363e248f7581ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 20 Aug 2022 20:13:05 +0200 Subject: [PATCH 0232/1020] ASoC: tas2764: Add control concerning overcurrent events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add control to expose the option of autoretry behavior on overcurrent events in the codec. Signed-off-by: Martin Povišer --- sound/soc/codecs/tas2764.c | 9 +++++++++ sound/soc/codecs/tas2764.h | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 0a89dc193e38e8..7b9cb764eeb0cf 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -625,12 +625,21 @@ static SOC_ENUM_SINGLE_DECL( tas2764_hpf_enum, TAS2764_DC_BLK0, TAS2764_DC_BLK0_HPF_FREQ_PB_SHIFT, tas2764_hpf_texts); +static const char * const tas2764_oce_texts[] = { + "Disable", "Retry", +}; + +static SOC_ENUM_SINGLE_DECL( + tas2764_oce_enum, TAS2764_MISC_CFG1, + TAS2764_MISC_CFG1_OCE_RETRY_SHIFT, tas2764_oce_texts); + static const struct snd_kcontrol_new tas2764_snd_controls[] = { SOC_SINGLE_TLV("Speaker Volume", TAS2764_DVC, 0, TAS2764_DVC_MAX, 1, tas2764_playback_volume), SOC_SINGLE_TLV("Amp Gain Volume", TAS2764_CHNL_0, 1, 0x14, 0, tas2764_digital_tlv), SOC_ENUM("HPF Corner Frequency", tas2764_hpf_enum), + SOC_ENUM("OCE Handling", tas2764_oce_enum), }; static const struct snd_soc_component_driver soc_component_driver_tas2764 = { diff --git a/sound/soc/codecs/tas2764.h b/sound/soc/codecs/tas2764.h index 0a40166040e7e8..20628e51bf94f0 100644 --- a/sound/soc/codecs/tas2764.h +++ b/sound/soc/codecs/tas2764.h @@ -44,6 +44,10 @@ #define TAS2764_CHNL_0 TAS2764_REG(0X0, 0x03) +/* Miscellaneous */ +#define TAS2764_MISC_CFG1 TAS2764_REG(0x0, 0x06) +#define TAS2764_MISC_CFG1_OCE_RETRY_SHIFT 5 + /* TDM Configuration Reg0 */ #define TAS2764_TDM_CFG0 TAS2764_REG(0X0, 0x08) #define TAS2764_TDM_CFG0_SMP_MASK BIT(5) From 0c31976d982f3b760e5739f04f9b4b97f099bb7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 19 Aug 2022 18:38:04 +0200 Subject: [PATCH 0233/1020] ASoC: ops: Move guts out of snd_soc_limit_volume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In advance of other changes, move the modification of the control itself into function of its own. Signed-off-by: Martin Povišer --- sound/soc/soc-ops.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 48193c7cbe5baa..654b8e8dc54ecb 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -631,6 +631,16 @@ int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range); +static int soc_limit_volume(struct snd_kcontrol *kctl, int max) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; + + if (max <= 0 || max > mc->max - mc->min) + return -EINVAL; + mc->platform_max = max; + return 0; +} + /** * snd_soc_limit_volume - Set new limit to an existing volume control. * @@ -644,21 +654,16 @@ int snd_soc_limit_volume(struct snd_soc_card *card, const char *name, int max) { struct snd_kcontrol *kctl; - int ret = -EINVAL; - /* Sanity check for name and max */ - if (unlikely(!name || max <= 0)) + /* Sanity check for name */ + if (unlikely(!name)) return -EINVAL; kctl = snd_soc_card_get_kcontrol(card, name); - if (kctl) { - struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; - if (max <= mc->max - mc->min) { - mc->platform_max = max; - ret = 0; - } - } - return ret; + if (!kctl) + return -EINVAL; + + return soc_limit_volume(kctl, max); } EXPORT_SYMBOL_GPL(snd_soc_limit_volume); From e5b1ec115a37cb61ee832856ce7ad0b906dbce56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 19 Aug 2022 18:58:29 +0200 Subject: [PATCH 0234/1020] ASoC: ops: Enforce platform maximum on initial value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lower the volume if it is violating the platform maximum at its initial value (i.e. at the time of the the 'snd_soc_limit_volume' call). Signed-off-by: Martin Povišer --- sound/soc/soc-ops.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 654b8e8dc54ecb..8f149a2d77b142 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -631,6 +631,33 @@ int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range); +static int soc_clip_to_platform_max(struct snd_kcontrol *kctl) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; + struct snd_ctl_elem_value uctl; + int ret; + + if (!mc->platform_max) + return 0; + + ret = kctl->get(kctl, &uctl); + if (ret < 0) + return ret; + + if (uctl.value.integer.value[0] > mc->platform_max) + uctl.value.integer.value[0] = mc->platform_max; + + if (snd_soc_volsw_is_stereo(mc) && + uctl.value.integer.value[1] > mc->platform_max) + uctl.value.integer.value[1] = mc->platform_max; + + ret = kctl->put(kctl, &uctl); + if (ret < 0) + return ret; + + return 0; +} + static int soc_limit_volume(struct snd_kcontrol *kctl, int max) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; @@ -638,7 +665,8 @@ static int soc_limit_volume(struct snd_kcontrol *kctl, int max) if (max <= 0 || max > mc->max - mc->min) return -EINVAL; mc->platform_max = max; - return 0; + + return soc_clip_to_platform_max(kctl); } /** From 1bfd3895cf5398d4f10ab2b728c26359565f9583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 19 Aug 2022 19:15:54 +0200 Subject: [PATCH 0235/1020] ASoC: ops: Accept patterns in snd_soc_limit_volume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In snd_soc_limit_volume, instead of looking up a single control by name, also understand wildcard-starting patterns like '* Amp Gain Volume' to touch many controls at one. Signed-off-by: Martin Povišer --- sound/soc/soc-ops.c | 51 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 8f149a2d77b142..fe6444306ba8e9 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -631,6 +631,29 @@ int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range); +static bool soc_control_matches(struct snd_kcontrol *kctl, + const char *pattern) +{ + const char *name = kctl->id.name; + + if (pattern[0] == '*') { + int namelen; + int patternlen; + + pattern++; + if (pattern[0] == ' ') + pattern++; + + namelen = strlen(name); + patternlen = strlen(pattern); + + if (namelen > patternlen) + name += namelen - patternlen; + } + + return !strcmp(name, pattern); +} + static int soc_clip_to_platform_max(struct snd_kcontrol *kctl) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; @@ -670,28 +693,44 @@ static int soc_limit_volume(struct snd_kcontrol *kctl, int max) } /** - * snd_soc_limit_volume - Set new limit to an existing volume control. + * snd_soc_limit_volume - Set new limit to existing volume controls * * @card: where to look for the control - * @name: Name of the control + * @name: name pattern * @max: new maximum limit + * + * Finds controls matching the given name (which can be either a name + * verbatim, or a pattern starting with the wildcard '*') and sets + * a platform volume limit on them. * - * Return 0 for success, else error. + * Return number of matching controls on success, else error. At least + * one control needs to match the pattern. */ int snd_soc_limit_volume(struct snd_soc_card *card, const char *name, int max) { struct snd_kcontrol *kctl; + int hits = 0; + int ret; /* Sanity check for name */ if (unlikely(!name)) return -EINVAL; - kctl = snd_soc_card_get_kcontrol(card, name); - if (!kctl) + list_for_each_entry(kctl, &card->snd_card->controls, list) { + if (!soc_control_matches(kctl, name)) + continue; + + ret = soc_limit_volume(kctl, max); + if (ret < 0) + return ret; + hits++; + } + + if (!hits) return -EINVAL; - return soc_limit_volume(kctl, max); + return hits; } EXPORT_SYMBOL_GPL(snd_soc_limit_volume); From a0cb58edd61c0026c8f85e0ecfcc34db542dcc69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 19 Aug 2022 19:24:35 +0200 Subject: [PATCH 0236/1020] ASoC: ops: Introduce 'snd_soc_deactivate_kctl' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new function can be used to deactivate controls -- either a single one or in bulk by pattern. It is something a machine driver may call in fixup_controls. Signed-off-by: Martin Povišer --- include/sound/soc.h | 2 ++ sound/soc/soc-ops.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/include/sound/soc.h b/include/sound/soc.h index 39613b406b1d0e..3b19f9a5795874 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -587,6 +587,8 @@ int snd_soc_get_volsw_range(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 fe6444306ba8e9..91fd8c39e6e67f 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -734,6 +734,44 @@ int snd_soc_limit_volume(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_limit_volume); +/** + * snd_soc_deactivate_kctl - Activate/deactive controls matching a pattern + * + * @card: where to look for the controls + * @name: name pattern + * @active: non-zero to activate, zero to deactivate + * + * Return number of matching controls on success, else error. + * No controls need to match. + */ +int snd_soc_deactivate_kctl(struct snd_soc_card *card, + const char *name, int active) +{ + struct snd_kcontrol *kctl; + int hits = 0; + int ret; + + /* Sanity check for name */ + if (unlikely(!name)) + return -EINVAL; + + list_for_each_entry(kctl, &card->snd_card->controls, list) { + if (!soc_control_matches(kctl, name)) + continue; + + ret = snd_ctl_activate_id(card->snd_card, &kctl->id, active); + if (ret < 0) + return ret; + hits++; + } + + if (!hits) + return -EINVAL; + + return hits; +} +EXPORT_SYMBOL_GPL(snd_soc_deactivate_kctl); + int snd_soc_bytes_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { From f92776b79d2c98ca92f3166ce3fbf821f57791bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 19 Aug 2022 19:25:36 +0200 Subject: [PATCH 0237/1020] ASoC: ops: Introduce 'soc_set_enum_kctl' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new function is to be used to set enumerated controls to desired values -- either a single control or many controls in bulk by pattern. It is something a machine driver may call in fixup_controls. Signed-off-by: Martin Povišer --- include/sound/soc.h | 2 ++ sound/soc/soc-ops.c | 70 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/include/sound/soc.h b/include/sound/soc.h index 3b19f9a5795874..4c03715df87afa 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -589,6 +589,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 91fd8c39e6e67f..15e88e0e074c94 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -772,6 +772,76 @@ 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 *strval) +{ + struct snd_ctl_elem_value value; + struct snd_ctl_elem_info info; + 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(strval, info.value.enumerated.name)) + break; + } + + if (sel == info.value.enumerated.items) + return -EINVAL; + + for (i = 0; i < info.count; i++) + value.value.enumerated.item[i] = sel; + + return kctl->put(kctl, &value); +} + +/** + * snd_soc_set_enum_kctl - Set enumerated controls matching a pattern + * + * @card: where to look for the controls + * @name: name pattern + * @value: string value to set the controls to + * + * Return number of matching and set controls on success, else error. + * No controls need to match. + */ +int snd_soc_set_enum_kctl(struct snd_soc_card *card, + const char *name, const char *value) +{ + struct snd_kcontrol *kctl; + int hits = 0; + int ret; + + /* Sanity check for name */ + if (unlikely(!name)) + return -EINVAL; + + list_for_each_entry(kctl, &card->snd_card->controls, list) { + if (!soc_control_matches(kctl, name)) + continue; + + ret = soc_set_enum_kctl(kctl, value); + if (ret < 0) + return ret; + hits++; + } + + if (!hits) + return -EINVAL; + + return hits; +} +EXPORT_SYMBOL_GPL(snd_soc_set_enum_kctl); + int snd_soc_bytes_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { From d928a58f9b5edf4256184f012a907f5f01e88bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 19 Aug 2022 21:09:35 +0200 Subject: [PATCH 0238/1020] ASoC: card: Let 'fixup_controls' return errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- include/sound/soc-card.h | 2 +- include/sound/soc.h | 2 +- sound/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 1f4c39922d8250..22682332ec5e55 100644 --- a/include/sound/soc-card.h +++ b/include/sound/soc-card.h @@ -46,7 +46,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); int snd_soc_card_set_bias_level(struct snd_soc_card *card, diff --git a/include/sound/soc.h b/include/sound/soc.h index 4c03715df87afa..536bccfccc91e6 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1009,7 +1009,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 a391066ab20459..4737614e82eb17 100644 --- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c +++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c @@ -1221,7 +1221,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 mt8188_mt6359_priv *priv = soc_card_data->mach_priv; @@ -1244,6 +1244,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 8a2f163da6bc9e..2220c0a2895268 100644 --- a/sound/soc/soc-card.c +++ b/sound/soc/soc-card.c @@ -217,10 +217,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 2ec13d1634b636..181b9fc7a564da 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2295,7 +2295,10 @@ static int snd_soc_bind_card(struct snd_soc_card *card) goto probe_end; snd_soc_dapm_new_widgets(card); - 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) { From b496739e0b84de1bdad367956f4cf70a016779a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 31 Mar 2022 01:16:48 +0200 Subject: [PATCH 0239/1020] dt-bindings: sound: Add Apple Macs sound peripherals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add binding for Apple Silicon Macs' machine-level integration of sound peripherals. Signed-off-by: Martin Povišer --- .../bindings/sound/apple,macaudio.yaml | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/apple,macaudio.yaml diff --git a/Documentation/devicetree/bindings/sound/apple,macaudio.yaml b/Documentation/devicetree/bindings/sound/apple,macaudio.yaml new file mode 100644 index 00000000000000..8fe22dec3015d6 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/apple,macaudio.yaml @@ -0,0 +1,162 @@ +# 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: Apple Silicon Macs integrated sound peripherals + +description: + This binding represents the overall machine-level integration of sound + peripherals on 'Apple Silicon' machines by Apple. + +maintainers: + - Martin Povišer + +properties: + compatible: + items: + - enum: + - apple,j274-macaudio + - apple,j293-macaudio + - apple,j314-macaudio + - const: apple,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 + + additionalProperties: false + + properties: + reg: + maxItems: 1 + + link-name: + description: | + Name for the peripheral, expecting 'Speaker' or 'Speakers' if this is + the speaker array. + $ref: /schemas/types.yaml#/definitions/string + + cpu: + type: object + + properties: + sound-dai: + description: | + DAI list with CPU-side I2S ports involved in this peripheral. + minItems: 1 + maxItems: 2 + + required: + - sound-dai + + codec: + type: object + + 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: + + J293: Left Front, Left Rear, Right Front, Right Rear + J314: Left Woofer 1, Left Tweeter, Left Woofer 2, + Right Woofer 1, Right Tweeter, Right Woofer 2 + + required: + - sound-dai + + required: + - reg + - cpu + - codec + +required: + - compatible + - model + +additionalProperties: false + +examples: + - | + mca: mca@9b600000 { + compatible = "apple,t6000-mca", "apple,mca"; + reg = <0x9b600000 0x10000>, + <0x9b500000 0x20000>; + + clocks = <&nco 0>, <&nco 1>, <&nco 2>, <&nco 3>; + power-domains = <&ps_audio_p>, <&ps_mca0>, <&ps_mca1>, + <&ps_mca2>, <&ps_mca3>; + dmas = <&admac 0>, <&admac 1>, <&admac 2>, <&admac 3>, + <&admac 4>, <&admac 5>, <&admac 6>, <&admac 7>, + <&admac 8>, <&admac 9>, <&admac 10>, <&admac 11>, + <&admac 12>, <&admac 13>, <&admac 14>, <&admac 15>; + dma-names = "tx0a", "rx0a", "tx0b", "rx0b", + "tx1a", "rx1a", "tx1b", "rx1b", + "tx2a", "rx2a", "tx2b", "rx2b", + "tx3a", "rx3a", "tx3b", "rx3b"; + + #sound-dai-cells = <1>; + }; + + sound { + compatible = "apple,j314-macaudio", "apple,macaudio"; + model = "MacBook Pro J314 integrated audio"; + + #address-cells = <1>; + #size-cells = <0>; + + dai-link@0 { + reg = <0>; + link-name = "Speakers"; + + cpu { + sound-dai = <&mca 0>, <&mca 1>; + }; + codec { + sound-dai = <&speaker_left_woof1>, + <&speaker_left_tweet>, + <&speaker_left_woof2>, + <&speaker_right_woof1>, + <&speaker_right_tweet>, + <&speaker_right_woof2>; + }; + }; + + dai-link@1 { + reg = <1>; + link-name = "Headphones Jack"; + + cpu { + sound-dai = <&mca 2>; + }; + codec { + sound-dai = <&jack_codec>; + }; + }; + }; From fcb35dd5818a70510ffb33feef89e1d1984c1d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 19 Feb 2022 09:49:56 +0100 Subject: [PATCH 0240/1020] ASoC: apple: Add macaudio machine driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/apple/Kconfig | 17 + sound/soc/apple/Makefile | 4 + sound/soc/apple/macaudio.c | 923 +++++++++++++++++++++++++++++++++++++ 3 files changed, 944 insertions(+) create mode 100644 sound/soc/apple/macaudio.c diff --git a/sound/soc/apple/Kconfig b/sound/soc/apple/Kconfig index 793f7782e0d721..992e416108be5f 100644 --- a/sound/soc/apple/Kconfig +++ b/sound/soc/apple/Kconfig @@ -6,3 +6,20 @@ config SND_SOC_APPLE_MCA help 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 SND_SIMPLE_CARD_UTILS + select APPLE_ADMAC + select COMMON_CLK_APPLE_NCO + select SND_SOC_TAS2764 + select SND_SOC_TAS2770 + select SND_SOC_CS42L83 + select SND_SOC_CS42L84 + default ARCH_APPLE + 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. diff --git a/sound/soc/apple/Makefile b/sound/soc/apple/Makefile index 7a30bf452817e0..a14b8fc7f349aa 100644 --- a/sound/soc/apple/Makefile +++ b/sound/soc/apple/Makefile @@ -1,3 +1,7 @@ snd-soc-apple-mca-objs := mca.o obj-$(CONFIG_SND_SOC_APPLE_MCA) += snd-soc-apple-mca.o + +snd-soc-macaudio-objs := 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 00000000000000..1e6007bd5336bf --- /dev/null +++ b/sound/soc/apple/macaudio.c @@ -0,0 +1,923 @@ +// 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.) + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +struct macaudio_snd_data { + struct snd_soc_card card; + struct snd_soc_jack jack; + int jack_plugin_state; + + bool has_speakers; + + struct macaudio_link_props { + /* frontend props */ + unsigned int bclk_ratio; + + /* backend props */ + bool is_speakers; + bool is_headphones; + unsigned int tdm_mask; + } *link_props; + + unsigned int speaker_nchans_array[2]; + struct snd_pcm_hw_constraint_list speaker_nchans_list; +}; + +static bool void_warranty; +module_param(void_warranty, bool, 0644); +MODULE_PARM_DESC(void_warranty, "Do not bail if safety is not assured"); + +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())); + +static struct snd_soc_dai_link macaudio_fe_links[] = { + { + .name = "Primary", + .stream_name = "Primary", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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_playback = 1, + .dpcm_merged_rate = 1, + .dpcm_merged_chan = 1, + .dpcm_merged_format = 1, + .dai_fmt = MACAUDIO_DAI_FMT, + SND_SOC_DAILINK_REG(secondary), + }, +}; + +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, + } +}; + +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->dpcm_playback = 1; + link->dpcm_capture = 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 ret; + } + + ret = macaudio_parse_of_component(cpu, be_index, link->cpus); + if (ret) + return ret; + + link->name = link->cpus[0].dai_name; + + 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(dev, "Error parsing card name: %d\n", ret); + return ret; + } + + /* 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) { + dev_err(dev, "missing CPU DAI node at %pOF\n", np); + ret = -EINVAL; + goto err_free; + } + + num_cpus = of_count_phandle_with_args(cpu, "sound-dai", + "#sound-dai-cells"); + + if (num_cpus <= 0) { + dev_err(card->dev, "missing sound-dai property at %pOF\n", cpu); + ret = -EINVAL; + 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; + + card->num_links = num_links; + 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; + + /* 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(card->dev, "missing link name\n"); + goto err_free; + } + + speakers = !strcmp(link_name, "Speaker") + || !strcmp(link_name, "Speakers"); + if (speakers) + ma->has_speakers = 1; + + cpu = of_get_child_by_name(np, "cpu"); + codec = of_get_child_by_name(np, "codec"); + + if (!codec || !cpu) { + dev_err(dev, "missing DAI specifications for '%s'\n", link_name); + ret = -EINVAL; + goto err_free; + } + + num_bes = of_count_phandle_with_args(cpu, "sound-dai", + "#sound-dai-cells"); + if (num_bes <= 0) { + dev_err(card->dev, "missing sound-dai property at %pOF\n", cpu); + ret = -EINVAL; + goto err_free; + } + + num_codecs = of_count_phandle_with_args(codec, "sound-dai", + "#sound-dai-cells"); + if (num_codecs <= 0) { + dev_err(card->dev, "missing sound-dai property at %pOF\n", codec); + ret = -EINVAL; + goto err_free; + } + + if (num_codecs % num_bes != 0) { + dev_err(card->dev, "bad combination of CODEC (%d) and CPU (%d) number at %pOF\n", + num_codecs, num_bes, np); + ret = -EINVAL; + 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); + + /* + * 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++) { + 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; + + 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; + } + + for (i = 0; i < ARRAY_SIZE(macaudio_fe_links); i++) + card->dai_link[i].platforms->of_node = platform; + + 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 snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + int bclk_ratio = macaudio_get_runtime_bclk_ratio(substream); + int i; + + 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 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 const struct snd_soc_ops macaudio_fe_ops = { + .shutdown = macaudio_dpcm_shutdown, + .hw_params = macaudio_dpcm_hw_params, +}; + +static const struct snd_soc_ops macaudio_be_ops = { + .shutdown = macaudio_dpcm_shutdown, + .hw_params = macaudio_dpcm_hw_params, +}; + +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; + } + + 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; + + 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, + }, + { + .pin = "Speaker", + .mask = SND_JACK_HEADPHONE, + .invert = 1, + }, +}; + +static int macaudio_probe(struct snd_soc_card *card) +{ + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + int ret; + + dev_dbg(card->dev, "%s!\n", __func__); + + 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 = "Headphone 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[1]; + 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 Pin Demux"; + } else { + r = &routes[nroutes++]; + r->source = "Jack HP"; + r->sink = "Headphone"; + } + + + 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; + } + } + + return 0; +} + +#define CHECK(call, pattern, value) \ + { \ + int ret = call(card, pattern, value); \ + if (ret < 1 && !void_warranty) { \ + dev_err(card->dev, "%s on '%s': %d\n", #call, pattern, ret); \ + return ret; \ + } \ + dev_dbg(card->dev, "%s on '%s': %d hits\n", #call, pattern, ret); \ + } + + +static int macaudio_j274_fixup_controls(struct snd_soc_card *card) +{ + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + + if (ma->has_speakers) { + CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 14); // 20 set by macOS, this is 3 dB below + } + + return 0; +} + +static int macaudio_j314_fixup_controls(struct snd_soc_card *card) +{ + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + + if (ma->has_speakers) { + CHECK(snd_soc_set_enum_kctl, "* ASI1 Sel", "Left"); + CHECK(snd_soc_deactivate_kctl, "* ASI1 Sel", 0); + CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 9); // 15 set by macOS, this is 3 dB below + CHECK(snd_soc_set_enum_kctl, "* Tweeter HPF Corner Freq", "800 Hz"); + CHECK(snd_soc_deactivate_kctl, "* Tweeter HPF Corner Freq", 0); + + /* + * The speaker amps suffer from spurious overcurrent + * events on their unmute, so enable autoretry. + */ + CHECK(snd_soc_set_enum_kctl, "* OCE Handling", "Retry"); + CHECK(snd_soc_deactivate_kctl, "* OCE Handling", 0); + + /* + * Since we don't set the right slots yet to avoid + * driver conflict on the I2S bus sending ISENSE/VSENSE + * samples from the codecs back to us, disable the + * controls. + */ + CHECK(snd_soc_deactivate_kctl, "* VSENSE Switch", 0); + CHECK(snd_soc_deactivate_kctl, "* ISENSE Switch", 0); + } + + return 0; +} + +static int macaudio_fallback_fixup_controls(struct snd_soc_card *card) +{ + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + + if (ma->has_speakers && !void_warranty) { + dev_err(card->dev, "driver can't assure safety on this model, refusing probe\n"); + return -EINVAL; + } + + return 0; +} + +#undef CHECK + +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("Headphones Playback Mux", macaudio_hp_mux_enum); + +static const char *macaudio_spk_demux_texts[] = { + "Inverse Jack", "Static", +}; + +static SOC_ENUM_SINGLE_DECL(macaudio_spk_demux_enum, + SND_SOC_NOPM, 0, macaudio_spk_demux_texts); + +static const struct snd_kcontrol_new macaudio_spk_demux = + SOC_DAPM_ENUM("Speaker Pin Demux", macaudio_spk_demux_enum); + +static const struct snd_soc_dapm_widget macaudio_snd_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_SPK("Speaker (Static)", 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_DEMUX("Speaker Pin Demux", SND_SOC_NOPM, 0, 0, &macaudio_spk_demux), + + 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("Headphone Capture", NULL, 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_kcontrol_new macaudio_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Speaker (Static)"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +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. + */ + + { "Speaker", "Inverse Jack", "Speaker Pin Demux" }, + { "Speaker (Static)", "Static", "Speaker Pin Demux" }, + + /* Capture paths */ + { "PCM0 RX", NULL, "Headphone Capture" }, +}; + +static const struct of_device_id macaudio_snd_device_id[] = { + { .compatible = "apple,j274-macaudio", .data = macaudio_j274_fixup_controls }, + { .compatible = "apple,j314-macaudio", .data = macaudio_j314_fixup_controls }, + { .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); + + card->owner = THIS_MODULE; + card->driver_name = DRIVER_NAME; + 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) + card->fixup_controls = of_id->data; + else + card->fixup_controls = macaudio_fallback_fixup_controls; + + ret = macaudio_parse_of(data); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed OF parsing\n"); + + 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; + } + } + + return devm_snd_soc_register_card(dev, card); +} + +static struct platform_driver macaudio_snd_driver = { + .probe = macaudio_snd_platform_probe, + .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 "); +MODULE_DESCRIPTION("Apple Silicon Macs machine-level sound driver"); +MODULE_LICENSE("GPL"); From 5341430c2a3fff5429f936dc5250a3654ec53e2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 3 Aug 2022 17:25:43 +0200 Subject: [PATCH 0241/1020] ASoC: cs42l42: Fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/codecs/cs42l42.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 60d366e53526ff..a57387ef13515a 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -1677,7 +1677,7 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data) return IRQ_NONE; } - /* Read sticky registers to clear interurpt */ + /* Read sticky registers to clear interrupt */ for (i = 0; i < ARRAY_SIZE(stickies); i++) { regmap_read(cs42l42->regmap, irq_params_table[i].status_addr, &(stickies[i])); From 04f174ab3f0d120c15d3329e716dea1449278519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 6 Sep 2022 14:51:29 +0200 Subject: [PATCH 0242/1020] ASoC: cs42l42: Do not advertise sample bit symmetry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/codecs/cs42l42.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index a57387ef13515a..762b93feb109fd 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -1149,7 +1149,6 @@ struct snd_soc_dai_driver cs42l42_dai = { .formats = CS42L42_FORMATS, }, .symmetric_rate = 1, - .symmetric_sample_bits = 1, .ops = &cs42l42_ops, }; EXPORT_SYMBOL_NS_GPL(cs42l42_dai, SND_SOC_CS42L42_CORE); From 08efde3869d06c0110fcde50bf81210b6ec06f9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 20 Aug 2022 20:50:24 +0200 Subject: [PATCH 0243/1020] dt-bindings: sound: Add CS42L84 codec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CS42L84 is a headphone jack codec made by Cirrus Logic and seen in Apple computer models starting with 2021 Macbook Pros. It is not a publicly documented part. To a degree the part is similar to the public CS42L42. (The L84 superseded L83 seen in earlier Apple models, and the L83 was pretty much the same as L42.) Signed-off-by: Martin Povišer --- sound/soc/codecs/cirrus,cs42l84.yaml | 60 ++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 sound/soc/codecs/cirrus,cs42l84.yaml diff --git a/sound/soc/codecs/cirrus,cs42l84.yaml b/sound/soc/codecs/cirrus,cs42l84.yaml new file mode 100644 index 00000000000000..12bc6dbeeddfac --- /dev/null +++ b/sound/soc/codecs/cirrus,cs42l84.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/cirrus,cs42l84.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic CS42L84 audio CODEC + +maintainers: + - povik+lin@cutebit.org + +description: + The CS42L84 is a headphone jack codec made by Cirrus Logic and embedded + in personal computers sold by Apple. It was first seen in 2021 Macbook Pro + models. + + It has stereo DAC for playback, mono ADC for capture, and is somewhat + similar to CS42L42 but with a different regmap. + +properties: + compatible: + enum: + - cirrus,cs42l84 + + reg: + description: + I2C address of the device + maxItems: 1 + + reset-gpios: + description: + Reset pin, asserted to reset the device, deasserted to bring + the device online + maxItems: 1 + + interrupts: + description: + Interrupt for the IRQ output line of the device + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + jack_codec: codec@4b { + compatible = "cirrus,cs42l84"; + reg = <0x4b>; + reset-gpios = <&pinctrl_nub 4 GPIO_ACTIVE_LOW>; + interrupts-extended = <&pinctrl_ap 180 IRQ_TYPE_LEVEL_LOW>; + #sound-dai-cells = <0>; + }; + }; From d014ff838dcc4c59862d909a611e6356d952854c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 29 Jun 2022 20:32:14 +0200 Subject: [PATCH 0244/1020] wip: ASoC: cs42l84: Start new codec driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/cs42l84.c | 1045 ++++++++++++++++++++++++++++++++++++ sound/soc/codecs/cs42l84.h | 214 ++++++++ 4 files changed, 1266 insertions(+) create mode 100644 sound/soc/codecs/cs42l84.c create mode 100644 sound/soc/codecs/cs42l84.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index f78ea2f86fa64f..b335e84e3ed4e0 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -83,6 +83,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_CS42L52 imply SND_SOC_CS42L56 imply SND_SOC_CS42L73 + imply SND_SOC_CS42L84 imply SND_SOC_CS4234 imply SND_SOC_CS4265 imply SND_SOC_CS4270 @@ -903,6 +904,10 @@ config SND_SOC_CS42L83 select REGMAP_I2C select SND_SOC_CS42L42_CORE +config SND_SOC_CS42L84 + tristate "Cirrus Logic CS42L84 CODEC" + depends on I2C + config SND_SOC_CS4234 tristate "Cirrus Logic CS4234 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 7c075539dc476c..38aa2c2b384536 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -89,6 +89,7 @@ snd-soc-cs42l52-objs := cs42l52.o snd-soc-cs42l56-objs := cs42l56.o snd-soc-cs42l73-objs := cs42l73.o snd-soc-cs42l83-i2c-objs := cs42l83-i2c.o +snd-soc-cs42l84-objs := cs42l84.o snd-soc-cs4234-objs := cs4234.o snd-soc-cs4265-objs := cs4265.o snd-soc-cs4270-objs := cs4270.o @@ -484,6 +485,7 @@ obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o obj-$(CONFIG_SND_SOC_CS42L56) += snd-soc-cs42l56.o obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o obj-$(CONFIG_SND_SOC_CS42L83) += snd-soc-cs42l83-i2c.o +obj-$(CONFIG_SND_SOC_CS42L84) += snd-soc-cs42l84.o obj-$(CONFIG_SND_SOC_CS4234) += snd-soc-cs4234.o obj-$(CONFIG_SND_SOC_CS4265) += snd-soc-cs4265.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c new file mode 100644 index 00000000000000..38dfee80ed9f73 --- /dev/null +++ b/sound/soc/codecs/cs42l84.c @@ -0,0 +1,1045 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs42l84.c -- CS42L84 ALSA SoC audio driver + * + * Copyright (C) The Asahi Linux Contributors + * + * Based on sound/soc/codecs/cs42l42{.c,.h} + * Copyright 2016 Cirrus Logic, Inc. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs42l84.h" +#include "cirrus_legacy.h" + +struct cs42l84_private { + struct regmap *regmap; + struct device *dev; + struct gpio_desc *reset_gpio; + struct snd_soc_jack *jack; + struct mutex irq_lock; + u8 plug_state; + int pll_config; + int bclk; + u8 pll_mclk_f; + u32 srate; + u8 stream_use; + int hs_type; +}; + +static const struct regmap_config cs42l84_regmap = { + .reg_bits = 16, + .val_bits = 8, + + .max_register = 0xffff, + .cache_type = REGCACHE_NONE, + + .use_single_read = true, + .use_single_write = true, +}; + +static int cs42l84_put_dac_vol(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *val) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kctl); + unsigned int vola, volb; + int ret, ret2; + + vola = val->value.integer.value[0]; + volb = val->value.integer.value[1]; + + ret = snd_soc_component_update_bits(component, CS42L84_FRZ_CTL, + CS42L84_FRZ_CTL_ENGAGE, + CS42L84_FRZ_CTL_ENGAGE); + if (ret < 0) + goto bail; + + ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHA_VOL_LSB, + 0xff, vola & 0xff); + if (ret < 0) + goto bail; + ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHA_VOL_MSB, + 0xff, (vola >> 8) & 0x01); + if (ret < 0) + goto bail; + ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHB_VOL_LSB, + 0xff, volb & 0xff); + if (ret < 0) + goto bail; + ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHB_VOL_MSB, + 0xff, (volb >> 8) & 0x01); + if (ret < 0) + goto bail; + +bail: + ret2 = snd_soc_component_update_bits(component, CS42L84_FRZ_CTL, + CS42L84_FRZ_CTL_ENGAGE, 0); + if (ret2 < 0 && ret >= 0) + ret = ret2; + + return ret; +} + +static int cs42l84_get_dac_vol(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *val) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kctl); + unsigned int vola, volb; + int ret; + + ret = snd_soc_component_read(component, CS42L84_DAC_CHA_VOL_LSB); + if (ret < 0) + return ret; + vola = ret; + + ret = snd_soc_component_read(component, CS42L84_DAC_CHA_VOL_MSB); + if (ret < 0) + return ret; + vola |= (ret & 1) << 8; + + ret = snd_soc_component_read(component, CS42L84_DAC_CHB_VOL_LSB); + if (ret < 0) + return ret; + volb = ret; + + ret = snd_soc_component_read(component, CS42L84_DAC_CHB_VOL_MSB); + if (ret < 0) + return ret; + volb |= (ret & 1) << 8; + + val->value.integer.value[0] = vola; + val->value.integer.value[1] = volb; + + return 0; +} + +/* TODO */ +static const DECLARE_TLV_DB_SCALE(cs42l84_dac_tlv, -25600, 50, 1); + +static const struct snd_kcontrol_new cs42l84_snd_controls[] = { + SOC_DOUBLE_R_EXT_TLV("DAC Playback Volume", CS42L84_DAC_CHA_VOL_LSB, + CS42L84_DAC_CHB_VOL_LSB, 0, 511, 0, + cs42l84_get_dac_vol, cs42l84_put_dac_vol, cs42l84_dac_tlv), + SOC_SINGLE("ADC Preamp Gain", CS42L84_ADC_CTL1, + CS42L84_ADC_CTL1_PREAMP_GAIN_SHIFT, 2, 0), + SOC_SINGLE("ADC PGA Gain", CS42L84_ADC_CTL1, + CS42L84_ADC_CTL1_PGA_GAIN_SHIFT, 31, 0), + SOC_SINGLE("ADC WNF Switch", CS42L84_ADC_CTL4, + CS42L84_ADC_CTL4_WNF_EN_SHIFT, 1, 0), + SOC_SINGLE("WNF Corner Frequency", CS42L84_ADC_CTL4, + CS42L84_ADC_CTL4_WNF_CF_SHIFT, 3, 0), + SOC_SINGLE("ADC HPF Switch", CS42L84_ADC_CTL4, + CS42L84_ADC_CTL4_HPF_EN_SHIFT, 1, 0), + SOC_SINGLE("HPF Corner Frequency", CS42L84_ADC_CTL4, + CS42L84_ADC_CTL4_HPF_CF_SHIFT, 3, 0), +}; + +static const char* const cs42l84_mux_text[] = { + "Blank", "ADC", "ASP RX CH1", "ASP RX CH2", +}; + +static const unsigned int cs42l84_mux_values[] = { + 0b0000, 0b0111, 0b1101, 0b1110, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(cs42l84_daca_mux_enum, + CS42L84_BUS_DAC_SRC, CS42L84_BUS_DAC_SRC_DACA_SHIFT, + 0b1111, cs42l84_mux_text, cs42l84_mux_values); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs42l84_dacb_mux_enum, + CS42L84_BUS_DAC_SRC, CS42L84_BUS_DAC_SRC_DACB_SHIFT, + 0b1111, cs42l84_mux_text, cs42l84_mux_values); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs42l84_sdout1_mux_enum, + CS42L84_BUS_ASP_TX_SRC, CS42L84_BUS_ASP_TX_SRC_CH1_SHIFT, + 0b1111, cs42l84_mux_text, cs42l84_mux_values); + +static const struct snd_kcontrol_new cs42l84_daca_mux_ctrl = + SOC_DAPM_ENUM("DACA Select", cs42l84_daca_mux_enum); + +static const struct snd_kcontrol_new cs42l84_dacb_mux_ctrl = + SOC_DAPM_ENUM("DACB Select", cs42l84_dacb_mux_enum); + +static const struct snd_kcontrol_new cs42l84_sdout1_mux_ctrl = + SOC_DAPM_ENUM("SDOUT1 Select", cs42l84_sdout1_mux_enum); + +static const struct snd_soc_dapm_widget cs42l84_dapm_widgets[] = { + /* Playback Path */ + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_DAC("DAC", NULL, CS42L84_MSM_BLOCK_EN2, CS42L84_MSM_BLOCK_EN2_DAC_SHIFT, 0), + SND_SOC_DAPM_MUX("DACA Select", SND_SOC_NOPM, 0, 0, &cs42l84_daca_mux_ctrl), + SND_SOC_DAPM_MUX("DACB Select", SND_SOC_NOPM, 0, 0, &cs42l84_dacb_mux_ctrl), + SND_SOC_DAPM_AIF_IN("SDIN1", NULL, 0, CS42L84_ASP_RX_EN, CS42L84_ASP_RX_EN_CH1_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("SDIN2", NULL, 1, CS42L84_ASP_RX_EN, CS42L84_ASP_RX_EN_CH2_SHIFT, 0), + + /* Capture Path */ + SND_SOC_DAPM_INPUT("HS"), + SND_SOC_DAPM_ADC("ADC", NULL, CS42L84_MSM_BLOCK_EN2, CS42L84_MSM_BLOCK_EN2_ADC_SHIFT, 0), + SND_SOC_DAPM_MUX("SDOUT1 Select", SND_SOC_NOPM, 0, 0, &cs42l84_sdout1_mux_ctrl), + SND_SOC_DAPM_AIF_OUT("SDOUT1", NULL, 0, CS42L84_ASP_TX_EN, CS42L84_ASP_TX_EN_CH1_SHIFT, 0), + + /* Playback/Capture Requirements */ + SND_SOC_DAPM_SUPPLY("BUS", CS42L84_MSM_BLOCK_EN2, CS42L84_MSM_BLOCK_EN2_BUS_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ASP", CS42L84_MSM_BLOCK_EN2, CS42L84_MSM_BLOCK_EN2_ASP_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BCLK", CS42L84_ASP_CTL, CS42L84_ASP_CTL_BCLK_EN_SHIFT, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route cs42l84_audio_map[] = { + /* Playback Path */ + {"HP", NULL, "DAC"}, + {"DAC", NULL, "DACA Select"}, + {"DAC", NULL, "DACB Select"}, + {"DACA Select", "ASP RX CH1", "SDIN1"}, + {"DACA Select", "ASP RX CH2", "SDIN2"}, + {"DACB Select", "ASP RX CH1", "SDIN1"}, + {"DACB Select", "ASP RX CH2", "SDIN2"}, + {"SDIN1", NULL, "Playback"}, + {"SDIN2", NULL, "Playback"}, + + {"ADC", NULL, "HS"}, + {"SDOUT1 Select", "ADC", "ADC"}, + {"SDOUT1", NULL, "SDOUT1 Select"}, + {"Capture", NULL, "SDOUT1"}, + + /* Playback Requirements */ + {"DAC", NULL, "BUS"}, + {"SDIN1", NULL, "ASP"}, + {"SDIN2", NULL, "ASP"}, + {"SDIN1", NULL, "BCLK"}, + {"SDIN2", NULL, "BCLK"}, + + /* Capture Requirements */ + {"SDOUT1", NULL, "BUS"}, + {"SDOUT1", NULL, "ASP"}, + {"SDOUT1", NULL, "BCLK"}, +}; + +static int cs42l84_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jk, void *d) +{ + struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component); + + /* Prevent race with interrupt handler */ + mutex_lock(&cs42l84->irq_lock); + cs42l84->jack = jk; + snd_soc_jack_report(jk, cs42l84->hs_type, SND_JACK_HEADSET); + mutex_unlock(&cs42l84->irq_lock); + + return 0; +} + +static int cs42l84_component_probe(struct snd_soc_component *component) +{ + snd_soc_component_update_bits(component, CS42L84_ASP_CTL, + CS42L84_ASP_CTL_TDM_MODE, 0); + snd_soc_component_update_bits(component, CS42L84_HP_VOL_CTL, + CS42L84_HP_VOL_CTL_SOFT | CS42L84_HP_VOL_CTL_ZERO_CROSS, + CS42L84_HP_VOL_CTL_ZERO_CROSS); + + /* TDM settings */ + snd_soc_component_update_bits(component, CS42L84_ASP_RX_CH1_CTL1, + CS42L84_ASP_RX_CHx_CTL1_EDGE | + CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB, 0); + snd_soc_component_update_bits(component, CS42L84_ASP_RX_CH1_CTL2, + CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB, 0); + snd_soc_component_update_bits(component, CS42L84_ASP_RX_CH2_CTL1, + CS42L84_ASP_RX_CHx_CTL1_EDGE | + CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB, + CS42L84_ASP_RX_CHx_CTL1_EDGE); + snd_soc_component_update_bits(component, CS42L84_ASP_RX_CH2_CTL2, + CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB, 0); + snd_soc_component_update_bits(component, CS42L84_ASP_TX_CH1_CTL1, + CS42L84_ASP_RX_CHx_CTL1_EDGE | \ + CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB, 0); + snd_soc_component_update_bits(component, CS42L84_ASP_TX_CH1_CTL2, + CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB, 0); + snd_soc_component_update_bits(component, CS42L84_ASP_TX_CH2_CTL1, + CS42L84_ASP_RX_CHx_CTL1_EDGE | \ + CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB, + CS42L84_ASP_RX_CHx_CTL1_EDGE); + snd_soc_component_update_bits(component, CS42L84_ASP_TX_CH2_CTL2, + CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB, 0); + /* Routing defaults */ + snd_soc_component_write(component, CS42L84_BUS_DAC_SRC, + 0b1101 << CS42L84_BUS_DAC_SRC_DACA_SHIFT | + 0b1110 << CS42L84_BUS_DAC_SRC_DACB_SHIFT); + snd_soc_component_write(component, CS42L84_BUS_ASP_TX_SRC, + 0b0111 << CS42L84_BUS_ASP_TX_SRC_CH1_SHIFT); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_cs42l84 = { + .set_jack = cs42l84_set_jack, + .probe = cs42l84_component_probe, + .controls = cs42l84_snd_controls, + .num_controls = ARRAY_SIZE(cs42l84_snd_controls), + .dapm_widgets = cs42l84_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42l84_dapm_widgets), + .dapm_routes = cs42l84_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs42l84_audio_map), + .endianness = 1, +}; + +struct cs42l84_pll_params { + u32 bclk; + u8 mclk_src_sel; + u8 bclk_prediv; + u8 pll_div_int; + u32 pll_div_frac; + u8 pll_mode; + u8 pll_divout; + u32 mclk_int; +}; + +/* + * Common PLL Settings for given BCLK + */ +static const struct cs42l84_pll_params pll_ratio_table[] = { + { 3072000, 1, 0, 0x40, 0x000000, 0x03, 0x10, 12288000}, + { 6144000, 1, 1, 0x40, 0x000000, 0x03, 0x10, 12288000}, + { 12288000, 0, 0, 0, 0, 0, 0, 12288000}, + { 24576000, 1, 3, 0x40, 0x000000, 0x03, 0x10, 12288000}, +}; + +static int cs42l84_pll_config(struct snd_soc_component *component) +{ + struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component); + int i; + u32 clk; + u32 fsync; + + clk = cs42l84->bclk; + + /* Don't reconfigure if there is an audio stream running */ + if (cs42l84->stream_use) { + if (pll_ratio_table[cs42l84->pll_config].bclk == clk) + return 0; + else + return -EBUSY; + } + + for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) { + if (pll_ratio_table[i].bclk == clk) { + cs42l84->pll_config = i; + break; + } + } + + if (i == ARRAY_SIZE(pll_ratio_table)) + return -EINVAL; + + /* Set up the LRCLK */ + fsync = clk / cs42l84->srate; + if (((fsync * cs42l84->srate) != clk) + || ((fsync % 2) != 0)) { + dev_err(component->dev, + "Unsupported bclk %d/sample rate %d\n", + clk, cs42l84->srate); + return -EINVAL; + } + + /* Set the LRCLK period */ + snd_soc_component_update_bits(component, CS42L84_ASP_FSYNC_CTL2, + CS42L84_ASP_FSYNC_CTL2_BCLK_PERIOD_LO, + FIELD_PREP(CS42L84_ASP_FSYNC_CTL2_BCLK_PERIOD_LO, fsync & 0x7f)); + snd_soc_component_update_bits(component, CS42L84_ASP_FSYNC_CTL3, + CS42L84_ASP_FSYNC_CTL3_BCLK_PERIOD_HI, + FIELD_PREP(CS42L84_ASP_FSYNC_CTL3_BCLK_PERIOD_HI, fsync >> 7)); + + /* Save what the MCLK will be */ + switch (pll_ratio_table[i].mclk_int) { + case 12000000: + cs42l84->pll_mclk_f = CS42L84_CCM_CTL1_MCLK_F_12MHZ; + break; + case 12288000: + cs42l84->pll_mclk_f = CS42L84_CCM_CTL1_MCLK_F_12_288KHZ; + break; + case 24000000: + cs42l84->pll_mclk_f = CS42L84_CCM_CTL1_MCLK_F_24MHZ; + break; + case 24576000: + cs42l84->pll_mclk_f = CS42L84_CCM_CTL1_MCLK_F_24_576KHZ; + break; + } + + if (pll_ratio_table[i].mclk_src_sel) { + /* Configure PLL */ + snd_soc_component_update_bits(component, + CS42L84_CCM_CTL3, CS42L84_CCM_CTL3_REFCLK_DIV, + FIELD_PREP(CS42L84_CCM_CTL3_REFCLK_DIV, pll_ratio_table[i].bclk_prediv)); + snd_soc_component_write(component, + CS42L84_PLL_DIV_INT, + pll_ratio_table[i].pll_div_int); + snd_soc_component_write(component, + CS42L84_PLL_DIV_FRAC0, + pll_ratio_table[i].pll_div_frac); + snd_soc_component_write(component, + CS42L84_PLL_DIV_FRAC1, + pll_ratio_table[i].pll_div_frac >> 8); + snd_soc_component_write(component, + CS42L84_PLL_DIV_FRAC2, + pll_ratio_table[i].pll_div_frac >> 16); + snd_soc_component_update_bits(component, + CS42L84_PLL_CTL1, CS42L84_PLL_CTL1_MODE, + FIELD_PREP(CS42L84_PLL_CTL1_MODE, pll_ratio_table[i].pll_mode)); + snd_soc_component_write(component, + CS42L84_PLL_DIVOUT, + pll_ratio_table[i].pll_divout); + + snd_soc_component_update_bits(component, + CS42L84_PLL_CTL1, CS42L84_PLL_CTL1_EN, + CS42L84_PLL_CTL1_EN); + } + + return 0; +} + +static int cs42l84_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_BC_FC: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + default: + return -EINVAL; + } + + /* Bitclock/frame inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cs42l84_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component); + int ret; + u32 ccm_samp_rate; + + cs42l84->srate = params_rate(params); + + ret = cs42l84_pll_config(component); + if (ret) + return ret; + + switch (params_rate(params)) { + case 44100: + ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_44K1HZ; + break; + case 48000: + ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_48KHZ; + break; + case 88200: + ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_88K2HZ; + break; + case 96000: + ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_96KHZ; + break; + case 176400: + ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_176K4HZ; + break; + case 192000: + ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_192KHZ; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, CS42L84_CCM_SAMP_RATE, ccm_samp_rate); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + snd_soc_component_write(component, CS42L84_ASP_RX_CH1_WIDTH, + params_width(params) - 1); + snd_soc_component_write(component, CS42L84_ASP_RX_CH2_WIDTH, + params_width(params) - 1); + break; + + case SNDRV_PCM_STREAM_CAPTURE: + snd_soc_component_write(component, CS42L84_ASP_TX_CH1_WIDTH, + params_width(params) - 1); + snd_soc_component_write(component, CS42L84_ASP_TX_CH2_WIDTH, + params_width(params) - 1); + break; + } + + return 0; +} + +static int cs42l84_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component); + int i; + + if (freq == 0) { + cs42l84->bclk = 0; + return 0; + } + + for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) { + if (pll_ratio_table[i].bclk == freq) { + cs42l84->bclk = freq; + return 0; + } + } + + dev_err(component->dev, "BCLK %u not supported\n", freq); + + return -EINVAL; +} + +static int cs42l84_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component); + unsigned int regval; + int ret; + + if (mute) { + /* Mute the headphone */ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_component_update_bits(component, CS42L84_DAC_CTL1, + CS42L84_DAC_CTL1_UNMUTE, 0); + cs42l84->stream_use &= ~(1 << stream); + if (!cs42l84->stream_use) { + /* Must disconnect PLL before stopping it */ + snd_soc_component_write(component, CS42L84_CCM_CTL1, + CS42L84_CCM_CTL1_RCO); + + usleep_range(150, 300); + + snd_soc_component_update_bits(component, CS42L84_PLL_CTL1, + CS42L84_PLL_CTL1_EN, 0); + + snd_soc_component_update_bits(component, CS42L84_CCM_CTL4, + CS42L84_CCM_CTL4_REFCLK_EN, 0); + } + } else { + if (!cs42l84->stream_use) { + /* SCLK must be running before codec unmute. + * + * Note carried over from CS42L42: + * + * PLL must not be started with ADC and HP both off + * otherwise the FILT+ supply will not charge properly. + * DAPM widgets power-up before stream unmute so at least + * one of the "DAC" or "ADC" widgets will already have + * powered-up. + */ + + snd_soc_component_update_bits(component, CS42L84_CCM_CTL4, + CS42L84_CCM_CTL4_REFCLK_EN, + CS42L84_CCM_CTL4_REFCLK_EN); + + if (pll_ratio_table[cs42l84->pll_config].mclk_src_sel) { + snd_soc_component_update_bits(component, CS42L84_PLL_CTL1, + CS42L84_PLL_CTL1_EN, + CS42L84_PLL_CTL1_EN); + /* TODO: should we be doing something with divout here? */ + + ret = regmap_read_poll_timeout(cs42l84->regmap, + CS42L84_PLL_LOCK_STATUS, + regval, + (regval & CS42L84_PLL_LOCK_STATUS_LOCKED), + CS42L84_PLL_LOCK_POLL_US, + CS42L84_PLL_LOCK_TIMEOUT_US); + if (ret < 0) + dev_warn(component->dev, "PLL failed to lock: %d\n", ret); + + /* PLL must be running to drive glitchless switch logic */ + snd_soc_component_update_bits(component, + CS42L84_CCM_CTL1, + CS42L84_CCM_CTL1_MCLK_SRC | CS42L84_CCM_CTL1_MCLK_FREQ, + FIELD_PREP(CS42L84_CCM_CTL1_MCLK_SRC, CS42L84_CCM_CTL1_MCLK_SRC_PLL) + | FIELD_PREP(CS42L84_CCM_CTL1_MCLK_FREQ, cs42l84->pll_mclk_f)); + usleep_range(CS42L84_CLOCK_SWITCH_DELAY_US, CS42L84_CLOCK_SWITCH_DELAY_US*2); + } else { + snd_soc_component_update_bits(component, + CS42L84_CCM_CTL1, + CS42L84_CCM_CTL1_MCLK_SRC | CS42L84_CCM_CTL1_MCLK_FREQ, + FIELD_PREP(CS42L84_CCM_CTL1_MCLK_SRC, CS42L84_CCM_CTL1_MCLK_SRC_BCLK) + | FIELD_PREP(CS42L84_CCM_CTL1_MCLK_FREQ, cs42l84->pll_mclk_f)); + usleep_range(CS42L84_CLOCK_SWITCH_DELAY_US, CS42L84_CLOCK_SWITCH_DELAY_US*2); + } + } + cs42l84->stream_use |= 1 << stream; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + /* Un-mute the headphone */ + snd_soc_component_update_bits(component, CS42L84_DAC_CTL1, + CS42L84_DAC_CTL1_UNMUTE, + CS42L84_DAC_CTL1_UNMUTE); + } + + return 0; +} + +static const struct snd_soc_dai_ops cs42l84_ops = { + .hw_params = cs42l84_pcm_hw_params, + .set_fmt = cs42l84_set_dai_fmt, + .set_sysclk = cs42l84_set_sysclk, + .mute_stream = cs42l84_mute_stream, +}; + +#define CS42L84_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver cs42l84_dai = { + .name = "cs42l84", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000, + .formats = CS42L84_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000, + .formats = CS42L84_FORMATS, + }, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + .ops = &cs42l84_ops, +}; + +struct cs42l84_irq_params { + u16 status_addr; + u16 mask_addr; + u8 mask; +}; + +static const struct cs42l84_irq_params irq_params_table[] = { + {CS42L84_TSRS_PLUG_INT_STATUS, CS42L84_TSRS_PLUG_INT_MASK, + CS42L84_TSRS_PLUG_VAL_MASK} +}; + +static void cs42l84_detect_hs(struct cs42l84_private *cs42l84) +{ + unsigned int reg; + + /* Power up HSBIAS */ + regmap_update_bits(cs42l84->regmap, + CS42L84_MISC_DET_CTL, + CS42L84_MISC_DET_CTL_HSBIAS_CTL | CS42L84_MISC_DET_CTL_DETECT_MODE, + FIELD_PREP(CS42L84_MISC_DET_CTL_HSBIAS_CTL, 3) | /* 2.7 V */ + FIELD_PREP(CS42L84_MISC_DET_CTL_DETECT_MODE, 0)); + + /* Power up level detection circuitry */ + regmap_update_bits(cs42l84->regmap, + CS42L84_MISC_DET_CTL, + CS42L84_MISC_DET_CTL_PDN_MIC_LVL_DET, 0); + + /* TODO: Optimize */ + msleep(100); + + /* Connect HSBIAS in CTIA wiring */ + /* TODO: Should likely be subject of detection */ + regmap_write(cs42l84->regmap, + CS42L84_HS_SWITCH_CTL, + CS42L84_HS_SWITCH_CTL_REF_HS3 | \ + CS42L84_HS_SWITCH_CTL_HSB_FILT_HS3 | \ + CS42L84_HS_SWITCH_CTL_GNDHS_HS3 | \ + CS42L84_HS_SWITCH_CTL_HSB_HS4); + regmap_update_bits(cs42l84->regmap, + CS42L84_HS_DET_CTL2, + CS42L84_HS_DET_CTL2_SET, + FIELD_PREP(CS42L84_HS_DET_CTL2_SET, 0)); + + regmap_update_bits(cs42l84->regmap, + CS42L84_MISC_DET_CTL, + CS42L84_MISC_DET_CTL_DETECT_MODE, + FIELD_PREP(CS42L84_MISC_DET_CTL_DETECT_MODE, 3)); + + /* TODO: Optimize */ + msleep(100); + + regmap_read(cs42l84->regmap, CS42L84_HS_DET_STATUS2, ®); + regmap_update_bits(cs42l84->regmap, + CS42L84_MISC_DET_CTL, + CS42L84_MISC_DET_CTL_PDN_MIC_LVL_DET, + CS42L84_MISC_DET_CTL_PDN_MIC_LVL_DET); + + switch (reg & 0b11) { + case 0b11: /* shorted */ + case 0b00: /* open */ + /* Power down HSBIAS */ + regmap_update_bits(cs42l84->regmap, + CS42L84_MISC_DET_CTL, + CS42L84_MISC_DET_CTL_HSBIAS_CTL, + FIELD_PREP(CS42L84_MISC_DET_CTL_HSBIAS_CTL, 1)); /* 0.0 V */ + break; + } + + switch (reg & 0b11) { + case 0b10: /* load */ + dev_dbg(cs42l84->dev, "Detected mic\n"); + cs42l84->hs_type = SND_JACK_HEADSET; + snd_soc_jack_report(cs42l84->jack, SND_JACK_HEADSET, + SND_JACK_HEADSET); + break; + + case 0b00: /* open */ + dev_dbg(cs42l84->dev, "Detected line-in\n"); + cs42l84->hs_type = SND_JACK_HEADSET; + snd_soc_jack_report(cs42l84->jack, SND_JACK_HEADSET, + SND_JACK_HEADSET); + break; + + case 0b11: /* shorted */ + default: + snd_soc_jack_report(cs42l84->jack, SND_JACK_HEADPHONE, + SND_JACK_HEADSET); + cs42l84->hs_type = SND_JACK_HEADPHONE; + dev_dbg(cs42l84->dev, "Detected bare headphone (no mic)\n"); + } +} + +static void cs42l84_revert_hs(struct cs42l84_private *cs42l84) +{ + /* Power down HSBIAS */ + regmap_update_bits(cs42l84->regmap, + CS42L84_MISC_DET_CTL, + CS42L84_MISC_DET_CTL_HSBIAS_CTL | CS42L84_MISC_DET_CTL_DETECT_MODE, + FIELD_PREP(CS42L84_MISC_DET_CTL_HSBIAS_CTL, 1) | /* 0.0 V */ + FIELD_PREP(CS42L84_MISC_DET_CTL_DETECT_MODE, 0)); + + /* Disconnect HSBIAS */ + regmap_write(cs42l84->regmap, + CS42L84_HS_SWITCH_CTL, + CS42L84_HS_SWITCH_CTL_REF_HS3 | \ + CS42L84_HS_SWITCH_CTL_REF_HS4 | \ + CS42L84_HS_SWITCH_CTL_HSB_FILT_HS3 | \ + CS42L84_HS_SWITCH_CTL_HSB_FILT_HS4 | \ + CS42L84_HS_SWITCH_CTL_GNDHS_HS3 | \ + CS42L84_HS_SWITCH_CTL_GNDHS_HS4); + regmap_update_bits(cs42l84->regmap, + CS42L84_HS_DET_CTL2, + CS42L84_HS_DET_CTL2_SET, + FIELD_PREP(CS42L84_HS_DET_CTL2_SET, 2)); +} + +static irqreturn_t cs42l84_irq_thread(int irq, void *data) +{ + struct cs42l84_private *cs42l84 = (struct cs42l84_private *)data; + unsigned int stickies[1]; + unsigned int masks[1]; + unsigned int reg; + u8 current_plug_status; + int i; + + mutex_lock(&cs42l84->irq_lock); + /* Read sticky registers to clear interrupt */ + for (i = 0; i < ARRAY_SIZE(stickies); i++) { + regmap_read(cs42l84->regmap, irq_params_table[i].status_addr, + &(stickies[i])); + regmap_read(cs42l84->regmap, irq_params_table[i].mask_addr, + &(masks[i])); + stickies[i] = stickies[i] & (~masks[i]) & + irq_params_table[i].mask; + } + + if ((~masks[0]) & irq_params_table[0].mask) { + regmap_read(cs42l84->regmap, CS42L84_TSRS_PLUG_STATUS, ®); + current_plug_status = (((char) reg) & + (CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >> + CS42L84_TS_PLUG_SHIFT; + + switch (current_plug_status) { + case CS42L84_PLUG: + if (cs42l84->plug_state != CS42L84_PLUG) { + cs42l84->plug_state = CS42L84_PLUG; + dev_dbg(cs42l84->dev, "Plug event\n"); + + cs42l84_detect_hs(cs42l84); + + /* + * Check the tip sense status again, and possibly invalidate + * the detection result + * + * Thanks to debounce, this should reliably indicate if the tip + * was disconnected at any point during the detection procedure. + */ + regmap_read(cs42l84->regmap, CS42L84_TSRS_PLUG_STATUS, ®); + current_plug_status = (((char) reg) & + (CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >> + CS42L84_TS_PLUG_SHIFT; + if (current_plug_status != CS42L84_PLUG) { + dev_dbg(cs42l84->dev, "Wobbly connection, detection invalidated\n"); + cs42l84->plug_state = CS42L84_UNPLUG; + cs42l84_revert_hs(cs42l84); + } + } + break; + + case CS42L84_UNPLUG: + if (cs42l84->plug_state != CS42L84_UNPLUG) { + cs42l84->plug_state = CS42L84_UNPLUG; + dev_dbg(cs42l84->dev, "Unplug event\n"); + + cs42l84_revert_hs(cs42l84); + cs42l84->hs_type = 0; + snd_soc_jack_report(cs42l84->jack, 0, + SND_JACK_HEADSET); + } + break; + + default: + if (cs42l84->plug_state != CS42L84_TRANS) + cs42l84->plug_state = CS42L84_TRANS; + } + } + mutex_unlock(&cs42l84->irq_lock); + + return IRQ_HANDLED; +} + +static void cs42l84_set_interrupt_masks(struct cs42l84_private *cs42l84) +{ + regmap_update_bits(cs42l84->regmap, CS42L84_TSRS_PLUG_INT_MASK, + CS42L84_RS_PLUG | CS42L84_RS_UNPLUG | + CS42L84_TS_PLUG | CS42L84_TS_UNPLUG, + CS42L84_RS_PLUG | CS42L84_RS_UNPLUG); +} + +static void cs42l84_setup_plug_detect(struct cs42l84_private *cs42l84) +{ + unsigned int reg; + + /* Set up plug detection */ + regmap_update_bits(cs42l84->regmap, CS42L84_MIC_DET_CTL4, + CS42L84_MIC_DET_CTL4_LATCH_TO_VP, + CS42L84_MIC_DET_CTL4_LATCH_TO_VP); + regmap_update_bits(cs42l84->regmap, CS42L84_TIP_SENSE_CTL2, + CS42L84_TIP_SENSE_CTL2_MODE, + FIELD_PREP(CS42L84_TIP_SENSE_CTL2_MODE, CS42L84_TIP_SENSE_CTL2_MODE_SHORT_DET)); + regmap_update_bits(cs42l84->regmap, CS42L84_RING_SENSE_CTL, + CS42L84_RING_SENSE_CTL_INV | CS42L84_RING_SENSE_CTL_UNK1 | + CS42L84_RING_SENSE_CTL_RISETIME | CS42L84_RING_SENSE_CTL_FALLTIME, + CS42L84_RING_SENSE_CTL_INV | CS42L84_RING_SENSE_CTL_UNK1 | + FIELD_PREP(CS42L84_RING_SENSE_CTL_RISETIME, CS42L84_DEBOUNCE_TIME_125MS) | + FIELD_PREP(CS42L84_RING_SENSE_CTL_FALLTIME, CS42L84_DEBOUNCE_TIME_125MS)); + regmap_update_bits(cs42l84->regmap, CS42L84_TIP_SENSE_CTL, + CS42L84_TIP_SENSE_CTL_INV | + CS42L84_TIP_SENSE_CTL_RISETIME | CS42L84_TIP_SENSE_CTL_FALLTIME, + CS42L84_TIP_SENSE_CTL_INV | + FIELD_PREP(CS42L84_TIP_SENSE_CTL_RISETIME, CS42L84_DEBOUNCE_TIME_500MS) | + FIELD_PREP(CS42L84_TIP_SENSE_CTL_FALLTIME, CS42L84_DEBOUNCE_TIME_125MS)); + regmap_update_bits(cs42l84->regmap, CS42L84_MSM_BLOCK_EN3, + CS42L84_MSM_BLOCK_EN3_TR_SENSE, + CS42L84_MSM_BLOCK_EN3_TR_SENSE); + + /* Save the initial status of the tip sense */ + regmap_read(cs42l84->regmap, CS42L84_TSRS_PLUG_STATUS, ®); + cs42l84->plug_state = (((char) reg) & + (CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >> + CS42L84_TS_PLUG_SHIFT; + + /* Set up mic detection */ + + /* Disconnect HSBIAS (initially) */ + regmap_write(cs42l84->regmap, + CS42L84_HS_SWITCH_CTL, + CS42L84_HS_SWITCH_CTL_REF_HS3 | \ + CS42L84_HS_SWITCH_CTL_REF_HS4 | \ + CS42L84_HS_SWITCH_CTL_HSB_FILT_HS3 | \ + CS42L84_HS_SWITCH_CTL_HSB_FILT_HS4 | \ + CS42L84_HS_SWITCH_CTL_GNDHS_HS3 | \ + CS42L84_HS_SWITCH_CTL_GNDHS_HS4); + regmap_update_bits(cs42l84->regmap, + CS42L84_HS_DET_CTL2, + CS42L84_HS_DET_CTL2_SET | CS42L84_HS_DET_CTL2_CTL, + FIELD_PREP(CS42L84_HS_DET_CTL2_SET, 2) | + FIELD_PREP(CS42L84_HS_DET_CTL2_CTL, 0)); + regmap_update_bits(cs42l84->regmap, + CS42L84_HS_CLAMP_DISABLE, 1, 1); + +} + +static int cs42l84_i2c_probe(struct i2c_client *i2c_client) +{ + struct cs42l84_private *cs42l84; + int ret, devid; + unsigned int reg; + + cs42l84 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l84_private), + GFP_KERNEL); + if (!cs42l84) + return -ENOMEM; + + cs42l84->dev = &i2c_client->dev; + i2c_set_clientdata(i2c_client, cs42l84); + mutex_init(&cs42l84->irq_lock); + + cs42l84->regmap = devm_regmap_init_i2c(i2c_client, &cs42l84_regmap); + if (IS_ERR(cs42l84->regmap)) { + ret = PTR_ERR(cs42l84->regmap); + dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + /* Reset the Device */ + cs42l84->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(cs42l84->reset_gpio)) { + ret = PTR_ERR(cs42l84->reset_gpio); + goto err_disable_noreset; + } + + if (cs42l84->reset_gpio) { + dev_dbg(&i2c_client->dev, "Found reset GPIO\n"); + gpiod_set_value_cansleep(cs42l84->reset_gpio, 1); + } + usleep_range(CS42L84_BOOT_TIME_US, CS42L84_BOOT_TIME_US * 2); + + /* Request IRQ if one was specified */ + if (i2c_client->irq) { + ret = request_threaded_irq(i2c_client->irq, + NULL, cs42l84_irq_thread, + IRQF_ONESHOT, + "cs42l84", cs42l84); + if (ret == -EPROBE_DEFER) { + goto err_disable_noirq; + } else if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to request IRQ: %d\n", ret); + goto err_disable_noirq; + } + } + + /* initialize codec */ + devid = cirrus_read_device_id(cs42l84->regmap, CS42L84_DEVID); + if (devid < 0) { + ret = devid; + dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret); + goto err_disable; + } + + if (devid != CS42L84_CHIP_ID) { + dev_err(&i2c_client->dev, + "CS42L84 Device ID (%X). Expected %X\n", + devid, CS42L84_CHIP_ID); + ret = -EINVAL; + goto err_disable; + } + + ret = regmap_read(cs42l84->regmap, CS42L84_REVID, ®); + if (ret < 0) { + dev_err(&i2c_client->dev, "Get Revision ID failed\n"); + goto err_shutdown; + } + + dev_info(&i2c_client->dev, + "Cirrus Logic CS42L84, Revision: %02X\n", reg & 0xFF); + + /* Setup plug detection */ + cs42l84_setup_plug_detect(cs42l84); + + /* Mask/Unmask Interrupts */ + cs42l84_set_interrupt_masks(cs42l84); + + /* Register codec for machine driver */ + ret = devm_snd_soc_register_component(&i2c_client->dev, + &soc_component_dev_cs42l84, &cs42l84_dai, 1); + if (ret < 0) + goto err_shutdown; + + return 0; + +err_shutdown: + /* Nothing to do */ + +err_disable: + if (i2c_client->irq) + free_irq(i2c_client->irq, cs42l84); + +err_disable_noirq: + gpiod_set_value_cansleep(cs42l84->reset_gpio, 0); +err_disable_noreset: + return ret; +} + +static void cs42l84_i2c_remove(struct i2c_client *i2c_client) +{ + struct cs42l84_private *cs42l84 = i2c_get_clientdata(i2c_client); + + if (i2c_client->irq) + free_irq(i2c_client->irq, cs42l84); + + gpiod_set_value_cansleep(cs42l84->reset_gpio, 0); +} + +static const struct of_device_id cs42l84_of_match[] = { + { .compatible = "cirrus,cs42l84", }, + {} +}; +MODULE_DEVICE_TABLE(of, cs42l84_of_match); + +static const struct i2c_device_id cs42l84_id[] = { + {"cs42l84", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, cs42l84_id); + +static struct i2c_driver cs42l84_i2c_driver = { + .driver = { + .name = "cs42l84", + .of_match_table = of_match_ptr(cs42l84_of_match), + }, + .id_table = cs42l84_id, + .probe = cs42l84_i2c_probe, + .remove = cs42l84_i2c_remove, +}; + +module_i2c_driver(cs42l84_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS42L84 driver"); +MODULE_AUTHOR("Martin Povišer "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l84.h b/sound/soc/codecs/cs42l84.h new file mode 100644 index 00000000000000..e7cbf5f0e2d0bb --- /dev/null +++ b/sound/soc/codecs/cs42l84.h @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) The Asahi Linux Contributors + * + * Based on sound/soc/codecs/cs42l42.h + * + * Copyright 2016 Cirrus Logic, Inc. + */ + + +#ifndef __CS42L84_H__ +#define __CS42L84_H__ + +#include + +#define CS42L84_CHIP_ID 0x42a84 + +#define CS42L84_DEVID 0x0000 +#define CS42L84_REVID 0x73fe +#define CS42L84_FRZ_CTL 0x0006 +#define CS42L84_FRZ_CTL_ENGAGE BIT(0) + +#define CS42L84_TSRS_PLUG_INT_STATUS 0x0400 +#define CS42L84_TSRS_PLUG_INT_MASK 0x0418 +#define CS42L84_RS_PLUG_SHIFT 0 +#define CS42L84_RS_PLUG BIT(0) +#define CS42L84_RS_UNPLUG BIT(1) +#define CS42L84_TS_PLUG_SHIFT 2 +#define CS42L84_TS_PLUG BIT(2) +#define CS42L84_TS_UNPLUG BIT(3) +#define CS42L84_TSRS_PLUG_VAL_MASK GENMASK(3, 0) +#define CS42L84_PLL_LOCK_STATUS 0x040e // probably bit 0x10 +#define CS42L84_PLL_LOCK_STATUS_LOCKED BIT(4) + +#define CS42L84_PLUG 3 +#define CS42L84_UNPLUG 0 +#define CS42L84_TRANS 1 + +#if 0 + l84.regs.RING_SENSE_CTRL.set(INV=1, UNK1=1, + RISETIME=E_DEBOUNCE_TIME.T_125MS, FALLTIME=E_DEBOUNCE_TIME.T_125MS) + l84.regs.TIP_SENSE_CTRL.set(INV=1, + RISETIME=E_DEBOUNCE_TIME.T_500MS, FALLTIME=E_DEBOUNCE_TIME.T_125MS) + l84.regs.MSM_BLOCK_EN3.set(TR_SENSE_EN=1) +#endif + +#define CS42L84_CCM_CTL1 0x0600 +#define CS42L84_CCM_CTL1_MCLK_SRC GENMASK(1, 0) +#define CS42L84_CCM_CTL1_MCLK_SRC_RCO 0 +#define CS42L84_CCM_CTL1_MCLK_SRC_MCLK 1 +#define CS42L84_CCM_CTL1_MCLK_SRC_BCLK 2 +#define CS42L84_CCM_CTL1_MCLK_SRC_PLL 3 +#define CS42L84_CCM_CTL1_MCLK_FREQ GENMASK(3, 2) +#define CS42L84_CCM_CTL1_MCLK_F_12MHZ 0b00 +#define CS42L84_CCM_CTL1_MCLK_F_24MHZ 0b01 +#define CS42L84_CCM_CTL1_MCLK_F_12_288KHZ 0b10 +#define CS42L84_CCM_CTL1_MCLK_F_24_576KHZ 0b11 +#define CS42L84_CCM_CTL1_RCO \ + (FIELD_PREP(CS42L84_CCM_CTL1_MCLK_SRC, CS42L84_CCM_CTL1_MCLK_SRC_RCO) \ + | FIELD_PREP(CS42L84_CCM_CTL1_MCLK_FREQ, CS42L84_CCM_CTL1_MCLK_F_12MHZ)) + +#define CS42L84_CCM_SAMP_RATE 0x0601 +#define CS42L84_CCM_SAMP_RATE_RATE_48KHZ 4 +#define CS42L84_CCM_SAMP_RATE_RATE_96KHZ 5 +#define CS42L84_CCM_SAMP_RATE_RATE_192KHZ 6 +#define CS42L84_CCM_SAMP_RATE_RATE_44K1HZ 12 +#define CS42L84_CCM_SAMP_RATE_RATE_88K2HZ 13 +#define CS42L84_CCM_SAMP_RATE_RATE_176K4HZ 14 +#define CS42L84_CCM_CTL3 0x0602 +#define CS42L84_CCM_CTL3_REFCLK_DIV GENMASK(2, 1) +#define CS42L84_CCM_CTL4 0x0603 +#define CS42L84_CCM_CTL4_REFCLK_EN BIT(0) + +#define CS42L84_CCM_ASP_CLK_CTRL 0x0608 + +#define CS42L84_PLL_CTL1 0x0800 +#define CS42L84_PLL_CTL1_EN BIT(0) +#define CS42L84_PLL_CTL1_MODE GENMASK(2, 1) +#define CS42L84_PLL_DIV_FRAC0 0x0804 +#define CS42L84_PLL_DIV_FRAC1 0x0805 +#define CS42L84_PLL_DIV_FRAC2 0x0806 +#define CS42L84_PLL_DIV_INT 0x0807 +#define CS42L84_PLL_DIVOUT 0x0808 + +#define CS42L84_RING_SENSE_CTL 0x1282 +#define CS42L84_RING_SENSE_CTL_INV BIT(7) +#define CS42L84_RING_SENSE_CTL_UNK1 BIT(6) +#define CS42L84_RING_SENSE_CTL_FALLTIME GENMASK(5, 3) +#define CS42L84_RING_SENSE_CTL_RISETIME GENMASK(2, 0) +#define CS42L84_TIP_SENSE_CTL 0x1283 +#define CS42L84_TIP_SENSE_CTL_INV BIT(7) +#define CS42L84_TIP_SENSE_CTL_FALLTIME GENMASK(5, 3) +#define CS42L84_TIP_SENSE_CTL_RISETIME GENMASK(2, 0) + +#define CS42L84_TSRS_PLUG_STATUS 0x1288 + +#define CS42L84_TIP_SENSE_CTL2 0x1473 +#define CS42L84_TIP_SENSE_CTL2_MODE GENMASK(7, 6) +#define CS42L84_TIP_SENSE_CTL2_MODE_DISABLED 0b00 +#define CS42L84_TIP_SENSE_CTL2_MODE_DIG_INPUT 0b01 +#define CS42L84_TIP_SENSE_CTL2_MODE_SHORT_DET 0b11 +#define CS42L84_TIP_SENSE_CTL2_INV BIT(5) + +#define CS42L84_MISC_DET_CTL 0x1474 +#define CS42L84_MISC_DET_CTL_DETECT_MODE GENMASK(4, 3) +#define CS42L84_MISC_DET_CTL_HSBIAS_CTL GENMASK(2, 1) +#define CS42L84_MISC_DET_CTL_PDN_MIC_LVL_DET BIT(0) + +#define CS42L84_MIC_DET_CTL4 0x1477 +#define CS42L84_MIC_DET_CTL4_LATCH_TO_VP BIT(1) + +#define CS42L84_HS_DET_STATUS2 0x147d + +#define CS42L84_MSM_BLOCK_EN1 0x1800 +#define CS42L84_MSM_BLOCK_EN2 0x1801 +#define CS42L84_MSM_BLOCK_EN2_ASP_SHIFT 6 +#define CS42L84_MSM_BLOCK_EN2_BUS_SHIFT 5 +#define CS42L84_MSM_BLOCK_EN2_DAC_SHIFT 4 +#define CS42L84_MSM_BLOCK_EN2_ADC_SHIFT 3 +#define CS42L84_MSM_BLOCK_EN3 0x1802 +#define CS42L84_MSM_BLOCK_EN3_TR_SENSE BIT(3) + +#define CS42L84_HS_DET_CTL2 0x1811 +#define CS42L84_HS_DET_CTL2_CTL GENMASK(7, 6) +#define CS42L84_HS_DET_CTL2_SET GENMASK(5, 4) +#define CS42L84_HS_DET_CTL2_REF BIT(3) +#define CS42L84_HS_DET_CTL2_AUTO_TIME GENMASK(1, 0) + +#define CS42L84_HS_SWITCH_CTL 0x1812 +#define CS42L84_HS_SWITCH_CTL_REF_HS3 BIT(7) +#define CS42L84_HS_SWITCH_CTL_REF_HS4 BIT(6) +#define CS42L84_HS_SWITCH_CTL_HSB_FILT_HS3 BIT(5) +#define CS42L84_HS_SWITCH_CTL_HSB_FILT_HS4 BIT(4) +#define CS42L84_HS_SWITCH_CTL_HSB_HS3 BIT(3) +#define CS42L84_HS_SWITCH_CTL_HSB_HS4 BIT(2) +#define CS42L84_HS_SWITCH_CTL_GNDHS_HS3 BIT(1) +#define CS42L84_HS_SWITCH_CTL_GNDHS_HS4 BIT(0) + +#define CS42L84_HS_CLAMP_DISABLE 0x1813 + +#define CS42L84_ADC_CTL1 0x2000 +#define CS42L84_ADC_CTL1_PREAMP_GAIN_SHIFT 6 +#define CS42L84_ADC_CTL1_PGA_GAIN_SHIFT 0 +#define CS42L84_ADC_CTL4 0x2003 +#define CS42L84_ADC_CTL4_WNF_CF_SHIFT 4 +#define CS42L84_ADC_CTL4_WNF_EN_SHIFT 3 +#define CS42L84_ADC_CTL4_HPF_CF_SHIFT 1 +#define CS42L84_ADC_CTL4_HPF_EN_SHIFT 0 + +#define CS42L84_DAC_CTL1 0x3000 +#define CS42L84_DAC_CTL1_UNMUTE BIT(0) +//#define CS42L84_DAC_CTL1_DACB_INV_SHIFT 1 +//#define CS42L84_DAC_CTL1_DACA_INV_SHIFT 0 +#define CS42L84_DAC_CTL2 0x3001 + +#define CS42L84_DAC_CHA_VOL_LSB 0x3004 +#define CS42L84_DAC_CHA_VOL_MSB 0x3005 +#define CS42L84_DAC_CHB_VOL_LSB 0x3006 +#define CS42L84_DAC_CHB_VOL_MSB 0x3007 +#define CS42L84_HP_VOL_CTL 0x3020 +#define CS42L84_HP_VOL_CTL_ZERO_CROSS BIT(1) +#define CS42L84_HP_VOL_CTL_SOFT BIT(0) + +#define CS42L84_SRC_ASP_RX_CH1 0b1101 +#define CS42L84_SRC_ASP_RX_CH2 0b1110 + +#define CS42L84_BUS_ASP_TX_SRC 0x4000 +#define CS42L84_BUS_ASP_TX_SRC_CH1_SHIFT 0 +#define CS42L84_BUS_DAC_SRC 0x4001 +#define CS42L84_BUS_DAC_SRC_DACA_SHIFT 0 +#define CS42L84_BUS_DAC_SRC_DACB_SHIFT 4 + +#define CS42L84_ASP_CTL 0x5000 +#define CS42L84_ASP_CTL_BCLK_EN_SHIFT 1 +#define CS42L84_ASP_CTL_TDM_MODE BIT(2) +#define CS42L84_ASP_FSYNC_CTL2 0x5010 +#define CS42L84_ASP_FSYNC_CTL2_BCLK_PERIOD_LO GENMASK(7, 1) +#define CS42L84_ASP_FSYNC_CTL3 0x5011 +#define CS42L84_ASP_FSYNC_CTL3_BCLK_PERIOD_HI GENMASK(4, 0) +#define CS42L84_ASP_DATA_CTL 0x5018 + +#define CS42L84_ASP_RX_EN 0x5020 +#define CS42L84_ASP_RX_EN_CH1_SHIFT 0 +#define CS42L84_ASP_RX_EN_CH2_SHIFT 1 +#define CS42L84_ASP_TX_EN 0x5024 +#define CS42L84_ASP_TX_EN_CH1_SHIFT 0 + +#define CS42L84_ASP_RX_CH1_CTL1 0x5028 +#define CS42L84_ASP_RX_CH1_CTL2 0x5029 +#define CS42L84_ASP_RX_CH1_WIDTH 0x502a +#define CS42L84_ASP_RX_CH2_CTL1 0x502c +#define CS42L84_ASP_RX_CH2_CTL2 0x502d +#define CS42L84_ASP_RX_CH2_WIDTH 0x502e + +#define CS42L84_ASP_RX_CHx_CTL1_EDGE BIT(0) +#define CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB GENMASK(7, 1) +#define CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB GENMASK(2, 0) + +#define CS42L84_ASP_TX_CH1_CTL1 0x5068 +#define CS42L84_ASP_TX_CH1_CTL2 0x5069 +#define CS42L84_ASP_TX_CH1_WIDTH 0x506a +#define CS42L84_ASP_TX_CH2_CTL1 0x506c +#define CS42L84_ASP_TX_CH2_CTL2 0x506d +#define CS42L84_ASP_TX_CH2_WIDTH 0x506e + +#define CS42L84_DEBOUNCE_TIME_125MS 0b001 +#define CS42L84_DEBOUNCE_TIME_500MS 0b011 + +#define CS42L84_BOOT_TIME_US 3000 +#define CS42L84_CLOCK_SWITCH_DELAY_US 150 +#define CS42L84_PLL_LOCK_POLL_US 250 +#define CS42L84_PLL_LOCK_TIMEOUT_US 1250 + +#endif /* __CS42L84_H__ */ From ee63bd26345dda47bfee3cafa67f8d02bae1e565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sun, 21 Aug 2022 02:40:29 +0200 Subject: [PATCH 0245/1020] ASoC: macaudio: Fix headset routes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/apple/macaudio.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 1e6007bd5336bf..d150676cacd0a1 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -626,7 +626,7 @@ static int macaudio_add_backend_dai_route(struct snd_soc_card *card, struct snd_ if (!is_speakers) { r = &routes[nroutes++]; r->source = dai->stream[SNDRV_PCM_STREAM_CAPTURE].widget->name; - r->sink = "Headphone Capture"; + r->sink = "Headset Capture"; } ret = snd_soc_dapm_add_routes(&card->dapm, routes, nroutes); @@ -639,7 +639,7 @@ static int macaudio_add_backend_dai_route(struct snd_soc_card *card, struct snd_ 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[1]; + struct snd_soc_dapm_route routes[2]; struct snd_soc_dapm_route *r; int nroutes = 0; char buf[32]; @@ -660,9 +660,11 @@ static int macaudio_add_pin_routes(struct snd_soc_card *card, struct snd_soc_com 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", @@ -813,7 +815,7 @@ static const struct snd_soc_dapm_widget macaudio_snd_widgets[] = { 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("Headphone Capture", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Headset Capture", NULL, 0, SND_SOC_NOPM, 0, 0), }; static const struct snd_kcontrol_new macaudio_controls[] = { @@ -840,7 +842,7 @@ static const struct snd_soc_dapm_route macaudio_dapm_routes[] = { { "Speaker (Static)", "Static", "Speaker Pin Demux" }, /* Capture paths */ - { "PCM0 RX", NULL, "Headphone Capture" }, + { "PCM0 RX", NULL, "Headset Capture" }, }; static const struct of_device_id macaudio_snd_device_id[] = { From 291217a386028d446a2770a03646d773346ed9cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sun, 21 Aug 2022 02:40:54 +0200 Subject: [PATCH 0246/1020] ASoC: dapm: Export new 'graph.dot' file in debugfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/soc-dapm.c | 137 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index ad8ba8fbbaeec0..b2305ec16998c2 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2202,6 +2202,139 @@ static const struct file_operations dapm_bias_fops = { .llseek = default_llseek, }; +static ssize_t dapm_graph_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct snd_soc_card *card = file->private_data; + struct snd_soc_dapm_context *dapm; + struct snd_soc_dapm_path *p; + struct snd_soc_dapm_widget *w; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dapm_widget *wdone[16]; + struct snd_soc_dai *dai; + int i, num_wdone = 0, cluster = 0; + char *buf; + ssize_t bufsize; + ssize_t ret = 0; + + bufsize = 1024 * card->num_dapm_widgets; + buf = kmalloc(bufsize, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&card->dapm_mutex); + +#define bufprintf(...) \ + ret += scnprintf(buf + ret, bufsize - ret, __VA_ARGS__) + + bufprintf("digraph dapm {\n"); + + /* + * Print the user-visible devices of the card. + */ + bufprintf("subgraph cluster_%d {\n", cluster++); + bufprintf("label=\"Devices\";style=filled;fillcolor=gray;\n"); + for_each_card_rtds(card, rtd) { + if (rtd->dai_link->no_pcm) + continue; + + bufprintf("w%pK [label=\"%d: %s\"];\n", rtd, + rtd->pcm->device, rtd->dai_link->name); + } + bufprintf("};\n"); + + /* + * Print the playback/capture widgets of DAIs just next to + * the user-visible devices. Keep the list of already printed + * widgets in 'wdone', so they will be skipped later. + */ + for_each_card_rtds(card, rtd) { + for_each_rtd_cpu_dais(rtd, i, dai) { + if (dai->stream[SNDRV_PCM_STREAM_PLAYBACK].widget) { + w = dai->stream[SNDRV_PCM_STREAM_PLAYBACK].widget; + bufprintf("w%pK [label=\"%s\"];\n", w, w->name); + if (!rtd->dai_link->no_pcm) + bufprintf("w%pK -> w%pK;\n", rtd, w); + wdone[num_wdone] = w; + if (num_wdone < ARRAY_SIZE(wdone)) + num_wdone++; + } + + if (dai->stream[SNDRV_PCM_STREAM_CAPTURE].widget) { + w = dai->stream[SNDRV_PCM_STREAM_CAPTURE].widget; + bufprintf("w%pK [label=\"%s\"];\n", w, w->name); + if (!rtd->dai_link->no_pcm) + bufprintf("w%pK -> w%pK;\n", w, rtd); + wdone[num_wdone] = w; + if (num_wdone < ARRAY_SIZE(wdone)) + num_wdone++; + } + } + } + + for_each_card_dapms(card, dapm) { + const char *prefix = soc_dapm_prefix(dapm); + + if (dapm != &card->dapm) { + bufprintf("subgraph cluster_%d {\n", cluster++); + if (prefix) + bufprintf("label=\"%s\";\n", prefix); + else if (dapm->component) + bufprintf("label=\"%s\";\n", + dapm->component->name); + } + + for_each_card_widgets(dapm->card, w) { + const char *name = w->name; + bool skip = false; + + if (w->dapm != dapm) + continue; + + if (list_empty(&w->edges[0]) && list_empty(&w->edges[1])) + continue; + + for (i = 0; i < num_wdone; i++) + if (wdone[i] == w) + skip = true; + if (skip) + continue; + + if (prefix && strlen(name) > strlen(prefix) + 1) + name += strlen(prefix) + 1; + + bufprintf("w%pK [label=\"%s\"];\n", w, name); + } + + if (dapm != &card->dapm) + bufprintf("}\n"); + } + + list_for_each_entry(p, &card->paths, list) { + if (p->name) + bufprintf("w%pK -> w%pK [label=\"%s\"];\n", + p->source, p->sink, p->name); + else + bufprintf("w%pK -> w%pK;\n", p->source, p->sink); + } + + bufprintf("}\n"); +#undef bufprintf + + mutex_unlock(&card->dapm_mutex); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + + kfree(buf); + return ret; +} + +static const struct file_operations dapm_graph_fops = { + .open = simple_open, + .read = dapm_graph_read_file, + .llseek = default_llseek, +}; + void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, struct dentry *parent) { @@ -2212,6 +2345,10 @@ void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, debugfs_create_file("bias_level", 0444, dapm->debugfs_dapm, dapm, &dapm_bias_fops); + + if (dapm == &dapm->card->dapm) + debugfs_create_file("graph.dot", 0444, dapm->debugfs_dapm, + dapm->card, &dapm_graph_fops); } static void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w) From b2842a37f385c77dba16dbe50cfd6b6f12867e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 23 Aug 2022 11:36:24 +0200 Subject: [PATCH 0247/1020] ASoC: macaudio: Add j375 fixup_controls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/apple/macaudio.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index d150676cacd0a1..82808a3fb6df84 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -758,6 +758,17 @@ static int macaudio_j314_fixup_controls(struct snd_soc_card *card) return 0; } +static int macaudio_j375_fixup_controls(struct snd_soc_card *card) +{ + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + + if (ma->has_speakers) { + CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 14); // 20 set by macOS, this is 3 dB below + } + + return 0; +} + static int macaudio_fallback_fixup_controls(struct snd_soc_card *card) { struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); @@ -848,6 +859,7 @@ static const struct snd_soc_dapm_route macaudio_dapm_routes[] = { static const struct of_device_id macaudio_snd_device_id[] = { { .compatible = "apple,j274-macaudio", .data = macaudio_j274_fixup_controls }, { .compatible = "apple,j314-macaudio", .data = macaudio_j314_fixup_controls }, + { .compatible = "apple,j375-macaudio", .data = macaudio_j375_fixup_controls }, { .compatible = "apple,macaudio"}, { } }; From c798bce5c759880d7fdfb4e62861339793d42bb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 30 Aug 2022 10:20:09 +0200 Subject: [PATCH 0248/1020] ASoC: macaudio: Add j493 fixup_controls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/apple/macaudio.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 82808a3fb6df84..543dc0c1134816 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -769,6 +769,17 @@ static int macaudio_j375_fixup_controls(struct snd_soc_card *card) return 0; } +static int macaudio_j493_fixup_controls(struct snd_soc_card *card) +{ + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + + if (ma->has_speakers) { + CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 9); // 15 set by macOS, this is 3 dB below + } + + return 0; +} + static int macaudio_fallback_fixup_controls(struct snd_soc_card *card) { struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); @@ -860,6 +871,7 @@ static const struct of_device_id macaudio_snd_device_id[] = { { .compatible = "apple,j274-macaudio", .data = macaudio_j274_fixup_controls }, { .compatible = "apple,j314-macaudio", .data = macaudio_j314_fixup_controls }, { .compatible = "apple,j375-macaudio", .data = macaudio_j375_fixup_controls }, + { .compatible = "apple,j493-macaudio", .data = macaudio_j493_fixup_controls }, { .compatible = "apple,macaudio"}, { } }; From 22ec73873b122617e383500d2844b27d1055a505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sun, 4 Sep 2022 10:29:34 +0200 Subject: [PATCH 0249/1020] ASoC: macaudio: Rename ALSA driver to simple 'macaudio' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/apple/macaudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 543dc0c1134816..f5f43002c4a2b8 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -898,7 +898,7 @@ static int macaudio_snd_platform_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, data); card->owner = THIS_MODULE; - card->driver_name = DRIVER_NAME; + card->driver_name = "macaudio"; card->dev = dev; card->dapm_widgets = macaudio_snd_widgets; card->num_dapm_widgets = ARRAY_SIZE(macaudio_snd_widgets); From 24cf2e1f10a50d500eb22bc3185b13c5d8786dd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 2 Sep 2022 19:40:16 +0200 Subject: [PATCH 0250/1020] ASoC: macaudio: Drop the 'inverse jack' speaker stuff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/apple/macaudio.c | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index f5f43002c4a2b8..fdc7293f376599 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -577,11 +577,6 @@ static struct snd_soc_jack_pin macaudio_jack_pins[] = { .pin = "Headset Mic", .mask = SND_JACK_MICROPHONE, }, - { - .pin = "Speaker", - .mask = SND_JACK_HEADPHONE, - .invert = 1, - }, }; static int macaudio_probe(struct snd_soc_card *card) @@ -655,7 +650,7 @@ static int macaudio_add_pin_routes(struct snd_soc_card *card, struct snd_soc_com snprintf(buf, sizeof(buf) - 1, "%s OUT", component->name_prefix); r->source = buf; } - r->sink = "Speaker Pin Demux"; + r->sink = "Speaker"; } else { r = &routes[nroutes++]; r->source = "Jack HP"; @@ -814,16 +809,6 @@ 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("Headphones Playback Mux", macaudio_hp_mux_enum); -static const char *macaudio_spk_demux_texts[] = { - "Inverse Jack", "Static", -}; - -static SOC_ENUM_SINGLE_DECL(macaudio_spk_demux_enum, - SND_SOC_NOPM, 0, macaudio_spk_demux_texts); - -static const struct snd_kcontrol_new macaudio_spk_demux = - SOC_DAPM_ENUM("Speaker Pin Demux", macaudio_spk_demux_enum); - static const struct snd_soc_dapm_widget macaudio_snd_widgets[] = { SND_SOC_DAPM_SPK("Speaker", NULL), SND_SOC_DAPM_SPK("Speaker (Static)", NULL), @@ -832,7 +817,6 @@ static const struct snd_soc_dapm_widget macaudio_snd_widgets[] = { 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_DEMUX("Speaker Pin Demux", SND_SOC_NOPM, 0, 0, &macaudio_spk_demux), 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), @@ -842,7 +826,6 @@ static const struct snd_soc_dapm_widget macaudio_snd_widgets[] = { static const struct snd_kcontrol_new macaudio_controls[] = { SOC_DAPM_PIN_SWITCH("Speaker"), - SOC_DAPM_PIN_SWITCH("Speaker (Static)"), SOC_DAPM_PIN_SWITCH("Headphone"), SOC_DAPM_PIN_SWITCH("Headset Mic"), }; @@ -860,9 +843,6 @@ static const struct snd_soc_dapm_route macaudio_dapm_routes[] = { * Additional paths (to specific I2S ports) are added dynamically. */ - { "Speaker", "Inverse Jack", "Speaker Pin Demux" }, - { "Speaker (Static)", "Static", "Speaker Pin Demux" }, - /* Capture paths */ { "PCM0 RX", NULL, "Headset Capture" }, }; From 046f20831fd492f40a110e23ec31c0b3418e44b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 6 Sep 2022 15:16:44 +0200 Subject: [PATCH 0251/1020] ASoC: macaudio: s/Freq/Frequency/ in TAS2764 control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/apple/macaudio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index fdc7293f376599..09310014d5e636 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -730,8 +730,8 @@ static int macaudio_j314_fixup_controls(struct snd_soc_card *card) CHECK(snd_soc_set_enum_kctl, "* ASI1 Sel", "Left"); CHECK(snd_soc_deactivate_kctl, "* ASI1 Sel", 0); CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 9); // 15 set by macOS, this is 3 dB below - CHECK(snd_soc_set_enum_kctl, "* Tweeter HPF Corner Freq", "800 Hz"); - CHECK(snd_soc_deactivate_kctl, "* Tweeter HPF Corner Freq", 0); + CHECK(snd_soc_set_enum_kctl, "* Tweeter HPF Corner Frequency", "800 Hz"); + CHECK(snd_soc_deactivate_kctl, "* Tweeter HPF Corner Frequency", 0); /* * The speaker amps suffer from spurious overcurrent From 3553cb375be506844df726046d3779f70896dc21 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 13 Sep 2022 19:56:12 +0900 Subject: [PATCH 0252/1020] ASoC: macaudio: s/void_warranty/please_blow_up_my_speakers/ We have no idea whether any of this voids warranties, but what it does do is blow up your speakers, so let's be explicit about what users are signing up for. Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 09310014d5e636..4806854ee0656f 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -67,9 +67,9 @@ struct macaudio_snd_data { struct snd_pcm_hw_constraint_list speaker_nchans_list; }; -static bool void_warranty; -module_param(void_warranty, bool, 0644); -MODULE_PARM_DESC(void_warranty, "Do not bail if safety is not assured"); +static bool please_blow_up_my_speakers; +module_param(please_blow_up_my_speakers, bool, 0644); +MODULE_PARM_DESC(please_blow_up_my_speakers, "Allow unsafe or untested operating configurations"); SND_SOC_DAILINK_DEFS(primary, DAILINK_COMP_ARRAY(COMP_CPU("mca-pcm-0")), // CPU @@ -703,7 +703,7 @@ static int macaudio_late_probe(struct snd_soc_card *card) #define CHECK(call, pattern, value) \ { \ int ret = call(card, pattern, value); \ - if (ret < 1 && !void_warranty) { \ + if (ret < 1 && !please_blow_up_my_speakers) { \ dev_err(card->dev, "%s on '%s': %d\n", #call, pattern, ret); \ return ret; \ } \ @@ -779,7 +779,7 @@ static int macaudio_fallback_fixup_controls(struct snd_soc_card *card) { struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); - if (ma->has_speakers && !void_warranty) { + if (ma->has_speakers && !please_blow_up_my_speakers) { dev_err(card->dev, "driver can't assure safety on this model, refusing probe\n"); return -EINVAL; } From c7dc72792cf47cbf5c31dde3dd11f68fc8ccd994 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 13 Sep 2022 19:56:47 +0900 Subject: [PATCH 0253/1020] ASoC: macaudio: Gate off experimental platforms We know at least some machines can have their speakers blown, even with these limits, so let's play it safe for now and require that users both enable stuff in the DT *and* pass this flag. Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 4806854ee0656f..2d4b21b95309e6 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -727,6 +727,11 @@ static int macaudio_j314_fixup_controls(struct snd_soc_card *card) struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); if (ma->has_speakers) { + if (!please_blow_up_my_speakers) { + dev_err(card->dev, "driver can't assure safety on this model, refusing probe\n"); + return -EINVAL; + } + CHECK(snd_soc_set_enum_kctl, "* ASI1 Sel", "Left"); CHECK(snd_soc_deactivate_kctl, "* ASI1 Sel", 0); CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 9); // 15 set by macOS, this is 3 dB below @@ -758,6 +763,11 @@ static int macaudio_j375_fixup_controls(struct snd_soc_card *card) struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); if (ma->has_speakers) { + if (!please_blow_up_my_speakers) { + dev_err(card->dev, "driver can't assure safety on this model, refusing probe\n"); + return -EINVAL; + } + CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 14); // 20 set by macOS, this is 3 dB below } @@ -769,6 +779,11 @@ static int macaudio_j493_fixup_controls(struct snd_soc_card *card) struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); if (ma->has_speakers) { + if (!please_blow_up_my_speakers) { + dev_err(card->dev, "driver can't assure safety on this model, refusing probe\n"); + return -EINVAL; + } + CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 9); // 15 set by macOS, this is 3 dB below } From 086492ccab6da50de895595971f9d70508460536 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 13 Sep 2022 19:58:17 +0900 Subject: [PATCH 0254/1020] ASoC: macaudio: Alias f413 fixups to j314 This works as far as following the same intent as j314, but we *know* these limits are not sufficient, so this one really needs the module parameter gate. Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 2d4b21b95309e6..0dd1253ac4f68b 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -866,6 +866,7 @@ static const struct of_device_id macaudio_snd_device_id[] = { { .compatible = "apple,j274-macaudio", .data = macaudio_j274_fixup_controls }, { .compatible = "apple,j314-macaudio", .data = macaudio_j314_fixup_controls }, { .compatible = "apple,j375-macaudio", .data = macaudio_j375_fixup_controls }, + { .compatible = "apple,j413-macaudio", .data = macaudio_j314_fixup_controls }, { .compatible = "apple,j493-macaudio", .data = macaudio_j493_fixup_controls }, { .compatible = "apple,macaudio"}, { } From 6279402054a6f0e6d3eb1fdb3598af56a6ea1bb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 14 Oct 2022 16:23:21 +0200 Subject: [PATCH 0255/1020] ASoC: cs42l84: There's no line-in on the jack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/codecs/cs42l84.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c index 38dfee80ed9f73..791d178bdbcf03 100644 --- a/sound/soc/codecs/cs42l84.c +++ b/sound/soc/codecs/cs42l84.c @@ -727,12 +727,8 @@ static void cs42l84_detect_hs(struct cs42l84_private *cs42l84) break; case 0b00: /* open */ - dev_dbg(cs42l84->dev, "Detected line-in\n"); - cs42l84->hs_type = SND_JACK_HEADSET; - snd_soc_jack_report(cs42l84->jack, SND_JACK_HEADSET, - SND_JACK_HEADSET); - break; - + dev_dbg(cs42l84->dev, "Detected open circuit on HS4\n"); + fallthrough; case 0b11: /* shorted */ default: snd_soc_jack_report(cs42l84->jack, SND_JACK_HEADPHONE, From 7001cd33eb1f80ac6507235f8bde19b71e49ba15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 14 Oct 2022 17:46:44 +0200 Subject: [PATCH 0256/1020] ASoC: cs42l84: Adjust mic-detection voltage threshold MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Raise the mic-detection voltage threshold to address some mics not getting detected. Signed-off-by: Martin Povišer --- sound/soc/codecs/cs42l84.c | 5 ++++- sound/soc/codecs/cs42l84.h | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c index 791d178bdbcf03..9f3508abae2a20 100644 --- a/sound/soc/codecs/cs42l84.c +++ b/sound/soc/codecs/cs42l84.c @@ -878,7 +878,10 @@ static void cs42l84_setup_plug_detect(struct cs42l84_private *cs42l84) (CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >> CS42L84_TS_PLUG_SHIFT; - /* Set up mic detection */ + /* Set mic-detection threshold */ + regmap_update_bits(cs42l84->regmap, + CS42L84_MIC_DET_CTL1, CS42L84_MIC_DET_CTL1_HS_DET_LEVEL, + FIELD_PREP(CS42L84_MIC_DET_CTL1_HS_DET_LEVEL, 0x2c)); /* ~1.9 V */ /* Disconnect HSBIAS (initially) */ regmap_write(cs42l84->regmap, diff --git a/sound/soc/codecs/cs42l84.h b/sound/soc/codecs/cs42l84.h index e7cbf5f0e2d0bb..9aaf19051d395f 100644 --- a/sound/soc/codecs/cs42l84.h +++ b/sound/soc/codecs/cs42l84.h @@ -106,6 +106,9 @@ #define CS42L84_MISC_DET_CTL_HSBIAS_CTL GENMASK(2, 1) #define CS42L84_MISC_DET_CTL_PDN_MIC_LVL_DET BIT(0) +#define CS42L84_MIC_DET_CTL1 0x1475 +#define CS42L84_MIC_DET_CTL1_HS_DET_LEVEL GENMASK(5, 0) + #define CS42L84_MIC_DET_CTL4 0x1477 #define CS42L84_MIC_DET_CTL4_LATCH_TO_VP BIT(1) From 1404a8e3434b90be6cb06ead87b9a4514aa21e14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sun, 16 Oct 2022 13:33:21 +0200 Subject: [PATCH 0257/1020] ASoC: cs42l84: Put the volume control in shape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous limits of the volume control were placeholders, this reflects the current understanding of the register semantics. Signed-off-by: Martin Povišer --- sound/soc/codecs/cs42l84.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c index 9f3508abae2a20..f2c5957ceee608 100644 --- a/sound/soc/codecs/cs42l84.c +++ b/sound/soc/codecs/cs42l84.c @@ -70,11 +70,15 @@ static int cs42l84_put_dac_vol(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *val) { struct snd_soc_component *component = snd_soc_kcontrol_component(kctl); - unsigned int vola, volb; + struct soc_mixer_control *mc = (struct soc_mixer_control *) kctl->private_value; + int vola, volb; int ret, ret2; - vola = val->value.integer.value[0]; - volb = val->value.integer.value[1]; + vola = val->value.integer.value[0] + mc->min; + volb = val->value.integer.value[1] + mc->min; + + if (vola < mc->min || vola > mc->max || volb < mc->min || volb > mc->max) + return -EINVAL; ret = snd_soc_component_update_bits(component, CS42L84_FRZ_CTL, CS42L84_FRZ_CTL_ENGAGE, @@ -112,7 +116,8 @@ static int cs42l84_get_dac_vol(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *val) { struct snd_soc_component *component = snd_soc_kcontrol_component(kctl); - unsigned int vola, volb; + struct soc_mixer_control *mc = (struct soc_mixer_control *) kctl->private_value; + int vola, volb; int ret; ret = snd_soc_component_read(component, CS42L84_DAC_CHA_VOL_LSB); @@ -135,18 +140,23 @@ static int cs42l84_get_dac_vol(struct snd_kcontrol *kctl, return ret; volb |= (ret & 1) << 8; - val->value.integer.value[0] = vola; - val->value.integer.value[1] = volb; + if (vola & BIT(8)) + vola |= ~((int)(BIT(8) - 1)); + if (volb & BIT(8)) + volb |= ~((int)(BIT(8) - 1)); + + val->value.integer.value[0] = vola - mc->min; + val->value.integer.value[1] = volb - mc->min; return 0; } /* TODO */ -static const DECLARE_TLV_DB_SCALE(cs42l84_dac_tlv, -25600, 50, 1); +static const DECLARE_TLV_DB_SCALE(cs42l84_dac_tlv, -12800, 50, true); static const struct snd_kcontrol_new cs42l84_snd_controls[] = { - SOC_DOUBLE_R_EXT_TLV("DAC Playback Volume", CS42L84_DAC_CHA_VOL_LSB, - CS42L84_DAC_CHB_VOL_LSB, 0, 511, 0, + SOC_DOUBLE_R_S_EXT_TLV("DAC Playback Volume", CS42L84_DAC_CHA_VOL_LSB, + CS42L84_DAC_CHB_VOL_LSB, 0, -256, 24, 8, 0, cs42l84_get_dac_vol, cs42l84_put_dac_vol, cs42l84_dac_tlv), SOC_SINGLE("ADC Preamp Gain", CS42L84_ADC_CTL1, CS42L84_ADC_CTL1_PREAMP_GAIN_SHIFT, 2, 0), From 11169b01cef938332d5a15065b71dd9c4162e246 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 17 Oct 2022 11:23:45 +0200 Subject: [PATCH 0258/1020] ASoC: apple: mca: Constrain channels according to TDM mask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't (and can't) configure the hardware correctly if the number of channels exceeds the weight of the TDM mask. Report that constraint in startup of FE. Signed-off-by: Martin Povišer --- sound/soc/apple/mca.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index 3780aca710769e..fb23cb27b00ad7 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -464,6 +464,28 @@ static int mca_configure_serdes(struct mca_cluster *cl, int serdes_unit, return -EINVAL; } +static int mca_fe_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mca_cluster *cl = mca_dai_to_cluster(dai); + unsigned int mask, nchannels; + + if (cl->tdm_slots) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + mask = cl->tdm_tx_mask; + else + mask = cl->tdm_rx_mask; + + nchannels = hweight32(mask); + } else { + nchannels = 2; + } + + return snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, + 1, nchannels); +} + static int mca_fe_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) { @@ -680,6 +702,7 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, } static const struct snd_soc_dai_ops mca_fe_ops = { + .startup = mca_fe_startup, .set_fmt = mca_fe_set_fmt, .set_bclk_ratio = mca_set_bclk_ratio, .set_tdm_slot = mca_fe_set_tdm_slot, From f14626d18ffd323580f8c41126f6fe856f06f28a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 17 Oct 2022 12:16:20 +0200 Subject: [PATCH 0259/1020] ASoC: macaudio: Improve message on opening of unrouted PCM devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/apple/macaudio.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 0dd1253ac4f68b..3ccfacefbf7e85 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -455,6 +455,29 @@ static int macaudio_dpcm_hw_params(struct snd_pcm_substream *substream, 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 (bad settings applied to the sound card)\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); @@ -473,7 +496,7 @@ static void macaudio_dpcm_shutdown(struct snd_pcm_substream *substream) static const struct snd_soc_ops macaudio_fe_ops = { .shutdown = macaudio_dpcm_shutdown, - .hw_params = macaudio_dpcm_hw_params, + .hw_params = macaudio_fe_hw_params, }; static const struct snd_soc_ops macaudio_be_ops = { From 55026215e5f589c740e8c14382dabb25ff1acc08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 17 Oct 2022 12:26:27 +0200 Subject: [PATCH 0260/1020] ASoC: cs42l84: Enable regcache (initially) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/codecs/cs42l84.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c index f2c5957ceee608..ae1d65a3ee46c8 100644 --- a/sound/soc/codecs/cs42l84.c +++ b/sound/soc/codecs/cs42l84.c @@ -55,12 +55,37 @@ struct cs42l84_private { int hs_type; }; +/* +static const struct reg_default cs42l84_reg_defaults[] = { +}; +*/ + +static bool cs42l84_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L84_DEVID ... CS42L84_DEVID+5: + case CS42L84_TSRS_PLUG_INT_STATUS: + case CS42L84_PLL_LOCK_STATUS: + case CS42L84_TSRS_PLUG_STATUS: + case CS42L84_HS_DET_STATUS2: + return true; + default: + return false; + } +} + static const struct regmap_config cs42l84_regmap = { .reg_bits = 16, .val_bits = 8, + .volatile_reg = cs42l84_volatile_register, + .max_register = 0xffff, - .cache_type = REGCACHE_NONE, + /* + .reg_defaults = cs42l84_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs42l84_reg_defaults), + */ + .cache_type = REGCACHE_RBTREE, .use_single_read = true, .use_single_write = true, From d7755eb3c1ad961ca550d5929a2be0ec8a42bbc6 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 18 Oct 2022 13:29:57 +0900 Subject: [PATCH 0261/1020] ASoC: cs42l84: Report volume updates correctly cs42l84_put_dac_vol needs to return 1 if the volume was updated. Before this patch, it was only doing that when the MSB changed. Correctly track changes in all the registers. Signed-off-by: Hector Martin --- sound/soc/codecs/cs42l84.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c index ae1d65a3ee46c8..f2784946b6900d 100644 --- a/sound/soc/codecs/cs42l84.c +++ b/sound/soc/codecs/cs42l84.c @@ -97,7 +97,7 @@ static int cs42l84_put_dac_vol(struct snd_kcontrol *kctl, struct snd_soc_component *component = snd_soc_kcontrol_component(kctl); struct soc_mixer_control *mc = (struct soc_mixer_control *) kctl->private_value; int vola, volb; - int ret, ret2; + int ret, ret2, updated = 0; vola = val->value.integer.value[0] + mc->min; volb = val->value.integer.value[1] + mc->min; @@ -110,23 +110,31 @@ static int cs42l84_put_dac_vol(struct snd_kcontrol *kctl, CS42L84_FRZ_CTL_ENGAGE); if (ret < 0) goto bail; + updated |= ret; ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHA_VOL_LSB, 0xff, vola & 0xff); if (ret < 0) goto bail; + updated |= ret; + ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHA_VOL_MSB, 0xff, (vola >> 8) & 0x01); if (ret < 0) goto bail; + updated |= ret; + ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHB_VOL_LSB, 0xff, volb & 0xff); if (ret < 0) goto bail; + updated |= ret; + ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHB_VOL_MSB, 0xff, (volb >> 8) & 0x01); if (ret < 0) goto bail; + ret |= updated; bail: ret2 = snd_soc_component_update_bits(component, CS42L84_FRZ_CTL, From 63901b8feda1cd3fabb2d13ccb04063a6d1376e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 27 Oct 2022 11:09:19 +0200 Subject: [PATCH 0262/1020] ASoC: macaudio: Add initial j313 fixup_controls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/apple/macaudio.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 3ccfacefbf7e85..a3da4ec0dae6a0 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -745,6 +745,36 @@ static int macaudio_j274_fixup_controls(struct snd_soc_card *card) return 0; } +static int macaudio_j313_fixup_controls(struct snd_soc_card *card) { + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + + if (ma->has_speakers) { + if (!please_blow_up_my_speakers) { + dev_err(card->dev, "driver can't assure safety on this model, refusing probe\n"); + return -EINVAL; + } + + CHECK(snd_soc_set_enum_kctl, "* ASI1 Sel", "Left"); + CHECK(snd_soc_deactivate_kctl, "* ASI1 Sel", 0); + + /* !!! This is copied from j274, not obtained by looking at + * what macOS sets. + */ + CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 14); + + /* + * Since we don't set the right slots yet to avoid + * driver conflict on the I2S bus sending ISENSE/VSENSE + * samples from the codecs back to us, disable the + * controls. + */ + CHECK(snd_soc_deactivate_kctl, "* VSENSE Switch", 0); + CHECK(snd_soc_deactivate_kctl, "* ISENSE Switch", 0); + } + + return 0; +} + static int macaudio_j314_fixup_controls(struct snd_soc_card *card) { struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); @@ -887,6 +917,7 @@ static const struct snd_soc_dapm_route macaudio_dapm_routes[] = { static const struct of_device_id macaudio_snd_device_id[] = { { .compatible = "apple,j274-macaudio", .data = macaudio_j274_fixup_controls }, + { .compatible = "apple,j313-macaudio", .data = macaudio_j313_fixup_controls }, { .compatible = "apple,j314-macaudio", .data = macaudio_j314_fixup_controls }, { .compatible = "apple,j375-macaudio", .data = macaudio_j375_fixup_controls }, { .compatible = "apple,j413-macaudio", .data = macaudio_j314_fixup_controls }, From 921d5b38f94475390e7d8e8a724c9d7a7b2c37db Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Mon, 24 Oct 2022 21:17:31 +1000 Subject: [PATCH 0263/1020] ASoC: macaudio: constrain frontend channel counts In order to support the wide range of audio arrangements possible on this platform in a generic way, it is necessary for the frontend PCMs to be populated with enough TDM slots to cover all intended use cases. Userspace therefore attempts to open "phantom" channels when a frontend has more channels than its associated backend, which results in garbled audio samples and dropped frames. We must therefore dynamically constrain the frontends when they are started to ensure that userspace can never open more channels than are present on the hardware being represented by the frontend in question. Signed-off-by: James Calligeros --- sound/soc/apple/macaudio.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index a3da4ec0dae6a0..e24006ca79c8fb 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -52,6 +52,7 @@ struct macaudio_snd_data { int jack_plugin_state; bool has_speakers; + unsigned int max_channels; struct macaudio_link_props { /* frontend props */ @@ -345,6 +346,10 @@ static int macaudio_parse_of(struct macaudio_snd_data *ma) 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. @@ -455,6 +460,25 @@ static int macaudio_dpcm_hw_params(struct snd_pcm_substream *substream, 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); + int ret; + + /* The FEs must never have more channels than the hardware */ + ret = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, 0, ma->max_channels); + + if (ret < 0) { + dev_err(rtd->dev, "Failed to constrain FE %d! %d", rtd->dai_link->id, ret); + return ret; + } + + return 0; +} + static int macaudio_fe_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -495,6 +519,7 @@ static void macaudio_dpcm_shutdown(struct snd_pcm_substream *substream) } static const struct snd_soc_ops macaudio_fe_ops = { + .startup = macaudio_fe_startup, .shutdown = macaudio_dpcm_shutdown, .hw_params = macaudio_fe_hw_params, }; From 254a1777230ef0a2e342826fc75ad7ed5b1b8822 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 15 Jan 2023 22:20:38 +0900 Subject: [PATCH 0264/1020] ASoC: cs42l84: Do not enable the PLL before the clocks are ready Enabling the PLL with no valid input clock leads to PLL errors. Don't do that, always disable the PLL before reconfiguring it and let unmute take care of enabling the PLL. Also monitor the PLL error bit and complain if it's set. Signed-off-by: Hector Martin --- sound/soc/codecs/cs42l84.c | 9 +++++---- sound/soc/codecs/cs42l84.h | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c index f2784946b6900d..cc4dff97e7a42d 100644 --- a/sound/soc/codecs/cs42l84.c +++ b/sound/soc/codecs/cs42l84.c @@ -433,6 +433,8 @@ static int cs42l84_pll_config(struct snd_soc_component *component) break; } + snd_soc_component_update_bits(component, CS42L84_PLL_CTL1, CS42L84_PLL_CTL1_EN, 0); + if (pll_ratio_table[i].mclk_src_sel) { /* Configure PLL */ snd_soc_component_update_bits(component, @@ -456,10 +458,6 @@ static int cs42l84_pll_config(struct snd_soc_component *component) snd_soc_component_write(component, CS42L84_PLL_DIVOUT, pll_ratio_table[i].pll_divout); - - snd_soc_component_update_bits(component, - CS42L84_PLL_CTL1, CS42L84_PLL_CTL1_EN, - CS42L84_PLL_CTL1_EN); } return 0; @@ -633,6 +631,9 @@ static int cs42l84_mute_stream(struct snd_soc_dai *dai, int mute, int stream) if (ret < 0) dev_warn(component->dev, "PLL failed to lock: %d\n", ret); + if (regval & CS42L84_PLL_LOCK_STATUS_ERROR) + dev_warn(component->dev, "PLL lock error\n"); + /* PLL must be running to drive glitchless switch logic */ snd_soc_component_update_bits(component, CS42L84_CCM_CTL1, diff --git a/sound/soc/codecs/cs42l84.h b/sound/soc/codecs/cs42l84.h index 9aaf19051d395f..35bd15e2ef17c9 100644 --- a/sound/soc/codecs/cs42l84.h +++ b/sound/soc/codecs/cs42l84.h @@ -31,6 +31,7 @@ #define CS42L84_TSRS_PLUG_VAL_MASK GENMASK(3, 0) #define CS42L84_PLL_LOCK_STATUS 0x040e // probably bit 0x10 #define CS42L84_PLL_LOCK_STATUS_LOCKED BIT(4) +#define CS42L84_PLL_LOCK_STATUS_ERROR BIT(5) #define CS42L84_PLUG 3 #define CS42L84_UNPLUG 0 From 3743b184615d5b356a2097a080bb8b94605c3282 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 16 Apr 2023 19:27:40 +0900 Subject: [PATCH 0265/1020] ASoC: cs42l42: Set a faster digital ramp-up rate With the default ramp-up rate, there is a noticeable fade-in when streams start. This can be undesirable with aggressive muting for power saving, since the beginning of the stream is lost. Lower the digital output ramp-up time from 8 samples per period to 2 samples per period. This still leaves some fade-in to avoid pops, but it is a lot less noticeable and no longer feels like the stream is fading in. Signed-off-by: Hector Martin --- include/sound/cs42l42.h | 4 ++++ sound/soc/codecs/cs42l42.c | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/include/sound/cs42l42.h b/include/sound/cs42l42.h index 1bd8eee54f6665..b3657965d49109 100644 --- a/include/sound/cs42l42.h +++ b/include/sound/cs42l42.h @@ -62,6 +62,10 @@ #define CS42L42_INTERNAL_FS_MASK (1 << CS42L42_INTERNAL_FS_SHIFT) #define CS42L42_SFTRAMP_RATE (CS42L42_PAGE_10 + 0x0A) +#define CS42L42_SFTRAMP_ASR_RATE_MASK GENMASK(7, 4) +#define CS42L42_SFTRAMP_ASR_RATE_SHIFT 4 +#define CS42L42_SFTRAMP_DSR_RATE_MASK GENMASK(3, 0) +#define CS42L42_SFTRAMP_DSR_RATE_SHIFT 0 #define CS42L42_SLOW_START_ENABLE (CS42L42_PAGE_10 + 0x0B) #define CS42L42_SLOW_START_EN_MASK GENMASK(6, 4) #define CS42L42_SLOW_START_EN_SHIFT 4 diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 762b93feb109fd..053c60d80d85e7 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -2420,6 +2420,16 @@ int cs42l42_init(struct cs42l42_private *cs42l42) (1 << CS42L42_ADC_PDN_SHIFT) | (0 << CS42L42_PDN_ALL_SHIFT)); + /* + * Configure a faster digital ramp time, to avoid an audible + * fade-in when streams start. + */ + regmap_update_bits(cs42l42->regmap, CS42L42_SFTRAMP_RATE, + CS42L42_SFTRAMP_ASR_RATE_MASK | + CS42L42_SFTRAMP_DSR_RATE_MASK, + (10 << CS42L42_SFTRAMP_ASR_RATE_SHIFT) | + (1 << CS42L42_SFTRAMP_DSR_RATE_SHIFT)); + ret = cs42l42_handle_device_data(cs42l42->dev, cs42l42); if (ret != 0) goto err_shutdown; From 15b54b0b08f8f8649a78efbfc5e4e1423ad31315 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 16 Apr 2023 18:53:40 +0900 Subject: [PATCH 0266/1020] ASoC: apple: mca: Move clock shutdown to be shutdown Codecs are set to mute after hw_free, so yanking the clock out from under them in hw_free leads to breakage. Move the clock shutdown to the shutdown op, which is late enough. Signed-off-by: Hector Martin --- sound/soc/apple/mca.c | 48 ++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index fb23cb27b00ad7..477819e818811f 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -355,33 +355,6 @@ static int mca_be_prepare(struct snd_pcm_substream *substream, return 0; } -static int mca_be_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct mca_cluster *cl = mca_dai_to_cluster(dai); - struct mca_data *mca = cl->host; - struct mca_cluster *fe_cl; - - if (cl->port_driver < 0) - return -EINVAL; - - /* - * We are operating on a foreign cluster here, but since we - * belong to the same PCM, accesses should have been - * synchronized at ASoC level. - */ - fe_cl = &mca->clusters[cl->port_driver]; - if (!mca_fe_clocks_in_use(fe_cl)) - return 0; /* Nothing to do */ - - cl->clocks_in_use[substream->stream] = false; - - if (!mca_fe_clocks_in_use(fe_cl)) - mca_fe_disable_clocks(fe_cl); - - return 0; -} - static unsigned int mca_crop_mask(unsigned int mask, int nchans) { while (hweight32(mask) > nchans) @@ -779,6 +752,26 @@ static void mca_be_shutdown(struct snd_pcm_substream *substream, struct mca_cluster *cl = mca_dai_to_cluster(dai); struct mca_data *mca = cl->host; + if (cl->clocks_in_use[substream->stream] && + !WARN_ON(cl->port_driver < 0)) { + struct mca_cluster *fe_cl = &mca->clusters[cl->port_driver]; + + /* + * Typically the CODECs we are paired with will require clocks + * to be present at time of mute with the 'mute_stream' op. + * We need to disable the clocks here at the earliest (hw_free + * would be too early). + * + * We are operating on a foreign cluster here, but since we + * belong to the same PCM, accesses should have been + * synchronized at ASoC level. + */ + cl->clocks_in_use[substream->stream] = false; + + if (!mca_fe_clocks_in_use(fe_cl)) + mca_fe_disable_clocks(fe_cl); + } + cl->port_started[substream->stream] = false; if (!mca_be_started(cl)) { @@ -796,7 +789,6 @@ static void mca_be_shutdown(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops mca_be_ops = { .prepare = mca_be_prepare, - .hw_free = mca_be_hw_free, .startup = mca_be_startup, .shutdown = mca_be_shutdown, }; From 35bdbd8b072330304696993af3460ad67b39a60a Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sun, 3 Sep 2023 17:09:59 +1000 Subject: [PATCH 0267/1020] ASoC: macaudio: alias j415 kcontrols to j314 Signed-off-by: James Calligeros --- sound/soc/apple/macaudio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index e24006ca79c8fb..b5543f9caf44c1 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -946,6 +946,7 @@ static const struct of_device_id macaudio_snd_device_id[] = { { .compatible = "apple,j314-macaudio", .data = macaudio_j314_fixup_controls }, { .compatible = "apple,j375-macaudio", .data = macaudio_j375_fixup_controls }, { .compatible = "apple,j413-macaudio", .data = macaudio_j314_fixup_controls }, + { .compatible = "apple,j415-macaudio", .data = macaudio_j314_fixup_controls }, { .compatible = "apple,j493-macaudio", .data = macaudio_j493_fixup_controls }, { .compatible = "apple,macaudio"}, { } From 758802e43a6e8823b56c8c7b610910175a0bc06a Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 20 Oct 2023 03:35:24 +0900 Subject: [PATCH 0268/1020] ASoC: cs42l84: Fix capture gain range & add TLVs Signed-off-by: Hector Martin --- sound/soc/codecs/cs42l84.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c index cc4dff97e7a42d..d8fc7bdb89bb00 100644 --- a/sound/soc/codecs/cs42l84.c +++ b/sound/soc/codecs/cs42l84.c @@ -186,15 +186,17 @@ static int cs42l84_get_dac_vol(struct snd_kcontrol *kctl, /* TODO */ static const DECLARE_TLV_DB_SCALE(cs42l84_dac_tlv, -12800, 50, true); +static const DECLARE_TLV_DB_SCALE(cs42l84_adc_tlv, -1200, 50, false); +static const DECLARE_TLV_DB_SCALE(cs42l84_pre_tlv, 0, 1000, false); static const struct snd_kcontrol_new cs42l84_snd_controls[] = { SOC_DOUBLE_R_S_EXT_TLV("DAC Playback Volume", CS42L84_DAC_CHA_VOL_LSB, CS42L84_DAC_CHB_VOL_LSB, 0, -256, 24, 8, 0, cs42l84_get_dac_vol, cs42l84_put_dac_vol, cs42l84_dac_tlv), - SOC_SINGLE("ADC Preamp Gain", CS42L84_ADC_CTL1, - CS42L84_ADC_CTL1_PREAMP_GAIN_SHIFT, 2, 0), - SOC_SINGLE("ADC PGA Gain", CS42L84_ADC_CTL1, - CS42L84_ADC_CTL1_PGA_GAIN_SHIFT, 31, 0), + SOC_SINGLE_TLV("ADC Preamp Capture Volume", CS42L84_ADC_CTL1, + CS42L84_ADC_CTL1_PREAMP_GAIN_SHIFT, 2, 0, cs42l84_pre_tlv), + SOC_SINGLE_TLV("ADC PGA Capture Volume", CS42L84_ADC_CTL1, + CS42L84_ADC_CTL1_PGA_GAIN_SHIFT, 24, 0, cs42l84_adc_tlv), SOC_SINGLE("ADC WNF Switch", CS42L84_ADC_CTL4, CS42L84_ADC_CTL4_WNF_EN_SHIFT, 1, 0), SOC_SINGLE("WNF Corner Frequency", CS42L84_ADC_CTL4, From dd1b4fb59b890327d2c174b688aad32a9fcf6fb1 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 9 Oct 2023 20:45:36 +0900 Subject: [PATCH 0269/1020] ALSA: control: Add kcontrol callbacks for lock/unlock This allows drivers to implement policy around locking/unlocking controls, such as enforcing that a group of controls may only be locked by the same process/file, and taking actions when the controls lock/unlock (such as granting special access on lock and resetting values on unlock). This is, in particular, useful to implement volume safety controls, such that only a particular process (that locks controls and completes a handshake) may increase volumes above a given safe limit. It also allows the volume to be automatically lowered if that process dies (which will trigger an implicit unlock). Signed-off-by: Hector Martin --- include/sound/control.h | 7 +++++++ sound/core/control.c | 16 ++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/include/sound/control.h b/include/sound/control.h index 9a4f4f7138da8f..67bae715835a52 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -14,9 +14,12 @@ #define snd_kcontrol_chip(kcontrol) ((kcontrol)->private_data) struct snd_kcontrol; +struct snd_ctl_file; typedef int (snd_kcontrol_info_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_info * uinfo); typedef int (snd_kcontrol_get_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); typedef int (snd_kcontrol_put_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); +typedef int (snd_kcontrol_lock_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_file *owner); +typedef void (snd_kcontrol_unlock_t) (struct snd_kcontrol * kcontrol); typedef int (snd_kcontrol_tlv_rw_t)(struct snd_kcontrol *kcontrol, int op_flag, /* SNDRV_CTL_TLV_OP_XXX */ unsigned int size, @@ -55,6 +58,8 @@ struct snd_kcontrol_new { snd_kcontrol_info_t *info; snd_kcontrol_get_t *get; snd_kcontrol_put_t *put; + snd_kcontrol_lock_t *lock; + snd_kcontrol_unlock_t *unlock; union { snd_kcontrol_tlv_rw_t *c; const unsigned int *p; @@ -74,6 +79,8 @@ struct snd_kcontrol { snd_kcontrol_info_t *info; snd_kcontrol_get_t *get; snd_kcontrol_put_t *put; + snd_kcontrol_lock_t *lock; + snd_kcontrol_unlock_t *unlock; union { snd_kcontrol_tlv_rw_t *c; const unsigned int *p; diff --git a/sound/core/control.c b/sound/core/control.c index fb0c60044f7b3b..35df047f151752 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -123,10 +123,12 @@ static int snd_ctl_release(struct inode *inode, struct file *file) scoped_guard(rwsem_write, &card->controls_rwsem) { list_for_each_entry(control, &card->controls, list) for (idx = 0; idx < control->count; idx++) - if (control->vd[idx].owner == ctl) + if (control->vd[idx].owner == ctl) { control->vd[idx].owner = NULL; + if (control->unlock) + control->unlock(control); + } } - snd_fasync_free(ctl->fasync); snd_ctl_empty_read_queue(ctl); put_pid(ctl->pid); @@ -303,6 +305,8 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, kctl->info = ncontrol->info; kctl->get = ncontrol->get; kctl->put = ncontrol->put; + kctl->lock = ncontrol->lock; + kctl->unlock = ncontrol->unlock; kctl->tlv.p = ncontrol->tlv.p; kctl->private_value = ncontrol->private_value; @@ -1384,6 +1388,12 @@ static int snd_ctl_elem_lock(struct snd_ctl_file *file, vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)]; if (vd->owner) return -EBUSY; + + if (kctl->lock) { + int err = kctl->lock(kctl, file); + if (err < 0) + return err; + } vd->owner = file; return 0; } @@ -1408,6 +1418,8 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file, if (vd->owner != file) return -EPERM; vd->owner = NULL; + if (kctl->unlock) + kctl->unlock(kctl); return 0; } From d07ac3e69638b7db58e500c281386dddd2e6dfc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 19 Jan 2023 07:45:47 +0100 Subject: [PATCH 0270/1020] ASoC: macaudio: Condition selecting NCO driver on COMMON_CLK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only select the NCO driver's symbol if COMMON_CLK is selected, otherwise we risk misconfiguration. Signed-off-by: Martin Povišer --- sound/soc/apple/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/apple/Kconfig b/sound/soc/apple/Kconfig index 992e416108be5f..9fb902340fff6e 100644 --- a/sound/soc/apple/Kconfig +++ b/sound/soc/apple/Kconfig @@ -13,7 +13,7 @@ config SND_SOC_APPLE_MACAUDIO select SND_SOC_APPLE_MCA select SND_SIMPLE_CARD_UTILS select APPLE_ADMAC - select COMMON_CLK_APPLE_NCO + select COMMON_CLK_APPLE_NCO if COMMON_CLK select SND_SOC_TAS2764 select SND_SOC_TAS2770 select SND_SOC_CS42L83 From 63fe515c0346c6f71a86d99e04d66d1801b936ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 20 Jan 2023 20:59:52 +0100 Subject: [PATCH 0271/1020] ASoC: macaudio: Tune DT parsing error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/apple/macaudio.c | 48 ++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index b5543f9caf44c1..83cef7eda7f1bd 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -206,12 +206,14 @@ static int macaudio_parse_of_be_dai_link(struct macaudio_snd_data *ma, for_each_link_codecs(link, i, comp) { ret = macaudio_parse_of_component(codec, codec_base + i, comp); if (ret) - return 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 ret; + return dev_err_probe(ma->card.dev, ret, "parsing CPU DAI of link '%s' at %pOF\n", + link->name, codec); link->name = link->cpus[0].dai_name; @@ -232,7 +234,7 @@ static int macaudio_parse_of(struct macaudio_snd_data *ma) ret = snd_soc_of_parse_card_name(card, "model"); if (ret) { - dev_err(dev, "Error parsing card name: %d\n", ret); + dev_err_probe(dev, ret, "parsing card name\n"); return ret; } @@ -245,8 +247,8 @@ static int macaudio_parse_of(struct macaudio_snd_data *ma) cpu = of_get_child_by_name(np, "cpu"); if (!cpu) { - dev_err(dev, "missing CPU DAI node at %pOF\n", np); - ret = -EINVAL; + ret = dev_err_probe(dev, -EINVAL, + "missing CPU DAI node at %pOF\n", np);; goto err_free; } @@ -254,8 +256,8 @@ static int macaudio_parse_of(struct macaudio_snd_data *ma) "#sound-dai-cells"); if (num_cpus <= 0) { - dev_err(card->dev, "missing sound-dai property at %pOF\n", cpu); - ret = -EINVAL; + ret = dev_err_probe(card->dev, -EINVAL, + "missing sound-dai property at %pOF\n", cpu); goto err_free; } of_node_put(cpu); @@ -296,10 +298,12 @@ static int macaudio_parse_of(struct macaudio_snd_data *ma) ret = of_property_read_string(np, "link-name", &link_name); if (ret) { - dev_err(card->dev, "missing link name\n"); + 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 = !strcmp(link_name, "Speaker") || !strcmp(link_name, "Speakers"); if (speakers) @@ -309,31 +313,34 @@ static int macaudio_parse_of(struct macaudio_snd_data *ma) codec = of_get_child_by_name(np, "codec"); if (!codec || !cpu) { - dev_err(dev, "missing DAI specifications for '%s'\n", link_name); - ret = -EINVAL; + 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) { - dev_err(card->dev, "missing sound-dai property at %pOF\n", cpu); - ret = -EINVAL; + 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) { - dev_err(card->dev, "missing sound-dai property at %pOF\n", codec); - ret = -EINVAL; + 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) { - dev_err(card->dev, "bad combination of CODEC (%d) and CPU (%d) number at %pOF\n", + ret = dev_err_probe(card->dev, -EINVAL, + "bad combination of CODEC (%d) and CPU (%d) number at %pOF\n", num_codecs, num_bes, np); - ret = -EINVAL; goto err_free; } @@ -363,6 +370,13 @@ static int macaudio_parse_of(struct macaudio_snd_data *ma) 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) @@ -994,7 +1008,7 @@ static int macaudio_snd_platform_probe(struct platform_device *pdev) ret = macaudio_parse_of(data); if (ret) - return dev_err_probe(&pdev->dev, ret, "failed OF parsing\n"); + return ret; for_each_card_prelinks(card, i, link) { if (link->no_pcm) { From 4824855ce28e00add89c1896a8b943dece86a722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 16 Sep 2022 13:43:25 +0200 Subject: [PATCH 0272/1020] ASoC: apple: mca: Separate data & clock port setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up until now FEs were always the clock providers -- feeding the clocks on any ports (BEs) they are attached to. This will soon change and FEs will be allowed to be clock consumers. Once that happens, the routing of clocks and data will to some degree decouple. In advance of the change, make preparations: * Narrow down semantics of what was formerly the 'port_driver' field to refer to clocks only. * On 'startup' of BEs, separate the clock and data aspects of the port setup. Signed-off-by: Martin Povišer --- sound/soc/apple/mca.c | 67 ++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index 477819e818811f..72235a9f1e7c6a 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -133,8 +133,8 @@ struct mca_cluster { struct clk *clk_parent; struct dma_chan *dma_chans[SNDRV_PCM_STREAM_LAST + 1]; - bool port_started[SNDRV_PCM_STREAM_LAST + 1]; - int port_driver; /* The cluster driving this cluster's port */ + bool port_clk_started[SNDRV_PCM_STREAM_LAST + 1]; + int port_clk_driver; /* The cluster driving this cluster's port */ bool clocks_in_use[SNDRV_PCM_STREAM_LAST + 1]; struct device_link *pd_link; @@ -157,7 +157,7 @@ struct mca_data { struct reset_control *rstc; struct device_link *pd_link; - /* Mutex for accessing port_driver of foreign clusters */ + /* Mutex for accessing port_clk_driver of foreign clusters */ struct mutex port_mutex; int nclusters; @@ -311,7 +311,7 @@ static bool mca_fe_clocks_in_use(struct mca_cluster *cl) for (i = 0; i < mca->nclusters; i++) { be_cl = &mca->clusters[i]; - if (be_cl->port_driver != cl->no) + if (be_cl->port_clk_driver != cl->no) continue; for_each_pcm_streams(stream) { @@ -333,10 +333,10 @@ static int mca_be_prepare(struct snd_pcm_substream *substream, struct mca_cluster *fe_cl; int ret; - if (cl->port_driver < 0) + if (cl->port_clk_driver < 0) return -EINVAL; - fe_cl = &mca->clusters[cl->port_driver]; + fe_cl = &mca->clusters[cl->port_clk_driver]; /* * Typically the CODECs we are paired with will require clocks @@ -683,12 +683,15 @@ static const struct snd_soc_dai_ops mca_fe_ops = { .trigger = mca_fe_trigger, }; -static bool mca_be_started(struct mca_cluster *cl) +/* + * Is there a FE attached which will be feeding this port's clocks? + */ +static bool mca_be_clk_started(struct mca_cluster *cl) { int stream; for_each_pcm_streams(stream) - if (cl->port_started[stream]) + if (cl->port_clk_started[stream]) return true; return false; } @@ -719,29 +722,35 @@ static int mca_be_startup(struct snd_pcm_substream *substream, fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); - if (mca_be_started(cl)) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + writel_relaxed(PORT_DATA_SEL_TXA(fe_cl->no), + cl->base + REG_PORT_DATA_SEL); + mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_TX_DATA, + PORT_ENABLES_TX_DATA); + } + + if (mca_be_clk_started(cl)) { /* * Port is already started in the other direction. * Make sure there isn't a conflict with another cluster - * driving the port. + * driving the port clocks. */ - if (cl->port_driver != fe_cl->no) + if (cl->port_clk_driver != fe_cl->no) return -EINVAL; - cl->port_started[substream->stream] = true; + cl->port_clk_started[substream->stream] = true; return 0; } - writel_relaxed(PORT_ENABLES_CLOCKS | PORT_ENABLES_TX_DATA, - cl->base + REG_PORT_ENABLES); writel_relaxed(FIELD_PREP(PORT_CLOCK_SEL, fe_cl->no + 1), cl->base + REG_PORT_CLOCK_SEL); - writel_relaxed(PORT_DATA_SEL_TXA(fe_cl->no), - cl->base + REG_PORT_DATA_SEL); + mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_CLOCKS, + PORT_ENABLES_CLOCKS); + mutex_lock(&mca->port_mutex); - cl->port_driver = fe_cl->no; + cl->port_clk_driver = fe_cl->no; mutex_unlock(&mca->port_mutex); - cl->port_started[substream->stream] = true; + cl->port_clk_started[substream->stream] = true; return 0; } @@ -753,8 +762,8 @@ static void mca_be_shutdown(struct snd_pcm_substream *substream, struct mca_data *mca = cl->host; if (cl->clocks_in_use[substream->stream] && - !WARN_ON(cl->port_driver < 0)) { - struct mca_cluster *fe_cl = &mca->clusters[cl->port_driver]; + !WARN_ON(cl->port_clk_driver < 0)) { + struct mca_cluster *fe_cl = &mca->clusters[cl->port_clk_driver]; /* * Typically the CODECs we are paired with will require clocks @@ -772,17 +781,21 @@ static void mca_be_shutdown(struct snd_pcm_substream *substream, mca_fe_disable_clocks(fe_cl); } - cl->port_started[substream->stream] = false; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_TX_DATA, 0); + writel_relaxed(0, cl->base + REG_PORT_DATA_SEL); + } - if (!mca_be_started(cl)) { + cl->port_clk_started[substream->stream] = false; + if (!mca_be_clk_started(cl)) { /* * Were we the last direction to shutdown? - * Turn off the lights. + * Turn off the lights (clocks). */ - writel_relaxed(0, cl->base + REG_PORT_ENABLES); - writel_relaxed(0, cl->base + REG_PORT_DATA_SEL); + mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_CLOCKS, 0); + writel_relaxed(0, cl->base + REG_PORT_CLOCK_SEL); mutex_lock(&mca->port_mutex); - cl->port_driver = -1; + cl->port_clk_driver = -1; mutex_unlock(&mca->port_mutex); } } @@ -1088,7 +1101,7 @@ static int apple_mca_probe(struct platform_device *pdev) cl->host = mca; cl->no = i; cl->base = base + CLUSTER_STRIDE * i; - cl->port_driver = -1; + cl->port_clk_driver = -1; cl->clk_parent = of_clk_get(pdev->dev.of_node, i); if (IS_ERR(cl->clk_parent)) { dev_err(&pdev->dev, "unable to obtain clock %d: %ld\n", From 67e21112e3cad8fa67269ed28aee57c4643a84e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 16 Sep 2022 14:18:16 +0200 Subject: [PATCH 0273/1020] ASoC: apple: mca: Factor out mca_be_get_fe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a function that we also want to use from within mca_be_shutdown, so factor it out. Signed-off-by: Martin Povišer --- sound/soc/apple/mca.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index 72235a9f1e7c6a..25c2e9cd3deaec 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -696,30 +696,35 @@ static bool mca_be_clk_started(struct mca_cluster *cl) return false; } -static int mca_be_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static struct snd_soc_pcm_runtime *mca_be_get_fe(struct snd_soc_pcm_runtime *be, + int stream) { - struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); - struct snd_soc_pcm_runtime *fe; - struct mca_cluster *cl = mca_dai_to_cluster(dai); - struct mca_cluster *fe_cl; - struct mca_data *mca = cl->host; + struct snd_soc_pcm_runtime *fe = NULL; struct snd_soc_dpcm *dpcm; - fe = NULL; - - for_each_dpcm_fe(be, substream->stream, dpcm) { + for_each_dpcm_fe(be, stream, dpcm) { if (fe && dpcm->fe != fe) { - dev_err(mca->dev, "many FE per one BE unsupported\n"); - return -EINVAL; + dev_err(be->dev, "many FE per one BE unsupported\n"); + return NULL; } fe = dpcm->fe; } + return fe; +} + +static int mca_be_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); + struct mca_cluster *cl = mca_dai_to_cluster(dai); + struct mca_cluster *fe_cl; + struct mca_data *mca = cl->host; + if (!fe) return -EINVAL; - fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { From 5c6a56e35e08199dd68d1e42aeda11a78b277a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 16 Sep 2022 14:25:04 +0200 Subject: [PATCH 0274/1020] ASoC: apple: mca: Support FEs being clock consumers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support FEs being I2S clock consumers. This does not mean we support accepting clocks from outside the SoC (although it paves the way for that support in the future), but it means multiple FEs can attach to one BE, one being clock producer and the rest clock consumers. This is useful for grabbing I/V sense data on some machines, since in such a scenario the format of the sense data on the I2S bus differs from that of the audio data (the two formats differing in slot width). With two FEs attached to the bus, we can split the responsibilities and command different slot widths to the two. Signed-off-by: Martin Povišer --- sound/soc/apple/mca.c | 109 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 88 insertions(+), 21 deletions(-) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index 25c2e9cd3deaec..89320430afc424 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -133,6 +133,8 @@ struct mca_cluster { struct clk *clk_parent; struct dma_chan *dma_chans[SNDRV_PCM_STREAM_LAST + 1]; + bool clk_provider; + bool port_clk_started[SNDRV_PCM_STREAM_LAST + 1]; int port_clk_driver; /* The cluster driving this cluster's port */ @@ -256,11 +258,32 @@ static int mca_fe_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } +static int mca_fe_get_port(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *be; + struct snd_soc_dpcm *dpcm; + + be = NULL; + for_each_dpcm_be(fe, substream->stream, dpcm) { + be = dpcm->be; + break; + } + + if (!be) + return -EINVAL; + + return mca_dai_to_cluster(snd_soc_rtd_to_cpu(be, 0))->no; +} + static int mca_fe_enable_clocks(struct mca_cluster *cl) { struct mca_data *mca = cl->host; int ret; + if (!cl->clk_provider) + return -EINVAL; + ret = clk_prepare_enable(cl->clk_parent); if (ret) { dev_err(mca->dev, @@ -334,7 +357,7 @@ static int mca_be_prepare(struct snd_pcm_substream *substream, int ret; if (cl->port_clk_driver < 0) - return -EINVAL; + return 0; fe_cl = &mca->clusters[cl->port_clk_driver]; @@ -355,6 +378,44 @@ static int mca_be_prepare(struct snd_pcm_substream *substream, return 0; } +static int mca_fe_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mca_cluster *cl = mca_dai_to_cluster(dai); + struct mca_data *mca = cl->host; + + if (cl->clk_provider) + return 0; + + if (!mca_fe_clocks_in_use(cl)) { + int port = mca_fe_get_port(substream); + writel_relaxed(port + 6 + 1, + cl->base + REG_SYNCGEN_MCLK_SEL); + mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, + SYNCGEN_STATUS_EN); + } + cl->clocks_in_use[substream->stream] = true; + + return 0; +} + +static int mca_fe_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mca_cluster *cl = mca_dai_to_cluster(dai); + + if (cl->clk_provider) + return 0; + + cl->clocks_in_use[substream->stream] = false; + if (mca_fe_clocks_in_use(cl)) + return 0; + + mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, 0); + + return 0; +} + static unsigned int mca_crop_mask(unsigned int mask, int nchans) { while (hweight32(mask) > nchans) @@ -480,9 +541,18 @@ static int mca_fe_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) u32 serdes_conf = 0; u32 bitstart; - if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != - SND_SOC_DAIFMT_BP_FP) + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_BP_FP: + cl->clk_provider = true; + break; + + case SND_SOC_DAIFMT_BC_FC: + cl->clk_provider = false; + break; + + default: goto err; + } switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: @@ -539,24 +609,6 @@ static int mca_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) return 0; } -static int mca_fe_get_port(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); - struct snd_soc_pcm_runtime *be; - struct snd_soc_dpcm *dpcm; - - be = NULL; - for_each_dpcm_be(fe, substream->stream, dpcm) { - be = dpcm->be; - break; - } - - if (!be) - return -EINVAL; - - return mca_dai_to_cluster(snd_soc_rtd_to_cpu(be, 0))->no; -} - static int mca_fe_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -681,6 +733,8 @@ static const struct snd_soc_dai_ops mca_fe_ops = { .set_tdm_slot = mca_fe_set_tdm_slot, .hw_params = mca_fe_hw_params, .trigger = mca_fe_trigger, + .prepare = mca_fe_prepare, + .hw_free = mca_fe_hw_free, }; /* @@ -734,6 +788,9 @@ static int mca_be_startup(struct snd_pcm_substream *substream, PORT_ENABLES_TX_DATA); } + if (!fe_cl->clk_provider) + return 0; + if (mca_be_clk_started(cl)) { /* * Port is already started in the other direction. @@ -763,7 +820,10 @@ static int mca_be_startup(struct snd_pcm_substream *substream, static void mca_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); struct mca_cluster *cl = mca_dai_to_cluster(dai); + struct mca_cluster *fe_cl; struct mca_data *mca = cl->host; if (cl->clocks_in_use[substream->stream] && @@ -786,11 +846,18 @@ static void mca_be_shutdown(struct snd_pcm_substream *substream, mca_fe_disable_clocks(fe_cl); } + if (!fe) + return; + fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_TX_DATA, 0); writel_relaxed(0, cl->base + REG_PORT_DATA_SEL); } + if (!fe_cl->clk_provider) + return; + cl->port_clk_started[substream->stream] = false; if (!mca_be_clk_started(cl)) { /* From 7f7126e64c86b678ce138ef76744b336a74a29eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 14 Dec 2022 13:07:14 +0100 Subject: [PATCH 0275/1020] ASoC: apple: mca: Fix SYNCGEN enable on FE clock consumers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/apple/mca.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index 89320430afc424..d54d071270d21c 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -141,6 +141,9 @@ struct mca_cluster { bool clocks_in_use[SNDRV_PCM_STREAM_LAST + 1]; struct device_link *pd_link; + /* In case of clock consumer FE */ + int syncgen_in_use; + unsigned int bclk_ratio; /* Masks etc. picked up via the set_tdm_slot method */ @@ -387,14 +390,24 @@ static int mca_fe_prepare(struct snd_pcm_substream *substream, if (cl->clk_provider) return 0; - if (!mca_fe_clocks_in_use(cl)) { + if (!cl->syncgen_in_use) { int port = mca_fe_get_port(substream); + + cl->pd_link = device_link_add(mca->dev, cl->pd_dev, + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (!cl->pd_link) { + dev_err(mca->dev, + "cluster %d: unable to prop-up power domain\n", cl->no); + return -EINVAL; + } + writel_relaxed(port + 6 + 1, cl->base + REG_SYNCGEN_MCLK_SEL); mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, SYNCGEN_STATUS_EN); } - cl->clocks_in_use[substream->stream] = true; + cl->syncgen_in_use |= 1 << substream->stream; return 0; } @@ -407,11 +420,13 @@ static int mca_fe_hw_free(struct snd_pcm_substream *substream, if (cl->clk_provider) return 0; - cl->clocks_in_use[substream->stream] = false; - if (mca_fe_clocks_in_use(cl)) + cl->syncgen_in_use &= ~(1 << substream->stream); + if (cl->syncgen_in_use) return 0; mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, 0); + if (cl->pd_link) + device_link_del(cl->pd_link); return 0; } From 16624c3774c7a5fa413c7e0ed6480b38bdc9cc80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 14 Dec 2022 13:06:26 +0100 Subject: [PATCH 0276/1020] ASoC: macaudio: Start speaker sense capture support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/apple/macaudio.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 83cef7eda7f1bd..36c88235c233a1 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -57,6 +57,7 @@ struct macaudio_snd_data { struct macaudio_link_props { /* frontend props */ unsigned int bclk_ratio; + bool is_sense; /* backend props */ bool is_speakers; @@ -82,6 +83,11 @@ SND_SOC_DAILINK_DEFS(secondary, 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", @@ -106,6 +112,17 @@ static struct snd_soc_dai_link macaudio_fe_links[] = { .dai_fmt = MACAUDIO_DAI_FMT, SND_SOC_DAILINK_REG(secondary), }, + { + .name = "Speaker Sense", + .stream_name = "Speaker Sense", + .dynamic = 1, + .dpcm_capture = 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[] = { @@ -133,6 +150,9 @@ static struct macaudio_link_props macaudio_fe_link_props[] = { * those fancy speaker arrays. */ .bclk_ratio = 256, + }, + { + .is_sense = 1, } }; @@ -626,6 +646,9 @@ static int macaudio_fe_init(struct snd_soc_pcm_runtime *rtd) 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); } @@ -686,6 +709,13 @@ static int macaudio_add_backend_dai_route(struct snd_soc_card *card, struct snd_ 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", @@ -929,6 +959,7 @@ static const struct snd_soc_dapm_widget macaudio_snd_widgets[] = { 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 const struct snd_kcontrol_new macaudio_controls[] = { @@ -952,6 +983,9 @@ static const struct snd_soc_dapm_route macaudio_dapm_routes[] = { /* Capture paths */ { "PCM0 RX", NULL, "Headset Capture" }, + + /* Sense paths */ + { "PCM2 RX", NULL, "Speaker Sense Capture" }, }; static const struct of_device_id macaudio_snd_device_id[] = { From b9e02557a7f04d5f1887967383e797bba7a0c566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 14 Dec 2022 13:07:51 +0100 Subject: [PATCH 0277/1020] ASoC: macaudio: Tweak "no audio route" message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/apple/macaudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 36c88235c233a1..e002327a39ab25 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -527,7 +527,7 @@ static int macaudio_fe_hw_params(struct snd_pcm_substream *substream, } if (!be) { - dev_err(rtd->dev, "opening PCM device '%s' with no audio route configured (bad settings applied to the sound card)\n", + dev_err(rtd->dev, "opening PCM device '%s' with no audio route configured by the user\n", rtd->dai_link->name); return -EINVAL; } From 833d1329f56e43201ef9ad8178aa2fd6abd2aeb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 19 Jan 2023 07:43:56 +0100 Subject: [PATCH 0278/1020] ASoC: macaudio: Do not constrain sense PCM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/apple/macaudio.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index e002327a39ab25..ae9a8a977c09ce 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -499,8 +499,12 @@ 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 ret; + if (props->is_sense) + return 0; + /* The FEs must never have more channels than the hardware */ ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 0, ma->max_channels); From 01873f1a55605f357da709fc3660581b1437aa78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 19 Jan 2023 07:22:14 +0100 Subject: [PATCH 0279/1020] ASoC: tas2770: Factor out set_ivsense_slots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new explicit function for the setting of I/V sense TDM slots. Signed-off-by: Martin Povišer --- sound/soc/codecs/tas2770.c | 40 +++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 99bf402eb56673..a1c57d2b4dac81 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -191,6 +191,31 @@ static int tas2770_mute(struct snd_soc_dai *dai, int mute, int direction) return tas2770_update_pwr_ctrl(tas2770); } +static int tas2770_set_ivsense_transmit(struct tas2770_priv *tas2770, + int i_slot, int v_slot) +{ + struct snd_soc_component *component = tas2770->component; + int ret; + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG5, + TAS2770_TDM_CFG_REG5_VSNS_MASK | + TAS2770_TDM_CFG_REG5_50_MASK, + TAS2770_TDM_CFG_REG5_VSNS_ENABLE | + v_slot); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG6, + TAS2770_TDM_CFG_REG6_ISNS_MASK | + TAS2770_TDM_CFG_REG6_50_MASK, + TAS2770_TDM_CFG_REG6_ISNS_ENABLE | + i_slot); + if (ret < 0) + return ret; + + return 0; +} + static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth) { int ret; @@ -223,19 +248,8 @@ static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth) if (ret < 0) return ret; - ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG5, - TAS2770_TDM_CFG_REG5_VSNS_MASK | - TAS2770_TDM_CFG_REG5_50_MASK, - TAS2770_TDM_CFG_REG5_VSNS_ENABLE | - tas2770->v_sense_slot); - if (ret < 0) - return ret; - - ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG6, - TAS2770_TDM_CFG_REG6_ISNS_MASK | - TAS2770_TDM_CFG_REG6_50_MASK, - TAS2770_TDM_CFG_REG6_ISNS_ENABLE | - tas2770->i_sense_slot); + ret = tas2770_set_ivsense_transmit(tas2770, tas2770->i_sense_slot, + tas2770->v_sense_slot); if (ret < 0) return ret; From 8fe82a83a1bb2f79c918c1b2e2d95846b36f048f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 19 Jan 2023 08:08:30 +0100 Subject: [PATCH 0280/1020] ASoC: tas2770: Fix and redo I/V sense TDM slot setting logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The former code sets the V slot from inside set_bitwidth according to the bitwidth of the PCM format. That's wrong, since: * It overrides the V slot parsed from DT binding. * The V slot is set shifted behind the I slot by the length of the PCM bitwidth, but the PCM bitwidth has no assured relation to the TDM slot width. Replace the former logic by setting up the I/V sense transmission only in case of both I/V slots being specified in devicetree, and never override those values. In case the slots are left unspecified, disable the transmission completely. There's an improbable case someone is relying on the old behavior, but if so, that's a setup that only works by accident, and cannot be sanely supported going forward. There's no indication anyone is consuming the I/V sense data up to today, so break the former behavior. Signed-off-by: Martin Povišer --- sound/soc/codecs/tas2770.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index a1c57d2b4dac81..db6731e9cff2d9 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -226,19 +226,16 @@ static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth) ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2, TAS2770_TDM_CFG_REG2_RXW_MASK, TAS2770_TDM_CFG_REG2_RXW_16BITS); - tas2770->v_sense_slot = tas2770->i_sense_slot + 2; break; case SNDRV_PCM_FORMAT_S24_LE: ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2, TAS2770_TDM_CFG_REG2_RXW_MASK, TAS2770_TDM_CFG_REG2_RXW_24BITS); - tas2770->v_sense_slot = tas2770->i_sense_slot + 4; break; case SNDRV_PCM_FORMAT_S32_LE: ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2, TAS2770_TDM_CFG_REG2_RXW_MASK, TAS2770_TDM_CFG_REG2_RXW_32BITS); - tas2770->v_sense_slot = tas2770->i_sense_slot + 4; break; default: @@ -248,11 +245,6 @@ static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth) if (ret < 0) return ret; - ret = tas2770_set_ivsense_transmit(tas2770, tas2770->i_sense_slot, - tas2770->v_sense_slot); - if (ret < 0) - return ret; - return 0; } @@ -507,6 +499,7 @@ static int tas2770_codec_probe(struct snd_soc_component *component) { struct tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component); + int ret; tas2770->component = component; @@ -518,6 +511,14 @@ static int tas2770_codec_probe(struct snd_soc_component *component) tas2770_reset(tas2770); regmap_reinit_cache(tas2770->regmap, &tas2770_i2c_regmap); + if (tas2770->i_sense_slot != -1 && tas2770->v_sense_slot != -1) { + ret = tas2770_set_ivsense_transmit(tas2770, tas2770->i_sense_slot, + tas2770->v_sense_slot); + + if (ret < 0) + return ret; + } + return 0; } @@ -645,7 +646,7 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770) dev_info(tas2770->dev, "Property %s is missing setting default slot\n", "ti,imon-slot-no"); - tas2770->i_sense_slot = 0; + tas2770->i_sense_slot = -1; } rc = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no", @@ -654,7 +655,7 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770) dev_info(tas2770->dev, "Property %s is missing setting default slot\n", "ti,vmon-slot-no"); - tas2770->v_sense_slot = 2; + tas2770->v_sense_slot = -1; } tas2770->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH); From 34248503bd6c52883a6c68af1588bff38ebe3848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 20 Jan 2023 12:17:09 +0100 Subject: [PATCH 0281/1020] ASoC: tas2764: Reinit cache on part reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the part is reset in component_probe, do not forget to reinit the regcache, otherwise the cache can get out of sync with the part's actual state. (This fix is similar to commit 0a0342ede303 which concerned the tas2770 driver.) Fixes: 827ed8a0fa50 ("ASoC: tas2764: Add the driver for the TAS2764") Signed-off-by: Martin Povišer --- sound/soc/codecs/tas2764.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 7b9cb764eeb0cf..0303762abdfe0f 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -541,6 +541,8 @@ static uint8_t sn012776_bop_presets[] = { 0x06, 0x3e, 0x37, 0x30, 0xff, 0xe6 }; +static const struct regmap_config tas2764_i2c_regmap; + static int tas2764_codec_probe(struct snd_soc_component *component) { struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); @@ -554,6 +556,7 @@ static int tas2764_codec_probe(struct snd_soc_component *component) } tas2764_reset(tas2764); + regmap_reinit_cache(tas2764->regmap, &tas2764_i2c_regmap); if (tas2764->irq) { ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK0, 0xff); From 5b2a0672ceb173350073ba86eb055956c6e90b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 20 Jan 2023 12:31:53 +0100 Subject: [PATCH 0282/1020] NOT UPSTREAMABLE: ASoC: tas2764: Redo I/V sense logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Only set up I/V sense transmission in case the slots are described in devicetree, never use defaults. * Move the enablement of I/V sense transmission away from hw_params up into component probe, do not condition it on the measurements itself being enabled. * Move the slot configuration from set_tdm_slot into component probe, so it's not separate from other configuration. Since this makes I/V sense unavailable in some configurations where it formerly was, and it also changes behavior depending on the pairing with a machine-level driver (depending on set_tdm_slot calls), it's probably not upstreamable as is. Signed-off-by: Martin Povišer --- sound/soc/codecs/tas2764.c | 61 ++++++++++++++------------------------ 1 file changed, 23 insertions(+), 38 deletions(-) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 0303762abdfe0f..f64e346ce73d87 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -258,7 +258,6 @@ static int tas2764_mute(struct snd_soc_dai *dai, int mute, int direction) static int tas2764_set_bitwidth(struct tas2764_priv *tas2764, int bitwidth) { struct snd_soc_component *component = tas2764->component; - int sense_en; int val; int ret; @@ -293,28 +292,6 @@ static int tas2764_set_bitwidth(struct tas2764_priv *tas2764, int bitwidth) if (val < 0) return val; - if (val & (1 << TAS2764_VSENSE_POWER_EN)) - sense_en = 0; - else - sense_en = TAS2764_TDM_CFG5_VSNS_ENABLE; - - ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG5, - TAS2764_TDM_CFG5_VSNS_ENABLE, - sense_en); - if (ret < 0) - return ret; - - if (val & (1 << TAS2764_ISENSE_POWER_EN)) - sense_en = 0; - else - sense_en = TAS2764_TDM_CFG6_ISNS_ENABLE; - - ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG6, - TAS2764_TDM_CFG6_ISNS_ENABLE, - sense_en); - if (ret < 0) - return ret; - return 0; } @@ -436,7 +413,6 @@ static int tas2764_set_dai_tdm_slot(struct snd_soc_dai *dai, int slots, int slot_width) { struct snd_soc_component *component = dai->component; - struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); int left_slot, right_slot; int slots_cfg; int slot_size; @@ -483,15 +459,26 @@ static int tas2764_set_dai_tdm_slot(struct snd_soc_dai *dai, if (ret < 0) return ret; - ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG5, + return 0; +} + +static int tas2764_set_ivsense_transmit(struct tas2764_priv *tas2764, int i_slot, int v_slot) +{ + int ret; + + ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG5, + TAS2764_TDM_CFG5_VSNS_ENABLE | TAS2764_TDM_CFG5_50_MASK, - tas2764->v_sense_slot); + TAS2764_TDM_CFG5_VSNS_ENABLE | + v_slot); if (ret < 0) return ret; - ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG6, + ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG6, + TAS2764_TDM_CFG6_ISNS_ENABLE | TAS2764_TDM_CFG6_50_MASK, - tas2764->i_sense_slot); + TAS2764_TDM_CFG6_ISNS_ENABLE | + i_slot); if (ret < 0) return ret; @@ -586,15 +573,13 @@ static int tas2764_codec_probe(struct snd_soc_component *component) dev_warn(tas2764->dev, "failed to request IRQ: %d\n", ret); } - ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG5, - TAS2764_TDM_CFG5_VSNS_ENABLE, 0); - if (ret < 0) - return ret; + if (tas2764->i_sense_slot != -1 && tas2764->v_sense_slot != -1) { + ret = tas2764_set_ivsense_transmit(tas2764, tas2764->i_sense_slot, + tas2764->v_sense_slot); - ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG6, - TAS2764_TDM_CFG6_ISNS_ENABLE, 0); - if (ret < 0) - return ret; + if (ret < 0) + return ret; + } if (tas2764->devid == DEVID_SN012776) { ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, @@ -731,12 +716,12 @@ static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764) ret = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no", &tas2764->i_sense_slot); if (ret) - tas2764->i_sense_slot = 0; + tas2764->i_sense_slot = -1; ret = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no", &tas2764->v_sense_slot); if (ret) - tas2764->v_sense_slot = 2; + tas2764->v_sense_slot = -1; return 0; } From c23a93d716c2041f951e39d757f9a73456f6191a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 23 Jan 2023 10:47:01 +0100 Subject: [PATCH 0283/1020] ASoC: macaudio: Tune constraining of FEs, add BCLK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/apple/macaudio.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index ae9a8a977c09ce..93cf3b3a2e63c9 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -45,6 +45,15 @@ 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 struct macaudio_snd_data { struct snd_soc_card card; @@ -500,19 +509,23 @@ 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 ret; + int max_rate, ret; if (props->is_sense) return 0; - /* The FEs must never have more channels than the hardware */ ret = snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_CHANNELS, 0, ma->max_channels); + SNDRV_PCM_HW_PARAM_CHANNELS, + 0, ma->max_channels); + if (ret < 0) + return ret; - if (ret < 0) { - dev_err(rtd->dev, "Failed to constrain FE %d! %d", rtd->dai_link->id, 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; } From 295793ced9940012f83b2d3655a382240aa92f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 24 Jan 2023 15:14:53 +0100 Subject: [PATCH 0284/1020] ASoC: apple: mca: Support capture on multiples BEs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When multiple BEs are linked to a FE, the former behavior was to source the data line from the DIN pin of the first BE only. Change this to ORing the DIN inputs of all linked BEs. As long as the unused slots on each BE's line are zeroed out and the slots on the BEs don't overlap, this will work out well. Signed-off-by: Martin Povišer --- sound/soc/apple/mca.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index d54d071270d21c..0b1cea66e2abaf 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -261,22 +261,18 @@ static int mca_fe_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } -static int mca_fe_get_port(struct snd_pcm_substream *substream) +static int mca_fe_get_portmask(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); - struct snd_soc_pcm_runtime *be; struct snd_soc_dpcm *dpcm; + int mask = 0; - be = NULL; for_each_dpcm_be(fe, substream->stream, dpcm) { - be = dpcm->be; - break; + int no = mca_dai_to_cluster(snd_soc_rtd_to_cpu(dpcm->be, 0))->no; + mask |= 1 << no; } - if (!be) - return -EINVAL; - - return mca_dai_to_cluster(snd_soc_rtd_to_cpu(be, 0))->no; + return mask; } static int mca_fe_enable_clocks(struct mca_cluster *cl) @@ -391,7 +387,7 @@ static int mca_fe_prepare(struct snd_pcm_substream *substream, return 0; if (!cl->syncgen_in_use) { - int port = mca_fe_get_port(substream); + int port = ffs(mca_fe_get_portmask(substream)) - 1; cl->pd_link = device_link_add(mca->dev, cl->pd_dev, DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | @@ -441,7 +437,7 @@ static unsigned int mca_crop_mask(unsigned int mask, int nchans) static int mca_configure_serdes(struct mca_cluster *cl, int serdes_unit, unsigned int mask, int slots, int nchans, - int slot_width, bool is_tx, int port) + int slot_width, bool is_tx, int portmask) { __iomem void *serdes_base = cl->base + serdes_unit; u32 serdes_conf, serdes_conf_mask; @@ -500,7 +496,7 @@ static int mca_configure_serdes(struct mca_cluster *cl, int serdes_unit, serdes_base + REG_RX_SERDES_SLOTMASK); writel_relaxed(~((u32)mca_crop_mask(mask, nchans)), serdes_base + REG_RX_SERDES_SLOTMASK + 0x4); - writel_relaxed(1 << port, + writel_relaxed(portmask, serdes_base + REG_RX_SERDES_PORT); } @@ -637,7 +633,7 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, unsigned long bclk_ratio; unsigned int tdm_slots, tdm_slot_width, tdm_mask; u32 regval, pad; - int ret, port, nchans_ceiled; + int ret, portmask, nchans_ceiled; if (!cl->tdm_slot_width) { /* @@ -686,13 +682,13 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, tdm_mask = (1 << tdm_slots) - 1; } - port = mca_fe_get_port(substream); - if (port < 0) - return port; + portmask = mca_fe_get_portmask(substream); + if (!portmask) + return -EINVAL; ret = mca_configure_serdes(cl, is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF, tdm_mask, tdm_slots, params_channels(params), - tdm_slot_width, is_tx, port); + tdm_slot_width, is_tx, portmask); if (ret) return ret; From 4d194329d98a4c95243a540180d895e682576a0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 24 Jan 2023 15:22:40 +0100 Subject: [PATCH 0285/1020] ASoC: tas2764: Configure zeroing of SDOUT slots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The codec has an option to zero out certain TDM slots on its SDOUT output according to a preconfigured mask (otherwise the output is, for the duration of unused slots, in a Hi-Z state). Configure this feature based on a mask read from the devicetree. Signed-off-by: Martin Povišer --- sound/soc/codecs/tas2764.c | 23 +++++++++++++++++++++++ sound/soc/codecs/tas2764.h | 11 +++++++++++ 2 files changed, 34 insertions(+) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index f64e346ce73d87..15e48175c0694f 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -42,6 +42,7 @@ struct tas2764_priv { int v_sense_slot; int i_sense_slot; + u32 sdout_zero_mask; bool dac_powered; bool unmuted; @@ -581,6 +582,23 @@ static int tas2764_codec_probe(struct snd_soc_component *component) return ret; } + if (tas2764->sdout_zero_mask) { + for (i = 0; i < 4; i++) { + ret = snd_soc_component_write(component, TAS2764_SDOUT_HIZ_1 + i, + (tas2764->sdout_zero_mask >> (i * 8)) & 0xff); + + if (ret < 0) + return ret; + } + + ret = snd_soc_component_update_bits(component, TAS2764_SDOUT_HIZ_9, + TAS2764_SDOUT_HIZ_9_FORCE_0_EN, + TAS2764_SDOUT_HIZ_9_FORCE_0_EN); + + if (ret < 0) + return ret; + } + if (tas2764->devid == DEVID_SN012776) { ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, TAS2764_PWR_CTRL_BOP_SRC, @@ -723,6 +741,11 @@ static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764) if (ret) tas2764->v_sense_slot = -1; + ret = fwnode_property_read_u32(dev->fwnode, "ti,sdout-force-zero-mask", + &tas2764->sdout_zero_mask); + if (ret) + tas2764->sdout_zero_mask = 0; + return 0; } diff --git a/sound/soc/codecs/tas2764.h b/sound/soc/codecs/tas2764.h index 20628e51bf94f0..10ef7d4a490e1d 100644 --- a/sound/soc/codecs/tas2764.h +++ b/sound/soc/codecs/tas2764.h @@ -117,4 +117,15 @@ #define TAS2764_BOP_CFG0 TAS2764_REG(0X0, 0x1d) +#define TAS2764_SDOUT_HIZ_1 TAS2764_REG(0x1, 0x3d) +#define TAS2764_SDOUT_HIZ_2 TAS2764_REG(0x1, 0x3e) +#define TAS2764_SDOUT_HIZ_3 TAS2764_REG(0x1, 0x3f) +#define TAS2764_SDOUT_HIZ_4 TAS2764_REG(0x1, 0x40) +#define TAS2764_SDOUT_HIZ_5 TAS2764_REG(0x1, 0x41) +#define TAS2764_SDOUT_HIZ_6 TAS2764_REG(0x1, 0x42) +#define TAS2764_SDOUT_HIZ_7 TAS2764_REG(0x1, 0x43) +#define TAS2764_SDOUT_HIZ_8 TAS2764_REG(0x1, 0x44) +#define TAS2764_SDOUT_HIZ_9 TAS2764_REG(0x1, 0x45) +#define TAS2764_SDOUT_HIZ_9_FORCE_0_EN BIT(7) + #endif /* __TAS2764__ */ From dd278a77b7a44884adfada3b6b311b2372463a4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 24 Jan 2023 15:24:28 +0100 Subject: [PATCH 0286/1020] WIP: ASoC: tas2764: Apply unknown Apple quirk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This maybe, possibly, fixes some fault states on the codec... Signed-off-by: Martin Povišer --- sound/soc/codecs/tas2764.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 15e48175c0694f..e8b6a81930962b 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -531,6 +531,36 @@ static uint8_t sn012776_bop_presets[] = { static const struct regmap_config tas2764_i2c_regmap; +static int tas2764_apply_unk_apple_quirk(struct snd_soc_component *component) +{ + int ret; + + ret = snd_soc_component_write(component, 0xfd0d, 0xd); + + if (ret < 0) + return ret; + + + ret = snd_soc_component_write(component, 0xfd6c, 0x2); + + if (ret < 0) + return ret; + + + ret = snd_soc_component_write(component, 0xfd6d, 0xf); + + if (ret < 0) + return ret; + + + ret = snd_soc_component_write(component, 0xfd0d, 0x0); + + if (ret < 0) + return ret; + + return 0; +} + static int tas2764_codec_probe(struct snd_soc_component *component) { struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); @@ -600,6 +630,11 @@ static int tas2764_codec_probe(struct snd_soc_component *component) } if (tas2764->devid == DEVID_SN012776) { + ret = tas2764_apply_unk_apple_quirk(component); + + if (ret < 0) + return ret; + ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, TAS2764_PWR_CTRL_BOP_SRC, TAS2764_PWR_CTRL_BOP_SRC); @@ -693,6 +728,9 @@ static bool tas2764_volatile_register(struct device *dev, unsigned int reg) case TAS2764_INT_LTCH0 ... TAS2764_INT_LTCH4: case TAS2764_INT_CLK_CFG: return true; + case TAS2764_REG(0xf0, 0x0) ... TAS2764_REG(0xff, 0x0): + /* TI's undocumented registers for the application of quirks */ + return true; default: return false; } From bf973d46a34875b1daa60aa9677a955269004bac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 24 Jan 2023 15:25:07 +0100 Subject: [PATCH 0287/1020] ASoC: tas2764: Raise regmap range maximum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/codecs/tas2764.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index e8b6a81930962b..b629ad9824cd68 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -713,7 +713,7 @@ static const struct reg_default tas2764_reg_defaults[] = { static const struct regmap_range_cfg tas2764_regmap_ranges[] = { { .range_min = 0, - .range_max = 1 * 128, + .range_max = 0xffff, .selector_reg = TAS2764_PAGE, .selector_mask = 0xff, .selector_shift = 0, @@ -745,7 +745,7 @@ static const struct regmap_config tas2764_i2c_regmap = { .cache_type = REGCACHE_RBTREE, .ranges = tas2764_regmap_ranges, .num_ranges = ARRAY_SIZE(tas2764_regmap_ranges), - .max_register = 1 * 128, + .max_register = 0xffff, }; static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764) From 63857d3a4787fae07ba0d3da07e412d013f1e3f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 25 Jan 2023 11:08:59 +0100 Subject: [PATCH 0288/1020] ASoC: tas2770: Export 'die_temp' to sysfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Export a file for the readout of die temperature measurements. Signed-off-by: Martin Povišer --- sound/soc/codecs/tas2770.c | 47 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index db6731e9cff2d9..c3733704b27ec5 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -493,6 +494,47 @@ static struct snd_soc_dai_driver tas2770_dai_driver[] = { }, }; +static int tas2770_read_die_temp(struct tas2770_priv *tas2770, int *result) +{ + int ret, reading; + + ret = snd_soc_component_read(tas2770->component, TAS2770_TEMP_MSB); + if (ret < 0) + return ret; + reading = ret << 4; + + ret = snd_soc_component_read(tas2770->component, TAS2770_TEMP_LSB); + if (ret < 0) + return ret; + reading |= ret >> 4; + + *result = reading - (93 * 16); + return 0; +} + +static ssize_t die_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tas2770_priv *tas2770 = i2c_get_clientdata(to_i2c_client(dev)); + int ret, temp; + + ret = tas2770_read_die_temp(tas2770, &temp); + + if (ret < 0) + return ret; + + return sysfs_emit(buf, "%d.%03d C\n", temp / 16, + (temp * 1000 / 16) % 1000); +} + +static DEVICE_ATTR_RO(die_temp); + +static struct attribute *tas2770_sysfs_attrs[] = { + &dev_attr_die_temp.attr, + NULL +}; +ATTRIBUTE_GROUPS(tas2770_sysfs); + static const struct regmap_config tas2770_i2c_regmap; static int tas2770_codec_probe(struct snd_soc_component *component) @@ -519,6 +561,11 @@ static int tas2770_codec_probe(struct snd_soc_component *component) return ret; } + ret = sysfs_create_groups(&component->dev->kobj, tas2770_sysfs_groups); + + if (ret < 0) + return ret; + return 0; } From 364c2e5b99a7748f09bdc3618e98b395f6dd200e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 25 Jan 2023 11:10:20 +0100 Subject: [PATCH 0289/1020] ASoC: tas2764: Export 'die_temp' to sysfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Export a file for the readout of die temperature measurements. Signed-off-by: Martin Povišer --- sound/soc/codecs/tas2764.c | 45 ++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/tas2764.h | 3 +++ 2 files changed, 48 insertions(+) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index b629ad9824cd68..2314d5ab99defe 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -561,6 +562,39 @@ static int tas2764_apply_unk_apple_quirk(struct snd_soc_component *component) return 0; } +static int tas2764_read_die_temp(struct tas2764_priv *tas2764, int *result) +{ + int ret; + + ret = snd_soc_component_read(tas2764->component, TAS2764_TEMP); + if (ret < 0) + return ret; + *result = ret - 93; + return 0; +} + +static ssize_t die_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tas2764_priv *tas2764 = i2c_get_clientdata(to_i2c_client(dev)); + int ret, temp; + + ret = tas2764_read_die_temp(tas2764, &temp); + + if (ret < 0) + return ret; + + return sysfs_emit(buf, "%d C\n", temp); +} + +static DEVICE_ATTR_RO(die_temp); + +static struct attribute *tas2764_sysfs_attrs[] = { + &dev_attr_die_temp.attr, + NULL +}; +ATTRIBUTE_GROUPS(tas2764_sysfs); + static int tas2764_codec_probe(struct snd_soc_component *component) { struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); @@ -651,9 +685,19 @@ static int tas2764_codec_probe(struct snd_soc_component *component) } } + ret = sysfs_create_groups(&component->dev->kobj, tas2764_sysfs_groups); + + if (ret < 0) + return ret; + return 0; } +static void tas2764_codec_remove(struct snd_soc_component *component) +{ + sysfs_remove_groups(&component->dev->kobj, tas2764_sysfs_groups); +} + static DECLARE_TLV_DB_SCALE(tas2764_digital_tlv, 1100, 50, 0); static DECLARE_TLV_DB_SCALE(tas2764_playback_volume, -10050, 50, 1); @@ -685,6 +729,7 @@ static const struct snd_kcontrol_new tas2764_snd_controls[] = { static const struct snd_soc_component_driver soc_component_driver_tas2764 = { .probe = tas2764_codec_probe, + .remove = tas2764_codec_remove, .suspend = tas2764_codec_suspend, .resume = tas2764_codec_resume, .controls = tas2764_snd_controls, diff --git a/sound/soc/codecs/tas2764.h b/sound/soc/codecs/tas2764.h index 10ef7d4a490e1d..dbe3f7fa901879 100644 --- a/sound/soc/codecs/tas2764.h +++ b/sound/soc/codecs/tas2764.h @@ -111,6 +111,9 @@ #define TAS2764_INT_LTCH3 TAS2764_REG(0x0, 0x50) #define TAS2764_INT_LTCH4 TAS2764_REG(0x0, 0x51) +/* Readout Registers */ +#define TAS2764_TEMP TAS2764_REG(0x0, 0x56) + /* Clock/IRQ Settings */ #define TAS2764_INT_CLK_CFG TAS2764_REG(0x0, 0x5c) #define TAS2764_INT_CLK_CFG_IRQZ_CLR BIT(2) From 0fee007b533180d35016330b277ce6fe58744121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 25 Jan 2023 11:14:05 +0100 Subject: [PATCH 0290/1020] ASoC: tas2764: Crop SDOUT zero-out mask based on BCLK ratio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/codecs/tas2764.c | 56 ++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 2314d5ab99defe..b6af29f477f6bf 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -349,6 +349,44 @@ static int tas2764_hw_params(struct snd_pcm_substream *substream, return tas2764_set_samplerate(tas2764, params_rate(params)); } +static int tas2764_write_sdout_zero_mask(struct tas2764_priv *tas2764, int bclk_ratio) +{ + struct snd_soc_component *component = tas2764->component; + int nsense_slots = bclk_ratio / 8; + u32 cropped_mask; + int i, ret; + + if (!tas2764->sdout_zero_mask) + return 0; + + cropped_mask = tas2764->sdout_zero_mask & GENMASK(nsense_slots - 1, 0); + + for (i = 0; i < 4; i++) { + ret = snd_soc_component_write(component, TAS2764_SDOUT_HIZ_1 + i, + (cropped_mask >> (i * 8)) & 0xff); + + if (ret < 0) + return ret; + } + + ret = snd_soc_component_update_bits(component, TAS2764_SDOUT_HIZ_9, + TAS2764_SDOUT_HIZ_9_FORCE_0_EN, + TAS2764_SDOUT_HIZ_9_FORCE_0_EN); + + if (ret < 0) + return ret; + + return 0; +} + +static int tas2764_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + + return tas2764_write_sdout_zero_mask(tas2764, ratio); +} + static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_component *component = dai->component; @@ -490,6 +528,7 @@ static int tas2764_set_ivsense_transmit(struct tas2764_priv *tas2764, int i_slot static const struct snd_soc_dai_ops tas2764_dai_ops = { .mute_stream = tas2764_mute, .hw_params = tas2764_hw_params, + .set_bclk_ratio = tas2764_set_bclk_ratio, .set_fmt = tas2764_set_fmt, .set_tdm_slot = tas2764_set_dai_tdm_slot, .no_capture_mute = 1, @@ -646,23 +685,6 @@ static int tas2764_codec_probe(struct snd_soc_component *component) return ret; } - if (tas2764->sdout_zero_mask) { - for (i = 0; i < 4; i++) { - ret = snd_soc_component_write(component, TAS2764_SDOUT_HIZ_1 + i, - (tas2764->sdout_zero_mask >> (i * 8)) & 0xff); - - if (ret < 0) - return ret; - } - - ret = snd_soc_component_update_bits(component, TAS2764_SDOUT_HIZ_9, - TAS2764_SDOUT_HIZ_9_FORCE_0_EN, - TAS2764_SDOUT_HIZ_9_FORCE_0_EN); - - if (ret < 0) - return ret; - } - if (tas2764->devid == DEVID_SN012776) { ret = tas2764_apply_unk_apple_quirk(component); From 5833d45dabc5141cb18e68b69b1533a1d20228eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 25 Jan 2023 13:41:42 +0100 Subject: [PATCH 0291/1020] ASoC: macaudio: Remove stale 'speaker_nchans' fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/apple/macaudio.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 93cf3b3a2e63c9..5be2f204d9f9e6 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -73,9 +73,6 @@ struct macaudio_snd_data { bool is_headphones; unsigned int tdm_mask; } *link_props; - - unsigned int speaker_nchans_array[2]; - struct snd_pcm_hw_constraint_list speaker_nchans_list; }; static bool please_blow_up_my_speakers; From 764317c56386d9fe0ae82cf8e48de677c5cba8fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 25 Jan 2023 16:16:13 +0100 Subject: [PATCH 0292/1020] ASoC: macaudio: Add 'Speakers Up Indicator' control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This control is there for userspace convenience, so that daemons watching I/V sense data know when to open the sense PCM. If they open the PCM without playback in progress, there will be no clocks on the bus and the sense capture PCM will be stuck. Signed-off-by: Martin Povišer --- sound/soc/apple/macaudio.c | 69 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 5be2f204d9f9e6..52f0fe9c271ffd 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -73,6 +73,9 @@ struct macaudio_snd_data { bool is_headphones; unsigned int tdm_mask; } *link_props; + + bool speakers_streaming; + struct snd_kcontrol *speakers_streaming_kctl; }; static bool please_blow_up_my_speakers; @@ -566,6 +569,36 @@ static void macaudio_dpcm_shutdown(struct snd_pcm_substream *substream) } } +static int macaudio_be_prepare(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]; + + if (props->is_speakers) { + ma->speakers_streaming = true; + snd_ctl_notify(ma->card.snd_card, SNDRV_CTL_EVENT_MASK_VALUE, + &ma->speakers_streaming_kctl->id); + } + + return 0; +} + +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]; + + if (props->is_speakers) { + ma->speakers_streaming = false; + snd_ctl_notify(ma->card.snd_card, SNDRV_CTL_EVENT_MASK_VALUE, + &ma->speakers_streaming_kctl->id); + } + + return 0; +} + static const struct snd_soc_ops macaudio_fe_ops = { .startup = macaudio_fe_startup, .shutdown = macaudio_dpcm_shutdown, @@ -573,6 +606,8 @@ static const struct snd_soc_ops macaudio_fe_ops = { }; static const struct snd_soc_ops macaudio_be_ops = { + .prepare = macaudio_be_prepare, + .hw_free = macaudio_be_hw_free, .shutdown = macaudio_dpcm_shutdown, .hw_params = macaudio_dpcm_hw_params, }; @@ -803,6 +838,8 @@ static int macaudio_late_probe(struct snd_soc_card *card) } } + ma->speakers_streaming_kctl = snd_soc_card_get_kcontrol(card, "Speakers Up Indicator"); + return 0; } @@ -976,10 +1013,42 @@ static const struct snd_soc_dapm_widget macaudio_snd_widgets[] = { 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_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + + 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->speakers_streaming; + + return 0; +} + 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 = "Speakers Up Indicator", + .info = macaudio_sss_info, .get = macaudio_sss_get, + }, }; static const struct snd_soc_dapm_route macaudio_dapm_routes[] = { From c0690e9a24b14406b47ef93f635e17a6178e3261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 26 Jan 2023 14:24:24 +0100 Subject: [PATCH 0293/1020] ASoC: tas2764: Add optional 'Apple quirks' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apple's SN012776 driver has some peculiar aspects to its behavior that are suspected to work around issues in the codec part. Add a module parameter for enabling individual quirks that should be imitated after the Apple driver. Signed-off-by: Martin Povišer --- sound/soc/codecs/tas2764-quirks.h | 185 ++++++++++++++++++++++++++++++ sound/soc/codecs/tas2764.c | 54 ++++----- 2 files changed, 213 insertions(+), 26 deletions(-) create mode 100644 sound/soc/codecs/tas2764-quirks.h diff --git a/sound/soc/codecs/tas2764-quirks.h b/sound/soc/codecs/tas2764-quirks.h new file mode 100644 index 00000000000000..7cb860e7e9c252 --- /dev/null +++ b/sound/soc/codecs/tas2764-quirks.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __TAS2764_QUIRKS__ +#define __TAS2764_QUIRKS__ + +#include + +#include "tas2764.h" + +/* + * Disable noise gate and flip down reserved bit in NS_CFG0 + */ +#define TAS2764_NOISE_GATE_DISABLE BIT(0) + +struct reg_sequence tas2764_noise_gate_dis_seq[] = { + REG_SEQ0(TAS2764_REG(0x0, 0x35), 0xb0) +}; + +/* + * CONV_VBAT_PVDD_MODE=1 + */ +#define TAS2764_CONV_VBAT_PVDD_MODE BIT(1) + +struct reg_sequence tas2764_conv_vbat_pvdd_mode_seq[] = { + REG_SEQ0(TAS2764_REG(0x0, 0x6b), 0x41) +}; + +/* + * Reset of DAC modulator when DSP is OFF + */ +#define TAS2764_DMOD_RST BIT(2) + +struct reg_sequence tas2764_dmod_rst_seq[] = { + REG_SEQ0(TAS2764_REG(0x0, 0x76), 0x0) +}; + +/* + * Unknown 0x133/0x137 writes (maybe TDM related) + */ +#define TAS2764_UNK_SEQ0 BIT(3) + +struct reg_sequence tas2764_unk_seq0[] = { + REG_SEQ0(TAS2764_REG(0x1, 0x33), 0x80), + REG_SEQ0(TAS2764_REG(0x1, 0x37), 0x3a), +}; + +/* + * Unknown 0x614 - 0x61f writes + */ +#define TAS2764_APPLE_UNK_SEQ1 BIT(4) + +struct reg_sequence tas2764_unk_seq1[] = { + REG_SEQ0(TAS2764_REG(0x6, 0x14), 0x0), + REG_SEQ0(TAS2764_REG(0x6, 0x15), 0x13), + REG_SEQ0(TAS2764_REG(0x6, 0x16), 0x52), + REG_SEQ0(TAS2764_REG(0x6, 0x17), 0x0), + REG_SEQ0(TAS2764_REG(0x6, 0x18), 0xe4), + REG_SEQ0(TAS2764_REG(0x6, 0x19), 0xc), + REG_SEQ0(TAS2764_REG(0x6, 0x16), 0xaa), + REG_SEQ0(TAS2764_REG(0x6, 0x1b), 0x0), + REG_SEQ0(TAS2764_REG(0x6, 0x1c), 0x12), + REG_SEQ0(TAS2764_REG(0x6, 0x1d), 0xa0), + REG_SEQ0(TAS2764_REG(0x6, 0x1e), 0xd8), + REG_SEQ0(TAS2764_REG(0x6, 0x1f), 0x0), +}; + +/* + * Unknown writes in the 0xfd page (with secondary paging inside) + */ +#define TAS2764_APPLE_UNK_SEQ2 BIT(5) + +struct reg_sequence tas2764_unk_seq2[] = { + REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0xd), + REG_SEQ0(TAS2764_REG(0xfd, 0x6c), 0x2), + REG_SEQ0(TAS2764_REG(0xfd, 0x6d), 0xf), + REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0x0), +}; + +/* + * Disable 'Thermal Threshold 1' + */ +#define TAS2764_THERMAL_TH1_DISABLE BIT(6) + +struct reg_sequence tas2764_thermal_th1_dis_seq[] = { + REG_SEQ0(TAS2764_REG(0x1, 0x47), 0x2), +}; + +/* + * Imitate Apple's shutdown dance + */ +#define TAS2764_SHUTDOWN_DANCE BIT(7) + +struct reg_sequence tas2764_shutdown_dance_init_seq[] = { + /* + * SDZ_MODE=01 (immediate) + * + * We want the shutdown to happen under the influence of + * the magic writes in the 0xfdXX region, so make sure + * the shutdown is immediate and there's no grace period + * followed by the codec part. + */ + REG_SEQ0(TAS2764_REG(0x0, 0x7), 0x60), +}; + +struct reg_sequence tas2764_pre_shutdown_seq[] = { + REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0xd), /* switch hidden page */ + REG_SEQ0(TAS2764_REG(0xfd, 0x64), 0x4), /* do write (unknown semantics) */ + REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0x0), /* switch hidden page back */ +}; + +struct reg_sequence tas2764_post_shutdown_seq[] = { + REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0xd), + REG_SEQ0(TAS2764_REG(0xfd, 0x64), 0x0), /* revert write from pre sequence */ + REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0x0), +}; + +static int tas2764_do_quirky_pwr_ctrl_change(struct tas2764_priv *tas2764, + unsigned int target) +{ + unsigned int curr; + int ret; + + curr = snd_soc_component_read_field(tas2764->component, + TAS2764_PWR_CTRL, + TAS2764_PWR_CTRL_MASK); + + if (target == curr) + return 0; + +#define TRANSITION(new, old) ((new) << 8 | (old)) + switch (TRANSITION(target, curr)) { + case TRANSITION(TAS2764_PWR_CTRL_SHUTDOWN, TAS2764_PWR_CTRL_MUTE): + case TRANSITION(TAS2764_PWR_CTRL_SHUTDOWN, TAS2764_PWR_CTRL_ACTIVE): + ret = regmap_multi_reg_write(tas2764->regmap, tas2764_pre_shutdown_seq, + ARRAY_SIZE(tas2764_pre_shutdown_seq)); + if (ret < 0) + break; + + ret = snd_soc_component_update_bits(tas2764->component, + TAS2764_PWR_CTRL, + TAS2764_PWR_CTRL_MASK, + TAS2764_PWR_CTRL_SHUTDOWN); + if (ret > 0) + break; + + ret = regmap_multi_reg_write(tas2764->regmap, tas2764_post_shutdown_seq, + ARRAY_SIZE(tas2764_post_shutdown_seq)); + + default: + ret = snd_soc_component_update_bits(tas2764->component, TAS2764_PWR_CTRL, + TAS2764_PWR_CTRL_MASK, target); + } +#undef TRANSITION + + if (ret < 0) + return ret; + return 0; +} + +/* + * Via devicetree (TODO): + * - switch from spread spectrum to class-D switching + * - disable edge control + * - set BOP settings (the BOP config bits *and* BOP_SRC) + */ + +/* + * Other setup TODOs: + * - DVC ramp rate + */ + +static struct tas2764_quirk_init_sequence { + struct reg_sequence *seq; + int len; +} tas2764_quirk_init_sequences[] = { + { tas2764_noise_gate_dis_seq, ARRAY_SIZE(tas2764_noise_gate_dis_seq) }, + { tas2764_dmod_rst_seq, ARRAY_SIZE(tas2764_dmod_rst_seq) }, + { tas2764_conv_vbat_pvdd_mode_seq, ARRAY_SIZE(tas2764_conv_vbat_pvdd_mode_seq) }, + { tas2764_unk_seq0, ARRAY_SIZE(tas2764_unk_seq0) }, + { tas2764_unk_seq1, ARRAY_SIZE(tas2764_unk_seq1) }, + { tas2764_unk_seq2, ARRAY_SIZE(tas2764_unk_seq2) }, + { tas2764_thermal_th1_dis_seq, ARRAY_SIZE(tas2764_thermal_th1_dis_seq) }, + { tas2764_shutdown_dance_init_seq, ARRAY_SIZE(tas2764_shutdown_dance_init_seq) }, +}; + +#endif /* __TAS2764_QUIRKS__ */ diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index b6af29f477f6bf..07301b389f6e29 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -49,6 +49,12 @@ struct tas2764_priv { bool unmuted; }; +static int apple_quirks; +module_param(apple_quirks, int, 0644); +MODULE_PARM_DESC(apple_quirks, "Mask of quirks to mimic after Apple's SN012776 driver"); + +#include "tas2764-quirks.h" + static const char *tas2764_int_ltch0_msgs[8] = { "fault: over temperature", /* INT_LTCH0 & BIT(0) */ "fault: over current", @@ -126,6 +132,9 @@ static int tas2764_update_pwr_ctrl(struct tas2764_priv *tas2764) else val = TAS2764_PWR_CTRL_SHUTDOWN; + if (apple_quirks & TAS2764_SHUTDOWN_DANCE) + return tas2764_do_quirky_pwr_ctrl_change(tas2764, val); + ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, TAS2764_PWR_CTRL_MASK, val); if (ret < 0) @@ -571,32 +580,25 @@ static uint8_t sn012776_bop_presets[] = { static const struct regmap_config tas2764_i2c_regmap; -static int tas2764_apply_unk_apple_quirk(struct snd_soc_component *component) +static int tas2764_apply_init_quirks(struct tas2764_priv * tas2764) { - int ret; - - ret = snd_soc_component_write(component, 0xfd0d, 0xd); - - if (ret < 0) - return ret; - - - ret = snd_soc_component_write(component, 0xfd6c, 0x2); - - if (ret < 0) - return ret; + int ret, i; + for (i = 0; i < ARRAY_SIZE(tas2764_quirk_init_sequences); i++) { + struct tas2764_quirk_init_sequence *init_seq = \ + &tas2764_quirk_init_sequences[i]; + if (!init_seq->seq) + continue; - ret = snd_soc_component_write(component, 0xfd6d, 0xf); + if (!(BIT(i) & apple_quirks)) + continue; - if (ret < 0) - return ret; + ret = regmap_multi_reg_write(tas2764->regmap, init_seq->seq, + init_seq->len); - - ret = snd_soc_component_write(component, 0xfd0d, 0x0); - - if (ret < 0) - return ret; + if (ret < 0) + return ret; + } return 0; } @@ -686,11 +688,6 @@ static int tas2764_codec_probe(struct snd_soc_component *component) } if (tas2764->devid == DEVID_SN012776) { - ret = tas2764_apply_unk_apple_quirk(component); - - if (ret < 0) - return ret; - ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, TAS2764_PWR_CTRL_BOP_SRC, TAS2764_PWR_CTRL_BOP_SRC); @@ -705,6 +702,11 @@ static int tas2764_codec_probe(struct snd_soc_component *component) if (ret < 0) return ret; } + + ret = tas2764_apply_init_quirks(tas2764); + + if (ret < 0) + return ret; } ret = sysfs_create_groups(&component->dev->kobj, tas2764_sysfs_groups); From 6dddda6cd8995f23a46446b3ea8f6aade53a1524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sun, 5 Feb 2023 22:53:20 +0100 Subject: [PATCH 0294/1020] ASoC: macaudio: Do not disable ISENSE/VSENSE switches on j314 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/soc/apple/macaudio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 52f0fe9c271ffd..4806cb665e78ea 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -924,8 +924,10 @@ static int macaudio_j314_fixup_controls(struct snd_soc_card *card) * samples from the codecs back to us, disable the * controls. */ +#if 0 CHECK(snd_soc_deactivate_kctl, "* VSENSE Switch", 0); CHECK(snd_soc_deactivate_kctl, "* ISENSE Switch", 0); +#endif } return 0; From 67b78c09a49d6b82bdcec8adc1428bb04f95256e Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 9 May 2023 19:04:18 +0900 Subject: [PATCH 0295/1020] ASoC: macaudio: Fix PD link double-frees? Signed-off-by: Hector Martin --- sound/soc/apple/mca.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index 0b1cea66e2abaf..50745d2d41bdcd 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -296,6 +296,7 @@ static int mca_fe_enable_clocks(struct mca_cluster *cl) * the power state driver would error out on seeing the device * as clock-gated. */ + WARN_ON(cl->pd_link); cl->pd_link = device_link_add(mca->dev, cl->pd_dev, DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE); @@ -319,7 +320,11 @@ static void mca_fe_disable_clocks(struct mca_cluster *cl) mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, 0); mca_modify(cl, REG_STATUS, STATUS_MCLK_EN, 0); - device_link_del(cl->pd_link); + if (cl->pd_link) { + device_link_del(cl->pd_link); + cl->pd_link = NULL; + } + clk_disable_unprepare(cl->clk_parent); } @@ -389,6 +394,7 @@ static int mca_fe_prepare(struct snd_pcm_substream *substream, if (!cl->syncgen_in_use) { int port = ffs(mca_fe_get_portmask(substream)) - 1; + WARN_ON(cl->pd_link); cl->pd_link = device_link_add(mca->dev, cl->pd_dev, DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE); @@ -421,8 +427,10 @@ static int mca_fe_hw_free(struct snd_pcm_substream *substream, return 0; mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, 0); - if (cl->pd_link) + if (cl->pd_link) { device_link_del(cl->pd_link); + cl->pd_link = NULL; + } return 0; } @@ -1108,8 +1116,10 @@ static void apple_mca_release(struct mca_data *mca) dev_pm_domain_detach(cl->pd_dev, true); } - if (mca->pd_link) + if (mca->pd_link) { device_link_del(mca->pd_link); + mca->pd_link = NULL; + } if (!IS_ERR_OR_NULL(mca->pd_dev)) dev_pm_domain_detach(mca->pd_dev, true); From 6796439fb0b7e123c4232a8218290f0f2b5d79f9 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 9 May 2023 19:05:29 +0900 Subject: [PATCH 0296/1020] ASoC: macaudio: Sense improvements - Export speakers sample rate via mixer control - Sense device open does not force the sample rate - No more timeouts on the sense device Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 83 +++++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 27 deletions(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 4806cb665e78ea..46476e234c299e 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -74,8 +74,8 @@ struct macaudio_snd_data { unsigned int tdm_mask; } *link_props; - bool speakers_streaming; - struct snd_kcontrol *speakers_streaming_kctl; + int speaker_sample_rate; + struct snd_kcontrol *speaker_sample_rate_kctl; }; static bool please_blow_up_my_speakers; @@ -483,10 +483,37 @@ 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) { + rate->min = rate->max = cpu_dai->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->rate; + + return 0; + } else { + /* + * 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; @@ -511,8 +538,14 @@ static int macaudio_fe_startup(struct snd_pcm_substream *substream) struct macaudio_link_props *props = &ma->link_props[rtd->dai_link->id]; int max_rate, ret; - if (props->is_sense) + 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, @@ -569,31 +602,28 @@ static void macaudio_dpcm_shutdown(struct snd_pcm_substream *substream) } } -static int macaudio_be_prepare(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]; - - if (props->is_speakers) { - ma->speakers_streaming = true; - snd_ctl_notify(ma->card.snd_card, SNDRV_CTL_EVENT_MASK_VALUE, - &ma->speakers_streaming_kctl->id); - } - - return 0; -} - 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) { - ma->speakers_streaming = false; + 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->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->speakers_streaming_kctl->id); + &ma->speaker_sample_rate_kctl->id); + } return 0; @@ -606,7 +636,6 @@ static const struct snd_soc_ops macaudio_fe_ops = { }; static const struct snd_soc_ops macaudio_be_ops = { - .prepare = macaudio_be_prepare, .hw_free = macaudio_be_hw_free, .shutdown = macaudio_dpcm_shutdown, .hw_params = macaudio_dpcm_hw_params, @@ -838,7 +867,7 @@ static int macaudio_late_probe(struct snd_soc_card *card) } } - ma->speakers_streaming_kctl = snd_soc_card_get_kcontrol(card, "Speakers Up Indicator"); + ma->speaker_sample_rate_kctl = snd_soc_card_get_kcontrol(card, "Speaker Sample Rate"); return 0; } @@ -1017,10 +1046,10 @@ static const struct snd_soc_dapm_widget macaudio_snd_widgets[] = { static int macaudio_sss_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; + uinfo->value.integer.max = 192000; return 0; } @@ -1035,7 +1064,7 @@ static int macaudio_sss_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v * 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->speakers_streaming; + uvalue->value.integer.value[0] = ma->speaker_sample_rate; return 0; } @@ -1048,7 +1077,7 @@ static const struct snd_kcontrol_new macaudio_controls[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, - .name = "Speakers Up Indicator", + .name = "Speaker Sample Rate", .info = macaudio_sss_info, .get = macaudio_sss_get, }, }; From f1a3927b7904c591d386ba7113a2b17b055f3483 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 9 Oct 2023 22:31:23 +0900 Subject: [PATCH 0297/1020] ASoC: ops: Export snd_soc_control_matches() This helper is useful for drivers that want to do their own control lookups and matching as part of more complex logic than the existing operations. Signed-off-by: Hector Martin --- include/sound/soc.h | 2 ++ sound/soc/soc-ops.c | 9 +++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index 536bccfccc91e6..3511f405039528 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -585,6 +585,8 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +bool snd_soc_control_matches(struct snd_kcontrol *kcontrol, + const char *pattern); 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, diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 15e88e0e074c94..50808c5745e9cf 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -631,7 +631,7 @@ int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range); -static bool soc_control_matches(struct snd_kcontrol *kctl, +bool snd_soc_control_matches(struct snd_kcontrol *kctl, const char *pattern) { const char *name = kctl->id.name; @@ -653,6 +653,7 @@ static bool soc_control_matches(struct snd_kcontrol *kctl, return !strcmp(name, pattern); } +EXPORT_SYMBOL_GPL(snd_soc_control_matches); static int soc_clip_to_platform_max(struct snd_kcontrol *kctl) { @@ -718,7 +719,7 @@ int snd_soc_limit_volume(struct snd_soc_card *card, return -EINVAL; list_for_each_entry(kctl, &card->snd_card->controls, list) { - if (!soc_control_matches(kctl, name)) + if (!snd_soc_control_matches(kctl, name)) continue; ret = soc_limit_volume(kctl, max); @@ -756,7 +757,7 @@ int snd_soc_deactivate_kctl(struct snd_soc_card *card, return -EINVAL; list_for_each_entry(kctl, &card->snd_card->controls, list) { - if (!soc_control_matches(kctl, name)) + if (!snd_soc_control_matches(kctl, name)) continue; ret = snd_ctl_activate_id(card->snd_card, &kctl->id, active); @@ -826,7 +827,7 @@ int snd_soc_set_enum_kctl(struct snd_soc_card *card, return -EINVAL; list_for_each_entry(kctl, &card->snd_card->controls, list) { - if (!soc_control_matches(kctl, name)) + if (!snd_soc_control_matches(kctl, name)) continue; ret = soc_set_enum_kctl(kctl, value); From 6c3a52dc116fcf82d701f33c50944f45af8dea5f Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 9 Oct 2023 23:36:27 +0900 Subject: [PATCH 0298/1020] macaudio: speaker volume safety interlocks Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 391 ++++++++++++++++++++++++++++++++++++- 1 file changed, 381 insertions(+), 10 deletions(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 46476e234c299e..5c9ad16ae8ba06 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -55,11 +55,29 @@ */ #define MACAUDIO_MAX_BCLK_FREQ 24576000 +#define SPEAKER_MAGIC_VALUE (s32)0xdec1be15 +/* milliseconds */ +#define SPEAKER_LOCK_TIMEOUT 250 + +#define MAX_LIMITS 6 + +struct macaudio_limit_cfg { + const char *match; + int max_limited; + int max_unlimited; +}; + +struct macaudio_platform_cfg { + struct macaudio_limit_cfg limits[MAX_LIMITS]; + int (*fixup)(struct snd_soc_card *card); +}; + 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; unsigned int max_channels; @@ -76,6 +94,18 @@ struct macaudio_snd_data { int speaker_sample_rate; struct snd_kcontrol *speaker_sample_rate_kctl; + + bool speaker_volume_unlocked; + bool speaker_volume_was_locked; + struct snd_kcontrol *speaker_lock_kctl; + struct snd_ctl_file *speaker_lock_owner; + 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; + }; static bool please_blow_up_my_speakers; @@ -165,6 +195,159 @@ static struct macaudio_link_props macaudio_fe_link_props[] = { } }; +static void macaudio_vlimit_unlock(struct macaudio_snd_data *ma, bool unlock) +{ + int i, ret, max; + + for (i = 0; i < ARRAY_SIZE(ma->cfg->limits); i++) { + const struct macaudio_limit_cfg *limit = &ma->cfg->limits[i]; + + if (!limit->match) + break; + + if (unlock) + max = limit->max_unlimited; + else + max = limit->max_limited; + + ret = snd_soc_limit_volume(&ma->card, limit->match, max); + if (ret < 0) + dev_err(ma->card.dev, "Failed to %slock volume %s: %d\n", + unlock ? "un" : "", limit->match, ret); + } +} + +static void macaudio_vlimit_update(struct macaudio_snd_data *ma) +{ + int i; + bool unlock = true; + struct snd_kcontrol *kctl; + const char *reason; + + /* Do nothing if there are no limits configured */ + if (!ma->cfg->limits[0].match) + return; + + /* Check that someone is holding the main lock */ + if (!ma->speaker_lock_owner) { + reason = "Main control not locked"; + unlock = false; + } + + /* Check that the control has been pinged within the timeout */ + if (ma->speaker_lock_remain <= 0) { + reason = "Lock timeout"; + unlock = false; + } + + /* Check that *every* limited control is locked by the same owner */ + list_for_each_entry(kctl, &ma->card.snd_card->controls, list) { + bool is_limit = false; + + for (i = 0; i < ARRAY_SIZE(ma->cfg->limits); i++) { + const struct macaudio_limit_cfg *limit = &ma->cfg->limits[i]; + if (!limit->match) + break; + + is_limit = snd_soc_control_matches(kctl, limit->match); + if (is_limit) + break; + } + + if (!is_limit) + continue; + + for (i = 0; i < kctl->count; i++) { + if (kctl->vd[i].owner != ma->speaker_lock_owner) { + reason = "Not all child controls locked by the same process"; + unlock = false; + } + } + } + + + 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; + } +} + +static void macaudio_vlimit_enable_timeout(struct macaudio_snd_data *ma) +{ + if (ma->speaker_lock_timeout_enabled) + return; + + down_write(&ma->card.snd_card->controls_rwsem); + + 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); + + up_write(&ma->card.snd_card->controls_rwsem); + ma->speaker_lock_timeout_enabled = true; +} + +static void macaudio_vlimit_disable_timeout(struct macaudio_snd_data *ma) +{ + ktime_t now = ktime_get(); + + if (!ma->speaker_lock_timeout_enabled) + return; + + down_write(&ma->card.snd_card->controls_rwsem); + + 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); + + up_write(&ma->card.snd_card->controls_rwsem); + 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); + + down_write(&ma->card.snd_card->controls_rwsem); + + ma->speaker_lock_remain = 0; + macaudio_vlimit_update(ma); + + up_write(&ma->card.snd_card->controls_rwsem); +} + +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) { @@ -623,7 +806,34 @@ static int macaudio_be_hw_free(struct snd_pcm_substream *substream) 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]; + + 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; @@ -639,6 +849,7 @@ 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) @@ -868,10 +1079,14 @@ static int macaudio_late_probe(struct snd_soc_card *card) } ma->speaker_sample_rate_kctl = snd_soc_card_get_kcontrol(card, "Speaker Sample Rate"); + ma->speaker_lock_kctl = snd_soc_card_get_kcontrol(card, "Speaker Volume Unlock"); return 0; } +#define TAS2764_0DB 201 +#define TAS2764_DB_REDUCTION(x) (TAS2764_0DB - 2 * (x)) + #define CHECK(call, pattern, value) \ { \ int ret = call(card, pattern, value); \ @@ -882,7 +1097,6 @@ static int macaudio_late_probe(struct snd_soc_card *card) dev_dbg(card->dev, "%s on '%s': %d hits\n", #call, pattern, ret); \ } - static int macaudio_j274_fixup_controls(struct snd_soc_card *card) { struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); @@ -894,6 +1108,10 @@ static int macaudio_j274_fixup_controls(struct snd_soc_card *card) return 0; } +struct macaudio_platform_cfg macaudio_j274_cfg = { + .fixup = macaudio_j274_fixup_controls, +}; + static int macaudio_j313_fixup_controls(struct snd_soc_card *card) { struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); @@ -919,11 +1137,17 @@ static int macaudio_j313_fixup_controls(struct snd_soc_card *card) { */ CHECK(snd_soc_deactivate_kctl, "* VSENSE Switch", 0); CHECK(snd_soc_deactivate_kctl, "* ISENSE Switch", 0); + + macaudio_vlimit_update(ma); } return 0; } +struct macaudio_platform_cfg macaudio_j313_cfg = { + .fixup = macaudio_j313_fixup_controls, +}; + static int macaudio_j314_fixup_controls(struct snd_soc_card *card) { struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); @@ -957,11 +1181,41 @@ static int macaudio_j314_fixup_controls(struct snd_soc_card *card) CHECK(snd_soc_deactivate_kctl, "* VSENSE Switch", 0); CHECK(snd_soc_deactivate_kctl, "* ISENSE Switch", 0); #endif + + macaudio_vlimit_update(ma); } return 0; } + +struct macaudio_platform_cfg macaudio_j314_cfg = { + .fixup = macaudio_j314_fixup_controls, + .limits = { + {.match = "* Tweeter Speaker Volume", TAS2764_DB_REDUCTION(20), TAS2764_0DB}, + {.match = "* Woofer Speaker Volume", TAS2764_DB_REDUCTION(20), TAS2764_0DB}, + } +}; + +struct macaudio_platform_cfg macaudio_j413_cfg = { + .fixup = macaudio_j314_fixup_controls, + .limits = { + /* Min gain: -17.47 dB */ + {.match = "* Tweeter Speaker Volume", TAS2764_DB_REDUCTION(20), TAS2764_0DB}, + /* Min gain: -10.63 dB */ + {.match = "* Woofer Speaker Volume", TAS2764_DB_REDUCTION(14), TAS2764_0DB}, + } +}; + +struct macaudio_platform_cfg macaudio_j415_cfg = { + .fixup = macaudio_j314_fixup_controls, + .limits = { + {.match = "* Tweeter Speaker Volume", TAS2764_DB_REDUCTION(20), TAS2764_0DB}, + {.match = "* Woofer 1 Speaker Volume", TAS2764_DB_REDUCTION(20), TAS2764_0DB}, + {.match = "* Woofer 2 Speaker Volume", TAS2764_DB_REDUCTION(20), TAS2764_0DB}, + } +}; + static int macaudio_j375_fixup_controls(struct snd_soc_card *card) { struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); @@ -973,11 +1227,17 @@ static int macaudio_j375_fixup_controls(struct snd_soc_card *card) } CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 14); // 20 set by macOS, this is 3 dB below + + macaudio_vlimit_update(ma); } return 0; } +struct macaudio_platform_cfg macaudio_j375_cfg = { + .fixup = macaudio_j375_fixup_controls, +}; + static int macaudio_j493_fixup_controls(struct snd_soc_card *card) { struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); @@ -989,11 +1249,17 @@ static int macaudio_j493_fixup_controls(struct snd_soc_card *card) } CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 9); // 15 set by macOS, this is 3 dB below + + macaudio_vlimit_update(ma); } return 0; } +struct macaudio_platform_cfg macaudio_j493_cfg = { + .fixup = macaudio_j493_fixup_controls +}; + static int macaudio_fallback_fixup_controls(struct snd_soc_card *card) { struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); @@ -1006,6 +1272,10 @@ static int macaudio_fallback_fixup_controls(struct snd_soc_card *card) return 0; } +struct macaudio_platform_cfg macaudio_fallback_cfg = { + .fixup = macaudio_fallback_fixup_controls +}; + #undef CHECK static const char * const macaudio_spk_mux_texts[] = { @@ -1069,10 +1339,91 @@ static int macaudio_sss_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v 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); + + if (!ma->speaker_lock_owner) + return -EPERM; + + 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); + + 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; +} + +int macaudio_slk_lock(struct snd_kcontrol *kcontrol, struct snd_ctl_file *owner) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + + ma->speaker_lock_owner = owner; + macaudio_vlimit_update(ma); + + /* + * Reset the unintended lock flag when the control is first locked. + * At this point the state is locked and cannot be unlocked until + * userspace writes to this control, so this cannot spuriously become + * true again until that point. + */ + ma->speaker_volume_was_locked = false; + + return 0; +} + +static void macaudio_slk_unlock(struct snd_kcontrol *kcontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + + ma->speaker_lock_owner = NULL; + ma->speaker_lock_timeout = 0; + macaudio_vlimit_update(ma); +} + +/* Speaker limit controls go last */ +#define MACAUDIO_NUM_SPEAKER_LIMIT_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_WRITE, + .name = "Speaker Volume Unlock", + .info = macaudio_slk_info, .put = macaudio_slk_put, + .lock = macaudio_slk_lock, .unlock = macaudio_slk_unlock, + }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READ | @@ -1103,13 +1454,13 @@ static const struct snd_soc_dapm_route macaudio_dapm_routes[] = { }; static const struct of_device_id macaudio_snd_device_id[] = { - { .compatible = "apple,j274-macaudio", .data = macaudio_j274_fixup_controls }, - { .compatible = "apple,j313-macaudio", .data = macaudio_j313_fixup_controls }, - { .compatible = "apple,j314-macaudio", .data = macaudio_j314_fixup_controls }, - { .compatible = "apple,j375-macaudio", .data = macaudio_j375_fixup_controls }, - { .compatible = "apple,j413-macaudio", .data = macaudio_j314_fixup_controls }, - { .compatible = "apple,j415-macaudio", .data = macaudio_j314_fixup_controls }, - { .compatible = "apple,j493-macaudio", .data = macaudio_j493_fixup_controls }, + { .compatible = "apple,j274-macaudio", .data = &macaudio_j274_cfg }, + { .compatible = "apple,j313-macaudio", .data = &macaudio_j313_cfg }, + { .compatible = "apple,j314-macaudio", .data = &macaudio_j314_cfg }, + { .compatible = "apple,j375-macaudio", .data = &macaudio_j375_cfg }, + { .compatible = "apple,j413-macaudio", .data = &macaudio_j413_cfg }, + { .compatible = "apple,j415-macaudio", .data = &macaudio_j415_cfg }, + { .compatible = "apple,j493-macaudio", .data = &macaudio_j493_cfg }, { .compatible = "apple,macaudio"}, { } }; @@ -1134,6 +1485,7 @@ static int macaudio_snd_platform_probe(struct platform_device *pdev) return -ENOMEM; card = &data->card; snd_soc_card_set_drvdata(card, data); + dev_set_drvdata(&pdev->dev, data); card->owner = THIS_MODULE; card->driver_name = "macaudio"; @@ -1150,9 +1502,15 @@ static int macaudio_snd_platform_probe(struct platform_device *pdev) card->fully_routed = true; if (of_id->data) - card->fixup_controls = of_id->data; + data->cfg = of_id->data; else - card->fixup_controls = macaudio_fallback_fixup_controls; + data->cfg = &macaudio_fallback_cfg; + + /* Remove speaker safety controls if we have no declared limits */ + if (!data->cfg->limits[0].match) + card->num_controls -= MACAUDIO_NUM_SPEAKER_LIMIT_CONTROLS; + + card->fixup_controls = data->cfg->fixup; ret = macaudio_parse_of(data); if (ret) @@ -1169,11 +1527,24 @@ static int macaudio_snd_platform_probe(struct platform_device *pdev) } } + 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 int macaudio_snd_platform_remove(struct platform_device *pdev) +{ + struct macaudio_snd_data *ma = dev_get_drvdata(&pdev->dev); + + cancel_delayed_work_sync(&ma->lock_timeout_work); + + return 0; +} + 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, From e28b2666d23df39eaa302dcaa931491aea5e2e0d Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 12 Oct 2023 22:53:57 +0900 Subject: [PATCH 0299/1020] alsa: pcm: Remove the qos request only if active Fixes warning: [ 8.502802] ------------[ cut here ]------------ [ 8.503445] cpu_latency_qos_remove_request called for unknown object [ 8.504269] WARNING: CPU: 5 PID: 2790 at kernel/power/qos.c:322 cpu_latency_qos_remove_request+0x48/0x98 [ 8.505499] CPU: 5 PID: 2790 Comm: wireplumber Tainted: G W 6.5.0-asahi-00708-gb9b88240f7ae #2291 [ 8.506777] Hardware name: Apple MacBook Air (13-inch, M2, 2022) (DT) [ 8.519099] Call trace: [ 8.519402] cpu_latency_qos_remove_request+0x48/0x98 [ 8.520027] snd_pcm_ioctl+0x86c/0x182c [ 8.520519] __arm64_sys_ioctl+0xf8/0xbd0 [ 8.521020] invoke_syscall.constprop.0+0x78/0xc8 [ 8.521604] do_el0_svc+0x58/0x154 [ 8.522026] el0_svc+0x34/0xe4 [ 8.522409] el0t_64_sync_handler+0x120/0x12c [ 8.522951] el0t_64_sync+0x190/0x194 [ 8.523408] ---[ end trace 0000000000000000 ]--- Signed-off-by: Hector Martin --- sound/core/pcm_native.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 0b76e76823d288..67228b64f90270 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -916,8 +916,9 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream) goto unlock; result = do_hw_free(substream); snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN); - cpu_latency_qos_remove_request(&substream->latency_pm_qos_req); - unlock: + if (cpu_latency_qos_request_active(&substream->latency_pm_qos_req)) + cpu_latency_qos_remove_request(&substream->latency_pm_qos_req); +unlock: snd_pcm_buffer_access_unlock(runtime); return result; } From cc9bb9f1bd557bba80cd9dc7a69b3276059f78b1 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 12 Oct 2023 23:01:12 +0900 Subject: [PATCH 0300/1020] macaudio: Add a getter for the interlock alsamixer/etc really don't like write-only controls... Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 5c9ad16ae8ba06..466cfb47342813 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -276,6 +276,8 @@ static void macaudio_vlimit_update(struct macaudio_snd_data *ma) 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); } } @@ -1381,7 +1383,17 @@ static int macaudio_slk_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v return 0; } -int macaudio_slk_lock(struct snd_kcontrol *kcontrol, struct snd_ctl_file *owner) +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; +} + +static int macaudio_slk_lock(struct snd_kcontrol *kcontrol, struct snd_ctl_file *owner) { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); @@ -1419,9 +1431,12 @@ static const struct snd_kcontrol_new macaudio_controls[] = { SOC_DAPM_PIN_SWITCH("Headset Mic"), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .access = SNDRV_CTL_ELEM_ACCESS_WRITE, + .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, + .info = macaudio_slk_info, + .put = macaudio_slk_put, .get = macaudio_slk_get, .lock = macaudio_slk_lock, .unlock = macaudio_slk_unlock, }, { From 7d007520b3e8a0bd4a658e46939373c9e8125dbc Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 13 Oct 2023 01:12:55 +0900 Subject: [PATCH 0301/1020] ASoC: apple: mca: Do not mark clocks in use for non-providers On the speakers PCM, this sequence: 1. Open playback 2. Open sense 3. Close playback 4. Close sense would result in the sense FE being marked as clocks in use at (2), since there is a clock provider (playback FE). Then at (4) this would WARN since there is no driver any more when closing the in use clocks. If (1) and (2) are reversed this does not happen, since the sense PCM is not marked as using the clocks when there is no provider yet. So, check explicitly whether the substream FE is a clock provider in be_prepare, and skip everything if it isn't. Signed-off-by: Hector Martin --- sound/soc/apple/mca.c | 67 ++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index 50745d2d41bdcd..c6b5ecc367c860 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -352,36 +352,6 @@ static bool mca_fe_clocks_in_use(struct mca_cluster *cl) return false; } -static int mca_be_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct mca_cluster *cl = mca_dai_to_cluster(dai); - struct mca_data *mca = cl->host; - struct mca_cluster *fe_cl; - int ret; - - if (cl->port_clk_driver < 0) - return 0; - - fe_cl = &mca->clusters[cl->port_clk_driver]; - - /* - * Typically the CODECs we are paired with will require clocks - * to be present at time of unmute with the 'mute_stream' op - * or at time of DAPM widget power-up. We need to enable clocks - * here at the latest (frontend prepare would be too late). - */ - if (!mca_fe_clocks_in_use(fe_cl)) { - ret = mca_fe_enable_clocks(fe_cl); - if (ret < 0) - return ret; - } - - cl->clocks_in_use[substream->stream] = true; - - return 0; -} - static int mca_fe_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -787,6 +757,43 @@ static struct snd_soc_pcm_runtime *mca_be_get_fe(struct snd_soc_pcm_runtime *be, return fe; } +static int mca_be_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); + struct mca_cluster *cl = mca_dai_to_cluster(dai); + struct mca_data *mca = cl->host; + struct mca_cluster *fe_cl, *fe_clk_cl; + int ret; + + fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); + + if (!fe_cl->clk_provider) + return 0; + + if (cl->port_clk_driver < 0) + return 0; + + fe_clk_cl = &mca->clusters[cl->port_clk_driver]; + + /* + * Typically the CODECs we are paired with will require clocks + * to be present at time of unmute with the 'mute_stream' op + * or at time of DAPM widget power-up. We need to enable clocks + * here at the latest (frontend prepare would be too late). + */ + if (!mca_fe_clocks_in_use(fe_clk_cl)) { + ret = mca_fe_enable_clocks(fe_clk_cl); + if (ret < 0) + return ret; + } + + cl->clocks_in_use[substream->stream] = true; + + return 0; +} + static int mca_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { From ac15445ea9ae25f6f97a7a78159f6abffd6257ba Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 13 Oct 2023 02:31:55 +0900 Subject: [PATCH 0302/1020] macaudio: Allow DT enabled speakers and gate them off in the driver For machines where we do not consider things safe yet, require the commandline argument. Without it, speakers are simply disabled, we don't refuse probe entirely. Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 466cfb47342813..95ccdcb742765f 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -70,6 +70,7 @@ struct macaudio_limit_cfg { struct macaudio_platform_cfg { struct macaudio_limit_cfg limits[MAX_LIMITS]; int (*fixup)(struct snd_soc_card *card); + bool enable_speakers; }; struct macaudio_snd_data { @@ -487,7 +488,6 @@ static int macaudio_parse_of(struct macaudio_snd_data *ma) if (!card->dai_link || !ma->link_props) return -ENOMEM; - card->num_links = num_links; link = card->dai_link; link_props = ma->link_props; @@ -503,6 +503,9 @@ static int macaudio_parse_of(struct macaudio_snd_data *ma) 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; @@ -520,8 +523,13 @@ static int macaudio_parse_of(struct macaudio_snd_data *ma) speakers = !strcmp(link_name, "Speaker") || !strcmp(link_name, "Speakers"); - if (speakers) + if (speakers) { + if (!ma->cfg->enable_speakers && !please_blow_up_my_speakers) { + dev_err(card->dev, "driver can't assure safety on this model, disabling speakers\n"); + continue; + } ma->has_speakers = 1; + } cpu = of_get_child_by_name(np, "cpu"); codec = of_get_child_by_name(np, "codec"); @@ -615,11 +623,15 @@ static int macaudio_parse_of(struct macaudio_snd_data *ma) 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; + card->num_links = num_links; + return 0; err_free: @@ -1112,17 +1124,13 @@ static int macaudio_j274_fixup_controls(struct snd_soc_card *card) struct macaudio_platform_cfg macaudio_j274_cfg = { .fixup = macaudio_j274_fixup_controls, + .enable_speakers = true, }; static int macaudio_j313_fixup_controls(struct snd_soc_card *card) { struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); if (ma->has_speakers) { - if (!please_blow_up_my_speakers) { - dev_err(card->dev, "driver can't assure safety on this model, refusing probe\n"); - return -EINVAL; - } - CHECK(snd_soc_set_enum_kctl, "* ASI1 Sel", "Left"); CHECK(snd_soc_deactivate_kctl, "* ASI1 Sel", 0); @@ -1155,11 +1163,6 @@ static int macaudio_j314_fixup_controls(struct snd_soc_card *card) struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); if (ma->has_speakers) { - if (!please_blow_up_my_speakers) { - dev_err(card->dev, "driver can't assure safety on this model, refusing probe\n"); - return -EINVAL; - } - CHECK(snd_soc_set_enum_kctl, "* ASI1 Sel", "Left"); CHECK(snd_soc_deactivate_kctl, "* ASI1 Sel", 0); CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 9); // 15 set by macOS, this is 3 dB below @@ -1223,11 +1226,6 @@ static int macaudio_j375_fixup_controls(struct snd_soc_card *card) struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); if (ma->has_speakers) { - if (!please_blow_up_my_speakers) { - dev_err(card->dev, "driver can't assure safety on this model, refusing probe\n"); - return -EINVAL; - } - CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 14); // 20 set by macOS, this is 3 dB below macaudio_vlimit_update(ma); @@ -1245,11 +1243,6 @@ static int macaudio_j493_fixup_controls(struct snd_soc_card *card) struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); if (ma->has_speakers) { - if (!please_blow_up_my_speakers) { - dev_err(card->dev, "driver can't assure safety on this model, refusing probe\n"); - return -EINVAL; - } - CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 9); // 15 set by macOS, this is 3 dB below macaudio_vlimit_update(ma); From 08492eac88f1baf1b3af49fa00d4bb2781e7d8a0 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 13 Oct 2023 02:43:12 +0900 Subject: [PATCH 0303/1020] macaudio: Enable VSENSE switches Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 95ccdcb742765f..1c6ab00c4febb7 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -1139,15 +1139,6 @@ static int macaudio_j313_fixup_controls(struct snd_soc_card *card) { */ CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 14); - /* - * Since we don't set the right slots yet to avoid - * driver conflict on the I2S bus sending ISENSE/VSENSE - * samples from the codecs back to us, disable the - * controls. - */ - CHECK(snd_soc_deactivate_kctl, "* VSENSE Switch", 0); - CHECK(snd_soc_deactivate_kctl, "* ISENSE Switch", 0); - macaudio_vlimit_update(ma); } @@ -1176,17 +1167,6 @@ static int macaudio_j314_fixup_controls(struct snd_soc_card *card) CHECK(snd_soc_set_enum_kctl, "* OCE Handling", "Retry"); CHECK(snd_soc_deactivate_kctl, "* OCE Handling", 0); - /* - * Since we don't set the right slots yet to avoid - * driver conflict on the I2S bus sending ISENSE/VSENSE - * samples from the codecs back to us, disable the - * controls. - */ -#if 0 - CHECK(snd_soc_deactivate_kctl, "* VSENSE Switch", 0); - CHECK(snd_soc_deactivate_kctl, "* ISENSE Switch", 0); -#endif - macaudio_vlimit_update(ma); } From 7fc369a9d174892feaa8f911c9afbc215fbf0274 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 13 Oct 2023 06:22:24 +0900 Subject: [PATCH 0304/1020] macaudio: Initialize speaker lock properly Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 1c6ab00c4febb7..8fada54279e17d 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -1095,6 +1095,8 @@ static int macaudio_late_probe(struct snd_soc_card *card) ma->speaker_sample_rate_kctl = snd_soc_card_get_kcontrol(card, "Speaker Sample Rate"); ma->speaker_lock_kctl = snd_soc_card_get_kcontrol(card, "Speaker Volume Unlock"); + macaudio_vlimit_unlock(ma, false); + return 0; } @@ -1138,8 +1140,6 @@ static int macaudio_j313_fixup_controls(struct snd_soc_card *card) { * what macOS sets. */ CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 14); - - macaudio_vlimit_update(ma); } return 0; @@ -1166,8 +1166,6 @@ static int macaudio_j314_fixup_controls(struct snd_soc_card *card) */ CHECK(snd_soc_set_enum_kctl, "* OCE Handling", "Retry"); CHECK(snd_soc_deactivate_kctl, "* OCE Handling", 0); - - macaudio_vlimit_update(ma); } return 0; @@ -1207,8 +1205,6 @@ static int macaudio_j375_fixup_controls(struct snd_soc_card *card) if (ma->has_speakers) { CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 14); // 20 set by macOS, this is 3 dB below - - macaudio_vlimit_update(ma); } return 0; @@ -1224,8 +1220,6 @@ static int macaudio_j493_fixup_controls(struct snd_soc_card *card) if (ma->has_speakers) { CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 9); // 15 set by macOS, this is 3 dB below - - macaudio_vlimit_update(ma); } return 0; From d4fcd2b48035fc72369c3cdc80fb16ceaeac96bb Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 13 Oct 2023 16:57:46 +0900 Subject: [PATCH 0305/1020] macaudio: Use the same volume limit for all amps These are unintentionally aliased. Pending a solution for this, let's just use the same limit for now. Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 8fada54279e17d..89b8a22839de59 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -1186,7 +1186,8 @@ struct macaudio_platform_cfg macaudio_j413_cfg = { /* Min gain: -17.47 dB */ {.match = "* Tweeter Speaker Volume", TAS2764_DB_REDUCTION(20), TAS2764_0DB}, /* Min gain: -10.63 dB */ - {.match = "* Woofer Speaker Volume", TAS2764_DB_REDUCTION(14), TAS2764_0DB}, + /* FIXME: These structures are aliased so we can't set different max volumes */ + {.match = "* Woofer Speaker Volume", TAS2764_DB_REDUCTION(20), TAS2764_0DB}, } }; From bbe50356ca761e004e8d649029f58780eaa61c62 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 13 Oct 2023 23:39:15 +0900 Subject: [PATCH 0306/1020] macaudio: Disable debug Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 89b8a22839de59..dacd68371026a1 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -20,7 +20,7 @@ * reparenting of live BEs.) */ -#define DEBUG +/* #define DEBUG */ #include #include From 709ee4966c0c7553918a7785a7f1eb7eed5a2d5e Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 13 Oct 2023 23:39:41 +0900 Subject: [PATCH 0307/1020] ASoC: tas2764: Enable main IRQs Signed-off-by: Hector Martin --- sound/soc/codecs/tas2764.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 07301b389f6e29..eaaa19ab1b618e 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -652,7 +652,7 @@ static int tas2764_codec_probe(struct snd_soc_component *component) regmap_reinit_cache(tas2764->regmap, &tas2764_i2c_regmap); if (tas2764->irq) { - ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK0, 0xff); + ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK0, 0x00); if (ret < 0) return ret; From 25977b9a4f946f574c0faac65ec9bb36dceb5759 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 21 Oct 2023 01:03:07 +0900 Subject: [PATCH 0308/1020] ASoC: tas2764: Power up/down amp on mute ops The ASoC convention is that clocks are removed after codec mute, and power up/down is more about top level power management. For these chips, the "mute" state still expects a TDM clock, and yanking the clock in this state will trigger clock errors. So, do the full shutdown<->mute<->active transition on the mute operation, so the amp is in software shutdown by the time the clocks are removed. This fixes TDM clock errors when streams are stopped. Signed-off-by: Hector Martin --- sound/soc/codecs/tas2764.c | 51 ++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index eaaa19ab1b618e..63ce2b8f9f565d 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -200,33 +200,6 @@ static SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new tas2764_asi1_mux = SOC_DAPM_ENUM("ASI1 Source", tas2764_ASI1_src_enum); -static int tas2764_dac_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); - int ret; - - switch (event) { - case SND_SOC_DAPM_POST_PMU: - tas2764->dac_powered = true; - ret = tas2764_update_pwr_ctrl(tas2764); - break; - case SND_SOC_DAPM_PRE_PMD: - tas2764->dac_powered = false; - ret = tas2764_update_pwr_ctrl(tas2764); - break; - default: - dev_err(tas2764->dev, "Unsupported event\n"); - return -EINVAL; - } - - if (ret < 0) - return ret; - - return 0; -} - static const struct snd_kcontrol_new isense_switch = SOC_DAPM_SINGLE("Switch", TAS2764_PWR_CTRL, TAS2764_ISENSE_POWER_EN, 1, 1); static const struct snd_kcontrol_new vsense_switch = @@ -239,8 +212,7 @@ static const struct snd_soc_dapm_widget tas2764_dapm_widgets[] = { 1, &isense_switch), SND_SOC_DAPM_SWITCH("VSENSE", TAS2764_PWR_CTRL, TAS2764_VSENSE_POWER_EN, 1, &vsense_switch), - SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2764_dac_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_OUTPUT("OUT"), SND_SOC_DAPM_SIGGEN("VMON"), SND_SOC_DAPM_SIGGEN("IMON") @@ -261,9 +233,28 @@ static int tas2764_mute(struct snd_soc_dai *dai, int mute, int direction) { struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(dai->component); + int ret; + + if (!mute) { + tas2764->dac_powered = true; + ret = tas2764_update_pwr_ctrl(tas2764); + if (ret) + return ret; + } tas2764->unmuted = !mute; - return tas2764_update_pwr_ctrl(tas2764); + ret = tas2764_update_pwr_ctrl(tas2764); + if (ret) + return ret; + + if (mute) { + tas2764->dac_powered = false; + ret = tas2764_update_pwr_ctrl(tas2764); + if (ret) + return ret; + } + + return 0; } static int tas2764_set_bitwidth(struct tas2764_priv *tas2764, int bitwidth) From 36bc1dd9659b08fb459f8b817cfae999b5aaa67c Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 21 Oct 2023 22:16:32 +0900 Subject: [PATCH 0309/1020] ASoC: tas2764: Add SDZ regulator Multiple amps can be connected to the same SDZ GPIO. Using raw GPIOs for this breaks, as there is no concept of refcounting/sharing. In order to model these platforms, introduce support for an SDZ "regulator". This allows us to represent the SDZ GPIO as a simple regulator-fixed, and then the regulator core takes care of refcounting so that all codecs are only powered down once all the driver instances are in the suspend state. Signed-off-by: Hector Martin --- sound/soc/codecs/tas2764.c | 39 +++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 63ce2b8f9f565d..cadcfe526dad1c 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -36,6 +36,7 @@ struct tas2764_priv { struct snd_soc_component *component; struct gpio_desc *reset_gpio; struct gpio_desc *sdz_gpio; + struct regulator *sdz_reg; struct regmap *regmap; struct device *dev; int irq; @@ -159,6 +160,8 @@ static int tas2764_codec_suspend(struct snd_soc_component *component) if (tas2764->sdz_gpio) gpiod_set_value_cansleep(tas2764->sdz_gpio, 0); + regulator_disable(tas2764->sdz_reg); + regcache_cache_only(tas2764->regmap, true); regcache_mark_dirty(tas2764->regmap); @@ -170,19 +173,26 @@ static int tas2764_codec_resume(struct snd_soc_component *component) struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); int ret; + ret = regulator_enable(tas2764->sdz_reg); + + if (ret) { + dev_err(tas2764->dev, "Failed to enable regulator\n"); + return ret; + } + if (tas2764->sdz_gpio) { gpiod_set_value_cansleep(tas2764->sdz_gpio, 1); - usleep_range(1000, 2000); } - ret = tas2764_update_pwr_ctrl(tas2764); + usleep_range(1000, 2000); + regcache_cache_only(tas2764->regmap, false); + + ret = regcache_sync(tas2764->regmap); if (ret < 0) return ret; - regcache_cache_only(tas2764->regmap, false); - - return regcache_sync(tas2764->regmap); + return tas2764_update_pwr_ctrl(tas2764); } #else #define tas2764_codec_suspend NULL @@ -215,7 +225,7 @@ static const struct snd_soc_dapm_widget tas2764_dapm_widgets[] = { SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_OUTPUT("OUT"), SND_SOC_DAPM_SIGGEN("VMON"), - SND_SOC_DAPM_SIGGEN("IMON") + SND_SOC_DAPM_SIGGEN("IMON"), }; static const struct snd_soc_dapm_route tas2764_audio_map[] = { @@ -634,11 +644,18 @@ static int tas2764_codec_probe(struct snd_soc_component *component) tas2764->component = component; + ret = regulator_enable(tas2764->sdz_reg); + if (ret != 0) { + dev_err(tas2764->dev, "Failed to enable regulator: %d\n", ret); + return ret; + } + if (tas2764->sdz_gpio) { gpiod_set_value_cansleep(tas2764->sdz_gpio, 1); - usleep_range(1000, 2000); } + usleep_range(1000, 2000); + tas2764_reset(tas2764); regmap_reinit_cache(tas2764->regmap, &tas2764_i2c_regmap); @@ -710,6 +727,9 @@ static int tas2764_codec_probe(struct snd_soc_component *component) static void tas2764_codec_remove(struct snd_soc_component *component) { + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + + regulator_disable(tas2764->sdz_reg); sysfs_remove_groups(&component->dev->kobj, tas2764_sysfs_groups); } @@ -812,6 +832,11 @@ static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764) { int ret = 0; + tas2764->sdz_reg = devm_regulator_get(dev, "SDZ"); + if (IS_ERR(tas2764->sdz_reg)) + return dev_err_probe(dev, PTR_ERR(tas2764->sdz_reg), + "Failed to get SDZ supply\n"); + tas2764->reset_gpio = devm_gpiod_get_optional(tas2764->dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(tas2764->reset_gpio)) { From 88da943542387a34dea08bb247374113480c67c7 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 21 Oct 2023 22:38:36 +0900 Subject: [PATCH 0310/1020] macaudio: Use an explicit mutex for the speaker volume lock Otherwise we can end up recursively locking the controls lock in the start/stop path, since it can be called from a control change. Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index dacd68371026a1..679e9b4a521e83 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -96,6 +96,7 @@ struct macaudio_snd_data { int speaker_sample_rate; struct snd_kcontrol *speaker_sample_rate_kctl; + struct mutex volume_lock_mutex; bool speaker_volume_unlocked; bool speaker_volume_was_locked; struct snd_kcontrol *speaker_lock_kctl; @@ -284,10 +285,12 @@ static void macaudio_vlimit_update(struct macaudio_snd_data *ma) static void macaudio_vlimit_enable_timeout(struct macaudio_snd_data *ma) { - if (ma->speaker_lock_timeout_enabled) - return; + mutex_lock(&ma->volume_lock_mutex); - down_write(&ma->card.snd_card->controls_rwsem); + if (ma->speaker_lock_timeout_enabled) { + mutex_unlock(&ma->volume_lock_mutex); + return; + } if (ma->speaker_lock_remain > 0) { ma->speaker_lock_timeout = ktime_add(ktime_get(), ma->speaker_lock_remain); @@ -298,18 +301,22 @@ static void macaudio_vlimit_enable_timeout(struct macaudio_snd_data *ma) macaudio_vlimit_update(ma); - up_write(&ma->card.snd_card->controls_rwsem); ma->speaker_lock_timeout_enabled = true; + mutex_unlock(&ma->volume_lock_mutex); } static void macaudio_vlimit_disable_timeout(struct macaudio_snd_data *ma) { - ktime_t now = ktime_get(); + ktime_t now; + + mutex_lock(&ma->volume_lock_mutex); - if (!ma->speaker_lock_timeout_enabled) + if (!ma->speaker_lock_timeout_enabled) { + mutex_unlock(&ma->volume_lock_mutex); return; + } - down_write(&ma->card.snd_card->controls_rwsem); + now = ktime_get(); cancel_delayed_work(&ma->lock_timeout_work); @@ -323,8 +330,9 @@ static void macaudio_vlimit_disable_timeout(struct macaudio_snd_data *ma) macaudio_vlimit_update(ma); - up_write(&ma->card.snd_card->controls_rwsem); ma->speaker_lock_timeout_enabled = false; + + mutex_unlock(&ma->volume_lock_mutex); } static void macaudio_vlimit_timeout_work(struct work_struct *wrk) @@ -332,12 +340,12 @@ 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); - down_write(&ma->card.snd_card->controls_rwsem); + mutex_lock(&ma->volume_lock_mutex); ma->speaker_lock_remain = 0; macaudio_vlimit_update(ma); - up_write(&ma->card.snd_card->controls_rwsem); + mutex_unlock(&ma->volume_lock_mutex); } static void macaudio_vlimit_update_work(struct work_struct *wrk) @@ -1095,7 +1103,9 @@ static int macaudio_late_probe(struct snd_soc_card *card) ma->speaker_sample_rate_kctl = snd_soc_card_get_kcontrol(card, "Speaker Sample Rate"); ma->speaker_lock_kctl = snd_soc_card_get_kcontrol(card, "Speaker Volume Unlock"); + mutex_lock(&ma->volume_lock_mutex); macaudio_vlimit_unlock(ma, false); + mutex_unlock(&ma->volume_lock_mutex); return 0; } @@ -1336,6 +1346,8 @@ static int macaudio_slk_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v return -ETIMEDOUT; } + mutex_lock(&ma->volume_lock_mutex); + cancel_delayed_work(&ma->lock_timeout_work); ma->speaker_lock_remain = ms_to_ktime(SPEAKER_LOCK_TIMEOUT); @@ -1348,6 +1360,8 @@ static int macaudio_slk_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v schedule_delayed_work(&ma->lock_timeout_work, usecs_to_jiffies(ktime_to_us(ma->speaker_lock_remain))); } + mutex_unlock(&ma->volume_lock_mutex); + return 0; } @@ -1366,6 +1380,7 @@ static int macaudio_slk_lock(struct snd_kcontrol *kcontrol, struct snd_ctl_file struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + mutex_lock(&ma->volume_lock_mutex); ma->speaker_lock_owner = owner; macaudio_vlimit_update(ma); @@ -1377,6 +1392,8 @@ static int macaudio_slk_lock(struct snd_kcontrol *kcontrol, struct snd_ctl_file */ ma->speaker_volume_was_locked = false; + mutex_unlock(&ma->volume_lock_mutex); + return 0; } @@ -1469,6 +1486,7 @@ static int macaudio_snd_platform_probe(struct platform_device *pdev) card = &data->card; snd_soc_card_set_drvdata(card, data); dev_set_drvdata(&pdev->dev, data); + mutex_init(&data->volume_lock_mutex); card->owner = THIS_MODULE; card->driver_name = "macaudio"; From 3a8c4ad0052a2daa83ea8cf034e89ddf9ba78db8 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 22 Oct 2023 05:28:27 +0900 Subject: [PATCH 0311/1020] ASoC: tas2764: Add reg defaults for TAS2764_INT_CLK_CFG Signed-off-by: Hector Martin --- sound/soc/codecs/tas2764.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index cadcfe526dad1c..867762a0e54935 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -788,6 +788,7 @@ static const struct reg_default tas2764_reg_defaults[] = { { TAS2764_TDM_CFG2, 0x0a }, { TAS2764_TDM_CFG3, 0x10 }, { TAS2764_TDM_CFG5, 0x42 }, + { TAS2764_INT_CLK_CFG, 0x19 }, }; static const struct regmap_range_cfg tas2764_regmap_ranges[] = { From ef8813ab349edb231c1224fd3b310df7e431e691 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 22 Oct 2023 05:29:07 +0900 Subject: [PATCH 0312/1020] ASoC: tas2764: Mark SW_RESET as volatile Since the bit is self-clearing. Signed-off-by: Hector Martin --- sound/soc/codecs/tas2764.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 867762a0e54935..8268cde5b57bc7 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -806,6 +806,7 @@ static const struct regmap_range_cfg tas2764_regmap_ranges[] = { static bool tas2764_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { + case TAS2764_SW_RST: case TAS2764_INT_LTCH0 ... TAS2764_INT_LTCH4: case TAS2764_INT_CLK_CFG: return true; From 1b275ffceb1eae93737c64f869ecdb43fc4ba49b Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 22 Oct 2023 05:29:54 +0900 Subject: [PATCH 0313/1020] ASoC: tas2764: Fix power control mask Signed-off-by: Hector Martin --- sound/soc/codecs/tas2764.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/tas2764.h b/sound/soc/codecs/tas2764.h index dbe3f7fa901879..786d81eb5b1e71 100644 --- a/sound/soc/codecs/tas2764.h +++ b/sound/soc/codecs/tas2764.h @@ -25,7 +25,7 @@ /* Power Control */ #define TAS2764_PWR_CTRL TAS2764_REG(0X0, 0x02) -#define TAS2764_PWR_CTRL_MASK GENMASK(1, 0) +#define TAS2764_PWR_CTRL_MASK GENMASK(2, 0) #define TAS2764_PWR_CTRL_ACTIVE 0x0 #define TAS2764_PWR_CTRL_MUTE BIT(0) #define TAS2764_PWR_CTRL_SHUTDOWN BIT(1) From d2b282cc5c59d07c99d06e8379bab1f7ff27d720 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 22 Oct 2023 07:07:40 +0900 Subject: [PATCH 0314/1020] ASoC: apple: mca: Increase reset timeout Saw this fail once, let's be safer. Signed-off-by: Hector Martin --- sound/soc/apple/mca.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index c6b5ecc367c860..194b1430cf1176 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -216,9 +216,9 @@ static void mca_fe_early_trigger(struct snd_pcm_substream *substream, int cmd, SERDES_STATUS_RST); /* * Experiments suggest that it takes at most ~1 us - * for the bit to clear, so wait 2 us for good measure. + * for the bit to clear, so wait 5 us for good measure. */ - udelay(2); + udelay(5); WARN_ON(readl_relaxed(cl->base + serdes_unit + REG_SERDES_STATUS) & SERDES_STATUS_RST); mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL, From 0ddfab996b920e6ad336035f4ce2067c99236b62 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 22 Oct 2023 08:24:10 +0900 Subject: [PATCH 0315/1020] ALSA: dmaengine: Always terminate DMA when a PCM is closed When a PCM is suspended, we pause the DMA. If the PCM is then closed while in this state, it does not receive the STOP trigger (as it is not running). In this case, we fail to properly terminate the DMA, calling dmaengine_synchronize() nonetheless, which is undefined behavior. Make sure we always call dmaengine_terminate_async() on PCM close, regardless of whether it has been called previously or not in the trigger callbacks. Signed-off-by: Hector Martin --- sound/core/pcm_dmaengine.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 494ec0c207fad1..299b73bb2054df 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -359,6 +359,11 @@ int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream) { struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + /* + * The PCM might have been closed while suspended, which would + * skip the STOP trigger. Make sure we terminate. + */ + dmaengine_terminate_async(prtd->dma_chan); dmaengine_synchronize(prtd->dma_chan); kfree(prtd); From 5d2819655d85113eb04afd4943f1cb0154425e13 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 22 Oct 2023 09:00:09 +0900 Subject: [PATCH 0316/1020] ASoC: tas2764: Wait for ramp-down after shutdown When we shut down the amp, we need to wait for the built-in ramp-down before we can remove the TDM clocks. There is no documented status regiter to poll, so the best we can do is a delay. Datasheet says 5.9ms for ramp-down and 1.5ms between shutdown and next startup, so let's do 6ms after mute and 2ms after shutdown. That gives us a cumulative 8ms for ramp-down and guaratees the required minimum shutdown time. Signed-off-by: Hector Martin --- sound/soc/codecs/tas2764.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 8268cde5b57bc7..66cb898d71f66c 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -165,6 +165,8 @@ static int tas2764_codec_suspend(struct snd_soc_component *component) regcache_cache_only(tas2764->regmap, true); regcache_mark_dirty(tas2764->regmap); + usleep_range(6000, 7000); + return 0; } @@ -258,10 +260,16 @@ static int tas2764_mute(struct snd_soc_dai *dai, int mute, int direction) return ret; if (mute) { + /* Wait for ramp-down */ + usleep_range(6000, 7000); + tas2764->dac_powered = false; ret = tas2764_update_pwr_ctrl(tas2764); if (ret) return ret; + + /* Wait a bit after shutdown */ + usleep_range(2000, 3000); } return 0; From 1420057f27ac1e137ae555f8194a23f052f4a89a Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 28 Oct 2023 21:00:13 +0900 Subject: [PATCH 0317/1020] ASoC: tas2764: Enable some Apple quirks by default 0xf seems to fix the random overcurrent behavior. Signed-off-by: Hector Martin --- sound/soc/codecs/tas2764.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 66cb898d71f66c..51084f722015bb 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -50,7 +50,7 @@ struct tas2764_priv { bool unmuted; }; -static int apple_quirks; +static int apple_quirks = 0xf; module_param(apple_quirks, int, 0644); MODULE_PARM_DESC(apple_quirks, "Mask of quirks to mimic after Apple's SN012776 driver"); From a1fe304a09c144315a2636188e1a62b3354ec7b3 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 28 Oct 2023 22:10:32 +0900 Subject: [PATCH 0318/1020] macaudio: Rework platform config & add all remaining platforms Instead of open-coding a fixup function for each platform, let's make it declarative. This is a lot less error-prone. Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 418 ++++++++++++++++++++++--------------- 1 file changed, 250 insertions(+), 168 deletions(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 679e9b4a521e83..33a4b423b6e0cf 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -59,20 +59,45 @@ /* milliseconds */ #define SPEAKER_LOCK_TIMEOUT 250 -#define MAX_LIMITS 6 +enum macaudio_amp_type { + AMP_NONE, + AMP_TAS5770, + AMP_SN012776, + AMP_SSM3515, +}; -struct macaudio_limit_cfg { - const char *match; - int max_limited; - int max_unlimited; +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 { - struct macaudio_limit_cfg limits[MAX_LIMITS]; - int (*fixup)(struct snd_soc_card *card); bool enable_speakers; + enum macaudio_amp_type amp; + enum macaudio_spkr_config speakers; + bool stereo; + int amp_gain; + int safe_vol; +}; + +static const char *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 macaudio_snd_data { struct snd_soc_card card; struct snd_soc_jack jack; @@ -80,6 +105,7 @@ struct macaudio_snd_data { const struct macaudio_platform_cfg *cfg; bool has_speakers; + bool has_safety; unsigned int max_channels; struct macaudio_link_props { @@ -199,24 +225,42 @@ static struct macaudio_link_props macaudio_fe_link_props[] = { static void macaudio_vlimit_unlock(struct macaudio_snd_data *ma, bool unlock) { - int i, ret, max; + int ret, max; + const char *name = volume_control_names[ma->cfg->amp]; - for (i = 0; i < ARRAY_SIZE(ma->cfg->limits); i++) { - const struct macaudio_limit_cfg *limit = &ma->cfg->limits[i]; - - if (!limit->match) - break; + 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 = limit->max_unlimited; + max = TAS5770_0DB; else - max = limit->max_limited; - - ret = snd_soc_limit_volume(&ma->card, limit->match, max); - if (ret < 0) - dev_err(ma->card.dev, "Failed to %slock volume %s: %d\n", - unlock ? "un" : "", limit->match, ret); + max = 1; //TAS5770_DB(ma->cfg->safe_vol); + break; + case AMP_SN012776: + if (unlock) + max = SN012776_0DB; + else + max = 1; //SN012776_DB(ma->cfg->safe_vol); + break; + case AMP_SSM3515: + if (unlock) + max = SSM3515_0DB; + else + max = SSM3515_DB(ma->cfg->safe_vol); + break; } + + ret = snd_soc_limit_volume(&ma->card, 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) @@ -226,8 +270,8 @@ static void macaudio_vlimit_update(struct macaudio_snd_data *ma) struct snd_kcontrol *kctl; const char *reason; - /* Do nothing if there are no limits configured */ - if (!ma->cfg->limits[0].match) + /* Do nothing if there is no safety configured */ + if (!ma->has_safety) return; /* Check that someone is holding the main lock */ @@ -244,19 +288,7 @@ static void macaudio_vlimit_update(struct macaudio_snd_data *ma) /* Check that *every* limited control is locked by the same owner */ list_for_each_entry(kctl, &ma->card.snd_card->controls, list) { - bool is_limit = false; - - for (i = 0; i < ARRAY_SIZE(ma->cfg->limits); i++) { - const struct macaudio_limit_cfg *limit = &ma->cfg->limits[i]; - if (!limit->match) - break; - - is_limit = snd_soc_control_matches(kctl, limit->match); - if (is_limit) - break; - } - - if (!is_limit) + if(!snd_soc_control_matches(kctl, volume_control_names[ma->cfg->amp])) continue; for (i = 0; i < kctl->count; i++) { @@ -1100,19 +1132,21 @@ static int macaudio_late_probe(struct snd_soc_card *card) } } - ma->speaker_sample_rate_kctl = snd_soc_card_get_kcontrol(card, "Speaker Sample Rate"); - ma->speaker_lock_kctl = snd_soc_card_get_kcontrol(card, "Speaker Volume Unlock"); + 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"); - mutex_lock(&ma->volume_lock_mutex); - macaudio_vlimit_unlock(ma, false); - mutex_unlock(&ma->volume_lock_mutex); + mutex_lock(&ma->volume_lock_mutex); + macaudio_vlimit_unlock(ma, false); + mutex_unlock(&ma->volume_lock_mutex); + } return 0; } -#define TAS2764_0DB 201 -#define TAS2764_DB_REDUCTION(x) (TAS2764_0DB - 2 * (x)) - #define CHECK(call, pattern, value) \ { \ int ret = call(card, pattern, value); \ @@ -1123,141 +1157,90 @@ static int macaudio_late_probe(struct snd_soc_card *card) dev_dbg(card->dev, "%s on '%s': %d hits\n", #call, pattern, ret); \ } -static int macaudio_j274_fixup_controls(struct snd_soc_card *card) -{ - struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); - - if (ma->has_speakers) { - CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 14); // 20 set by macOS, this is 3 dB below - } - - return 0; -} - -struct macaudio_platform_cfg macaudio_j274_cfg = { - .fixup = macaudio_j274_fixup_controls, - .enable_speakers = true, -}; - -static int macaudio_j313_fixup_controls(struct snd_soc_card *card) { - struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); - - if (ma->has_speakers) { - CHECK(snd_soc_set_enum_kctl, "* ASI1 Sel", "Left"); - CHECK(snd_soc_deactivate_kctl, "* ASI1 Sel", 0); - - /* !!! This is copied from j274, not obtained by looking at - * what macOS sets. - */ - CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 14); +#define CHECK_CONCAT(call, suffix, value) \ + { \ + snprintf(buf, sizeof(buf), "%s%s", prefix, suffix); \ + CHECK(call, buf, value); \ } - return 0; -} - -struct macaudio_platform_cfg macaudio_j313_cfg = { - .fixup = macaudio_j313_fixup_controls, -}; - -static int macaudio_j314_fixup_controls(struct snd_soc_card *card) +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) { - CHECK(snd_soc_set_enum_kctl, "* ASI1 Sel", "Left"); - CHECK(snd_soc_deactivate_kctl, "* ASI1 Sel", 0); - CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 9); // 15 set by macOS, this is 3 dB below - CHECK(snd_soc_set_enum_kctl, "* Tweeter HPF Corner Frequency", "800 Hz"); - CHECK(snd_soc_deactivate_kctl, "* Tweeter HPF Corner Frequency", 0); - - /* - * The speaker amps suffer from spurious overcurrent - * events on their unmute, so enable autoretry. - */ - CHECK(snd_soc_set_enum_kctl, "* OCE Handling", "Retry"); - CHECK(snd_soc_deactivate_kctl, "* OCE Handling", 0); - } + if (!ma->has_speakers) + return 0; - 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); + } -struct macaudio_platform_cfg macaudio_j314_cfg = { - .fixup = macaudio_j314_fixup_controls, - .limits = { - {.match = "* Tweeter Speaker Volume", TAS2764_DB_REDUCTION(20), TAS2764_0DB}, - {.match = "* Woofer Speaker Volume", TAS2764_DB_REDUCTION(20), TAS2764_0DB}, - } -}; + 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"); -struct macaudio_platform_cfg macaudio_j413_cfg = { - .fixup = macaudio_j314_fixup_controls, - .limits = { - /* Min gain: -17.47 dB */ - {.match = "* Tweeter Speaker Volume", TAS2764_DB_REDUCTION(20), TAS2764_0DB}, - /* Min gain: -10.63 dB */ - /* FIXME: These structures are aliased so we can't set different max volumes */ - {.match = "* Woofer Speaker Volume", TAS2764_DB_REDUCTION(20), TAS2764_0DB}, - } -}; + if (!please_blow_up_my_speakers) + CHECK_CONCAT(snd_soc_deactivate_kctl, "HPF Corner Frequency", 0); -struct macaudio_platform_cfg macaudio_j415_cfg = { - .fixup = macaudio_j314_fixup_controls, - .limits = { - {.match = "* Tweeter Speaker Volume", TAS2764_DB_REDUCTION(20), TAS2764_0DB}, - {.match = "* Woofer 1 Speaker Volume", TAS2764_DB_REDUCTION(20), TAS2764_0DB}, - {.match = "* Woofer 2 Speaker Volume", TAS2764_DB_REDUCTION(20), TAS2764_0DB}, - } -}; + 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"); -static int macaudio_j375_fixup_controls(struct snd_soc_card *card) -{ - struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + if (!please_blow_up_my_speakers) + CHECK_CONCAT(snd_soc_deactivate_kctl, "DAC Analog Gain Select", 0); - if (ma->has_speakers) { - CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 14); // 20 set by macOS, this is 3 dB below + /* TODO: HPF, needs new call to set */ + break; + default: + return -EINVAL; } return 0; } -struct macaudio_platform_cfg macaudio_j375_cfg = { - .fixup = macaudio_j375_fixup_controls, -}; - -static int macaudio_j493_fixup_controls(struct snd_soc_card *card) +static int macaudio_fixup_controls(struct snd_soc_card *card) { struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); - if (ma->has_speakers) { - CHECK(snd_soc_limit_volume, "* Amp Gain Volume", 9); // 15 set by macOS, this is 3 dB below - } - - return 0; -} - -struct macaudio_platform_cfg macaudio_j493_cfg = { - .fixup = macaudio_j493_fixup_controls -}; - -static int macaudio_fallback_fixup_controls(struct snd_soc_card *card) -{ - struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + if (!ma->has_speakers) + return 0; - if (ma->has_speakers && !please_blow_up_my_speakers) { - dev_err(card->dev, "driver can't assure safety on this model, refusing probe\n"); - return -EINVAL; + switch(ma->cfg->speakers) { + case SPKR_NONE: + WARN_ON(!please_blow_up_my_speakers); + return please_blow_up_my_speakers ? 0 : -EINVAL; + case SPKR_1W: + case SPKR_2W: + CHECK(macaudio_set_speaker, "* ", false); + break; + case SPKR_1W1T: + CHECK(macaudio_set_speaker, "* Tweeter ", true); + CHECK(macaudio_set_speaker, "* Woofer ", false); + break; + case SPKR_2W1T: + CHECK(macaudio_set_speaker, "* Tweeter ", true); + CHECK(macaudio_set_speaker, "* Woofer 1 ", false); + CHECK(macaudio_set_speaker, "* Woofer 2 ", false); + break; } return 0; } -struct macaudio_platform_cfg macaudio_fallback_cfg = { - .fixup = macaudio_fallback_fixup_controls -}; - -#undef CHECK - static const char * const macaudio_spk_mux_texts[] = { "Primary", "Secondary" @@ -1407,8 +1390,17 @@ static void macaudio_slk_unlock(struct snd_kcontrol *kcontrol) macaudio_vlimit_update(ma); } -/* Speaker limit controls go last */ -#define MACAUDIO_NUM_SPEAKER_LIMIT_CONTROLS 2 +/* + * 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"), @@ -1417,19 +1409,19 @@ static const struct snd_kcontrol_new macaudio_controls[] = { { .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, - .lock = macaudio_slk_lock, .unlock = macaudio_slk_unlock, + .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 Sample Rate", - .info = macaudio_sss_info, .get = macaudio_sss_get, + .name = "Speaker Volume Unlock", + .info = macaudio_slk_info, + .put = macaudio_slk_put, .get = macaudio_slk_get, + .lock = macaudio_slk_lock, .unlock = macaudio_slk_unlock, }, }; @@ -1453,14 +1445,100 @@ static const struct snd_soc_dapm_route macaudio_dapm_routes[] = { { "PCM2 RX", NULL, "Speaker Sense Capture" }, }; +/* enable amp speakers stereo gain safe_vol */ +struct macaudio_platform_cfg macaudio_j180_cfg = { + false, AMP_SN012776, SPKR_1W1T, false, 4, -20, +}; +struct macaudio_platform_cfg macaudio_j274_cfg = { + true, AMP_TAS5770, SPKR_1W, false, 14, 0, /* TODO: safety */ +}; + +struct macaudio_platform_cfg macaudio_j293_cfg = { + false, AMP_TAS5770, SPKR_2W, true, 9, -20, /* TODO: check */ +}; + +struct macaudio_platform_cfg macaudio_j313_cfg = { + false, AMP_TAS5770, SPKR_1W, true, 4, -20, /* TODO: check */ +}; + +struct macaudio_platform_cfg macaudio_j314_j316_cfg = { + false, AMP_SN012776, SPKR_2W1T, true, 9, -20, +}; + +struct macaudio_platform_cfg macaudio_j37x_j47x_cfg = { + false, AMP_SN012776, SPKR_1W, false, 14, -20, +}; + +struct macaudio_platform_cfg macaudio_j413_cfg = { + false, AMP_SN012776, SPKR_1W1T, true, 9, -20, +}; + +struct macaudio_platform_cfg macaudio_j415_cfg = { + false, AMP_SN012776, SPKR_2W1T, true, 9, -20, +}; + +struct macaudio_platform_cfg macaudio_j45x_cfg = { + false, AMP_SSM3515, SPKR_1W1T, true, 9, -20, /* TODO: gain?? */ +}; + +struct macaudio_platform_cfg macaudio_j493_cfg = { + false, AMP_SN012776, SPKR_2W, true, 9, -20, +}; + +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 }, - { .compatible = "apple,j314-macaudio", .data = &macaudio_j314_cfg }, - { .compatible = "apple,j375-macaudio", .data = &macaudio_j375_cfg }, + /* j314 AID8 sn012776 15 2× 2W+1T */ + { .compatible = "apple,j314-macaudio", .data = &macaudio_j314_j316_cfg }, + /* j316 AID9 sn012776 15 2× 2W+1T */ + { .compatible = "apple,j316-macaudio", .data = &macaudio_j314_j316_cfg }, + /* j375 AID10 sn012776 15 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_j415_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"}, { } }; @@ -1507,16 +1585,20 @@ static int macaudio_snd_platform_probe(struct platform_device *pdev) else data->cfg = &macaudio_fallback_cfg; - /* Remove speaker safety controls if we have no declared limits */ - if (!data->cfg->limits[0].match) - card->num_controls -= MACAUDIO_NUM_SPEAKER_LIMIT_CONTROLS; - - card->fixup_controls = data->cfg->fixup; + 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; From 1b53080b105d94792619633cd216591a7ac46ad3 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 29 Oct 2023 17:31:48 +0900 Subject: [PATCH 0319/1020] ASoC: tas2770: Add SDZ regulator Multiple amps can be connected to the same SDZ GPIO. Using raw GPIOs for this breaks, as there is no concept of refcounting/sharing. In order to model these platforms, introduce support for an SDZ "regulator". This allows us to represent the SDZ GPIO as a simple regulator-fixed, and then the regulator core takes care of refcounting so that all codecs are only powered down once all the driver instances are in the suspend state. This also reworks the sleep/resume logic to copy what tas2764 does, which makes more sense. Signed-off-by: Hector Martin --- sound/soc/codecs/tas2770.c | 72 ++++++++++++++++++++++++++------------ sound/soc/codecs/tas2770.h | 1 + 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index c3733704b27ec5..79eb6781f15e7a 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -73,23 +73,21 @@ static int tas2770_codec_suspend(struct snd_soc_component *component) struct tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component); int ret = 0; - regcache_cache_only(tas2770->regmap, true); - regcache_mark_dirty(tas2770->regmap); + ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_SHUTDOWN); + if (ret < 0) + return ret; - if (tas2770->sdz_gpio) { + if (tas2770->sdz_gpio) gpiod_set_value_cansleep(tas2770->sdz_gpio, 0); - } else { - ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, - TAS2770_PWR_CTRL_MASK, - TAS2770_PWR_CTRL_SHUTDOWN); - if (ret < 0) { - regcache_cache_only(tas2770->regmap, false); - regcache_sync(tas2770->regmap); - return ret; - } - ret = 0; - } + regulator_disable(tas2770->sdz_reg); + + regcache_cache_only(tas2770->regmap, true); + regcache_mark_dirty(tas2770->regmap); + + usleep_range(6000, 7000); return ret; } @@ -99,18 +97,26 @@ static int tas2770_codec_resume(struct snd_soc_component *component) struct tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component); int ret; - if (tas2770->sdz_gpio) { - gpiod_set_value_cansleep(tas2770->sdz_gpio, 1); - usleep_range(1000, 2000); - } else { - ret = tas2770_update_pwr_ctrl(tas2770); - if (ret < 0) - return ret; + ret = regulator_enable(tas2770->sdz_reg); + + if (ret) { + dev_err(tas2770->dev, "Failed to enable regulator\n"); + return ret; } + if (tas2770->sdz_gpio) + gpiod_set_value_cansleep(tas2770->sdz_gpio, 1); + + + usleep_range(1000, 2000); + regcache_cache_only(tas2770->regmap, false); - return regcache_sync(tas2770->regmap); + ret = regcache_sync(tas2770->regmap); + if (ret < 0) + return ret; + + return tas2770_update_pwr_ctrl(tas2770); } #else #define tas2770_codec_suspend NULL @@ -545,11 +551,18 @@ static int tas2770_codec_probe(struct snd_soc_component *component) tas2770->component = component; + ret = regulator_enable(tas2770->sdz_reg); + if (ret != 0) { + dev_err(tas2770->dev, "Failed to enable regulator: %d\n", ret); + return ret; + } + if (tas2770->sdz_gpio) { gpiod_set_value_cansleep(tas2770->sdz_gpio, 1); - usleep_range(1000, 2000); } + usleep_range(1000, 2000); + tas2770_reset(tas2770); regmap_reinit_cache(tas2770->regmap, &tas2770_i2c_regmap); @@ -569,6 +582,13 @@ static int tas2770_codec_probe(struct snd_soc_component *component) return 0; } +static void tas2770_codec_remove(struct snd_soc_component *component) +{ + struct tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component); + + regulator_disable(tas2770->sdz_reg); +} + static DECLARE_TLV_DB_SCALE(tas2770_digital_tlv, 1100, 50, 0); static DECLARE_TLV_DB_SCALE(tas2770_playback_volume, -12750, 50, 0); @@ -581,6 +601,7 @@ static const struct snd_kcontrol_new tas2770_snd_controls[] = { static const struct snd_soc_component_driver soc_component_driver_tas2770 = { .probe = tas2770_codec_probe, + .remove = tas2770_codec_remove, .suspend = tas2770_codec_suspend, .resume = tas2770_codec_resume, .controls = tas2770_snd_controls, @@ -705,6 +726,11 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770) tas2770->v_sense_slot = -1; } + tas2770->sdz_reg = devm_regulator_get(dev, "SDZ"); + if (IS_ERR(tas2770->sdz_reg)) + return dev_err_probe(dev, PTR_ERR(tas2770->sdz_reg), + "Failed to get SDZ supply\n"); + tas2770->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH); if (IS_ERR(tas2770->sdz_gpio)) { if (PTR_ERR(tas2770->sdz_gpio) == -EPROBE_DEFER) diff --git a/sound/soc/codecs/tas2770.h b/sound/soc/codecs/tas2770.h index f75f40781ab136..f75baf23caf3a1 100644 --- a/sound/soc/codecs/tas2770.h +++ b/sound/soc/codecs/tas2770.h @@ -134,6 +134,7 @@ struct tas2770_priv { struct snd_soc_component *component; struct gpio_desc *reset_gpio; struct gpio_desc *sdz_gpio; + struct regulator *sdz_reg; struct regmap *regmap; struct device *dev; int v_sense_slot; From 4b65f27d9877b2c2b80c70a3b240d11a294d99ce Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 29 Oct 2023 21:57:42 +0900 Subject: [PATCH 0320/1020] ASoC: tas2770: Power cycle amp on ISENSE/VSENSE change The ISENSE/VSENSE blocks are only powered up when the amplifier transitions from shutdown to active. This means that if those controls are flipped on while the amplifier is already playing back audio, they will have no effect. Fix this by forcing a power cycle around transitions in those controls. Signed-off-by: Hector Martin --- sound/soc/codecs/tas2770.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 79eb6781f15e7a..148fd28257b506 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -165,11 +165,41 @@ static const struct snd_kcontrol_new isense_switch = static const struct snd_kcontrol_new vsense_switch = SOC_DAPM_SINGLE("Switch", TAS2770_PWR_CTRL, 2, 1, 1); +static int sense_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component); + int ret = 0; + + /* + * Powering up ISENSE/VSENSE requires a trip through the shutdown state. + * Do that here to ensure that our changes are applied properly, otherwise + * we might end up with non-functional IVSENSE if playback started earlier, + * which would break software speaker protection. + */ + + switch (event) { + case SND_SOC_DAPM_PRE_REG: + ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_SHUTDOWN); + break; + case SND_SOC_DAPM_POST_REG: + ret = tas2770_update_pwr_ctrl(tas2770); + break; + } + + return 0; +} + static const struct snd_soc_dapm_widget tas2770_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2770_asi1_mux), - SND_SOC_DAPM_SWITCH("ISENSE", TAS2770_PWR_CTRL, 3, 1, &isense_switch), - SND_SOC_DAPM_SWITCH("VSENSE", TAS2770_PWR_CTRL, 2, 1, &vsense_switch), + SND_SOC_DAPM_SWITCH_E("ISENSE", TAS2770_PWR_CTRL, 3, 1, &isense_switch, + sense_event, SND_SOC_DAPM_PRE_REG | SND_SOC_DAPM_POST_REG), + SND_SOC_DAPM_SWITCH_E("VSENSE", TAS2770_PWR_CTRL, 2, 1, &vsense_switch, + sense_event, SND_SOC_DAPM_PRE_REG | SND_SOC_DAPM_POST_REG), SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2770_dac_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_OUTPUT("OUT"), From 12fa04c633166c86577227f64cfc97a32e4666c4 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 29 Oct 2023 22:00:01 +0900 Subject: [PATCH 0321/1020] ASoC: tas2770: Add zero-fill and pull-down controls Expose the bits that control the behavior of the SDOUT pin when not actively transmitting slot data. Zero-fill is useful when there is a single amp on the SDOUT bus (e.g. Apple machines with mono speakers or a single stereo pair, where L/R are on separate buses). Pull-down is useful, though not perfect, when multiple amps share a bus. It typically takes around 2 bits for the line to transition from high to low after going Hi-Z, with the pull-down. Signed-off-by: Hector Martin --- sound/soc/codecs/tas2770.c | 17 +++++++++++++++++ sound/soc/codecs/tas2770.h | 13 +++++++++++++ 2 files changed, 30 insertions(+) diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 148fd28257b506..7c43b4dab38e0f 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -604,6 +604,20 @@ static int tas2770_codec_probe(struct snd_soc_component *component) return ret; } + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG4, + TAS2770_TDM_CFG_REG4_TX_FILL, + tas2770->sdout_zfill ? 0 : + TAS2770_TDM_CFG_REG4_TX_FILL); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2770_DIN_PD, + TAS2770_DIN_PD_SDOUT, + tas2770->sdout_pd ? + TAS2770_DIN_PD_SDOUT : 0); + if (ret < 0) + return ret; + ret = sysfs_create_groups(&component->dev->kobj, tas2770_sysfs_groups); if (ret < 0) @@ -756,6 +770,9 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770) tas2770->v_sense_slot = -1; } + tas2770->sdout_pd = fwnode_property_read_bool(dev->fwnode, "ti,sdout-pull-down"); + tas2770->sdout_zfill = fwnode_property_read_bool(dev->fwnode, "ti,sdout-zero-fill"); + tas2770->sdz_reg = devm_regulator_get(dev, "SDZ"); if (IS_ERR(tas2770->sdz_reg)) return dev_err_probe(dev, PTR_ERR(tas2770->sdz_reg), diff --git a/sound/soc/codecs/tas2770.h b/sound/soc/codecs/tas2770.h index f75baf23caf3a1..2c2cd777f4bcba 100644 --- a/sound/soc/codecs/tas2770.h +++ b/sound/soc/codecs/tas2770.h @@ -67,6 +67,14 @@ #define TAS2770_TDM_CFG_REG3_RXS_SHIFT 0x4 #define TAS2770_TDM_CFG_REG3_30_MASK GENMASK(3, 0) #define TAS2770_TDM_CFG_REG3_30_SHIFT 0 + /* TDM Configuration Reg4 */ +#define TAS2770_TDM_CFG_REG4 TAS2770_REG(0X0, 0x0E) +#define TAS2770_TDM_CFG_REG4_TX_LSB_CFG BIT(7) +#define TAS2770_TDM_CFG_REG4_TX_KEEPER_CFG BIT(6) +#define TAS2770_TDM_CFG_REG4_TX_KEEPER BIT(5) +#define TAS2770_TDM_CFG_REG4_TX_FILL BIT(4) +#define TAS2770_TDM_CFG_REG4_TX_OFFSET_MASK GENMASK(3, 1) +#define TAS2770_TDM_CFG_REG4_TX_EDGE_FALLING BIT(0) /* TDM Configuration Reg5 */ #define TAS2770_TDM_CFG_REG5 TAS2770_REG(0X0, 0x0F) #define TAS2770_TDM_CFG_REG5_VSNS_MASK BIT(6) @@ -110,6 +118,9 @@ #define TAS2770_TEMP_LSB TAS2770_REG(0X0, 0x2A) /* Interrupt Configuration */ #define TAS2770_INT_CFG TAS2770_REG(0X0, 0x30) + /* Data In Pull-Down */ +#define TAS2770_DIN_PD TAS2770_REG(0X0, 0x31) +#define TAS2770_DIN_PD_SDOUT BIT(7) /* Misc IRQ */ #define TAS2770_MISC_IRQ TAS2770_REG(0X0, 0x32) /* Clock Configuration */ @@ -139,6 +150,8 @@ struct tas2770_priv { struct device *dev; int v_sense_slot; int i_sense_slot; + bool sdout_pd; + bool sdout_zfill; bool dac_powered; bool unmuted; }; From 3347cc0fbf58f756b42e2775b1bcc2382c6fd8b7 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 29 Oct 2023 22:02:30 +0900 Subject: [PATCH 0322/1020] ASoC: tas2770: Support setting the PDM TX slot We don't actually support configuring the PDM input right now. Rather, this is useful as a hack. On Apple Silicon machines, amps are split between two I2S buses which are logically ANDed internally at the SoC. Odd and even slot groups are driven by amps on either bus respectively. Since the signals are ANDed, unused slot groups must be driven as zero to avoid corrupting the data from the other side. On most recent machines (TAS2764-based), this is accomplished using the "SDOUT zero mask" feature of that chip. Unfortunately, TAS2770 does not support this. It does support zeroing out *all* unused slots, which works well for machines with a single amp per I2S bus. That is all, except one. The 13" M1 MacBook Pro is the only machine using TAS2764 and two amps per I2S bus: L Bus: SPK0I SPK0V Hi-Z Hi-Z SPK2I SPK2V Hi-Z Hi-Z R Bus: Hi-Z Hi-Z SPK1I SPK2V Hi-Z Hi-Z SPK3I SPK3V To ensure uncorrupted data, we need to force all the Hi-Z periods to zero. We cannot use the "force all zero" feature, as that would cause a bus conflict between both amps. We can use the pull-down feature, but that leaves a few bits of garbage on the trailing edge of the speaker data, since the pull-down is weak. This is where the PDM transmit feature comes in. With PDM grounded and disabled (the default state), the PDM slot is transmitted as all zeroes. We can use that to force a zero 16-bit slot after the voltage data for each speaker, cleaning it up. Then the pull-down ensures the line stays low for the subsequent slot: L Bus: SPK0I SPK0V PDM0 PulDn SPK2I SPK2V PDM0 PulDn R Bus: PDM0 PulDn SPK1I SPK2V PDM0 PulDn SPK3I SPK3V Yes, this is a horrible hack, but it beats adding dummy slots that would be visible to the userspace capture side. There may be some other way to fix the logical AND behavior on the MCA side... that would make this unnecessary. ("How does Apple deal with this"? - they don't, macOS does not use IVSENSE on TAS2764 machines even though it's physically wired up, but we want to do so on Linux.) Signed-off-by: Hector Martin --- sound/soc/codecs/tas2770.c | 26 ++++++++++++++++++++++++++ sound/soc/codecs/tas2770.h | 6 ++++++ 2 files changed, 32 insertions(+) diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 7c43b4dab38e0f..1160be3a7cd080 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -253,6 +253,19 @@ static int tas2770_set_ivsense_transmit(struct tas2770_priv *tas2770, return 0; } +static int tas2770_set_pdm_transmit(struct tas2770_priv *tas2770, int slot) +{ + struct snd_soc_component *component = tas2770->component; + int ret; + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG7, + TAS2770_TDM_CFG_REG7_PDM_MASK | + TAS2770_TDM_CFG_REG7_50_MASK, + TAS2770_TDM_CFG_REG7_PDM_ENABLE | + slot); + return ret; +} + static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth) { int ret; @@ -604,6 +617,13 @@ static int tas2770_codec_probe(struct snd_soc_component *component) return ret; } + if (tas2770->pdm_slot != -1) { + ret = tas2770_set_pdm_transmit(tas2770, tas2770->pdm_slot); + + if (ret < 0) + return ret; + } + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG4, TAS2770_TDM_CFG_REG4_TX_FILL, tas2770->sdout_zfill ? 0 : @@ -770,6 +790,12 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770) tas2770->v_sense_slot = -1; } + rc = fwnode_property_read_u32(dev->fwnode, "ti,pdm-slot-no", + &tas2770->pdm_slot); + if (rc) { + tas2770->pdm_slot = -1; + } + tas2770->sdout_pd = fwnode_property_read_bool(dev->fwnode, "ti,sdout-pull-down"); tas2770->sdout_zfill = fwnode_property_read_bool(dev->fwnode, "ti,sdout-zero-fill"); diff --git a/sound/soc/codecs/tas2770.h b/sound/soc/codecs/tas2770.h index 2c2cd777f4bcba..b309d19c58e1da 100644 --- a/sound/soc/codecs/tas2770.h +++ b/sound/soc/codecs/tas2770.h @@ -85,6 +85,11 @@ #define TAS2770_TDM_CFG_REG6_ISNS_MASK BIT(6) #define TAS2770_TDM_CFG_REG6_ISNS_ENABLE BIT(6) #define TAS2770_TDM_CFG_REG6_50_MASK GENMASK(5, 0) + /* TDM Configuration Reg10 */ +#define TAS2770_TDM_CFG_REG7 TAS2770_REG(0X0, 0x11) +#define TAS2770_TDM_CFG_REG7_PDM_MASK BIT(6) +#define TAS2770_TDM_CFG_REG7_PDM_ENABLE BIT(6) +#define TAS2770_TDM_CFG_REG7_50_MASK GENMASK(5, 0) /* Brown Out Prevention Reg0 */ #define TAS2770_BO_PRV_REG0 TAS2770_REG(0X0, 0x1B) /* Interrupt MASK Reg0 */ @@ -150,6 +155,7 @@ struct tas2770_priv { struct device *dev; int v_sense_slot; int i_sense_slot; + int pdm_slot; bool sdout_pd; bool sdout_zfill; bool dac_powered; From 6bebd203e89e9b2a47e0b423d67734bfaca6d98d Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 30 Oct 2023 00:12:22 +0900 Subject: [PATCH 0323/1020] ASoC: tas2770: Fix volume scale The scale starts at -100dB, not -128dB. Signed-off-by: Hector Martin --- sound/soc/codecs/tas2770.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 1160be3a7cd080..19934844f4c22e 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -654,7 +654,7 @@ static void tas2770_codec_remove(struct snd_soc_component *component) } static DECLARE_TLV_DB_SCALE(tas2770_digital_tlv, 1100, 50, 0); -static DECLARE_TLV_DB_SCALE(tas2770_playback_volume, -12750, 50, 0); +static DECLARE_TLV_DB_SCALE(tas2770_playback_volume, -10050, 50, 0); static const struct snd_kcontrol_new tas2770_snd_controls[] = { SOC_SINGLE_TLV("Speaker Playback Volume", TAS2770_PLAY_CFG_REG2, From c54a89fb9a81d83b3dfbe963cb423d7d58ae1125 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 30 Oct 2023 00:26:59 +0900 Subject: [PATCH 0324/1020] macaudio: Remove -3dB safety pad from j313 This one already uses a gain lower than the others. It doesn't look like full scale no-DSP output with typical music is particularly dangerous here, and we probably want the headroom for DSP, so let's not do it for this one. Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 33a4b423b6e0cf..70df2d9ae508f0 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -1458,7 +1458,7 @@ struct macaudio_platform_cfg macaudio_j293_cfg = { }; struct macaudio_platform_cfg macaudio_j313_cfg = { - false, AMP_TAS5770, SPKR_1W, true, 4, -20, /* TODO: check */ + false, AMP_TAS5770, SPKR_1W, true, 10, -20, }; struct macaudio_platform_cfg macaudio_j314_j316_cfg = { From 4cc320e9b0ce76c225b11786547c42839f55bfba Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 3 Nov 2023 21:10:11 +0900 Subject: [PATCH 0325/1020] macaudio: Skip speaker sense PCM if no sense or no speakers This PCM triggers speakersafetyd, so hide it if it can't work. Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 70df2d9ae508f0..261f4562df6523 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -105,6 +105,7 @@ struct macaudio_snd_data { const struct macaudio_platform_cfg *cfg; bool has_speakers; + bool has_sense; bool has_safety; unsigned int max_channels; @@ -569,6 +570,8 @@ static int macaudio_parse_of(struct macaudio_snd_data *ma) 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"); @@ -670,6 +673,18 @@ static int macaudio_parse_of(struct macaudio_snd_data *ma) 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; From 1fb21ee18b6984a8509e4bcfa778bbb6907c6f3d Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 7 Nov 2023 21:16:57 +0900 Subject: [PATCH 0326/1020] macaudio: Officially enable j313 speakers Still hard gated on speakersafetyd for now. Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 261f4562df6523..c44ebef38ed86f 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -1473,7 +1473,7 @@ struct macaudio_platform_cfg macaudio_j293_cfg = { }; struct macaudio_platform_cfg macaudio_j313_cfg = { - false, AMP_TAS5770, SPKR_1W, true, 10, -20, + true, AMP_TAS5770, SPKR_1W, true, 10, -20, }; struct macaudio_platform_cfg macaudio_j314_j316_cfg = { From f60f98eeca4fab9197e356c7c39eb3bd68b51e0e Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 9 Nov 2023 18:32:57 +0900 Subject: [PATCH 0327/1020] ASoC: tas2764: Set the SDOUT polarity correctly TX launch polarity needs to be the opposite of RX capture polarity, to generate the right bit slot alignment. Signed-off-by: Hector Martin --- sound/soc/codecs/tas2764.c | 10 +++++++++- sound/soc/codecs/tas2764.h | 6 ++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 51084f722015bb..d1159bd7398059 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -409,7 +409,7 @@ static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_component *component = dai->component; struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); - u8 tdm_rx_start_slot = 0, asi_cfg_0 = 0, asi_cfg_1 = 0; + u8 tdm_rx_start_slot = 0, asi_cfg_0 = 0, asi_cfg_1 = 0, asi_cfg_4 = 0; int ret; switch (fmt & SND_SOC_DAIFMT_INV_MASK) { @@ -418,12 +418,14 @@ static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) fallthrough; case SND_SOC_DAIFMT_NB_NF: asi_cfg_1 = TAS2764_TDM_CFG1_RX_RISING; + asi_cfg_4 = TAS2764_TDM_CFG4_TX_FALLING; break; case SND_SOC_DAIFMT_IB_IF: asi_cfg_0 ^= TAS2764_TDM_CFG0_FRAME_START; fallthrough; case SND_SOC_DAIFMT_IB_NF: asi_cfg_1 = TAS2764_TDM_CFG1_RX_FALLING; + asi_cfg_4 = TAS2764_TDM_CFG4_TX_RISING; break; } @@ -433,6 +435,12 @@ static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) if (ret < 0) return ret; + ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG4, + TAS2764_TDM_CFG4_TX_MASK, + asi_cfg_4); + if (ret < 0) + return ret; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: asi_cfg_0 ^= TAS2764_TDM_CFG0_FRAME_START; diff --git a/sound/soc/codecs/tas2764.h b/sound/soc/codecs/tas2764.h index 786d81eb5b1e71..8d9579cffd3e20 100644 --- a/sound/soc/codecs/tas2764.h +++ b/sound/soc/codecs/tas2764.h @@ -84,6 +84,12 @@ #define TAS2764_TDM_CFG3_RXS_SHIFT 0x4 #define TAS2764_TDM_CFG3_MASK GENMASK(3, 0) +/* TDM Configuration Reg4 */ +#define TAS2764_TDM_CFG4 TAS2764_REG(0X0, 0x0c) +#define TAS2764_TDM_CFG4_TX_MASK BIT(0) +#define TAS2764_TDM_CFG4_TX_RISING 0x0 +#define TAS2764_TDM_CFG4_TX_FALLING BIT(0) + /* TDM Configuration Reg5 */ #define TAS2764_TDM_CFG5 TAS2764_REG(0X0, 0x0e) #define TAS2764_TDM_CFG5_VSNS_MASK BIT(6) From 1322eef03de987086cbb1f0e81ae01110c04b3aa Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 9 Nov 2023 18:33:41 +0900 Subject: [PATCH 0328/1020] ASoC: tas2770: Set the SDOUT polarity correctly TX launch polarity needs to be the opposite of RX capture polarity, to generate the right bit slot alignment. Signed-off-by: Hector Martin --- sound/soc/codecs/tas2770.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 19934844f4c22e..d278cb36acb014 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -364,7 +364,7 @@ static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) struct snd_soc_component *component = dai->component; struct tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component); - u8 tdm_rx_start_slot = 0, invert_fpol = 0, fpol_preinv = 0, asi_cfg_1 = 0; + u8 tdm_rx_start_slot = 0, invert_fpol = 0, fpol_preinv = 0, asi_cfg_1 = 0, asi_cfg_4 = 0; int ret; switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { @@ -381,6 +381,7 @@ static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) fallthrough; case SND_SOC_DAIFMT_NB_NF: asi_cfg_1 |= TAS2770_TDM_CFG_REG1_RX_RSING; + asi_cfg_4 |= TAS2770_TDM_CFG_REG4_TX_EDGE_FALLING; break; case SND_SOC_DAIFMT_IB_IF: invert_fpol = 1; @@ -399,6 +400,12 @@ static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) if (ret < 0) return ret; + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG4, + TAS2770_TDM_CFG_REG4_TX_EDGE_FALLING, + asi_cfg_4); + if (ret < 0) + return ret; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: tdm_rx_start_slot = 1; From 1e643912187f9755e19dc1063df306fb19b0424d Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 25 Nov 2023 11:30:44 +0100 Subject: [PATCH 0329/1020] fixup! ASoC: tas2764: Add optional 'Apple quirks' Fix fallthrough warning and remove stray spaces before tabs for indenting. Signed-off-by: Janne Grunau --- sound/soc/codecs/tas2764-quirks.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/tas2764-quirks.h b/sound/soc/codecs/tas2764-quirks.h index 7cb860e7e9c252..9cbbc2a9e5944f 100644 --- a/sound/soc/codecs/tas2764-quirks.h +++ b/sound/soc/codecs/tas2764-quirks.h @@ -40,7 +40,7 @@ struct reg_sequence tas2764_dmod_rst_seq[] = { struct reg_sequence tas2764_unk_seq0[] = { REG_SEQ0(TAS2764_REG(0x1, 0x33), 0x80), - REG_SEQ0(TAS2764_REG(0x1, 0x37), 0x3a), + REG_SEQ0(TAS2764_REG(0x1, 0x37), 0x3a), }; /* @@ -137,17 +137,17 @@ static int tas2764_do_quirky_pwr_ctrl_change(struct tas2764_priv *tas2764, ret = snd_soc_component_update_bits(tas2764->component, TAS2764_PWR_CTRL, - TAS2764_PWR_CTRL_MASK, - TAS2764_PWR_CTRL_SHUTDOWN); + TAS2764_PWR_CTRL_MASK, + TAS2764_PWR_CTRL_SHUTDOWN); if (ret > 0) break; ret = regmap_multi_reg_write(tas2764->regmap, tas2764_post_shutdown_seq, ARRAY_SIZE(tas2764_post_shutdown_seq)); - + fallthrough; default: ret = snd_soc_component_update_bits(tas2764->component, TAS2764_PWR_CTRL, - TAS2764_PWR_CTRL_MASK, target); + TAS2764_PWR_CTRL_MASK, target); } #undef TRANSITION From 11d968cd03d3be701726c2c2249455048a82403b Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 28 Nov 2023 19:18:55 +0900 Subject: [PATCH 0330/1020] fixup! ASoC: tas2764: Set the SDOUT polarity correctly --- sound/soc/codecs/tas2764.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/tas2764.h b/sound/soc/codecs/tas2764.h index 8d9579cffd3e20..4a419c11d4b08e 100644 --- a/sound/soc/codecs/tas2764.h +++ b/sound/soc/codecs/tas2764.h @@ -85,7 +85,7 @@ #define TAS2764_TDM_CFG3_MASK GENMASK(3, 0) /* TDM Configuration Reg4 */ -#define TAS2764_TDM_CFG4 TAS2764_REG(0X0, 0x0c) +#define TAS2764_TDM_CFG4 TAS2764_REG(0X0, 0x0d) #define TAS2764_TDM_CFG4_TX_MASK BIT(0) #define TAS2764_TDM_CFG4_TX_RISING 0x0 #define TAS2764_TDM_CFG4_TX_FALLING BIT(0) From 7fd4a32c4b3bc9b4f857731cd35f37e90099d838 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 28 Nov 2023 23:13:01 +0900 Subject: [PATCH 0331/1020] fixup! ASoC: tas2764: Enable some Apple quirks by default --- sound/soc/codecs/tas2764.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index d1159bd7398059..95ed2efe911e8e 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -50,7 +50,7 @@ struct tas2764_priv { bool unmuted; }; -static int apple_quirks = 0xf; +static int apple_quirks = 0x3f; module_param(apple_quirks, int, 0644); MODULE_PARM_DESC(apple_quirks, "Mask of quirks to mimic after Apple's SN012776 driver"); From 32e3eea6fb12b0c2de5de6c9043a43220b2a7c11 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 4 Dec 2023 01:21:33 +0900 Subject: [PATCH 0332/1020] macaudio: Set the card name explicitly This might fix a udev race, and also makes it possible to switch to a more descriptive "AppleJxxx" name (but before that we need to update userspace to avoid breaking users). Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index c44ebef38ed86f..e8b0dc2b004f15 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -1230,6 +1230,14 @@ static int macaudio_set_speaker(struct snd_soc_card *card, const char *prefix, b 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), + "%s", p + 1); + } if (!ma->has_speakers) return 0; From 3bfa5391943cd86052be167862c9e5531df5d0b8 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 5 Dec 2023 12:36:52 +0900 Subject: [PATCH 0333/1020] macaudio: Change device ID form Jxxx to AppleJxxx Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index e8b0dc2b004f15..a1415217d0de80 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -1236,7 +1236,7 @@ static int macaudio_fixup_controls(struct snd_soc_card *card) p = strrchr(card->name, ' '); if (p) { snprintf(card->snd_card->id, sizeof(card->snd_card->id), - "%s", p + 1); + "Apple%s", p + 1); } if (!ma->has_speakers) From c1c6af97c3f162eb2062eb980b145503d31f95d9 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 11 Dec 2023 22:34:15 +0900 Subject: [PATCH 0334/1020] macaudio: Turn please_blow_up_my_speakers into an int 1 enables new models, 2 further removes safeties. Mostly so that people who set it to 1 for early access and forget don't get stuck without safety nets. Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index a1415217d0de80..f988222b0c858a 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -137,8 +137,8 @@ struct macaudio_snd_data { }; -static bool please_blow_up_my_speakers; -module_param(please_blow_up_my_speakers, bool, 0644); +static int please_blow_up_my_speakers; +module_param(please_blow_up_my_speakers, int, 0644); MODULE_PARM_DESC(please_blow_up_my_speakers, "Allow unsafe or untested operating configurations"); SND_SOC_DAILINK_DEFS(primary, @@ -1165,7 +1165,7 @@ static int macaudio_late_probe(struct snd_soc_card *card) #define CHECK(call, pattern, value) \ { \ int ret = call(card, pattern, value); \ - if (ret < 1 && !please_blow_up_my_speakers) { \ + if (ret < 1 && (please_blow_up_my_speakers < 2)) { \ dev_err(card->dev, "%s on '%s': %d\n", #call, pattern, ret); \ return ret; \ } \ @@ -1205,7 +1205,7 @@ static int macaudio_set_speaker(struct snd_soc_card *card, const char *prefix, b CHECK_CONCAT(snd_soc_set_enum_kctl, "HPF Corner Frequency", tweeter ? "800 Hz" : "2 Hz"); - if (!please_blow_up_my_speakers) + if (please_blow_up_my_speakers < 2) CHECK_CONCAT(snd_soc_deactivate_kctl, "HPF Corner Frequency", 0); CHECK_CONCAT(snd_soc_set_enum_kctl, "OCE Handling", "Retry"); @@ -1215,7 +1215,7 @@ static int macaudio_set_speaker(struct snd_soc_card *card, const char *prefix, b /* TODO: check */ CHECK_CONCAT(snd_soc_set_enum_kctl, "DAC Analog Gain Select", "8.4 V Span"); - if (!please_blow_up_my_speakers) + if (please_blow_up_my_speakers < 2) CHECK_CONCAT(snd_soc_deactivate_kctl, "DAC Analog Gain Select", 0); /* TODO: HPF, needs new call to set */ @@ -1244,8 +1244,8 @@ static int macaudio_fixup_controls(struct snd_soc_card *card) switch(ma->cfg->speakers) { case SPKR_NONE: - WARN_ON(!please_blow_up_my_speakers); - return please_blow_up_my_speakers ? 0 : -EINVAL; + WARN_ON(please_blow_up_my_speakers < 2); + return please_blow_up_my_speakers >= 2 ? 0 : -EINVAL; case SPKR_1W: case SPKR_2W: CHECK(macaudio_set_speaker, "* ", false); From 049c35421f56a3dd9fdb8928708704f1017cb9a3 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 11 Dec 2023 22:34:56 +0900 Subject: [PATCH 0335/1020] macaudio: Sync all gains with macOS We want the extra headroom, and speakersafetyd seems to be reliable. 3dB lower gain isn't going to buy us much safety at this point. Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index f988222b0c858a..d136f7d6f2d2dc 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -1470,14 +1470,14 @@ static const struct snd_soc_dapm_route macaudio_dapm_routes[] = { /* enable amp speakers stereo gain safe_vol */ struct macaudio_platform_cfg macaudio_j180_cfg = { - false, AMP_SN012776, SPKR_1W1T, false, 4, -20, + false, AMP_SN012776, SPKR_1W1T, false, 10, -20, }; struct macaudio_platform_cfg macaudio_j274_cfg = { - true, AMP_TAS5770, SPKR_1W, false, 14, 0, /* TODO: safety */ + true, AMP_TAS5770, SPKR_1W, false, 20, -20, }; struct macaudio_platform_cfg macaudio_j293_cfg = { - false, AMP_TAS5770, SPKR_2W, true, 9, -20, /* TODO: check */ + false, AMP_TAS5770, SPKR_2W, true, 15, -20, }; struct macaudio_platform_cfg macaudio_j313_cfg = { @@ -1485,19 +1485,19 @@ struct macaudio_platform_cfg macaudio_j313_cfg = { }; struct macaudio_platform_cfg macaudio_j314_j316_cfg = { - false, AMP_SN012776, SPKR_2W1T, true, 9, -20, + false, AMP_SN012776, SPKR_2W1T, true, 15, -20, }; struct macaudio_platform_cfg macaudio_j37x_j47x_cfg = { - false, AMP_SN012776, SPKR_1W, false, 14, -20, + false, AMP_SN012776, SPKR_1W, false, 20, -20, }; struct macaudio_platform_cfg macaudio_j413_cfg = { - false, AMP_SN012776, SPKR_1W1T, true, 9, -20, + false, AMP_SN012776, SPKR_1W1T, true, 15, -20, }; struct macaudio_platform_cfg macaudio_j415_cfg = { - false, AMP_SN012776, SPKR_2W1T, true, 9, -20, + false, AMP_SN012776, SPKR_2W1T, true, 15, -20, }; struct macaudio_platform_cfg macaudio_j45x_cfg = { @@ -1505,7 +1505,7 @@ struct macaudio_platform_cfg macaudio_j45x_cfg = { }; struct macaudio_platform_cfg macaudio_j493_cfg = { - false, AMP_SN012776, SPKR_2W, true, 9, -20, + false, AMP_SN012776, SPKR_2W, true, 15, -20, }; struct macaudio_platform_cfg macaudio_fallback_cfg = { From 6c6fbaab46ed442b0242831320e6f3bfe2a5baea Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 12 Dec 2023 19:57:05 +0900 Subject: [PATCH 0336/1020] fixup! ASoC: macaudio: Start speaker sense capture support --- sound/soc/apple/macaudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index d136f7d6f2d2dc..4879ce5580670b 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -1465,7 +1465,7 @@ static const struct snd_soc_dapm_route macaudio_dapm_routes[] = { { "PCM0 RX", NULL, "Headset Capture" }, /* Sense paths */ - { "PCM2 RX", NULL, "Speaker Sense Capture" }, + { "PCM2 RX", NULL, "Speaker Sense Capture" }, }; /* enable amp speakers stereo gain safe_vol */ From 21b42cf0cf883cd61492e51953e2526132158970 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 12 Dec 2023 19:57:23 +0900 Subject: [PATCH 0337/1020] macaudio: Fix CHECK return condition checking Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 4879ce5580670b..1118ca2ac05220 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -1162,20 +1162,25 @@ static int macaudio_late_probe(struct snd_soc_card *card) return 0; } -#define CHECK(call, pattern, value) \ - { \ - int ret = call(card, pattern, value); \ - if (ret < 1 && (please_blow_up_my_speakers < 2)) { \ - dev_err(card->dev, "%s on '%s': %d\n", #call, pattern, ret); \ - return ret; \ - } \ - dev_dbg(card->dev, "%s on '%s': %d hits\n", #call, pattern, ret); \ +#define CHECK(call, pattern, value, min) \ + { \ + int ret = call(card, pattern, value); \ + int err = (ret >= 0 && ret < min) ? -ERANGE : ret; \ + if (err < 0) { \ + dev_err(card->dev, "%s on '%s': %d\n", #call, pattern, \ + ret); \ + if (please_blow_up_my_speakers < 2) \ + return err; \ + } else { \ + dev_dbg(card->dev, "%s on '%s': %d hits\n", #call, \ + pattern, ret); \ + } \ } #define CHECK_CONCAT(call, suffix, value) \ { \ snprintf(buf, sizeof(buf), "%s%s", prefix, suffix); \ - CHECK(call, buf, value); \ + CHECK(call, buf, value, 1); \ } static int macaudio_set_speaker(struct snd_soc_card *card, const char *prefix, bool tweeter) @@ -1248,16 +1253,16 @@ static int macaudio_fixup_controls(struct snd_soc_card *card) return please_blow_up_my_speakers >= 2 ? 0 : -EINVAL; case SPKR_1W: case SPKR_2W: - CHECK(macaudio_set_speaker, "* ", false); + CHECK(macaudio_set_speaker, "* ", false, 0); break; case SPKR_1W1T: - CHECK(macaudio_set_speaker, "* Tweeter ", true); - CHECK(macaudio_set_speaker, "* Woofer ", false); + CHECK(macaudio_set_speaker, "* Tweeter ", true, 0); + CHECK(macaudio_set_speaker, "* Woofer ", false, 0); break; case SPKR_2W1T: - CHECK(macaudio_set_speaker, "* Tweeter ", true); - CHECK(macaudio_set_speaker, "* Woofer 1 ", false); - CHECK(macaudio_set_speaker, "* Woofer 2 ", false); + CHECK(macaudio_set_speaker, "* Tweeter ", true, 0); + CHECK(macaudio_set_speaker, "* Woofer 1 ", false, 0); + CHECK(macaudio_set_speaker, "* Woofer 2 ", false, 0); break; } From 5435762251fb0ab32ccabaf53518b38c0ebfe82c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 12 Dec 2023 23:26:16 +0100 Subject: [PATCH 0338/1020] macaudio: Avoid matches against cs42l84's constrols On systems with cs42l84 headset codec "* " can't be used as control name pattern since it would match "Jack HPF Corner Frequency". Its control is not an enum and thus will always return -EINVAL. Signed-off-by: Janne Grunau --- sound/soc/apple/macaudio.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 1118ca2ac05220..60ae14086e24c9 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -1247,13 +1247,25 @@ static int macaudio_fixup_controls(struct snd_soc_card *card) if (!ma->has_speakers) return 0; + /* + * This needs some care to avoid matches against cs42l84's + * "Jack HPF Corner Frequency". + */ switch(ma->cfg->speakers) { case SPKR_NONE: WARN_ON(please_blow_up_my_speakers < 2); return please_blow_up_my_speakers >= 2 ? 0 : -EINVAL; case SPKR_1W: + /* only 1W stereo system (J313) is uses cs42l83 */ + if (ma->cfg->stereo) { + CHECK(macaudio_set_speaker, "* ", false, 0); + } else { + CHECK(macaudio_set_speaker, "", false, 0); + } + break; case SPKR_2W: - CHECK(macaudio_set_speaker, "* ", false, 0); + CHECK(macaudio_set_speaker, "* Front ", false, 0); + CHECK(macaudio_set_speaker, "* Rear ", false, 0); break; case SPKR_1W1T: CHECK(macaudio_set_speaker, "* Tweeter ", true, 0); From fee539b949132a89b6e6c1aaa3b9ad15a451d002 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 14 Dec 2023 21:21:12 +0900 Subject: [PATCH 0339/1020] ASoC: apple: mca: Add delay after configuring clock Right after the early FE setup, ADMAC gets told to start the DMA. This can end up in a weird "slip" state with the channels transposed. Waiting a bit fixes this; presumably this allows the clock to stabilize. Signed-off-by: Hector Martin --- sound/soc/apple/mca.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index 194b1430cf1176..aff52ec1990b1b 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -225,6 +225,12 @@ static void mca_fe_early_trigger(struct snd_pcm_substream *substream, int cmd, FIELD_PREP(SERDES_CONF_SYNC_SEL, 0)); mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL, FIELD_PREP(SERDES_CONF_SYNC_SEL, cl->no + 1)); + /* + * ADMAC gets started right after this. This delay seems + * to be needed for that to be reliable, e.g. ensure the + * clock is stable? + */ + udelay(10); break; default: break; From 51b9f34a4b1198e077641a0badd6e82a6739c099 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 15 Dec 2023 20:27:42 +0900 Subject: [PATCH 0340/1020] macaudio: Disable j313 and j274 We are going to enable these out of band. If you are a distro packager: ** WARNING: ** ** YOU ABSOLUTELY NEED THIS PATCH IN YOUR LSP-PLUGINS PACKAGE ** https://github.com/lsp-plugins/lsp-dsp-lib/pull/20 Do NOT enable speakers without that patch, on any model. It can/will result in nasty noise that could damage them. Signed-off-by: Hector Martin --- sound/soc/apple/macaudio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index 60ae14086e24c9..418d4fd37ab99b 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -1490,7 +1490,7 @@ struct macaudio_platform_cfg macaudio_j180_cfg = { false, AMP_SN012776, SPKR_1W1T, false, 10, -20, }; struct macaudio_platform_cfg macaudio_j274_cfg = { - true, AMP_TAS5770, SPKR_1W, false, 20, -20, + false, AMP_TAS5770, SPKR_1W, false, 20, -20, }; struct macaudio_platform_cfg macaudio_j293_cfg = { @@ -1498,7 +1498,7 @@ struct macaudio_platform_cfg macaudio_j293_cfg = { }; struct macaudio_platform_cfg macaudio_j313_cfg = { - true, AMP_TAS5770, SPKR_1W, true, 10, -20, + false, AMP_TAS5770, SPKR_1W, true, 10, -20, }; struct macaudio_platform_cfg macaudio_j314_j316_cfg = { From 793537a018c473a75c689192f1061f1c1661ae21 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 17 Dec 2023 14:35:33 +0900 Subject: [PATCH 0341/1020] ASoC: apple: mca: Add more delay after configuring clock Sigh... hope this works. Signed-off-by: Hector Martin --- sound/soc/apple/mca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index aff52ec1990b1b..3d70e7299a63a8 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -230,7 +230,7 @@ static void mca_fe_early_trigger(struct snd_pcm_substream *substream, int cmd, * to be needed for that to be reliable, e.g. ensure the * clock is stable? */ - udelay(10); + udelay(100); break; default: break; From 6b453759a9c726375cc1b5142d29b2d82dc36f19 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 19 Dec 2023 18:21:53 +0900 Subject: [PATCH 0342/1020] ASoC: apple: mca: More delay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ¯\_(ツ)_/¯ Signed-off-by: Hector Martin --- sound/soc/apple/mca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index 3d70e7299a63a8..aa93be31cbde9d 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -218,7 +218,7 @@ static void mca_fe_early_trigger(struct snd_pcm_substream *substream, int cmd, * Experiments suggest that it takes at most ~1 us * for the bit to clear, so wait 5 us for good measure. */ - udelay(5); + udelay(50); WARN_ON(readl_relaxed(cl->base + serdes_unit + REG_SERDES_STATUS) & SERDES_STATUS_RST); mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL, From 4591abe90a19b4b3d93d43ca4202e066a2dd2b7f Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Sun, 17 Dec 2023 16:16:03 +0100 Subject: [PATCH 0343/1020] macaudio: Fix missing kconfig requirement Signed-off-by: Sasha Finkelstein --- sound/soc/apple/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/apple/Kconfig b/sound/soc/apple/Kconfig index 9fb902340fff6e..d95a6341dc3fdf 100644 --- a/sound/soc/apple/Kconfig +++ b/sound/soc/apple/Kconfig @@ -18,6 +18,7 @@ config SND_SOC_APPLE_MACAUDIO select SND_SOC_TAS2770 select SND_SOC_CS42L83 select SND_SOC_CS42L84 + select REGULATOR_FIXED_VOLTAGE default ARCH_APPLE help This option enables an ASoC machine-level driver for Apple Silicon Macs From 6200ec833351c5ed57dd47015ca2b3efce38c596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sun, 4 Dec 2022 15:34:53 +0100 Subject: [PATCH 0344/1020] fixup! dmaengine: apple-admac: Add Apple ADMAC driver --- drivers/dma/apple-admac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/apple-admac.c b/drivers/dma/apple-admac.c index 9588773dd2eb67..610bc6994dd330 100644 --- a/drivers/dma/apple-admac.c +++ b/drivers/dma/apple-admac.c @@ -252,7 +252,7 @@ static struct dma_async_tx_descriptor *admac_prep_dma_cyclic( size_t period_len, enum dma_transfer_direction direction, unsigned long flags) { - struct admac_chan *adchan = container_of(chan, struct admac_chan, chan); + struct admac_chan *adchan = to_admac_chan(chan); struct admac_tx *adtx; if (direction != admac_chan_direction(adchan->no)) From 2eb65172b2a938669ca1c2fc7974a21c0decfecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 23 Feb 2023 17:57:56 +0100 Subject: [PATCH 0345/1020] HACK: ALSA: Export 'snd_pcm_known_rates' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/core/pcm_native.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 67228b64f90270..3f7185d4321533 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2424,6 +2424,7 @@ const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = { .count = ARRAY_SIZE(rates), .list = rates, }; +EXPORT_SYMBOL_GPL(snd_pcm_known_rates); static int snd_pcm_hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) From 01272d386daacf50b0498969097ca6c2a2d70def Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 23 Feb 2023 12:34:28 +0100 Subject: [PATCH 0346/1020] ALSA: Introduce 'snd_interval_rate_bits' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- include/sound/pcm.h | 1 + sound/core/pcm_lib.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 210096f124eed5..0ae424f9cf9430 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1060,6 +1060,7 @@ int snd_interval_ranges(struct snd_interval *i, unsigned int count, int snd_interval_ratnum(struct snd_interval *i, unsigned int rats_count, const struct snd_ratnum *rats, unsigned int *nump, unsigned int *denp); +int snd_interval_rate_bits(struct snd_interval *i, unsigned int rate_bits); void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params); void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 6f73b3c2c205ee..92200db035f253 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1129,6 +1129,43 @@ static int snd_interval_step(struct snd_interval *i, unsigned int step) return changed; } +/** + * snd_interval_rate_bits - refine the rate interval from a rate bitmask + * @i: the rate interval to refine + * @mask: the rate bitmask + * + * Refines the interval value, assumed to be the sample rate, according to + * a bitmask of available rates (an ORed combination of SNDRV_PCM_RATE_*). + * + * Return: Positive if the value is changed, zero if it's not changed, or a + * negative error code. + */ +int snd_interval_rate_bits(struct snd_interval *i, unsigned int mask) +{ + unsigned int k; + struct snd_interval mask_range; + + if (!mask) + return -EINVAL; + + snd_interval_any(&mask_range); + mask_range.min = UINT_MAX; + mask_range.max = 0; + for (k = 0; k < snd_pcm_known_rates.count; k++) { + unsigned int rate = snd_pcm_known_rates.list[k]; + if (!(mask & (1 << k))) + continue; + + if (rate > mask_range.max) + mask_range.max = rate; + + if (rate < mask_range.min) + mask_range.min = rate; + } + return snd_interval_refine(i, &mask_range); +} +EXPORT_SYMBOL(snd_interval_rate_bits); + /* Info constraints helpers */ /** From cd8f0af313dbcf989720590a23acc6043eac492b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 17 Feb 2023 17:02:10 +0100 Subject: [PATCH 0347/1020] ALSA: Support nonatomic dmaengine PCMs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- sound/core/pcm_dmaengine.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 299b73bb2054df..889155ee3fd815 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -22,6 +22,8 @@ struct dmaengine_pcm_runtime_data { struct dma_chan *dma_chan; dma_cookie_t cookie; + struct work_struct complete_wq; /* for nonatomic PCM */ + struct snd_pcm_substream *substream; unsigned int pos; }; @@ -145,6 +147,21 @@ static void dmaengine_pcm_dma_complete(void *arg) snd_pcm_period_elapsed(substream); } +static void dmaengine_pcm_dma_complete_nonatomic(struct work_struct *wq) +{ + struct dmaengine_pcm_runtime_data *prtd = \ + container_of(wq, struct dmaengine_pcm_runtime_data, complete_wq); + struct snd_pcm_substream *substream = prtd->substream; + dmaengine_pcm_dma_complete(substream); +} + +static void dmaengine_pcm_dma_complete_nonatomic_callback(void *arg) +{ + struct snd_pcm_substream *substream = arg; + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + schedule_work(&prtd->complete_wq); +} + static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream) { struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); @@ -167,7 +184,11 @@ static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream) if (!desc) return -ENOMEM; - desc->callback = dmaengine_pcm_dma_complete; + if (substream->pcm->nonatomic) + desc->callback = dmaengine_pcm_dma_complete_nonatomic_callback; + else + desc->callback = dmaengine_pcm_dma_complete; + desc->callback_param = substream; prtd->cookie = dmaengine_submit(desc); @@ -320,6 +341,10 @@ int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, if (!prtd) return -ENOMEM; + if (substream->pcm->nonatomic) + INIT_WORK(&prtd->complete_wq, dmaengine_pcm_dma_complete_nonatomic); + + prtd->substream = substream; prtd->dma_chan = chan; substream->runtime->private_data = prtd; @@ -365,6 +390,8 @@ int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream) */ dmaengine_terminate_async(prtd->dma_chan); dmaengine_synchronize(prtd->dma_chan); + if (substream->pcm->nonatomic) + flush_work(&prtd->complete_wq); kfree(prtd); return 0; From 8173acbcba4639ef71fdef97cef1ae226dde38bb Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 12 Dec 2021 20:40:04 +0100 Subject: [PATCH 0348/1020] HID: add device IDs for Apple SPI HID devices Apple Silicon based laptop use SPI as transport for HID. Add support for SPI-based HID devices and and Apple keyboard and trackpad devices. Intel based laptops using the keyboard input driver applespi use the same HID over SPI protocol and can be supported later. This requires SPI keyboard/mouse HID types since Apple's intenal keyboards/trackpads use the same product id. Signed-off-by: Janne Grunau --- drivers/hid/hid-core.c | 3 +++ drivers/hid/hid-ids.h | 5 +++++ include/linux/hid.h | 6 +++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index de7a477d66656c..d887916ae3867d 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2249,6 +2249,9 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) case BUS_I2C: bus = "I2C"; break; + case BUS_SPI: + bus = "SPI"; + break; case BUS_VIRTUAL: bus = "VIRTUAL"; break; diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 8376fb5e2d0b40..3e118c184d06ac 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -89,6 +89,7 @@ #define USB_VENDOR_ID_APPLE 0x05ac #define BT_VENDOR_ID_APPLE 0x004c +#define SPI_VENDOR_ID_APPLE 0x05ac #define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304 #define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d #define USB_DEVICE_ID_APPLE_MAGICMOUSE2 0x0269 @@ -187,6 +188,10 @@ #define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021 0x029f #define USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT 0x8102 #define USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY 0x8302 +#define SPI_DEVICE_ID_APPLE_MACBOOK_AIR_2020 0x0281 +#define SPI_DEVICE_ID_APPLE_MACBOOK_PRO13_2020 0x0341 +#define SPI_DEVICE_ID_APPLE_MACBOOK_PRO14_2021 0x0342 +#define SPI_DEVICE_ID_APPLE_MACBOOK_PRO16_2021 0x0343 #define USB_VENDOR_ID_ASUS 0x0486 #define USB_DEVICE_ID_ASUS_T91MT 0x0185 diff --git a/include/linux/hid.h b/include/linux/hid.h index b12cb1c8e68214..5f0d9d61c09d2e 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -587,7 +587,9 @@ struct hid_input { enum hid_type { HID_TYPE_OTHER = 0, HID_TYPE_USBMOUSE, - HID_TYPE_USBNONE + HID_TYPE_USBNONE, + HID_TYPE_SPI_KEYBOARD, + HID_TYPE_SPI_MOUSE, }; enum hid_battery_status { @@ -745,6 +747,8 @@ struct hid_descriptor { .bus = BUS_BLUETOOTH, .vendor = (ven), .product = (prod) #define HID_I2C_DEVICE(ven, prod) \ .bus = BUS_I2C, .vendor = (ven), .product = (prod) +#define HID_SPI_DEVICE(ven, prod) \ + .bus = BUS_SPI, .vendor = (ven), .product = (prod) #define HID_REPORT_ID(rep) \ .report_type = (rep) From 1eece1d69aeed2c7104175f7e2e4d47c940c5f51 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 16 Dec 2021 21:15:31 +0100 Subject: [PATCH 0349/1020] HID: apple: add support for internal keyboards Apple MacBook keyboards started using HID over SPI in 2015. With the addition of the SPI HID transport they can be supported by this driver. Support all product ids over with the Apple SPI vendor id for now. Individual product ids will have to be added for a correct Fn/function key mapping. Enable by default on the Apple Arm platform. Signed-off-by: Janne Grunau --- drivers/hid/Kconfig | 2 +- drivers/hid/hid-apple.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 4c682c65070408..b170c9c0e43e89 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -129,7 +129,7 @@ config HID_APPLE tristate "Apple {i,Power,Mac}Books" depends on LEDS_CLASS depends on NEW_LEDS - default !EXPERT + default !EXPERT || SPI_HID_APPLE help Support for some Apple devices which less or more break HID specification. diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index bd022e0043569c..b1724b06bb4037 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -829,6 +829,10 @@ static int apple_probe(struct hid_device *hdev, struct apple_sc *asc; int ret; + if (id->bus == BUS_SPI && id->vendor == SPI_VENDOR_ID_APPLE && + hdev->type != HID_TYPE_SPI_KEYBOARD) + return -ENODEV; + asc = devm_kzalloc(&hdev->dev, sizeof(*asc), GFP_KERNEL); if (asc == NULL) { hid_err(hdev, "can't alloc apple descriptor\n"); @@ -1073,6 +1077,8 @@ static const struct hid_device_id apple_devices[] = { .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY }, { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021), .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, + { HID_SPI_DEVICE(SPI_VENDOR_ID_APPLE, HID_ANY_ID), + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, { } }; From f43fd034580861e944be64b276718c9ed57ee036 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 19 Dec 2021 18:08:15 +0100 Subject: [PATCH 0350/1020] HID: apple: add Fn key mapping for Apple silicon MacBooks Signed-off-by: Janne Grunau --- drivers/hid/hid-apple.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index b1724b06bb4037..5448aa42dd5d60 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -262,6 +262,28 @@ static const struct apple_key_translation apple_fn_keys[] = { { } }; +static const struct apple_key_translation apple_fn_keys_spi[] = { + { KEY_BACKSPACE, KEY_DELETE }, + { KEY_ENTER, KEY_INSERT }, + { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, + { KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY }, + { KEY_F3, KEY_SCALE, APPLE_FLAG_FKEY }, + { KEY_F4, KEY_SEARCH, APPLE_FLAG_FKEY }, + { KEY_F5, KEY_RECORD, APPLE_FLAG_FKEY }, + { KEY_F6, KEY_SLEEP, APPLE_FLAG_FKEY }, + { KEY_F7, KEY_PREVIOUSSONG, APPLE_FLAG_FKEY }, + { KEY_F8, KEY_PLAYPAUSE, APPLE_FLAG_FKEY }, + { KEY_F9, KEY_NEXTSONG, APPLE_FLAG_FKEY }, + { KEY_F10, KEY_MUTE, APPLE_FLAG_FKEY }, + { KEY_F11, KEY_VOLUMEDOWN, APPLE_FLAG_FKEY }, + { KEY_F12, KEY_VOLUMEUP, APPLE_FLAG_FKEY }, + { KEY_UP, KEY_PAGEUP }, + { KEY_DOWN, KEY_PAGEDOWN }, + { KEY_LEFT, KEY_HOME }, + { KEY_RIGHT, KEY_END }, + { } +}; + static const struct apple_key_translation powerbook_fn_keys[] = { { KEY_BACKSPACE, KEY_DELETE }, { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, @@ -477,6 +499,8 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, else if (hid->product >= USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI && hid->product <= USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS) table = macbookair_fn_keys; + else if (hid->vendor == SPI_VENDOR_ID_APPLE) + table = apple_fn_keys_spi; else if (hid->product < 0x21d || hid->product >= 0x300) table = powerbook_fn_keys; else @@ -656,6 +680,7 @@ static void apple_setup_input(struct input_dev *input) /* Enable all needed keys */ apple_setup_key_translation(input, apple_fn_keys); + apple_setup_key_translation(input, apple_fn_keys_spi); apple_setup_key_translation(input, powerbook_fn_keys); apple_setup_key_translation(input, powerbook_numlock_keys); apple_setup_key_translation(input, apple_iso_keyboard); From 930b0006c800841707161b6b96faf77e596b77d6 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 5 Jan 2022 23:27:34 +0100 Subject: [PATCH 0351/1020] HID: apple: add Fn key mapping for Macbook Pro with touchbar Signed-off-by: Janne Grunau --- drivers/hid/hid-apple.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 5448aa42dd5d60..cc406552a35d92 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -284,6 +284,28 @@ static const struct apple_key_translation apple_fn_keys_spi[] = { { } }; +static const struct apple_key_translation apple_fn_keys_mbp13[] = { + { KEY_BACKSPACE, KEY_DELETE }, + { KEY_ENTER, KEY_INSERT }, + { KEY_UP, KEY_PAGEUP }, + { KEY_DOWN, KEY_PAGEDOWN }, + { KEY_LEFT, KEY_HOME }, + { KEY_RIGHT, KEY_END }, + { KEY_1, KEY_F1 }, + { KEY_2, KEY_F2 }, + { KEY_3, KEY_F3 }, + { KEY_4, KEY_F4 }, + { KEY_5, KEY_F5 }, + { KEY_6, KEY_F6 }, + { KEY_7, KEY_F7 }, + { KEY_8, KEY_F8 }, + { KEY_9, KEY_F9 }, + { KEY_0, KEY_F10 }, + { KEY_MINUS, KEY_F11 }, + { KEY_EQUAL, KEY_F12 }, + { } +}; + static const struct apple_key_translation powerbook_fn_keys[] = { { KEY_BACKSPACE, KEY_DELETE }, { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, @@ -499,6 +521,9 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, else if (hid->product >= USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI && hid->product <= USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS) table = macbookair_fn_keys; + else if (hid->vendor == SPI_VENDOR_ID_APPLE && + hid->product == SPI_DEVICE_ID_APPLE_MACBOOK_PRO13_2020) + table = apple_fn_keys_mbp13; else if (hid->vendor == SPI_VENDOR_ID_APPLE) table = apple_fn_keys_spi; else if (hid->product < 0x21d || hid->product >= 0x300) @@ -681,6 +706,7 @@ static void apple_setup_input(struct input_dev *input) /* Enable all needed keys */ apple_setup_key_translation(input, apple_fn_keys); apple_setup_key_translation(input, apple_fn_keys_spi); + apple_setup_key_translation(input, apple_fn_keys_mbp13); apple_setup_key_translation(input, powerbook_fn_keys); apple_setup_key_translation(input, powerbook_numlock_keys); apple_setup_key_translation(input, apple_iso_keyboard); From 311d14303b38ac6a2f1858072ea28685ac35935c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 16 Dec 2021 00:10:51 +0100 Subject: [PATCH 0352/1020] HID: magicmouse: use a define of the max number of touch contacts Signed-off-by: Janne Grunau --- drivers/hid/hid-magicmouse.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index a46ff4e8b99f54..087103a78915ba 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -61,6 +61,8 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie #define DOUBLE_REPORT_ID 0xf7 #define USB_BATTERY_TIMEOUT_MS 60000 +#define MAX_CONTACTS 16 + /* These definitions are not precise, but they're close enough. (Bits * 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem * to be some kind of bit mask -- 0x20 may be a near-field reading, @@ -142,8 +144,8 @@ struct magicmouse_sc { u8 size; bool scroll_x_active; bool scroll_y_active; - } touches[16]; - int tracking_ids[16]; + } touches[MAX_CONTACTS]; + int tracking_ids[MAX_CONTACTS]; struct hid_device *hdev; struct delayed_work work; @@ -595,7 +597,7 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd __set_bit(EV_ABS, input->evbit); - error = input_mt_init_slots(input, 16, mt_flags); + error = input_mt_init_slots(input, MAX_CONTACTS, mt_flags); if (error) return error; input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2, From 490b81320eb329e996797dc7b74b8ec217e2d586 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 16 Dec 2021 00:12:35 +0100 Subject: [PATCH 0353/1020] HID: magicmouse: use struct input_mt_pos for X/Y Signed-off-by: Janne Grunau --- drivers/hid/hid-magicmouse.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 087103a78915ba..375775eacb1e67 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -134,9 +134,8 @@ struct magicmouse_sc { int scroll_accel; unsigned long scroll_jiffies; + struct input_mt_pos pos[MAX_CONTACTS]; struct { - short x; - short y; short scroll_x; short scroll_y; short scroll_x_hr; @@ -193,7 +192,7 @@ static void magicmouse_emit_buttons(struct magicmouse_sc *msc, int state) } else if (last_state != 0) { state = last_state; } else if ((id = magicmouse_firm_touch(msc)) >= 0) { - int x = msc->touches[id].x; + int x = msc->pos[id].x; if (x < middle_button_start) state = 1; else if (x > middle_button_stop) @@ -254,8 +253,8 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda /* Store tracking ID and other fields. */ msc->tracking_ids[raw_id] = id; - msc->touches[id].x = x; - msc->touches[id].y = y; + msc->pos[id].x = x; + msc->pos[id].y = y; msc->touches[id].size = size; /* If requested, emulate a scroll wheel by detecting small From 0232fb1365cbe6428fbf42e4ac15b81bbe8306eb Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 16 Dec 2021 00:15:30 +0100 Subject: [PATCH 0354/1020] HID: magicmouse: use ops function pointers for input functionality Will be used for supporting MacBook trackpads connected via SPI. Signed-off-by: Janne Grunau --- drivers/hid/hid-magicmouse.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 375775eacb1e67..fe420f6681cee6 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -113,6 +113,13 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie #define TRACKPAD2_RES_Y \ ((TRACKPAD2_MAX_Y - TRACKPAD2_MIN_Y) / (TRACKPAD2_DIMENSION_Y / 100)) + +struct magicmouse_input_ops { + int (*raw_event)(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size); + int (*setup_input)(struct input_dev *input, struct hid_device *hdev); +}; + /** * struct magicmouse_sc - Tracks Magic Mouse-specific data. * @input: Input device through which we report events. @@ -149,6 +156,7 @@ struct magicmouse_sc { struct hid_device *hdev; struct delayed_work work; struct timer_list battery_timer; + struct magicmouse_input_ops input_ops; }; static int magicmouse_firm_touch(struct magicmouse_sc *msc) @@ -378,6 +386,14 @@ static int magicmouse_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { struct magicmouse_sc *msc = hid_get_drvdata(hdev); + + return msc->input_ops.raw_event(hdev, report, data, size); +} + +static int magicmouse_raw_event_usb(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct magicmouse_sc *msc = hid_get_drvdata(hdev); struct input_dev *input = msc->input; int x = 0, y = 0, ii, clicks = 0, npoints; @@ -523,7 +539,17 @@ static int magicmouse_event(struct hid_device *hdev, struct hid_field *field, return 0; } -static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev) + +static int magicmouse_setup_input(struct input_dev *input, + struct hid_device *hdev) +{ + struct magicmouse_sc *msc = hid_get_drvdata(hdev); + + return msc->input_ops.setup_input(input, hdev); +} + +static int magicmouse_setup_input_usb(struct input_dev *input, + struct hid_device *hdev) { int error; int mt_flags = 0; @@ -810,6 +836,9 @@ static int magicmouse_probe(struct hid_device *hdev, return -ENOMEM; } + msc->input_ops.raw_event = magicmouse_raw_event_usb; + msc->input_ops.setup_input = magicmouse_setup_input_usb; + msc->scroll_accel = SCROLL_ACCEL_DEFAULT; msc->hdev = hdev; INIT_DEFERRABLE_WORK(&msc->work, magicmouse_enable_mt_work); From 6aca967ef225ba50592a9cc6683952f613eb788e Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 16 Dec 2021 01:17:48 +0100 Subject: [PATCH 0355/1020] HID: magicmouse: add support for Macbook trackpads The trackpads in Macbooks beginning in 2015 are HID devices connected over SPI. On Intel Macbooks they are currently supported by applespi.c. This chang adds support for the trackpads on Apple Silicon Macbooks starting in late 2020. They use a new HID over SPI transport driver. The touch report format differs from USB/BT Magic Trackpads. It is the same format as the type 4 format supported by bcm5974.c. Signed-off-by: Janne Grunau --- drivers/hid/Kconfig | 4 +- drivers/hid/hid-magicmouse.c | 259 ++++++++++++++++++++++++++++++++++- 2 files changed, 260 insertions(+), 3 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index b170c9c0e43e89..c5de28ce799421 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -669,11 +669,13 @@ config LOGIWHEELS_FF config HID_MAGICMOUSE tristate "Apple Magic Mouse/Trackpad multi-touch support" + default SPI_HID_APPLE help Support for the Apple Magic Mouse/Trackpad multi-touch. Say Y here if you want support for the multi-touch features of the - Apple Wireless "Magic" Mouse and the Apple Wireless "Magic" Trackpad. + Apple Wireless "Magic" Mouse, the Apple Wireless "Magic" Trackpad and + force touch Trackpads in Macbooks starting from 2015. config HID_MALTRON tristate "Maltron L90 keyboard" diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index fe420f6681cee6..780265efbf2e92 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -113,6 +113,18 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie #define TRACKPAD2_RES_Y \ ((TRACKPAD2_MAX_Y - TRACKPAD2_MIN_Y) / (TRACKPAD2_DIMENSION_Y / 100)) +#define J314_TP_DIMENSION_X (float)13000 +#define J314_TP_MIN_X -5900 +#define J314_TP_MAX_X 6500 +#define J314_TP_RES_X \ + ((J314_TP_MAX_X - J314_TP_MIN_X) / (J314_TP_DIMENSION_X / 100)) +#define J314_TP_DIMENSION_Y (float)8100 +#define J314_TP_MIN_Y -200 +#define J314_TP_MAX_Y 7400 +#define J314_TP_RES_Y \ + ((J314_TP_MAX_Y - J314_TP_MIN_Y) / (J314_TP_DIMENSION_Y / 100)) + +#define J314_TP_MAX_FINGER_ORIENTATION 16384 struct magicmouse_input_ops { int (*raw_event)(struct hid_device *hdev, @@ -522,6 +534,157 @@ static int magicmouse_raw_event_usb(struct hid_device *hdev, return 1; } +/** + * struct tp_finger - single trackpad finger structure, le16-aligned + * + * @unknown1: unknown + * @unknown2: unknown + * @abs_x: absolute x coordinate + * @abs_y: absolute y coordinate + * @rel_x: relative x coordinate + * @rel_y: relative y coordinate + * @tool_major: tool area, major axis + * @tool_minor: tool area, minor axis + * @orientation: 16384 when point, else 15 bit angle + * @touch_major: touch area, major axis + * @touch_minor: touch area, minor axis + * @unused: zeros + * @pressure: pressure on forcetouch touchpad + * @multi: one finger: varies, more fingers: constant + * @crc16: on last finger: crc over the whole message struct + * (i.e. message header + this struct) minus the last + * @crc16 field; unknown on all other fingers. + */ +struct tp_finger { + __le16 unknown1; + __le16 unknown2; + __le16 abs_x; + __le16 abs_y; + __le16 rel_x; + __le16 rel_y; + __le16 tool_major; + __le16 tool_minor; + __le16 orientation; + __le16 touch_major; + __le16 touch_minor; + __le16 unused[2]; + __le16 pressure; + __le16 multi; +} __attribute__((packed, aligned(2))); + +/** + * struct trackpad report + * + * @report_id: reportid + * @buttons: HID Usage Buttons 3 1-bit reports + * @num_fingers: the number of fingers being reported in @fingers + * @clicked: same as @buttons + */ +struct tp_header { + // HID mouse report + u8 report_id; + u8 buttons; + u8 rel_x; + u8 rel_y; + u8 padding[4]; + // HID vendor part, up to 1751 bytes + u8 unknown[22]; + u8 num_fingers; + u8 clicked; + u8 unknown3[14]; +}; + +static inline int le16_to_int(__le16 x) +{ + return (signed short)le16_to_cpu(x); +} + +static void report_finger_data(struct input_dev *input, int slot, + const struct input_mt_pos *pos, + const struct tp_finger *f) +{ + input_mt_slot(input, slot); + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); + + input_report_abs(input, ABS_MT_TOUCH_MAJOR, + le16_to_int(f->touch_major) << 1); + input_report_abs(input, ABS_MT_TOUCH_MINOR, + le16_to_int(f->touch_minor) << 1); + input_report_abs(input, ABS_MT_WIDTH_MAJOR, + le16_to_int(f->tool_major) << 1); + input_report_abs(input, ABS_MT_WIDTH_MINOR, + le16_to_int(f->tool_minor) << 1); + input_report_abs(input, ABS_MT_ORIENTATION, + J314_TP_MAX_FINGER_ORIENTATION - le16_to_int(f->orientation)); + input_report_abs(input, ABS_MT_PRESSURE, le16_to_int(f->pressure)); + input_report_abs(input, ABS_MT_POSITION_X, pos->x); + input_report_abs(input, ABS_MT_POSITION_Y, pos->y); +} + +static int magicmouse_raw_event_spi(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct magicmouse_sc *msc = hid_get_drvdata(hdev); + struct input_dev *input = msc->input; + struct tp_header *tp_hdr; + struct tp_finger *f; + int i, n; + u32 npoints; + const size_t hdr_sz = sizeof(struct tp_header); + const size_t touch_sz = sizeof(struct tp_finger); + u8 map_contacs[MAX_CONTACTS]; + + // hid_warn(hdev, "%s\n", __func__); + // print_hex_dump_debug("appleft ev: ", DUMP_PREFIX_OFFSET, 16, 1, data, + // size, false); + + if (data[0] != TRACKPAD2_USB_REPORT_ID) + return 0; + + /* Expect 46 bytes of prefix, and N * 30 bytes of touch data. */ + if (size < hdr_sz || ((size - hdr_sz) % touch_sz) != 0) + return 0; + + tp_hdr = (struct tp_header *)data; + + npoints = (size - hdr_sz) / touch_sz; + if (npoints < tp_hdr->num_fingers || npoints > MAX_CONTACTS) { + hid_warn(hdev, + "unexpected number of touches (%u) for " + "report\n", + npoints); + return 0; + } + + n = 0; + for (i = 0; i < tp_hdr->num_fingers; i++) { + f = (struct tp_finger *)(data + hdr_sz + i * touch_sz); + if (le16_to_int(f->touch_major) == 0) + continue; + + hid_dbg(hdev, "ev x:%04x y:%04x\n", le16_to_int(f->abs_x), + le16_to_int(f->abs_y)); + msc->pos[n].x = le16_to_int(f->abs_x); + msc->pos[n].y = -le16_to_int(f->abs_y); + map_contacs[n] = i; + n++; + } + + input_mt_assign_slots(input, msc->tracking_ids, msc->pos, n, 0); + + for (i = 0; i < n; i++) { + int idx = map_contacs[i]; + f = (struct tp_finger *)(data + hdr_sz + idx * touch_sz); + report_finger_data(input, msc->tracking_ids[i], &msc->pos[i], f); + } + + input_mt_sync_frame(input); + input_report_key(input, BTN_MOUSE, data[1] & 1); + + input_sync(input); + return 1; +} + static int magicmouse_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value) { @@ -701,6 +864,79 @@ static int magicmouse_setup_input_usb(struct input_dev *input, return 0; } +static int magicmouse_setup_input_spi(struct input_dev *input, + struct hid_device *hdev) +{ + int error; + int mt_flags = 0; + + __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); + __clear_bit(BTN_0, input->keybit); + __clear_bit(BTN_RIGHT, input->keybit); + __clear_bit(BTN_MIDDLE, input->keybit); + __clear_bit(EV_REL, input->evbit); + __clear_bit(REL_X, input->relbit); + __clear_bit(REL_Y, input->relbit); + + mt_flags = INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK; + + /* finger touch area */ + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 5000, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 5000, 0, 0); + + /* finger approach area */ + input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 5000, 0, 0); + input_set_abs_params(input, ABS_MT_WIDTH_MINOR, 0, 5000, 0, 0); + + /* Note: Touch Y position from the device is inverted relative + * to how pointer motion is reported (and relative to how USB + * HID recommends the coordinates work). This driver keeps + * the origin at the same position, and just uses the additive + * inverse of the reported Y. + */ + + input_set_abs_params(input, ABS_MT_PRESSURE, 0, 6000, 0, 0); + + /* + * This makes libinput recognize this as a PressurePad and + * stop trying to use pressure for touch size. Pressure unit + * seems to be ~grams on these touchpads. + */ + input_abs_set_res(input, ABS_MT_PRESSURE, 1); + + /* finger orientation */ + input_set_abs_params(input, ABS_MT_ORIENTATION, -J314_TP_MAX_FINGER_ORIENTATION, + J314_TP_MAX_FINGER_ORIENTATION, 0, 0); + + /* finger position */ + input_set_abs_params(input, ABS_MT_POSITION_X, J314_TP_MIN_X, J314_TP_MAX_X, + 0, 0); + /* Y axis is inverted */ + input_set_abs_params(input, ABS_MT_POSITION_Y, -J314_TP_MAX_Y, -J314_TP_MIN_Y, + 0, 0); + + /* X/Y resolution */ + input_abs_set_res(input, ABS_MT_POSITION_X, J314_TP_RES_X); + input_abs_set_res(input, ABS_MT_POSITION_Y, J314_TP_RES_Y); + + input_set_events_per_packet(input, 60); + + /* touchpad button */ + input_set_capability(input, EV_KEY, BTN_MOUSE); + + /* + * hid-input may mark device as using autorepeat, but the trackpad does + * not actually want it. + */ + __clear_bit(EV_REP, input->evbit); + + error = input_mt_init_slots(input, MAX_CONTACTS, mt_flags); + if (error) + return error; + + return 0; +} + static int magicmouse_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) @@ -756,6 +992,9 @@ static int magicmouse_enable_multitouch(struct hid_device *hdev) feature_size = sizeof(feature_mt_trackpad2_usb); feature = feature_mt_trackpad2_usb; } + } else if (hdev->vendor == SPI_VENDOR_ID_APPLE) { + feature_size = sizeof(feature_mt_trackpad2_usb); + feature = feature_mt_trackpad2_usb; } else if (hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) { feature_size = sizeof(feature_mt_mouse2); feature = feature_mt_mouse2; @@ -830,14 +1069,26 @@ static int magicmouse_probe(struct hid_device *hdev, struct hid_report *report; int ret; + if (id->bus == BUS_SPI && id->vendor == SPI_VENDOR_ID_APPLE && + hdev->type != HID_TYPE_SPI_MOUSE) + return -ENODEV; + msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL); if (msc == NULL) { hid_err(hdev, "can't alloc magicmouse descriptor\n"); return -ENOMEM; } - msc->input_ops.raw_event = magicmouse_raw_event_usb; - msc->input_ops.setup_input = magicmouse_setup_input_usb; + // internal trackpad use a data format use input ops to avoid + // conflicts with the report ID. + if (id->vendor == SPI_VENDOR_ID_APPLE) { + msc->input_ops.raw_event = magicmouse_raw_event_spi; + msc->input_ops.setup_input = magicmouse_setup_input_spi; + + } else { + msc->input_ops.raw_event = magicmouse_raw_event_usb; + msc->input_ops.setup_input = magicmouse_setup_input_usb; + } msc->scroll_accel = SCROLL_ACCEL_DEFAULT; msc->hdev = hdev; @@ -887,6 +1138,8 @@ static int magicmouse_probe(struct hid_device *hdev, else /* USB_VENDOR_ID_APPLE */ report = hid_register_report(hdev, HID_INPUT_REPORT, TRACKPAD2_USB_REPORT_ID, 0); + } else if (id->vendor == SPI_VENDOR_ID_APPLE) { + report = hid_register_report(hdev, HID_INPUT_REPORT, 2, 0); } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ report = hid_register_report(hdev, HID_INPUT_REPORT, TRACKPAD_REPORT_ID, 0); @@ -981,6 +1234,8 @@ static const struct hid_device_id magic_mice[] = { USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 }, + { HID_SPI_DEVICE(SPI_VENDOR_ID_APPLE, HID_ANY_ID), + .driver_data = 0 }, { } }; MODULE_DEVICE_TABLE(hid, magic_mice); From 5426aca0bfcc41c1a6dce25e914d1b61afbd4711 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Fri, 10 Dec 2021 19:38:43 +0100 Subject: [PATCH 0356/1020] WIP: HID: transport: spi: add Apple SPI transport Keyboard and trackpad of Apple Sillicon SoCs (M1, M1 Pro/Max) laptops are are HID devices connected via SPI. This is the same protocol as implemented by applespi.c. It was not noticed that protocol is a transport for HID. Adding support for ACPI based Intel MacBooks will be done in a separate commit. How HID is mapped in this protocol is not yet fully understood. Microsoft has a specification for HID over SPI [1] incompatible with the transport protocol used by Apple. [1] https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/hid-over-spi Signed-off-by: Janne Grunau --- drivers/hid/Kconfig | 2 + drivers/hid/Makefile | 2 + drivers/hid/spi-hid/Kconfig | 26 + drivers/hid/spi-hid/Makefile | 10 + drivers/hid/spi-hid/spi-hid-apple-core.c | 1030 ++++++++++++++++++++++ drivers/hid/spi-hid/spi-hid-apple-of.c | 136 +++ drivers/hid/spi-hid/spi-hid-apple.h | 31 + 7 files changed, 1237 insertions(+) create mode 100644 drivers/hid/spi-hid/Kconfig create mode 100644 drivers/hid/spi-hid/Makefile create mode 100644 drivers/hid/spi-hid/spi-hid-apple-core.c create mode 100644 drivers/hid/spi-hid/spi-hid-apple-of.c create mode 100644 drivers/hid/spi-hid/spi-hid-apple.h diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index c5de28ce799421..2203f6abed9383 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1353,4 +1353,6 @@ source "drivers/hid/amd-sfh-hid/Kconfig" source "drivers/hid/surface-hid/Kconfig" +source "drivers/hid/spi-hid/Kconfig" + endif # HID_SUPPORT diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 082a728eac6004..2f150d0777e6e5 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -170,3 +170,5 @@ obj-$(INTEL_ISH_FIRMWARE_DOWNLOADER) += intel-ish-hid/ obj-$(CONFIG_AMD_SFH_HID) += amd-sfh-hid/ obj-$(CONFIG_SURFACE_HID_CORE) += surface-hid/ + +obj-$(CONFIG_SPI_HID_APPLE_CORE) += spi-hid/ diff --git a/drivers/hid/spi-hid/Kconfig b/drivers/hid/spi-hid/Kconfig new file mode 100644 index 00000000000000..8e37f0fec28ac9 --- /dev/null +++ b/drivers/hid/spi-hid/Kconfig @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0-only +menu "SPI HID support" + depends on SPI + +config SPI_HID_APPLE_OF + tristate "HID over SPI transport layer for Apple Silicon SoCs" + default ARCH_APPLE + depends on SPI && INPUT && OF + help + Say Y here if you use Apple Silicon based laptop. The keyboard and + touchpad are HID based devices connected via SPI. + + If unsure, say N. + + This support is also available as a module. If so, the module + will be called spi-hid-apple-of. It will also build/depend on the + module spi-hid-apple. + +endmenu + +config SPI_HID_APPLE_CORE + tristate + default y if SPI_HID_APPLE_OF=y + default m if SPI_HID_APPLE_OF=m + select HID + select CRC16 diff --git a/drivers/hid/spi-hid/Makefile b/drivers/hid/spi-hid/Makefile new file mode 100644 index 00000000000000..f276ee12cb94fc --- /dev/null +++ b/drivers/hid/spi-hid/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for SPI HID tarnsport drivers +# + +obj-$(CONFIG_SPI_HID_APPLE_CORE) += spi-hid-apple.o + +spi-hid-apple-objs = spi-hid-apple-core.o + +obj-$(CONFIG_SPI_HID_APPLE_OF) += spi-hid-apple-of.o diff --git a/drivers/hid/spi-hid/spi-hid-apple-core.c b/drivers/hid/spi-hid/spi-hid-apple-core.c new file mode 100644 index 00000000000000..cd018df4f38715 --- /dev/null +++ b/drivers/hid/spi-hid/spi-hid-apple-core.c @@ -0,0 +1,1030 @@ +/* + * SPDX-License-Identifier: GPL-2.0 + * + * Apple SPI HID transport driver + * + * Copyright (C) The Asahi Linux Contributors + * + * Based on: drivers/input/applespi.c + * + * MacBook (Pro) SPI keyboard and touchpad driver + * + * Copyright (c) 2015-2018 Federico Lorenzi + * Copyright (c) 2017-2018 Ronald Tschalär + * + */ + +//#define DEBUG 2 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spi-hid-apple.h" + +#define SPIHID_DEF_WAIT msecs_to_jiffies(1000) + +#define SPIHID_MAX_INPUT_REPORT_SIZE 0x800 + +/* support only keyboard, trackpad and management dev for now */ +#define SPIHID_MAX_DEVICES 3 + +#define SPIHID_DEVICE_ID_MNGT 0x0 +#define SPIHID_DEVICE_ID_KBD 0x1 +#define SPIHID_DEVICE_ID_TP 0x2 +#define SPIHID_DEVICE_ID_INFO 0xd0 + +#define SPIHID_READ_PACKET 0x20 +#define SPIHID_WRITE_PACKET 0x40 + +#define SPIHID_DESC_MAX 512 + +#define SPIHID_SET_LEDS 0x0151 /* caps lock */ + +#define SPI_RW_CHG_DELAY_US 200 /* 'Inter Stage Us'? */ + +static const u8 spi_hid_apple_booted[4] = { 0xa0, 0x80, 0x00, 0x00 }; +static const u8 spi_hid_apple_status_ok[4] = { 0xac, 0x27, 0x68, 0xd5 }; + +struct spihid_interface { + struct hid_device *hid; + u8 *hid_desc; + u32 hid_desc_len; + u32 id; + unsigned country; + u32 max_control_report_len; + u32 max_input_report_len; + u32 max_output_report_len; + u8 name[32]; + bool ready; +}; + +struct spihid_input_report { + u8 *buf; + u32 length; + u32 offset; + u8 device; + u8 flags; +}; + +struct spihid_apple { + struct spi_device *spidev; + + struct spihid_apple_ops *ops; + + struct spihid_interface mngt; + struct spihid_interface kbd; + struct spihid_interface tp; + + wait_queue_head_t wait; + struct mutex tx_lock; //< protects against concurrent SPI writes + + struct spi_message rx_msg; + struct spi_message tx_msg; + struct spi_transfer rx_transfer; + struct spi_transfer tx_transfer; + struct spi_transfer status_transfer; + + u8 *rx_buf; + u8 *tx_buf; + u8 *status_buf; + + u8 vendor[32]; + u8 product[64]; + u8 serial[32]; + + u32 num_devices; + + u32 vendor_id; + u32 product_id; + u32 version_number; + + u8 msg_id; + + /* fragmented HID report */ + struct spihid_input_report report; + + /* state tracking flags */ + bool status_booted; +}; + +/** + * struct spihid_msg_hdr - common header of protocol messages. + * + * Each message begins with fixed header, followed by a message-type specific + * payload, and ends with a 16-bit crc. Because of the varying lengths of the + * payload, the crc is defined at the end of each payload struct, rather than + * in this struct. + * + * @unknown0: request type? output, input (0x10), feature, protocol + * @unknown1: maybe report id? + * @unknown2: mostly zero, in info request maybe device num + * @msgid: incremented on each message, rolls over after 255; there is a + * separate counter for each message type. + * @rsplen: response length (the exact nature of this field is quite + * speculative). On a request/write this is often the same as + * @length, though in some cases it has been seen to be much larger + * (e.g. 0x400); on a response/read this the same as on the + * request; for reads that are not responses it is 0. + * @length: length of the remainder of the data in the whole message + * structure (after re-assembly in case of being split over + * multiple spi-packets), minus the trailing crc. The total size + * of a message is therefore @length + 10. + */ + +struct spihid_msg_hdr { + u8 unknown0; + u8 unknown1; + u8 unknown2; + u8 id; + __le16 rsplen; + __le16 length; +}; + +/** + * struct spihid_transfer_packet - a complete spi packet; always 256 bytes. This carries + * the (parts of the) message in the data. But note that this does not + * necessarily contain a complete message, as in some cases (e.g. many + * fingers pressed) the message is split over multiple packets (see the + * @offset, @remain, and @length fields). In general the data parts in + * spihid_transfer_packet's are concatenated until @remaining is 0, and the + * result is an message. + * + * @flags: 0x40 = write (to device), 0x20 = read (from device); note that + * the response to a write still has 0x40. + * @device: 1 = keyboard, 2 = touchpad + * @offset: specifies the offset of this packet's data in the complete + * message; i.e. > 0 indicates this is a continuation packet (in + * the second packet for a message split over multiple packets + * this would then be the same as the @length in the first packet) + * @remain: number of message bytes remaining in subsequents packets (in + * the first packet of a message split over two packets this would + * then be the same as the @length in the second packet) + * @length: length of the valid data in the @data in this packet + * @data: all or part of a message + * @crc16: crc over this whole structure minus this @crc16 field. This + * covers just this packet, even on multi-packet messages (in + * contrast to the crc in the message). + */ +struct spihid_transfer_packet { + u8 flags; + u8 device; + __le16 offset; + __le16 remain; + __le16 length; + u8 data[246]; + __le16 crc16; +}; + +/* + * how HID is mapped onto the protocol is not fully clear. This are the known + * reports/request: + * + * pkt.flags pkt.dev? msg.u0 msg.u1 msg.u2 + * info 0x40 0xd0 0x20 0x01 0xd0 + * + * info mngt: 0x40 0xd0 0x20 0x10 0x00 + * info kbd: 0x40 0xd0 0x20 0x10 0x01 + * info tp: 0x40 0xd0 0x20 0x10 0x02 + * + * desc kbd: 0x40 0xd0 0x20 0x10 0x01 + * desc trackpad: 0x40 0xd0 0x20 0x10 0x02 + * + * mt mode: 0x40 0x02 0x52 0x02 0x00 set protocol? + * capslock led 0x40 0x01 0x51 0x01 0x00 output report + * + * report kbd: 0x20 0x01 0x10 0x01 0x00 input report + * report tp: 0x20 0x02 0x10 0x02 0x00 input report + * + */ + + +static int spihid_apple_request(struct spihid_apple *spihid, u8 target, u8 unk0, + u8 unk1, u8 unk2, u16 resp_len, u8 *buf, + size_t len) +{ + struct spihid_transfer_packet *pkt; + struct spihid_msg_hdr *hdr; + u16 crc; + int err; + + /* know reports are small enoug to fit in a single packet */ + if (len > sizeof(pkt->data) - sizeof(*hdr) - sizeof(__le16)) + return -EINVAL; + + err = mutex_lock_interruptible(&spihid->tx_lock); + if (err < 0) + return err; + + pkt = (struct spihid_transfer_packet *)spihid->tx_buf; + + memset(pkt, 0, sizeof(*pkt)); + pkt->flags = SPIHID_WRITE_PACKET; + pkt->device = target; + pkt->length = cpu_to_le16(sizeof(*hdr) + len + sizeof(__le16)); + + hdr = (struct spihid_msg_hdr *)&pkt->data[0]; + hdr->unknown0 = unk0; + hdr->unknown1 = unk1; + hdr->unknown2 = unk2; + hdr->id = spihid->msg_id++; + hdr->rsplen = cpu_to_le16(resp_len); + hdr->length = cpu_to_le16(len); + + if (len) + memcpy(pkt->data + sizeof(*hdr), buf, len); + crc = crc16(0, &pkt->data[0], sizeof(*hdr) + len); + put_unaligned_le16(crc, pkt->data + sizeof(*hdr) + len); + + pkt->crc16 = cpu_to_le16(crc16(0, spihid->tx_buf, + offsetof(struct spihid_transfer_packet, crc16))); + + err = spi_sync(spihid->spidev, &spihid->tx_msg); + mutex_unlock(&spihid->tx_lock); + if (err < 0) + return err; + + return (int)len; +} + +static struct spihid_apple *spihid_get_data(struct spihid_interface *idev) +{ + switch (idev->id) { + case SPIHID_DEVICE_ID_KBD: + return container_of(idev, struct spihid_apple, kbd); + case SPIHID_DEVICE_ID_TP: + return container_of(idev, struct spihid_apple, tp); + default: + return NULL; + } +} + +static int apple_ll_start(struct hid_device *hdev) +{ + /* no-op SPI transport is already setup */ + return 0; +}; + +static void apple_ll_stop(struct hid_device *hdev) +{ + /* no-op, devices will be desstroyed on driver destruction */ +} + +static int apple_ll_open(struct hid_device *hdev) +{ + struct spihid_apple *spihid; + struct spihid_interface *idev = hdev->driver_data; + + if (idev->hid_desc_len == 0) { + spihid = spihid_get_data(idev); + dev_warn(&spihid->spidev->dev, + "HID descriptor missing for dev %u", idev->id); + } else + idev->ready = true; + + return 0; +} + +static void apple_ll_close(struct hid_device *hdev) +{ + struct spihid_interface *idev = hdev->driver_data; + idev->ready = false; +} + +static int apple_ll_parse(struct hid_device *hdev) +{ + struct spihid_interface *idev = hdev->driver_data; + + return hid_parse_report(hdev, idev->hid_desc, idev->hid_desc_len); +} + +static int apple_ll_raw_request(struct hid_device *hdev, + unsigned char reportnum, __u8 *buf, size_t len, + unsigned char rtype, int reqtype) +{ + struct spihid_interface *idev = hdev->driver_data; + struct spihid_apple *spihid = spihid_get_data(idev); + + dev_dbg(&spihid->spidev->dev, + "apple_ll_raw_request: device:%u reportnum:%hhu rtype:%hhu", + idev->id, reportnum, rtype); + + switch (reqtype) { + case HID_REQ_GET_REPORT: + return -EINVAL; // spihid_get_raw_report(); + case HID_REQ_SET_REPORT: + if (buf[0] != reportnum) + return -EINVAL; + if (reportnum != idev->id) { + dev_warn(&spihid->spidev->dev, + "device:%u reportnum:" + "%hhu mismatch", + idev->id, reportnum); + return -EINVAL; + } + return spihid_apple_request(spihid, idev->id, 0x52, reportnum, 0x00, 2, buf, len); + default: + return -EIO; + } +} + +static int apple_ll_output_report(struct hid_device *hdev, __u8 *buf, + size_t len) +{ + struct spihid_interface *idev = hdev->driver_data; + struct spihid_apple *spihid = spihid_get_data(idev); + if (!spihid) + return -1; + + dev_dbg(&spihid->spidev->dev, + "apple_ll_output_report: device:%u len:%zu:", + idev->id, len); + // second idev->id should maybe be buf[0]? + return spihid_apple_request(spihid, idev->id, 0x51, idev->id, 0x00, 0, buf, len); +} + +static struct hid_ll_driver apple_hid_ll = { + .start = &apple_ll_start, + .stop = &apple_ll_stop, + .open = &apple_ll_open, + .close = &apple_ll_close, + .parse = &apple_ll_parse, + .raw_request = &apple_ll_raw_request, + .output_report = &apple_ll_output_report, +}; + +static struct spihid_interface *spihid_get_iface(struct spihid_apple *spihid, + u32 iface) +{ + switch (iface) { + case SPIHID_DEVICE_ID_MNGT: + return &spihid->mngt; + case SPIHID_DEVICE_ID_KBD: + return &spihid->kbd; + case SPIHID_DEVICE_ID_TP: + return &spihid->tp; + default: + return NULL; + } +} + +static int spihid_verify_msg(struct spihid_apple *spihid, u8 *buf, size_t len) +{ + u16 msg_crc, crc; + struct device *dev = &spihid->spidev->dev; + + crc = crc16(0, buf, len - sizeof(__le16)); + msg_crc = get_unaligned_le16(buf + len - sizeof(__le16)); + if (crc != msg_crc) { + dev_warn_ratelimited(dev, "Read message crc mismatch\n"); + return 0; + } + return 1; +} + +static bool spihid_status_report(struct spihid_apple *spihid, u8 *pl, + size_t len) +{ + struct device *dev = &spihid->spidev->dev; + dev_dbg(dev, "%s: len: %zu", __func__, len); + if (len == 5 && pl[0] == 0xe0) + return true; + + return false; +} + +static bool spihid_process_input_report(struct spihid_apple *spihid, u32 device, + struct spihid_msg_hdr *hdr, u8 *payload, + size_t len) +{ + //dev_dbg(&spihid>spidev->dev, "input report: req:%hx iface:%u ", hdr->unknown0, device); + if (hdr->unknown0 != 0x10) + return false; + + /* HID device as well but Vendor usage only, handle it internally for now */ + if (device == 0) { + if (hdr->unknown1 == 0xe0) { + return spihid_status_report(spihid, payload, len); + } + } else if (device < SPIHID_MAX_DEVICES) { + struct spihid_interface *iface = + spihid_get_iface(spihid, device); + if (iface && iface->hid && iface->ready) { + hid_input_report(iface->hid, HID_INPUT_REPORT, payload, + len, 1); + return true; + } + } else + dev_dbg(&spihid->spidev->dev, + "unexpected iface:%u for input report", device); + + return false; +} + +struct spihid_device_info { + __le16 u0[2]; + __le16 num_devices; + __le16 vendor_id; + __le16 product_id; + __le16 version_number; + __le16 vendor_str[2]; //< offset and string length + __le16 product_str[2]; //< offset and string length + __le16 serial_str[2]; //< offset and string length +}; + +static bool spihid_process_device_info(struct spihid_apple *spihid, u32 iface, + u8 *payload, size_t len) +{ + struct device *dev = &spihid->spidev->dev; + + if (iface != SPIHID_DEVICE_ID_INFO) + return false; + + if (spihid->vendor_id == 0 && + len >= sizeof(struct spihid_device_info)) { + struct spihid_device_info *info = + (struct spihid_device_info *)payload; + u16 voff, vlen, poff, plen, soff, slen; + u32 num_devices; + + num_devices = __le16_to_cpu(info->num_devices); + + if (num_devices < SPIHID_MAX_DEVICES) { + dev_err(dev, + "Device info reports %u devices, expecting at least 3", + num_devices); + return false; + } + spihid->num_devices = num_devices; + + if (spihid->num_devices > SPIHID_MAX_DEVICES) { + dev_info( + dev, + "limiting the number of devices to mngt, kbd and mouse"); + spihid->num_devices = SPIHID_MAX_DEVICES; + } + + spihid->vendor_id = __le16_to_cpu(info->vendor_id); + spihid->product_id = __le16_to_cpu(info->product_id); + spihid->version_number = __le16_to_cpu(info->version_number); + + voff = __le16_to_cpu(info->vendor_str[0]); + vlen = __le16_to_cpu(info->vendor_str[1]); + + if (voff < len && vlen <= len - voff && + vlen < sizeof(spihid->vendor)) { + memcpy(spihid->vendor, payload + voff, vlen); + spihid->vendor[vlen] = '\0'; + } + + poff = __le16_to_cpu(info->product_str[0]); + plen = __le16_to_cpu(info->product_str[1]); + + if (poff < len && plen <= len - poff && + plen < sizeof(spihid->product)) { + memcpy(spihid->product, payload + poff, plen); + spihid->product[plen] = '\0'; + } + + soff = __le16_to_cpu(info->serial_str[0]); + slen = __le16_to_cpu(info->serial_str[1]); + + if (soff < len && slen <= len - soff && + slen < sizeof(spihid->serial)) { + memcpy(spihid->vendor, payload + soff, slen); + spihid->serial[slen] = '\0'; + } + + wake_up_interruptible(&spihid->wait); + } + return true; +} + +struct spihid_iface_info { + u8 u_0; + u8 interface_num; + u8 u_2; + u8 u_3; + u8 u_4; + u8 country_code; + __le16 max_input_report_len; + __le16 max_output_report_len; + __le16 max_control_report_len; + __le16 name_offset; + __le16 name_length; +}; + +static bool spihid_process_iface_info(struct spihid_apple *spihid, u32 num, + u8 *payload, size_t len) +{ + struct spihid_iface_info *info; + struct spihid_interface *iface = spihid_get_iface(spihid, num); + u32 name_off, name_len; + + if (!iface) + return false; + + if (!iface->max_input_report_len) { + if (len < sizeof(*info)) + return false; + + info = (struct spihid_iface_info *)payload; + + iface->max_input_report_len = + le16_to_cpu(info->max_input_report_len); + iface->max_output_report_len = + le16_to_cpu(info->max_output_report_len); + iface->max_control_report_len = + le16_to_cpu(info->max_control_report_len); + iface->country = info->country_code; + + name_off = le16_to_cpu(info->name_offset); + name_len = le16_to_cpu(info->name_length); + + if (name_off < len && name_len <= len - name_off && + name_len < sizeof(iface->name)) { + memcpy(iface->name, payload + name_off, name_len); + iface->name[name_len] = '\0'; + } + + dev_dbg(&spihid->spidev->dev, "Info for %s, country code: 0x%x", + iface->name, iface->country); + + wake_up_interruptible(&spihid->wait); + } + + return true; +} + +static int spihid_register_hid_device(struct spihid_apple *spihid, + struct spihid_interface *idev, u8 device); + +static bool spihid_process_iface_hid_report_desc(struct spihid_apple *spihid, + u32 num, u8 *payload, + size_t len) +{ + struct spihid_interface *iface = spihid_get_iface(spihid, num); + + if (!iface) + return false; + + if (iface->hid_desc_len == 0) { + if (len > SPIHID_DESC_MAX) + return false; + memcpy(iface->hid_desc, payload, len); + iface->hid_desc_len = len; + + /* do not register the mngt iface as HID device */ + if (num > 0) + spihid_register_hid_device(spihid, iface, num); + + wake_up_interruptible(&spihid->wait); + } + return true; +} + +static bool spihid_process_response(struct spihid_apple *spihid, + struct spihid_msg_hdr *hdr, u8 *payload, + size_t len) +{ + if (hdr->unknown0 == 0x20) { + switch (hdr->unknown1) { + case 0x01: + return spihid_process_device_info(spihid, hdr->unknown2, + payload, len); + case 0x02: + return spihid_process_iface_info(spihid, hdr->unknown2, + payload, len); + case 0x10: + return spihid_process_iface_hid_report_desc( + spihid, hdr->unknown2, payload, len); + default: + break; + } + } + + return false; +} + +static void spihid_process_message(struct spihid_apple *spihid, u8 *data, + size_t length, u8 device, u8 flags) +{ + struct device *dev = &spihid->spidev->dev; + struct spihid_msg_hdr *hdr; + bool handled = false; + u8 *payload; + + if (!spihid_verify_msg(spihid, data, length)) + return; + + hdr = (struct spihid_msg_hdr *)data; + + if (hdr->length == 0) + return; + + payload = data + sizeof(struct spihid_msg_hdr); + + switch (flags) { + case SPIHID_READ_PACKET: + handled = spihid_process_input_report(spihid, device, hdr, + payload, le16_to_cpu(hdr->length)); + break; + case SPIHID_WRITE_PACKET: + handled = spihid_process_response(spihid, hdr, payload, + le16_to_cpu(hdr->length)); + break; + default: + break; + } + +#if defined(DEBUG) && DEBUG > 1 + { + dev_dbg(dev, + "R msg: req:%02hhx rep:%02hhx dev:%02hhx id:%hu len:%hu\n", + hdr->unknown0, hdr->unknown1, hdr->unknown2, hdr->id, + hdr->length); + print_hex_dump_debug("spihid msg: ", DUMP_PREFIX_OFFSET, 16, 1, + payload, le16_to_cpu(hdr->length), true); + } +#else + if (!handled) { + dev_dbg(dev, + "R unhandled msg: req:%02hhx rep:%02hhx dev:%02hhx id:%hu len:%hu\n", + hdr->unknown0, hdr->unknown1, hdr->unknown2, hdr->id, + hdr->length); + print_hex_dump_debug("spihid msg: ", DUMP_PREFIX_OFFSET, 16, 1, + payload, le16_to_cpu(hdr->length), true); + } +#endif +} + +static void spihid_assemble_message(struct spihid_apple *spihid, + struct spihid_transfer_packet *pkt) +{ + size_t length, offset, remain; + struct device *dev = &spihid->spidev->dev; + struct spihid_input_report *rep = &spihid->report; + + length = le16_to_cpu(pkt->length); + remain = le16_to_cpu(pkt->remain); + offset = le16_to_cpu(pkt->offset); + + if (offset + length + remain > U16_MAX) { + return; + } + + if (pkt->device != rep->device || pkt->flags != rep->flags || + offset != rep->offset) { + rep->device = 0; + rep->flags = 0; + rep->offset = 0; + rep->length = 0; + } + + if (offset == 0) { + if (rep->offset != 0) { + dev_warn(dev, "incomplete report off:%u len:%u", + rep->offset, rep->length); + } + memcpy(rep->buf, pkt->data, length); + rep->offset = length; + rep->length = length + remain; + rep->device = pkt->device; + rep->flags = pkt->flags; + } else if (offset == rep->offset) { + if (offset + length + remain != rep->length) { + dev_warn(dev, "incomplete report off:%u len:%u", + rep->offset, rep->length); + return; + } + memcpy(rep->buf + offset, pkt->data, length); + rep->offset += length; + + if (rep->offset == rep->length) { + spihid_process_message(spihid, rep->buf, rep->length, + rep->device, rep->flags); + rep->device = 0; + rep->flags = 0; + rep->offset = 0; + rep->length = 0; + } + } +} + +static void spihid_process_read(struct spihid_apple *spihid) +{ + u16 crc; + size_t length; + struct device *dev = &spihid->spidev->dev; + struct spihid_transfer_packet *pkt; + + pkt = (struct spihid_transfer_packet *)spihid->rx_buf; + + /* check transfer packet crc */ + crc = crc16(0, spihid->rx_buf, + offsetof(struct spihid_transfer_packet, crc16)); + if (crc != le16_to_cpu(pkt->crc16)) { + dev_warn_ratelimited(dev, "Read package crc mismatch\n"); + return; + } + + length = le16_to_cpu(pkt->length); + + if (length < sizeof(struct spihid_msg_hdr) + 2) { + if (length == sizeof(spi_hid_apple_booted) && + !memcmp(pkt->data, spi_hid_apple_booted, length)) { + if (!spihid->status_booted) { + spihid->status_booted = true; + wake_up_interruptible(&spihid->wait); + } + } else { + dev_info(dev, "R short packet: len:%zu\n", length); + print_hex_dump(KERN_INFO, "spihid pkt:", + DUMP_PREFIX_OFFSET, 16, 1, pkt->data, + length, false); + } + return; + } + +#if defined(DEBUG) && DEBUG > 1 + dev_dbg(dev, + "R pkt: flags:%02hhx dev:%02hhx off:%hu remain:%hu, len:%zu\n", + pkt->flags, pkt->device, pkt->offset, pkt->remain, length); +#if defined(DEBUG) && DEBUG > 2 + print_hex_dump_debug("spihid pkt: ", DUMP_PREFIX_OFFSET, 16, 1, + spihid->rx_buf, + sizeof(struct spihid_transfer_packet), true); +#endif +#endif + + if (length > sizeof(pkt->data)) { + dev_warn_ratelimited(dev, "Invalid pkt len:%zu", length); + return; + } + + /* short message */ + if (pkt->offset == 0 && pkt->remain == 0) { + spihid_process_message(spihid, pkt->data, length, pkt->device, + pkt->flags); + } else { + spihid_assemble_message(spihid, pkt); + } +} + +static void spihid_read_packet_sync(struct spihid_apple *spihid) +{ + int err; + + err = spi_sync(spihid->spidev, &spihid->rx_msg); + if (!err) { + spihid_process_read(spihid); + } else { + dev_warn(&spihid->spidev->dev, "RX failed: %d\n", err); + } +} + +irqreturn_t spihid_apple_core_irq(int irq, void *data) +{ + struct spi_device *spi = data; + struct spihid_apple *spihid = spi_get_drvdata(spi); + + spihid_read_packet_sync(spihid); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(spihid_apple_core_irq); + +static void spihid_apple_setup_spi_msgs(struct spihid_apple *spihid) +{ + memset(&spihid->rx_transfer, 0, sizeof(spihid->rx_transfer)); + + spihid->rx_transfer.rx_buf = spihid->rx_buf; + spihid->rx_transfer.len = sizeof(struct spihid_transfer_packet); + + spi_message_init(&spihid->rx_msg); + spi_message_add_tail(&spihid->rx_transfer, &spihid->rx_msg); + + memset(&spihid->tx_transfer, 0, sizeof(spihid->rx_transfer)); + memset(&spihid->status_transfer, 0, sizeof(spihid->status_transfer)); + + spihid->tx_transfer.tx_buf = spihid->tx_buf; + spihid->tx_transfer.len = sizeof(struct spihid_transfer_packet); + spihid->tx_transfer.delay.unit = SPI_DELAY_UNIT_USECS; + spihid->tx_transfer.delay.value = SPI_RW_CHG_DELAY_US; + + spihid->status_transfer.rx_buf = spihid->status_buf; + spihid->status_transfer.len = sizeof(spi_hid_apple_status_ok); + + spi_message_init(&spihid->tx_msg); + spi_message_add_tail(&spihid->tx_transfer, &spihid->tx_msg); + spi_message_add_tail(&spihid->status_transfer, &spihid->tx_msg); +} + +static int spihid_apple_setup_spi(struct spihid_apple *spihid) +{ + spihid_apple_setup_spi_msgs(spihid); + + return spihid->ops->power_on(spihid->ops); +} + +static int spihid_register_hid_device(struct spihid_apple *spihid, + struct spihid_interface *iface, u8 device) +{ + int ret; + struct hid_device *hid; + + iface->id = device; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) + return PTR_ERR(hid); + + strscpy(hid->name, spihid->product, sizeof(hid->name)); + snprintf(hid->phys, sizeof(hid->phys), "%s (%hhx)", + dev_name(&spihid->spidev->dev), device); + strscpy(hid->uniq, spihid->serial, sizeof(hid->uniq)); + + hid->ll_driver = &apple_hid_ll; + hid->bus = BUS_SPI; + hid->vendor = spihid->vendor_id; + hid->product = spihid->product_id; + hid->version = spihid->version_number; + + if (device == SPIHID_DEVICE_ID_KBD) + hid->type = HID_TYPE_SPI_KEYBOARD; + else if (device == SPIHID_DEVICE_ID_TP) + hid->type = HID_TYPE_SPI_MOUSE; + + hid->country = iface->country; + hid->dev.parent = &spihid->spidev->dev; + hid->driver_data = iface; + + ret = hid_add_device(hid); + if (ret < 0) { + hid_destroy_device(hid); + dev_warn(&spihid->spidev->dev, + "Failed to register hid device %hhu", device); + return ret; + } + + iface->hid = hid; + + return 0; +} + +static void spihid_destroy_hid_device(struct spihid_interface *iface) +{ + if (iface->hid) { + hid_destroy_device(iface->hid); + iface->hid = NULL; + } + iface->ready = false; +} + +int spihid_apple_core_probe(struct spi_device *spi, struct spihid_apple_ops *ops) +{ + struct device *dev = &spi->dev; + struct spihid_apple *spihid; + int err, i; + + if (!ops || !ops->power_on || !ops->power_off || !ops->enable_irq || !ops->disable_irq) + return -EINVAL; + + spihid = devm_kzalloc(dev, sizeof(*spihid), GFP_KERNEL); + if (!spihid) + return -ENOMEM; + + spihid->ops = ops; + spihid->spidev = spi; + + // init spi + spi_set_drvdata(spi, spihid); + + /* allocate SPI buffers */ + spihid->rx_buf = devm_kmalloc( + &spi->dev, sizeof(struct spihid_transfer_packet), GFP_KERNEL); + spihid->tx_buf = devm_kmalloc( + &spi->dev, sizeof(struct spihid_transfer_packet), GFP_KERNEL); + spihid->status_buf = devm_kmalloc( + &spi->dev, sizeof(spi_hid_apple_status_ok), GFP_KERNEL); + + if (!spihid->rx_buf || !spihid->tx_buf || !spihid->status_buf) + return -ENOMEM; + + spihid->report.buf = + devm_kmalloc(dev, SPIHID_MAX_INPUT_REPORT_SIZE, GFP_KERNEL); + + spihid->kbd.hid_desc = devm_kmalloc(dev, SPIHID_DESC_MAX, GFP_KERNEL); + spihid->tp.hid_desc = devm_kmalloc(dev, SPIHID_DESC_MAX, GFP_KERNEL); + + if (!spihid->report.buf || !spihid->kbd.hid_desc || + !spihid->tp.hid_desc) + return -ENOMEM; + + init_waitqueue_head(&spihid->wait); + + mutex_init(&spihid->tx_lock); + + /* Init spi transfer buffers and power device on */ + err = spihid_apple_setup_spi(spihid); + if (err < 0) + goto error; + + /* enable HID irq */ + spihid->ops->enable_irq(spihid->ops); + + // wait for boot message + err = wait_event_interruptible_timeout(spihid->wait, + spihid->status_booted, + msecs_to_jiffies(1000)); + if (err == 0) + err = -ENODEV; + if (err < 0) { + dev_err(dev, "waiting for device boot failed: %d", err); + goto error; + } + + /* request device information */ + dev_dbg(dev, "request device info"); + spihid_apple_request(spihid, 0xd0, 0x20, 0x01, 0xd0, 0, NULL, 0); + err = wait_event_interruptible_timeout(spihid->wait, spihid->vendor_id, + SPIHID_DEF_WAIT); + if (err == 0) + err = -ENODEV; + if (err < 0) { + dev_err(dev, "waiting for device info failed: %d", err); + goto error; + } + + /* request interface information */ + for (i = 0; i < spihid->num_devices; i++) { + struct spihid_interface *iface = spihid_get_iface(spihid, i); + if (!iface) + continue; + dev_dbg(dev, "request interface info 0x%02x", i); + spihid_apple_request(spihid, 0xd0, 0x20, 0x02, i, + SPIHID_DESC_MAX, NULL, 0); + err = wait_event_interruptible_timeout( + spihid->wait, iface->max_input_report_len, + SPIHID_DEF_WAIT); + } + + /* request HID report descriptors */ + for (i = 1; i < spihid->num_devices; i++) { + struct spihid_interface *iface = spihid_get_iface(spihid, i); + if (!iface) + continue; + dev_dbg(dev, "request hid report desc 0x%02x", i); + spihid_apple_request(spihid, 0xd0, 0x20, 0x10, i, + SPIHID_DESC_MAX, NULL, 0); + wait_event_interruptible_timeout( + spihid->wait, iface->hid_desc_len, SPIHID_DEF_WAIT); + } + + return 0; +error: + return err; +} +EXPORT_SYMBOL_GPL(spihid_apple_core_probe); + +void spihid_apple_core_remove(struct spi_device *spi) +{ + struct spihid_apple *spihid = spi_get_drvdata(spi); + + /* destroy input devices */ + + spihid_destroy_hid_device(&spihid->tp); + spihid_destroy_hid_device(&spihid->kbd); + + /* disable irq */ + spihid->ops->disable_irq(spihid->ops); + + /* power SPI device down */ + spihid->ops->power_off(spihid->ops); +} +EXPORT_SYMBOL_GPL(spihid_apple_core_remove); + +void spihid_apple_core_shutdown(struct spi_device *spi) +{ + struct spihid_apple *spihid = spi_get_drvdata(spi); + + /* disable irq */ + spihid->ops->disable_irq(spihid->ops); + + /* power SPI device down */ + spihid->ops->power_off(spihid->ops); +} +EXPORT_SYMBOL_GPL(spihid_apple_core_shutdown); + +MODULE_DESCRIPTION("Apple SPI HID transport driver"); +MODULE_AUTHOR("Janne Grunau "); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/spi-hid/spi-hid-apple-of.c b/drivers/hid/spi-hid/spi-hid-apple-of.c new file mode 100644 index 00000000000000..f1380bfc52672e --- /dev/null +++ b/drivers/hid/spi-hid/spi-hid-apple-of.c @@ -0,0 +1,136 @@ +/* + * SPDX-License-Identifier: GPL-2.0 + * + * Apple SPI HID transport driver - Open Firmware + * + * Copyright (C) The Asahi Linux Contributors + */ + +#include +#include +#include +#include + +#include "spi-hid-apple.h" + + +struct spihid_apple_of { + struct spihid_apple_ops ops; + + struct gpio_desc *enable_gpio; + int irq; +}; + +static int spihid_apple_of_power_on(struct spihid_apple_ops *ops) +{ + struct spihid_apple_of *sh_of = container_of(ops, struct spihid_apple_of, ops); + + /* reset the controller on boot */ + gpiod_direction_output(sh_of->enable_gpio, 1); + msleep(5); + gpiod_direction_output(sh_of->enable_gpio, 0); + msleep(5); + /* turn SPI device on */ + gpiod_direction_output(sh_of->enable_gpio, 1); + msleep(50); + + return 0; +} + +static int spihid_apple_of_power_off(struct spihid_apple_ops *ops) +{ + struct spihid_apple_of *sh_of = container_of(ops, struct spihid_apple_of, ops); + + /* turn SPI device off */ + gpiod_direction_output(sh_of->enable_gpio, 0); + + return 0; +} + +static int spihid_apple_of_enable_irq(struct spihid_apple_ops *ops) +{ + struct spihid_apple_of *sh_of = container_of(ops, struct spihid_apple_of, ops); + + enable_irq(sh_of->irq); + + return 0; +} + +static int spihid_apple_of_disable_irq(struct spihid_apple_ops *ops) +{ + struct spihid_apple_of *sh_of = container_of(ops, struct spihid_apple_of, ops); + + disable_irq(sh_of->irq); + + return 0; +} + +static int spihid_apple_of_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct spihid_apple_of *spihid_of; + int err; + + spihid_of = devm_kzalloc(dev, sizeof(*spihid_of), GFP_KERNEL); + if (!spihid_of) + return -ENOMEM; + + spihid_of->ops.power_on = spihid_apple_of_power_on; + spihid_of->ops.power_off = spihid_apple_of_power_off; + spihid_of->ops.enable_irq = spihid_apple_of_enable_irq; + spihid_of->ops.disable_irq = spihid_apple_of_disable_irq; + + spihid_of->enable_gpio = devm_gpiod_get_index(dev, "spien", 0, 0); + if (IS_ERR(spihid_of->enable_gpio)) { + err = PTR_ERR(spihid_of->enable_gpio); + dev_err(dev, "failed to get 'spien' gpio pin: %d", err); + return err; + } + + spihid_of->irq = of_irq_get(dev->of_node, 0); + if (spihid_of->irq < 0) { + err = spihid_of->irq; + dev_err(dev, "failed to get 'extended-irq': %d", err); + return err; + } + err = devm_request_threaded_irq(dev, spihid_of->irq, NULL, + spihid_apple_core_irq, IRQF_ONESHOT | IRQF_NO_AUTOEN, + "spi-hid-apple-irq", spi); + if (err < 0) { + dev_err(dev, "failed to request extended-irq %d: %d", + spihid_of->irq, err); + return err; + } + + return spihid_apple_core_probe(spi, &spihid_of->ops); +} + +static const struct of_device_id spihid_apple_of_match[] = { + { .compatible = "apple,spi-hid-transport" }, + {}, +}; +MODULE_DEVICE_TABLE(of, spihid_apple_of_match); + +static struct spi_device_id spihid_apple_of_id[] = { + { "spi-hid-transport", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, spihid_apple_of_id); + +static struct spi_driver spihid_apple_of_driver = { + .driver = { + .name = "spi-hid-apple-of", + //.pm = &spi_hid_apple_of_pm, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(spihid_apple_of_match), + }, + + .id_table = spihid_apple_of_id, + .probe = spihid_apple_of_probe, + .remove = spihid_apple_core_remove, + .shutdown = spihid_apple_core_shutdown, +}; + +module_spi_driver(spihid_apple_of_driver); + +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/spi-hid/spi-hid-apple.h b/drivers/hid/spi-hid/spi-hid-apple.h new file mode 100644 index 00000000000000..2d9554e8a5f819 --- /dev/null +++ b/drivers/hid/spi-hid/spi-hid-apple.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ + +#ifndef SPI_HID_APPLE_H +#define SPI_HID_APPLE_H + +#include +#include + +/** + * struct spihid_apple_ops - Ops to control the device from the core driver. + * + * @power_on: reset and power the device on. + * @power_off: power the device off. + * @enable_irq: enable irq or ACPI gpe. + * @disable_irq: disable irq or ACPI gpe. + */ + +struct spihid_apple_ops { + int (*power_on)(struct spihid_apple_ops *ops); + int (*power_off)(struct spihid_apple_ops *ops); + int (*enable_irq)(struct spihid_apple_ops *ops); + int (*disable_irq)(struct spihid_apple_ops *ops); +}; + +irqreturn_t spihid_apple_core_irq(int irq, void *data); + +int spihid_apple_core_probe(struct spi_device *spi, struct spihid_apple_ops *ops); +void spihid_apple_core_remove(struct spi_device *spi); +void spihid_apple_core_shutdown(struct spi_device *spi); + +#endif /* SPI_HID_APPLE_H */ From 701fb141b43cf4c5518f63aaa2a1a84f3e251593 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 8 Jul 2022 00:29:43 +0900 Subject: [PATCH 0357/1020] HID: add HOST vendor/device IDs for Apple MTP devices Apple M2 chips have an embedded MTP processor that handles all HID functions, and does not go over a traditional bus like SPI. The devices still have real IDs, so add them here. Signed-off-by: Hector Martin --- drivers/hid/hid-ids.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 3e118c184d06ac..0cccf53c66e6e0 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -90,6 +90,7 @@ #define USB_VENDOR_ID_APPLE 0x05ac #define BT_VENDOR_ID_APPLE 0x004c #define SPI_VENDOR_ID_APPLE 0x05ac +#define HOST_VENDOR_ID_APPLE 0x05ac #define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304 #define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d #define USB_DEVICE_ID_APPLE_MAGICMOUSE2 0x0269 @@ -192,6 +193,8 @@ #define SPI_DEVICE_ID_APPLE_MACBOOK_PRO13_2020 0x0341 #define SPI_DEVICE_ID_APPLE_MACBOOK_PRO14_2021 0x0342 #define SPI_DEVICE_ID_APPLE_MACBOOK_PRO16_2021 0x0343 +#define HOST_DEVICE_ID_APPLE_MACBOOK_AIR13_2022 0x0351 +#define HOST_DEVICE_ID_APPLE_MACBOOK_PRO13_2022 0x0354 #define USB_VENDOR_ID_ASUS 0x0486 #define USB_DEVICE_ID_ASUS_T91MT 0x0185 From 9d8c9e86b1c51b57e21276e6860fe46a3c715ff9 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 8 Jul 2022 02:06:15 +0900 Subject: [PATCH 0358/1020] HID: core: Handle HOST bus type when announcing devices Signed-off-by: Hector Martin --- drivers/hid/hid-core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index d887916ae3867d..149c53b932bea8 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2252,6 +2252,9 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) case BUS_SPI: bus = "SPI"; break; + case BUS_HOST: + bus = "HOST"; + break; case BUS_VIRTUAL: bus = "VIRTUAL"; break; From 5fe172aa16fc55482faf1fccf361948735f55e7f Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 8 Jul 2022 02:12:24 +0900 Subject: [PATCH 0359/1020] hid: apple: Bind to HOST devices for MTP We use BUS_HOST for MTP HID subdevices Signed-off-by: Hector Martin --- drivers/hid/hid-apple.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index cc406552a35d92..0964fac0bedd07 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -521,11 +521,16 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, else if (hid->product >= USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI && hid->product <= USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS) table = macbookair_fn_keys; - else if (hid->vendor == SPI_VENDOR_ID_APPLE && - hid->product == SPI_DEVICE_ID_APPLE_MACBOOK_PRO13_2020) - table = apple_fn_keys_mbp13; - else if (hid->vendor == SPI_VENDOR_ID_APPLE) - table = apple_fn_keys_spi; + else if (hid->bus == BUS_HOST || hid->bus == BUS_SPI) + switch (hid->product) { + case SPI_DEVICE_ID_APPLE_MACBOOK_PRO13_2020: + case HOST_DEVICE_ID_APPLE_MACBOOK_PRO13_2022: + table = apple_fn_keys_mbp13; + break; + default: + table = apple_fn_keys_spi; + break; + } else if (hid->product < 0x21d || hid->product >= 0x300) table = powerbook_fn_keys; else @@ -880,7 +885,7 @@ static int apple_probe(struct hid_device *hdev, struct apple_sc *asc; int ret; - if (id->bus == BUS_SPI && id->vendor == SPI_VENDOR_ID_APPLE && + if ((id->bus == BUS_SPI || id->bus == BUS_HOST) && id->vendor == SPI_VENDOR_ID_APPLE && hdev->type != HID_TYPE_SPI_KEYBOARD) return -ENODEV; @@ -1130,6 +1135,8 @@ static const struct hid_device_id apple_devices[] = { .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, { HID_SPI_DEVICE(SPI_VENDOR_ID_APPLE, HID_ANY_ID), .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, + { HID_DEVICE(BUS_HOST, HID_GROUP_ANY, HOST_VENDOR_ID_APPLE, HID_ANY_ID), + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, { } }; From 71aa155bda9d3a0082bacc03f42300aceb3659f1 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 8 Jul 2022 02:12:57 +0900 Subject: [PATCH 0360/1020] hid: magicmouse: Add MTP multi-touch device support Apple M2 devices expose the multi-touch device over the HID over DockChannel transport, which we represent as the HOST bus type. The report format is the same, except the legacy mouse header is gone and there is no enable request needed. Signed-off-by: Hector Martin --- drivers/hid/hid-magicmouse.c | 67 ++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 780265efbf2e92..d04058e3ee39cb 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -59,6 +59,8 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie #define MOUSE_REPORT_ID 0x29 #define MOUSE2_REPORT_ID 0x12 #define DOUBLE_REPORT_ID 0xf7 +#define SPI_REPORT_ID 0x02 +#define MTP_REPORT_ID 0x75 #define USB_BATTERY_TIMEOUT_MS 60000 #define MAX_CONTACTS 16 @@ -573,25 +575,32 @@ struct tp_finger { } __attribute__((packed, aligned(2))); /** - * struct trackpad report + * vendor trackpad report * - * @report_id: reportid - * @buttons: HID Usage Buttons 3 1-bit reports * @num_fingers: the number of fingers being reported in @fingers - * @clicked: same as @buttons + * @buttons: same as HID buttons */ struct tp_header { + // HID vendor part, up to 1751 bytes + u8 unknown[22]; + u8 num_fingers; + u8 buttons; + u8 unknown3[14]; +}; + +/** + * standard HID mouse report + * + * @report_id: reportid + * @buttons: HID Usage Buttons 3 1-bit reports + */ +struct tp_mouse_report { // HID mouse report u8 report_id; u8 buttons; u8 rel_x; u8 rel_y; u8 padding[4]; - // HID vendor part, up to 1751 bytes - u8 unknown[22]; - u8 num_fingers; - u8 clicked; - u8 unknown3[14]; }; static inline int le16_to_int(__le16 x) @@ -621,7 +630,7 @@ static void report_finger_data(struct input_dev *input, int slot, input_report_abs(input, ABS_MT_POSITION_Y, pos->y); } -static int magicmouse_raw_event_spi(struct hid_device *hdev, +static int magicmouse_raw_event_mtp(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { struct magicmouse_sc *msc = hid_get_drvdata(hdev); @@ -638,9 +647,6 @@ static int magicmouse_raw_event_spi(struct hid_device *hdev, // print_hex_dump_debug("appleft ev: ", DUMP_PREFIX_OFFSET, 16, 1, data, // size, false); - if (data[0] != TRACKPAD2_USB_REPORT_ID) - return 0; - /* Expect 46 bytes of prefix, and N * 30 bytes of touch data. */ if (size < hdr_sz || ((size - hdr_sz) % touch_sz) != 0) return 0; @@ -679,12 +685,26 @@ static int magicmouse_raw_event_spi(struct hid_device *hdev, } input_mt_sync_frame(input); - input_report_key(input, BTN_MOUSE, data[1] & 1); + input_report_key(input, BTN_MOUSE, tp_hdr->buttons & 1); input_sync(input); return 1; } +static int magicmouse_raw_event_spi(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + const size_t hdr_sz = sizeof(struct tp_mouse_report); + + if (size < hdr_sz) + return 0; + + if (data[0] != TRACKPAD2_USB_REPORT_ID) + return 0; + + return magicmouse_raw_event_mtp(hdev, report, data + hdr_sz, size - hdr_sz); +} + static int magicmouse_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value) { @@ -1069,7 +1089,7 @@ static int magicmouse_probe(struct hid_device *hdev, struct hid_report *report; int ret; - if (id->bus == BUS_SPI && id->vendor == SPI_VENDOR_ID_APPLE && + if ((id->bus == BUS_SPI || id->bus == BUS_HOST) && id->vendor == SPI_VENDOR_ID_APPLE && hdev->type != HID_TYPE_SPI_MOUSE) return -ENODEV; @@ -1081,7 +1101,10 @@ static int magicmouse_probe(struct hid_device *hdev, // internal trackpad use a data format use input ops to avoid // conflicts with the report ID. - if (id->vendor == SPI_VENDOR_ID_APPLE) { + if (id->bus == BUS_HOST) { + msc->input_ops.raw_event = magicmouse_raw_event_mtp; + msc->input_ops.setup_input = magicmouse_setup_input_spi; + } else if (id->bus == BUS_SPI) { msc->input_ops.raw_event = magicmouse_raw_event_spi; msc->input_ops.setup_input = magicmouse_setup_input_spi; @@ -1138,8 +1161,10 @@ static int magicmouse_probe(struct hid_device *hdev, else /* USB_VENDOR_ID_APPLE */ report = hid_register_report(hdev, HID_INPUT_REPORT, TRACKPAD2_USB_REPORT_ID, 0); - } else if (id->vendor == SPI_VENDOR_ID_APPLE) { - report = hid_register_report(hdev, HID_INPUT_REPORT, 2, 0); + } else if (id->bus == BUS_SPI) { + report = hid_register_report(hdev, HID_INPUT_REPORT, SPI_REPORT_ID, 0); + } else if (id->bus == BUS_HOST) { + report = hid_register_report(hdev, HID_INPUT_REPORT, MTP_REPORT_ID, 0); } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ report = hid_register_report(hdev, HID_INPUT_REPORT, TRACKPAD_REPORT_ID, 0); @@ -1154,6 +1179,10 @@ static int magicmouse_probe(struct hid_device *hdev, } report->size = 6; + /* MTP devices do not need the MT enable, this is handled by the MTP driver */ + if (id->bus == BUS_HOST) + return 0; + /* * Some devices repond with 'invalid report id' when feature * report switching it into multitouch mode is sent to it. @@ -1236,6 +1265,8 @@ static const struct hid_device_id magic_mice[] = { USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 }, { HID_SPI_DEVICE(SPI_VENDOR_ID_APPLE, HID_ANY_ID), .driver_data = 0 }, + { HID_DEVICE(BUS_HOST, HID_GROUP_ANY, HOST_VENDOR_ID_APPLE, + HID_ANY_ID), .driver_data = 0 }, { } }; MODULE_DEVICE_TABLE(hid, magic_mice); From b13e8bef4029ef0a78a13fecc22995a93c0b0c7f Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 8 Jul 2022 02:09:24 +0900 Subject: [PATCH 0361/1020] soc: apple: Add DockChannel driver DockChannel is a simple FIFO interface used to communicate between SoC blocks. Add a driver that represents the shared interrupt controller for the DockChannel block, and then exposes probe and data transfer functions that child device drivers can use to instantiate individual FIFOs. Signed-off-by: Hector Martin --- drivers/soc/apple/Kconfig | 10 + drivers/soc/apple/Makefile | 3 + drivers/soc/apple/dockchannel.c | 407 ++++++++++++++++++++++++++ include/linux/soc/apple/dockchannel.h | 26 ++ 4 files changed, 446 insertions(+) create mode 100644 drivers/soc/apple/dockchannel.c create mode 100644 include/linux/soc/apple/dockchannel.h diff --git a/drivers/soc/apple/Kconfig b/drivers/soc/apple/Kconfig index 6388cbe1e56b5a..cceb514b05bbfa 100644 --- a/drivers/soc/apple/Kconfig +++ b/drivers/soc/apple/Kconfig @@ -41,6 +41,16 @@ config APPLE_SART Say 'y' here if you have an Apple SoC. +config APPLE_DOCKCHANNEL + tristate "Apple DockChannel FIFO" + depends on ARCH_APPLE || COMPILE_TEST + default ARCH_APPLE + help + DockChannel is a simple FIFO used on Apple SoCs for debug and inter-processor + communications. + + Say 'y' here if you have an Apple SoC. + endmenu endif diff --git a/drivers/soc/apple/Makefile b/drivers/soc/apple/Makefile index 4d9ab8f3037b71..07d43360426110 100644 --- a/drivers/soc/apple/Makefile +++ b/drivers/soc/apple/Makefile @@ -8,3 +8,6 @@ apple-rtkit-y = rtkit.o rtkit-crashlog.o obj-$(CONFIG_APPLE_SART) += apple-sart.o apple-sart-y = sart.o + +obj-$(CONFIG_APPLE_DOCKCHANNEL) += apple-dockchannel.o +apple-dockchannel-y = dockchannel.o diff --git a/drivers/soc/apple/dockchannel.c b/drivers/soc/apple/dockchannel.c new file mode 100644 index 00000000000000..b4d793bf210266 --- /dev/null +++ b/drivers/soc/apple/dockchannel.c @@ -0,0 +1,407 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple DockChannel FIFO driver + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DOCKCHANNEL_MAX_IRQ 32 + +#define DOCKCHANNEL_TX_TIMEOUT_MS 1000 +#define DOCKCHANNEL_RX_TIMEOUT_MS 1000 + +#define IRQ_MASK 0x0 +#define IRQ_FLAG 0x4 + +#define IRQ_TX BIT(0) +#define IRQ_RX BIT(1) + +#define CONFIG_TX_THRESH 0x0 +#define CONFIG_RX_THRESH 0x4 + +#define DATA_TX8 0x4 +#define DATA_TX16 0x8 +#define DATA_TX24 0xc +#define DATA_TX32 0x10 +#define DATA_TX_FREE 0x14 +#define DATA_RX8 0x1c +#define DATA_RX16 0x20 +#define DATA_RX24 0x24 +#define DATA_RX32 0x28 +#define DATA_RX_COUNT 0x2c + +struct dockchannel { + struct device *dev; + int tx_irq; + int rx_irq; + + void __iomem *config_base; + void __iomem *data_base; + + u32 fifo_size; + bool awaiting; + struct completion tx_comp; + struct completion rx_comp; + + void *cookie; + void (*data_available)(void *cookie, size_t avail); +}; + +struct dockchannel_common { + struct device *dev; + struct irq_domain *domain; + int irq; + + void __iomem *irq_base; +}; + +/* Dockchannel FIFO functions */ + +static irqreturn_t dockchannel_tx_irq(int irq, void *data) +{ + struct dockchannel *dockchannel = data; + + disable_irq_nosync(irq); + complete(&dockchannel->tx_comp); + + return IRQ_HANDLED; +} + +static irqreturn_t dockchannel_rx_irq(int irq, void *data) +{ + struct dockchannel *dockchannel = data; + + disable_irq_nosync(irq); + + if (dockchannel->awaiting) { + return IRQ_WAKE_THREAD; + } else { + complete(&dockchannel->rx_comp); + return IRQ_HANDLED; + } +} + +static irqreturn_t dockchannel_rx_irq_thread(int irq, void *data) +{ + struct dockchannel *dockchannel = data; + size_t avail = readl_relaxed(dockchannel->data_base + DATA_RX_COUNT); + + dockchannel->awaiting = false; + dockchannel->data_available(dockchannel->cookie, avail); + + return IRQ_HANDLED; +} + +int dockchannel_send(struct dockchannel *dockchannel, const void *buf, size_t count) +{ + size_t left = count; + const u8 *p = buf; + + while (left > 0) { + size_t avail = readl_relaxed(dockchannel->data_base + DATA_TX_FREE); + size_t block = min(left, avail); + + if (avail == 0) { + size_t threshold = min((size_t)(dockchannel->fifo_size / 2), left); + + writel_relaxed(threshold, dockchannel->config_base + CONFIG_TX_THRESH); + reinit_completion(&dockchannel->tx_comp); + enable_irq(dockchannel->tx_irq); + + if (!wait_for_completion_timeout(&dockchannel->tx_comp, + msecs_to_jiffies(DOCKCHANNEL_TX_TIMEOUT_MS))) { + disable_irq(dockchannel->tx_irq); + return -ETIMEDOUT; + } + + continue; + } + + while (block >= 4) { + writel_relaxed(get_unaligned_le32(p), dockchannel->data_base + DATA_TX32); + p += 4; + left -= 4; + block -= 4; + } + while (block > 0) { + writeb_relaxed(*p++, dockchannel->data_base + DATA_TX8); + left--; + block--; + } + } + + return count; +} +EXPORT_SYMBOL(dockchannel_send); + +int dockchannel_recv(struct dockchannel *dockchannel, void *buf, size_t count) +{ + size_t left = count; + u8 *p = buf; + + while (left > 0) { + size_t avail = readl_relaxed(dockchannel->data_base + DATA_RX_COUNT); + size_t block = min(left, avail); + + if (avail == 0) { + size_t threshold = min((size_t)(dockchannel->fifo_size / 2), left); + + writel_relaxed(threshold, dockchannel->config_base + CONFIG_RX_THRESH); + reinit_completion(&dockchannel->rx_comp); + enable_irq(dockchannel->rx_irq); + + if (!wait_for_completion_timeout(&dockchannel->rx_comp, + msecs_to_jiffies(DOCKCHANNEL_RX_TIMEOUT_MS))) { + disable_irq(dockchannel->rx_irq); + return -ETIMEDOUT; + } + + continue; + } + + while (block >= 4) { + put_unaligned_le32(readl_relaxed(dockchannel->data_base + DATA_RX32), p); + p += 4; + left -= 4; + block -= 4; + } + while (block > 0) { + *p++ = readl_relaxed(dockchannel->data_base + DATA_RX8) >> 8; + left--; + block--; + } + } + + return count; +} +EXPORT_SYMBOL(dockchannel_recv); + +int dockchannel_await(struct dockchannel *dockchannel, + void (*callback)(void *cookie, size_t avail), + void *cookie, size_t count) +{ + size_t threshold = min((size_t)dockchannel->fifo_size, count); + + if (!count) { + dockchannel->awaiting = false; + disable_irq(dockchannel->rx_irq); + return 0; + } + + dockchannel->data_available = callback; + dockchannel->cookie = cookie; + dockchannel->awaiting = true; + writel_relaxed(threshold, dockchannel->config_base + CONFIG_RX_THRESH); + enable_irq(dockchannel->rx_irq); + + return threshold; +} +EXPORT_SYMBOL(dockchannel_await); + +struct dockchannel *dockchannel_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dockchannel *dockchannel; + int ret; + + dockchannel = devm_kzalloc(dev, sizeof(*dockchannel), GFP_KERNEL); + if (!dockchannel) + return ERR_PTR(-ENOMEM); + + dockchannel->dev = dev; + dockchannel->config_base = devm_platform_ioremap_resource_byname(pdev, "config"); + if (IS_ERR(dockchannel->config_base)) + return (__force void *)dockchannel->config_base; + + dockchannel->data_base = devm_platform_ioremap_resource_byname(pdev, "data"); + if (IS_ERR(dockchannel->data_base)) + return (__force void *)dockchannel->data_base; + + ret = of_property_read_u32(dev->of_node, "apple,fifo-size", &dockchannel->fifo_size); + if (ret) + return ERR_PTR(dev_err_probe(dev, ret, "Missing apple,fifo-size property")); + + init_completion(&dockchannel->tx_comp); + init_completion(&dockchannel->rx_comp); + + dockchannel->tx_irq = platform_get_irq_byname(pdev, "tx"); + if (dockchannel->tx_irq <= 0) { + return ERR_PTR(dev_err_probe(dev, dockchannel->tx_irq, + "Failed to get TX IRQ")); + } + + dockchannel->rx_irq = platform_get_irq_byname(pdev, "rx"); + if (dockchannel->rx_irq <= 0) { + return ERR_PTR(dev_err_probe(dev, dockchannel->rx_irq, + "Failed to get RX IRQ")); + } + + ret = devm_request_irq(dev, dockchannel->tx_irq, dockchannel_tx_irq, IRQF_NO_AUTOEN, + "apple-dockchannel-tx", dockchannel); + if (ret) + return ERR_PTR(dev_err_probe(dev, ret, "Failed to request TX IRQ")); + + ret = devm_request_threaded_irq(dev, dockchannel->rx_irq, dockchannel_rx_irq, + dockchannel_rx_irq_thread, IRQF_NO_AUTOEN, + "apple-dockchannel-rx", dockchannel); + if (ret) + return ERR_PTR(dev_err_probe(dev, ret, "Failed to request RX IRQ")); + + return dockchannel; +} +EXPORT_SYMBOL(dockchannel_init); + + +/* Dockchannel IRQchip */ + +static void dockchannel_irq(struct irq_desc *desc) +{ + unsigned int irq = irq_desc_get_irq(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + struct dockchannel_common *dcc = irq_get_handler_data(irq); + unsigned long flags = readl_relaxed(dcc->irq_base + IRQ_FLAG); + int bit; + + chained_irq_enter(chip, desc); + + for_each_set_bit(bit, &flags, DOCKCHANNEL_MAX_IRQ) + generic_handle_domain_irq(dcc->domain, bit); + + chained_irq_exit(chip, desc); +} + +static void dockchannel_irq_ack(struct irq_data *data) +{ + struct dockchannel_common *dcc = irq_data_get_irq_chip_data(data); + unsigned int hwirq = data->hwirq; + + writel_relaxed(BIT(hwirq), dcc->irq_base + IRQ_FLAG); +} + +static void dockchannel_irq_mask(struct irq_data *data) +{ + struct dockchannel_common *dcc = irq_data_get_irq_chip_data(data); + unsigned int hwirq = data->hwirq; + u32 val = readl_relaxed(dcc->irq_base + IRQ_MASK); + + writel_relaxed(val & ~BIT(hwirq), dcc->irq_base + IRQ_MASK); +} + +static void dockchannel_irq_unmask(struct irq_data *data) +{ + struct dockchannel_common *dcc = irq_data_get_irq_chip_data(data); + unsigned int hwirq = data->hwirq; + u32 val = readl_relaxed(dcc->irq_base + IRQ_MASK); + + writel_relaxed(val | BIT(hwirq), dcc->irq_base + IRQ_MASK); +} + +static const struct irq_chip dockchannel_irqchip = { + .name = "dockchannel-irqc", + .irq_ack = dockchannel_irq_ack, + .irq_mask = dockchannel_irq_mask, + .irq_unmask = dockchannel_irq_unmask, +}; + +static int dockchannel_irq_domain_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_data(virq, d->host_data); + irq_set_chip_and_handler(virq, &dockchannel_irqchip, handle_level_irq); + + return 0; +} + +static const struct irq_domain_ops dockchannel_irq_domain_ops = { + .xlate = irq_domain_xlate_twocell, + .map = dockchannel_irq_domain_map, +}; + +static int dockchannel_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dockchannel_common *dcc; + struct device_node *child; + + dcc = devm_kzalloc(dev, sizeof(*dcc), GFP_KERNEL); + if (!dcc) + return -ENOMEM; + + dcc->dev = dev; + platform_set_drvdata(pdev, dcc); + + dcc->irq_base = devm_platform_ioremap_resource_byname(pdev, "irq"); + if (IS_ERR(dcc->irq_base)) + return PTR_ERR(dcc->irq_base); + + writel_relaxed(0, dcc->irq_base + IRQ_MASK); + writel_relaxed(~0, dcc->irq_base + IRQ_FLAG); + + dcc->domain = irq_domain_add_linear(dev->of_node, DOCKCHANNEL_MAX_IRQ, + &dockchannel_irq_domain_ops, dcc); + if (!dcc->domain) + return -ENOMEM; + + dcc->irq = platform_get_irq(pdev, 0); + if (dcc->irq <= 0) + return dev_err_probe(dev, dcc->irq, "Failed to get IRQ"); + + irq_set_handler_data(dcc->irq, dcc); + irq_set_chained_handler(dcc->irq, dockchannel_irq); + + for_each_child_of_node(dev->of_node, child) + of_platform_device_create(child, NULL, dev); + + return 0; +} + +static int dockchannel_remove(struct platform_device *pdev) +{ + struct dockchannel_common *dcc = platform_get_drvdata(pdev); + int hwirq; + + device_for_each_child(&pdev->dev, NULL, of_platform_device_destroy); + + irq_set_chained_handler_and_data(dcc->irq, NULL, NULL); + + for (hwirq = 0; hwirq < DOCKCHANNEL_MAX_IRQ; hwirq++) + irq_dispose_mapping(irq_find_mapping(dcc->domain, hwirq)); + + irq_domain_remove(dcc->domain); + + writel_relaxed(0, dcc->irq_base + IRQ_MASK); + writel_relaxed(~0, dcc->irq_base + IRQ_FLAG); + + return 0; +} + +static const struct of_device_id dockchannel_of_match[] = { + { .compatible = "apple,dockchannel" }, + {}, +}; +MODULE_DEVICE_TABLE(of, dockchannel_of_match); + +static struct platform_driver dockchannel_driver = { + .driver = { + .name = "dockchannel", + .of_match_table = dockchannel_of_match, + }, + .probe = dockchannel_probe, + .remove = dockchannel_remove, +}; +module_platform_driver(dockchannel_driver); + +MODULE_AUTHOR("Hector Martin "); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("Apple DockChannel driver"); diff --git a/include/linux/soc/apple/dockchannel.h b/include/linux/soc/apple/dockchannel.h new file mode 100644 index 00000000000000..0b7093935ddf47 --- /dev/null +++ b/include/linux/soc/apple/dockchannel.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* + * Apple Dockchannel devices + * Copyright (C) The Asahi Linux Contributors + */ +#ifndef _LINUX_APPLE_DOCKCHANNEL_H_ +#define _LINUX_APPLE_DOCKCHANNEL_H_ + +#include +#include +#include + +#if IS_ENABLED(CONFIG_APPLE_DOCKCHANNEL) + +struct dockchannel; + +struct dockchannel *dockchannel_init(struct platform_device *pdev); + +int dockchannel_send(struct dockchannel *dockchannel, const void *buf, size_t count); +int dockchannel_recv(struct dockchannel *dockchannel, void *buf, size_t count); +int dockchannel_await(struct dockchannel *dockchannel, + void (*callback)(void *cookie, size_t avail), + void *cookie, size_t count); + +#endif +#endif From c8d9a1dc19fad0d9be2580227673541e7b3bb9a0 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 8 Jul 2022 02:11:21 +0900 Subject: [PATCH 0362/1020] hid: Add Apple DockChannel HID transport driver Apple M2 devices have an MTP coprocessor embedded in the SoC that handles HID for the integrated touchpad/keyboard, and communicates over the DockChannel interface. This driver implements this new interface. Signed-off-by: Hector Martin --- drivers/hid/Kconfig | 2 + drivers/hid/Makefile | 2 + drivers/hid/dockchannel-hid/Kconfig | 14 + drivers/hid/dockchannel-hid/Makefile | 6 + drivers/hid/dockchannel-hid/dockchannel-hid.c | 1216 +++++++++++++++++ 5 files changed, 1240 insertions(+) create mode 100644 drivers/hid/dockchannel-hid/Kconfig create mode 100644 drivers/hid/dockchannel-hid/Makefile create mode 100644 drivers/hid/dockchannel-hid/dockchannel-hid.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 2203f6abed9383..1e3bdbc229f712 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1355,4 +1355,6 @@ source "drivers/hid/surface-hid/Kconfig" source "drivers/hid/spi-hid/Kconfig" +source "drivers/hid/dockchannel-hid/Kconfig" + endif # HID_SUPPORT diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 2f150d0777e6e5..2098922ca2ef6d 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -172,3 +172,5 @@ obj-$(CONFIG_AMD_SFH_HID) += amd-sfh-hid/ obj-$(CONFIG_SURFACE_HID_CORE) += surface-hid/ obj-$(CONFIG_SPI_HID_APPLE_CORE) += spi-hid/ + +obj-$(CONFIG_HID_DOCKCHANNEL) += dockchannel-hid/ diff --git a/drivers/hid/dockchannel-hid/Kconfig b/drivers/hid/dockchannel-hid/Kconfig new file mode 100644 index 00000000000000..8a81d551a83d51 --- /dev/null +++ b/drivers/hid/dockchannel-hid/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +menu "DockChannel HID support" + depends on APPLE_DOCKCHANNEL + +config HID_DOCKCHANNEL + tristate "HID over DockChannel transport layer for Apple Silicon SoCs" + default ARCH_APPLE + depends on APPLE_DOCKCHANNEL && INPUT && OF && HID + help + Say Y here if you use an M2 or later Apple Silicon based laptop. + The keyboard and touchpad are HID based devices connected via the + proprietary DockChannel interface. + +endmenu diff --git a/drivers/hid/dockchannel-hid/Makefile b/drivers/hid/dockchannel-hid/Makefile new file mode 100644 index 00000000000000..7dba766b047fcc --- /dev/null +++ b/drivers/hid/dockchannel-hid/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# +# Makefile for DockChannel HID transport drivers +# + +obj-$(CONFIG_HID_DOCKCHANNEL) += dockchannel-hid.o diff --git a/drivers/hid/dockchannel-hid/dockchannel-hid.c b/drivers/hid/dockchannel-hid/dockchannel-hid.c new file mode 100644 index 00000000000000..6589917664c4df --- /dev/null +++ b/drivers/hid/dockchannel-hid/dockchannel-hid.c @@ -0,0 +1,1216 @@ +/* + * SPDX-License-Identifier: GPL-2.0 OR MIT + * + * Apple DockChannel HID transport driver + * + * Copyright The Asahi Linux Contributors + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../hid-ids.h" + +#define COMMAND_TIMEOUT_MS 1000 +#define START_TIMEOUT_MS 2000 + +#define MAX_INTERFACES 16 + +/* Data + checksum */ +#define MAX_PKT_SIZE (0xffff + 4) + +#define DCHID_CHANNEL_CMD 0x11 +#define DCHID_CHANNEL_REPORT 0x12 + +struct dchid_hdr { + u8 hdr_len; + u8 channel; + u16 length; + u8 seq; + u8 iface; + u16 pad; +} __packed; + +#define IFACE_COMM 0 + +#define FLAGS_GROUP GENMASK(7, 6) +#define FLAGS_REQ GENMASK(5, 0) + +#define GROUP_INPUT 0 +#define GROUP_OUTPUT 1 +#define GROUP_CMD 2 + +#define REQ_SET_REPORT 0 +#define REQ_GET_REPORT 1 + +struct dchid_subhdr { + u8 flags; + u8 unk; + u16 length; + u32 retcode; +} __packed; + +#define EVENT_GPIO_CMD 0xa0 +#define EVENT_INIT 0xf0 +#define EVENT_READY 0xf1 + +struct dchid_init_hdr { + u8 type; + u8 unk1; + u8 unk2; + u8 iface; + char name[16]; + u8 more_packets; + u8 unkpad; +} __packed; + +#define INIT_HID_DESCRIPTOR 0 +#define INIT_GPIO_REQUEST 1 +#define INIT_TERMINATOR 2 +#define INIT_PRODUCT_NAME 7 + +#define CMD_RESET_INTERFACE 0x40 +#define CMD_SEND_FIRMWARE 0x95 +#define CMD_ENABLE_INTERFACE 0xb4 +#define CMD_ACK_GPIO_CMD 0xa1 + +struct dchid_init_block_hdr { + u16 type; + u16 length; +} __packed; + +#define MAX_GPIO_NAME 32 + +struct dchid_gpio_request { + u16 unk; + u16 id; + char name[MAX_GPIO_NAME]; +} __packed; + +struct dchid_gpio_cmd { + u8 type; + u8 iface; + u8 gpio; + u8 unk; + u8 cmd; +} __packed; + +struct dchid_gpio_ack { + u8 type; + u32 retcode; + u8 cmd[]; +} __packed; + +#define STM_REPORT_ID 0x10 +#define STM_REPORT_SERIAL 0x11 +#define STM_REPORT_KEYBTYPE 0x14 + +struct dchid_stm_id { + u8 unk; + u16 vendor_id; + u16 product_id; + u16 version_number; + u8 unk2; + u8 unk3; + u8 keyboard_type; + u8 serial_length; + /* Serial follows, but we grab it with a different report. */ +} __packed; + +#define FW_MAGIC 0x46444948 +#define FW_VER 1 + +struct fw_header { + u32 magic; + u32 version; + u32 hdr_length; + u32 data_length; + u32 iface_offset; +} __packed; + +struct dchid_work { + struct work_struct work; + struct dchid_iface *iface; + + struct dchid_hdr hdr; + u8 data[]; +}; + +struct dchid_iface { + struct dockchannel_hid *dchid; + struct hid_device *hid; + struct workqueue_struct *wq; + + bool creating; + struct work_struct create_work; + + int index; + const char *name; + const struct device_node *of_node; + + uint8_t tx_seq; + bool deferred; + bool starting; + bool open; + struct completion ready; + + void *hid_desc; + size_t hid_desc_len; + + struct gpio_desc *gpio; + char gpio_name[MAX_GPIO_NAME]; + int gpio_id; + + struct mutex out_mutex; + u32 out_flags; + int out_report; + u32 retcode; + void *resp_buf; + size_t resp_size; + struct completion out_complete; + + u32 keyboard_layout_id; +}; + +struct dockchannel_hid { + struct device *dev; + struct dockchannel *dc; + struct device_link *helper_link; + + bool id_ready; + struct dchid_stm_id device_id; + char serial[64]; + + struct dchid_iface *comm; + struct dchid_iface *ifaces[MAX_INTERFACES]; + + u8 pkt_buf[MAX_PKT_SIZE]; + + /* Workqueue to asynchronously create HID devices */ + struct workqueue_struct *new_iface_wq; +}; + +static ssize_t apple_layout_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev = to_hid_device(dev); + struct dchid_iface *iface = hdev->driver_data; + + return scnprintf(buf, PAGE_SIZE, "%d\n", iface->keyboard_layout_id); +} + +static DEVICE_ATTR_RO(apple_layout_id); + +static struct dchid_iface * +dchid_get_interface(struct dockchannel_hid *dchid, int index, const char *name) +{ + struct dchid_iface *iface; + + if (index >= MAX_INTERFACES) { + dev_err(dchid->dev, "Interface index %d out of range\n", index); + return NULL; + } + + if (dchid->ifaces[index]) + return dchid->ifaces[index]; + + iface = devm_kzalloc(dchid->dev, sizeof(struct dchid_iface), GFP_KERNEL); + if (!iface) + return NULL; + + iface->index = index; + iface->name = devm_kstrdup(dchid->dev, name, GFP_KERNEL); + iface->dchid = dchid; + iface->out_report= -1; + init_completion(&iface->out_complete); + init_completion(&iface->ready); + mutex_init(&iface->out_mutex); + iface->wq = alloc_ordered_workqueue("dchid-%s", WQ_MEM_RECLAIM, iface->name); + if (!iface->wq) + return NULL; + + /* Comm is not a HID subdevice */ + if (!strcmp(name, "comm")) { + dchid->ifaces[index] = iface; + return iface; + } + + iface->of_node = of_get_child_by_name(dchid->dev->of_node, name); + if (!iface->of_node) { + dev_warn(dchid->dev, "No OF node for subdevice %s, ignoring.", name); + return NULL; + } + + dchid->ifaces[index] = iface; + return iface; +} + +static u32 dchid_checksum(void *p, size_t length) +{ + u32 sum = 0; + + while (length >= 4) { + sum += get_unaligned_le32(p); + p += 4; + length -= 4; + } + + WARN_ON_ONCE(length); + return sum; +} + +static int dchid_send(struct dchid_iface *iface, u32 flags, void *msg, size_t size) +{ + u32 checksum = 0xffffffff; + size_t wsize = round_down(size, 4); + size_t tsize = size - wsize; + int ret; + struct { + struct dchid_hdr hdr; + struct dchid_subhdr sub; + } __packed h; + + memset(&h, 0, sizeof(h)); + h.hdr.hdr_len = sizeof(h.hdr); + h.hdr.channel = DCHID_CHANNEL_CMD; + h.hdr.length = round_up(size, 4) + sizeof(h.sub); + h.hdr.seq = iface->tx_seq; + h.hdr.iface = iface->index; + h.sub.flags = flags; + h.sub.length = size; + + ret = dockchannel_send(iface->dchid->dc, &h, sizeof(h)); + if (ret < 0) + return ret; + checksum -= dchid_checksum(&h, sizeof(h)); + + ret = dockchannel_send(iface->dchid->dc, msg, wsize); + if (ret < 0) + return ret; + checksum -= dchid_checksum(msg, wsize); + + if (tsize) { + u8 tail[4] = {0, 0, 0, 0}; + + memcpy(tail, msg + wsize, tsize); + ret = dockchannel_send(iface->dchid->dc, tail, sizeof(tail)); + if (ret < 0) + return ret; + checksum -= dchid_checksum(tail, sizeof(tail)); + } + + ret = dockchannel_send(iface->dchid->dc, &checksum, sizeof(checksum)); + if (ret < 0) + return ret; + + return 0; +} + +static int dchid_cmd(struct dchid_iface *iface, u32 type, u32 req, + void *data, size_t size, void *resp_buf, size_t resp_size) +{ + int ret; + int report_id = *(u8*)data; + + mutex_lock(&iface->out_mutex); + + WARN_ON(iface->out_report != -1); + iface->out_report = report_id; + iface->out_flags = FIELD_PREP(FLAGS_GROUP, type) | FIELD_PREP(FLAGS_REQ, req); + iface->resp_buf = resp_buf; + iface->resp_size = resp_size; + reinit_completion(&iface->out_complete); + + ret = dchid_send(iface, iface->out_flags, data, size); + if (ret < 0) + goto done; + + if (!wait_for_completion_timeout(&iface->out_complete, msecs_to_jiffies(COMMAND_TIMEOUT_MS))) { + dev_err(iface->dchid->dev, "output report 0x%x to iface %d (%s) timed out\n", + report_id, iface->index, iface->name); + ret = -ETIMEDOUT; + goto done; + } + + ret = iface->resp_size; + if (iface->retcode) { + dev_err(iface->dchid->dev, + "output report 0x%x to iface %d (%s) failed with err 0x%x\n", + report_id, iface->index, iface->name, iface->retcode); + ret = -EIO; + } + +done: + iface->tx_seq++; + iface->out_report = -1; + iface->out_flags = 0; + iface->resp_buf = NULL; + iface->resp_size = 0; + mutex_unlock(&iface->out_mutex); + return ret; +} + +static int dchid_comm_cmd(struct dockchannel_hid *dchid, void *cmd, size_t size) +{ + return dchid_cmd(dchid->comm, GROUP_CMD, REQ_SET_REPORT, cmd, size, NULL, 0); +} + +static int dchid_enable_interface(struct dchid_iface *iface) +{ + u8 msg[] = { CMD_ENABLE_INTERFACE, iface->index }; + + return dchid_comm_cmd(iface->dchid, msg, sizeof(msg)); +} + +static int dchid_reset_interface(struct dchid_iface *iface, int state) +{ + u8 msg[] = { CMD_RESET_INTERFACE, 1, iface->index, state }; + + return dchid_comm_cmd(iface->dchid, msg, sizeof(msg)); +} + +static int dchid_send_firmware(struct dchid_iface *iface, void *firmware, size_t size) +{ + struct { + u8 cmd; + u8 unk1; + u8 unk2; + u8 iface; + u64 addr; + u32 size; + } __packed msg = { + .cmd = CMD_SEND_FIRMWARE, + .unk1 = 2, + .unk2 = 0, + .iface = iface->index, + .size = size, + }; + dma_addr_t addr; + void *buf = dmam_alloc_coherent(iface->dchid->dev, size, &addr, GFP_KERNEL); + + if (IS_ERR_OR_NULL(buf)) + return buf ? PTR_ERR(buf) : -ENOMEM; + + msg.addr = addr; + memcpy(buf, firmware, size); + wmb(); + + return dchid_comm_cmd(iface->dchid, &msg, sizeof(msg)); +} + +static int dchid_get_firmware(struct dchid_iface *iface, void **firmware, size_t *size) +{ + int ret; + const char *fw_name; + const struct firmware *fw; + struct fw_header *hdr; + u8 *fw_data; + + ret = of_property_read_string(iface->of_node, "firmware-name", &fw_name); + if (ret) { + /* Firmware is only for some devices */ + *firmware = NULL; + *size = 0; + return 0; + } + + ret = request_firmware(&fw, fw_name, iface->dchid->dev); + if (ret) + return ret; + + hdr = (struct fw_header *)fw->data; + + if (hdr->magic != FW_MAGIC || hdr->version != FW_VER || + hdr->hdr_length < sizeof(*hdr) || hdr->hdr_length > fw->size || + (hdr->hdr_length + (size_t)hdr->data_length) > fw->size || + hdr->iface_offset >= hdr->data_length) { + dev_warn(iface->dchid->dev, "%s: invalid firmware header\n", + fw_name); + ret = -EINVAL; + goto done; + } + + fw_data = devm_kmemdup(iface->dchid->dev, fw->data + hdr->hdr_length, + hdr->data_length, GFP_KERNEL); + if (!fw_data) { + ret = -ENOMEM; + goto done; + } + + if (hdr->iface_offset) + fw_data[hdr->iface_offset] = iface->index; + + *firmware = fw_data; + *size = hdr->data_length; + +done: + release_firmware(fw); + return ret; +} + +static int dchid_request_gpio(struct dchid_iface *iface) +{ + char prop_name[MAX_GPIO_NAME + 16]; + + if (iface->gpio) + return 0; + + dev_info(iface->dchid->dev, "Requesting GPIO %s#%d: %s\n", + iface->name, iface->gpio_id, iface->gpio_name); + + snprintf(prop_name, sizeof(prop_name), "apple,%s", iface->gpio_name); + + iface->gpio = devm_gpiod_get_index(iface->dchid->dev, prop_name, 0, GPIOD_OUT_LOW); + + if (IS_ERR_OR_NULL(iface->gpio)) { + dev_err(iface->dchid->dev, "Failed to request GPIO %s-gpios\n", prop_name); + iface->gpio = NULL; + return -1; + } + + return 0; +} + +static int dchid_start_interface(struct dchid_iface *iface) +{ + void *fw; + size_t size; + int ret; + + if (iface->starting) { + dev_warn(iface->dchid->dev, "Interface %s is already starting", iface->name); + return -EINPROGRESS; + } + + dev_info(iface->dchid->dev, "Starting interface %s\n", iface->name); + + iface->starting = true; + + /* Look to see if we need firmware */ + ret = dchid_get_firmware(iface, &fw, &size); + if (ret < 0) + goto err; + + /* If we need a GPIO, make sure we have it. */ + if (iface->gpio_id) { + ret = dchid_request_gpio(iface); + if (ret < 0) + goto err; + } + + /* Only multi-touch has firmware */ + if (fw && size) { + + /* Send firmware to the device */ + dev_info(iface->dchid->dev, "Sending firmware for %s\n", iface->name); + ret = dchid_send_firmware(iface, fw, size); + if (ret < 0) { + dev_err(iface->dchid->dev, "Failed to send %s firmwareS", iface->name); + goto err; + } + + /* After loading firmware, multi-touch needs a reset */ + dev_info(iface->dchid->dev, "Resetting %s\n", iface->name); + dchid_reset_interface(iface, 0); + dchid_reset_interface(iface, 2); + } + + return 0; + +err: + iface->starting = false; + return ret; +} + +static int dchid_start(struct hid_device *hdev) +{ + struct dchid_iface *iface = hdev->driver_data; + + if (iface->keyboard_layout_id) { + int ret = device_create_file(&hdev->dev, &dev_attr_apple_layout_id); + if (ret) { + dev_warn(iface->dchid->dev, "Failed to create apple_layout_id: %d", ret); + iface->keyboard_layout_id = 0; + } + } + + return 0; +}; + +static void dchid_stop(struct hid_device *hdev) +{ + struct dchid_iface *iface = hdev->driver_data; + + if (iface->keyboard_layout_id) + device_remove_file(&hdev->dev, &dev_attr_apple_layout_id); +} + +static int dchid_open(struct hid_device *hdev) +{ + struct dchid_iface *iface = hdev->driver_data; + int ret; + + if (!completion_done(&iface->ready)) { + ret = dchid_start_interface(iface); + if (ret < 0) + return ret; + + if (!wait_for_completion_timeout(&iface->ready, msecs_to_jiffies(START_TIMEOUT_MS))) { + dev_err(iface->dchid->dev, "iface %s start timed out\n", iface->name); + return -ETIMEDOUT; + } + } + + iface->open = true; + return 0; +} + +static void dchid_close(struct hid_device *hdev) +{ + struct dchid_iface *iface = hdev->driver_data; + + iface->open = false; +} + +static int dchid_parse(struct hid_device *hdev) +{ + struct dchid_iface *iface = hdev->driver_data; + + return hid_parse_report(hdev, iface->hid_desc, iface->hid_desc_len); +} + +/* Note: buf excludes report number! For ease of fetching strings/etc. */ +static int dchid_get_report_cmd(struct dchid_iface *iface, u8 reportnum, void *buf, size_t len) +{ + int ret = dchid_cmd(iface, GROUP_CMD, REQ_GET_REPORT, &reportnum, 1, buf, len); + + return ret <= 0 ? ret : ret - 1; +} + +/* Note: buf includes report number! */ +static int dchid_set_report(struct dchid_iface *iface, void *buf, size_t len) +{ + return dchid_cmd(iface, GROUP_OUTPUT, REQ_SET_REPORT, buf, len, NULL, 0); +} + +static int dchid_raw_request(struct hid_device *hdev, + unsigned char reportnum, __u8 *buf, size_t len, + unsigned char rtype, int reqtype) +{ + struct dchid_iface *iface = hdev->driver_data; + + switch (reqtype) { + case HID_REQ_GET_REPORT: + buf[0] = reportnum; + return dchid_cmd(iface, GROUP_OUTPUT, REQ_GET_REPORT, &reportnum, 1, buf + 1, len - 1); + case HID_REQ_SET_REPORT: + return dchid_set_report(iface, buf, len); + default: + return -EIO; + } + + return 0; +} + +static struct hid_ll_driver dchid_ll = { + .start = &dchid_start, + .stop = &dchid_stop, + .open = &dchid_open, + .close = &dchid_close, + .parse = &dchid_parse, + .raw_request = &dchid_raw_request, +}; + +static void dchid_create_interface_work(struct work_struct *ws) +{ + struct dchid_iface *iface = container_of(ws, struct dchid_iface, create_work); + struct dockchannel_hid *dchid = iface->dchid; + struct hid_device *hid; + int ret; + + if (iface->hid) { + dev_warn(dchid->dev, "Interface %s already created!\n", + iface->name); + return; + } + + dev_info(dchid->dev, "New interface %s\n", iface->name); + + /* Start the interface. This is not the entire init process, as firmware is loaded later on device open. */ + ret = dchid_enable_interface(iface); + if (ret < 0) { + dev_warn(dchid->dev, "Failed to enable %s: %d\n", iface->name, ret); + return; + } + + iface->deferred = false; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) + return; + + snprintf(hid->name, sizeof(hid->name), "Apple MTP %s", iface->name); + snprintf(hid->phys, sizeof(hid->phys), "%s.%d (%s)", + dev_name(dchid->dev), iface->index, iface->name); + strscpy(hid->uniq, dchid->serial, sizeof(hid->uniq)); + + hid->ll_driver = &dchid_ll; + hid->bus = BUS_HOST; + hid->vendor = dchid->device_id.vendor_id; + hid->product = dchid->device_id.product_id; + hid->version = dchid->device_id.version_number; + hid->type = HID_TYPE_OTHER; + if (!strcmp(iface->name, "multi-touch")) { + hid->type = HID_TYPE_SPI_MOUSE; + } else if (!strcmp(iface->name, "keyboard")) { + u32 country_code = 0; + + hid->type = HID_TYPE_SPI_KEYBOARD; + + /* + * We have to get the country code from the device tree, since the + * device provides no reliable way to get this info. + */ + if (!of_property_read_u32(iface->of_node, "hid-country-code", &country_code)) + hid->country = country_code; + + of_property_read_u32(iface->of_node, "apple,keyboard-layout-id", + &iface->keyboard_layout_id); + } + + hid->dev.parent = iface->dchid->dev; + hid->driver_data = iface; + + iface->hid = hid; + + ret = hid_add_device(hid); + if (ret < 0) { + iface->hid = NULL; + hid_destroy_device(hid); + dev_warn(iface->dchid->dev, "Failed to register hid device %s", iface->name); + } +} + +static int dchid_create_interface(struct dchid_iface *iface) +{ + if (iface->creating) + return -EBUSY; + + iface->creating = true; + INIT_WORK(&iface->create_work, dchid_create_interface_work); + return queue_work(iface->dchid->new_iface_wq, &iface->create_work); +} + +static void dchid_handle_descriptor(struct dchid_iface *iface, void *hid_desc, size_t desc_len) +{ + if (iface->hid) { + dev_warn(iface->dchid->dev, "Tried to initialize already started interface %s!\n", + iface->name); + return; + } + + iface->hid_desc = devm_kmemdup(iface->dchid->dev, hid_desc, desc_len, GFP_KERNEL); + if (!iface->hid_desc) + return; + + iface->hid_desc_len = desc_len; +} + +static void dchid_handle_ready(struct dockchannel_hid *dchid, void *data, size_t length) +{ + struct dchid_iface *iface; + u8 *pkt = data; + u8 index; + int i, ret; + + if (length < 2) { + dev_err(dchid->dev, "Bad length for ready message: %zu\n", length); + return; + } + + index = pkt[1]; + + if (index >= MAX_INTERFACES) { + dev_err(dchid->dev, "Got ready notification for bad iface %d\n", index); + return; + } + + iface = dchid->ifaces[index]; + if (!iface) { + dev_err(dchid->dev, "Got ready notification for unknown iface %d\n", index); + return; + } + + dev_info(dchid->dev, "Interface %s is now ready\n", iface->name); + complete_all(&iface->ready); + + /* When STM is ready, grab global device info */ + if (!strcmp(iface->name, "stm")) { + ret = dchid_get_report_cmd(iface, STM_REPORT_ID, &dchid->device_id, + sizeof(dchid->device_id)); + if (ret < sizeof(dchid->device_id)) { + dev_warn(iface->dchid->dev, "Failed to get device ID from STM!\n"); + /* Fake it and keep going. Things might still work... */ + memset(&dchid->device_id, 0, sizeof(dchid->device_id)); + dchid->device_id.vendor_id = HOST_VENDOR_ID_APPLE; + } + ret = dchid_get_report_cmd(iface, STM_REPORT_SERIAL, dchid->serial, + sizeof(dchid->serial) - 1); + if (ret < 0) { + dev_warn(iface->dchid->dev, "Failed to get serial from STM!\n"); + dchid->serial[0] = 0; + } + + dchid->id_ready = true; + for (i = 0; i < MAX_INTERFACES; i++) { + if (!dchid->ifaces[i] || !dchid->ifaces[i]->deferred) + continue; + dchid_create_interface(dchid->ifaces[i]); + } + } +} + +static void dchid_handle_init(struct dockchannel_hid *dchid, void *data, size_t length) +{ + struct dchid_init_hdr *hdr = data; + struct dchid_iface *iface; + struct dchid_init_block_hdr *blk; + + if (length < sizeof(*hdr)) + return; + + iface = dchid_get_interface(dchid, hdr->iface, hdr->name); + if (!iface) + return; + + data += sizeof(*hdr); + length -= sizeof(*hdr); + + while (length >= sizeof(*blk)) { + blk = data; + data += sizeof(*blk); + length -= sizeof(*blk); + + if (blk->length > length) + break; + + switch (blk->type) { + case INIT_HID_DESCRIPTOR: + dchid_handle_descriptor(iface, data, blk->length); + break; + + case INIT_GPIO_REQUEST: { + struct dchid_gpio_request *req = data; + + if (sizeof(*req) > length) + break; + + if (iface->gpio_id) { + dev_err(dchid->dev, + "Cannot request more than one GPIO per interface!\n"); + break; + } + + strlcpy(iface->gpio_name, req->name, MAX_GPIO_NAME); + iface->gpio_id = req->id; + break; + } + + case INIT_TERMINATOR: + break; + + case INIT_PRODUCT_NAME: { + char *product = data; + + if (product[blk->length - 1] != 0) { + dev_warn(dchid->dev, "Unterminated product name for %s\n", + iface->name); + } else { + dev_info(dchid->dev, "Product name for %s: %s\n", + iface->name, product); + } + break; + } + + default: + dev_warn(dchid->dev, "Unknown init packet %d for %s\n", + blk->type, iface->name); + break; + } + + data += blk->length; + length -= blk->length; + + if (blk->type == INIT_TERMINATOR) + break; + } + + if (hdr->more_packets) + return; + + /* We need to enable STM first, since it'll give us the device IDs */ + if (iface->dchid->id_ready || !strcmp(iface->name, "stm")) { + dchid_create_interface(iface); + } else { + iface->deferred = true; + } +} + +static void dchid_handle_gpio(struct dockchannel_hid *dchid, void *data, size_t length) +{ + struct dchid_gpio_cmd *cmd = data; + struct dchid_iface *iface; + u32 retcode = 0xe000f00d; /* Give it a random Apple-style error code */ + struct dchid_gpio_ack *ack; + + if (length < sizeof(*cmd)) + return; + + if (cmd->iface >= MAX_INTERFACES || !(iface = dchid->ifaces[cmd->iface])) { + dev_err(dchid->dev, "Got GPIO command for bad inteface %d\n", cmd->iface); + goto err; + } + + if (dchid_request_gpio(iface) < 0) + goto err; + + if (!iface->gpio || cmd->gpio != iface->gpio_id) { + dev_err(dchid->dev, "Got GPIO command for bad GPIO %s#%d\n", + iface->name, cmd->gpio); + goto err; + } + + dev_info(dchid->dev, "GPIO command: %s#%d: %d\n", iface->name, cmd->gpio, cmd->cmd); + + switch (cmd->cmd) { + case 3: + /* Pulse. */ + gpiod_set_value_cansleep(iface->gpio, 1); + msleep(10); /* Random guess... */ + gpiod_set_value_cansleep(iface->gpio, 0); + retcode = 0; + break; + default: + dev_err(dchid->dev, "Unknown GPIO command %d\n", cmd->cmd ); + break; + } + +err: + /* Ack it */ + ack = kzalloc(sizeof(*ack) + length, GFP_KERNEL); + if (!ack) + return; + + ack->type = CMD_ACK_GPIO_CMD; + ack->retcode = retcode; + memcpy(ack->cmd, data, length); + + if (dchid_comm_cmd(dchid, ack, sizeof(*ack) + length) < 0) + dev_err(dchid->dev, "Failed to ACK GPIO command\n"); + + kfree(ack); +} + +static void dchid_handle_event(struct dockchannel_hid *dchid, void *data, size_t length) +{ + u8 *p = data; + switch (*p) { + case EVENT_INIT: + dchid_handle_init(dchid, data, length); + break; + case EVENT_READY: + dchid_handle_ready(dchid, data, length); + break; + case EVENT_GPIO_CMD: + dchid_handle_gpio(dchid, data, length); + break; + } +} + +static void dchid_handle_report(struct dchid_iface *iface, void *data, size_t length) +{ + struct dockchannel_hid *dchid = iface->dchid; + + if (!iface->hid) { + dev_warn(dchid->dev, "Report received but %s is not initialized!\n", iface->name); + return; + } + + if (!iface->open) + return; + + hid_input_report(iface->hid, HID_INPUT_REPORT, data, length, 1); +} + +static void dchid_packet_work(struct work_struct *ws) +{ + struct dchid_work *work = container_of(ws, struct dchid_work, work); + struct dchid_subhdr *shdr = (void *)work->data; + struct dockchannel_hid *dchid = work->iface->dchid; + int type = FIELD_GET(FLAGS_GROUP, shdr->flags); + u8 *payload = work->data + sizeof(*shdr); + + if (shdr->length + sizeof(*shdr) > work->hdr.length) { + dev_err(dchid->dev, "Bad sub header length (%d > %zu)\n", + shdr->length, work->hdr.length - sizeof(*shdr)); + return; + } + + switch (type) { + case GROUP_INPUT: + if (work->hdr.iface == IFACE_COMM) + dchid_handle_event(dchid, payload, shdr->length); + else + dchid_handle_report(work->iface, payload, shdr->length); + break; + default: + dev_err(dchid->dev, "Received unknown packet type %d\n", type); + break; + } + + kfree(work); +} + +static void dchid_handle_ack(struct dchid_iface *iface, struct dchid_hdr *hdr, void *data) +{ + struct dchid_subhdr *shdr = (void *)data; + u8 *payload = data + sizeof(*shdr); + + if (shdr->length + sizeof(*shdr) > hdr->length) { + dev_err(iface->dchid->dev, "Bad sub header length (%d > %ld)\n", + shdr->length, hdr->length - sizeof(*shdr)); + return; + } + if (shdr->flags != iface->out_flags) { + dev_err(iface->dchid->dev, + "Received unexpected flags 0x%x on ACK channel (expFected 0x%x)\n", + shdr->flags, iface->out_flags); + return; + } + + if (shdr->length < 1) { + dev_err(iface->dchid->dev, "Received length 0 output report ack\n"); + return; + } + if (iface->tx_seq != hdr->seq) { + dev_err(iface->dchid->dev, "Received ACK with bad seq (expected %d, got %d)\n", + iface->tx_seq, hdr->seq); + return; + } + if (iface->out_report != payload[0]) { + dev_err(iface->dchid->dev, "Received ACK with bad report (expected %d, got %d\n", + iface->out_report, payload[0]); + return; + } + + if (iface->resp_buf && iface->resp_size) + memcpy(iface->resp_buf, payload + 1, min((size_t)shdr->length - 1, iface->resp_size)); + + iface->resp_size = shdr->length; + iface->out_report = -1; + iface->retcode = shdr->retcode; + complete(&iface->out_complete); +} + +static void dchid_handle_packet(void *cookie, size_t avail) +{ + struct dockchannel_hid *dchid = cookie; + struct dchid_hdr hdr; + struct dchid_work *work; + struct dchid_iface *iface; + u32 checksum; + + if (dockchannel_recv(dchid->dc, &hdr, sizeof(hdr)) != sizeof(hdr)) { + dev_err(dchid->dev, "Read failed (header)\n"); + return; + } + + if (hdr.hdr_len != sizeof(hdr)) { + dev_err(dchid->dev, "Bad header length %d\n", hdr.hdr_len); + goto done; + } + + if (dockchannel_recv(dchid->dc, dchid->pkt_buf, hdr.length + 4) != (hdr.length + 4)) { + dev_err(dchid->dev, "Read failed (body)\n"); + goto done; + } + + checksum = dchid_checksum(&hdr, sizeof(hdr)); + checksum += dchid_checksum(dchid->pkt_buf, hdr.length + 4); + + if (checksum != 0xffffffff) { + dev_err(dchid->dev, "Checksum mismatch (iface %d): 0x%08x != 0xffffffff\n", + hdr.iface, checksum); + goto done; + } + + + if (hdr.iface >= MAX_INTERFACES) { + dev_err(dchid->dev, "Bad iface %d\n", hdr.iface); + } + + iface = dchid->ifaces[hdr.iface]; + + if (!iface) { + dev_err(dchid->dev, "Received packet for uninitialized iface %d\n", hdr.iface); + goto done; + } + + switch (hdr.channel) { + case DCHID_CHANNEL_CMD: + dchid_handle_ack(iface, &hdr, dchid->pkt_buf); + goto done; + case DCHID_CHANNEL_REPORT: + break; + default: + dev_warn(dchid->dev, "Unknown channel 0x%x, treating as report...\n", + hdr.channel); + break; + } + + work = kzalloc(sizeof(*work) + hdr.length, GFP_KERNEL); + if (!work) + return; + + work->hdr = hdr; + work->iface = iface; + memcpy(work->data, dchid->pkt_buf, hdr.length); + INIT_WORK(&work->work, dchid_packet_work); + + queue_work(iface->wq, &work->work); + +done: + dockchannel_await(dchid->dc, dchid_handle_packet, dchid, sizeof(struct dchid_hdr)); +} + +static int dockchannel_hid_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dockchannel_hid *dchid; + struct device_node *child, *helper; + struct platform_device *helper_pdev; + struct property *prop; + int ret; + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) + return ret; + + dchid = devm_kzalloc(dev, sizeof(*dchid), GFP_KERNEL); + if (!dchid) { + return -ENOMEM; + } + + dchid->dev = dev; + + /* + * First make sure all the GPIOs are available, in cased we need to defer. + * This is necessary because MTP will request them by name later, and by then + * it's too late to defer the probe. + */ + + for_each_child_of_node(dev->of_node, child) { + for_each_property_of_node(child, prop) { + size_t len = strlen(prop->name); + struct gpio_desc *gpio; + + if (len < 12 || strncmp("apple,", prop->name, 6) || + strcmp("-gpios", prop->name + len - 6)) + continue; + + gpio = fwnode_gpiod_get_index(&child->fwnode, prop->name, 0, GPIOD_ASIS, + prop->name); + if (IS_ERR_OR_NULL(gpio)) { + if (PTR_ERR(gpio) == -EPROBE_DEFER) { + of_node_put(child); + return -EPROBE_DEFER; + } + } else { + gpiod_put(gpio); + } + } + } + + /* + * Make sure we also have the MTP coprocessor available, and + * defer probe if the helper hasn't probed yet. + */ + helper = of_parse_phandle(dev->of_node, "apple,helper-cpu", 0); + if (!helper) { + dev_err(dev, "Missing apple,helper-cpu property"); + return -EINVAL; + } + + helper_pdev = of_find_device_by_node(helper); + of_node_put(helper); + if (!helper_pdev) { + dev_err(dev, "Failed to find helper device"); + return -EINVAL; + } + + dchid->helper_link = device_link_add(dev, &helper_pdev->dev, + DL_FLAG_AUTOREMOVE_CONSUMER); + put_device(&helper_pdev->dev); + if (!dchid->helper_link) { + dev_err(dev, "Failed to link to helper device"); + return -EINVAL; + } + + if (dchid->helper_link->supplier->links.status != DL_DEV_DRIVER_BOUND) + return -EPROBE_DEFER; + + /* Now it is safe to begin initializing */ + dchid->dc = dockchannel_init(pdev); + if (IS_ERR_OR_NULL(dchid->dc)) { + return PTR_ERR(dchid->dc); + } + dchid->new_iface_wq = alloc_workqueue("dchid-new", WQ_MEM_RECLAIM, 0); + if (!dchid->new_iface_wq) + return -ENOMEM; + + dchid->comm = dchid_get_interface(dchid, IFACE_COMM, "comm"); + if (!dchid->comm) { + dev_err(dchid->dev, "Failed to initialize comm interface"); + return -EIO; + } + + dev_info(dchid->dev, "Initialized, awaiting packets\n"); + dockchannel_await(dchid->dc, dchid_handle_packet, dchid, sizeof(struct dchid_hdr)); + + return 0; +} + +static int dockchannel_hid_remove(struct platform_device *pdev) +{ + BUG_ON(1); + return 0; +} + +static const struct of_device_id dockchannel_hid_of_match[] = { + { .compatible = "apple,dockchannel-hid" }, + {}, +}; +MODULE_DEVICE_TABLE(of, dockchannel_hid_of_match); +MODULE_FIRMWARE("apple/tpmtfw-*.bin"); + +static struct platform_driver dockchannel_hid_driver = { + .driver = { + .name = "dockchannel-hid", + .of_match_table = dockchannel_hid_of_match, + }, + .probe = dockchannel_hid_probe, + .remove = dockchannel_hid_remove, +}; +module_platform_driver(dockchannel_hid_driver); + +MODULE_DESCRIPTION("Apple DockChannel HID transport driver"); +MODULE_AUTHOR("Hector Martin "); +MODULE_LICENSE("Dual MIT/GPL"); From 17e6ec5b4474e9ffbe7c6d5652a2896062eaaeed Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 3 Jul 2022 23:33:37 +0900 Subject: [PATCH 0363/1020] soc: apple: Add RTKit helper driver This driver can be used for coprocessors that do some background task or communicate out-of-band, and do not do any mailbox I/O beyond the standard RTKit initialization. Signed-off-by: Hector Martin --- drivers/soc/apple/Kconfig | 14 +++ drivers/soc/apple/Makefile | 3 + drivers/soc/apple/rtkit-helper.c | 153 +++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 drivers/soc/apple/rtkit-helper.c diff --git a/drivers/soc/apple/Kconfig b/drivers/soc/apple/Kconfig index cceb514b05bbfa..4aeb027a533d0b 100644 --- a/drivers/soc/apple/Kconfig +++ b/drivers/soc/apple/Kconfig @@ -30,6 +30,20 @@ config APPLE_RTKIT Say 'y' here if you have an Apple SoC. +config APPLE_RTKIT_HELPER + tristate "Apple Generic RTKit helper co-processor" + depends on APPLE_RTKIT + depends on ARCH_APPLE || COMPILE_TEST + default ARCH_APPLE + help + Apple SoCs such as the M1 come with various co-processors running + their proprietary RTKit operating system. This option enables support + for a generic co-processor that does not implement any additional + in-band communications. It can be used for testing purposes, or for + coprocessors such as MTP that communicate over a different interface. + + Say 'y' here if you have an Apple SoC. + config APPLE_SART tristate "Apple SART DMA address filter" depends on ARCH_APPLE || COMPILE_TEST diff --git a/drivers/soc/apple/Makefile b/drivers/soc/apple/Makefile index 07d43360426110..8ae2890bc40b45 100644 --- a/drivers/soc/apple/Makefile +++ b/drivers/soc/apple/Makefile @@ -6,6 +6,9 @@ apple-mailbox-y = mailbox.o obj-$(CONFIG_APPLE_RTKIT) += apple-rtkit.o apple-rtkit-y = rtkit.o rtkit-crashlog.o +obj-$(CONFIG_APPLE_RTKIT_HELPER) += apple-rtkit-helper.o +apple-rtkit-helper-y = rtkit-helper.o + obj-$(CONFIG_APPLE_SART) += apple-sart.o apple-sart-y = sart.o diff --git a/drivers/soc/apple/rtkit-helper.c b/drivers/soc/apple/rtkit-helper.c new file mode 100644 index 00000000000000..c2817e12004af0 --- /dev/null +++ b/drivers/soc/apple/rtkit-helper.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple Generic RTKit helper coprocessor + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define APPLE_ASC_CPU_CONTROL 0x44 +#define APPLE_ASC_CPU_CONTROL_RUN BIT(4) + +struct apple_rtkit_helper { + struct device *dev; + struct apple_rtkit *rtk; + + void __iomem *asc_base; + + struct resource *sram; + void __iomem *sram_base; +}; + +static int apple_rtkit_helper_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr) +{ + struct apple_rtkit_helper *helper = cookie; + struct resource res = { + .start = bfr->iova, + .end = bfr->iova + bfr->size - 1, + .name = "rtkit_map", + }; + + if (!bfr->iova) { + bfr->buffer = dma_alloc_coherent(helper->dev, bfr->size, + &bfr->iova, GFP_KERNEL); + if (!bfr->buffer) + return -ENOMEM; + return 0; + } + + if (!helper->sram) { + dev_err(helper->dev, + "RTKit buffer request with no SRAM region: %pR", &res); + return -EFAULT; + } + + res.flags = helper->sram->flags; + + if (res.end < res.start || !resource_contains(helper->sram, &res)) { + dev_err(helper->dev, + "RTKit buffer request outside SRAM region: %pR", &res); + return -EFAULT; + } + + bfr->iomem = helper->sram_base + (res.start - helper->sram->start); + bfr->is_mapped = true; + + return 0; +} + +static void apple_rtkit_helper_shmem_destroy(void *cookie, struct apple_rtkit_shmem *bfr) +{ + // no-op +} + +static const struct apple_rtkit_ops apple_rtkit_helper_ops = { + .shmem_setup = apple_rtkit_helper_shmem_setup, + .shmem_destroy = apple_rtkit_helper_shmem_destroy, +}; + +static int apple_rtkit_helper_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct apple_rtkit_helper *helper; + int ret; + + /* 44 bits for addresses in standard RTKit requests */ + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(44)); + if (ret) + return ret; + + helper = devm_kzalloc(dev, sizeof(*helper), GFP_KERNEL); + if (!helper) + return -ENOMEM; + + helper->dev = dev; + platform_set_drvdata(pdev, helper); + + helper->asc_base = devm_platform_ioremap_resource_byname(pdev, "asc"); + if (IS_ERR(helper->asc_base)) + return PTR_ERR(helper->asc_base); + + helper->sram = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram"); + if (helper->sram) { + helper->sram_base = devm_ioremap_resource(dev, helper->sram); + if (IS_ERR(helper->sram_base)) + return dev_err_probe(dev, PTR_ERR(helper->sram_base), + "Failed to map SRAM region"); + } + + helper->rtk = + devm_apple_rtkit_init(dev, helper, NULL, 0, &apple_rtkit_helper_ops); + if (IS_ERR(helper->rtk)) + return dev_err_probe(dev, PTR_ERR(helper->rtk), + "Failed to intialize RTKit"); + + writel_relaxed(APPLE_ASC_CPU_CONTROL_RUN, + helper->asc_base + APPLE_ASC_CPU_CONTROL); + + /* Works for both wake and boot */ + ret = apple_rtkit_wake(helper->rtk); + if (ret != 0) + return dev_err_probe(dev, ret, "Failed to wake up coprocessor"); + + return 0; +} + +static int apple_rtkit_helper_remove(struct platform_device *pdev) +{ + struct apple_rtkit_helper *helper = platform_get_drvdata(pdev); + + if (apple_rtkit_is_running(helper->rtk)) + apple_rtkit_quiesce(helper->rtk); + + writel_relaxed(0, helper->asc_base + APPLE_ASC_CPU_CONTROL); + + return 0; +} + +static const struct of_device_id apple_rtkit_helper_of_match[] = { + { .compatible = "apple,rtk-helper-asc4" }, + {}, +}; +MODULE_DEVICE_TABLE(of, apple_rtkit_helper_of_match); + +static struct platform_driver apple_rtkit_helper_driver = { + .driver = { + .name = "rtkit-helper", + .of_match_table = apple_rtkit_helper_of_match, + }, + .probe = apple_rtkit_helper_probe, + .remove = apple_rtkit_helper_remove, +}; +module_platform_driver(apple_rtkit_helper_driver); + +MODULE_AUTHOR("Hector Martin "); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("Apple RTKit helper driver"); From 149fa803363b614257fb8a9f7e973f271ae31d66 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 11 Dec 2022 20:08:45 +0100 Subject: [PATCH 0364/1020] HID: transport: spi: Check status message after transmits Probably pointless but might be helpful to debug issues. Gets rid of 'spi_hid_apple_status_ok' is unused compiler warning. Signed-off-by: Janne Grunau --- drivers/hid/spi-hid/spi-hid-apple-core.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/hid/spi-hid/spi-hid-apple-core.c b/drivers/hid/spi-hid/spi-hid-apple-core.c index cd018df4f38715..29317a824c7270 100644 --- a/drivers/hid/spi-hid/spi-hid-apple-core.c +++ b/drivers/hid/spi-hid/spi-hid-apple-core.c @@ -248,7 +248,17 @@ static int spihid_apple_request(struct spihid_apple *spihid, u8 target, u8 unk0, pkt->crc16 = cpu_to_le16(crc16(0, spihid->tx_buf, offsetof(struct spihid_transfer_packet, crc16))); + memset(spihid->status_buf, 0, sizeof(spi_hid_apple_status_ok)); + err = spi_sync(spihid->spidev, &spihid->tx_msg); + + if (memcmp(spihid->status_buf, spi_hid_apple_status_ok, + sizeof(spi_hid_apple_status_ok))) { + u8 *b = spihid->status_buf; + dev_warn_ratelimited(&spihid->spidev->dev, "status message " + "mismatch: %02x %02x %02x %02x\n", + b[0], b[1], b[2], b[3]); + } mutex_unlock(&spihid->tx_lock); if (err < 0) return err; From 0b3cd83ad08d004dd2c22adc31ea922eb5facea7 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 11 Dec 2022 22:56:16 +0100 Subject: [PATCH 0365/1020] HID: magicmouse: Add .reset_resume for SPI trackpads The trackpad has to request multi touch reports during resume. Signed-off-by: Janne Grunau --- drivers/hid/hid-magicmouse.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index d04058e3ee39cb..fc6db10d8d3800 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -1271,6 +1271,16 @@ static const struct hid_device_id magic_mice[] = { }; MODULE_DEVICE_TABLE(hid, magic_mice); +#ifdef CONFIG_PM +static int magicmouse_reset_resume(struct hid_device *hdev) +{ + if (hdev->bus == BUS_SPI) + return magicmouse_enable_multitouch(hdev); + + return 0; +} +#endif + static struct hid_driver magicmouse_driver = { .name = "magicmouse", .id_table = magic_mice, @@ -1281,6 +1291,10 @@ static struct hid_driver magicmouse_driver = { .event = magicmouse_event, .input_mapping = magicmouse_input_mapping, .input_configured = magicmouse_input_configured, +#ifdef CONFIG_PM + .reset_resume = magicmouse_reset_resume, +#endif + }; module_hid_driver(magicmouse_driver); From 89965606f934bcff823df60ea7d5abe62e996b8b Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 11 Dec 2022 23:24:41 +0100 Subject: [PATCH 0366/1020] HID: transport: spi: Add suspend support Working suspend and resume. The keyboard can not yet used as wakeup source. Most likely caused by missing irq_set_wake support in the gpio/pinctrl driver. Signed-off-by: Janne Grunau --- drivers/hid/spi-hid/spi-hid-apple-core.c | 89 ++++++++++++++++++++++++ drivers/hid/spi-hid/spi-hid-apple-of.c | 18 ++++- drivers/hid/spi-hid/spi-hid-apple.h | 4 ++ 3 files changed, 110 insertions(+), 1 deletion(-) diff --git a/drivers/hid/spi-hid/spi-hid-apple-core.c b/drivers/hid/spi-hid/spi-hid-apple-core.c index 29317a824c7270..787dcd9ceb4ffd 100644 --- a/drivers/hid/spi-hid/spi-hid-apple-core.c +++ b/drivers/hid/spi-hid/spi-hid-apple-core.c @@ -115,6 +115,10 @@ struct spihid_apple { /* state tracking flags */ bool status_booted; + +#ifdef IRQ_WAKE_SUPPORT + bool irq_wake_enabled; +#endif }; /** @@ -1035,6 +1039,91 @@ void spihid_apple_core_shutdown(struct spi_device *spi) } EXPORT_SYMBOL_GPL(spihid_apple_core_shutdown); +#ifdef CONFIG_PM_SLEEP +static int spihid_apple_core_suspend(struct device *dev) +{ + int ret; +#ifdef IRQ_WAKE_SUPPORT + int wake_status; +#endif + struct spihid_apple *spihid = spi_get_drvdata(to_spi_device(dev)); + + if (spihid->tp.hid) { + ret = hid_driver_suspend(spihid->tp.hid, PMSG_SUSPEND); + if (ret < 0) + return ret; + } + + if (spihid->kbd.hid) { + ret = hid_driver_suspend(spihid->kbd.hid, PMSG_SUSPEND); + if (ret < 0) { + if (spihid->tp.hid) + hid_driver_resume(spihid->tp.hid); + return ret; + } + } + + /* Save some power */ + spihid->ops->disable_irq(spihid->ops); + +#ifdef IRQ_WAKE_SUPPORT + if (device_may_wakeup(dev)) { + wake_status = spihid->ops->enable_irq_wake(spihid->ops); + if (!wake_status) + spihid->irq_wake_enabled = true; + else + dev_warn(dev, "Failed to enable irq wake: %d\n", + wake_status); + } else { + spihid->ops->power_off(spihid->ops); + } +#else + spihid->ops->power_off(spihid->ops); +#endif + + return 0; +} + +static int spihid_apple_core_resume(struct device *dev) +{ + int ret_tp = 0, ret_kbd = 0; + struct spihid_apple *spihid = spi_get_drvdata(to_spi_device(dev)); +#ifdef IRQ_WAKE_SUPPORT + int wake_status; + + if (!device_may_wakeup(dev)) { + spihid->ops->power_on(spihid->ops); + } else if (spihid->irq_wake_enabled) { + wake_status = spihid->ops->disable_irq_wake(spihid->ops); + if (!wake_status) + spihid->irq_wake_enabled = false; + else + dev_warn(dev, "Failed to disable irq wake: %d\n", + wake_status); + } +#endif + + spihid->ops->enable_irq(spihid->ops); + spihid->ops->power_on(spihid->ops); + + if (spihid->tp.hid) + ret_tp = hid_driver_reset_resume(spihid->tp.hid); + if (spihid->kbd.hid) + ret_kbd = hid_driver_reset_resume(spihid->kbd.hid); + + if (ret_tp < 0) + return ret_tp; + + return ret_kbd; +} +#endif + +const struct dev_pm_ops spihid_apple_core_pm = { + SET_SYSTEM_SLEEP_PM_OPS(spihid_apple_core_suspend, + spihid_apple_core_resume) +}; +EXPORT_SYMBOL_GPL(spihid_apple_core_pm); + MODULE_DESCRIPTION("Apple SPI HID transport driver"); MODULE_AUTHOR("Janne Grunau "); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/spi-hid/spi-hid-apple-of.c b/drivers/hid/spi-hid/spi-hid-apple-of.c index f1380bfc52672e..6531f15e2516e9 100644 --- a/drivers/hid/spi-hid/spi-hid-apple-of.c +++ b/drivers/hid/spi-hid/spi-hid-apple-of.c @@ -65,6 +65,20 @@ static int spihid_apple_of_disable_irq(struct spihid_apple_ops *ops) return 0; } +static int spihid_apple_of_enable_irq_wake(struct spihid_apple_ops *ops) +{ + struct spihid_apple_of *sh_of = container_of(ops, struct spihid_apple_of, ops); + + return enable_irq_wake(sh_of->irq); +} + +static int spihid_apple_of_disable_irq_wake(struct spihid_apple_ops *ops) +{ + struct spihid_apple_of *sh_of = container_of(ops, struct spihid_apple_of, ops); + + return disable_irq_wake(sh_of->irq); +} + static int spihid_apple_of_probe(struct spi_device *spi) { struct device *dev = &spi->dev; @@ -79,6 +93,8 @@ static int spihid_apple_of_probe(struct spi_device *spi) spihid_of->ops.power_off = spihid_apple_of_power_off; spihid_of->ops.enable_irq = spihid_apple_of_enable_irq; spihid_of->ops.disable_irq = spihid_apple_of_disable_irq; + spihid_of->ops.enable_irq_wake = spihid_apple_of_enable_irq_wake; + spihid_of->ops.disable_irq_wake = spihid_apple_of_disable_irq_wake; spihid_of->enable_gpio = devm_gpiod_get_index(dev, "spien", 0, 0); if (IS_ERR(spihid_of->enable_gpio)) { @@ -120,7 +136,7 @@ MODULE_DEVICE_TABLE(spi, spihid_apple_of_id); static struct spi_driver spihid_apple_of_driver = { .driver = { .name = "spi-hid-apple-of", - //.pm = &spi_hid_apple_of_pm, + .pm = &spihid_apple_core_pm, .owner = THIS_MODULE, .of_match_table = of_match_ptr(spihid_apple_of_match), }, diff --git a/drivers/hid/spi-hid/spi-hid-apple.h b/drivers/hid/spi-hid/spi-hid-apple.h index 2d9554e8a5f819..9abecd1ba78028 100644 --- a/drivers/hid/spi-hid/spi-hid-apple.h +++ b/drivers/hid/spi-hid/spi-hid-apple.h @@ -20,6 +20,8 @@ struct spihid_apple_ops { int (*power_off)(struct spihid_apple_ops *ops); int (*enable_irq)(struct spihid_apple_ops *ops); int (*disable_irq)(struct spihid_apple_ops *ops); + int (*enable_irq_wake)(struct spihid_apple_ops *ops); + int (*disable_irq_wake)(struct spihid_apple_ops *ops); }; irqreturn_t spihid_apple_core_irq(int irq, void *data); @@ -28,4 +30,6 @@ int spihid_apple_core_probe(struct spi_device *spi, struct spihid_apple_ops *ops void spihid_apple_core_remove(struct spi_device *spi); void spihid_apple_core_shutdown(struct spi_device *spi); +extern const struct dev_pm_ops spihid_apple_core_pm; + #endif /* SPI_HID_APPLE_H */ From a838be302594886505998a647ab32e40d08c609b Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 10 Apr 2023 22:44:44 +0900 Subject: [PATCH 0367/1020] HID: Bump maximum report size to 16384 This maximum is arbitrary. Recent Apple devices have some vendor-defined reports with 16384 here which fail to parse without this, so let's bump it to that. This value is used as follows: report->size += parser->global.report_size * parser->global.report_count; [...] /* Total size check: Allow for possible report index byte */ if (report->size > (max_buffer_size - 1) << 3) { hid_err(parser->device, "report is too long\n"); return -1; } All of these fields are unsigned integers, and report_count is bounded by HID_MAX_USAGES (12288). Therefore, as long as the respective maximums do not overflow an unsigned integer (let's say a signed integer just in case), we're safe. This holds for 16384. Signed-off-by: Hector Martin --- drivers/hid/hid-core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 149c53b932bea8..5a56f196dabc7d 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -436,7 +436,10 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: parser->global.report_size = item_udata(item); - if (parser->global.report_size > 256) { + /* Arbitrary maximum. Some Apple devices have 16384 here. + * This * HID_MAX_USAGES must fit in a signed integer. + */ + if (parser->global.report_size > 16384) { hid_err(parser->device, "invalid report_size %d\n", parser->global.report_size); return -1; From 436041a76365f1393e58de42d04563ad9b65397a Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 30 Apr 2023 23:48:45 +0900 Subject: [PATCH 0368/1020] HID: magicmouse: Handle touch controller resets on SPI devices On at least some SPI devices (e.g. recent Apple Silicon machines), the Broadcom touch controller is prone to crashing. When this happens, the STM eventually notices and resets it. It then notifies the driver via HID report 0x60, and the driver needs to re-enable MT mode to make things work again. This poses an additional issue: the hidinput core will close the low-level transport while the device is closed, which can cause us to miss a reset notification. To fix this, override the input open/close callbacks and send the MT enable every time the HID device is opened, instead of only once on probe. This should increase general robustness, even if the reset mechanism doesn't work for some reason, so it's worth doing it for USB devices too. MTP devices are exempt since they do not require the MT enable at all. Signed-off-by: Hector Martin --- drivers/hid/hid-magicmouse.c | 203 +++++++++++++++++++++++------------ 1 file changed, 133 insertions(+), 70 deletions(-) diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index fc6db10d8d3800..335da031e934f7 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -60,6 +60,7 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie #define MOUSE2_REPORT_ID 0x12 #define DOUBLE_REPORT_ID 0xf7 #define SPI_REPORT_ID 0x02 +#define SPI_RESET_REPORT_ID 0x60 #define MTP_REPORT_ID 0x75 #define USB_BATTERY_TIMEOUT_MS 60000 @@ -173,6 +174,97 @@ struct magicmouse_sc { struct magicmouse_input_ops input_ops; }; +static int magicmouse_enable_multitouch(struct hid_device *hdev) +{ + const u8 *feature; + const u8 feature_mt[] = { 0xD7, 0x01 }; + const u8 feature_mt_mouse2[] = { 0xF1, 0x02, 0x01 }; + const u8 feature_mt_trackpad2_usb[] = { 0x02, 0x01 }; + const u8 feature_mt_trackpad2_bt[] = { 0xF1, 0x02, 0x01 }; + u8 *buf; + int ret; + int feature_size; + + if (hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { + if (hdev->vendor == BT_VENDOR_ID_APPLE) { + feature_size = sizeof(feature_mt_trackpad2_bt); + feature = feature_mt_trackpad2_bt; + } else { /* USB_VENDOR_ID_APPLE */ + feature_size = sizeof(feature_mt_trackpad2_usb); + feature = feature_mt_trackpad2_usb; + } + } else if (hdev->vendor == SPI_VENDOR_ID_APPLE) { + feature_size = sizeof(feature_mt_trackpad2_usb); + feature = feature_mt_trackpad2_usb; + } else if (hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) { + feature_size = sizeof(feature_mt_mouse2); + feature = feature_mt_mouse2; + } else { + feature_size = sizeof(feature_mt); + feature = feature_mt; + } + + buf = kmemdup(feature, feature_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = hid_hw_raw_request(hdev, buf[0], buf, feature_size, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + kfree(buf); + return ret; +} + +static void magicmouse_enable_mt_work(struct work_struct *work) +{ + struct magicmouse_sc *msc = + container_of(work, struct magicmouse_sc, work.work); + int ret; + + ret = magicmouse_enable_multitouch(msc->hdev); + if (ret < 0) + hid_err(msc->hdev, "unable to request touch data (%d)\n", ret); +} + +static int magicmouse_open(struct input_dev *dev) +{ + struct hid_device *hdev = input_get_drvdata(dev); + struct magicmouse_sc *msc = hid_get_drvdata(hdev); + int ret; + + ret = hid_hw_open(hdev); + if (ret) + return ret; + + /* + * Some devices repond with 'invalid report id' when feature + * report switching it into multitouch mode is sent to it. + * + * This results in -EIO from the _raw low-level transport callback, + * but there seems to be no other way of switching the mode. + * Thus the super-ugly hacky success check below. + */ + ret = magicmouse_enable_multitouch(hdev); + if (ret == -EIO && hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) { + schedule_delayed_work(&msc->work, msecs_to_jiffies(500)); + return 0; + } + if (ret < 0) + hid_err(hdev, "unable to request touch data (%d)\n", ret); + + /* + * MT enable is usually not required after the first time, so don't + * consider it fatal. + */ + return 0; +} + +static void magicmouse_close(struct input_dev *dev) +{ + struct hid_device *hdev = input_get_drvdata(dev); + + hid_hw_close(hdev); +} + static int magicmouse_firm_touch(struct magicmouse_sc *msc) { int touch = -1; @@ -694,12 +786,19 @@ static int magicmouse_raw_event_mtp(struct hid_device *hdev, static int magicmouse_raw_event_spi(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { + struct magicmouse_sc *msc = hid_get_drvdata(hdev); const size_t hdr_sz = sizeof(struct tp_mouse_report); - if (size < hdr_sz) + if (!size) return 0; - if (data[0] != TRACKPAD2_USB_REPORT_ID) + if (data[0] == SPI_RESET_REPORT_ID) { + hid_info(hdev, "Touch controller was reset, re-enabling touch mode\n"); + schedule_delayed_work(&msc->work, msecs_to_jiffies(10)); + return 1; + } + + if (data[0] != TRACKPAD2_USB_REPORT_ID || size < hdr_sz) return 0; return magicmouse_raw_event_mtp(hdev, report, data + hdr_sz, size - hdr_sz); @@ -881,10 +980,17 @@ static int magicmouse_setup_input_usb(struct input_dev *input, */ __clear_bit(EV_REP, input->evbit); + /* + * This isn't strictly speaking needed for USB, but enabling MT on + * device open is probably more robust than only doing it once on probe + * even if USB devices are not known to suffer from the SPI reset issue. + */ + input->open = magicmouse_open; + input->close = magicmouse_close; return 0; } -static int magicmouse_setup_input_spi(struct input_dev *input, +static int magicmouse_setup_input_mtp(struct input_dev *input, struct hid_device *hdev) { int error; @@ -957,6 +1063,25 @@ static int magicmouse_setup_input_spi(struct input_dev *input, return 0; } +static int magicmouse_setup_input_spi(struct input_dev *input, + struct hid_device *hdev) +{ + int ret = magicmouse_setup_input_mtp(input, hdev); + if (ret) + return ret; + + /* + * Override the default input->open function to send the MT + * enable every time the device is opened. This ensures it works + * even if we missed a reset event due to the device being closed. + * input->close is overridden for symmetry. + */ + input->open = magicmouse_open; + input->close = magicmouse_close; + + return 0; +} + static int magicmouse_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) @@ -993,57 +1118,6 @@ static int magicmouse_input_configured(struct hid_device *hdev, return 0; } -static int magicmouse_enable_multitouch(struct hid_device *hdev) -{ - const u8 *feature; - const u8 feature_mt[] = { 0xD7, 0x01 }; - const u8 feature_mt_mouse2[] = { 0xF1, 0x02, 0x01 }; - const u8 feature_mt_trackpad2_usb[] = { 0x02, 0x01 }; - const u8 feature_mt_trackpad2_bt[] = { 0xF1, 0x02, 0x01 }; - u8 *buf; - int ret; - int feature_size; - - if (hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { - if (hdev->vendor == BT_VENDOR_ID_APPLE) { - feature_size = sizeof(feature_mt_trackpad2_bt); - feature = feature_mt_trackpad2_bt; - } else { /* USB_VENDOR_ID_APPLE */ - feature_size = sizeof(feature_mt_trackpad2_usb); - feature = feature_mt_trackpad2_usb; - } - } else if (hdev->vendor == SPI_VENDOR_ID_APPLE) { - feature_size = sizeof(feature_mt_trackpad2_usb); - feature = feature_mt_trackpad2_usb; - } else if (hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) { - feature_size = sizeof(feature_mt_mouse2); - feature = feature_mt_mouse2; - } else { - feature_size = sizeof(feature_mt); - feature = feature_mt; - } - - buf = kmemdup(feature, feature_size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = hid_hw_raw_request(hdev, buf[0], buf, feature_size, - HID_FEATURE_REPORT, HID_REQ_SET_REPORT); - kfree(buf); - return ret; -} - -static void magicmouse_enable_mt_work(struct work_struct *work) -{ - struct magicmouse_sc *msc = - container_of(work, struct magicmouse_sc, work.work); - int ret; - - ret = magicmouse_enable_multitouch(msc->hdev); - if (ret < 0) - hid_err(msc->hdev, "unable to request touch data (%d)\n", ret); -} - static int magicmouse_fetch_battery(struct hid_device *hdev) { #ifdef CONFIG_HID_BATTERY_STRENGTH @@ -1103,7 +1177,7 @@ static int magicmouse_probe(struct hid_device *hdev, // conflicts with the report ID. if (id->bus == BUS_HOST) { msc->input_ops.raw_event = magicmouse_raw_event_mtp; - msc->input_ops.setup_input = magicmouse_setup_input_spi; + msc->input_ops.setup_input = magicmouse_setup_input_mtp; } else if (id->bus == BUS_SPI) { msc->input_ops.raw_event = magicmouse_raw_event_spi; msc->input_ops.setup_input = magicmouse_setup_input_spi; @@ -1183,21 +1257,10 @@ static int magicmouse_probe(struct hid_device *hdev, if (id->bus == BUS_HOST) return 0; - /* - * Some devices repond with 'invalid report id' when feature - * report switching it into multitouch mode is sent to it. - * - * This results in -EIO from the _raw low-level transport callback, - * but there seems to be no other way of switching the mode. - * Thus the super-ugly hacky success check below. - */ - ret = magicmouse_enable_multitouch(hdev); - if (ret != -EIO && ret < 0) { - hid_err(hdev, "unable to request touch data (%d)\n", ret); - goto err_stop_hw; - } - if (ret == -EIO && id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) { - schedule_delayed_work(&msc->work, msecs_to_jiffies(500)); + /* SPI devices need to watch for reset events to re-send the MT enable */ + if (id->bus == BUS_SPI) { + report = hid_register_report(hdev, HID_INPUT_REPORT, SPI_RESET_REPORT_ID, 0); + report->size = 2; } return 0; From 77a6544b30d672ffc7e217e1cf4d0eaa4989c458 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 3 Dec 2023 20:47:04 +0900 Subject: [PATCH 0369/1020] fixup! hid: Add Apple DockChannel HID transport driver --- drivers/hid/dockchannel-hid/dockchannel-hid.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/hid/dockchannel-hid/dockchannel-hid.c b/drivers/hid/dockchannel-hid/dockchannel-hid.c index 6589917664c4df..bb70e342b8aac6 100644 --- a/drivers/hid/dockchannel-hid/dockchannel-hid.c +++ b/drivers/hid/dockchannel-hid/dockchannel-hid.c @@ -43,10 +43,6 @@ struct dchid_hdr { #define FLAGS_GROUP GENMASK(7, 6) #define FLAGS_REQ GENMASK(5, 0) -#define GROUP_INPUT 0 -#define GROUP_OUTPUT 1 -#define GROUP_CMD 2 - #define REQ_SET_REPORT 0 #define REQ_GET_REPORT 1 @@ -360,7 +356,7 @@ static int dchid_cmd(struct dchid_iface *iface, u32 type, u32 req, static int dchid_comm_cmd(struct dockchannel_hid *dchid, void *cmd, size_t size) { - return dchid_cmd(dchid->comm, GROUP_CMD, REQ_SET_REPORT, cmd, size, NULL, 0); + return dchid_cmd(dchid->comm, HID_FEATURE_REPORT, REQ_SET_REPORT, cmd, size, NULL, 0); } static int dchid_enable_interface(struct dchid_iface *iface) @@ -590,7 +586,7 @@ static int dchid_parse(struct hid_device *hdev) /* Note: buf excludes report number! For ease of fetching strings/etc. */ static int dchid_get_report_cmd(struct dchid_iface *iface, u8 reportnum, void *buf, size_t len) { - int ret = dchid_cmd(iface, GROUP_CMD, REQ_GET_REPORT, &reportnum, 1, buf, len); + int ret = dchid_cmd(iface, HID_FEATURE_REPORT, REQ_GET_REPORT, &reportnum, 1, buf, len); return ret <= 0 ? ret : ret - 1; } @@ -598,7 +594,7 @@ static int dchid_get_report_cmd(struct dchid_iface *iface, u8 reportnum, void *b /* Note: buf includes report number! */ static int dchid_set_report(struct dchid_iface *iface, void *buf, size_t len) { - return dchid_cmd(iface, GROUP_OUTPUT, REQ_SET_REPORT, buf, len, NULL, 0); + return dchid_cmd(iface, HID_OUTPUT_REPORT, REQ_SET_REPORT, buf, len, NULL, 0); } static int dchid_raw_request(struct hid_device *hdev, @@ -610,7 +606,7 @@ static int dchid_raw_request(struct hid_device *hdev, switch (reqtype) { case HID_REQ_GET_REPORT: buf[0] = reportnum; - return dchid_cmd(iface, GROUP_OUTPUT, REQ_GET_REPORT, &reportnum, 1, buf + 1, len - 1); + return dchid_cmd(iface, rtype, REQ_GET_REPORT, &reportnum, 1, buf + 1, len - 1); case HID_REQ_SET_REPORT: return dchid_set_report(iface, buf, len); default: @@ -965,7 +961,7 @@ static void dchid_packet_work(struct work_struct *ws) } switch (type) { - case GROUP_INPUT: + case HID_INPUT_REPORT: if (work->hdr.iface == IFACE_COMM) dchid_handle_event(dchid, payload, shdr->length); else From 4146286222af8943f78543f768cbb3e58ef53aed Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 3 Dec 2023 23:10:21 +0900 Subject: [PATCH 0370/1020] HID: transport: spi: Implement GET FEATURE This is used for fetching trackpad dimensions. Signed-off-by: Hector Martin --- drivers/hid/spi-hid/spi-hid-apple-core.c | 51 ++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/drivers/hid/spi-hid/spi-hid-apple-core.c b/drivers/hid/spi-hid/spi-hid-apple-core.c index 787dcd9ceb4ffd..bfcae013cb5cc2 100644 --- a/drivers/hid/spi-hid/spi-hid-apple-core.c +++ b/drivers/hid/spi-hid/spi-hid-apple-core.c @@ -65,6 +65,8 @@ struct spihid_interface { u32 max_input_report_len; u32 max_output_report_len; u8 name[32]; + u8 reply_buf[SPIHID_DESC_MAX]; + u32 reply_len; bool ready; }; @@ -327,6 +329,7 @@ static int apple_ll_raw_request(struct hid_device *hdev, { struct spihid_interface *idev = hdev->driver_data; struct spihid_apple *spihid = spihid_get_data(idev); + int ret; dev_dbg(&spihid->spidev->dev, "apple_ll_raw_request: device:%u reportnum:%hhu rtype:%hhu", @@ -334,7 +337,25 @@ static int apple_ll_raw_request(struct hid_device *hdev, switch (reqtype) { case HID_REQ_GET_REPORT: - return -EINVAL; // spihid_get_raw_report(); + if (rtype != HID_FEATURE_REPORT) + return -EINVAL; + + idev->reply_len = 0; + ret = spihid_apple_request(spihid, idev->id, 0x32, reportnum, 0x00, len, NULL, 0); + if (ret < 0) + return ret; + + ret = wait_event_interruptible_timeout(spihid->wait, idev->reply_len, + SPIHID_DEF_WAIT); + if (ret == 0) + ret = -ETIMEDOUT; + if (ret < 0) { + dev_err(&spihid->spidev->dev, "waiting for get report failed: %d", ret); + return ret; + } + memcpy(buf, idev->reply_buf, max_t(size_t, len, idev->reply_len)); + return idev->reply_len; + case HID_REQ_SET_REPORT: if (buf[0] != reportnum) return -EINVAL; @@ -606,7 +627,27 @@ static bool spihid_process_iface_hid_report_desc(struct spihid_apple *spihid, return true; } -static bool spihid_process_response(struct spihid_apple *spihid, +static bool spihid_process_iface_get_report(struct spihid_apple *spihid, + u32 device, u8 report, + u8 *payload, size_t len) +{ + struct spihid_interface *iface = spihid_get_iface(spihid, device); + + if (!iface) + return false; + + if (len > sizeof(iface->reply_buf) || len < 1) + return false; + + memcpy(iface->reply_buf, payload, len); + iface->reply_len = len; + + wake_up_interruptible(&spihid->wait); + + return true; +} + +static bool spihid_process_response(struct spihid_apple *spihid, u32 device, struct spihid_msg_hdr *hdr, u8 *payload, size_t len) { @@ -626,6 +667,10 @@ static bool spihid_process_response(struct spihid_apple *spihid, } } + if (hdr->unknown0 == 0x32) { + return spihid_process_iface_get_report(spihid, device, hdr->unknown1, payload, len); + } + return false; } @@ -653,7 +698,7 @@ static void spihid_process_message(struct spihid_apple *spihid, u8 *data, payload, le16_to_cpu(hdr->length)); break; case SPIHID_WRITE_PACKET: - handled = spihid_process_response(spihid, hdr, payload, + handled = spihid_process_response(spihid, device, hdr, payload, le16_to_cpu(hdr->length)); break; default: From e5a0411a87aa56850b48435ac537b3011e21fde0 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 3 Dec 2023 21:08:17 +0900 Subject: [PATCH 0371/1020] HID: magicmouse: Query device dimensions via HID report For SPI/MTP trackpads, query the dimensions via HID report instead of hardcoding values. TODO: Does this work for the USB/BT devices? Maybe we can get rid of the hardcoded sizes everywhere? Signed-off-by: Hector Martin --- drivers/hid/hid-magicmouse.c | 102 +++++++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 21 deletions(-) diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 335da031e934f7..831625e22bd342 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -62,6 +62,7 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie #define SPI_REPORT_ID 0x02 #define SPI_RESET_REPORT_ID 0x60 #define MTP_REPORT_ID 0x75 +#define SENSOR_DIMENSIONS_REPORT_ID 0xd9 #define USB_BATTERY_TIMEOUT_MS 60000 #define MAX_CONTACTS 16 @@ -116,6 +117,7 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie #define TRACKPAD2_RES_Y \ ((TRACKPAD2_MAX_Y - TRACKPAD2_MIN_Y) / (TRACKPAD2_DIMENSION_Y / 100)) +/* These are fallback values, since the real values will be queried from the device. */ #define J314_TP_DIMENSION_X (float)13000 #define J314_TP_MIN_X -5900 #define J314_TP_MAX_X 6500 @@ -139,6 +141,7 @@ struct magicmouse_input_ops { * struct magicmouse_sc - Tracks Magic Mouse-specific data. * @input: Input device through which we report events. * @quirks: Currently unused. + * @query_dimensions: Whether to query and update dimensions on first open * @ntouches: Number of touches in most recent touch report. * @scroll_accel: Number of consecutive scroll motions. * @scroll_jiffies: Time of last scroll motion. @@ -151,6 +154,7 @@ struct magicmouse_input_ops { struct magicmouse_sc { struct input_dev *input; unsigned long quirks; + bool query_dimensions; int ntouches; int scroll_accel; @@ -174,6 +178,11 @@ struct magicmouse_sc { struct magicmouse_input_ops input_ops; }; +static inline int le16_to_int(__le16 x) +{ + return (signed short)le16_to_cpu(x); +} + static int magicmouse_enable_multitouch(struct hid_device *hdev) { const u8 *feature; @@ -242,19 +251,71 @@ static int magicmouse_open(struct input_dev *dev) * This results in -EIO from the _raw low-level transport callback, * but there seems to be no other way of switching the mode. * Thus the super-ugly hacky success check below. + * + * MTP devices do not need this. */ - ret = magicmouse_enable_multitouch(hdev); - if (ret == -EIO && hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) { - schedule_delayed_work(&msc->work, msecs_to_jiffies(500)); - return 0; + if (hdev->bus != BUS_HOST) { + ret = magicmouse_enable_multitouch(hdev); + if (ret == -EIO && hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) { + schedule_delayed_work(&msc->work, msecs_to_jiffies(500)); + return 0; + } + if (ret < 0) + hid_err(hdev, "unable to request touch data (%d)\n", ret); } - if (ret < 0) - hid_err(hdev, "unable to request touch data (%d)\n", ret); - /* * MT enable is usually not required after the first time, so don't * consider it fatal. */ + + /* + * For Apple Silicon trackpads, we want to query the dimensions on + * device open. This is because doing so requires the firmware, but + * we don't want to force a firmware load until the device is opened + * for the first time. So do that here and update the input properties + * just in time before userspace queries them. + */ + if (msc->query_dimensions) { + struct input_dev *input = msc->input; + u8 buf[32]; + struct { + __le32 width; + __le32 height; + __le16 min_x; + __le16 min_y; + __le16 max_x; + __le16 max_y; + } dim; + uint32_t x_span, y_span; + + ret = hid_hw_raw_request(hdev, SENSOR_DIMENSIONS_REPORT_ID, buf, sizeof(buf), HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + if (ret < (int)(1 + sizeof(dim))) { + hid_err(hdev, "unable to request dimensions (%d)\n", ret); + return ret; + } + + memcpy(&dim, buf + 1, sizeof(dim)); + + /* finger position */ + input_set_abs_params(input, ABS_MT_POSITION_X, + le16_to_int(dim.min_x), le16_to_int(dim.max_x), 0, 0); + /* Y axis is inverted */ + input_set_abs_params(input, ABS_MT_POSITION_Y, + -le16_to_int(dim.max_y), -le16_to_int(dim.min_y), 0, 0); + x_span = le16_to_int(dim.max_x) - le16_to_int(dim.min_x); + y_span = le16_to_int(dim.max_y) - le16_to_int(dim.min_y); + + /* X/Y resolution */ + input_abs_set_res(input, ABS_MT_POSITION_X, 100 * x_span / le32_to_cpu(dim.width) ); + input_abs_set_res(input, ABS_MT_POSITION_Y, 100 * y_span / le32_to_cpu(dim.height) ); + + /* copy info, as input_mt_init_slots() does */ + dev->absinfo[ABS_X] = dev->absinfo[ABS_MT_POSITION_X]; + dev->absinfo[ABS_Y] = dev->absinfo[ABS_MT_POSITION_Y]; + + msc->query_dimensions = false; + } + return 0; } @@ -695,11 +756,6 @@ struct tp_mouse_report { u8 padding[4]; }; -static inline int le16_to_int(__le16 x) -{ - return (signed short)le16_to_cpu(x); -} - static void report_finger_data(struct input_dev *input, int slot, const struct input_mt_pos *pos, const struct tp_finger *f) @@ -995,6 +1051,7 @@ static int magicmouse_setup_input_mtp(struct input_dev *input, { int error; int mt_flags = 0; + struct magicmouse_sc *msc = hid_get_drvdata(hdev); __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); __clear_bit(BTN_0, input->keybit); @@ -1060,6 +1117,18 @@ static int magicmouse_setup_input_mtp(struct input_dev *input, if (error) return error; + /* + * Override the default input->open function to send the MT + * enable every time the device is opened. This ensures it works + * even if we missed a reset event due to the device being closed. + * input->close is overridden for symmetry. + * + * This also takes care of the dimensions query. + */ + input->open = magicmouse_open; + input->close = magicmouse_close; + msc->query_dimensions = true; + return 0; } @@ -1070,15 +1139,6 @@ static int magicmouse_setup_input_spi(struct input_dev *input, if (ret) return ret; - /* - * Override the default input->open function to send the MT - * enable every time the device is opened. This ensures it works - * even if we missed a reset event due to the device being closed. - * input->close is overridden for symmetry. - */ - input->open = magicmouse_open; - input->close = magicmouse_close; - return 0; } From 021382c64b073e36af9f07132a5d4e08fda6da42 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 27 Feb 2024 19:16:39 +0100 Subject: [PATCH 0372/1020] fixup! hid: Add Apple DockChannel HID transport driver --- drivers/hid/dockchannel-hid/dockchannel-hid.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hid/dockchannel-hid/dockchannel-hid.c b/drivers/hid/dockchannel-hid/dockchannel-hid.c index bb70e342b8aac6..86c36ba67f366f 100644 --- a/drivers/hid/dockchannel-hid/dockchannel-hid.c +++ b/drivers/hid/dockchannel-hid/dockchannel-hid.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include From 84ec2b3be4e40ec187bdf4b4b7c8928c475d6f1a Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 27 Feb 2024 19:16:39 +0100 Subject: [PATCH 0373/1020] fixup! hid: Add Apple DockChannel HID transport driver --- drivers/hid/dockchannel-hid/dockchannel-hid.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hid/dockchannel-hid/dockchannel-hid.c b/drivers/hid/dockchannel-hid/dockchannel-hid.c index 86c36ba67f366f..1bd149c8fb5d53 100644 --- a/drivers/hid/dockchannel-hid/dockchannel-hid.c +++ b/drivers/hid/dockchannel-hid/dockchannel-hid.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "../hid-ids.h" From b1ede3ac29b9db4a11967e9255eddbf373691715 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 27 Feb 2024 19:16:39 +0100 Subject: [PATCH 0374/1020] fixup! hid: Add Apple DockChannel HID transport driver --- drivers/hid/dockchannel-hid/dockchannel-hid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/dockchannel-hid/dockchannel-hid.c b/drivers/hid/dockchannel-hid/dockchannel-hid.c index 1bd149c8fb5d53..053b1c1b702b38 100644 --- a/drivers/hid/dockchannel-hid/dockchannel-hid.c +++ b/drivers/hid/dockchannel-hid/dockchannel-hid.c @@ -817,7 +817,7 @@ static void dchid_handle_init(struct dockchannel_hid *dchid, void *data, size_t break; } - strlcpy(iface->gpio_name, req->name, MAX_GPIO_NAME); + strscpy(iface->gpio_name, req->name, MAX_GPIO_NAME); iface->gpio_id = req->id; break; } From 538e5d70a2bdf2229cd5fb47fefca563862a3040 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 27 Feb 2024 19:16:39 +0100 Subject: [PATCH 0375/1020] fixup! soc: apple: Add DockChannel driver --- drivers/soc/apple/dockchannel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/apple/dockchannel.c b/drivers/soc/apple/dockchannel.c index b4d793bf210266..0c1d07ce3849f1 100644 --- a/drivers/soc/apple/dockchannel.c +++ b/drivers/soc/apple/dockchannel.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include From dc43a21b6919808ad1c2a53010c471bfac5d9dec Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 1 Feb 2022 00:40:51 +0900 Subject: [PATCH 0376/1020] lib/vsprintf: Add support for generic FOURCCs by extending %p4cc %p4cc is designed for DRM/V4L2 FOURCCs with their specific quirks, but it's useful to be able to print generic 4-character codes formatted as an integer. Extend it to add format specifiers for printing generic 32-bit FOURCCs with various endian semantics: %p4ch Host-endian %p4cl Little-endian %p4cb Big-endian %p4cr Reverse-endian The endianness determines how bytes are interpreted as a u32, and the FOURCC is then always printed MSByte-first (this is the opposite of V4L/DRM FOURCCs). This covers most practical cases, e.g. %p4cr would allow printing LSByte-first FOURCCs stored in host endian order (other than the hex form being in character order, not the integer value). Signed-off-by: Hector Martin --- Documentation/core-api/printk-formats.rst | 32 +++++++++++++++++++++ lib/vsprintf.c | 35 +++++++++++++++++++---- 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/Documentation/core-api/printk-formats.rst b/Documentation/core-api/printk-formats.rst index 4451ef50193613..c726a846f752e9 100644 --- a/Documentation/core-api/printk-formats.rst +++ b/Documentation/core-api/printk-formats.rst @@ -632,6 +632,38 @@ Examples:: %p4cc Y10 little-endian (0x20303159) %p4cc NV12 big-endian (0xb231564e) +Generic FourCC code +------------------- + +:: + %p4c[hnbl] gP00 (0x67503030) + +Print a generic FourCC code, as both ASCII characters and its numerical +value as hexadecimal. + +The additional ``h``, ``r``, ``b``, and ``l`` specifiers are used to specify +host, reversed, big or little endian order data respectively. Host endian +order means the data is interpreted as a 32-bit integer and the most +significant byte is printed first; that is, the character code as printed +matches the byte order stored in memory on big-endian systems, and is reversed +on little-endian systems. + +Passed by reference. + +Examples for a little-endian machine, given &(u32)0x67503030:: + + %p4ch gP00 (0x67503030) + %p4cl gP00 (0x67503030) + %p4cb 00Pg (0x30305067) + %p4cr 00Pg (0x30305067) + +Examples for a big-endian machine, given &(u32)0x67503030:: + + %p4ch gP00 (0x67503030) + %p4cl 00Pg (0x30305067) + %p4cb gP00 (0x67503030) + %p4cr 00Pg (0x30305067) + Rust ---- diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 552738f14275a7..e048f3496574b9 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -1760,27 +1760,50 @@ char *fourcc_string(char *buf, char *end, const u32 *fourcc, char output[sizeof("0123 little-endian (0x01234567)")]; char *p = output; unsigned int i; + bool pix_fmt = false; u32 orig, val; - if (fmt[1] != 'c' || fmt[2] != 'c') + if (fmt[1] != 'c') return error_string(buf, end, "(%p4?)", spec); if (check_pointer(&buf, end, fourcc, spec)) return buf; orig = get_unaligned(fourcc); - val = orig & ~BIT(31); + switch (fmt[2]) { + case 'h': + val = orig; + break; + case 'r': + val = orig = swab32(orig); + break; + case 'l': + val = orig = le32_to_cpu(orig); + break; + case 'b': + val = orig = be32_to_cpu(orig); + break; + case 'c': + /* Pixel formats are printed LSB-first */ + val = swab32(orig & ~BIT(31)); + pix_fmt = true; + break; + default: + return error_string(buf, end, "(%p4?)", spec); + } for (i = 0; i < sizeof(u32); i++) { - unsigned char c = val >> (i * 8); + unsigned char c = val >> ((3 - i) * 8); /* Print non-control ASCII characters as-is, dot otherwise */ *p++ = isascii(c) && isprint(c) ? c : '.'; } - *p++ = ' '; - strcpy(p, orig & BIT(31) ? "big-endian" : "little-endian"); - p += strlen(p); + if (pix_fmt) { + *p++ = ' '; + strcpy(p, orig & BIT(31) ? "big-endian" : "little-endian"); + p += strlen(p); + } *p++ = ' '; *p++ = '('; From 8fbc67273e43c13729d323219ac2922e25584047 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 4 Feb 2022 12:47:07 +0900 Subject: [PATCH 0377/1020] platform/apple: Add new Apple Mac SMC driver This driver implements support for the SMC (System Management Controller) in Apple Macs. In contrast to the existing applesmc driver, it uses pluggable backends that allow it to support different SMC implementations, and uses the MFD subsystem to expose the core SMC functionality so that specific features (gpio, hwmon, battery, etc.) can be implemented by separate drivers in their respective downstream subsystems. The initial RTKit backend adds support for Apple Silicon Macs (M1 et al). We hope a backend for T2 Macs will be written in the future (since those are not supported by applesmc), and eventually an x86 backend would allow us to fully deprecate applesmc in favor of this driver. Signed-off-by: Hector Martin --- drivers/platform/Kconfig | 2 + drivers/platform/Makefile | 1 + drivers/platform/apple/Kconfig | 49 ++++ drivers/platform/apple/Makefile | 11 + drivers/platform/apple/smc.h | 28 ++ drivers/platform/apple/smc_core.c | 249 ++++++++++++++++ drivers/platform/apple/smc_rtkit.c | 454 +++++++++++++++++++++++++++++ include/linux/mfd/macsmc.h | 86 ++++++ 8 files changed, 880 insertions(+) create mode 100644 drivers/platform/apple/Kconfig create mode 100644 drivers/platform/apple/Makefile create mode 100644 drivers/platform/apple/smc.h create mode 100644 drivers/platform/apple/smc_core.c create mode 100644 drivers/platform/apple/smc_rtkit.c create mode 100644 include/linux/mfd/macsmc.h diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig index 868b20361769c3..8bfd8a13802391 100644 --- a/drivers/platform/Kconfig +++ b/drivers/platform/Kconfig @@ -14,3 +14,5 @@ source "drivers/platform/olpc/Kconfig" source "drivers/platform/surface/Kconfig" source "drivers/platform/x86/Kconfig" + +source "drivers/platform/apple/Kconfig" diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile index 41640172975a79..d2baa4eb4f133e 100644 --- a/drivers/platform/Makefile +++ b/drivers/platform/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_OLPC_EC) += olpc/ obj-$(CONFIG_GOLDFISH) += goldfish/ obj-$(CONFIG_CHROME_PLATFORMS) += chrome/ obj-$(CONFIG_SURFACE_PLATFORMS) += surface/ +obj-$(CONFIG_APPLE_PLATFORMS) += apple/ diff --git a/drivers/platform/apple/Kconfig b/drivers/platform/apple/Kconfig new file mode 100644 index 00000000000000..5bcadd349493ac --- /dev/null +++ b/drivers/platform/apple/Kconfig @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Apple Platform-Specific Drivers +# + +menuconfig APPLE_PLATFORMS + bool "Apple Mac Platform-Specific Device Drivers" + default y + help + Say Y here to get to see options for platform-specific device drivers + for Apple devices. This option alone does not add any kernel code. + + If you say N, all options in this submenu will be skipped and disabled. + +if APPLE_PLATFORMS + +config APPLE_SMC + tristate "Apple SMC Driver" + depends on ARCH_APPLE || (COMPILE_TEST && 64BIT) + default ARCH_APPLE + select MFD_CORE + help + Build support for the Apple System Management Controller present in + Apple Macs. This driver currently supports the SMC in Apple Silicon + Macs. For x86 Macs, see the applesmc driver (SENSORS_APPLESMC). + + Say Y here if you have an Apple Silicon Mac. + + To compile this driver as a module, choose M here: the module will + be called macsmc. + +if APPLE_SMC + +config APPLE_SMC_RTKIT + tristate "RTKit (Apple Silicon) backend" + depends on ARCH_APPLE || (COMPILE_TEST && 64BIT) + depends on APPLE_RTKIT + default ARCH_APPLE + help + Build support for SMC communications via the RTKit backend. This is + required for Apple Silicon Macs. + + Say Y here if you have an Apple Silicon Mac. + + To compile this driver as a module, choose M here: the module will + be called macsmc-rtkit. + +endif +endif diff --git a/drivers/platform/apple/Makefile b/drivers/platform/apple/Makefile new file mode 100644 index 00000000000000..79fac195398b0c --- /dev/null +++ b/drivers/platform/apple/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for linux/drivers/platform/apple +# Apple Platform-Specific Drivers +# + +macsmc-y += smc_core.o +macsmc-rtkit-y += smc_rtkit.o + +obj-$(CONFIG_APPLE_SMC) += macsmc.o +obj-$(CONFIG_APPLE_SMC_RTKIT) += macsmc-rtkit.o diff --git a/drivers/platform/apple/smc.h b/drivers/platform/apple/smc.h new file mode 100644 index 00000000000000..8ae51887b2c553 --- /dev/null +++ b/drivers/platform/apple/smc.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SMC internal core definitions + * Copyright (C) The Asahi Linux Contributors + */ + +#ifndef _SMC_H +#define _SMC_H + +#include + +struct apple_smc_backend_ops { + int (*read_key)(void *cookie, smc_key key, void *buf, size_t size); + int (*write_key)(void *cookie, smc_key key, void *buf, size_t size); + int (*write_key_atomic)(void *cookie, smc_key key, void *buf, size_t size); + int (*rw_key)(void *cookie, smc_key key, void *wbuf, size_t wsize, + void *rbuf, size_t rsize); + int (*get_key_by_index)(void *cookie, int index, smc_key *key); + int (*get_key_info)(void *cookie, smc_key key, struct apple_smc_key_info *info); +}; + +struct apple_smc *apple_smc_probe(struct device *dev, const struct apple_smc_backend_ops *ops, + void *cookie); +void *apple_smc_get_cookie(struct apple_smc *smc); +int apple_smc_remove(struct apple_smc *smc); +void apple_smc_event_received(struct apple_smc *smc, uint32_t event); + +#endif diff --git a/drivers/platform/apple/smc_core.c b/drivers/platform/apple/smc_core.c new file mode 100644 index 00000000000000..daf029cd072f52 --- /dev/null +++ b/drivers/platform/apple/smc_core.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SMC core framework + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include "smc.h" + +struct apple_smc { + struct device *dev; + + void *be_cookie; + const struct apple_smc_backend_ops *be; + + struct mutex mutex; + + u32 key_count; + smc_key first_key; + smc_key last_key; + + struct blocking_notifier_head event_handlers; +}; + +static const struct mfd_cell apple_smc_devs[] = { + { + .name = "macsmc-gpio", + }, + { + .name = "macsmc-hid", + }, + { + .name = "macsmc-power", + }, + { + .name = "macsmc-reboot", + }, + { + .name = "macsmc-rtc", + }, +}; + +int apple_smc_read(struct apple_smc *smc, smc_key key, void *buf, size_t size) +{ + int ret; + + mutex_lock(&smc->mutex); + ret = smc->be->read_key(smc->be_cookie, key, buf, size); + mutex_unlock(&smc->mutex); + + return ret; +} +EXPORT_SYMBOL(apple_smc_read); + +int apple_smc_write(struct apple_smc *smc, smc_key key, void *buf, size_t size) +{ + int ret; + + mutex_lock(&smc->mutex); + ret = smc->be->write_key(smc->be_cookie, key, buf, size); + mutex_unlock(&smc->mutex); + + return ret; +} +EXPORT_SYMBOL(apple_smc_write); + +int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, void *buf, size_t size) +{ + int ret; + + /* + * Will fail if SMC is busy. This is only used by SMC reboot/poweroff + * final calls, so it doesn't really matter at that point. + */ + if (!mutex_trylock(&smc->mutex)) + return -EBUSY; + + ret = smc->be->write_key_atomic(smc->be_cookie, key, buf, size); + mutex_unlock(&smc->mutex); + + return ret; +} +EXPORT_SYMBOL(apple_smc_write_atomic); + +int apple_smc_rw(struct apple_smc *smc, smc_key key, void *wbuf, size_t wsize, + void *rbuf, size_t rsize) +{ + int ret; + + mutex_lock(&smc->mutex); + ret = smc->be->rw_key(smc->be_cookie, key, wbuf, wsize, rbuf, rsize); + mutex_unlock(&smc->mutex); + + return ret; +} +EXPORT_SYMBOL(apple_smc_rw); + +int apple_smc_get_key_by_index(struct apple_smc *smc, int index, smc_key *key) +{ + int ret; + + mutex_lock(&smc->mutex); + ret = smc->be->get_key_by_index(smc->be_cookie, index, key); + mutex_unlock(&smc->mutex); + + return ret; +} +EXPORT_SYMBOL(apple_smc_get_key_by_index); + +int apple_smc_get_key_info(struct apple_smc *smc, smc_key key, struct apple_smc_key_info *info) +{ + int ret; + + mutex_lock(&smc->mutex); + ret = smc->be->get_key_info(smc->be_cookie, key, info); + mutex_unlock(&smc->mutex); + + return ret; +} +EXPORT_SYMBOL(apple_smc_get_key_info); + +int apple_smc_find_first_key_index(struct apple_smc *smc, smc_key key) +{ + int start = 0, count = smc->key_count; + int ret; + + if (key <= smc->first_key) + return 0; + if (key > smc->last_key) + return smc->key_count; + + while (count > 1) { + int pivot = start + ((count - 1) >> 1); + smc_key pkey; + + ret = apple_smc_get_key_by_index(smc, pivot, &pkey); + if (ret < 0) + return ret; + + if (pkey == key) + return pivot; + + pivot++; + + if (pkey < key) { + count -= pivot - start; + start = pivot; + } else { + count = pivot - start; + } + } + + return start; +} +EXPORT_SYMBOL(apple_smc_find_first_key_index); + +int apple_smc_get_key_count(struct apple_smc *smc) +{ + return smc->key_count; +} +EXPORT_SYMBOL(apple_smc_get_key_count); + +void apple_smc_event_received(struct apple_smc *smc, uint32_t event) +{ + dev_dbg(smc->dev, "Event: 0x%08x\n", event); + blocking_notifier_call_chain(&smc->event_handlers, event, NULL); +} +EXPORT_SYMBOL(apple_smc_event_received); + +int apple_smc_register_notifier(struct apple_smc *smc, struct notifier_block *n) +{ + return blocking_notifier_chain_register(&smc->event_handlers, n); +} +EXPORT_SYMBOL(apple_smc_register_notifier); + +int apple_smc_unregister_notifier(struct apple_smc *smc, struct notifier_block *n) +{ + return blocking_notifier_chain_unregister(&smc->event_handlers, n); +} +EXPORT_SYMBOL(apple_smc_unregister_notifier); + +void *apple_smc_get_cookie(struct apple_smc *smc) +{ + return smc->be_cookie; +} +EXPORT_SYMBOL(apple_smc_get_cookie); + +struct apple_smc *apple_smc_probe(struct device *dev, const struct apple_smc_backend_ops *ops, void *cookie) +{ + struct apple_smc *smc; + u32 count; + int ret; + + smc = devm_kzalloc(dev, sizeof(*smc), GFP_KERNEL); + if (!smc) + return ERR_PTR(-ENOMEM); + + smc->dev = dev; + smc->be_cookie = cookie; + smc->be = ops; + mutex_init(&smc->mutex); + BLOCKING_INIT_NOTIFIER_HEAD(&smc->event_handlers); + + ret = apple_smc_read_u32(smc, SMC_KEY(#KEY), &count); + if (ret) + return ERR_PTR(dev_err_probe(dev, ret, "Failed to get key count")); + smc->key_count = be32_to_cpu(count); + + ret = apple_smc_get_key_by_index(smc, 0, &smc->first_key); + if (ret) + return ERR_PTR(dev_err_probe(dev, ret, "Failed to get first key")); + + ret = apple_smc_get_key_by_index(smc, smc->key_count - 1, &smc->last_key); + if (ret) + return ERR_PTR(dev_err_probe(dev, ret, "Failed to get last key")); + + /* Enable notifications */ + apple_smc_write_flag(smc, SMC_KEY(NTAP), 1); + + dev_info(dev, "Initialized (%d keys %p4ch..%p4ch)\n", + smc->key_count, &smc->first_key, &smc->last_key); + + dev_set_drvdata(dev, smc); + + ret = mfd_add_devices(dev, -1, apple_smc_devs, ARRAY_SIZE(apple_smc_devs), NULL, 0, NULL); + if (ret) + return ERR_PTR(dev_err_probe(dev, ret, "Subdevice initialization failed")); + + return smc; +} +EXPORT_SYMBOL(apple_smc_probe); + +int apple_smc_remove(struct apple_smc *smc) +{ + mfd_remove_devices(smc->dev); + + /* Disable notifications */ + apple_smc_write_flag(smc, SMC_KEY(NTAP), 1); + + return 0; +} +EXPORT_SYMBOL(apple_smc_remove); + +MODULE_AUTHOR("Hector Martin "); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("Apple SMC core"); diff --git a/drivers/platform/apple/smc_rtkit.c b/drivers/platform/apple/smc_rtkit.c new file mode 100644 index 00000000000000..314aabe9a14492 --- /dev/null +++ b/drivers/platform/apple/smc_rtkit.c @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SMC RTKit backend + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "smc.h" + +#define SMC_ENDPOINT 0x20 + +/* Guess */ +#define SMC_SHMEM_SIZE 0x1000 + +#define SMC_MSG_READ_KEY 0x10 +#define SMC_MSG_WRITE_KEY 0x11 +#define SMC_MSG_GET_KEY_BY_INDEX 0x12 +#define SMC_MSG_GET_KEY_INFO 0x13 +#define SMC_MSG_INITIALIZE 0x17 +#define SMC_MSG_NOTIFICATION 0x18 +#define SMC_MSG_RW_KEY 0x20 + +#define SMC_DATA GENMASK(63, 32) +#define SMC_WSIZE GENMASK(31, 24) +#define SMC_SIZE GENMASK(23, 16) +#define SMC_ID GENMASK(15, 12) +#define SMC_MSG GENMASK(7, 0) +#define SMC_RESULT SMC_MSG + +#define SMC_RECV_TIMEOUT 500 + +struct apple_smc_rtkit { + struct device *dev; + struct apple_smc *core; + struct apple_rtkit *rtk; + + struct completion init_done; + bool initialized; + bool alive; + + struct resource *sram; + void __iomem *sram_base; + struct apple_rtkit_shmem shmem; + + unsigned int msg_id; + + bool atomic_pending; + struct completion cmd_done; + u64 cmd_ret; +}; + +static int apple_smc_rtkit_write_key_atomic(void *cookie, smc_key key, void *buf, size_t size) +{ + struct apple_smc_rtkit *smc = cookie; + int ret; + u64 msg; + u8 result; + + if (size > SMC_SHMEM_SIZE || size == 0) + return -EINVAL; + + if (!smc->alive) + return -EIO; + + memcpy_toio(smc->shmem.iomem, buf, size); + smc->msg_id = (smc->msg_id + 1) & 0xf; + msg = (FIELD_PREP(SMC_MSG, SMC_MSG_WRITE_KEY) | + FIELD_PREP(SMC_SIZE, size) | + FIELD_PREP(SMC_ID, smc->msg_id) | + FIELD_PREP(SMC_DATA, key)); + smc->atomic_pending = true; + + ret = apple_rtkit_send_message(smc->rtk, SMC_ENDPOINT, msg, NULL, true); + if (ret < 0) { + dev_err(smc->dev, "Failed to send command (%d)\n", ret); + return ret; + } + + while (smc->atomic_pending) { + ret = apple_rtkit_poll(smc->rtk); + if (ret < 0) { + dev_err(smc->dev, "RTKit poll failed (%llx)", msg); + return ret; + } + udelay(100); + } + + if (FIELD_GET(SMC_ID, smc->cmd_ret) != smc->msg_id) { + dev_err(smc->dev, "Command sequence mismatch (expected %d, got %d)\n", + smc->msg_id, (unsigned int)FIELD_GET(SMC_ID, smc->cmd_ret)); + return -EIO; + } + + result = FIELD_GET(SMC_RESULT, smc->cmd_ret); + if (result != 0) + return -result; + + return FIELD_GET(SMC_SIZE, smc->cmd_ret); +} + +static int apple_smc_cmd(struct apple_smc_rtkit *smc, u64 cmd, u64 arg, + u64 size, u64 wsize, u32 *ret_data) +{ + int ret; + u64 msg; + u8 result; + + if (!smc->alive) + return -EIO; + + reinit_completion(&smc->cmd_done); + + smc->msg_id = (smc->msg_id + 1) & 0xf; + msg = (FIELD_PREP(SMC_MSG, cmd) | + FIELD_PREP(SMC_SIZE, size) | + FIELD_PREP(SMC_WSIZE, wsize) | + FIELD_PREP(SMC_ID, smc->msg_id) | + FIELD_PREP(SMC_DATA, arg)); + + ret = apple_rtkit_send_message(smc->rtk, SMC_ENDPOINT, msg, NULL, false); + if (ret < 0) { + dev_err(smc->dev, "Failed to send command\n"); + return ret; + } + + do { + if (wait_for_completion_timeout(&smc->cmd_done, + msecs_to_jiffies(SMC_RECV_TIMEOUT)) == 0) { + dev_err(smc->dev, "Command timed out (%llx)", msg); + return -ETIMEDOUT; + } + if (FIELD_GET(SMC_ID, smc->cmd_ret) == smc->msg_id) + break; + dev_err(smc->dev, "Command sequence mismatch (expected %d, got %d)\n", + smc->msg_id, (unsigned int)FIELD_GET(SMC_ID, smc->cmd_ret)); + } while(1); + + result = FIELD_GET(SMC_RESULT, smc->cmd_ret); + if (result != 0) + return -result; + + if (ret_data) + *ret_data = FIELD_GET(SMC_DATA, smc->cmd_ret); + + return FIELD_GET(SMC_SIZE, smc->cmd_ret); +} + +static int _apple_smc_rtkit_read_key(struct apple_smc_rtkit *smc, smc_key key, + void *buf, size_t size, size_t wsize) +{ + int ret; + u32 rdata; + u64 cmd; + + if (size > SMC_SHMEM_SIZE || size == 0) + return -EINVAL; + + cmd = wsize ? SMC_MSG_RW_KEY : SMC_MSG_READ_KEY; + + ret = apple_smc_cmd(smc, cmd, key, size, wsize, &rdata); + if (ret < 0) + return ret; + + if (size <= 4) + memcpy(buf, &rdata, size); + else + memcpy_fromio(buf, smc->shmem.iomem, size); + + return ret; +} + +static int apple_smc_rtkit_read_key(void *cookie, smc_key key, void *buf, size_t size) +{ + return _apple_smc_rtkit_read_key(cookie, key, buf, size, 0); +} + +static int apple_smc_rtkit_write_key(void *cookie, smc_key key, void *buf, size_t size) +{ + struct apple_smc_rtkit *smc = cookie; + + if (size > SMC_SHMEM_SIZE || size == 0) + return -EINVAL; + + memcpy_toio(smc->shmem.iomem, buf, size); + return apple_smc_cmd(smc, SMC_MSG_WRITE_KEY, key, size, 0, NULL); +} + +static int apple_smc_rtkit_rw_key(void *cookie, smc_key key, + void *wbuf, size_t wsize, void *rbuf, size_t rsize) +{ + struct apple_smc_rtkit *smc = cookie; + + if (wsize > SMC_SHMEM_SIZE || wsize == 0) + return -EINVAL; + + memcpy_toio(smc->shmem.iomem, wbuf, wsize); + return _apple_smc_rtkit_read_key(smc, key, rbuf, rsize, wsize); +} + +static int apple_smc_rtkit_get_key_by_index(void *cookie, int index, smc_key *key) +{ + struct apple_smc_rtkit *smc = cookie; + int ret; + + ret = apple_smc_cmd(smc, SMC_MSG_GET_KEY_BY_INDEX, index, 0, 0, key); + + *key = swab32(*key); + return ret; +} + +static int apple_smc_rtkit_get_key_info(void *cookie, smc_key key, struct apple_smc_key_info *info) +{ + struct apple_smc_rtkit *smc = cookie; + u8 key_info[6]; + int ret; + + ret = apple_smc_cmd(smc, SMC_MSG_GET_KEY_INFO, key, 0, 0, NULL); + if (ret >= 0 && info) { + memcpy_fromio(key_info, smc->shmem.iomem, sizeof(key_info)); + info->size = key_info[0]; + info->type_code = get_unaligned_be32(&key_info[1]); + info->flags = key_info[5]; + } + return ret; +} + +static const struct apple_smc_backend_ops apple_smc_rtkit_be_ops = { + .read_key = apple_smc_rtkit_read_key, + .write_key = apple_smc_rtkit_write_key, + .write_key_atomic = apple_smc_rtkit_write_key_atomic, + .rw_key = apple_smc_rtkit_rw_key, + .get_key_by_index = apple_smc_rtkit_get_key_by_index, + .get_key_info = apple_smc_rtkit_get_key_info, +}; + +static void apple_smc_rtkit_crashed(void *cookie) +{ + struct apple_smc_rtkit *smc = cookie; + + dev_err(smc->dev, "SMC crashed! Your system will reboot in a few seconds...\n"); + smc->alive = false; +} + +static int apple_smc_rtkit_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr) +{ + struct apple_smc_rtkit *smc = cookie; + struct resource res = { + .start = bfr->iova, + .end = bfr->iova + bfr->size - 1, + .name = "rtkit_map", + .flags = smc->sram->flags, + }; + + if (!bfr->iova) { + dev_err(smc->dev, "RTKit wants a RAM buffer\n"); + return -EIO; + } + + if (res.end < res.start || !resource_contains(smc->sram, &res)) { + dev_err(smc->dev, + "RTKit buffer request outside SRAM region: %pR", &res); + return -EFAULT; + } + + bfr->iomem = smc->sram_base + (res.start - smc->sram->start); + bfr->is_mapped = true; + + return 0; +} + +static void apple_smc_rtkit_shmem_destroy(void *cookie, struct apple_rtkit_shmem *bfr) +{ + // no-op +} + +static bool apple_smc_rtkit_recv_early(void *cookie, u8 endpoint, u64 message) +{ + struct apple_smc_rtkit *smc = cookie; + + if (endpoint != SMC_ENDPOINT) { + dev_err(smc->dev, "Received message for unknown endpoint 0x%x\n", endpoint); + return false; + } + + if (!smc->initialized) { + int ret; + + smc->shmem.iova = message; + smc->shmem.size = SMC_SHMEM_SIZE; + ret = apple_smc_rtkit_shmem_setup(smc, &smc->shmem); + if (ret < 0) + dev_err(smc->dev, "Failed to initialize shared memory\n"); + else + smc->alive = true; + smc->initialized = true; + complete(&smc->init_done); + } else if (FIELD_GET(SMC_MSG, message) == SMC_MSG_NOTIFICATION) { + /* Handle these in the RTKit worker thread */ + return false; + } else { + smc->cmd_ret = message; + if (smc->atomic_pending) { + smc->atomic_pending = false; + } else { + complete(&smc->cmd_done); + } + } + + return true; +} + +static void apple_smc_rtkit_recv(void *cookie, u8 endpoint, u64 message) +{ + struct apple_smc_rtkit *smc = cookie; + + if (endpoint != SMC_ENDPOINT) { + dev_err(smc->dev, "Received message for unknown endpoint 0x%x\n", endpoint); + return; + } + + if (FIELD_GET(SMC_MSG, message) != SMC_MSG_NOTIFICATION) { + dev_err(smc->dev, "Received unknown message from worker: 0x%llx\n", message); + return; + } + + apple_smc_event_received(smc->core, FIELD_GET(SMC_DATA, message)); +} + +static const struct apple_rtkit_ops apple_smc_rtkit_ops = { + .crashed = apple_smc_rtkit_crashed, + .recv_message = apple_smc_rtkit_recv, + .recv_message_early = apple_smc_rtkit_recv_early, + .shmem_setup = apple_smc_rtkit_shmem_setup, + .shmem_destroy = apple_smc_rtkit_shmem_destroy, +}; + +static int apple_smc_rtkit_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct apple_smc_rtkit *smc; + int ret; + + smc = devm_kzalloc(dev, sizeof(*smc), GFP_KERNEL); + if (!smc) + return -ENOMEM; + + smc->dev = dev; + + smc->sram = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram"); + if (!smc->sram) + return dev_err_probe(dev, EIO, + "No SRAM region"); + + smc->sram_base = devm_ioremap_resource(dev, smc->sram); + if (IS_ERR(smc->sram_base)) + return dev_err_probe(dev, PTR_ERR(smc->sram_base), + "Failed to map SRAM region"); + + smc->rtk = + devm_apple_rtkit_init(dev, smc, NULL, 0, &apple_smc_rtkit_ops); + if (IS_ERR(smc->rtk)) + return dev_err_probe(dev, PTR_ERR(smc->rtk), + "Failed to intialize RTKit"); + + ret = apple_rtkit_wake(smc->rtk); + if (ret != 0) + return dev_err_probe(dev, ret, + "Failed to wake up SMC"); + + ret = apple_rtkit_start_ep(smc->rtk, SMC_ENDPOINT); + if (ret != 0) { + dev_err(dev, "Failed to start endpoint"); + goto cleanup; + } + + init_completion(&smc->init_done); + init_completion(&smc->cmd_done); + + ret = apple_rtkit_send_message(smc->rtk, SMC_ENDPOINT, + FIELD_PREP(SMC_MSG, SMC_MSG_INITIALIZE), NULL, false); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to send init message"); + + if (wait_for_completion_timeout(&smc->init_done, + msecs_to_jiffies(SMC_RECV_TIMEOUT)) == 0) { + ret = -ETIMEDOUT; + dev_err(dev, "Timed out initializing SMC"); + goto cleanup; + } + + if (!smc->alive) { + ret = -EIO; + goto cleanup; + } + + smc->core = apple_smc_probe(dev, &apple_smc_rtkit_be_ops, smc); + if (IS_ERR(smc->core)) { + ret = PTR_ERR(smc->core); + goto cleanup; + } + + return 0; + +cleanup: + /* Try to shut down RTKit, if it's not completely wedged */ + if (apple_rtkit_is_running(smc->rtk)) + apple_rtkit_quiesce(smc->rtk); + + return ret; +} + +static int apple_smc_rtkit_remove(struct platform_device *pdev) +{ + struct apple_smc *core = platform_get_drvdata(pdev); + struct apple_smc_rtkit *smc = apple_smc_get_cookie(core); + + apple_smc_remove(core); + + if (apple_rtkit_is_running(smc->rtk)) + apple_rtkit_quiesce(smc->rtk); + + return 0; +} + +static const struct of_device_id apple_smc_rtkit_of_match[] = { + { .compatible = "apple,smc" }, + {}, +}; +MODULE_DEVICE_TABLE(of, apple_smc_rtkit_of_match); + +static struct platform_driver apple_smc_rtkit_driver = { + .driver = { + .name = "macsmc-rtkit", + .owner = THIS_MODULE, + .of_match_table = apple_smc_rtkit_of_match, + }, + .probe = apple_smc_rtkit_probe, + .remove = apple_smc_rtkit_remove, +}; +module_platform_driver(apple_smc_rtkit_driver); + +MODULE_AUTHOR("Hector Martin "); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("Apple SMC RTKit backend driver"); diff --git a/include/linux/mfd/macsmc.h b/include/linux/mfd/macsmc.h new file mode 100644 index 00000000000000..39b4dc4ca88106 --- /dev/null +++ b/include/linux/mfd/macsmc.h @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SMC core definitions + * Copyright (C) The Asahi Linux Contributors + */ + +#ifndef _LINUX_MFD_MACSMC_H +#define _LINUX_MFD_MACSMC_H + +struct apple_smc; + +typedef u32 smc_key; + +#define SMC_KEY(s) (smc_key)(_SMC_KEY(#s)) +#define _SMC_KEY(s) (((s)[0] << 24) | ((s)[1] << 16) | ((s)[2] << 8) | (s)[3]) + +#define APPLE_SMC_READABLE BIT(7) +#define APPLE_SMC_WRITABLE BIT(6) +#define APPLE_SMC_FUNCTION BIT(4) + +struct apple_smc_key_info { + u8 size; + u32 type_code; + u8 flags; +}; + +int apple_smc_read(struct apple_smc *smc, smc_key key, void *buf, size_t size); +int apple_smc_write(struct apple_smc *smc, smc_key key, void *buf, size_t size); +int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, void *buf, size_t size); +int apple_smc_rw(struct apple_smc *smc, smc_key key, void *wbuf, size_t wsize, + void *rbuf, size_t rsize); + +int apple_smc_get_key_count(struct apple_smc *smc); +int apple_smc_find_first_key_index(struct apple_smc *smc, smc_key key); +int apple_smc_get_key_by_index(struct apple_smc *smc, int index, smc_key *key); +int apple_smc_get_key_info(struct apple_smc *smc, smc_key key, struct apple_smc_key_info *info); + +static inline bool apple_smc_key_exists(struct apple_smc *smc, smc_key key) +{ + return apple_smc_get_key_info(smc, key, NULL) >= 0; +} + +#define APPLE_SMC_TYPE_OPS(type) \ + static inline int apple_smc_read_##type(struct apple_smc *smc, smc_key key, type *p) \ + { \ + int ret = apple_smc_read(smc, key, p, sizeof(*p)); \ + return (ret < 0) ? ret : ((ret != sizeof(*p)) ? -EINVAL : 0); \ + } \ + static inline int apple_smc_write_##type(struct apple_smc *smc, smc_key key, type p) \ + { \ + return apple_smc_write(smc, key, &p, sizeof(p)); \ + } \ + static inline int apple_smc_write_##type##_atomic(struct apple_smc *smc, smc_key key, type p) \ + { \ + return apple_smc_write_atomic(smc, key, &p, sizeof(p)); \ + } \ + static inline int apple_smc_rw_##type(struct apple_smc *smc, smc_key key, \ + type w, type *r) \ + { \ + int ret = apple_smc_rw(smc, key, &w, sizeof(w), r, sizeof(*r)); \ + return (ret < 0) ? ret : ((ret != sizeof(*r)) ? -EINVAL : 0); \ + } + +APPLE_SMC_TYPE_OPS(u64) +APPLE_SMC_TYPE_OPS(u32) +APPLE_SMC_TYPE_OPS(u16) +APPLE_SMC_TYPE_OPS(u8) +APPLE_SMC_TYPE_OPS(s64) +APPLE_SMC_TYPE_OPS(s32) +APPLE_SMC_TYPE_OPS(s16) +APPLE_SMC_TYPE_OPS(s8) + +static inline int apple_smc_read_flag(struct apple_smc *smc, smc_key key) +{ + u8 val; + int ret = apple_smc_read_u8(smc, key, &val); + if (ret < 0) + return ret; + return val ? 1 : 0; +} +#define apple_smc_write_flag apple_smc_write_u8 + +int apple_smc_register_notifier(struct apple_smc *smc, struct notifier_block *n); +int apple_smc_unregister_notifier(struct apple_smc *smc, struct notifier_block *n); + +#endif From 833c15a22a492de2329123ee2891678e9be782fa Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 4 Feb 2022 12:52:52 +0900 Subject: [PATCH 0378/1020] gpio: Add new gpio-macsmc driver for Apple Macs This driver implements the GPIO service on top of the SMC framework on Apple Mac machines. In particular, these are the GPIOs present in the PMU IC which are used to control power to certain on-board devices. Although the underlying hardware supports various pin config settings (input/output, open drain, etc.), this driver does not implement that functionality and leaves it up to the firmware to configure things properly. We also don't yet support interrupts/events. This is sufficient for device power control, which is the only thing we need to support at this point. More features will be implemented when needed. To our knowledge, only Apple Silicon Macs implement this SMC feature. Signed-off-by: Hector Martin --- drivers/gpio/Kconfig | 11 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-macsmc.c | 238 +++++++++++++++++++++++++++++++++++++ 3 files changed, 250 insertions(+) create mode 100644 drivers/gpio/gpio-macsmc.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b50d0b47084977..a7ca0668ba0e30 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1393,6 +1393,17 @@ config GPIO_LP87565 This driver can also be built as a module. If so, the module will be called gpio-lp87565. +config GPIO_MACSMC + tristate "Apple Mac SMC GPIO" + depends on APPLE_SMC + default ARCH_APPLE + help + Support for GPIOs controlled by the SMC microcontroller on Apple Mac + systems. + + This driver can also be built as a module. If so, the module will be + called gpio-macsmc. + config GPIO_MADERA tristate "Cirrus Logic Madera class codecs" depends on PINCTRL_MADERA diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index fdd28c58d89047..37be1e60af63d4 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -91,6 +91,7 @@ obj-$(CONFIG_GPIO_LP873X) += gpio-lp873x.o obj-$(CONFIG_GPIO_LP87565) += gpio-lp87565.o obj-$(CONFIG_GPIO_LPC18XX) += gpio-lpc18xx.o obj-$(CONFIG_GPIO_LPC32XX) += gpio-lpc32xx.o +obj-$(CONFIG_GPIO_MACSMC) += gpio-macsmc.o obj-$(CONFIG_GPIO_MADERA) += gpio-madera.o obj-$(CONFIG_GPIO_MAX3191X) += gpio-max3191x.o obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o diff --git a/drivers/gpio/gpio-macsmc.c b/drivers/gpio/gpio-macsmc.c new file mode 100644 index 00000000000000..ff9950afb69af0 --- /dev/null +++ b/drivers/gpio/gpio-macsmc.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SMC GPIO driver + * Copyright The Asahi Linux Contributors + * + * This driver implements basic SMC PMU GPIO support that can read inputs + * and write outputs. Mode changes and IRQ config are not yet implemented. + */ + +#include +#include +#include +#include +#include + +#define MAX_GPIO 64 + +/* + * Commands 0-6 are, presumably, the intended API. + * Command 0xff lets you get/set the pin configuration in detail directly, + * but the bit meanings seem not to be stable between devices/PMU hardware + * versions. + * + * We're going to try to make do with the low commands for now. + * We don't implement pin mode changes at this time. + */ + +#define CMD_ACTION (0 << 24) +#define CMD_OUTPUT (1 << 24) +#define CMD_INPUT (2 << 24) +#define CMD_PINMODE (3 << 24) +#define CMD_IRQ_ENABLE (4 << 24) +#define CMD_IRQ_ACK (5 << 24) +#define CMD_IRQ_MODE (6 << 24) +#define CMD_CONFIG (0xff << 24) + +#define MODE_INPUT 0 +#define MODE_OUTPUT 1 +#define MODE_VALUE_0 0 +#define MODE_VALUE_1 2 + +#define IRQ_MODE_HIGH 0 +#define IRQ_MODE_LOW 1 +#define IRQ_MODE_RISING 2 +#define IRQ_MODE_FALLING 3 +#define IRQ_MODE_BOTH 4 + +#define CONFIG_MASK GENMASK(23, 16) +#define CONFIG_VAL GENMASK(7, 0) + +#define CONFIG_OUTMODE GENMASK(7, 6) +#define CONFIG_IRQMODE GENMASK(5, 3) +#define CONFIG_PULLDOWN BIT(2) +#define CONFIG_PULLUP BIT(1) +#define CONFIG_OUTVAL BIT(0) + +/* + * output modes seem to differ depending on the PMU in use... ? + * j274 / M1 (Sera PMU): + * 0 = input + * 1 = output + * 2 = open drain + * 3 = disable + * j314 / M1Pro (Maverick PMU): + * 0 = input + * 1 = open drain + * 2 = output + * 3 = ? + */ + +struct macsmc_gpio { + struct device *dev; + struct apple_smc *smc; + struct gpio_chip gc; + + int first_index; +}; + +static int macsmc_gpio_nr(smc_key key) +{ + int low = hex_to_bin(key & 0xff); + int high = hex_to_bin((key >> 8) & 0xff); + + if (low < 0 || high < 0) + return -1; + + return low | (high << 4); +} + +static int macsmc_gpio_key(unsigned int offset) +{ + return _SMC_KEY("gP\0\0") | (hex_asc_hi(offset) << 8) | hex_asc_lo(offset); +} + +static int macsmc_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct macsmc_gpio *smcgp = gpiochip_get_data(gc); + smc_key key = macsmc_gpio_key(offset); + u32 val; + int ret; + + /* First try reading the explicit pin mode register */ + ret = apple_smc_rw_u32(smcgp->smc, key, CMD_PINMODE, &val); + if (!ret) + return (val & MODE_OUTPUT) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; + + /* + * Less common IRQ configs cause CMD_PINMODE to fail, and so does open drain mode. + * Fall back to reading IRQ mode, which will only succeed for inputs. + */ + ret = apple_smc_rw_u32(smcgp->smc, key, CMD_IRQ_MODE, &val); + return (!ret) ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; +} + +static int macsmc_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct macsmc_gpio *smcgp = gpiochip_get_data(gc); + smc_key key = macsmc_gpio_key(offset); + u32 val; + int ret; + + ret = macsmc_gpio_get_direction(gc, offset); + if (ret < 0) + return ret; + + if (ret == GPIO_LINE_DIRECTION_OUT) + ret = apple_smc_rw_u32(smcgp->smc, key, CMD_OUTPUT, &val); + else + ret = apple_smc_rw_u32(smcgp->smc, key, CMD_INPUT, &val); + + if (ret < 0) + return ret; + + return val ? 1 : 0; +} + +static void macsmc_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct macsmc_gpio *smcgp = gpiochip_get_data(gc); + smc_key key = macsmc_gpio_key(offset); + int ret; + + value |= CMD_OUTPUT; + ret = apple_smc_write_u32(smcgp->smc, key, CMD_OUTPUT | value); + if (ret < 0) + dev_err(smcgp->dev, "GPIO set failed %p4ch = 0x%x\n", &key, value); +} + +static int macsmc_gpio_init_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, unsigned int ngpios) +{ + struct macsmc_gpio *smcgp = gpiochip_get_data(gc); + int count = apple_smc_get_key_count(smcgp->smc) - smcgp->first_index; + int i; + + if (count > MAX_GPIO) + count = MAX_GPIO; + + bitmap_zero(valid_mask, ngpios); + + for (i = 0; i < count; i++) { + smc_key key; + int gpio_nr; + int ret = apple_smc_get_key_by_index(smcgp->smc, smcgp->first_index + i, &key); + + if (ret < 0) + return ret; + + if (key > SMC_KEY(gPff)) + break; + + gpio_nr = macsmc_gpio_nr(key); + if (gpio_nr < 0 || gpio_nr > MAX_GPIO) { + dev_err(smcgp->dev, "Bad GPIO key %p4ch\n", &key); + continue; + } + + set_bit(gpio_nr, valid_mask); + } + + return 0; +} + +static int macsmc_gpio_probe(struct platform_device *pdev) +{ + struct macsmc_gpio *smcgp; + struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); + smc_key key; + int ret; + + smcgp = devm_kzalloc(&pdev->dev, sizeof(*smcgp), GFP_KERNEL); + if (!smcgp) + return -ENOMEM; + + pdev->dev.of_node = of_get_child_by_name(pdev->dev.parent->of_node, "gpio"); + + smcgp->dev = &pdev->dev; + smcgp->smc = smc; + smcgp->first_index = apple_smc_find_first_key_index(smc, SMC_KEY(gP00)); + + if (smcgp->first_index >= apple_smc_get_key_count(smc)) + return -ENODEV; + + ret = apple_smc_get_key_by_index(smc, smcgp->first_index, &key); + if (ret < 0) + return ret; + + if (key > macsmc_gpio_key(MAX_GPIO - 1)) + return -ENODEV; + + dev_info(smcgp->dev, "First GPIO key: %p4ch\n", &key); + + smcgp->gc.label = "macsmc-pmu-gpio"; + smcgp->gc.owner = THIS_MODULE; + smcgp->gc.get = macsmc_gpio_get; + smcgp->gc.set = macsmc_gpio_set; + smcgp->gc.get_direction = macsmc_gpio_get_direction; + smcgp->gc.init_valid_mask = macsmc_gpio_init_valid_mask; + smcgp->gc.can_sleep = true; + smcgp->gc.ngpio = MAX_GPIO; + smcgp->gc.base = -1; + smcgp->gc.parent = &pdev->dev; + + return devm_gpiochip_add_data(&pdev->dev, &smcgp->gc, smcgp); +} + +static struct platform_driver macsmc_gpio_driver = { + .driver = { + .name = "macsmc-gpio", + }, + .probe = macsmc_gpio_probe, +}; +module_platform_driver(macsmc_gpio_driver); + +MODULE_AUTHOR("Hector Martin "); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("Apple SMC GPIO driver"); +MODULE_ALIAS("platform:macsmc-gpio"); From 33fd2d89731d8bed0c2a3f6b5c05322cfbb176f0 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 8 Feb 2022 02:30:16 +0900 Subject: [PATCH 0379/1020] power: supply: macsmc_power: Driver for Apple SMC power/battery stats This driver implements support for battery stats on top of the macsmc framework, to support Apple M1 Mac machines. Co-authored-by: Joey Gouly Signed-off-by: Hector Martin --- drivers/power/supply/Kconfig | 7 + drivers/power/supply/Makefile | 1 + drivers/power/supply/macsmc_power.c | 257 ++++++++++++++++++++++++++++ 3 files changed, 265 insertions(+) create mode 100644 drivers/power/supply/macsmc_power.c diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 3e31375491d580..6ed31fc7bb443f 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -985,4 +985,11 @@ config FUEL_GAUGE_MM8013 the state of charge, temperature, cycle count, actual and design capacity, etc. +config CHARGER_MACSMC + tristate "Apple SMC Charger / Battery support" + depends on APPLE_SMC + help + Say Y here to enable support for the charger and battery controls on + Apple SMC controllers, as used on Apple Silicon Macs. + endif # POWER_SUPPLY diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 58b56727803498..3f980b4d6a8302 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -73,6 +73,7 @@ obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o obj-$(CONFIG_CHARGER_LT3651) += lt3651-charger.o obj-$(CONFIG_CHARGER_LTC4162L) += ltc4162-l-charger.o +obj-$(CONFIG_CHARGER_MACSMC) += macsmc_power.o obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o obj-$(CONFIG_CHARGER_DETECTOR_MAX14656) += max14656_charger_detector.o obj-$(CONFIG_CHARGER_MAX77650) += max77650-charger.o diff --git a/drivers/power/supply/macsmc_power.c b/drivers/power/supply/macsmc_power.c new file mode 100644 index 00000000000000..dc357334407d6b --- /dev/null +++ b/drivers/power/supply/macsmc_power.c @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SMC Power/Battery Management + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include + +#define MAX_STRING_LENGTH 256 + +struct macsmc_power { + struct device *dev; + struct apple_smc *smc; + struct power_supply *psy; + char model_name[MAX_STRING_LENGTH]; + char serial_number[MAX_STRING_LENGTH]; + + struct notifier_block nb; +}; + +static int macsmc_battery_get_status(struct macsmc_power *power) +{ + u8 val; + int ret; + + ret = apple_smc_read_u8(power->smc, SMC_KEY(BSFC), &val); + if (ret) + return ret; + if (val == 1) + return POWER_SUPPLY_STATUS_FULL; + + ret = apple_smc_read_u8(power->smc, SMC_KEY(CHSC), &val); + if (ret) + return ret; + if (val == 1) + return POWER_SUPPLY_STATUS_CHARGING; + + ret = apple_smc_read_u8(power->smc, SMC_KEY(CHCC), &val); + if (ret) + return ret; + if (val == 0) + return POWER_SUPPLY_STATUS_DISCHARGING; + + ret = apple_smc_read_u8(power->smc, SMC_KEY(CHCE), &val); + if (ret) + return ret; + if (val == 0) + return POWER_SUPPLY_STATUS_DISCHARGING; + else + return POWER_SUPPLY_STATUS_NOT_CHARGING; +} + +static int macsmc_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct macsmc_power *power = power_supply_get_drvdata(psy); + int ret = 0; + u16 vu16; + u32 vu32; + s16 vs16; + s32 vs32; + s64 vs64; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = macsmc_battery_get_status(power); + ret = val->intval < 0 ? val->intval : 0; + break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0TE), &vu16); + val->intval = vu16 == 0xffff ? 0 : vu16 * 60; + break; + case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0TF), &vu16); + val->intval = vu16 == 0xffff ? 0 : vu16 * 60; + break; + case POWER_SUPPLY_PROP_CAPACITY: + ret = apple_smc_read_u16(power->smc, SMC_KEY(BRSC), &vu16); + val->intval = vu16; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0AV), &vu16); + val->intval = vu16 * 1000; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = apple_smc_read_s16(power->smc, SMC_KEY(B0AC), &vs16); + val->intval = vs16 * 1000; + break; + case POWER_SUPPLY_PROP_POWER_NOW: + ret = apple_smc_read_s32(power->smc, SMC_KEY(B0AP), &vs32); + val->intval = vs32 * 1000; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + ret = apple_smc_read_u16(power->smc, SMC_KEY(BITV), &vu16); + val->intval = vu16 * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RC), &vu16); + val->intval = vu16 * 1000; + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = apple_smc_read_u32(power->smc, SMC_KEY(CSIL), &vu32); + val->intval = vu32 * 1000; + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RI), &vu16); + val->intval = vu16 * 1000; + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RV), &vu16); + val->intval = vu16 * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0DC), &vu16); + val->intval = vu16 * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0FC), &vu16); + val->intval = vu16 * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RM), &vu16); + val->intval = swab16(vu16) * 1000; + break; + case POWER_SUPPLY_PROP_TEMP: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0AT), &vu16); + val->intval = vu16 - 2732; + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + ret = apple_smc_read_s64(power->smc, SMC_KEY(BAAC), &vs64); + val->intval = vs64; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = power->model_name; + break; + case POWER_SUPPLY_PROP_SERIAL_NUMBER: + val->strval = power->serial_number; + break; + default: + return -EINVAL; + } + + return ret; +} + +static enum power_supply_property macsmc_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_POWER_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_SERIAL_NUMBER, +}; + +static const struct power_supply_desc macsmc_battery_desc = { + .name = "macsmc-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = macsmc_battery_get_property, + .properties = macsmc_battery_props, + .num_properties = ARRAY_SIZE(macsmc_battery_props), +}; + +static int macsmc_power_event(struct notifier_block *nb, unsigned long event, void *data) +{ + struct macsmc_power *power = container_of(nb, struct macsmc_power, nb); + + if ((event & 0xffffff00) == 0x71010100) { + bool charging = (event & 0xff) != 0; + + dev_info(power->dev, "Charging: %d\n", charging); + power_supply_changed(power->psy); + + return NOTIFY_OK; + } + + return NOTIFY_DONE; +} + +static int macsmc_power_probe(struct platform_device *pdev) +{ + struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); + struct power_supply_config psy_cfg = {}; + struct macsmc_power *power; + int ret; + + power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL); + if (!power) + return -ENOMEM; + + power->dev = &pdev->dev; + power->smc = smc; + dev_set_drvdata(&pdev->dev, power); + + /* Ignore devices without a charger/battery */ + if (macsmc_battery_get_status(power) <= POWER_SUPPLY_STATUS_UNKNOWN) + return -ENODEV; + + /* Fetch string properties */ + apple_smc_read(smc, SMC_KEY(BMDN), power->model_name, sizeof(power->model_name) - 1); + apple_smc_read(smc, SMC_KEY(BMSN), power->serial_number, sizeof(power->serial_number) - 1); + + psy_cfg.drv_data = power; + power->psy = devm_power_supply_register(&pdev->dev, &macsmc_battery_desc, &psy_cfg); + if (IS_ERR(power->psy)) { + dev_err(&pdev->dev, "Failed to register power supply\n"); + ret = PTR_ERR(power->psy); + return ret; + } + + power->nb.notifier_call = macsmc_power_event; + apple_smc_register_notifier(power->smc, &power->nb); + + return 0; +} + +static int macsmc_power_remove(struct platform_device *pdev) +{ + struct macsmc_power *power = dev_get_drvdata(&pdev->dev); + + apple_smc_unregister_notifier(power->smc, &power->nb); + + return 0; +} + +static struct platform_driver macsmc_power_driver = { + .driver = { + .name = "macsmc-power", + .owner = THIS_MODULE, + }, + .probe = macsmc_power_probe, + .remove = macsmc_power_remove, +}; +module_platform_driver(macsmc_power_driver); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("Apple SMC battery and power management driver"); +MODULE_AUTHOR("Hector Martin "); +MODULE_ALIAS("platform:macsmc-power"); From 18e698d447600a19ef2a6a67bbc5ceffedda2673 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 8 Feb 2022 02:51:35 +0900 Subject: [PATCH 0380/1020] power: supply: macsmc_power: Add cycle count and health props Signed-off-by: Hector Martin --- drivers/power/supply/macsmc_power.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/power/supply/macsmc_power.c b/drivers/power/supply/macsmc_power.c index dc357334407d6b..be3df3578ab245 100644 --- a/drivers/power/supply/macsmc_power.c +++ b/drivers/power/supply/macsmc_power.c @@ -136,6 +136,15 @@ static int macsmc_battery_get_property(struct power_supply *psy, ret = apple_smc_read_s64(power->smc, SMC_KEY(BAAC), &vs64); val->intval = vs64; break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0CT), &vu16); + val->intval = vu16; + break; + case POWER_SUPPLY_PROP_HEALTH: + ret = apple_smc_read_flag(power->smc, SMC_KEY(BBAD)); + val->intval = ret == 1 ? POWER_SUPPLY_HEALTH_DEAD : POWER_SUPPLY_HEALTH_GOOD; + ret = ret < 0 ? ret : 0; + break; case POWER_SUPPLY_PROP_MODEL_NAME: val->strval = power->model_name; break; @@ -167,6 +176,8 @@ static enum power_supply_property macsmc_battery_props[] = { POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_SERIAL_NUMBER, }; From 790fac6ba0705a256fe820111bbef10692110221 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 8 Feb 2022 11:01:17 +0900 Subject: [PATCH 0381/1020] power: supply: macsmc_power: Add present prop Signed-off-by: Hector Martin --- drivers/power/supply/macsmc_power.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/power/supply/macsmc_power.c b/drivers/power/supply/macsmc_power.c index be3df3578ab245..0e8251de4599b3 100644 --- a/drivers/power/supply/macsmc_power.c +++ b/drivers/power/supply/macsmc_power.c @@ -72,6 +72,9 @@ static int macsmc_battery_get_property(struct power_supply *psy, val->intval = macsmc_battery_get_status(power); ret = val->intval < 0 ? val->intval : 0; break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: ret = apple_smc_read_u16(power->smc, SMC_KEY(B0TE), &vu16); val->intval = vu16 == 0xffff ? 0 : vu16 * 60; @@ -160,6 +163,7 @@ static int macsmc_battery_get_property(struct power_supply *psy, static enum power_supply_property macsmc_battery_props[] = { POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, POWER_SUPPLY_PROP_CAPACITY, From baf701cf43338fa3fb909d16b2191884334d6f6c Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 15 Feb 2022 02:20:20 +0900 Subject: [PATCH 0382/1020] power: supply: macsmc_power: Add more props, rework others Signed-off-by: Hector Martin --- drivers/power/supply/macsmc_power.c | 223 +++++++++++++++++++++++++--- 1 file changed, 202 insertions(+), 21 deletions(-) diff --git a/drivers/power/supply/macsmc_power.c b/drivers/power/supply/macsmc_power.c index 0e8251de4599b3..c730640e9175b9 100644 --- a/drivers/power/supply/macsmc_power.c +++ b/drivers/power/supply/macsmc_power.c @@ -4,6 +4,7 @@ * Copyright The Asahi Linux Contributors */ +#include #include #include #include @@ -19,48 +20,177 @@ struct macsmc_power { struct power_supply *psy; char model_name[MAX_STRING_LENGTH]; char serial_number[MAX_STRING_LENGTH]; + char mfg_date[MAX_STRING_LENGTH]; struct notifier_block nb; }; +#define CHNC_BATTERY_FULL BIT(0) +#define CHNC_NO_CHARGER BIT(7) +#define CHNC_NOCHG_CH0C BIT(14) +#define CHNC_NOCHG_CH0B_CH0K BIT(15) +#define CHNC_BATTERY_FULL_2 BIT(18) +#define CHNC_BMS_BUSY BIT(23) +#define CHNC_NOAC_CH0J BIT(53) +#define CHNC_NOAC_CH0I BIT(54) + +#define CH0R_LOWER_FLAGS GENMASK(15, 0) +#define CH0R_NOAC_CH0I BIT(0) +#define CH0R_NOAC_CH0J BIT(5) +#define CH0R_BMS_BUSY BIT(8) +#define CH0R_NOAC_CH0K BIT(9) + +#define CH0X_CH0C BIT(0) +#define CH0X_CH0B BIT(1) + static int macsmc_battery_get_status(struct macsmc_power *power) { - u8 val; + u64 nocharge_flags; + u32 nopower_flags; + u16 ac_current; int ret; - ret = apple_smc_read_u8(power->smc, SMC_KEY(BSFC), &val); - if (ret) + /* + * Note: there are fallbacks in case some of these SMC keys disappear in the future + * or are not present on some machines. We treat the absence of the CHCE/CHCC/BSFC/CHSC + * flags as an error, since they are quite fundamental and simple booleans. + */ + + /* + * If power input is inhibited, we are definitely discharging. + * However, if the only reason is the BMS is doing a balancing cycle, + * go ahead and ignore that one to avoid spooking users. + */ + ret = apple_smc_read_u32(power->smc, SMC_KEY(CH0R), &nopower_flags); + if (!ret && (nopower_flags & CH0R_LOWER_FLAGS & ~CH0R_BMS_BUSY)) + return POWER_SUPPLY_STATUS_DISCHARGING; + + /* If no charger is present, we are definitely discharging. */ + ret = apple_smc_read_flag(power->smc, SMC_KEY(CHCE)); + if (ret < 0) + return ret; + else if (!ret) + return POWER_SUPPLY_STATUS_DISCHARGING; + + /* If AC is not charge capable, we are definitely discharging. */ + ret = apple_smc_read_flag(power->smc, SMC_KEY(CHCC)); + if (ret < 0) return ret; - if (val == 1) + else if (!ret) + return POWER_SUPPLY_STATUS_DISCHARGING; + + /* + * If the AC input current limit is tiny or 0, we are discharging no matter + * how much the BMS believes it can charge. + */ + ret = apple_smc_read_u16(power->smc, SMC_KEY(AC-i), &ac_current); + if (!ret && ac_current < 100) + return POWER_SUPPLY_STATUS_DISCHARGING; + + /* If the battery is full, report it as such. */ + ret = apple_smc_read_flag(power->smc, SMC_KEY(BSFC)); + if (ret < 0) + return ret; + else if (ret) return POWER_SUPPLY_STATUS_FULL; - ret = apple_smc_read_u8(power->smc, SMC_KEY(CHSC), &val); - if (ret) + /* If there are reasons we aren't charging... */ + ret = apple_smc_read_u64(power->smc, SMC_KEY(CHNC), &nocharge_flags); + if (!ret) { + /* Perhaps the battery is full after all */ + if (nocharge_flags & CHNC_BATTERY_FULL) + return POWER_SUPPLY_STATUS_FULL; + /* Or maybe the BMS is just busy doing something, if so call it charging anyway */ + else if (nocharge_flags == CHNC_BMS_BUSY) + return POWER_SUPPLY_STATUS_CHARGING; + /* If we have other reasons we aren't charging, say we aren't */ + else if (nocharge_flags) + return POWER_SUPPLY_STATUS_NOT_CHARGING; + /* Else we're either charging or about to charge */ + else + return POWER_SUPPLY_STATUS_CHARGING; + } + + /* As a fallback, use the system charging flag. */ + ret = apple_smc_read_flag(power->smc, SMC_KEY(CHSC)); + if (ret < 0) return ret; - if (val == 1) + if (!ret) + return POWER_SUPPLY_STATUS_NOT_CHARGING; + else return POWER_SUPPLY_STATUS_CHARGING; +} - ret = apple_smc_read_u8(power->smc, SMC_KEY(CHCC), &val); +static int macsmc_battery_get_charge_behaviour(struct macsmc_power *power) +{ + int ret; + u8 val; + + /* CH0I returns a bitmask like the low byte of CH0R */ + ret = apple_smc_read_u8(power->smc, SMC_KEY(CH0I), &val); if (ret) return ret; - if (val == 0) - return POWER_SUPPLY_STATUS_DISCHARGING; + if (val & CH0R_NOAC_CH0I) + return POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE; - ret = apple_smc_read_u8(power->smc, SMC_KEY(CHCE), &val); + /* CH0C returns a bitmask containing CH0B/CH0C flags */ + ret = apple_smc_read_u8(power->smc, SMC_KEY(CH0C), &val); if (ret) return ret; - if (val == 0) - return POWER_SUPPLY_STATUS_DISCHARGING; + if (val & CH0X_CH0C) + return POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE; else - return POWER_SUPPLY_STATUS_NOT_CHARGING; + return POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO; +} + +static int macsmc_battery_set_charge_behaviour(struct macsmc_power *power, int val) +{ + u8 ch0i, ch0c; + int ret; + + /* + * CH0I/CH0C are "hard" controls that will allow the battery to run down to 0. + * CH0K/CH0B are "soft" controls that are reset to 0 when SOC drops below 50%; + * we don't expose these yet. + */ + + switch (val) { + case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO: + ch0i = ch0c = 0; + break; + case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE: + ch0i = 0; + ch0c = 1; + break; + case POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE: + ch0i = 1; + ch0c = 0; + break; + default: + return -EINVAL; + } + ret = apple_smc_write_u8(power->smc, SMC_KEY(CH0I), ch0i); + if (ret) + return ret; + return apple_smc_write_u8(power->smc, SMC_KEY(CH0C), ch0c); +} + +static int macsmc_battery_get_date(const char *s, int *out) +{ + if (!isdigit(s[0]) || !isdigit(s[1])) + return -ENOTSUPP; + + *out = (s[0] - '0') * 10 + s[1] - '0'; + return 0; } static int macsmc_battery_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) + enum power_supply_property psp, + union power_supply_propval *val) { struct macsmc_power *power = power_supply_get_drvdata(psy); int ret = 0; + u8 vu8; u16 vu16; u32 vu32; s16 vs16; @@ -75,6 +205,10 @@ static int macsmc_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_PRESENT: val->intval = 1; break; + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + val->intval = macsmc_battery_get_charge_behaviour(power); + ret = val->intval < 0 ? val->intval : 0; + break; case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: ret = apple_smc_read_u16(power->smc, SMC_KEY(B0TE), &vu16); val->intval = vu16 == 0xffff ? 0 : vu16 * 60; @@ -143,6 +277,9 @@ static int macsmc_battery_get_property(struct power_supply *psy, ret = apple_smc_read_u16(power->smc, SMC_KEY(B0CT), &vu16); val->intval = vu16; break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_SYSTEM; + break; case POWER_SUPPLY_PROP_HEALTH: ret = apple_smc_read_flag(power->smc, SMC_KEY(BBAD)); val->intval = ret == 1 ? POWER_SUPPLY_HEALTH_DEAD : POWER_SUPPLY_HEALTH_GOOD; @@ -154,6 +291,16 @@ static int macsmc_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_SERIAL_NUMBER: val->strval = power->serial_number; break; + case POWER_SUPPLY_PROP_MANUFACTURE_YEAR: + ret = macsmc_battery_get_date(&power->mfg_date[0], &val->intval); + val->intval += 2000 - 8; /* -8 is a fixup for a firmware bug... */ + break; + case POWER_SUPPLY_PROP_MANUFACTURE_MONTH: + ret = macsmc_battery_get_date(&power->mfg_date[2], &val->intval); + break; + case POWER_SUPPLY_PROP_MANUFACTURE_DAY: + ret = macsmc_battery_get_date(&power->mfg_date[4], &val->intval); + break; default: return -EINVAL; } @@ -161,9 +308,36 @@ static int macsmc_battery_get_property(struct power_supply *psy, return ret; } +static int macsmc_battery_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct macsmc_power *power = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + return macsmc_battery_set_charge_behaviour(power, val->intval); + default: + return -EINVAL; + } +} + +static int macsmc_battery_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + return true; + default: + return false; + } +} + + static enum power_supply_property macsmc_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR, POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, POWER_SUPPLY_PROP_CAPACITY, @@ -181,17 +355,23 @@ static enum power_supply_property macsmc_battery_props[] = { POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_SCOPE, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_SERIAL_NUMBER, + POWER_SUPPLY_PROP_MANUFACTURE_YEAR, + POWER_SUPPLY_PROP_MANUFACTURE_MONTH, + POWER_SUPPLY_PROP_MANUFACTURE_DAY, }; static const struct power_supply_desc macsmc_battery_desc = { - .name = "macsmc-battery", - .type = POWER_SUPPLY_TYPE_BATTERY, - .get_property = macsmc_battery_get_property, - .properties = macsmc_battery_props, - .num_properties = ARRAY_SIZE(macsmc_battery_props), + .name = "macsmc-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = macsmc_battery_get_property, + .set_property = macsmc_battery_set_property, + .property_is_writeable = macsmc_battery_property_is_writeable, + .properties = macsmc_battery_props, + .num_properties = ARRAY_SIZE(macsmc_battery_props), }; static int macsmc_power_event(struct notifier_block *nb, unsigned long event, void *data) @@ -232,6 +412,7 @@ static int macsmc_power_probe(struct platform_device *pdev) /* Fetch string properties */ apple_smc_read(smc, SMC_KEY(BMDN), power->model_name, sizeof(power->model_name) - 1); apple_smc_read(smc, SMC_KEY(BMSN), power->serial_number, sizeof(power->serial_number) - 1); + apple_smc_read(smc, SMC_KEY(BMDT), power->mfg_date, sizeof(power->mfg_date) - 1); psy_cfg.drv_data = power; power->psy = devm_power_supply_register(&pdev->dev, &macsmc_battery_desc, &psy_cfg); From 376a67a6b7d91ccddeb936e3307251a78512e87b Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 15 Feb 2022 02:23:33 +0900 Subject: [PATCH 0383/1020] power: supply: macsmc_power: Use BUIC instead of BRSC for charge Signed-off-by: Hector Martin --- drivers/power/supply/macsmc_power.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/macsmc_power.c b/drivers/power/supply/macsmc_power.c index c730640e9175b9..cb2e8e0414fdcf 100644 --- a/drivers/power/supply/macsmc_power.c +++ b/drivers/power/supply/macsmc_power.c @@ -218,8 +218,8 @@ static int macsmc_battery_get_property(struct power_supply *psy, val->intval = vu16 == 0xffff ? 0 : vu16 * 60; break; case POWER_SUPPLY_PROP_CAPACITY: - ret = apple_smc_read_u16(power->smc, SMC_KEY(BRSC), &vu16); - val->intval = vu16; + ret = apple_smc_read_u8(power->smc, SMC_KEY(BUIC), &vu8); + val->intval = vu8; break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: ret = apple_smc_read_u16(power->smc, SMC_KEY(B0AV), &vu16); From d58c6eb5f25096f9b7e3d1f235535c242031e135 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 15 Feb 2022 02:24:13 +0900 Subject: [PATCH 0384/1020] power: supply: macsmc_power: Turn off OBC flags if macOS left them on Signed-off-by: Hector Martin --- drivers/power/supply/macsmc_power.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/power/supply/macsmc_power.c b/drivers/power/supply/macsmc_power.c index cb2e8e0414fdcf..61a2133e9a4734 100644 --- a/drivers/power/supply/macsmc_power.c +++ b/drivers/power/supply/macsmc_power.c @@ -414,6 +414,10 @@ static int macsmc_power_probe(struct platform_device *pdev) apple_smc_read(smc, SMC_KEY(BMSN), power->serial_number, sizeof(power->serial_number) - 1); apple_smc_read(smc, SMC_KEY(BMDT), power->mfg_date, sizeof(power->mfg_date) - 1); + /* Turn off the "optimized battery charging" flags, in case macOS left them on */ + apple_smc_write_u8(power->smc, SMC_KEY(CH0K), 0); + apple_smc_write_u8(power->smc, SMC_KEY(CH0B), 0); + psy_cfg.drv_data = power; power->psy = devm_power_supply_register(&pdev->dev, &macsmc_battery_desc, &psy_cfg); if (IS_ERR(power->psy)) { From cbcc1418d7db0f7699acb9740f1de854c8e77a52 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 15 Feb 2022 02:24:42 +0900 Subject: [PATCH 0385/1020] power: supply: macsmc_power: Add AC power supply Signed-off-by: Hector Martin --- drivers/power/supply/macsmc_power.c | 72 ++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 7 deletions(-) diff --git a/drivers/power/supply/macsmc_power.c b/drivers/power/supply/macsmc_power.c index 61a2133e9a4734..72ac697f55ec3d 100644 --- a/drivers/power/supply/macsmc_power.c +++ b/drivers/power/supply/macsmc_power.c @@ -17,11 +17,14 @@ struct macsmc_power { struct device *dev; struct apple_smc *smc; - struct power_supply *psy; + + struct power_supply *batt; char model_name[MAX_STRING_LENGTH]; char serial_number[MAX_STRING_LENGTH]; char mfg_date[MAX_STRING_LENGTH]; + struct power_supply *ac; + struct notifier_block nb; }; @@ -333,7 +336,6 @@ static int macsmc_battery_property_is_writeable(struct power_supply *psy, } } - static enum power_supply_property macsmc_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, @@ -374,6 +376,54 @@ static const struct power_supply_desc macsmc_battery_desc = { .num_properties = ARRAY_SIZE(macsmc_battery_props), }; +static int macsmc_ac_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct macsmc_power *power = power_supply_get_drvdata(psy); + int ret = 0; + u16 vu16; + u32 vu32; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + ret = apple_smc_read_u32(power->smc, SMC_KEY(CHIS), &vu32); + val->intval = !!vu32; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = apple_smc_read_u16(power->smc, SMC_KEY(AC-n), &vu16); + val->intval = vu16 * 1000; + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = apple_smc_read_u16(power->smc, SMC_KEY(AC-i), &vu16); + val->intval = vu16 * 1000; + break; + case POWER_SUPPLY_PROP_INPUT_POWER_LIMIT: + ret = apple_smc_read_u32(power->smc, SMC_KEY(ACPW), &vu32); + val->intval = vu32 * 1000; + break; + default: + return -EINVAL; + } + + return ret; +} + +static enum power_supply_property macsmc_ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_INPUT_POWER_LIMIT, +}; + +static const struct power_supply_desc macsmc_ac_desc = { + .name = "macsmc-ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .get_property = macsmc_ac_get_property, + .properties = macsmc_ac_props, + .num_properties = ARRAY_SIZE(macsmc_ac_props), +}; + static int macsmc_power_event(struct notifier_block *nb, unsigned long event, void *data) { struct macsmc_power *power = container_of(nb, struct macsmc_power, nb); @@ -382,7 +432,8 @@ static int macsmc_power_event(struct notifier_block *nb, unsigned long event, vo bool charging = (event & 0xff) != 0; dev_info(power->dev, "Charging: %d\n", charging); - power_supply_changed(power->psy); + power_supply_changed(power->batt); + power_supply_changed(power->ac); return NOTIFY_OK; } @@ -419,10 +470,17 @@ static int macsmc_power_probe(struct platform_device *pdev) apple_smc_write_u8(power->smc, SMC_KEY(CH0B), 0); psy_cfg.drv_data = power; - power->psy = devm_power_supply_register(&pdev->dev, &macsmc_battery_desc, &psy_cfg); - if (IS_ERR(power->psy)) { - dev_err(&pdev->dev, "Failed to register power supply\n"); - ret = PTR_ERR(power->psy); + power->batt = devm_power_supply_register(&pdev->dev, &macsmc_battery_desc, &psy_cfg); + if (IS_ERR(power->batt)) { + dev_err(&pdev->dev, "Failed to register battery\n"); + ret = PTR_ERR(power->batt); + return ret; + } + + power->ac = devm_power_supply_register(&pdev->dev, &macsmc_ac_desc, &psy_cfg); + if (IS_ERR(power->ac)) { + dev_err(&pdev->dev, "Failed to register AC adapter\n"); + ret = PTR_ERR(power->ac); return ret; } From 756a6f4e222320d04c098a41df70b33edb9e00d2 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 8 Feb 2022 19:17:40 +0900 Subject: [PATCH 0386/1020] power: reset: macsmc-reboot: Add driver for rebooting via Apple SMC This driver implements the reboot/shutdown support exposed by the SMC on Apple Silicon machines, such as Apple M1 Macs. Signed-off-by: Hector Martin --- drivers/power/reset/Kconfig | 12 + drivers/power/reset/Makefile | 1 + drivers/power/reset/macsmc-reboot.c | 336 ++++++++++++++++++++++++++++ 3 files changed, 349 insertions(+) create mode 100644 drivers/power/reset/macsmc-reboot.c diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index fece990af4a75b..d5c1d3fff97f6d 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -117,6 +117,18 @@ config POWER_RESET_LINKSTATION Say Y here if you have a Buffalo LinkStation LS421D/E. +config POWER_RESET_MACSMC + tristate "Apple SMC reset/power-off driver" + depends on ARCH_APPLE || COMPILE_TEST + depends on APPLE_SMC + depends on OF + default ARCH_APPLE + help + This driver supports reset and power-off on Apple Mac machines + that implement this functionality via the SMC. + + Say Y here if you have an Apple Silicon Mac. + config POWER_RESET_MSM bool "Qualcomm MSM power-off driver" depends on ARCH_QCOM diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index a95d1bd275d187..615536ca49e9cc 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o obj-$(CONFIG_POWER_RESET_LINKSTATION) += linkstation-poweroff.o +obj-$(CONFIG_POWER_RESET_MACSMC) += macsmc-reboot.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o obj-$(CONFIG_POWER_RESET_MT6323) += mt6323-poweroff.o obj-$(CONFIG_POWER_RESET_QCOM_PON) += qcom-pon.o diff --git a/drivers/power/reset/macsmc-reboot.c b/drivers/power/reset/macsmc-reboot.c new file mode 100644 index 00000000000000..c33ba2a7852d48 --- /dev/null +++ b/drivers/power/reset/macsmc-reboot.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SMC Reboot/Poweroff Handler + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct macsmc_reboot_nvmem { + struct nvmem_cell *shutdown_flag; + struct nvmem_cell *pm_setting; + struct nvmem_cell *boot_stage; + struct nvmem_cell *boot_error_count; + struct nvmem_cell *panic_count; +}; + +static const char *nvmem_names[] = { + "shutdown_flag", + "pm_setting", + "boot_stage", + "boot_error_count", + "panic_count", +}; + +enum boot_stage { + BOOT_STAGE_SHUTDOWN = 0x00, /* Clean shutdown */ + BOOT_STAGE_IBOOT_DONE = 0x2f, /* Last stage of bootloader */ + BOOT_STAGE_KERNEL_STARTED = 0x30, /* Normal OS booting */ +}; + +enum pm_setting { + PM_SETTING_AC_POWER_RESTORE = 0x02, + PM_SETTING_AC_POWER_OFF = 0x03, +}; + +static const char *ac_power_modes[] = { "off", "restore" }; + +static int ac_power_mode_map[] = { + PM_SETTING_AC_POWER_OFF, + PM_SETTING_AC_POWER_RESTORE, +}; + +struct macsmc_reboot { + struct device *dev; + struct apple_smc *smc; + struct notifier_block reboot_notify; + + union { + struct macsmc_reboot_nvmem nvm; + struct nvmem_cell *nvm_cells[ARRAY_SIZE(nvmem_names)]; + }; +}; + +/* Helpers to read/write a u8 given a struct nvmem_cell */ +static int nvmem_cell_get_u8(struct nvmem_cell *cell) +{ + size_t len; + u8 val; + void *ret = nvmem_cell_read(cell, &len); + + if (IS_ERR(ret)) + return PTR_ERR(ret); + + if (len < 1) { + kfree(ret); + return -EINVAL; + } + + val = *(u8 *)ret; + kfree(ret); + return val; +} + +static int nvmem_cell_set_u8(struct nvmem_cell *cell, u8 val) +{ + return nvmem_cell_write(cell, &val, sizeof(val)); +} + +static ssize_t macsmc_ac_power_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + struct macsmc_reboot *reboot = dev_get_drvdata(dev); + int mode; + int ret; + + mode = sysfs_match_string(ac_power_modes, buf); + if (mode < 0) + return mode; + + ret = nvmem_cell_set_u8(reboot->nvm.pm_setting, ac_power_mode_map[mode]); + if (ret < 0) + return ret; + + return n; +} + +static ssize_t macsmc_ac_power_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct macsmc_reboot *reboot = dev_get_drvdata(dev); + int len = 0; + int i; + int mode = nvmem_cell_get_u8(reboot->nvm.pm_setting); + + if (mode < 0) + return mode; + + for (i = 0; i < ARRAY_SIZE(ac_power_mode_map); i++) + if (mode == ac_power_mode_map[i]) + len += scnprintf(buf+len, PAGE_SIZE-len, + "[%s] ", ac_power_modes[i]); + else + len += scnprintf(buf+len, PAGE_SIZE-len, + "%s ", ac_power_modes[i]); + buf[len-1] = '\n'; + return len; +} +static DEVICE_ATTR(ac_power_mode, 0644, macsmc_ac_power_mode_show, + macsmc_ac_power_mode_store); + +/* + * SMC 'MBSE' key actions: + * + * 'offw' - shutdown warning + * 'slpw' - sleep warning + * 'rest' - restart warning + * 'off1' - shutdown (needs PMU bit set to stay on) + * 'susp' - suspend + * 'phra' - restart ("PE Halt Restart Action"?) + * 'panb' - panic beginning + * 'pane' - panic end + */ + +static int macsmc_power_off(struct sys_off_data *data) +{ + struct macsmc_reboot *reboot = data->cb_data; + + dev_info(reboot->dev, "Issuing power off (off1)\n"); + + if (apple_smc_write_u32_atomic(reboot->smc, SMC_KEY(MBSE), SMC_KEY(off1)) < 0) { + dev_err(reboot->dev, "Failed to issue MBSE = off1 (power_off)\n"); + } else { + mdelay(100); + WARN_ON(1); + } + + return NOTIFY_OK; +} + +static int macsmc_restart(struct sys_off_data *data) +{ + struct macsmc_reboot *reboot = data->cb_data; + + dev_info(reboot->dev, "Issuing restart (phra)\n"); + + if (apple_smc_write_u32_atomic(reboot->smc, SMC_KEY(MBSE), SMC_KEY(phra)) < 0) { + dev_err(reboot->dev, "Failed to issue MBSE = phra (restart)\n"); + } else { + mdelay(100); + WARN_ON(1); + } + + return NOTIFY_OK; +} + +static int macsmc_reboot_notify(struct notifier_block *this, unsigned long action, void *data) +{ + struct macsmc_reboot *reboot = container_of(this, struct macsmc_reboot, reboot_notify); + u32 val; + u8 shutdown_flag; + + switch (action) { + case SYS_RESTART: + val = SMC_KEY(rest); + shutdown_flag = 0; + break; + case SYS_POWER_OFF: + val = SMC_KEY(offw); + shutdown_flag = 1; + break; + default: + return NOTIFY_DONE; + } + + dev_info(reboot->dev, "Preparing for reboot (%p4ch)\n", &val); + + /* On the Mac Mini, this will turn off the LED for power off */ + if (apple_smc_write_u32(reboot->smc, SMC_KEY(MBSE), val) < 0) + dev_err(reboot->dev, "Failed to issue MBSE = %p4ch (reboot_prepare)\n", &val); + + /* Set the boot_stage to 0, which means we're doing a clean shutdown/reboot. */ + if (reboot->nvm.boot_stage && + nvmem_cell_set_u8(reboot->nvm.boot_stage, BOOT_STAGE_SHUTDOWN) < 0) + dev_err(reboot->dev, "Failed to write boot_stage\n"); + + /* + * Set the PMU flag to actually reboot into the off state. + * Without this, the device will just reboot. We make it optional in case it is no longer + * necessary on newer hardware. + */ + if (reboot->nvm.shutdown_flag && + nvmem_cell_set_u8(reboot->nvm.shutdown_flag, shutdown_flag) < 0) + dev_err(reboot->dev, "Failed to write shutdown_flag\n"); + + return NOTIFY_OK; +} + +static void macsmc_power_init_error_counts(struct macsmc_reboot *reboot) +{ + int boot_error_count, panic_count; + + if (!reboot->nvm.boot_error_count || !reboot->nvm.panic_count) + return; + + boot_error_count = nvmem_cell_get_u8(reboot->nvm.boot_error_count); + if (boot_error_count < 0) { + dev_err(reboot->dev, "Failed to read boot_error_count (%d)\n", boot_error_count); + return; + } + + panic_count = nvmem_cell_get_u8(reboot->nvm.panic_count); + if (panic_count < 0) { + dev_err(reboot->dev, "Failed to read panic_count (%d)\n", panic_count); + return; + } + + if (!boot_error_count && !panic_count) + return; + + dev_warn(reboot->dev, "PMU logged %d boot error(s) and %d panic(s)\n", + boot_error_count, panic_count); + + if (nvmem_cell_set_u8(reboot->nvm.panic_count, 0) < 0) + dev_err(reboot->dev, "Failed to reset panic_count\n"); + if (nvmem_cell_set_u8(reboot->nvm.boot_error_count, 0) < 0) + dev_err(reboot->dev, "Failed to reset boot_error_count\n"); +} + +static int macsmc_reboot_probe(struct platform_device *pdev) +{ + struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); + struct macsmc_reboot *reboot; + int ret, i; + + /* Ignore devices without this functionality */ + if (!apple_smc_key_exists(smc, SMC_KEY(MBSE))) + return -ENODEV; + + reboot = devm_kzalloc(&pdev->dev, sizeof(*reboot), GFP_KERNEL); + if (!reboot) + return -ENOMEM; + + reboot->dev = &pdev->dev; + reboot->smc = smc; + + platform_set_drvdata(pdev, reboot); + + pdev->dev.of_node = of_get_child_by_name(pdev->dev.parent->of_node, "reboot"); + + for (i = 0; i < ARRAY_SIZE(nvmem_names); i++) { + struct nvmem_cell *cell; + cell = devm_nvmem_cell_get(&pdev->dev, + nvmem_names[i]); + if (IS_ERR(cell)) { + if (PTR_ERR(cell) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_warn(&pdev->dev, "Missing NVMEM cell %s (%ld)\n", + nvmem_names[i], PTR_ERR(cell)); + /* Non fatal, we'll deal with it */ + cell = NULL; + } + reboot->nvm_cells[i] = cell; + } + + /* Set the boot_stage to indicate we're running the OS kernel */ + if (reboot->nvm.boot_stage && + nvmem_cell_set_u8(reboot->nvm.boot_stage, BOOT_STAGE_KERNEL_STARTED) < 0) + dev_err(reboot->dev, "Failed to write boot_stage\n"); + + /* Display and clear the error counts */ + macsmc_power_init_error_counts(reboot); + + reboot->reboot_notify.notifier_call = macsmc_reboot_notify; + + ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_HIGH, + macsmc_power_off, reboot); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to register power-off handler\n"); + + ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART, SYS_OFF_PRIO_HIGH, + macsmc_restart, reboot); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to register restart handler\n"); + + ret = devm_register_reboot_notifier(&pdev->dev, &reboot->reboot_notify); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to register reboot notifier\n"); + + dev_info(&pdev->dev, "Handling reboot and poweroff requests via SMC\n"); + + if (device_create_file(&pdev->dev, &dev_attr_ac_power_mode)) + dev_warn(&pdev->dev, "could not create sysfs file\n"); + + return 0; +} + +static int macsmc_reboot_remove(struct platform_device *pdev) +{ + device_remove_file(&pdev->dev, &dev_attr_ac_power_mode); + + return 0; +} + + +static struct platform_driver macsmc_reboot_driver = { + .driver = { + .name = "macsmc-reboot", + .owner = THIS_MODULE, + }, + .probe = macsmc_reboot_probe, + .remove = macsmc_reboot_remove, +}; +module_platform_driver(macsmc_reboot_driver); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("Apple SMC reboot/poweroff driver"); +MODULE_AUTHOR("Hector Martin "); +MODULE_ALIAS("platform:macsmc-reboot"); From c2efa33466b4b9197d13641e2da80238b091a09c Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 15 Feb 2022 18:47:13 +0900 Subject: [PATCH 0387/1020] rtc: Add new rtc-macsmc driver for Apple Silicon Macs Apple Silicon Macs (M1, etc.) have an RTC that is part of the PMU IC, but most of the PMU functionality is abstracted out by the SMC. On T600x machines, the RTC counter must be accessed via the SMC to get full functionality, and it seems likely that future machines will move towards making SMC handle all RTC functionality. The SMC RTC counter access is implemented on all current machines as of the time of this writing, on firmware 12.x. However, the RTC offset (needed to set the time) is still only accessible via direct PMU access. To handle this, we expose the RTC offset as an NVMEM cell from the SPMI PMU device node, and this driver consumes that cell and uses it to compute/set the current time. Alarm functionality is not yet implemented. This would also go via the PMU today, but could change in the future. Signed-off-by: Hector Martin --- drivers/rtc/Kconfig | 13 ++++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-macsmc.c | 130 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 drivers/rtc/rtc-macsmc.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index c63e32d012f23c..cb6522ee760db4 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -2033,4 +2033,17 @@ config RTC_DRV_SSD202D This driver can also be built as a module, if so, the module will be called "rtc-ssd20xd". +config RTC_DRV_MACSMC + tristate "Apple Mac SMC RTC" + depends on ARCH_APPLE || COMPILE_TEST + depends on APPLE_SMC + depends on OF + default ARCH_APPLE + help + If you say yes here you get support for RTC functions + inside Apple SPMI PMUs. + + To compile this driver as a module, choose M here: the + module will be called rtc-macsmc. + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 6efff381c484d5..80b4ff89ae49b2 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -89,6 +89,7 @@ obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o obj-$(CONFIG_RTC_DRV_MA35D1) += rtc-ma35d1.o +obj-$(CONFIG_RTC_DRV_MACSMC) += rtc-macsmc.o obj-$(CONFIG_RTC_DRV_MAX31335) += rtc-max31335.o obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o diff --git a/drivers/rtc/rtc-macsmc.c b/drivers/rtc/rtc-macsmc.c new file mode 100644 index 00000000000000..34730c92524883 --- /dev/null +++ b/drivers/rtc/rtc-macsmc.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SMC RTC driver + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 48-bit RTC */ +#define RTC_BYTES 6 +#define RTC_BITS (8 * RTC_BYTES) + +/* 32768 Hz clock */ +#define RTC_SEC_SHIFT 15 + +struct macsmc_rtc { + struct device *dev; + struct apple_smc *smc; + struct rtc_device *rtc_dev; + struct nvmem_cell *rtc_offset; +}; + +static int macsmc_rtc_get_time(struct device *dev, struct rtc_time *tm) +{ + struct macsmc_rtc *rtc = dev_get_drvdata(dev); + u64 ctr = 0, off = 0; + time64_t now; + void *p_off; + size_t len; + int ret; + + ret = apple_smc_read(rtc->smc, SMC_KEY(CLKM), &ctr, RTC_BYTES); + if (ret != RTC_BYTES) + return ret < 0 ? ret : -EIO; + + p_off = nvmem_cell_read(rtc->rtc_offset, &len); + if (IS_ERR(p_off)) + return PTR_ERR(p_off); + if (len < RTC_BYTES) { + kfree(p_off); + return -EIO; + } + + memcpy(&off, p_off, RTC_BYTES); + kfree(p_off); + + /* Sign extend from 48 to 64 bits, then arithmetic shift right 15 bits to get seconds */ + now = sign_extend64(ctr + off, RTC_BITS - 1) >> RTC_SEC_SHIFT; + rtc_time64_to_tm(now, tm); + + return ret; +} + +static int macsmc_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct macsmc_rtc *rtc = dev_get_drvdata(dev); + u64 ctr = 0, off = 0; + int ret; + + ret = apple_smc_read(rtc->smc, SMC_KEY(CLKM), &ctr, RTC_BYTES); + if (ret != RTC_BYTES) + return ret < 0 ? ret : -EIO; + + /* This sets the offset such that the set second begins now */ + off = (rtc_tm_to_time64(tm) << RTC_SEC_SHIFT) - ctr; + return nvmem_cell_write(rtc->rtc_offset, &off, RTC_BYTES); +} + +static const struct rtc_class_ops macsmc_rtc_ops = { + .read_time = macsmc_rtc_get_time, + .set_time = macsmc_rtc_set_time, +}; + +static int macsmc_rtc_probe(struct platform_device *pdev) +{ + struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); + struct macsmc_rtc *rtc; + + /* Ignore devices without this functionality */ + if (!apple_smc_key_exists(smc, SMC_KEY(CLKM))) + return -ENODEV; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->dev = &pdev->dev; + rtc->smc = smc; + + pdev->dev.of_node = of_get_child_by_name(pdev->dev.parent->of_node, "rtc"); + + rtc->rtc_offset = devm_nvmem_cell_get(&pdev->dev, "rtc_offset"); + if (IS_ERR(rtc->rtc_offset)) + return dev_err_probe(&pdev->dev, PTR_ERR(rtc->rtc_offset), + "Failed to get rtc_offset NVMEM cell\n"); + + rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + rtc->rtc_dev->ops = &macsmc_rtc_ops; + rtc->rtc_dev->range_min = S64_MIN >> (RTC_SEC_SHIFT + (64 - RTC_BITS)); + rtc->rtc_dev->range_max = S64_MAX >> (RTC_SEC_SHIFT + (64 - RTC_BITS)); + + platform_set_drvdata(pdev, rtc); + + return devm_rtc_register_device(rtc->rtc_dev); +} + +static struct platform_driver macsmc_rtc_driver = { + .driver = { + .name = "macsmc-rtc", + .owner = THIS_MODULE, + }, + .probe = macsmc_rtc_probe, +}; +module_platform_driver(macsmc_rtc_driver); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("Apple SMC RTC driver"); +MODULE_AUTHOR("Hector Martin "); +MODULE_ALIAS("platform:macsmc-rtc"); From fa997f3e0c3141e01e5e71a43f933a5f6aa710ea Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 4 Mar 2022 19:21:19 +0900 Subject: [PATCH 0388/1020] Input: macsmc-hid: New driver to handle the Apple Mac SMC buttons/lid This driver implements power button and lid switch support for Apple Mac devices using SMC controllers driven by the macsmc driver. In addition to basic input support, this also responds to the final shutdown warning (when the power button is held down long enough) by doing an emergency kernel poweroff. This allows the NVMe controller to be cleanly shut down, which prevents data loss for in-cache data. Signed-off-by: Hector Martin --- drivers/input/misc/Kconfig | 12 +++ drivers/input/misc/Makefile | 1 + drivers/input/misc/macsmc-hid.c | 157 ++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 drivers/input/misc/macsmc-hid.c diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 6ba984d7f0b186..52ec588b0476ff 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -939,4 +939,16 @@ config INPUT_STPMIC1_ONKEY To compile this driver as a module, choose M here: the module will be called stpmic1_onkey. +config INPUT_MACSMC_HID + tristate "Apple Mac SMC lid/buttons" + depends on APPLE_SMC + default ARCH_APPLE + help + Say Y here if you want to use the input events delivered via the + SMC controller on Apple Mac machines using the macsmc driver. + This includes lid open/close and the power button. + + To compile this driver as a module, choose M here: the + module will be called macsmc-hid. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 04296a4abe8e87..6dc2e375211f3d 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_INPUT_IQS7222) += iqs7222.o obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o +obj-$(CONFIG_INPUT_MACSMC_HID) += macsmc-hid.o obj-$(CONFIG_INPUT_MAX77650_ONKEY) += max77650-onkey.o obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o diff --git a/drivers/input/misc/macsmc-hid.c b/drivers/input/misc/macsmc-hid.c new file mode 100644 index 00000000000000..1c0f7476081f30 --- /dev/null +++ b/drivers/input/misc/macsmc-hid.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SMC input event driver + * Copyright The Asahi Linux Contributors + * + * This driver exposes HID events from the SMC as an input device. + * This includes the lid open/close and power button notifications. + */ + +#include +#include +#include +#include +#include + +struct macsmc_hid { + struct device *dev; + struct apple_smc *smc; + struct input_dev *input; + struct notifier_block nb; +}; + +#define SMC_EV_BTN 0x7201 +#define SMC_EV_LID 0x7203 + +#define BTN_POWER 0x06 +#define BTN_POWER_HELD1 0xfe +#define BTN_POWER_HELD2 0x00 + +static int macsmc_hid_event(struct notifier_block *nb, unsigned long event, void *data) +{ + struct macsmc_hid *smchid = container_of(nb, struct macsmc_hid, nb); + u16 type = event >> 16; + u8 d1 = (event >> 8) & 0xff; + u8 d2 = event & 0xff; + + switch (type) { + case SMC_EV_BTN: + switch (d1) { + case BTN_POWER: + input_report_key(smchid->input, KEY_POWER, d2); + input_sync(smchid->input); + break; + case BTN_POWER_HELD1: + /* + * TODO: is this pre-warning useful? + */ + if (d2) + dev_warn(smchid->dev, "Power button held down\n"); + break; + case BTN_POWER_HELD2: + /* + * If we get here, we have about 4 seconds before forced shutdown. + * Try to do an emergency shutdown to make sure the NVMe cache is + * flushed. macOS actually does this by panicing (!)... + */ + if (d2) { + dev_crit(smchid->dev, "Triggering forced shutdown!\n"); + if (kernel_can_power_off()) + kernel_power_off(); + else /* Missing macsmc-reboot driver? */ + kernel_restart("SMC power button triggered restart"); + } + break; + default: + dev_info(smchid->dev, "Unknown SMC button event: %02x %02x\n", d1, d2); + break; + } + return NOTIFY_OK; + case SMC_EV_LID: + input_report_switch(smchid->input, SW_LID, d1); + input_sync(smchid->input); + return NOTIFY_OK; + } + + return NOTIFY_DONE; +} + +static int macsmc_hid_probe(struct platform_device *pdev) +{ + struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); + struct macsmc_hid *smchid; + bool have_lid, have_power; + int ret; + + have_lid = apple_smc_key_exists(smc, SMC_KEY(MSLD)); + have_power = apple_smc_key_exists(smc, SMC_KEY(bHLD)); + + if (!have_lid && !have_power) + return -ENODEV; + + smchid = devm_kzalloc(&pdev->dev, sizeof(*smchid), GFP_KERNEL); + if (!smchid) + return -ENOMEM; + + smchid->dev = &pdev->dev; + smchid->smc = smc; + + smchid->input = devm_input_allocate_device(&pdev->dev); + if (!smchid->input) + return -ENOMEM; + + smchid->input->phys = "macsmc-hid (0)"; + smchid->input->name = "Apple SMC power/lid events"; + + if (have_lid) + input_set_capability(smchid->input, EV_SW, SW_LID); + if (have_power) + input_set_capability(smchid->input, EV_KEY, KEY_POWER); + + ret = input_register_device(smchid->input); + if (ret) { + dev_err(&pdev->dev, "Failed to register input device: %d\n", ret); + return ret; + } + + if (have_lid) { + u8 val; + + ret = apple_smc_read_u8(smc, SMC_KEY(MSLD), &val); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read initial lid state\n"); + } else { + input_report_switch(smchid->input, SW_LID, val); + } + } + if (have_power) { + u32 val; + + ret = apple_smc_read_u32(smc, SMC_KEY(bHLD), &val); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read initial power button state\n"); + } else { + input_report_key(smchid->input, KEY_POWER, val & 1); + } + } + + input_sync(smchid->input); + + smchid->nb.notifier_call = macsmc_hid_event; + apple_smc_register_notifier(smc, &smchid->nb); + + return 0; +} + +static struct platform_driver macsmc_hid_driver = { + .driver = { + .name = "macsmc-hid", + }, + .probe = macsmc_hid_probe, +}; +module_platform_driver(macsmc_hid_driver); + +MODULE_AUTHOR("Hector Martin "); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("Apple SMC GPIO driver"); +MODULE_ALIAS("platform:macsmc-hid"); From 35e67f61ab0d5fd1e6961baa35cf13ce2c3fdca2 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sat, 5 Mar 2022 01:08:04 +0900 Subject: [PATCH 0389/1020] Input: macsmc-hid: Support button/lid wakeups Signed-off-by: Hector Martin --- drivers/input/misc/macsmc-hid.c | 41 +++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/macsmc-hid.c b/drivers/input/misc/macsmc-hid.c index 1c0f7476081f30..c4c7440d4ee68c 100644 --- a/drivers/input/misc/macsmc-hid.c +++ b/drivers/input/misc/macsmc-hid.c @@ -18,6 +18,7 @@ struct macsmc_hid { struct apple_smc *smc; struct input_dev *input; struct notifier_block nb; + bool wakeup_mode; }; #define SMC_EV_BTN 0x7201 @@ -38,8 +39,15 @@ static int macsmc_hid_event(struct notifier_block *nb, unsigned long event, void case SMC_EV_BTN: switch (d1) { case BTN_POWER: - input_report_key(smchid->input, KEY_POWER, d2); - input_sync(smchid->input); + if (smchid->wakeup_mode) { + if (d2) { + dev_info(smchid->dev, "Button wakeup\n"); + pm_wakeup_hard_event(smchid->dev); + } + } else { + input_report_key(smchid->input, KEY_POWER, d2); + input_sync(smchid->input); + } break; case BTN_POWER_HELD1: /* @@ -68,6 +76,10 @@ static int macsmc_hid_event(struct notifier_block *nb, unsigned long event, void } return NOTIFY_OK; case SMC_EV_LID: + if (smchid->wakeup_mode && !d1) { + dev_info(smchid->dev, "Lid wakeup\n"); + pm_wakeup_hard_event(smchid->dev); + } input_report_switch(smchid->input, SW_LID, d1); input_sync(smchid->input); return NOTIFY_OK; @@ -95,6 +107,7 @@ static int macsmc_hid_probe(struct platform_device *pdev) smchid->dev = &pdev->dev; smchid->smc = smc; + platform_set_drvdata(pdev, smchid); smchid->input = devm_input_allocate_device(&pdev->dev); if (!smchid->input) @@ -140,12 +153,36 @@ static int macsmc_hid_probe(struct platform_device *pdev) smchid->nb.notifier_call = macsmc_hid_event; apple_smc_register_notifier(smc, &smchid->nb); + device_init_wakeup(&pdev->dev, 1); + + return 0; +} + +static int macsmc_hid_pm_prepare(struct device *dev) +{ + struct macsmc_hid *smchid = dev_get_drvdata(dev); + + smchid->wakeup_mode = true; return 0; } +static void macsmc_hid_pm_complete(struct device *dev) +{ + struct macsmc_hid *smchid = dev_get_drvdata(dev); + + smchid->wakeup_mode = false; +} + +static const struct dev_pm_ops macsmc_hid_pm_ops = { + .prepare = macsmc_hid_pm_prepare, + .complete = macsmc_hid_pm_complete, +}; + static struct platform_driver macsmc_hid_driver = { .driver = { .name = "macsmc-hid", + .owner = THIS_MODULE, + .pm = &macsmc_hid_pm_ops, }, .probe = macsmc_hid_probe, }; From 1cb14800a678073655b41d9653d32d9f13e907f7 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 5 May 2022 01:38:19 +0900 Subject: [PATCH 0390/1020] gpio: macsmc: Add IRQ support Signed-off-by: Hector Martin --- drivers/gpio/gpio-macsmc.c | 150 +++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/drivers/gpio/gpio-macsmc.c b/drivers/gpio/gpio-macsmc.c index ff9950afb69af0..98fc74af69d4c1 100644 --- a/drivers/gpio/gpio-macsmc.c +++ b/drivers/gpio/gpio-macsmc.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -68,10 +69,21 @@ * 3 = ? */ +#define SMC_EV_GPIO 0x7202 + struct macsmc_gpio { struct device *dev; struct apple_smc *smc; struct gpio_chip gc; + struct irq_chip ic; + struct notifier_block nb; + + struct mutex irq_mutex; + DECLARE_BITMAP(irq_supported, MAX_GPIO); + DECLARE_BITMAP(irq_enable_shadow, MAX_GPIO); + DECLARE_BITMAP(irq_enable, MAX_GPIO); + u32 irq_mode_shadow[MAX_GPIO]; + u32 irq_mode[MAX_GPIO]; int first_index; }; @@ -161,6 +173,7 @@ static int macsmc_gpio_init_valid_mask(struct gpio_chip *gc, for (i = 0; i < count; i++) { smc_key key; int gpio_nr; + u32 val; int ret = apple_smc_get_key_by_index(smcgp->smc, smcgp->first_index + i, &key); if (ret < 0) @@ -176,11 +189,127 @@ static int macsmc_gpio_init_valid_mask(struct gpio_chip *gc, } set_bit(gpio_nr, valid_mask); + + /* Check for IRQ support */ + ret = apple_smc_rw_u32(smcgp->smc, key, CMD_IRQ_MODE, &val); + if (!ret) + set_bit(gpio_nr, smcgp->irq_supported); + } + + return 0; +} + +static int macsmc_gpio_event(struct notifier_block *nb, unsigned long event, void *data) +{ + struct macsmc_gpio *smcgp = container_of(nb, struct macsmc_gpio, nb); + u16 type = event >> 16; + u8 offset = (event >> 8) & 0xff; + smc_key key = macsmc_gpio_key(offset); + unsigned long flags; + + if (type != SMC_EV_GPIO) + return NOTIFY_DONE; + + if (offset > MAX_GPIO) { + dev_err(smcgp->dev, "GPIO event index %d out of range\n", offset); + return NOTIFY_BAD; + } + + local_irq_save(flags); + generic_handle_irq_desc(irq_resolve_mapping(smcgp->gc.irq.domain, offset)); + local_irq_restore(flags); + + if (apple_smc_write_u32(smcgp->smc, key, CMD_IRQ_ACK | 1) < 0) + dev_err(smcgp->dev, "GPIO IRQ ack failed for %p4ch\n", &key); + + return NOTIFY_OK; +} + +static void macsmc_gpio_irq_enable(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct macsmc_gpio *smcgp = gpiochip_get_data(gc); + + set_bit(irqd_to_hwirq(d), smcgp->irq_enable_shadow); +} + +static void macsmc_gpio_irq_disable(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct macsmc_gpio *smcgp = gpiochip_get_data(gc); + + clear_bit(irqd_to_hwirq(d), smcgp->irq_enable_shadow); +} + +static int macsmc_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct macsmc_gpio *smcgp = gpiochip_get_data(gc); + int offset = irqd_to_hwirq(d); + u32 mode; + + if (!test_bit(offset, smcgp->irq_supported)) + return -EINVAL; + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_LEVEL_HIGH: + mode = IRQ_MODE_HIGH; + break; + case IRQ_TYPE_LEVEL_LOW: + mode = IRQ_MODE_LOW; + break; + case IRQ_TYPE_EDGE_RISING: + mode = IRQ_MODE_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + mode = IRQ_MODE_FALLING; + break; + case IRQ_TYPE_EDGE_BOTH: + mode = IRQ_MODE_BOTH; + break; + default: + return -EINVAL; } + smcgp->irq_mode_shadow[offset] = mode; return 0; } +static void macsmc_gpio_irq_bus_lock(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct macsmc_gpio *smcgp = gpiochip_get_data(gc); + + mutex_lock(&smcgp->irq_mutex); +} + +static void macsmc_gpio_irq_bus_sync_unlock(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct macsmc_gpio *smcgp = gpiochip_get_data(gc); + smc_key key = macsmc_gpio_key(irqd_to_hwirq(d)); + int offset = irqd_to_hwirq(d); + bool val; + + if (smcgp->irq_mode_shadow[offset] != smcgp->irq_mode[offset]) { + u32 cmd = CMD_IRQ_MODE | smcgp->irq_mode_shadow[offset]; + if (apple_smc_write_u32(smcgp->smc, key, cmd) < 0) + dev_err(smcgp->dev, "GPIO IRQ config failed for %p4ch = 0x%x\n", &key, cmd); + else + smcgp->irq_mode_shadow[offset] = smcgp->irq_mode[offset]; + } + + val = test_bit(offset, smcgp->irq_enable_shadow); + if (test_bit(offset, smcgp->irq_enable) != val) { + if (apple_smc_write_u32(smcgp->smc, key, CMD_IRQ_ENABLE | val) < 0) + dev_err(smcgp->dev, "GPIO IRQ en/disable failed for %p4ch\n", &key); + else + change_bit(offset, smcgp->irq_enable); + } + + mutex_unlock(&smcgp->irq_mutex); +} + static int macsmc_gpio_probe(struct platform_device *pdev) { struct macsmc_gpio *smcgp; @@ -221,6 +350,27 @@ static int macsmc_gpio_probe(struct platform_device *pdev) smcgp->gc.base = -1; smcgp->gc.parent = &pdev->dev; + smcgp->ic.name = "macsmc-pmu-gpio"; + smcgp->ic.irq_mask = macsmc_gpio_irq_disable; + smcgp->ic.irq_unmask = macsmc_gpio_irq_enable; + smcgp->ic.irq_set_type = macsmc_gpio_irq_set_type; + smcgp->ic.irq_bus_lock = macsmc_gpio_irq_bus_lock; + smcgp->ic.irq_bus_sync_unlock = macsmc_gpio_irq_bus_sync_unlock; + smcgp->ic.irq_set_type = macsmc_gpio_irq_set_type; + smcgp->ic.flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND; + + smcgp->gc.irq.chip = &smcgp->ic; + smcgp->gc.irq.parent_handler = NULL; + smcgp->gc.irq.num_parents = 0; + smcgp->gc.irq.parents = NULL; + smcgp->gc.irq.default_type = IRQ_TYPE_NONE; + smcgp->gc.irq.handler = handle_simple_irq; + + mutex_init(&smcgp->irq_mutex); + + smcgp->nb.notifier_call = macsmc_gpio_event; + apple_smc_register_notifier(smc, &smcgp->nb); + return devm_gpiochip_add_data(&pdev->dev, &smcgp->gc, smcgp); } From 1287c6cd7db1898e7accc78c35d790f1d00af252 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 18 Sep 2022 23:06:02 +0900 Subject: [PATCH 0391/1020] Input: macsmc-hid: Support the power button on desktops Signed-off-by: Hector Martin --- drivers/input/misc/macsmc-hid.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/input/misc/macsmc-hid.c b/drivers/input/misc/macsmc-hid.c index c4c7440d4ee68c..49296cbb70cc67 100644 --- a/drivers/input/misc/macsmc-hid.c +++ b/drivers/input/misc/macsmc-hid.c @@ -24,7 +24,8 @@ struct macsmc_hid { #define SMC_EV_BTN 0x7201 #define SMC_EV_LID 0x7203 -#define BTN_POWER 0x06 +#define BTN_POWER 0x01 +#define BTN_TOUCHID 0x06 #define BTN_POWER_HELD1 0xfe #define BTN_POWER_HELD2 0x00 @@ -39,6 +40,7 @@ static int macsmc_hid_event(struct notifier_block *nb, unsigned long event, void case SMC_EV_BTN: switch (d1) { case BTN_POWER: + case BTN_TOUCHID: if (smchid->wakeup_mode) { if (d2) { dev_info(smchid->dev, "Button wakeup\n"); From d8324e2dba56d285658710bd2949190c7dccf6df Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 4 Dec 2022 21:51:01 +0900 Subject: [PATCH 0392/1020] power: supply: macsmc_power: Add critical level shutdown & misc events Signed-off-by: Hector Martin --- drivers/power/supply/macsmc_power.c | 123 ++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/drivers/power/supply/macsmc_power.c b/drivers/power/supply/macsmc_power.c index 72ac697f55ec3d..38681a07613ccf 100644 --- a/drivers/power/supply/macsmc_power.c +++ b/drivers/power/supply/macsmc_power.c @@ -11,6 +11,9 @@ #include #include #include +#include +#include +#include #define MAX_STRING_LENGTH 256 @@ -26,6 +29,9 @@ struct macsmc_power { struct power_supply *ac; struct notifier_block nb; + + struct work_struct critical_work; + bool shutdown_started; }; #define CHNC_BATTERY_FULL BIT(0) @@ -46,6 +52,9 @@ struct macsmc_power { #define CH0X_CH0C BIT(0) #define CH0X_CH0B BIT(1) +#define ACSt_CAN_BOOT_AP BIT(2) +#define ACSt_CAN_BOOT_IBOOT BIT(1) + static int macsmc_battery_get_status(struct macsmc_power *power) { u64 nocharge_flags; @@ -187,6 +196,34 @@ static int macsmc_battery_get_date(const char *s, int *out) return 0; } +static int macsmc_battery_get_capacity_level(struct macsmc_power *power) +{ + u32 val; + int ret; + + /* Check for emergency shutdown condition */ + if (apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &val) >= 0 && val) + return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + + /* Check AC status for whether we could boot in this state */ + if (apple_smc_read_u32(power->smc, SMC_KEY(ACSt), &val) >= 0) { + if (!(val & ACSt_CAN_BOOT_IBOOT)) + return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + + if (!(val & ACSt_CAN_BOOT_AP)) + return POWER_SUPPLY_CAPACITY_LEVEL_LOW; + } + + /* Check battery full flag */ + ret = apple_smc_read_flag(power->smc, SMC_KEY(BSFC)); + if (ret > 0) + return POWER_SUPPLY_CAPACITY_LEVEL_FULL; + else if (ret == 0) + return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + else + return POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; +} + static int macsmc_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -224,6 +261,10 @@ static int macsmc_battery_get_property(struct power_supply *psy, ret = apple_smc_read_u8(power->smc, SMC_KEY(BUIC), &vu8); val->intval = vu8; break; + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + val->intval = macsmc_battery_get_capacity_level(power); + ret = val->intval < 0 ? val->intval : 0; + break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: ret = apple_smc_read_u16(power->smc, SMC_KEY(B0AV), &vu16); val->intval = vu16 * 1000; @@ -343,6 +384,7 @@ static enum power_supply_property macsmc_battery_props[] = { POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_POWER_NOW, @@ -424,6 +466,59 @@ static const struct power_supply_desc macsmc_ac_desc = { .num_properties = ARRAY_SIZE(macsmc_ac_props), }; +static void macsmc_power_critical_work(struct work_struct *wrk) { + struct macsmc_power *power = container_of(wrk, struct macsmc_power, critical_work); + int ret; + u32 bcf0; + u16 bitv, b0av; + + /* + * Check if the battery voltage is below the design voltage. If it is, + * we have a few seconds until the machine dies. Explicitly shut down, + * which at least gets the NVMe controller to flush its cache. + */ + if (apple_smc_read_u16(power->smc, SMC_KEY(BITV), &bitv) >= 0 && + apple_smc_read_u16(power->smc, SMC_KEY(B0AV), &b0av) >= 0 && + b0av < bitv) { + dev_crit(power->dev, "Emergency notification: Battery is critical\n"); + if (kernel_can_power_off()) + kernel_power_off(); + else /* Missing macsmc-reboot driver? In this state, this will not boot anyway. */ + kernel_restart("Battery is critical"); + } + + /* This spams once per second, so make sure we only trigger shutdown once. */ + if (power->shutdown_started) + return; + + /* Check for battery empty condition */ + ret = apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &bcf0); + if (ret < 0) { + dev_err(power->dev, + "Emergency notification: Failed to read battery status\n"); + } else if (bcf0 == 0) { + dev_warn(power->dev, "Emergency notification: Battery status is OK?\n"); + return; + } else { + dev_warn(power->dev, "Emergency notification: Battery is empty\n"); + } + + power->shutdown_started = true; + + /* + * Attempt to trigger an orderly shutdown. At this point, we should have a few + * minutes of reserve capacity left, enough to do a clean shutdown. + */ + dev_warn(power->dev, "Shutting down in 10 seconds\n"); + ssleep(10); + + /* + * Don't force it; if this stalls or fails, the last-resort check above will + * trigger a hard shutdown when shutdown is truly imminent. + */ + orderly_poweroff(false); +} + static int macsmc_power_event(struct notifier_block *nb, unsigned long event, void *data) { struct macsmc_power *power = container_of(nb, struct macsmc_power, nb); @@ -435,6 +530,28 @@ static int macsmc_power_event(struct notifier_block *nb, unsigned long event, vo power_supply_changed(power->batt); power_supply_changed(power->ac); + return NOTIFY_OK; + } else if (event == 0x71020000) { + schedule_work(&power->critical_work); + + return NOTIFY_OK; + } else if ((event & 0xffff0000) == 0x71060000) { + u8 changed_port = event >> 8; + u8 cur_port; + + /* Port charging state change? */ + if (apple_smc_read_u8(power->smc, SMC_KEY(AC-W), &cur_port) >= 0) { + dev_info(power->dev, "Port %d state change (charge port: %d)\n", + changed_port + 1, cur_port); + } + + power_supply_changed(power->batt); + power_supply_changed(power->ac); + + return NOTIFY_OK; + } else if ((event & 0xff000000) == 0x71000000) { + dev_info(power->dev, "Unknown charger event 0x%lx\n", event); + return NOTIFY_OK; } @@ -446,6 +563,7 @@ static int macsmc_power_probe(struct platform_device *pdev) struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); struct power_supply_config psy_cfg = {}; struct macsmc_power *power; + u32 val; int ret; power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL); @@ -469,6 +587,9 @@ static int macsmc_power_probe(struct platform_device *pdev) apple_smc_write_u8(power->smc, SMC_KEY(CH0K), 0); apple_smc_write_u8(power->smc, SMC_KEY(CH0B), 0); + /* Doing one read of this flag enables critical shutdown notifications */ + apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &val); + psy_cfg.drv_data = power; power->batt = devm_power_supply_register(&pdev->dev, &macsmc_battery_desc, &psy_cfg); if (IS_ERR(power->batt)) { @@ -487,6 +608,8 @@ static int macsmc_power_probe(struct platform_device *pdev) power->nb.notifier_call = macsmc_power_event; apple_smc_register_notifier(power->smc, &power->nb); + INIT_WORK(&power->critical_work, macsmc_power_critical_work); + return 0; } From f4dada7289627c032ffcfd98f4d5e862a562172b Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 12 Dec 2022 23:51:41 +0900 Subject: [PATCH 0393/1020] platform/apple: smc: Add apple_smc_read_f32_scaled Signed-off-by: Hector Martin --- drivers/platform/apple/smc_core.c | 48 +++++++++++++++++++++++++++++++ include/linux/mfd/macsmc.h | 2 ++ 2 files changed, 50 insertions(+) diff --git a/drivers/platform/apple/smc_core.c b/drivers/platform/apple/smc_core.c index daf029cd072f52..27605398be3b24 100644 --- a/drivers/platform/apple/smc_core.c +++ b/drivers/platform/apple/smc_core.c @@ -4,6 +4,7 @@ * Copyright The Asahi Linux Contributors */ +#include #include #include #include @@ -98,6 +99,53 @@ int apple_smc_rw(struct apple_smc *smc, smc_key key, void *wbuf, size_t wsize, } EXPORT_SYMBOL(apple_smc_rw); +int apple_smc_read_f32_scaled(struct apple_smc *smc, smc_key key, int *p, int scale) +{ + u32 fval; + u64 val; + int ret, exp; + + ret = apple_smc_read_u32(smc, key, &fval); + if (ret < 0) + return ret; + + val = ((u64)((fval & GENMASK(22, 0)) | BIT(23))); + exp = ((fval >> 23) & 0xff) - 127 - 23; + if (scale < 0) { + val <<= 32; + exp -= 32; + val /= -scale; + } else { + val *= scale; + } + + if (exp > 63) + val = U64_MAX; + else if (exp < -63) + val = 0; + else if (exp < 0) + val >>= -exp; + else if (exp != 0 && (val & ~((1UL << (64 - exp)) - 1))) /* overflow */ + val = U64_MAX; + else + val <<= exp; + + if (fval & BIT(31)) { + if (val > (-(s64)INT_MIN)) + *p = INT_MIN; + else + *p = -val; + } else { + if (val > INT_MAX) + *p = INT_MAX; + else + *p = val; + } + + return ret; +} +EXPORT_SYMBOL(apple_smc_read_f32_scaled); + int apple_smc_get_key_by_index(struct apple_smc *smc, int index, smc_key *key) { int ret; diff --git a/include/linux/mfd/macsmc.h b/include/linux/mfd/macsmc.h index 39b4dc4ca88106..30bcac19818c81 100644 --- a/include/linux/mfd/macsmc.h +++ b/include/linux/mfd/macsmc.h @@ -80,6 +80,8 @@ static inline int apple_smc_read_flag(struct apple_smc *smc, smc_key key) } #define apple_smc_write_flag apple_smc_write_u8 +int apple_smc_read_f32_scaled(struct apple_smc *smc, smc_key key, int *p, int scale); + int apple_smc_register_notifier(struct apple_smc *smc, struct notifier_block *n); int apple_smc_unregister_notifier(struct apple_smc *smc, struct notifier_block *n); From 60520db6a0bb0887f1447d1397561bccf3f96582 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 12 Dec 2022 23:36:17 +0900 Subject: [PATCH 0394/1020] power: supply: macsmc_power: Add a debug mode to print power usage Signed-off-by: Hector Martin --- drivers/power/supply/macsmc_power.c | 81 ++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/macsmc_power.c b/drivers/power/supply/macsmc_power.c index 38681a07613ccf..9e5080b13832c0 100644 --- a/drivers/power/supply/macsmc_power.c +++ b/drivers/power/supply/macsmc_power.c @@ -32,8 +32,25 @@ struct macsmc_power { struct work_struct critical_work; bool shutdown_started; + + struct delayed_work dbg_log_work; +}; + +static int macsmc_log_power_set(const char *val, const struct kernel_param *kp); + +static const struct kernel_param_ops macsmc_log_power_ops = { + .set = macsmc_log_power_set, + .get = param_get_bool, }; +static bool log_power = false; +module_param_cb(log_power, &macsmc_log_power_ops, &log_power, 0644); +MODULE_PARM_DESC(log_power, "Periodically log power consumption for debugging"); + +#define POWER_LOG_INTERVAL (HZ) + +static struct macsmc_power *g_power; + #define CHNC_BATTERY_FULL BIT(0) #define CHNC_NO_CHARGER BIT(7) #define CHNC_NOCHG_CH0C BIT(14) @@ -466,7 +483,58 @@ static const struct power_supply_desc macsmc_ac_desc = { .num_properties = ARRAY_SIZE(macsmc_ac_props), }; -static void macsmc_power_critical_work(struct work_struct *wrk) { +static int macsmc_log_power_set(const char *val, const struct kernel_param *kp) +{ + int ret = param_set_bool(val, kp); + + if (ret < 0) + return ret; + + if (log_power && g_power) + schedule_delayed_work(&g_power->dbg_log_work, 0); + + return 0; +} + +static void macsmc_dbg_work(struct work_struct *wrk) +{ + struct macsmc_power *power = container_of(to_delayed_work(wrk), + struct macsmc_power, dbg_log_work); + int p_in = 0, p_sys = 0, p_3v8 = 0, p_mpmu = 0, p_spmu = 0, p_clvr = 0, p_cpu = 0; + s32 p_bat = 0; + s16 t_full = 0, t_empty = 0; + u8 charge = 0; + + apple_smc_read_f32_scaled(power->smc, SMC_KEY(PDTR), &p_in, 1000); + apple_smc_read_f32_scaled(power->smc, SMC_KEY(PSTR), &p_sys, 1000); + apple_smc_read_f32_scaled(power->smc, SMC_KEY(PMVR), &p_3v8, 1000); + apple_smc_read_f32_scaled(power->smc, SMC_KEY(PHPC), &p_cpu, 1000); + apple_smc_read_f32_scaled(power->smc, SMC_KEY(PSVR), &p_clvr, 1000); + apple_smc_read_f32_scaled(power->smc, SMC_KEY(PPMC), &p_mpmu, 1000); + apple_smc_read_f32_scaled(power->smc, SMC_KEY(PPSC), &p_spmu, 1000); + apple_smc_read_s32(power->smc, SMC_KEY(B0AP), &p_bat); + apple_smc_read_s16(power->smc, SMC_KEY(B0TE), &t_empty); + apple_smc_read_s16(power->smc, SMC_KEY(B0TF), &t_full); + apple_smc_read_u8(power->smc, SMC_KEY(BUIC), &charge); + +#define FD3(x) ((x) / 1000), abs((x) % 1000) + + dev_info(power->dev, + "In %2d.%03dW Sys %2d.%03dW 3V8 %2d.%03dW MPMU %2d.%03dW SPMU %2d.%03dW " + "CLVR %2d.%03dW CPU %2d.%03dW Batt %2d.%03dW %d%% T%s %dm\n", + FD3(p_in), FD3(p_sys), FD3(p_3v8), FD3(p_mpmu), FD3(p_spmu), FD3(p_clvr), + FD3(p_cpu), FD3(p_bat), charge, + t_full >= 0 ? "full" : "empty", + t_full >= 0 ? t_full : t_empty); + +#undef FD3 + + if (log_power) + schedule_delayed_work(&power->dbg_log_work, POWER_LOG_INTERVAL); +} + +static void macsmc_power_critical_work(struct work_struct *wrk) +{ struct macsmc_power *power = container_of(wrk, struct macsmc_power, critical_work); int ret; u32 bcf0; @@ -609,6 +677,12 @@ static int macsmc_power_probe(struct platform_device *pdev) apple_smc_register_notifier(power->smc, &power->nb); INIT_WORK(&power->critical_work, macsmc_power_critical_work); + INIT_DELAYED_WORK(&power->dbg_log_work, macsmc_dbg_work); + + g_power = power; + + if (log_power) + schedule_delayed_work(&power->dbg_log_work, 0); return 0; } @@ -617,6 +691,11 @@ static int macsmc_power_remove(struct platform_device *pdev) { struct macsmc_power *power = dev_get_drvdata(&pdev->dev); + cancel_work(&power->critical_work); + cancel_delayed_work(&power->dbg_log_work); + + g_power = NULL; + apple_smc_unregister_notifier(power->smc, &power->nb); return 0; From 01648d19575538974ecefa5b38486aee866f7646 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 18 Apr 2023 17:14:05 +0900 Subject: [PATCH 0395/1020] macsmc: Fix race between backend and core on notifications The core enables notifications (NTAP) before returning from the probe function, before the backend has a chance to set smc->core. This leads to a race if a notification arrives early. We already rely on the core setting the drvdata to itself, so move that ahead of NTAP=1 and drop smc->core entirely. That way it's safe for the backend notification callback to fire before the core probe function returns. Signed-off-by: Hector Martin --- drivers/platform/apple/smc.h | 3 +-- drivers/platform/apple/smc_core.c | 18 +++++++++--------- drivers/platform/apple/smc_rtkit.c | 10 ++++------ 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/drivers/platform/apple/smc.h b/drivers/platform/apple/smc.h index 8ae51887b2c553..34131f77fe09cb 100644 --- a/drivers/platform/apple/smc.h +++ b/drivers/platform/apple/smc.h @@ -19,8 +19,7 @@ struct apple_smc_backend_ops { int (*get_key_info)(void *cookie, smc_key key, struct apple_smc_key_info *info); }; -struct apple_smc *apple_smc_probe(struct device *dev, const struct apple_smc_backend_ops *ops, - void *cookie); +int apple_smc_probe(struct device *dev, const struct apple_smc_backend_ops *ops, void *cookie); void *apple_smc_get_cookie(struct apple_smc *smc); int apple_smc_remove(struct apple_smc *smc); void apple_smc_event_received(struct apple_smc *smc, uint32_t event); diff --git a/drivers/platform/apple/smc_core.c b/drivers/platform/apple/smc_core.c index 27605398be3b24..ee1df85e0aecff 100644 --- a/drivers/platform/apple/smc_core.c +++ b/drivers/platform/apple/smc_core.c @@ -236,7 +236,7 @@ void *apple_smc_get_cookie(struct apple_smc *smc) } EXPORT_SYMBOL(apple_smc_get_cookie); -struct apple_smc *apple_smc_probe(struct device *dev, const struct apple_smc_backend_ops *ops, void *cookie) +int apple_smc_probe(struct device *dev, const struct apple_smc_backend_ops *ops, void *cookie) { struct apple_smc *smc; u32 count; @@ -244,7 +244,7 @@ struct apple_smc *apple_smc_probe(struct device *dev, const struct apple_smc_bac smc = devm_kzalloc(dev, sizeof(*smc), GFP_KERNEL); if (!smc) - return ERR_PTR(-ENOMEM); + return -ENOMEM; smc->dev = dev; smc->be_cookie = cookie; @@ -254,16 +254,18 @@ struct apple_smc *apple_smc_probe(struct device *dev, const struct apple_smc_bac ret = apple_smc_read_u32(smc, SMC_KEY(#KEY), &count); if (ret) - return ERR_PTR(dev_err_probe(dev, ret, "Failed to get key count")); + return dev_err_probe(dev, ret, "Failed to get key count"); smc->key_count = be32_to_cpu(count); ret = apple_smc_get_key_by_index(smc, 0, &smc->first_key); if (ret) - return ERR_PTR(dev_err_probe(dev, ret, "Failed to get first key")); + return dev_err_probe(dev, ret, "Failed to get first key"); ret = apple_smc_get_key_by_index(smc, smc->key_count - 1, &smc->last_key); if (ret) - return ERR_PTR(dev_err_probe(dev, ret, "Failed to get last key")); + return dev_err_probe(dev, ret, "Failed to get last key"); + + dev_set_drvdata(dev, smc); /* Enable notifications */ apple_smc_write_flag(smc, SMC_KEY(NTAP), 1); @@ -271,13 +273,11 @@ struct apple_smc *apple_smc_probe(struct device *dev, const struct apple_smc_bac dev_info(dev, "Initialized (%d keys %p4ch..%p4ch)\n", smc->key_count, &smc->first_key, &smc->last_key); - dev_set_drvdata(dev, smc); - ret = mfd_add_devices(dev, -1, apple_smc_devs, ARRAY_SIZE(apple_smc_devs), NULL, 0, NULL); if (ret) - return ERR_PTR(dev_err_probe(dev, ret, "Subdevice initialization failed")); + return dev_err_probe(dev, ret, "Subdevice initialization failed"); - return smc; + return 0; } EXPORT_SYMBOL(apple_smc_probe); diff --git a/drivers/platform/apple/smc_rtkit.c b/drivers/platform/apple/smc_rtkit.c index 314aabe9a14492..ba5df56306822d 100644 --- a/drivers/platform/apple/smc_rtkit.c +++ b/drivers/platform/apple/smc_rtkit.c @@ -40,7 +40,6 @@ struct apple_smc_rtkit { struct device *dev; - struct apple_smc *core; struct apple_rtkit *rtk; struct completion init_done; @@ -321,6 +320,7 @@ static bool apple_smc_rtkit_recv_early(void *cookie, u8 endpoint, u64 message) static void apple_smc_rtkit_recv(void *cookie, u8 endpoint, u64 message) { struct apple_smc_rtkit *smc = cookie; + struct apple_smc *core = dev_get_drvdata(smc->dev); if (endpoint != SMC_ENDPOINT) { dev_err(smc->dev, "Received message for unknown endpoint 0x%x\n", endpoint); @@ -332,7 +332,7 @@ static void apple_smc_rtkit_recv(void *cookie, u8 endpoint, u64 message) return; } - apple_smc_event_received(smc->core, FIELD_GET(SMC_DATA, message)); + apple_smc_event_received(core, FIELD_GET(SMC_DATA, message)); } static const struct apple_rtkit_ops apple_smc_rtkit_ops = { @@ -403,11 +403,9 @@ static int apple_smc_rtkit_probe(struct platform_device *pdev) goto cleanup; } - smc->core = apple_smc_probe(dev, &apple_smc_rtkit_be_ops, smc); - if (IS_ERR(smc->core)) { - ret = PTR_ERR(smc->core); + ret = apple_smc_probe(dev, &apple_smc_rtkit_be_ops, smc); + if (ret) goto cleanup; - } return 0; From 3afa993a6c6137361beaa8e47a2caf13068d78e0 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 18 Apr 2023 04:18:14 +0900 Subject: [PATCH 0396/1020] power: supply: macsmc_power: Log power data on button presses This helps catch s2idle power stats, since we get early data when the system resumes due to a power button press. Signed-off-by: Hector Martin --- drivers/power/supply/macsmc_power.c | 64 +++++++++++++++++------------ 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/drivers/power/supply/macsmc_power.c b/drivers/power/supply/macsmc_power.c index 9e5080b13832c0..f239a63d6aa833 100644 --- a/drivers/power/supply/macsmc_power.c +++ b/drivers/power/supply/macsmc_power.c @@ -72,6 +72,36 @@ static struct macsmc_power *g_power; #define ACSt_CAN_BOOT_AP BIT(2) #define ACSt_CAN_BOOT_IBOOT BIT(1) +static void macsmc_do_dbg(struct macsmc_power *power) +{ + int p_in = 0, p_sys = 0, p_3v8 = 0, p_mpmu = 0, p_spmu = 0, p_clvr = 0, p_cpu = 0; + s32 p_bat = 0; + s16 t_full = 0, t_empty = 0; + u8 charge = 0; + + apple_smc_read_f32_scaled(power->smc, SMC_KEY(PDTR), &p_in, 1000); + apple_smc_read_f32_scaled(power->smc, SMC_KEY(PSTR), &p_sys, 1000); + apple_smc_read_f32_scaled(power->smc, SMC_KEY(PMVR), &p_3v8, 1000); + apple_smc_read_f32_scaled(power->smc, SMC_KEY(PHPC), &p_cpu, 1000); + apple_smc_read_f32_scaled(power->smc, SMC_KEY(PSVR), &p_clvr, 1000); + apple_smc_read_f32_scaled(power->smc, SMC_KEY(PPMC), &p_mpmu, 1000); + apple_smc_read_f32_scaled(power->smc, SMC_KEY(PPSC), &p_spmu, 1000); + apple_smc_read_s32(power->smc, SMC_KEY(B0AP), &p_bat); + apple_smc_read_s16(power->smc, SMC_KEY(B0TE), &t_empty); + apple_smc_read_s16(power->smc, SMC_KEY(B0TF), &t_full); + apple_smc_read_u8(power->smc, SMC_KEY(BUIC), &charge); + +#define FD3(x) ((x) / 1000), abs((x) % 1000) + dev_info(power->dev, + "In %2d.%03dW Sys %2d.%03dW 3V8 %2d.%03dW MPMU %2d.%03dW SPMU %2d.%03dW " + "CLVR %2d.%03dW CPU %2d.%03dW Batt %2d.%03dW %d%% T%s %dm\n", + FD3(p_in), FD3(p_sys), FD3(p_3v8), FD3(p_mpmu), FD3(p_spmu), FD3(p_clvr), + FD3(p_cpu), FD3(p_bat), charge, + t_full >= 0 ? "full" : "empty", + t_full >= 0 ? t_full : t_empty); +#undef FD3 +} + static int macsmc_battery_get_status(struct macsmc_power *power) { u64 nocharge_flags; @@ -500,34 +530,8 @@ static void macsmc_dbg_work(struct work_struct *wrk) { struct macsmc_power *power = container_of(to_delayed_work(wrk), struct macsmc_power, dbg_log_work); - int p_in = 0, p_sys = 0, p_3v8 = 0, p_mpmu = 0, p_spmu = 0, p_clvr = 0, p_cpu = 0; - s32 p_bat = 0; - s16 t_full = 0, t_empty = 0; - u8 charge = 0; - apple_smc_read_f32_scaled(power->smc, SMC_KEY(PDTR), &p_in, 1000); - apple_smc_read_f32_scaled(power->smc, SMC_KEY(PSTR), &p_sys, 1000); - apple_smc_read_f32_scaled(power->smc, SMC_KEY(PMVR), &p_3v8, 1000); - apple_smc_read_f32_scaled(power->smc, SMC_KEY(PHPC), &p_cpu, 1000); - apple_smc_read_f32_scaled(power->smc, SMC_KEY(PSVR), &p_clvr, 1000); - apple_smc_read_f32_scaled(power->smc, SMC_KEY(PPMC), &p_mpmu, 1000); - apple_smc_read_f32_scaled(power->smc, SMC_KEY(PPSC), &p_spmu, 1000); - apple_smc_read_s32(power->smc, SMC_KEY(B0AP), &p_bat); - apple_smc_read_s16(power->smc, SMC_KEY(B0TE), &t_empty); - apple_smc_read_s16(power->smc, SMC_KEY(B0TF), &t_full); - apple_smc_read_u8(power->smc, SMC_KEY(BUIC), &charge); - -#define FD3(x) ((x) / 1000), abs((x) % 1000) - - dev_info(power->dev, - "In %2d.%03dW Sys %2d.%03dW 3V8 %2d.%03dW MPMU %2d.%03dW SPMU %2d.%03dW " - "CLVR %2d.%03dW CPU %2d.%03dW Batt %2d.%03dW %d%% T%s %dm\n", - FD3(p_in), FD3(p_sys), FD3(p_3v8), FD3(p_mpmu), FD3(p_spmu), FD3(p_clvr), - FD3(p_cpu), FD3(p_bat), charge, - t_full >= 0 ? "full" : "empty", - t_full >= 0 ? t_full : t_empty); - -#undef FD3 + macsmc_do_dbg(power); if (log_power) schedule_delayed_work(&power->dbg_log_work, POWER_LOG_INTERVAL); @@ -620,6 +624,12 @@ static int macsmc_power_event(struct notifier_block *nb, unsigned long event, vo } else if ((event & 0xff000000) == 0x71000000) { dev_info(power->dev, "Unknown charger event 0x%lx\n", event); + return NOTIFY_OK; + } else if ((event & 0xffff0000) == 0x72010000) { + /* Button event handled by macsmc-hid, but let's do a debug print */ + if (log_power) + macsmc_do_dbg(power); + return NOTIFY_OK; } From 5e720844111c66bb5ccbd6c49064b050e6b897d0 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 30 May 2023 19:52:09 +0900 Subject: [PATCH 0397/1020] power: supply: macsmc_power: Add CHWA charge thresholds This is a hardcoded charge threshold feature present in firmware 13.0 or newer. Userspace settings are rounded to one of the two possible behaviors. Signed-off-by: Hector Martin --- drivers/power/supply/macsmc_power.c | 67 +++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/drivers/power/supply/macsmc_power.c b/drivers/power/supply/macsmc_power.c index f239a63d6aa833..f9ae0f50b9cd7a 100644 --- a/drivers/power/supply/macsmc_power.c +++ b/drivers/power/supply/macsmc_power.c @@ -20,11 +20,13 @@ struct macsmc_power { struct device *dev; struct apple_smc *smc; + struct power_supply_desc batt_desc; struct power_supply *batt; char model_name[MAX_STRING_LENGTH]; char serial_number[MAX_STRING_LENGTH]; char mfg_date[MAX_STRING_LENGTH]; + bool has_chwa; struct power_supply *ac; @@ -62,9 +64,11 @@ static struct macsmc_power *g_power; #define CH0R_LOWER_FLAGS GENMASK(15, 0) #define CH0R_NOAC_CH0I BIT(0) +#define CH0R_NOAC_DISCONNECTED BIT(4) #define CH0R_NOAC_CH0J BIT(5) #define CH0R_BMS_BUSY BIT(8) #define CH0R_NOAC_CH0K BIT(9) +#define CH0R_NOAC_CHWA BIT(11) #define CH0X_CH0C BIT(0) #define CH0X_CH0B BIT(1) @@ -72,6 +76,10 @@ static struct macsmc_power *g_power; #define ACSt_CAN_BOOT_AP BIT(2) #define ACSt_CAN_BOOT_IBOOT BIT(1) +#define CHWA_FIXED_START_THRESHOLD 75 +#define CHWA_FIXED_END_THRESHOLD 80 +#define CHWA_PROP_WRITE_THRESHOLD 95 + static void macsmc_do_dbg(struct macsmc_power *power) { int p_in = 0, p_sys = 0, p_3v8 = 0, p_mpmu = 0, p_spmu = 0, p_clvr = 0, p_cpu = 0; @@ -107,6 +115,7 @@ static int macsmc_battery_get_status(struct macsmc_power *power) u64 nocharge_flags; u32 nopower_flags; u16 ac_current; + bool chwa_limit = false; int ret; /* @@ -153,14 +162,29 @@ static int macsmc_battery_get_status(struct macsmc_power *power) else if (ret) return POWER_SUPPLY_STATUS_FULL; + /* + * If we have charge limits supported and enabled and the SoC is > 75%, + * that means we are not charging for that reason (if not charging). + */ + if (power->has_chwa && apple_smc_read_flag(power->smc, SMC_KEY(CHWA)) == 1) { + u8 buic = 0; + + if (apple_smc_read_u8(power->smc, SMC_KEY(BUIC), &buic) >= 0 && + buic >= CHWA_FIXED_START_THRESHOLD) + chwa_limit = true; + } + /* If there are reasons we aren't charging... */ ret = apple_smc_read_u64(power->smc, SMC_KEY(CHNC), &nocharge_flags); if (!ret) { /* Perhaps the battery is full after all */ if (nocharge_flags & CHNC_BATTERY_FULL) return POWER_SUPPLY_STATUS_FULL; - /* Or maybe the BMS is just busy doing something, if so call it charging anyway */ - else if (nocharge_flags == CHNC_BMS_BUSY) + /* + * Or maybe the BMS is just busy doing something, if so call it charging anyway. + * But CHWA limits show up as this, so exclude those. + */ + else if (nocharge_flags == CHNC_BMS_BUSY && !chwa_limit) return POWER_SUPPLY_STATUS_CHARGING; /* If we have other reasons we aren't charging, say we aren't */ else if (nocharge_flags) @@ -392,6 +416,16 @@ static int macsmc_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_MANUFACTURE_DAY: ret = macsmc_battery_get_date(&power->mfg_date[4], &val->intval); break; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD: + ret = apple_smc_read_flag(power->smc, SMC_KEY(CHWA)); + val->intval = ret == 1 ? CHWA_FIXED_START_THRESHOLD : 100; + ret = ret < 0 ? ret : 0; + break; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: + ret = apple_smc_read_flag(power->smc, SMC_KEY(CHWA)); + val->intval = ret == 1 ? CHWA_FIXED_END_THRESHOLD : 100; + ret = ret < 0 ? ret : 0; + break; default: return -EINVAL; } @@ -408,6 +442,16 @@ static int macsmc_battery_set_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: return macsmc_battery_set_charge_behaviour(power, val->intval); + case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD: + /* + * Ignore, we allow writes so userspace isn't confused but this is + * not configurable independently, it always is 75 or 100 depending + * on the end_threshold boolean setting. + */ + return 0; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: + return apple_smc_write_flag(power->smc, SMC_KEY(CHWA), + val->intval <= CHWA_PROP_WRITE_THRESHOLD); default: return -EINVAL; } @@ -416,15 +460,20 @@ static int macsmc_battery_set_property(struct power_supply *psy, static int macsmc_battery_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { + struct macsmc_power *power = power_supply_get_drvdata(psy); + switch (psp) { case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: return true; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD: + case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: + return power->has_chwa; default: return false; } } -static enum power_supply_property macsmc_battery_props[] = { +static const enum power_supply_property macsmc_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR, @@ -453,6 +502,8 @@ static enum power_supply_property macsmc_battery_props[] = { POWER_SUPPLY_PROP_MANUFACTURE_YEAR, POWER_SUPPLY_PROP_MANUFACTURE_MONTH, POWER_SUPPLY_PROP_MANUFACTURE_DAY, + POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD, + POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD }; static const struct power_supply_desc macsmc_battery_desc = { @@ -650,6 +701,7 @@ static int macsmc_power_probe(struct platform_device *pdev) power->dev = &pdev->dev; power->smc = smc; + power->batt_desc = macsmc_battery_desc; dev_set_drvdata(&pdev->dev, power); /* Ignore devices without a charger/battery */ @@ -665,11 +717,18 @@ static int macsmc_power_probe(struct platform_device *pdev) apple_smc_write_u8(power->smc, SMC_KEY(CH0K), 0); apple_smc_write_u8(power->smc, SMC_KEY(CH0B), 0); + if (apple_smc_read_flag(power->smc, SMC_KEY(CHWA)) >= 0) { + power->has_chwa = true; + } else { + /* Remove the last 2 properties that control the charge threshold */ + power->batt_desc.num_properties -= 2; + } + /* Doing one read of this flag enables critical shutdown notifications */ apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &val); psy_cfg.drv_data = power; - power->batt = devm_power_supply_register(&pdev->dev, &macsmc_battery_desc, &psy_cfg); + power->batt = devm_power_supply_register(&pdev->dev, &power->batt_desc, &psy_cfg); if (IS_ERR(power->batt)) { dev_err(&pdev->dev, "Failed to register battery\n"); ret = PTR_ERR(power->batt); From 76935dd6f1aeb97830775469c5e76d3b20a98c2d Mon Sep 17 00:00:00 2001 From: Jean-Francois Bortolotti Date: Fri, 4 Feb 2022 00:06:13 +0100 Subject: [PATCH 0398/1020] spmi: add a first basic spmi driver for Apple SoC Signed-off-by: Jean-Francois Bortolotti --- drivers/spmi/Kconfig | 8 + drivers/spmi/Makefile | 1 + drivers/spmi/spmi-apple-controller.c | 223 +++++++++++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100644 drivers/spmi/spmi-apple-controller.c diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig index 73780204631463..96c73c5b572022 100644 --- a/drivers/spmi/Kconfig +++ b/drivers/spmi/Kconfig @@ -45,4 +45,12 @@ config SPMI_MTK_PMIF This is required for communicating with Mediatek PMICs and other devices that have the SPMI interface. +config SPMI_APPLE + tristate "Apple SoC SPMI Controller platform driver" + depends on ARCH_APPLE || COMPILE_TEST + help + This enables basic support for the SPMI controller present on + many Apple SoCs, including the t8103 (M1) and t600x + (M1 Pro/Max). + endif diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile index 7f152167bb05b2..8c80236dfac41b 100644 --- a/drivers/spmi/Makefile +++ b/drivers/spmi/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_SPMI) += spmi.o spmi-devres.o obj-$(CONFIG_SPMI_HISI3670) += hisi-spmi-controller.o obj-$(CONFIG_SPMI_MSM_PMIC_ARB) += spmi-pmic-arb.o obj-$(CONFIG_SPMI_MTK_PMIF) += spmi-mtk-pmif.o +obj-$(CONFIG_SPMI_APPLE) += spmi-apple-controller.o diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c new file mode 100644 index 00000000000000..c14c4874654d15 --- /dev/null +++ b/drivers/spmi/spmi-apple-controller.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Apple SoC SPMI device driver + * + * Copyright The Asahi Linux Contributors + * + * Inspired by: + * OpenBSD support Copyright (c) 2021 Mark Kettenis + * Correllium support Copyright (C) 2021 Corellium LLC + * hisi-spmi-controller.c + * spmi-pmic-ard.c Copyright (c) 2021, The Linux Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* SPMI Controller Registers */ +#define SPMI_STATUS_REG 0 +#define SPMI_CMD_REG 0x4 +#define SPMI_RSP_REG 0x8 + +#define SPMI_RX_FIFO_EMPTY BIT(24) +#define SPMI_TX_FIFO_EMPTY BIT(8) + +/* Apple SPMI controler */ +struct apple_spmi { + void __iomem *regs; + struct spmi_controller *ctrl; +}; + +static inline u32 read_reg(struct apple_spmi *spmi, int offset) +{ + return (readl_relaxed(spmi->regs + offset)); +} + +static inline void write_reg(u32 value, struct apple_spmi *spmi, int offset) +{ + writel_relaxed(value, spmi->regs + offset); +} + +static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 slave_id, + u16 slave_addr, u8 *__buf, size_t bc) +{ + struct apple_spmi *spmi; + u32 spmi_cmd = opc | slave_id << 8 | slave_addr << 16 | (bc - 1) | + (1 << 15); + u32 rsp; + volatile u32 status; + size_t len_to_read; + u8 i; + + spmi = spmi_controller_get_drvdata(ctrl); + + write_reg(spmi_cmd, spmi, SPMI_CMD_REG); + + /* Wait for Rx FIFO to have something */ + /* Quite ugly msleep, need to find a better way to do it */ + i = 0; + do { + status = read_reg(spmi, SPMI_STATUS_REG); + msleep(10); + i += 1; + } while ((status & SPMI_RX_FIFO_EMPTY) && i < 5); + + if (i >= 5) { + dev_err(&ctrl->dev, + "spmi_read_cmd:took to long to get the status"); + return -1; + } + + /* Read SPMI reply status */ + rsp = read_reg(spmi, SPMI_RSP_REG); + + len_to_read = 0; + /* Read SPMI data reply */ + while (!(status & SPMI_RX_FIFO_EMPTY) && (len_to_read < bc)) { + rsp = read_reg(spmi, SPMI_RSP_REG); + i = 0; + while ((len_to_read < bc) && (i < 4)) { + __buf[len_to_read++] = ((0xff << (8 * i)) & rsp) >> + (8 * i); + i += 1; + } + } + + return 0; +} + +static int spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 slave_id, + u16 slave_addr, const u8 *__buf, size_t bc) +{ + struct apple_spmi *spmi; + u32 spmi_cmd = opc | slave_id << 8 | slave_addr << 16 | (bc - 1) | + (1 << 15); + volatile u32 rsp; + volatile u32 status; + size_t i = 0, j; + + spmi = spmi_controller_get_drvdata(ctrl); + + write_reg(spmi_cmd, spmi, SPMI_CMD_REG); + + while (i < bc) { + j = 0; + spmi_cmd = 0; + while ((j < 4) & (i < bc)) { + spmi_cmd |= __buf[i++] << (j++ * 8); + } + write_reg(spmi_cmd, spmi, SPMI_CMD_REG); + } + + /* Wait for Rx FIFO to have something */ + /* Quite ugly msleep, need to find a better way to do it */ + i = 0; + do { + status = read_reg(spmi, SPMI_STATUS_REG); + msleep(10); + i += 1; + } while ((status & SPMI_RX_FIFO_EMPTY) && i < 5); + + if (i >= 5) { + dev_err(&ctrl->dev, + "spmi_write_cmd:took to long to get the status"); + return -1; + } + + rsp = read_reg(spmi, SPMI_RSP_REG); + (void)rsp; // TODO: check stuff here + + return 0; +} + +static int spmi_controller_probe(struct platform_device *pdev) +{ + struct apple_spmi *spmi; + struct spmi_controller *ctrl; + int ret; + + ctrl = spmi_controller_alloc(&pdev->dev, sizeof(struct apple_spmi)); + if (IS_ERR(ctrl)) { + dev_err_probe(&pdev->dev, PTR_ERR(ctrl), + "Can't allocate spmi_controller data\n"); + return -ENOMEM; + } + + spmi = spmi_controller_get_drvdata(ctrl); + spmi->ctrl = ctrl; + platform_set_drvdata(pdev, ctrl); + + spmi->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(spmi->regs)) { + dev_err_probe(&pdev->dev, PTR_ERR(spmi->regs), + "Can't get ioremap regs.\n"); + return PTR_ERR(spmi->regs); + } + + ctrl->dev.of_node = of_node_get(pdev->dev.of_node); + + /* Callbacks */ + ctrl->read_cmd = spmi_read_cmd; + ctrl->write_cmd = spmi_write_cmd; + + ret = spmi_controller_add(ctrl); + if (ret) { + dev_err(&pdev->dev, + "spmi_controller_add failed with error %d!\n", ret); + goto err_put_controller; + } + + /* Let's look for other nodes in device tree like the rtc */ + ret = devm_of_platform_populate(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, + "spmi_controller_probe: devm_of_platform_populate failed with error %d!\n", + ret); + goto err_devm_of_platform_populate; + } + + return 0; + +err_put_controller: + spmi_controller_put(ctrl); +err_devm_of_platform_populate: + return ret; +} + +static int spmi_del_controller(struct platform_device *pdev) +{ + struct spmi_controller *ctrl = platform_get_drvdata(pdev); + + spmi_controller_remove(ctrl); + spmi_controller_put(ctrl); + return 0; +} + +static const struct of_device_id spmi_controller_match_table[] = { + { + .compatible = "apple,spmi", + }, + {} +}; +MODULE_DEVICE_TABLE(of, spmi_controller_match_table); + +static struct platform_driver spmi_controller_driver = { + .probe = spmi_controller_probe, + .remove = spmi_del_controller, + .driver = { + .name = "apple-spmi", + .owner = THIS_MODULE, + .of_match_table = spmi_controller_match_table, + }, +}; +module_platform_driver(spmi_controller_driver); + +MODULE_AUTHOR("Jean-Francois Bortolotti "); +MODULE_DESCRIPTION("Apple SoC SPMI driver"); +MODULE_LICENSE("GPL"); From 05cf5267baf0ef7622c38b29451775a30b4a9f06 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 15 Feb 2022 18:43:17 +0900 Subject: [PATCH 0399/1020] mfd: Add a simple-mfd-spmi driver This is the SPMI counterpart to simple-mfd-i2c. It merely exposes the SPMI register address space as an MFD device, such that different aspects of a device can be managed by separate drivers. Signed-off-by: Hector Martin --- drivers/mfd/Kconfig | 28 ++++++++++++++++++++ drivers/mfd/Makefile | 1 + drivers/mfd/simple-mfd-spmi.c | 49 +++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 drivers/mfd/simple-mfd-spmi.c diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 4b023ee229cf1c..7faa9b99282403 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -52,6 +52,21 @@ config MFD_ACT8945A linear regulators, along with a complete ActivePath battery charger. +config MFD_APPLE_SPMI_PMU + tristate "Apple SPMI PMUs" + depends on SPMI + depends on ARCH_APPLE || COMPILE_TEST + default ARCH_APPLE + select MFD_SIMPLE_MFD_SPMI + help + Say yes here to enable support for Apple PMUs attached via the + SPMI bus. These can be found on Apple devices such as Apple + Silicon Macs. + + This driver itself only attaches to the core device, and relies + on subsystem drivers for individual device functions. You must + enable those for it to be useful. + config MFD_SUN4I_GPADC tristate "Allwinner sunxi platforms' GPADC MFD driver" select MFD_CORE @@ -1312,6 +1327,19 @@ config MFD_SIMPLE_MFD_I2C sub-devices represented by child nodes in Device Tree will be subsequently registered. +config MFD_SIMPLE_MFD_SPMI + tristate + depends on SPMI + select MFD_CORE + select REGMAP_SPMI + help + This driver creates a single register map with the intention for it + to be shared by all sub-devices. + + Once the register map has been successfully initialised, any + sub-devices represented by child nodes in Device Tree will be + subsequently registered. + config MFD_SL28CPLD tristate "Kontron sl28cpld Board Management Controller" depends on I2C diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index c66f07edcd0e62..23ec49ecb9e536 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -271,6 +271,7 @@ obj-$(CONFIG_MFD_QCOM_PM8008) += qcom-pm8008.o obj-$(CONFIG_SGI_MFD_IOC3) += ioc3.o obj-$(CONFIG_MFD_SIMPLE_MFD_I2C) += simple-mfd-i2c.o +obj-$(CONFIG_MFD_SIMPLE_MFD_SPMI) += simple-mfd-spmi.o obj-$(CONFIG_MFD_SMPRO) += smpro-core.o obj-$(CONFIG_MFD_INTEL_M10_BMC_CORE) += intel-m10-bmc-core.o diff --git a/drivers/mfd/simple-mfd-spmi.c b/drivers/mfd/simple-mfd-spmi.c new file mode 100644 index 00000000000000..99f25751000a3b --- /dev/null +++ b/drivers/mfd/simple-mfd-spmi.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Simple MFD - SPMI + * + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include + +static const struct regmap_config spmi_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0xffff, +}; + +static int simple_spmi_probe(struct spmi_device *sdev) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_spmi_ext(sdev, &spmi_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return devm_of_platform_populate(&sdev->dev); +} + +static const struct of_device_id simple_spmi_id_table[] = { + { .compatible = "apple,spmi-pmu" }, + {} +}; +MODULE_DEVICE_TABLE(of, simple_spmi_id_table); + +static struct spmi_driver pmic_spmi_driver = { + .probe = simple_spmi_probe, + .driver = { + .name = "simple-mfd-spmi", + .owner = THIS_MODULE, + .of_match_table = simple_spmi_id_table, + }, +}; +module_spmi_driver(pmic_spmi_driver); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("Simple MFD - SPMI driver"); +MODULE_AUTHOR("Hector Martin "); From 7df181a1235d679ee5669718de30d6ed474d6978 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 15 Feb 2022 18:45:25 +0900 Subject: [PATCH 0400/1020] nvmem: Add spmi-mfd-nvmem driver This driver exposes part of an SPMI MFD device as an NVMEM device. It is intended to be used with e.g. PMUs/PMICs that are used to hold power-management configuration, such as used on Apple Silicon Macs. Signed-off-by: Hector Martin --- drivers/nvmem/Kconfig | 13 +++++ drivers/nvmem/Makefile | 2 + drivers/nvmem/spmi-mfd-nvmem.c | 98 ++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 drivers/nvmem/spmi-mfd-nvmem.c diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 283134498fbc33..9317dfbc972077 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -299,6 +299,19 @@ config NVMEM_SNVS_LPGPR This driver can also be built as a module. If so, the module will be called nvmem-snvs-lpgpr. +config NVMEM_SPMI_MFD + tristate "Generic SPMI MFD NVMEM" + depends on MFD_SIMPLE_MFD_SPMI || COMPILE_TEST + default ARCH_APPLE + help + Say y here to build a generic driver to expose an SPMI MFD device + as a NVMEM provider. This can be used for PMIC/PMU devices which + are used to store power and RTC-related settings on certain + platforms, such as Apple Silicon Macs. + + This driver can also be built as a module. If so, the module + will be called nvmem-spmi-mfd. + config NVMEM_SPMI_SDAM tristate "SPMI SDAM Support" depends on SPMI diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index cdd01fbf1313b5..04533daadd58ea 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -62,6 +62,8 @@ obj-$(CONFIG_NVMEM_SC27XX_EFUSE) += nvmem-sc27xx-efuse.o nvmem-sc27xx-efuse-y := sc27xx-efuse.o obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o nvmem_snvs_lpgpr-y := snvs_lpgpr.o +obj-$(CONFIG_NVMEM_SPMI_MFD) += nvmem_spmi_mfd.o +nvmem_spmi_mfd-y := spmi-mfd-nvmem.o obj-$(CONFIG_NVMEM_SPMI_SDAM) += nvmem_qcom-spmi-sdam.o nvmem_qcom-spmi-sdam-y += qcom-spmi-sdam.o obj-$(CONFIG_NVMEM_SPRD_EFUSE) += nvmem_sprd_efuse.o diff --git a/drivers/nvmem/spmi-mfd-nvmem.c b/drivers/nvmem/spmi-mfd-nvmem.c new file mode 100644 index 00000000000000..e74ced47e68d85 --- /dev/null +++ b/drivers/nvmem/spmi-mfd-nvmem.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Generic SPMI MFD NVMEM driver + * + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include + +struct spmi_mfd_nvmem { + struct regmap *regmap; + unsigned int base; +}; + +static int spmi_mfd_nvmem_read(void *priv, unsigned int offset, + void *val, size_t bytes) +{ + struct spmi_mfd_nvmem *nvmem = priv; + + return regmap_bulk_read(nvmem->regmap, nvmem->base + offset, val, bytes); +} + +static int spmi_mfd_nvmem_write(void *priv, unsigned int offset, + void *val, size_t bytes) +{ + struct spmi_mfd_nvmem *nvmem = priv; + + return regmap_bulk_write(nvmem->regmap, nvmem->base + offset, val, bytes); +} + +static int spmi_mfd_nvmem_probe(struct platform_device *pdev) +{ + struct spmi_mfd_nvmem *nvmem; + const __be32 *addr; + int len; + struct nvmem_config nvmem_cfg = { + .dev = &pdev->dev, + .name = "spmi_mfd_nvmem", + .id = NVMEM_DEVID_AUTO, + .word_size = 1, + .stride = 1, + .reg_read = spmi_mfd_nvmem_read, + .reg_write = spmi_mfd_nvmem_write, + }; + + nvmem = devm_kzalloc(&pdev->dev, sizeof(*nvmem), GFP_KERNEL); + if (!nvmem) + return -ENOMEM; + + nvmem_cfg.priv = nvmem; + + nvmem->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!nvmem->regmap) { + dev_err(&pdev->dev, "Parent regmap unavailable.\n"); + return -ENXIO; + } + + addr = of_get_property(pdev->dev.of_node, "reg", &len); + if (!addr) { + dev_err(&pdev->dev, "no reg property\n"); + return -EINVAL; + } + if (len != 2 * sizeof(u32)) { + dev_err(&pdev->dev, "invalid reg property\n"); + return -EINVAL; + } + + nvmem->base = be32_to_cpup(&addr[0]); + nvmem_cfg.size = be32_to_cpup(&addr[1]); + + return PTR_ERR_OR_ZERO(devm_nvmem_register(&pdev->dev, &nvmem_cfg)); +} + +static const struct of_device_id spmi_mfd_nvmem_id_table[] = { + { .compatible = "apple,spmi-pmu-nvmem" }, + { .compatible = "spmi-mfd-nvmem" }, + { }, +}; +MODULE_DEVICE_TABLE(of, spmi_mfd_nvmem_id_table); + +static struct platform_driver spmi_mfd_nvmem_driver = { + .probe = spmi_mfd_nvmem_probe, + .driver = { + .name = "spmi-mfd-nvmem", + .of_match_table = spmi_mfd_nvmem_id_table, + }, +}; + +module_platform_driver(spmi_mfd_nvmem_driver); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_AUTHOR("Hector Martin "); +MODULE_DESCRIPTION("SPMI MFD NVMEM driver"); From 8dbd45c4ae986bf73f3414d930c973158674b704 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 27 Feb 2024 22:36:52 +0100 Subject: [PATCH 0401/1020] nvmem: spmi-mfd-nvmem: use legacy fixed layout Fixes probing of nvmem cells in v6.8-rc6 Signed-off-by: Janne Grunau --- drivers/nvmem/spmi-mfd-nvmem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/nvmem/spmi-mfd-nvmem.c b/drivers/nvmem/spmi-mfd-nvmem.c index e74ced47e68d85..462f350640d1e4 100644 --- a/drivers/nvmem/spmi-mfd-nvmem.c +++ b/drivers/nvmem/spmi-mfd-nvmem.c @@ -46,6 +46,7 @@ static int spmi_mfd_nvmem_probe(struct platform_device *pdev) .stride = 1, .reg_read = spmi_mfd_nvmem_read, .reg_write = spmi_mfd_nvmem_write, + .add_legacy_fixed_of_cells = true, }; nvmem = devm_kzalloc(&pdev->dev, sizeof(*nvmem), GFP_KERNEL); From 846e4de674111b753f65d595b8dfbba428dca6b7 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 18 Apr 2023 21:50:43 +0900 Subject: [PATCH 0402/1020] cpufreq: apple-soc: Drop setting the PS2 field on M2+ Newer devices don't use this. We still don't know what it does, but let's keep to the same behavior macOS has here. Signed-off-by: Hector Martin --- drivers/cpufreq/apple-soc-cpufreq.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/apple-soc-cpufreq.c b/drivers/cpufreq/apple-soc-cpufreq.c index 021f423705e1b1..51a71322f07ac8 100644 --- a/drivers/cpufreq/apple-soc-cpufreq.c +++ b/drivers/cpufreq/apple-soc-cpufreq.c @@ -25,7 +25,7 @@ #define APPLE_DVFS_CMD 0x20 #define APPLE_DVFS_CMD_BUSY BIT(31) #define APPLE_DVFS_CMD_SET BIT(25) -#define APPLE_DVFS_CMD_PS2 GENMASK(16, 12) +#define APPLE_DVFS_CMD_PS2 GENMASK(15, 12) #define APPLE_DVFS_CMD_PS1 GENMASK(4, 0) /* Same timebase as CPU counter (24MHz) */ @@ -55,6 +55,7 @@ #define APPLE_DVFS_TRANSITION_TIMEOUT 100 struct apple_soc_cpufreq_info { + bool has_ps2; u64 max_pstate; u64 cur_pstate_mask; u64 cur_pstate_shift; @@ -69,18 +70,21 @@ struct apple_cpu_priv { static struct cpufreq_driver apple_soc_cpufreq_driver; static const struct apple_soc_cpufreq_info soc_t8103_info = { + .has_ps2 = true, .max_pstate = 15, .cur_pstate_mask = APPLE_DVFS_STATUS_CUR_PS_T8103, .cur_pstate_shift = APPLE_DVFS_STATUS_CUR_PS_SHIFT_T8103, }; static const struct apple_soc_cpufreq_info soc_t8112_info = { + .has_ps2 = false, .max_pstate = 31, .cur_pstate_mask = APPLE_DVFS_STATUS_CUR_PS_T8112, .cur_pstate_shift = APPLE_DVFS_STATUS_CUR_PS_SHIFT_T8112, }; static const struct apple_soc_cpufreq_info soc_default_info = { + .has_ps2 = false, .max_pstate = 15, .cur_pstate_mask = 0, /* fallback */ }; @@ -148,9 +152,12 @@ static int apple_soc_cpufreq_set_target(struct cpufreq_policy *policy, return -EIO; } - reg &= ~(APPLE_DVFS_CMD_PS1 | APPLE_DVFS_CMD_PS2); + reg &= ~APPLE_DVFS_CMD_PS1; reg |= FIELD_PREP(APPLE_DVFS_CMD_PS1, pstate); - reg |= FIELD_PREP(APPLE_DVFS_CMD_PS2, pstate); + if (priv->info->has_ps2) { + reg &= ~APPLE_DVFS_CMD_PS2; + reg |= FIELD_PREP(APPLE_DVFS_CMD_PS2, pstate); + } reg |= APPLE_DVFS_CMD_SET; writeq_relaxed(reg, priv->reg_base + APPLE_DVFS_CMD); From 5c2f764474e3ee7f687620ee324e391cf89d0108 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 30 Aug 2022 02:09:14 +0900 Subject: [PATCH 0403/1020] usb: renesas-xhci: Build loader into the main xhci-pci module This doesn't make any sense as a module, since it becomes a symbol dependency of the main driver if enabled, and therefore would always be loaded regardless of the user's hardware. Just compile it in, if enabled. Signed-off-by: Hector Martin --- drivers/usb/host/Kconfig | 4 ++-- drivers/usb/host/Makefile | 3 ++- drivers/usb/host/{xhci-pci.c => xhci-pci-core.c} | 0 drivers/usb/host/xhci-pci-renesas.c | 4 +--- drivers/usb/host/xhci-pci.h | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) rename drivers/usb/host/{xhci-pci.c => xhci-pci-core.c} (100%) diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 4448d0ab06f0d4..17b66c10716aef 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -40,11 +40,11 @@ config USB_XHCI_DBGCAP config USB_XHCI_PCI tristate depends on USB_PCI - depends on USB_XHCI_PCI_RENESAS || !USB_XHCI_PCI_RENESAS default y config USB_XHCI_PCI_RENESAS - tristate "Support for additional Renesas xHCI controller with firmware" + bool "Support for Renesas xHCI controllers with firmware" + depends on USB_XHCI_PCI help Say 'Y' to enable the support for the Renesas xHCI controller with firmware. Make sure you have the firmware for the device and diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index be4e5245c52fe9..7776f997a55703 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -68,7 +68,8 @@ obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o obj-$(CONFIG_USB_FHCI_HCD) += fhci.o obj-$(CONFIG_USB_XHCI_HCD) += xhci-hcd.o obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o -obj-$(CONFIG_USB_XHCI_PCI_RENESAS) += xhci-pci-renesas.o +xhci-pci-y += xhci-pci-core.o +xhci-pci-$(CONFIG_USB_XHCI_PCI_RENESAS) += xhci-pci-renesas.o obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o obj-$(CONFIG_USB_XHCI_HISTB) += xhci-histb.o obj-$(CONFIG_USB_XHCI_RCAR) += xhci-rcar-hcd.o diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci-core.c similarity index 100% rename from drivers/usb/host/xhci-pci.c rename to drivers/usb/host/xhci-pci-core.c diff --git a/drivers/usb/host/xhci-pci-renesas.c b/drivers/usb/host/xhci-pci-renesas.c index 93f8b355bc706e..dddd35e50a2c54 100644 --- a/drivers/usb/host/xhci-pci-renesas.c +++ b/drivers/usb/host/xhci-pci-renesas.c @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -625,6 +624,5 @@ int renesas_xhci_check_request_fw(struct pci_dev *pdev, release_firmware(fw); return err; } -EXPORT_SYMBOL_GPL(renesas_xhci_check_request_fw); -MODULE_LICENSE("GPL v2"); +MODULE_FIRMWARE("renesas_usb_fw.mem"); diff --git a/drivers/usb/host/xhci-pci.h b/drivers/usb/host/xhci-pci.h index cb9a8f331a4463..dff1b99564cc69 100644 --- a/drivers/usb/host/xhci-pci.h +++ b/drivers/usb/host/xhci-pci.h @@ -9,7 +9,7 @@ int renesas_xhci_check_request_fw(struct pci_dev *dev, const struct pci_device_id *id); #else -static int renesas_xhci_check_request_fw(struct pci_dev *dev, +static inline int renesas_xhci_check_request_fw(struct pci_dev *dev, const struct pci_device_id *id) { return 0; From 8bfcafad56d2d06eb3123c50cfc167050e122797 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 30 Aug 2022 02:11:48 +0900 Subject: [PATCH 0404/1020] xhci-pci: asmedia: Add a firmware loader for ASM2214a chips Apple ships ASM2214a ICs in some Apple Silicon hardware (notably, the 2021 iMac and the 2022 Mac Studio) without a flash ROM, and relies on the OS to load firmware on startup. Add support for this to the generic xhci-pci driver. The loader code first checks the firmware version, and only attempts to load firmware if the version isn't the known ROM version. Since this arrangement only exists on Apple machines so far, and Apple are the only source of the (non-redistributable) firmware intended for use on these machines, the firmware is named asmedia/asm2214a-apple.bin. If this style of firmware loading ever becomes necessary on non-Apple machines, we should add a generic firmware name at the time (if it can be part of linux-firmware) or another vendor-specific firmware name. Signed-off-by: Hector Martin --- drivers/usb/host/Kconfig | 9 + drivers/usb/host/Makefile | 1 + drivers/usb/host/xhci-pci-asmedia.c | 394 ++++++++++++++++++++++++++++ drivers/usb/host/xhci-pci-core.c | 24 ++ drivers/usb/host/xhci-pci.h | 13 + drivers/usb/host/xhci.h | 1 + 6 files changed, 442 insertions(+) create mode 100644 drivers/usb/host/xhci-pci-asmedia.c diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 17b66c10716aef..fdcbc71153182c 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -51,6 +51,15 @@ config USB_XHCI_PCI_RENESAS installed on your system for this device to work. If unsure, say 'N'. +config USB_XHCI_PCI_ASMEDIA + bool "Support for ASMedia xHCI controller with firmware" + default ARCH_APPLE + depends on USB_XHCI_PCI + help + Say 'Y' to enable support for ASMedia xHCI controllers with + host-supplied firmware. These are usually present on Apple devices. + If unsure, say 'N'. + config USB_XHCI_PLATFORM tristate "Generic xHCI driver for a platform device" help diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 7776f997a55703..b64cb3398b7606 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_USB_XHCI_HCD) += xhci-hcd.o obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o xhci-pci-y += xhci-pci-core.o xhci-pci-$(CONFIG_USB_XHCI_PCI_RENESAS) += xhci-pci-renesas.o +xhci-pci-$(CONFIG_USB_XHCI_PCI_ASMEDIA) += xhci-pci-asmedia.o obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o obj-$(CONFIG_USB_XHCI_HISTB) += xhci-histb.o obj-$(CONFIG_USB_XHCI_RCAR) += xhci-rcar-hcd.o diff --git a/drivers/usb/host/xhci-pci-asmedia.c b/drivers/usb/host/xhci-pci-asmedia.c new file mode 100644 index 00000000000000..80f1b3eabd6deb --- /dev/null +++ b/drivers/usb/host/xhci-pci-asmedia.c @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * ASMedia xHCI firmware loader + * Copyright (C) The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include + +#include "xhci.h" +#include "xhci-trace.h" +#include "xhci-pci.h" + +/* Configuration space registers */ +#define ASMT_CFG_CONTROL 0xe0 +#define ASMT_CFG_CONTROL_WRITE BIT(1) +#define ASMT_CFG_CONTROL_READ BIT(0) + +#define ASMT_CFG_SRAM_ADDR 0xe2 + +#define ASMT_CFG_SRAM_ACCESS 0xef +#define ASMT_CFG_SRAM_ACCESS_READ BIT(6) +#define ASMT_CFG_SRAM_ACCESS_ENABLE BIT(7) + +#define ASMT_CFG_DATA_READ0 0xf0 +#define ASMT_CFG_DATA_READ1 0xf4 + +#define ASMT_CFG_DATA_WRITE0 0xf8 +#define ASMT_CFG_DATA_WRITE1 0xfc + +#define ASMT_CMD_GET_FWVER 0x8000060840 +#define ASMT_FWVER_ROM 0x010250090816 + +/* BAR0 registers */ +#define ASMT_REG_ADDR 0x3000 + +#define ASMT_REG_WDATA 0x3004 +#define ASMT_REG_RDATA 0x3008 + +#define ASMT_REG_STATUS 0x3009 +#define ASMT_REG_STATUS_BUSY BIT(7) + +#define ASMT_REG_CODE_WDATA 0x3010 +#define ASMT_REG_CODE_RDATA 0x3018 + +#define ASMT_MMIO_CPU_MISC 0x500e +#define ASMT_MMIO_CPU_MISC_CODE_RAM_WR BIT(0) + +#define ASMT_MMIO_CPU_MODE_NEXT 0x5040 +#define ASMT_MMIO_CPU_MODE_CUR 0x5041 + +#define ASMT_MMIO_CPU_MODE_RAM BIT(0) +#define ASMT_MMIO_CPU_MODE_HALFSPEED BIT(1) + +#define ASMT_MMIO_CPU_EXEC_CTRL 0x5042 +#define ASMT_MMIO_CPU_EXEC_CTRL_RESET BIT(0) +#define ASMT_MMIO_CPU_EXEC_CTRL_HALT BIT(1) + +#define TIMEOUT_USEC 10000 +#define RESET_TIMEOUT_USEC 500000 + +static int asmedia_mbox_tx(struct pci_dev *pdev, u64 data) +{ + u8 op; + int i; + + for (i = 0; i < TIMEOUT_USEC; i++) { + pci_read_config_byte(pdev, ASMT_CFG_CONTROL, &op); + if (!(op & ASMT_CFG_CONTROL_WRITE)) + break; + udelay(1); + } + + if (op & ASMT_CFG_CONTROL_WRITE) { + dev_err(&pdev->dev, + "Timed out on mailbox tx: 0x%llx\n", + data); + return -ETIMEDOUT; + } + + pci_write_config_dword(pdev, ASMT_CFG_DATA_WRITE0, data); + pci_write_config_dword(pdev, ASMT_CFG_DATA_WRITE1, data >> 32); + pci_write_config_byte(pdev, ASMT_CFG_CONTROL, + ASMT_CFG_CONTROL_WRITE); + + return 0; +} + +static int asmedia_mbox_rx(struct pci_dev *pdev, u64 *data) +{ + u8 op; + u32 low, high; + int i; + + for (i = 0; i < TIMEOUT_USEC; i++) { + pci_read_config_byte(pdev, ASMT_CFG_CONTROL, &op); + if (op & ASMT_CFG_CONTROL_READ) + break; + udelay(1); + } + + if (!(op & ASMT_CFG_CONTROL_READ)) { + dev_err(&pdev->dev, "Timed out on mailbox rx\n"); + return -ETIMEDOUT; + } + + pci_read_config_dword(pdev, ASMT_CFG_DATA_READ0, &low); + pci_read_config_dword(pdev, ASMT_CFG_DATA_READ1, &high); + pci_write_config_byte(pdev, ASMT_CFG_CONTROL, + ASMT_CFG_CONTROL_READ); + + *data = ((u64)high << 32) | low; + return 0; +} + +static int asmedia_get_fw_version(struct pci_dev *pdev, u64 *version) +{ + int err = 0; + u64 cmd; + + err = asmedia_mbox_tx(pdev, ASMT_CMD_GET_FWVER); + if (err) + return err; + err = asmedia_mbox_tx(pdev, 0); + if (err) + return err; + + err = asmedia_mbox_rx(pdev, &cmd); + if (err) + return err; + err = asmedia_mbox_rx(pdev, version); + if (err) + return err; + + if (cmd != ASMT_CMD_GET_FWVER) { + dev_err(&pdev->dev, "Unexpected reply command 0x%llx\n", cmd); + return -EIO; + } + + return 0; +} + +static bool asmedia_check_firmware(struct pci_dev *pdev) +{ + u64 fwver; + int ret; + + ret = asmedia_get_fw_version(pdev, &fwver); + if (ret) + return ret; + + dev_info(&pdev->dev, "Firmware version: 0x%llx\n", fwver); + + return fwver != ASMT_FWVER_ROM; +} + +static int asmedia_wait_reset(struct pci_dev *pdev) +{ + struct usb_hcd *hcd = dev_get_drvdata(&pdev->dev); + struct xhci_cap_regs __iomem *cap = hcd->regs; + struct xhci_op_regs __iomem *op; + u32 val; + int ret; + + op = hcd->regs + HC_LENGTH(readl(&cap->hc_capbase)); + + ret = readl_poll_timeout(&op->command, + val, !(val & CMD_RESET), + 1000, RESET_TIMEOUT_USEC); + + if (!ret) + return 0; + + dev_err(hcd->self.controller, "Reset timed out, trying to kick it\n"); + + pci_write_config_byte(pdev, ASMT_CFG_SRAM_ACCESS, + ASMT_CFG_SRAM_ACCESS_ENABLE); + + pci_write_config_byte(pdev, ASMT_CFG_SRAM_ACCESS, 0); + + ret = readl_poll_timeout(&op->command, + val, !(val & CMD_RESET), + 1000, RESET_TIMEOUT_USEC); + + if (ret) + dev_err(hcd->self.controller, "Reset timed out, giving up\n"); + + return ret; +} + +static u8 asmedia_read_reg(struct usb_hcd *hcd, u16 addr) { + void __iomem *regs = hcd->regs; + u8 status; + int ret; + + ret = readb_poll_timeout(regs + ASMT_REG_STATUS, + status, !(status & ASMT_REG_STATUS_BUSY), + 1000, TIMEOUT_USEC); + + if (ret) { + dev_err(hcd->self.controller, + "Read reg wait timed out ([%04x])\n", addr); + return ~0; + } + + writew_relaxed(addr, regs + ASMT_REG_ADDR); + + ret = readb_poll_timeout(regs + ASMT_REG_STATUS, + status, !(status & ASMT_REG_STATUS_BUSY), + 1000, TIMEOUT_USEC); + + if (ret) { + dev_err(hcd->self.controller, + "Read reg addr timed out ([%04x])\n", addr); + return ~0; + } + + return readb_relaxed(regs + ASMT_REG_RDATA); +} + +static void asmedia_write_reg(struct usb_hcd *hcd, u16 addr, u8 data, bool wait) { + void __iomem *regs = hcd->regs; + u8 status; + int ret, i; + + writew_relaxed(addr, regs + ASMT_REG_ADDR); + + ret = readb_poll_timeout(regs + ASMT_REG_STATUS, + status, !(status & ASMT_REG_STATUS_BUSY), + 1000, TIMEOUT_USEC); + + if (ret) + dev_err(hcd->self.controller, + "Write reg addr timed out ([%04x] = %02x)\n", + addr, data); + + writeb_relaxed(data, regs + ASMT_REG_WDATA); + + ret = readb_poll_timeout(regs + ASMT_REG_STATUS, + status, !(status & ASMT_REG_STATUS_BUSY), + 1000, TIMEOUT_USEC); + + if (ret) + dev_err(hcd->self.controller, + "Write reg data timed out ([%04x] = %02x)\n", + addr, data); + + if (!wait) + return; + + for (i = 0; i < TIMEOUT_USEC; i++) { + if (asmedia_read_reg(hcd, addr) == data) + break; + } + + if (i >= TIMEOUT_USEC) { + dev_err(hcd->self.controller, + "Verify register timed out ([%04x] = %02x)\n", + addr, data); + } +} + +static int asmedia_load_fw(struct pci_dev *pdev, const struct firmware *fw) +{ + struct usb_hcd *hcd; + void __iomem *regs; + const u16 *fw_data = (const u16 *)fw->data; + u16 raddr; + u32 data; + size_t index = 0, addr = 0; + size_t words = fw->size >> 1; + int ret, i; + + hcd = dev_get_drvdata(&pdev->dev); + regs = hcd->regs; + + asmedia_write_reg(hcd, ASMT_MMIO_CPU_MODE_NEXT, + ASMT_MMIO_CPU_MODE_HALFSPEED, false); + + asmedia_write_reg(hcd, ASMT_MMIO_CPU_EXEC_CTRL, + ASMT_MMIO_CPU_EXEC_CTRL_RESET, false); + + ret = asmedia_wait_reset(pdev); + if (ret) { + dev_err(hcd->self.controller, "Failed pre-upload reset\n"); + return ret; + } + + asmedia_write_reg(hcd, ASMT_MMIO_CPU_EXEC_CTRL, + ASMT_MMIO_CPU_EXEC_CTRL_HALT, false); + + asmedia_write_reg(hcd, ASMT_MMIO_CPU_MISC, + ASMT_MMIO_CPU_MISC_CODE_RAM_WR, true); + + pci_write_config_byte(pdev, ASMT_CFG_SRAM_ACCESS, + ASMT_CFG_SRAM_ACCESS_ENABLE); + + /* The firmware upload is interleaved in 0x4000 word blocks */ + addr = index = 0; + while (index < words) { + data = fw_data[index]; + if ((index | 0x4000) < words) + data |= fw_data[index | 0x4000] << 16; + + pci_write_config_word(pdev, ASMT_CFG_SRAM_ADDR, + addr); + + writel_relaxed(data, regs + ASMT_REG_CODE_WDATA); + + for (i = 0; i < TIMEOUT_USEC; i++) { + pci_read_config_word(pdev, ASMT_CFG_SRAM_ADDR, &raddr); + if (raddr != addr) + break; + udelay(1); + } + + if (raddr == addr) { + dev_err(hcd->self.controller, "Word write timed out\n"); + return -ETIMEDOUT; + } + + if (++index & 0x4000) + index += 0x4000; + addr += 2; + } + + pci_write_config_byte(pdev, ASMT_CFG_SRAM_ACCESS, 0); + + asmedia_write_reg(hcd, ASMT_MMIO_CPU_MISC, 0, true); + + asmedia_write_reg(hcd, ASMT_MMIO_CPU_MODE_NEXT, + ASMT_MMIO_CPU_MODE_RAM | + ASMT_MMIO_CPU_MODE_HALFSPEED, false); + + asmedia_write_reg(hcd, ASMT_MMIO_CPU_EXEC_CTRL, 0, false); + + ret = asmedia_wait_reset(pdev); + if (ret) { + dev_err(hcd->self.controller, "Failed post-upload reset\n"); + return ret; + } + + return 0; +} + +int asmedia_xhci_check_request_fw(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct xhci_driver_data *driver_data = + (struct xhci_driver_data *)id->driver_data; + const char *fw_name = driver_data->firmware; + const struct firmware *fw; + int ret; + + /* Check if device has firmware, if so skip everything */ + ret = asmedia_check_firmware(pdev); + if (ret < 0) + return ret; + else if (ret == 1) + return 0; + + pci_dev_get(pdev); + ret = request_firmware(&fw, fw_name, &pdev->dev); + pci_dev_put(pdev); + if (ret) { + dev_err(&pdev->dev, "Could not load firmware %s: %d\n", + fw_name, ret); + return ret; + } + + ret = asmedia_load_fw(pdev, fw); + if (ret) { + dev_err(&pdev->dev, "Firmware upload failed: %d\n", ret); + goto err; + } + + ret = asmedia_check_firmware(pdev); + if (ret < 0) { + goto err; + } else if (ret != 1) { + dev_err(&pdev->dev, "Firmware version is too old after upload\n"); + ret = -EIO; + } else { + ret = 0; + } + +err: + release_firmware(fw); + return ret; +} diff --git a/drivers/usb/host/xhci-pci-core.c b/drivers/usb/host/xhci-pci-core.c index 93b6976480188c..10756af53ab708 100644 --- a/drivers/usb/host/xhci-pci-core.c +++ b/drivers/usb/host/xhci-pci-core.c @@ -534,6 +534,18 @@ static int xhci_pci_setup(struct usb_hcd *hcd) struct xhci_hcd *xhci; struct pci_dev *pdev = to_pci_dev(hcd->self.controller); int retval; + struct xhci_driver_data *driver_data; + const struct pci_device_id *id; + + id = pci_match_id(to_pci_driver(pdev->dev.driver)->id_table, pdev); + if (id && id->driver_data && usb_hcd_is_primary_hcd(hcd)) { + driver_data = (struct xhci_driver_data *)id->driver_data; + if (driver_data->quirks & XHCI_ASMEDIA_FW_QUIRK) { + retval = asmedia_xhci_check_request_fw(pdev, id); + if (retval < 0) + return retval; + } + } xhci = hcd_to_xhci(hcd); if (!xhci->sbrn) @@ -887,6 +899,11 @@ static const struct xhci_driver_data reneses_data = { .firmware = "renesas_usb_fw.mem", }; +static const struct xhci_driver_data asmedia_data = { + .quirks = XHCI_ASMEDIA_FW_QUIRK, + .firmware = "asmedia/asm2214a-apple.bin", +}; + /* PCI driver selection metadata; PCI hotplugging uses this */ static const struct pci_device_id pci_ids[] = { { PCI_DEVICE(0x1912, 0x0014), @@ -895,6 +912,9 @@ static const struct pci_device_id pci_ids[] = { { PCI_DEVICE(0x1912, 0x0015), .driver_data = (unsigned long)&reneses_data, }, + { PCI_DEVICE(0x1b21, 0x2142), + .driver_data = (unsigned long)&asmedia_data, + }, /* handle any USB 3.0 xHCI controller */ { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, ~0), }, @@ -910,6 +930,10 @@ MODULE_DEVICE_TABLE(pci, pci_ids); MODULE_FIRMWARE("renesas_usb_fw.mem"); #endif +#if IS_ENABLED(CONFIG_USB_XHCI_PCI_ASMEDIA) +MODULE_FIRMWARE("asmedia/asm2214a-apple.bin"); +#endif + /* pci driver glue; this is a "new style" PCI driver module */ static struct pci_driver xhci_pci_driver = { .name = hcd_name, diff --git a/drivers/usb/host/xhci-pci.h b/drivers/usb/host/xhci-pci.h index dff1b99564cc69..279c95acc43f6d 100644 --- a/drivers/usb/host/xhci-pci.h +++ b/drivers/usb/host/xhci-pci.h @@ -17,6 +17,19 @@ static inline int renesas_xhci_check_request_fw(struct pci_dev *dev, #endif +#if IS_ENABLED(CONFIG_USB_XHCI_PCI_ASMEDIA) +int asmedia_xhci_check_request_fw(struct pci_dev *dev, + const struct pci_device_id *id); + +#else +static inline int asmedia_xhci_check_request_fw(struct pci_dev *dev, + const struct pci_device_id *id) +{ + return 0; +} + +#endif + struct xhci_driver_data { u64 quirks; const char *firmware; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 6f4bf98a628245..654299e114f78b 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1628,6 +1628,7 @@ struct xhci_hcd { #define XHCI_RESET_TO_DEFAULT BIT_ULL(44) #define XHCI_ZHAOXIN_TRB_FETCH BIT_ULL(45) #define XHCI_ZHAOXIN_HOST BIT_ULL(46) +#define XHCI_ASMEDIA_FW_QUIRK BIT_ULL(47) unsigned int num_active_eps; unsigned int limit_active_eps; From 7d11b1a59e35c72f80b3b8c0bfce7db0ff50aeb0 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 2 May 2022 21:17:41 +0900 Subject: [PATCH 0405/1020] dt-bindings: pci: apple,pcie: Add subnode binding, pwren-gpios property We weren't properly validating root port subnodes, so let's do that. Then, also add the new `pwren-gpios` property there to handle device power-up. Signed-off-by: Hector Martin --- .../devicetree/bindings/pci/apple,pcie.yaml | 51 +++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/apple,pcie.yaml b/Documentation/devicetree/bindings/pci/apple,pcie.yaml index 215ff9a9c8352e..8936f8a4aeda43 100644 --- a/Documentation/devicetree/bindings/pci/apple,pcie.yaml +++ b/Documentation/devicetree/bindings/pci/apple,pcie.yaml @@ -72,6 +72,27 @@ properties: power-domains: maxItems: 1 +patternProperties: + "^pci@": + $ref: /schemas/pci/pci-bus.yaml# + type: object + description: A single PCI root port + + properties: + reg: + maxItems: 1 + + pwren-gpios: + description: Optional GPIO to power on the device + maxItems: 1 + + required: + - reset-gpios + - interrupt-controller + - "#interrupt-cells" + - interrupt-map-mask + - interrupt-map + required: - compatible - reg @@ -142,7 +163,7 @@ examples: pinctrl-0 = <&pcie_pins>; pinctrl-names = "default"; - pci@0,0 { + port00: pci@0,0 { device_type = "pci"; reg = <0x0 0x0 0x0 0x0 0x0>; reset-gpios = <&pinctrl_ap 152 0>; @@ -150,9 +171,17 @@ examples: #address-cells = <3>; #size-cells = <2>; ranges; + + interrupt-controller; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &port00 0 0 0 0>, + <0 0 0 2 &port00 0 0 0 1>, + <0 0 0 3 &port00 0 0 0 2>, + <0 0 0 4 &port00 0 0 0 3>; }; - pci@1,0 { + port01: pci@1,0 { device_type = "pci"; reg = <0x800 0x0 0x0 0x0 0x0>; reset-gpios = <&pinctrl_ap 153 0>; @@ -160,9 +189,17 @@ examples: #address-cells = <3>; #size-cells = <2>; ranges; + + interrupt-controller; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &port01 0 0 0 0>, + <0 0 0 2 &port01 0 0 0 1>, + <0 0 0 3 &port01 0 0 0 2>, + <0 0 0 4 &port01 0 0 0 3>; }; - pci@2,0 { + port02: pci@2,0 { device_type = "pci"; reg = <0x1000 0x0 0x0 0x0 0x0>; reset-gpios = <&pinctrl_ap 33 0>; @@ -170,6 +207,14 @@ examples: #address-cells = <3>; #size-cells = <2>; ranges; + + interrupt-controller; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &port02 0 0 0 0>, + <0 0 0 2 &port02 0 0 0 1>, + <0 0 0 3 &port02 0 0 0 2>, + <0 0 0 4 &port02 0 0 0 3>; }; }; }; From b894a64d2b3c367077641a4843cf641a12735c8b Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 2 May 2022 21:22:46 +0900 Subject: [PATCH 0406/1020] PCI: apple: Use gpiod_set_value_cansleep in probe flow We're allowed to sleep here, so tell the GPIO core by using gpiod_set_value_cansleep instead of gpiod_set_value. Fixes: 1e33888fbe44 ("PCI: apple: Add initial hardware bring-up") Acked-by: Marc Zyngier Signed-off-by: Hector Martin --- drivers/pci/controller/pcie-apple.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index f7a248393a8f16..7e6bd63a6425e6 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -541,7 +541,7 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, rmw_set(PORT_APPCLK_EN, port->base + PORT_APPCLK); /* Assert PERST# before setting up the clock */ - gpiod_set_value(reset, 1); + gpiod_set_value_cansleep(reset, 1); ret = apple_pcie_setup_refclk(pcie, port); if (ret < 0) @@ -552,7 +552,7 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, /* Deassert PERST# */ rmw_set(PORT_PERST_OFF, port->base + PORT_PERST); - gpiod_set_value(reset, 0); + gpiod_set_value_cansleep(reset, 0); /* Wait for 100ms after PERST# deassertion (PCIe r5.0, 6.6.1) */ msleep(100); From a338d7a2aff76c4de9afb4dc5bbf7453fc7765c3 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 6 Feb 2022 21:15:39 +0900 Subject: [PATCH 0407/1020] PCI: apple: Probe all GPIOs for availability first If we're probing the PCI controller and some GPIOs are not available and cause a probe defer, we can end up leaving some ports initialized and not others and making a mess. Check for PERST# GPIOs for all ports first, and just return -EPROBE_DEFER if any are not ready yet, without bringing anything up. Fixes: 1e33888fbe44 ("PCI: apple: Add initial hardware bring-up") Cc: stable@vger.kernel.org Acked-by: Marc Zyngier Signed-off-by: Hector Martin --- drivers/pci/controller/pcie-apple.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index 7e6bd63a6425e6..5e4e1fc9e357c8 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -507,6 +507,20 @@ static u32 apple_pcie_rid2sid_write(struct apple_pcie_port *port, return readl_relaxed(port->base + PORT_RID2SID(idx)); } +static int apple_pcie_probe_port(struct device_node *np) +{ + struct gpio_desc *gd; + + gd = fwnode_gpiod_get_index(of_fwnode_handle(np), "reset", 0, + GPIOD_OUT_LOW, "PERST#"); + if (IS_ERR(gd)) { + return PTR_ERR(gd); + } + + gpiod_put(gd); + return 0; +} + static int apple_pcie_setup_port(struct apple_pcie *pcie, struct device_node *np) { @@ -801,8 +815,19 @@ static int apple_pcie_init(struct pci_config_window *cfg) static int apple_pcie_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + struct device_node *of_port; int ret; + /* Check for probe dependencies for all ports first */ + for_each_available_child_of_node(dev->of_node, of_port) { + ret = apple_pcie_probe_port(of_port); + if (ret) { + of_node_put(of_port); + return dev_err_probe(dev, ret, "Port %pOF probe fail\n", of_port); + } + } + ret = bus_register_notifier(&pci_bus_type, &apple_pcie_nb); if (ret) return ret; From cac6032155a8efc5c1ead6c3b3ce5801f9ba8b9f Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 6 Feb 2022 21:18:18 +0900 Subject: [PATCH 0408/1020] PCI: apple: Add support for optional PWREN GPIO WiFi and SD card devices on M1 Macs have a separate power enable GPIO. Add support for this to the PCIe controller. This is modeled after how pcie-fu740 does it. Acked-by: Marc Zyngier Signed-off-by: Hector Martin --- drivers/pci/controller/pcie-apple.c | 34 ++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index 5e4e1fc9e357c8..336943b4652f67 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -518,6 +518,16 @@ static int apple_pcie_probe_port(struct device_node *np) } gpiod_put(gd); + + gd = fwnode_gpiod_get_index(of_fwnode_handle(np), "pwren", 0, + GPIOD_OUT_LOW, "PWREN"); + if (IS_ERR(gd)) { + if (PTR_ERR(gd) != -ENOENT) + return PTR_ERR(gd); + } else { + gpiod_put(gd); + } + return 0; } @@ -526,7 +536,7 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, { struct platform_device *platform = to_platform_device(pcie->dev); struct apple_pcie_port *port; - struct gpio_desc *reset; + struct gpio_desc *reset, *pwren = NULL; u32 stat, idx; int ret, i; @@ -535,6 +545,15 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, if (IS_ERR(reset)) return PTR_ERR(reset); + pwren = devm_fwnode_gpiod_get(pcie->dev, of_fwnode_handle(np), "pwren", + GPIOD_OUT_LOW, "PWREN"); + if (IS_ERR(pwren)) { + if (PTR_ERR(pwren) == -ENOENT) + pwren = NULL; + else + return PTR_ERR(pwren); + } + port = devm_kzalloc(pcie->dev, sizeof(*port), GFP_KERNEL); if (!port) return -ENOMEM; @@ -557,12 +576,21 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, /* Assert PERST# before setting up the clock */ gpiod_set_value_cansleep(reset, 1); + /* Power on the device if required */ + gpiod_set_value_cansleep(pwren, 1); + ret = apple_pcie_setup_refclk(pcie, port); if (ret < 0) return ret; - /* The minimal Tperst-clk value is 100us (PCIe CEM r5.0, 2.9.2) */ - usleep_range(100, 200); + /* + * The minimal Tperst-clk value is 100us (PCIe CEM r5.0, 2.9.2) + * If powering up, the minimal Tpvperl is 100ms + */ + if (pwren) + msleep(100); + else + usleep_range(100, 200); /* Deassert PERST# */ rmw_set(PORT_PERST_OFF, port->base + PORT_PERST); From c562b9e954c222beaafa3826c0ed8be726d2beac Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 5 Dec 2022 18:13:40 +0900 Subject: [PATCH 0409/1020] PCI: apple: Fix missing OF node reference in apple_pcie_setup_port In the success path we hang onto a reference to the node, so make sure to grab one. The caller iterator puts our borrowed reference when we return. Signed-off-by: Hector Martin --- drivers/pci/controller/pcie-apple.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index 336943b4652f67..6339fd4818a3ac 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -627,6 +627,9 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, list_add_tail(&port->entry, &pcie->ports); init_completion(&pcie->event); + /* In the success path, we keep a reference to np around */ + of_node_get(np); + ret = apple_pcie_port_register_irqs(port); WARN_ON(ret); From dd816ae8448021b7843433fafa44489957412402 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 13 Feb 2023 21:48:15 +0100 Subject: [PATCH 0410/1020] PCI: apple: Set only available ports up Fixes "interrupt-map" parsing in of_irq_parse_raw() which takes the node's availability into account. This became apparent after disabling unused PCIe ports in the Apple silicon device trees instead of disabling them. Link: https://lore.kernel.org/asahi/20230214-apple_dts_pcie_disable_unused-v1-0-5ea0d3ddcde3@jannau.net/ Link: https://lore.kernel.org/asahi/1ea2107a-bb86-8c22-0bbc-82c453ab08ce@linaro.org/ Fixes: 1e33888fbe44 ("PCI: apple: Add initial hardware bring-up") Cc: stable@vger.kernel.org Signed-off-by: Janne Grunau --- drivers/pci/controller/pcie-apple.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index 6339fd4818a3ac..50d8c40c81c12f 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -832,7 +832,7 @@ static int apple_pcie_init(struct pci_config_window *cfg) if (ret) return ret; - for_each_child_of_node(dev->of_node, of_port) { + for_each_available_child_of_node(dev->of_node, of_port) { ret = apple_pcie_setup_port(pcie, of_port); if (ret) { dev_err(pcie->dev, "Port %pOF setup fail: %d\n", of_port, ret); From e7a0ed3d0fd018d493e08fa787540ea9fd50d1ea Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 11 Apr 2023 19:35:27 +0900 Subject: [PATCH 0411/1020] PCI: apple: Move port PHY registers to their own reg items T602x PCIe cores move these registers around. Instead of hardcoding in another offset, let's move them into their own reg entries. This matches what Apple does on macOS device trees too. Maintains backwards compatibility with old DTs by using the old offsets. Signed-off-by: Hector Martin --- drivers/pci/controller/pcie-apple.c | 51 +++++++++++++++++++---------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index 50d8c40c81c12f..8bba82a59c5d3e 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -40,14 +40,18 @@ #define CORE_RC_STAT_READY BIT(0) #define CORE_FABRIC_STAT 0x04000 #define CORE_FABRIC_STAT_MASK 0x001F001F -#define CORE_LANE_CFG(port) (0x84000 + 0x4000 * (port)) -#define CORE_LANE_CFG_REFCLK0REQ BIT(0) -#define CORE_LANE_CFG_REFCLK1REQ BIT(1) -#define CORE_LANE_CFG_REFCLK0ACK BIT(2) -#define CORE_LANE_CFG_REFCLK1ACK BIT(3) -#define CORE_LANE_CFG_REFCLKEN (BIT(9) | BIT(10)) -#define CORE_LANE_CTL(port) (0x84004 + 0x4000 * (port)) -#define CORE_LANE_CTL_CFGACC BIT(15) + +#define CORE_PHY_DEFAULT_BASE(port) (0x84000 + 0x4000 * (port)) + +#define PHY_LANE_CFG 0x00000 +#define PHY_LANE_CFG_REFCLK0REQ BIT(0) +#define PHY_LANE_CFG_REFCLK1REQ BIT(1) +#define PHY_LANE_CFG_REFCLK0ACK BIT(2) +#define PHY_LANE_CFG_REFCLK1ACK BIT(3) +#define PHY_LANE_CFG_REFCLKEN (BIT(9) | BIT(10)) +#define PHY_LANE_CFG_REFCLKCGEN (BIT(30) | BIT(31)) +#define PHY_LANE_CTL 0x00004 +#define PHY_LANE_CTL_CFGACC BIT(15) #define PORT_LTSSMCTL 0x00080 #define PORT_LTSSMCTL_START BIT(0) @@ -146,6 +150,7 @@ struct apple_pcie_port { struct apple_pcie *pcie; struct device_node *np; void __iomem *base; + void __iomem *phy; struct irq_domain *domain; struct list_head entry; DECLARE_BITMAP(sid_map, MAX_RID2SID); @@ -474,26 +479,26 @@ static int apple_pcie_setup_refclk(struct apple_pcie *pcie, if (res < 0) return res; - rmw_set(CORE_LANE_CTL_CFGACC, pcie->base + CORE_LANE_CTL(port->idx)); - rmw_set(CORE_LANE_CFG_REFCLK0REQ, pcie->base + CORE_LANE_CFG(port->idx)); + rmw_set(PHY_LANE_CTL_CFGACC, port->phy + PHY_LANE_CTL); + rmw_set(PHY_LANE_CFG_REFCLK0REQ, port->phy + PHY_LANE_CFG); - res = readl_relaxed_poll_timeout(pcie->base + CORE_LANE_CFG(port->idx), - stat, stat & CORE_LANE_CFG_REFCLK0ACK, + res = readl_relaxed_poll_timeout(port->phy + PHY_LANE_CFG, + stat, stat & PHY_LANE_CFG_REFCLK0ACK, 100, 50000); if (res < 0) return res; - rmw_set(CORE_LANE_CFG_REFCLK1REQ, pcie->base + CORE_LANE_CFG(port->idx)); - res = readl_relaxed_poll_timeout(pcie->base + CORE_LANE_CFG(port->idx), - stat, stat & CORE_LANE_CFG_REFCLK1ACK, + rmw_set(PHY_LANE_CFG_REFCLK1REQ, port->phy + PHY_LANE_CFG); + res = readl_relaxed_poll_timeout(port->phy + PHY_LANE_CFG, + stat, stat & PHY_LANE_CFG_REFCLK1ACK, 100, 50000); if (res < 0) return res; - rmw_clear(CORE_LANE_CTL_CFGACC, pcie->base + CORE_LANE_CTL(port->idx)); + rmw_clear(PHY_LANE_CTL_CFGACC, port->phy + PHY_LANE_CTL); - rmw_set(CORE_LANE_CFG_REFCLKEN, pcie->base + CORE_LANE_CFG(port->idx)); + rmw_set(PHY_LANE_CFG_REFCLKEN, port->phy + PHY_LANE_CFG); rmw_set(PORT_REFCLK_EN, port->base + PORT_REFCLK); return 0; @@ -539,6 +544,7 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, struct gpio_desc *reset, *pwren = NULL; u32 stat, idx; int ret, i; + char name[16]; reset = devm_fwnode_gpiod_get(pcie->dev, of_fwnode_handle(np), "reset", GPIOD_OUT_LOW, "PERST#"); @@ -567,9 +573,18 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, port->pcie = pcie; port->np = np; - port->base = devm_platform_ioremap_resource(platform, port->idx + 2); + snprintf(name, sizeof(name), "port%d", port->idx); + port->base = devm_platform_ioremap_resource_byname(platform, name); if (IS_ERR(port->base)) + port->base = devm_platform_ioremap_resource(platform, port->idx + 2); + if (IS_ERR(port->base)) { return PTR_ERR(port->base); + } + + snprintf(name, sizeof(name), "phy%d", port->idx); + port->phy = devm_platform_ioremap_resource_byname(platform, name); + if (IS_ERR(port->phy)) + port->phy = pcie->base + CORE_PHY_DEFAULT_BASE(port->idx); rmw_set(PORT_APPCLK_EN, port->base + PORT_APPCLK); From f19bf9661a6dd6c030868b7b930c88746cf108a8 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 11 Apr 2023 19:37:52 +0900 Subject: [PATCH 0412/1020] PCI: apple: Drop poll for CORE_RC_PHYIF_STAT_REFCLK This is checking a core refclk in per-port setup which doesn't make a lot of sense, and the bootloader needs to have gone through this anyway. It doesn't work on T602x, so just drop it across the board. --- drivers/pci/controller/pcie-apple.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index 8bba82a59c5d3e..0b832f2b84341e 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -473,12 +473,6 @@ static int apple_pcie_setup_refclk(struct apple_pcie *pcie, u32 stat; int res; - res = readl_relaxed_poll_timeout(pcie->base + CORE_RC_PHYIF_STAT, stat, - stat & CORE_RC_PHYIF_STAT_REFCLK, - 100, 50000); - if (res < 0) - return res; - rmw_set(PHY_LANE_CTL_CFGACC, port->phy + PHY_LANE_CTL); rmw_set(PHY_LANE_CFG_REFCLK0REQ, port->phy + PHY_LANE_CFG); From 22cc698d20383f94ea452fdb44690aa7d2ef9271 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 11 Apr 2023 19:38:37 +0900 Subject: [PATCH 0413/1020] PCIE: apple: Add T602x PCIe support This version of the hardware moved around a bunch of registers, so we drop the old compatible for these and introduce register offset structures to handle the differences. Signed-off-by: Hector Martin --- drivers/pci/controller/pcie-apple.c | 127 +++++++++++++++++++++++----- 1 file changed, 106 insertions(+), 21 deletions(-) diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index 0b832f2b84341e..a816acee184b28 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -105,7 +106,7 @@ #define PORT_REFCLK_CGDIS BIT(8) #define PORT_PERST 0x00814 #define PORT_PERST_OFF BIT(0) -#define PORT_RID2SID(i16) (0x00828 + 4 * (i16)) +#define PORT_RID2SID 0x00828 #define PORT_RID2SID_VALID BIT(31) #define PORT_RID2SID_SID_SHIFT 16 #define PORT_RID2SID_BUS_SHIFT 8 @@ -123,7 +124,7 @@ #define PORT_TUNSTAT_PERST_ACK_PEND BIT(1) #define PORT_PREFMEM_ENABLE 0x00994 -#define MAX_RID2SID 64 +#define MAX_RID2SID 512 /* * The doorbell address is set to 0xfffff000, which by convention @@ -134,6 +135,57 @@ */ #define DOORBELL_ADDR CONFIG_PCIE_APPLE_MSI_DOORBELL_ADDR +struct reg_info { + u32 phy_lane_ctl; + u32 port_msiaddr; + u32 port_msiaddr_hi; + u32 port_refclk; + u32 port_perst; + u32 port_rid2sid; + u32 port_msimap; + u32 max_rid2sid; + u32 max_msimap; +}; + +const struct reg_info t8103_hw = { + .phy_lane_ctl = PHY_LANE_CTL, + .port_msiaddr = PORT_MSIADDR, + .port_msiaddr_hi = 0, + .port_refclk = PORT_REFCLK, + .port_perst = PORT_PERST, + .port_rid2sid = PORT_RID2SID, + .port_msimap = 0, + .max_rid2sid = 64, + .max_msimap = 0, +}; + +#define PORT_T602X_MSIADDR 0x016c +#define PORT_T602X_MSIADDR_HI 0x0170 +#define PORT_T602X_PERST 0x082c +#define PORT_T602X_RID2SID 0x3000 +#define PORT_T602X_MSIMAP 0x3800 + +#define PORT_MSIMAP_ENABLE BIT(31) +#define PORT_MSIMAP_TARGET GENMASK(7, 0) + +const struct reg_info t602x_hw = { + .phy_lane_ctl = 0, + .port_msiaddr = PORT_T602X_MSIADDR, + .port_msiaddr_hi = PORT_T602X_MSIADDR_HI, + .port_refclk = 0, + .port_perst = PORT_T602X_PERST, + .port_rid2sid = PORT_T602X_RID2SID, + .port_msimap = PORT_T602X_MSIMAP, + .max_rid2sid = 512, /* 16 on t602x, guess for autodetect on future HW */ + .max_msimap = 512, /* 96 on t602x, guess for autodetect on future HW */ +}; + +static const struct of_device_id apple_pcie_of_match_hw[] = { + { .compatible = "apple,t6020-pcie", .data = &t602x_hw }, + { .compatible = "apple,pcie", .data = &t8103_hw }, + { } +}; + struct apple_pcie { struct mutex lock; struct device *dev; @@ -144,6 +196,7 @@ struct apple_pcie { struct completion event; struct irq_fwspec fwspec; u32 nvecs; + const struct reg_info *hw; }; struct apple_pcie_port { @@ -267,14 +320,14 @@ static void apple_port_irq_mask(struct irq_data *data) { struct apple_pcie_port *port = irq_data_get_irq_chip_data(data); - writel_relaxed(BIT(data->hwirq), port->base + PORT_INTMSKSET); + rmw_set(BIT(data->hwirq), port->base + PORT_INTMSK); } static void apple_port_irq_unmask(struct irq_data *data) { struct apple_pcie_port *port = irq_data_get_irq_chip_data(data); - writel_relaxed(BIT(data->hwirq), port->base + PORT_INTMSKCLR); + rmw_clear(BIT(data->hwirq), port->base + PORT_INTMSK); } static bool hwirq_is_intx(unsigned int hwirq) @@ -378,6 +431,7 @@ static void apple_port_irq_handler(struct irq_desc *desc) static int apple_pcie_port_setup_irq(struct apple_pcie_port *port) { struct fwnode_handle *fwnode = &port->np->fwnode; + struct apple_pcie *pcie = port->pcie; unsigned int irq; /* FIXME: consider moving each interrupt under each port */ @@ -393,19 +447,35 @@ static int apple_pcie_port_setup_irq(struct apple_pcie_port *port) return -ENOMEM; /* Disable all interrupts */ - writel_relaxed(~0, port->base + PORT_INTMSKSET); + writel_relaxed(~0, port->base + PORT_INTMSK); writel_relaxed(~0, port->base + PORT_INTSTAT); + writel_relaxed(~0, port->base + PORT_LINKCMDSTS); irq_set_chained_handler_and_data(irq, apple_port_irq_handler, port); /* Configure MSI base address */ - BUILD_BUG_ON(upper_32_bits(DOORBELL_ADDR)); - writel_relaxed(lower_32_bits(DOORBELL_ADDR), port->base + PORT_MSIADDR); + BUG_ON(upper_32_bits(DOORBELL_ADDR)); + writel_relaxed(lower_32_bits(DOORBELL_ADDR), + port->base + pcie->hw->port_msiaddr); + if (pcie->hw->port_msiaddr_hi) + writel_relaxed(0, port->base + pcie->hw->port_msiaddr_hi); /* Enable MSIs, shared between all ports */ - writel_relaxed(0, port->base + PORT_MSIBASE); - writel_relaxed((ilog2(port->pcie->nvecs) << PORT_MSICFG_L2MSINUM_SHIFT) | - PORT_MSICFG_EN, port->base + PORT_MSICFG); + if (pcie->hw->port_msimap) { + int i; + + for (i = 0; i < pcie->nvecs; i++) { + writel_relaxed(FIELD_PREP(PORT_MSIMAP_TARGET, i) | + PORT_MSIMAP_ENABLE, + port->base + pcie->hw->port_msimap + 4 * i); + } + + writel_relaxed(PORT_MSICFG_EN, port->base + PORT_MSICFG); + } else { + writel_relaxed(0, port->base + PORT_MSIBASE); + writel_relaxed((ilog2(pcie->nvecs) << PORT_MSICFG_L2MSINUM_SHIFT) | + PORT_MSICFG_EN, port->base + PORT_MSICFG); + } return 0; } @@ -473,7 +543,9 @@ static int apple_pcie_setup_refclk(struct apple_pcie *pcie, u32 stat; int res; - rmw_set(PHY_LANE_CTL_CFGACC, port->phy + PHY_LANE_CTL); + if (pcie->hw->phy_lane_ctl) + rmw_set(PHY_LANE_CTL_CFGACC, port->phy + pcie->hw->phy_lane_ctl); + rmw_set(PHY_LANE_CFG_REFCLK0REQ, port->phy + PHY_LANE_CFG); res = readl_relaxed_poll_timeout(port->phy + PHY_LANE_CFG, @@ -490,10 +562,13 @@ static int apple_pcie_setup_refclk(struct apple_pcie *pcie, if (res < 0) return res; - rmw_clear(PHY_LANE_CTL_CFGACC, port->phy + PHY_LANE_CTL); + if (pcie->hw->phy_lane_ctl) + rmw_clear(PHY_LANE_CTL_CFGACC, port->phy + pcie->hw->phy_lane_ctl); rmw_set(PHY_LANE_CFG_REFCLKEN, port->phy + PHY_LANE_CFG); - rmw_set(PORT_REFCLK_EN, port->base + PORT_REFCLK); + + if (pcie->hw->port_refclk) + rmw_set(PORT_REFCLK_EN, port->base + pcie->hw->port_refclk); return 0; } @@ -501,9 +576,9 @@ static int apple_pcie_setup_refclk(struct apple_pcie *pcie, static u32 apple_pcie_rid2sid_write(struct apple_pcie_port *port, int idx, u32 val) { - writel_relaxed(val, port->base + PORT_RID2SID(idx)); + writel_relaxed(val, port->base + port->pcie->hw->port_rid2sid + 4 * idx); /* Read back to ensure completion of the write */ - return readl_relaxed(port->base + PORT_RID2SID(idx)); + return readl_relaxed(port->base + port->pcie->hw->port_rid2sid + 4 * idx); } static int apple_pcie_probe_port(struct device_node *np) @@ -602,7 +677,7 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, usleep_range(100, 200); /* Deassert PERST# */ - rmw_set(PORT_PERST_OFF, port->base + PORT_PERST); + rmw_set(PORT_PERST_OFF, port->base + pcie->hw->port_perst); gpiod_set_value_cansleep(reset, 0); /* Wait for 100ms after PERST# deassertion (PCIe r5.0, 6.6.1) */ @@ -615,15 +690,12 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, return ret; } - rmw_clear(PORT_REFCLK_CGDIS, port->base + PORT_REFCLK); - rmw_clear(PORT_APPCLK_CGDIS, port->base + PORT_APPCLK); - ret = apple_pcie_port_setup_irq(port); if (ret) return ret; /* Reset all RID/SID mappings, and check for RAZ/WI registers */ - for (i = 0; i < MAX_RID2SID; i++) { + for (i = 0; i < pcie->hw->max_rid2sid; i++) { if (apple_pcie_rid2sid_write(port, i, 0xbad1d) != 0xbad1d) break; apple_pcie_rid2sid_write(port, i, 0); @@ -647,6 +719,12 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, if (!wait_for_completion_timeout(&pcie->event, HZ / 10)) dev_warn(pcie->dev, "%pOF link didn't come up\n", np); + if (pcie->hw->port_refclk) + rmw_clear(PORT_REFCLK_CGDIS, port->base + PORT_REFCLK); + else + rmw_set(PHY_LANE_CFG_REFCLKCGEN, port->phy + PHY_LANE_CFG); + rmw_clear(PORT_APPCLK_CGDIS, port->base + PORT_APPCLK); + return 0; } @@ -763,7 +841,7 @@ static void apple_pcie_release_device(struct apple_pcie_port *port, for_each_set_bit(idx, port->sid_map, port->sid_map_sz) { u32 val; - val = readl_relaxed(port->base + PORT_RID2SID(idx)); + val = readl_relaxed(port->base + port->pcie->hw->port_rid2sid + 4 * idx); if ((val & 0xffff) == rid) { apple_pcie_rid2sid_write(port, idx, 0); bitmap_release_region(port->sid_map, idx, 0); @@ -820,13 +898,19 @@ static int apple_pcie_init(struct pci_config_window *cfg) struct platform_device *platform = to_platform_device(dev); struct device_node *of_port; struct apple_pcie *pcie; + const struct of_device_id *match; int ret; + match = of_match_device(apple_pcie_of_match_hw, dev); + if (!match) + return -ENODEV; + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); if (!pcie) return -ENOMEM; pcie->dev = dev; + pcie->hw = match->data; mutex_init(&pcie->lock); @@ -889,6 +973,7 @@ static const struct pci_ecam_ops apple_pcie_cfg_ecam_ops = { }; static const struct of_device_id apple_pcie_of_match[] = { + { .compatible = "apple,t6020-pcie", .data = &apple_pcie_cfg_ecam_ops }, { .compatible = "apple,pcie", .data = &apple_pcie_cfg_ecam_ops }, { } }; From ff9279a75193bb9eec82055b6c087344da2359bf Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 20 Apr 2023 23:24:59 +0200 Subject: [PATCH 0414/1020] PCI: apple: Skip controller port setup for online links U-boot gained recently support for PCIe controller on Apple silicon devices. It is currently unkown how to reset / retrain already brought up ports. Redoing the controller level setup breaks the links. Check the link status before performing controller level port/link setup. Link: https://lore.kernel.org/u-boot/20230121192800.82428-1-kettenis@openbsd.org/ Signed-off-by: Janne Grunau --- drivers/pci/controller/pcie-apple.c | 88 ++++++++++++++++++----------- 1 file changed, 55 insertions(+), 33 deletions(-) diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index a816acee184b28..09f0a0e9f476ca 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -605,15 +605,13 @@ static int apple_pcie_probe_port(struct device_node *np) return 0; } -static int apple_pcie_setup_port(struct apple_pcie *pcie, +static int apple_pcie_setup_link(struct apple_pcie *pcie, + struct apple_pcie_port *port, struct device_node *np) { - struct platform_device *platform = to_platform_device(pcie->dev); - struct apple_pcie_port *port; - struct gpio_desc *reset, *pwren = NULL; - u32 stat, idx; - int ret, i; - char name[16]; + struct gpio_desc *reset, *pwren; + u32 stat; + int ret; reset = devm_fwnode_gpiod_get(pcie->dev, of_fwnode_handle(np), "reset", GPIOD_OUT_LOW, "PERST#"); @@ -629,32 +627,6 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, return PTR_ERR(pwren); } - port = devm_kzalloc(pcie->dev, sizeof(*port), GFP_KERNEL); - if (!port) - return -ENOMEM; - - ret = of_property_read_u32_index(np, "reg", 0, &idx); - if (ret) - return ret; - - /* Use the first reg entry to work out the port index */ - port->idx = idx >> 11; - port->pcie = pcie; - port->np = np; - - snprintf(name, sizeof(name), "port%d", port->idx); - port->base = devm_platform_ioremap_resource_byname(platform, name); - if (IS_ERR(port->base)) - port->base = devm_platform_ioremap_resource(platform, port->idx + 2); - if (IS_ERR(port->base)) { - return PTR_ERR(port->base); - } - - snprintf(name, sizeof(name), "phy%d", port->idx); - port->phy = devm_platform_ioremap_resource_byname(platform, name); - if (IS_ERR(port->phy)) - port->phy = pcie->base + CORE_PHY_DEFAULT_BASE(port->idx); - rmw_set(PORT_APPCLK_EN, port->base + PORT_APPCLK); /* Assert PERST# before setting up the clock */ @@ -690,6 +662,52 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, return ret; } + return 0; +} + +static int apple_pcie_setup_port(struct apple_pcie *pcie, + struct device_node *np) +{ + struct platform_device *platform = to_platform_device(pcie->dev); + struct apple_pcie_port *port; + u32 link_stat, idx; + int ret, i; + char name[16]; + + port = devm_kzalloc(pcie->dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + ret = of_property_read_u32_index(np, "reg", 0, &idx); + if (ret) + return ret; + + /* Use the first reg entry to work out the port index */ + port->idx = idx >> 11; + port->pcie = pcie; + port->np = np; + + snprintf(name, sizeof(name), "port%d", port->idx); + port->base = devm_platform_ioremap_resource_byname(platform, name); + if (IS_ERR(port->base)) + port->base = devm_platform_ioremap_resource(platform, port->idx + 2); + if (IS_ERR(port->base)) { + return PTR_ERR(port->base); + } + + snprintf(name, sizeof(name), "phy%d", port->idx); + port->phy = devm_platform_ioremap_resource_byname(platform, name); + if (IS_ERR(port->phy)) + port->phy = pcie->base + CORE_PHY_DEFAULT_BASE(port->idx); + + /* link might be already brought up by u-boot, skip setup then */ + link_stat = readl_relaxed(port->base + PORT_LINKSTS); + if (!(link_stat & PORT_LINKSTS_UP)) { + ret = apple_pcie_setup_link(pcie, port, np); + if (ret) + return ret; + } + ret = apple_pcie_port_setup_irq(port); if (ret) return ret; @@ -714,6 +732,10 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, ret = apple_pcie_port_register_irqs(port); WARN_ON(ret); + if (link_stat & PORT_LINKSTS_UP) + return 0; + + /* start link training */ writel_relaxed(PORT_LTSSMCTL_START, port->base + PORT_LTSSMCTL); if (!wait_for_completion_timeout(&pcie->event, HZ / 10)) From e0b63a2419ed4901b1184a1913236a4789291f48 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 18 May 2023 16:12:39 +0900 Subject: [PATCH 0415/1020] PCI: apple: Make link up timeout configurable, default to 500ms We're seeing link up timeouts and it looks like devices are just too slow. Let's just increase this. Signed-off-by: Hector Martin --- drivers/pci/controller/pcie-apple.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index 09f0a0e9f476ca..690a4002159f7f 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -31,6 +31,10 @@ #include #include +static int link_up_timeout = 500; +module_param(link_up_timeout, int, 0644); +MODULE_PARM_DESC(link_up_timeout, "PCIe link training timeout in milliseconds"); + #define CORE_RC_PHYIF_CTL 0x00024 #define CORE_RC_PHYIF_CTL_RUN BIT(0) #define CORE_RC_PHYIF_STAT 0x00028 @@ -738,7 +742,7 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, /* start link training */ writel_relaxed(PORT_LTSSMCTL_START, port->base + PORT_LTSSMCTL); - if (!wait_for_completion_timeout(&pcie->event, HZ / 10)) + if (!wait_for_completion_timeout(&pcie->event, link_up_timeout * HZ / 1000)) dev_warn(pcie->dev, "%pOF link didn't come up\n", np); if (pcie->hw->port_refclk) From 83e14cfd92c2ea7a59a8474af3ea9e217c3ecf15 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 18 May 2023 16:18:29 +0900 Subject: [PATCH 0416/1020] PCI: apple: Reorder & improve link-up logic Always re-check LINKSTS right before deciding whether to start the link training and wait for it, just in case the link happened to come up while we were setting up IRQs. Also, always do the clock-gate disable even if the link is already up. Signed-off-by: Hector Martin --- drivers/pci/controller/pcie-apple.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index 690a4002159f7f..feaf874d457ad0 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -736,14 +736,14 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, ret = apple_pcie_port_register_irqs(port); WARN_ON(ret); - if (link_stat & PORT_LINKSTS_UP) - return 0; - - /* start link training */ - writel_relaxed(PORT_LTSSMCTL_START, port->base + PORT_LTSSMCTL); + link_stat = readl_relaxed(port->base + PORT_LINKSTS); + if (!(link_stat & PORT_LINKSTS_UP)) { + /* start link training */ + writel_relaxed(PORT_LTSSMCTL_START, port->base + PORT_LTSSMCTL); - if (!wait_for_completion_timeout(&pcie->event, link_up_timeout * HZ / 1000)) - dev_warn(pcie->dev, "%pOF link didn't come up\n", np); + if (!wait_for_completion_timeout(&pcie->event, link_up_timeout * HZ / 1000)) + dev_warn(pcie->dev, "%pOF link didn't come up\n", np); + } if (pcie->hw->port_refclk) rmw_clear(PORT_REFCLK_CGDIS, port->base + PORT_REFCLK); From 37caf08704a070f3dfdd3cd5cbab9b0f0fdc3008 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 18 May 2023 17:03:32 +0900 Subject: [PATCH 0417/1020] PCI: apple: Log the time it takes for links to come up Signed-off-by: Hector Martin --- drivers/pci/controller/pcie-apple.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index feaf874d457ad0..659a5dac7daf35 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -738,11 +738,18 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, link_stat = readl_relaxed(port->base + PORT_LINKSTS); if (!(link_stat & PORT_LINKSTS_UP)) { + unsigned long timeout, left; /* start link training */ writel_relaxed(PORT_LTSSMCTL_START, port->base + PORT_LTSSMCTL); - if (!wait_for_completion_timeout(&pcie->event, link_up_timeout * HZ / 1000)) + timeout = link_up_timeout * HZ / 1000; + left = wait_for_completion_timeout(&pcie->event, timeout); + if (!left) dev_warn(pcie->dev, "%pOF link didn't come up\n", np); + else + dev_info(pcie->dev, "%pOF link up after %ldms\n", np, + (timeout - left) * 1000 / HZ); + } if (pcie->hw->port_refclk) From ae01779aaf4927c1030aa9d895b3f7eb2b8f0e91 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 6 Jun 2023 15:31:01 +0900 Subject: [PATCH 0418/1020] PCI: apple: Do not power down devices on port setup If a device is already powered, leave it powered. Otherwise port setup done by u-boot breaks. Signed-off-by: Hector Martin --- drivers/pci/controller/pcie-apple.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index 659a5dac7daf35..13a95a73546b13 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -598,7 +598,7 @@ static int apple_pcie_probe_port(struct device_node *np) gpiod_put(gd); gd = fwnode_gpiod_get_index(of_fwnode_handle(np), "pwren", 0, - GPIOD_OUT_LOW, "PWREN"); + GPIOD_ASIS, "PWREN"); if (IS_ERR(gd)) { if (PTR_ERR(gd) != -ENOENT) return PTR_ERR(gd); @@ -623,7 +623,7 @@ static int apple_pcie_setup_link(struct apple_pcie *pcie, return PTR_ERR(reset); pwren = devm_fwnode_gpiod_get(pcie->dev, of_fwnode_handle(np), "pwren", - GPIOD_OUT_LOW, "PWREN"); + GPIOD_ASIS, "PWREN"); if (IS_ERR(pwren)) { if (PTR_ERR(pwren) == -ENOENT) pwren = NULL; From 450cfd8757f8a4e88b2243090b46624089f6f5ac Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 13 Apr 2024 12:23:26 +0200 Subject: [PATCH 0419/1020] fixup! PCI: apple: Move port PHY registers to their own reg items open code devm_platform_ioremap_resource_byname() to avoid error messages on older platforms with missing resources in the pcie node. Avoids "pcie-apple 590000000.pcie: invalid resource (null)" on probe. Signed-off-by: Janne Grunau --- drivers/pci/controller/pcie-apple.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index 13a95a73546b13..00ebb17a22722e 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -674,6 +674,7 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, { struct platform_device *platform = to_platform_device(pcie->dev); struct apple_pcie_port *port; + struct resource *res; u32 link_stat, idx; int ret, i; char name[16]; @@ -692,16 +693,21 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, port->np = np; snprintf(name, sizeof(name), "port%d", port->idx); - port->base = devm_platform_ioremap_resource_byname(platform, name); - if (IS_ERR(port->base)) + res = platform_get_resource_byname(platform, IORESOURCE_MEM, name); + if (res) { + port->base = devm_ioremap_resource(&platform->dev, res); + } else { port->base = devm_platform_ioremap_resource(platform, port->idx + 2); + } if (IS_ERR(port->base)) { return PTR_ERR(port->base); } snprintf(name, sizeof(name), "phy%d", port->idx); - port->phy = devm_platform_ioremap_resource_byname(platform, name); - if (IS_ERR(port->phy)) + res = platform_get_resource_byname(platform, IORESOURCE_MEM, name); + if (res) + port->phy = devm_ioremap_resource(&platform->dev, res); + else port->phy = pcie->base + CORE_PHY_DEFAULT_BASE(port->idx); /* link might be already brought up by u-boot, skip setup then */ From 624418fdda3bb6add0999d9c2fbb6028d99bb25a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 13 Oct 2023 18:49:35 +0200 Subject: [PATCH 0420/1020] dt-bindings: dma: apple,sio: Add schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Describe the SIO coprocessor which serves as pretend DMA controller on recent Apple platforms. Reviewed-by: Rob Herring Signed-off-by: Martin Povišer --- .../devicetree/bindings/dma/apple,sio.yaml | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/apple,sio.yaml diff --git a/Documentation/devicetree/bindings/dma/apple,sio.yaml b/Documentation/devicetree/bindings/dma/apple,sio.yaml new file mode 100644 index 00000000000000..0e3780ad9dd79a --- /dev/null +++ b/Documentation/devicetree/bindings/dma/apple,sio.yaml @@ -0,0 +1,111 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dma/apple,sio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple SIO Coprocessor + +description: + SIO is a coprocessor on Apple M1 and later chips (and maybe also on earlier + chips). Its role is to offload SPI, UART and DisplayPort audio transfers, + being a pretend DMA controller. + +maintainers: + - Martin Povišer + +allOf: + - $ref: dma-controller.yaml# + +properties: + compatible: + items: + - enum: + - apple,t6000-sio + - apple,t8103-sio + - const: apple,sio + + reg: + maxItems: 1 + + '#dma-cells': + const: 1 + description: + DMA clients specify a single cell that corresponds to the RTKit endpoint + number used for arranging the transfers in question + + dma-channels: + maximum: 128 + + mboxes: + maxItems: 1 + + iommus: + maxItems: 1 + + power-domains: + maxItems: 1 + + memory-region: + minItems: 2 + maxItems: 8 + description: + A number of references to reserved memory regions among which are the DATA/TEXT + sections of coprocessor executable firmware and also auxiliary firmware data + describing the available DMA-enabled peripherals + + apple,sio-firmware-params: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: | + Parameters in the form of opaque key/value pairs that are to be sent to the SIO + coprocesssor once it boots. These parameters can point into the reserved memory + regions (in device address space). + + Note that unlike Apple's firmware, we treat the parameters, and the data they + refer to, as opaque. Apple embed short data blobs into their SIO devicetree node + that describe the DMA-enabled peripherals (presumably with defined semantics). + Their driver processes those blobs and sets up data structure in mapped device + memory, then references this memory in the parameters sent to the SIO. At the + level of description we are opting for in this binding, we assume the job of + constructing those data structures has been done in advance, leaving behind an + opaque list of key/value parameter pairs to be sent by a prospective driver. + + This approach is chosen for two reasons: + + - It means we don't need to try to understand the semantics of Apple's blobs + as long as we know the transformation we need to do from Apple's devicetree + data to SIO data (which can be shoved away into a loader). It also means the + semantics of Apple's blobs (or of something to replace them) need not be part + of the binding and be kept up with Apple's firmware changes in the future. + + - It leaves less work for the driver attaching on this binding. Instead the work + is done upfront in the loader which can be better suited for keeping up with + Apple's firmware changes. + +required: + - compatible + - reg + - '#dma-cells' + - dma-channels + - mboxes + - iommus + - power-domains + +additionalProperties: false + +examples: + - | + sio: dma-controller@36400000 { + compatible = "apple,t8103-sio", "apple,sio"; + reg = <0x36400000 0x8000>; + dma-channels = <128>; + #dma-cells = <1>; + mboxes = <&sio_mbox>; + iommus = <&sio_dart 0>; + power-domains = <&ps_sio_cpu>; + memory-region = <&sio_text>, <&sio_data>, + <&sio_auxdata1>, <&sio_auxdata2>; /* Filled by loader */ + apple,sio-firmware-params = <0xb 0x10>, <0xc 0x1b80>, <0xf 0x14>, + <0x10 0x1e000>, <0x30d 0x34>, <0x30e 0x4000>, + <0x1a 0x38>, <0x1b 0x50>; /* Filled by loader */ + }; From 478d5cde648595c3e8e5f2da9d49506e45bdb156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 28 Nov 2022 09:55:07 +0100 Subject: [PATCH 0421/1020] dmaengine: apple-sio: Add Apple SIO driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a dmaengine driver for the Apple SIO coprocessor found on Apple SoCs where it provides DMA services. Have the driver support cyclic transactions so that ALSA drivers can rely on it in audio output to HDMI and DisplayPort. Signed-off-by: Martin Povišer --- MAINTAINERS | 2 + drivers/dma/Kconfig | 11 + drivers/dma/Makefile | 1 + drivers/dma/apple-sio.c | 913 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 927 insertions(+) create mode 100644 drivers/dma/apple-sio.c diff --git a/MAINTAINERS b/MAINTAINERS index 28e20975c26f5c..bb08eae63b16b9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1932,8 +1932,10 @@ M: Martin Povišer L: asahi@lists.linux.dev L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Maintained +F: Documentation/devicetree/bindings/dma/apple,sio.yaml F: Documentation/devicetree/bindings/sound/adi,ssm3515.yaml F: Documentation/devicetree/bindings/sound/apple,* +F: drivers/dma/apple-sio.c F: sound/soc/apple/* F: sound/soc/codecs/cs42l83-i2c.c F: sound/soc/codecs/ssm3515.c diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 002a5ec806207c..ec711e891963f4 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -89,10 +89,21 @@ config APPLE_ADMAC tristate "Apple ADMAC support" depends on ARCH_APPLE || COMPILE_TEST select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS default ARCH_APPLE help Enable support for Audio DMA Controller found on Apple Silicon SoCs. +config APPLE_SIO + tristate "Apple SIO support" + depends on ARCH_APPLE || COMPILE_TEST + depends on APPLE_RTKIT + select DMA_ENGINE + default m if ARCH_APPLE + help + Enable support for the SIO coprocessor found on Apple Silicon SoCs + where it provides DMA services. + config AT_HDMAC tristate "Atmel AHB DMA support" depends on ARCH_AT91 diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index dfd40d14e4089d..17fa2330aa65a4 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/ obj-$(CONFIG_AMD_PTDMA) += ptdma/ obj-$(CONFIG_APPLE_ADMAC) += apple-admac.o +obj-$(CONFIG_APPLE_SIO) += apple-sio.o obj-$(CONFIG_AT_HDMAC) += at_hdmac.o obj-$(CONFIG_AT_XDMAC) += at_xdmac.o obj-$(CONFIG_AXI_DMAC) += dma-axi-dmac.o diff --git a/drivers/dma/apple-sio.c b/drivers/dma/apple-sio.c new file mode 100644 index 00000000000000..4dc119f1d30211 --- /dev/null +++ b/drivers/dma/apple-sio.c @@ -0,0 +1,913 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Driver for SIO coprocessor on t8103 (M1) and other Apple SoCs + * + * Copyright (C) The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dmaengine.h" +#include "virt-dma.h" + +#define NCHANNELS_MAX 0x80 + +#define REG_CPU_CONTROL 0x44 +#define CPU_CONTROL_RUN BIT(4) + +#define SIOMSG_DATA GENMASK(63, 32) +#define SIOMSG_TYPE GENMASK(23, 16) +#define SIOMSG_PARAM GENMASK(31, 24) +#define SIOMSG_TAG GENMASK(13, 8) +#define SIOMSG_EP GENMASK(7, 0) + +#define EP_SIO 0x20 + +#define MSG_START 0x2 +#define MSG_SETUP 0x3 +#define MSG_CONFIGURE 0x5 +#define MSG_ISSUE 0x6 +#define MSG_TERMINATE 0x8 +#define MSG_ACK 0x65 +#define MSG_NACK 0x66 +#define MSG_STARTED 0x67 +#define MSG_REPORT 0x68 + +#define SIO_CALL_TIMEOUT_MS 100 +#define SIO_SHMEM_SIZE 0x1000 +#define SIO_NO_DESC_SLOTS 64 + +/* + * There are two kinds of 'transaction descriptors' in play here. + * + * There's the struct sio_tx, and the struct dma_async_tx_descriptor embedded + * inside, which jointly represent a transaction to the dmaengine subsystem. + * At this time we only support those transactions to be cyclic. + * + * Then there are the coprocessor descriptors, which is what the coprocessor + * knows and understands. These don't seem to have a cyclic regime, so we can't + * map the dmaengine transaction on an exact coprocessor counterpart. Instead + * we continually queue up many coprocessor descriptors to implement a cyclic + * transaction. + * + * The number below is the maximum of how far ahead (how many) coprocessor + * descriptors we should be queuing up, per channel, for a cyclic transaction. + * Basically it's a made-up number. + */ +#define SIO_MAX_NINFLIGHT 4 + +struct sio_coproc_desc { + u32 pad1; + u32 flag; + u64 unk; + u64 iova; + u64 size; + u64 pad2; + u64 pad3; +} __packed; +static_assert(sizeof(struct sio_coproc_desc) == 48); + +struct sio_shmem_chan_config { + u32 datashape; + u32 timeout; + u32 fifo; + u32 threshold; + u32 limit; +} __packed; + +struct sio_data; +struct sio_tx; + +struct sio_chan { + unsigned int no; + struct sio_data *host; + struct virt_dma_chan vc; + struct work_struct terminate_wq; + + bool configured; + struct sio_shmem_chan_config cfg; + + struct sio_tx *current_tx; +}; + +#define SIO_NTAGS 16 + +typedef void (*sio_ack_callback)(struct sio_chan *, void *, bool); + +struct sio_data { + void __iomem *base; + struct dma_device dma; + struct device *dev; + struct apple_rtkit *rtk; + void *shmem; + struct sio_coproc_desc *shmem_desc_base; + unsigned long *desc_allocated; + + struct sio_tagdata { + DECLARE_BITMAP(allocated, SIO_NTAGS); + int last_tag; + + struct completion completions[SIO_NTAGS]; + bool atomic[SIO_NTAGS]; + bool acked[SIO_NTAGS]; + + sio_ack_callback ack_callback[SIO_NTAGS]; + void *cookie[SIO_NTAGS]; + } tags; + + int nchannels; + struct sio_chan channels[]; +}; + +struct sio_tx { + struct virt_dma_desc vd; + struct completion done; + + bool terminated; + size_t period_len; + int nperiods; + int ninflight; + int next; + + struct sio_coproc_desc *siodesc[]; +}; + +static int sio_send_siomsg(struct sio_data *sio, u64 msg); +static int sio_send_siomsg_atomic(struct sio_data *sio, u64 msg, + sio_ack_callback ack_callback, + void *cookie); +static int sio_call(struct sio_data *sio, u64 msg); + +static struct sio_chan *to_sio_chan(struct dma_chan *chan) +{ + return container_of(chan, struct sio_chan, vc.chan); +} + +static struct sio_tx *to_sio_tx(struct dma_async_tx_descriptor *tx) +{ + return container_of(tx, struct sio_tx, vd.tx); +} + +static int sio_alloc_tag(struct sio_data *sio) +{ + struct sio_tagdata *tags = &sio->tags; + int tag, i; + + /* + * Because tag number 0 is special, the usable tag range + * is 1...(SIO_NTAGS - 1). So, to pick the next usable tag, + * we do modulo (SIO_NTAGS - 1) *then* plus one. + */ + +#define SIO_USABLE_TAGS (SIO_NTAGS - 1) + tag = (READ_ONCE(tags->last_tag) % SIO_USABLE_TAGS) + 1; + + for (i = 0; i < SIO_USABLE_TAGS; i++) { + if (!test_and_set_bit(tag, tags->allocated)) + break; + + tag = (tag % SIO_USABLE_TAGS) + 1; + } + + WRITE_ONCE(tags->last_tag, tag); + + if (i < SIO_USABLE_TAGS) + return tag; + else + return -EBUSY; +#undef SIO_USABLE_TAGS +} + +static void sio_free_tag(struct sio_data *sio, int tag) +{ + struct sio_tagdata *tags = &sio->tags; + + if (WARN_ON(tag >= SIO_NTAGS)) + return; + + tags->atomic[tag] = false; + tags->ack_callback[tag] = NULL; + + WARN_ON(!test_and_clear_bit(tag, tags->allocated)); +} + +static void sio_set_tag_atomic(struct sio_data *sio, int tag, + sio_ack_callback ack_callback, + void *cookie) +{ + struct sio_tagdata *tags = &sio->tags; + + tags->atomic[tag] = true; + tags->ack_callback[tag] = ack_callback; + tags->cookie[tag] = cookie; +} + +static struct sio_coproc_desc *sio_alloc_desc(struct sio_data *sio) +{ + int i; + + for (i = 0; i < SIO_NO_DESC_SLOTS; i++) + if (!test_and_set_bit(i, sio->desc_allocated)) + return sio->shmem_desc_base + i; + + return NULL; +} + +static void sio_free_desc(struct sio_data *sio, struct sio_coproc_desc *desc) +{ + clear_bit(desc - sio->shmem_desc_base, sio->desc_allocated); +} + +static int sio_coproc_desc_slot(struct sio_data *sio, struct sio_coproc_desc *desc) +{ + return (desc - sio->shmem_desc_base) * 4; +} + +static enum dma_transfer_direction sio_chan_direction(int channo) +{ + /* Channel directions are fixed based on channel number */ + return (channo & 1) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; +} + +static void sio_tx_free(struct virt_dma_desc *vd) +{ + struct sio_data *sio = to_sio_chan(vd->tx.chan)->host; + struct sio_tx *siotx = to_sio_tx(&vd->tx); + int i; + + for (i = 0; i < siotx->nperiods; i++) + if (siotx->siodesc[i]) + sio_free_desc(sio, siotx->siodesc[i]); + kfree(siotx); +} + +static struct dma_async_tx_descriptor *sio_prep_dma_cyclic( + struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction direction, + unsigned long flags) +{ + struct sio_chan *siochan = to_sio_chan(chan); + struct sio_tx *siotx = NULL; + int i, nperiods = buf_len / period_len; + + if (direction != sio_chan_direction(siochan->no)) + return NULL; + + siotx = kzalloc(struct_size(siotx, siodesc, nperiods), GFP_NOWAIT); + if (!siotx) + return NULL; + + init_completion(&siotx->done); + siotx->period_len = period_len; + siotx->nperiods = nperiods; + + for (i = 0; i < nperiods; i++) { + struct sio_coproc_desc *d; + + siotx->siodesc[i] = d = sio_alloc_desc(siochan->host); + if (!d) { + sio_tx_free(&siotx->vd); + return NULL; + } + + d->flag = 1; /* not sure what's up with this */ + d->iova = buf_addr + period_len * i; + d->size = period_len; + } + dma_wmb(); + + return vchan_tx_prep(&siochan->vc, &siotx->vd, flags); +} + +static enum dma_status sio_tx_status(struct dma_chan *chan, dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + struct sio_chan *siochan = to_sio_chan(chan); + struct virt_dma_desc *vd; + struct sio_tx *siotx; + enum dma_status ret; + unsigned long flags; + int periods_residue; + size_t residue; + + ret = dma_cookie_status(chan, cookie, txstate); + if (ret == DMA_COMPLETE || !txstate) + return ret; + + spin_lock_irqsave(&siochan->vc.lock, flags); + siotx = siochan->current_tx; + + if (siotx && siotx->vd.tx.cookie == cookie) { + ret = DMA_IN_PROGRESS; + periods_residue = siotx->next - siotx->ninflight; + while (periods_residue < 0) + periods_residue += siotx->nperiods; + residue = (siotx->nperiods - periods_residue) * siotx->period_len; + } else { + ret = DMA_IN_PROGRESS; + residue = 0; + vd = vchan_find_desc(&siochan->vc, cookie); + if (vd) { + siotx = to_sio_tx(&vd->tx); + residue = siotx->period_len * siotx->nperiods; + } + } + spin_unlock_irqrestore(&siochan->vc.lock, flags); + dma_set_residue(txstate, residue); + + return ret; +} + +static bool sio_fill_in_locked(struct sio_chan *siochan); + +static void sio_handle_issue_ack(struct sio_chan *siochan, void *cookie, bool ok) +{ + dma_cookie_t tx_cookie = (unsigned long) cookie; + unsigned long flags; + struct sio_tx *tx; + + if (!ok) { + dev_err(siochan->host->dev, "nacked issue on chan %d\n", siochan->no); + return; + } + + spin_lock_irqsave(&siochan->vc.lock, flags); + if (!siochan->current_tx || tx_cookie != siochan->current_tx->vd.tx.cookie || + siochan->current_tx->terminated) + goto out; + + tx = siochan->current_tx; + tx->next = (tx->next + 1) % tx->nperiods; + tx->ninflight++; + sio_fill_in_locked(siochan); + +out: + spin_unlock_irqrestore(&siochan->vc.lock, flags); +} + +static bool sio_fill_in_locked(struct sio_chan *siochan) +{ + struct sio_data *sio = siochan->host; + struct sio_tx *tx = siochan->current_tx; + struct sio_coproc_desc *d = tx->siodesc[tx->next]; + int ret; + + if (tx->ninflight >= SIO_MAX_NINFLIGHT || tx->terminated) + return false; + + static_assert(sizeof(dma_cookie_t) <= sizeof(void *)); + ret = sio_send_siomsg_atomic(sio, FIELD_PREP(SIOMSG_EP, siochan->no) | + FIELD_PREP(SIOMSG_TYPE, MSG_ISSUE) | + FIELD_PREP(SIOMSG_DATA, sio_coproc_desc_slot(sio, d)), + sio_handle_issue_ack, (void *) (uintptr_t) tx->vd.tx.cookie); + if (ret < 0) + dev_err_ratelimited(sio->dev, "can't issue on chan %d ninflight %d: %d\n", + siochan->no, tx->ninflight, ret); + return true; +} + +static void sio_update_current_tx_locked(struct sio_chan *siochan) +{ + struct virt_dma_desc *vd = vchan_next_desc(&siochan->vc); + + if (vd && !siochan->current_tx) { + list_del(&vd->node); + siochan->current_tx = to_sio_tx(&vd->tx); + sio_fill_in_locked(siochan); + } +} + +static void sio_issue_pending(struct dma_chan *chan) +{ + struct sio_chan *siochan = to_sio_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&siochan->vc.lock, flags); + vchan_issue_pending(&siochan->vc); + sio_update_current_tx_locked(siochan); + spin_unlock_irqrestore(&siochan->vc.lock, flags); +} + +static int sio_terminate_all(struct dma_chan *chan) +{ + struct sio_chan *siochan = to_sio_chan(chan); + unsigned long flags; + LIST_HEAD(to_free); + + spin_lock_irqsave(&siochan->vc.lock, flags); + if (siochan->current_tx && !siochan->current_tx->terminated) { + dma_cookie_complete(&siochan->current_tx->vd.tx); + siochan->current_tx->terminated = true; + schedule_work(&siochan->terminate_wq); + } + vchan_get_all_descriptors(&siochan->vc, &to_free); + spin_unlock_irqrestore(&siochan->vc.lock, flags); + + vchan_dma_desc_free_list(&siochan->vc, &to_free); + + return 0; +} + +static void sio_terminate_work(struct work_struct *wq) +{ + struct sio_chan *siochan = container_of(wq, struct sio_chan, terminate_wq); + struct sio_tx *tx; + unsigned long flags; + int ret; + + spin_lock_irqsave(&siochan->vc.lock, flags); + tx = siochan->current_tx; + spin_unlock_irqrestore(&siochan->vc.lock, flags); + + if (WARN_ON(!tx)) + return; + + ret = sio_call(siochan->host, FIELD_PREP(SIOMSG_EP, siochan->no) | + FIELD_PREP(SIOMSG_TYPE, MSG_TERMINATE)); + if (ret < 0) + dev_err(siochan->host->dev, "terminate call on chan %d failed: %d\n", + siochan->no, ret); + + ret = wait_for_completion_timeout(&tx->done, msecs_to_jiffies(500)); + if (!ret) + dev_err(siochan->host->dev, "terminate descriptor wait timed out\n"); + + tasklet_kill(&siochan->vc.task); + + spin_lock_irqsave(&siochan->vc.lock, flags); + WARN_ON(siochan->current_tx != tx); + siochan->current_tx = NULL; + sio_update_current_tx_locked(siochan); + spin_unlock_irqrestore(&siochan->vc.lock, flags); + + sio_tx_free(&tx->vd); +} + +static void sio_synchronize(struct dma_chan *chan) +{ + struct sio_chan *siochan = to_sio_chan(chan); + + flush_work(&siochan->terminate_wq); +} + +static void sio_free_chan_resources(struct dma_chan *chan) +{ + sio_terminate_all(chan); + sio_synchronize(chan); + vchan_free_chan_resources(&to_sio_chan(chan)->vc); +} + +static struct dma_chan *sio_dma_of_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct sio_data *sio = (struct sio_data *) ofdma->of_dma_data; + unsigned int index = dma_spec->args[0]; + + if (dma_spec->args_count != 1 || index >= sio->nchannels) + return ERR_PTR(-EINVAL); + + return dma_get_slave_channel(&sio->channels[index].vc.chan); +} + +static void sio_rtk_crashed(void *cookie) +{ + struct sio_data *sio = cookie; + + dev_err(sio->dev, "SIO down (crashed)"); +} + +static void sio_process_report(struct sio_chan *siochan) +{ + unsigned long flags; + + spin_lock_irqsave(&siochan->vc.lock, flags); + if (siochan->current_tx) { + struct sio_tx *tx = siochan->current_tx; + + if (tx->ninflight) + tx->ninflight--; + vchan_cyclic_callback(&tx->vd); + if (!sio_fill_in_locked(siochan) && !tx->ninflight) + complete(&tx->done); + } + spin_unlock_irqrestore(&siochan->vc.lock, flags); +} + +static void sio_recv_msg(void *cookie, u8 ep, u64 msg) +{ + struct sio_data *sio = cookie; + struct sio_tagdata *tags = &sio->tags; + u32 data; + u8 param, type, tag, sioep; + + if (ep != EP_SIO) + goto unknown; + + data = FIELD_GET(SIOMSG_DATA, msg); + param = FIELD_GET(SIOMSG_PARAM, msg); + type = FIELD_GET(SIOMSG_TYPE, msg); + tag = FIELD_GET(SIOMSG_TAG, msg); + sioep = FIELD_GET(SIOMSG_EP, msg); + + switch (type) { + case MSG_STARTED: + dev_info(sio->dev, "SIO protocol v%u\n", data); + type = MSG_ACK; /* Pretend this is an ACK */ + fallthrough; + case MSG_ACK: + case MSG_NACK: + if (WARN_ON(tag >= SIO_NTAGS)) + break; + + if (tags->atomic[tag]) { + sio_ack_callback callback = tags->ack_callback[tag]; + + if (callback && !WARN_ON(sioep >= sio->nchannels)) + callback(&sio->channels[sioep], + tags->cookie[tag], type == MSG_ACK); + if (type == MSG_NACK) + dev_err(sio->dev, "got a NACK on channel %d\n", sioep); + sio_free_tag(sio, tag); + } else { + tags->acked[tag] = (type == MSG_ACK); + complete(&tags->completions[tag]); + } + break; + + case MSG_REPORT: + if (WARN_ON(sioep >= sio->nchannels)) + break; + + sio_process_report(&sio->channels[sioep]); + break; + + default: + goto unknown; + } + return; + +unknown: + dev_warn(sio->dev, "received unknown message: ep %x data %016llx\n", + ep, msg); +} + +static int _sio_send_siomsg(struct sio_data *sio, u64 msg, bool atomic, + sio_ack_callback ack_callback, void *cookie) +{ + int tag, ret; + + tag = sio_alloc_tag(sio); + if (tag < 0) + return tag; + + if (atomic) + sio_set_tag_atomic(sio, tag, ack_callback, cookie); + else + reinit_completion(&sio->tags.completions[tag]); + + msg &= ~SIOMSG_TAG; + msg |= FIELD_PREP(SIOMSG_TAG, tag); + ret = apple_rtkit_send_message(sio->rtk, EP_SIO, msg, NULL, + atomic); + if (ret < 0) { + sio_free_tag(sio, tag); + return ret; + } + + return tag; +} + +static int sio_send_siomsg(struct sio_data *sio, u64 msg) +{ + return _sio_send_siomsg(sio, msg, false, NULL, NULL); +} + +static int sio_send_siomsg_atomic(struct sio_data *sio, u64 msg, + sio_ack_callback ack_callback, + void *cookie) +{ + return _sio_send_siomsg(sio, msg, true, ack_callback, cookie); +} + +static int sio_call(struct sio_data *sio, u64 msg) +{ + int tag, ret; + + tag = sio_send_siomsg(sio, msg); + if (tag < 0) + return tag; + + ret = wait_for_completion_timeout(&sio->tags.completions[tag], + msecs_to_jiffies(SIO_CALL_TIMEOUT_MS)); + if (!ret) { + dev_warn(sio->dev, "call %8llx timed out\n", msg); + sio_free_tag(sio, tag); + return -ETIME; + } + + ret = sio->tags.acked[tag]; + sio_free_tag(sio, tag); + + return ret; +} + +static const struct apple_rtkit_ops sio_rtkit_ops = { + .crashed = sio_rtk_crashed, + .recv_message = sio_recv_msg, +}; + +static int sio_device_config(struct dma_chan *chan, + struct dma_slave_config *config) +{ + struct sio_chan *siochan = to_sio_chan(chan); + struct sio_data *sio = siochan->host; + bool is_tx = sio_chan_direction(siochan->no) == DMA_MEM_TO_DEV; + struct sio_shmem_chan_config *cfg_shmem = sio->shmem; + struct sio_shmem_chan_config cfg; + int ret; + + switch (is_tx ? config->dst_addr_width : config->src_addr_width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + cfg.datashape = 0; + break; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + cfg.datashape = 1; + break; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + cfg.datashape = 2; + break; + default: + return -EINVAL; + } + + cfg.timeout = 0; + cfg.fifo = 0x800; + cfg.limit = 0x800; + cfg.threshold = 0x800; + + /* + * Dmaengine prescribes we ought to apply the new configuration only + * to newly-queued descriptors. + * + * To comply with dmaengine's interface we take the lazy path here: + * we apply the configuration right away, we only allow the channel + * to be configured once, which means subsequent calls to `device_config` + * either return -EBUSY if the configuration differs, or they are + * a no-op if the configuration is the same as the starting one. + * + * This is the reasonable thing to do given that these sio channels + * are tied to fixed peripherals, and what's more given that the + * only planned consumer of this dmaengine driver in the kernel is + * diplayport audio support, where the DMA configuration is fixed, + * and no more than a single descriptor (a cyclic one) gets ever issued + * at the same time. + * + * The code complexity cost of tracking to which descriptor + * the configuration relates would be significant here, especially + * since we need to do a non-atomic operation to apply it (a call to + * the coprocessor) and dmaengine has its bunch of atomicity + * restrictions. And this complexity would be for naught since it + * doesn't even get exercised by the only planned consumer. + */ + if (siochan->configured && memcmp(&siochan->cfg, &cfg, sizeof(cfg))) + return -EBUSY; + + *cfg_shmem = cfg; + dma_wmb(); + + ret = sio_call(sio, FIELD_PREP(SIOMSG_TYPE, MSG_CONFIGURE) | + FIELD_PREP(SIOMSG_EP, siochan->no)); + + if (ret == 1) + ret = 0; + else if (ret == 0) + ret = -EINVAL; + + if (ret == 0) { + siochan->configured = true; + siochan->cfg = cfg; + } + + return ret; +} + +static int sio_alloc_shmem(struct sio_data *sio) +{ + dma_addr_t iova; + int err; + + sio->shmem = dma_alloc_coherent(sio->dev, SIO_SHMEM_SIZE, + &iova, GFP_KERNEL | __GFP_ZERO); + if (!sio->shmem) + return -ENOMEM; + + sio->shmem_desc_base = (struct sio_coproc_desc *) (sio->shmem + 56); + sio->desc_allocated = devm_kzalloc(sio->dev, SIO_NO_DESC_SLOTS / 32, + GFP_KERNEL); + if (!sio->desc_allocated) + return -ENOMEM; + + err = sio_call(sio, FIELD_PREP(SIOMSG_TYPE, MSG_SETUP) | + FIELD_PREP(SIOMSG_PARAM, 1) | + FIELD_PREP(SIOMSG_DATA, iova >> 12)); + if (err != 1) { + if (err == 0) + err = -EINVAL; + return err; + } + + err = sio_call(sio, FIELD_PREP(SIOMSG_TYPE, MSG_SETUP) | + FIELD_PREP(SIOMSG_PARAM, 2) | + FIELD_PREP(SIOMSG_DATA, SIO_SHMEM_SIZE)); + if (err != 1) { + if (err == 0) + err = -EINVAL; + return err; + } + + return 0; +} + +static int sio_send_dt_params(struct sio_data *sio) +{ + struct device_node *np = sio->dev->of_node; + const char *propname = "apple,sio-firmware-params"; + int nparams, err, i; + + nparams = of_property_count_u32_elems(np, propname); + if (nparams < 0) { + err = nparams; + goto badprop; + } + + for (i = 0; i < nparams / 2; i++) { + u32 key, val; + + err = of_property_read_u32_index(np, propname, 2 * i, &key); + if (err) + goto badprop; + err = of_property_read_u32_index(np, propname, 2 * i + 1, &val); + if (err) + goto badprop; + + err = sio_call(sio, FIELD_PREP(SIOMSG_TYPE, MSG_SETUP) | + FIELD_PREP(SIOMSG_PARAM, key & 0xff) | + FIELD_PREP(SIOMSG_EP, key >> 8) | + FIELD_PREP(SIOMSG_DATA, val)); + if (err < 1) { + if (err == 0) + err = -ENXIO; + return dev_err_probe(sio->dev, err, "sending SIO parameter %#x value %#x\n", + key, val); + } + } + + return 0; + +badprop: + return dev_err_probe(sio->dev, err, "failed to read '%s'\n", propname); +} + +static int sio_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct sio_data *sio; + struct dma_device *dma; + int nchannels; + int err, i; + + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(42)); + if (err) + return dev_err_probe(&pdev->dev, err, "Failed to set DMA mask\n"); + + err = of_property_read_u32(np, "dma-channels", &nchannels); + if (err || nchannels > NCHANNELS_MAX) + return dev_err_probe(&pdev->dev, -EINVAL, + "missing or invalid dma-channels property\n"); + + sio = devm_kzalloc(&pdev->dev, struct_size(sio, channels, nchannels), GFP_KERNEL); + if (!sio) + return -ENOMEM; + + platform_set_drvdata(pdev, sio); + sio->dev = &pdev->dev; + sio->nchannels = nchannels; + + sio->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(sio->base)) + return PTR_ERR(sio->base); + + sio->rtk = devm_apple_rtkit_init(&pdev->dev, sio, NULL, 0, &sio_rtkit_ops); + if (IS_ERR(sio->rtk)) + return dev_err_probe(&pdev->dev, PTR_ERR(sio->rtk), + "couldn't initialize rtkit\n"); + for (i = 1; i < SIO_NTAGS; i++) + init_completion(&sio->tags.completions[i]); + + dma = &sio->dma; + dma_cap_set(DMA_PRIVATE, dma->cap_mask); + dma_cap_set(DMA_CYCLIC, dma->cap_mask); + + dma->dev = &pdev->dev; + dma->device_free_chan_resources = sio_free_chan_resources; + dma->device_tx_status = sio_tx_status; + dma->device_issue_pending = sio_issue_pending; + dma->device_terminate_all = sio_terminate_all; + dma->device_synchronize = sio_synchronize; + dma->device_prep_dma_cyclic = sio_prep_dma_cyclic; + dma->device_config = sio_device_config; + + dma->directions = BIT(DMA_MEM_TO_DEV); + dma->residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT; + dma->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + + INIT_LIST_HEAD(&dma->channels); + for (i = 0; i < nchannels; i++) { + struct sio_chan *siochan = &sio->channels[i]; + + siochan->host = sio; + siochan->no = i; + siochan->vc.desc_free = sio_tx_free; + INIT_WORK(&siochan->terminate_wq, sio_terminate_work); + vchan_init(&siochan->vc, dma); + } + + writel(CPU_CONTROL_RUN, sio->base + REG_CPU_CONTROL); + + err = apple_rtkit_boot(sio->rtk); + if (err) + return dev_err_probe(&pdev->dev, err, "SIO did not boot\n"); + + err = apple_rtkit_start_ep(sio->rtk, EP_SIO); + if (err) + return dev_err_probe(&pdev->dev, err, "starting SIO endpoint\n"); + + err = sio_call(sio, FIELD_PREP(SIOMSG_TYPE, MSG_START)); + if (err < 1) { + if (err == 0) + err = -ENXIO; + return dev_err_probe(&pdev->dev, err, "starting SIO service\n"); + } + + err = sio_send_dt_params(sio); + if (err < 0) + return dev_err_probe(&pdev->dev, err, "failed to send boot-up parameters\n"); + + err = sio_alloc_shmem(sio); + if (err < 0) + return err; + + err = dma_async_device_register(&sio->dma); + if (err) + return dev_err_probe(&pdev->dev, err, "failed to register DMA device\n"); + + err = of_dma_controller_register(pdev->dev.of_node, sio_dma_of_xlate, sio); + if (err) { + dma_async_device_unregister(&sio->dma); + return dev_err_probe(&pdev->dev, err, "failed to register with OF\n"); + } + + return 0; +} + +static int sio_remove(struct platform_device *pdev) +{ + struct sio_data *sio = platform_get_drvdata(pdev); + + of_dma_controller_free(pdev->dev.of_node); + dma_async_device_unregister(&sio->dma); + return 0; +} + +static const struct of_device_id sio_of_match[] = { + { .compatible = "apple,sio", }, + { } +}; +MODULE_DEVICE_TABLE(of, sio_of_match); + +static struct platform_driver apple_sio_driver = { + .driver = { + .name = "apple-sio", + .of_match_table = sio_of_match, + }, + .probe = sio_probe, + .remove = sio_remove, +}; +module_platform_driver(apple_sio_driver); + +MODULE_AUTHOR("Martin Povišer "); +MODULE_DESCRIPTION("Driver for SIO coprocessor on Apple SoCs"); +MODULE_LICENSE("Dual MIT/GPL"); From 72daeb4eb4e6e8796a71febda82e01ce98bc8890 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Fri, 27 Aug 2021 11:19:51 -0400 Subject: [PATCH 0422/1020] WIP: drm/apple: Add DCP display driver Add a DRM/KMS driver for Apple system on chips using the DCP coprocessor, namely the Apple M1. The DCP was added in Apple A14; this driver does not apply to older iDevices. This driver targets the DCP firmware API shipped by macOS 12.1. Currently no incompatibilities with macOS 12.0.1 or 12.2.1 are known. Signed-off-by: Alyssa Rosenzweig Co-developed-by: Janne Grunau Signed-off-by: Janne Grunau --- MAINTAINERS | 7 + drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/apple/Kconfig | 11 + drivers/gpu/drm/apple/Makefile | 9 + drivers/gpu/drm/apple/apple_drv.c | 440 ++++++++ drivers/gpu/drm/apple/dcp.c | 1393 ++++++++++++++++++++++++++ drivers/gpu/drm/apple/dcp.h | 44 + drivers/gpu/drm/apple/dcpep.h | 406 ++++++++ drivers/gpu/drm/apple/dummy-piodma.c | 31 + drivers/gpu/drm/apple/parser.c | 451 +++++++++ drivers/gpu/drm/apple/parser.h | 32 + 12 files changed, 2827 insertions(+) create mode 100644 drivers/gpu/drm/apple/Kconfig create mode 100644 drivers/gpu/drm/apple/Makefile create mode 100644 drivers/gpu/drm/apple/apple_drv.c create mode 100644 drivers/gpu/drm/apple/dcp.c create mode 100644 drivers/gpu/drm/apple/dcp.h create mode 100644 drivers/gpu/drm/apple/dcpep.h create mode 100644 drivers/gpu/drm/apple/dummy-piodma.c create mode 100644 drivers/gpu/drm/apple/parser.c create mode 100644 drivers/gpu/drm/apple/parser.h diff --git a/MAINTAINERS b/MAINTAINERS index 28e20975c26f5c..c462767f26d06b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1486,6 +1486,13 @@ L: linux-input@vger.kernel.org S: Odd fixes F: drivers/input/mouse/bcm5974.c +APPLE DRM DISPLAY DRIVER +M: Alyssa Rosenzweig +L: dri-devel@lists.freedesktop.org +S: Maintained +T: git git://anongit.freedesktop.org/drm/drm-misc +F: drivers/gpu/drm/apple/ + APPLE PCIE CONTROLLER DRIVER M: Alyssa Rosenzweig M: Marc Zyngier diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 5a0c476361c300..50a19d96460294 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -385,6 +385,8 @@ source "drivers/gpu/drm/solomon/Kconfig" source "drivers/gpu/drm/sprd/Kconfig" +source "drivers/gpu/drm/apple/Kconfig" + source "drivers/gpu/drm/imagination/Kconfig" config DRM_HYPERV diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 104b42df2e956b..082f89d6e9deeb 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -179,6 +179,7 @@ obj-$(CONFIG_DRM_XEN) += xen/ obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/ obj-$(CONFIG_DRM_LIMA) += lima/ obj-$(CONFIG_DRM_PANFROST) += panfrost/ +obj-$(CONFIG_DRM_APPLE) += apple/ obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/ obj-$(CONFIG_DRM_MCDE) += mcde/ obj-$(CONFIG_DRM_TIDSS) += tidss/ diff --git a/drivers/gpu/drm/apple/Kconfig b/drivers/gpu/drm/apple/Kconfig new file mode 100644 index 00000000000000..53c7d4edfd017e --- /dev/null +++ b/drivers/gpu/drm/apple/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +config DRM_APPLE + tristate "DRM Support for Apple display controllers" + depends on DRM && OF && ARM64 + depends on ARCH_APPLE || COMPILE_TEST + select DRM_KMS_HELPER + select DRM_KMS_DMA_HELPER + select DRM_GEM_DMA_HELPER + select VIDEOMODE_HELPERS + help + Say Y if you have an Apple Silicon chipset. diff --git a/drivers/gpu/drm/apple/Makefile b/drivers/gpu/drm/apple/Makefile new file mode 100644 index 00000000000000..db7ef359987d3d --- /dev/null +++ b/drivers/gpu/drm/apple/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only + +appledrm-y := apple_drv.o +apple_dcp-y := dcp.o parser.o +apple_piodma-y := dummy-piodma.o + +obj-$(CONFIG_DRM_APPLE) += appledrm.o +obj-$(CONFIG_DRM_APPLE) += apple_dcp.o +obj-$(CONFIG_DRM_APPLE) += apple_piodma.o diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c new file mode 100644 index 00000000000000..c0ad2e3d426909 --- /dev/null +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright 2021 Alyssa Rosenzweig */ +/* Based on meson driver which is + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * Copyright (C) 2014 Endless Mobile + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dcp.h" + +#define DRIVER_NAME "apple" +#define DRIVER_DESC "Apple display controller DRM driver" + +#define FRAC_16_16(mult, div) (((mult) << 16) / (div)) + +#define MAX_COPROCESSORS 2 + +struct apple_drm_private { + struct drm_device drm; +}; + +DEFINE_DRM_GEM_DMA_FOPS(apple_fops); + +static const struct drm_driver apple_drm_driver = { + DRM_GEM_DMA_DRIVER_OPS, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = "20210901", + .major = 1, + .minor = 0, + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, + .fops = &apple_fops, +}; + +static int apple_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state; + struct drm_crtc_state *crtc_state; + + new_plane_state = drm_atomic_get_new_plane_state(state, plane); + + if (!new_plane_state->crtc) + return 0; + + crtc_state = drm_atomic_get_crtc_state(state, new_plane_state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + /* + * DCP limits downscaling to 2x and upscaling to 4x. Attempting to + * scale outside these bounds errors out when swapping. + * + * This function also takes care of clipping the src/dest rectangles, + * which is required for correct operation. Partially off-screen + * surfaces may appear corrupted. + * + * DCP does not distinguish plane types in the hardware, so we set + * can_position. If the primary plane does not fill the screen, the + * hardware will fill in zeroes (black). + */ + return drm_atomic_helper_check_plane_state(new_plane_state, + crtc_state, + FRAC_16_16(1, 4), + FRAC_16_16(2, 1), + true, true); +} + +static void apple_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + /* Handled in atomic_flush */ +} + +static const struct drm_plane_helper_funcs apple_plane_helper_funcs = { + .atomic_check = apple_plane_atomic_check, + .atomic_update = apple_plane_atomic_update, +}; + +static const struct drm_plane_funcs apple_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +/* + * Table of supported formats, mapping from DRM fourccs to DCP fourccs. + * + * For future work, DCP supports more formats not listed, including YUV + * formats, an extra RGBA format, and a biplanar RGB10_A8 format (fourcc b3a8) + * used for HDR. + * + * Note: we don't have non-alpha formats but userspace breaks without XRGB. It + * doesn't matter for the primary plane, but cursors/overlays must not + * advertise formats without alpha. + */ +static const u32 dcp_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ABGR8888, +}; + +u64 apple_format_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static struct drm_plane *apple_plane_init(struct drm_device *dev, + enum drm_plane_type type) +{ + int ret; + struct drm_plane *plane; + + plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL); + + ret = drm_universal_plane_init(dev, plane, 0x1, &apple_plane_funcs, + dcp_formats, ARRAY_SIZE(dcp_formats), + apple_format_modifiers, type, NULL); + if (ret) + return ERR_PTR(ret); + + drm_plane_helper_add(plane, &apple_plane_helper_funcs); + + return plane; +} + +static int apple_enable_vblank(struct drm_crtc *crtc) +{ + to_apple_crtc(crtc)->vsync_disabled = false; + + return 0; +} + +static void apple_disable_vblank(struct drm_crtc *crtc) +{ + to_apple_crtc(crtc)->vsync_disabled = true; +} + +static enum drm_connector_status +apple_connector_detect(struct drm_connector *connector, bool force) +{ + struct apple_connector *apple_connector = to_apple_connector(connector); + + return apple_connector->connected ? connector_status_connected : + connector_status_disconnected; +} + +static void apple_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + drm_crtc_vblank_on(crtc); +} + +static void apple_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + drm_crtc_vblank_off(crtc); + + if (crtc->state->event && !crtc->state->active) { + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + spin_unlock_irq(&crtc->dev->event_lock); + + crtc->state->event = NULL; + } +} + +static void apple_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct apple_crtc *apple_crtc = to_apple_crtc(crtc); + unsigned long flags; + + if (crtc->state->event) { + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + spin_lock_irqsave(&crtc->dev->event_lock, flags); + apple_crtc->event = crtc->state->event; + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + crtc->state->event = NULL; + } +} + +void apple_crtc_vblank(struct apple_crtc *crtc) +{ + unsigned long flags; + + if (crtc->vsync_disabled) + return; + + drm_crtc_handle_vblank(&crtc->base); + + spin_lock_irqsave(&crtc->base.dev->event_lock, flags); + if (crtc->event) { + drm_crtc_send_vblank_event(&crtc->base, crtc->event); + drm_crtc_vblank_put(&crtc->base); + crtc->event = NULL; + } + spin_unlock_irqrestore(&crtc->base.dev->event_lock, flags); +} + +static const struct drm_crtc_funcs apple_crtc_funcs = { + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .destroy = drm_crtc_cleanup, + .page_flip = drm_atomic_helper_page_flip, + .reset = drm_atomic_helper_crtc_reset, + .set_config = drm_atomic_helper_set_config, + .enable_vblank = apple_enable_vblank, + .disable_vblank = apple_disable_vblank, +}; + +static const struct drm_mode_config_funcs apple_mode_config_funcs = { + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, + .fb_create = drm_gem_fb_create, +}; + +static const struct drm_mode_config_helper_funcs apple_mode_config_helpers = { + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, +}; + +static const struct drm_connector_funcs apple_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .detect = apple_connector_detect, +}; + +static const struct drm_connector_helper_funcs apple_connector_helper_funcs = { + .get_modes = dcp_get_modes, + .mode_valid = dcp_mode_valid, +}; + +static const struct drm_crtc_helper_funcs apple_crtc_helper_funcs = { + .atomic_begin = apple_crtc_atomic_begin, + .atomic_flush = dcp_flush, + .atomic_enable = apple_crtc_atomic_enable, + .atomic_disable = apple_crtc_atomic_disable, +}; + +static int apple_probe_per_dcp(struct device *dev, + struct drm_device *drm, + struct platform_device *dcp) +{ + struct apple_crtc *crtc; + struct apple_connector *connector; + struct drm_encoder *encoder; + struct drm_plane *primary; + int ret; + + primary = apple_plane_init(drm, DRM_PLANE_TYPE_PRIMARY); + + if (IS_ERR(primary)) + return PTR_ERR(primary); + + crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL); + ret = drm_crtc_init_with_planes(drm, &crtc->base, primary, NULL, + &apple_crtc_funcs, NULL); + if (ret) + return ret; + + drm_crtc_helper_add(&crtc->base, &apple_crtc_helper_funcs); + + encoder = devm_kzalloc(dev, sizeof(*encoder), GFP_KERNEL); + encoder->possible_crtcs = drm_crtc_mask(&crtc->base); + ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); + if (ret) + return ret; + + connector = devm_kzalloc(dev, sizeof(*connector), GFP_KERNEL); + drm_connector_helper_add(&connector->base, + &apple_connector_helper_funcs); + + ret = drm_connector_init(drm, &connector->base, &apple_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA); + if (ret) + return ret; + + connector->base.polled = DRM_CONNECTOR_POLL_HPD; + connector->connected = true; /* XXX */ + connector->dcp = dcp; + + INIT_WORK(&connector->hotplug_wq, dcp_hotplug); + + crtc->dcp = dcp; + dcp_link(dcp, crtc, connector); + + return drm_connector_attach_encoder(&connector->base, encoder); +} + +static int apple_platform_probe(struct platform_device *pdev) +{ + struct apple_drm_private *apple; + struct platform_device *dcp[MAX_COPROCESSORS]; + int ret, nr_dcp, i; + + for (nr_dcp = 0; nr_dcp < MAX_COPROCESSORS; ++nr_dcp) { + struct device_node *np; + + np = of_parse_phandle(pdev->dev.of_node, "apple,coprocessors", + nr_dcp); + + if (!np) + break; + + dcp[nr_dcp] = of_find_device_by_node(np); + + if (!dcp[nr_dcp]) + return -ENODEV; + + /* DCP needs to be initialized before KMS can come online */ + if (!platform_get_drvdata(dcp[nr_dcp])) + return -EPROBE_DEFER; + + if (!dcp_is_initialized(dcp[nr_dcp])) + return -EPROBE_DEFER; + } + + /* Need at least 1 DCP for a display subsystem */ + if (nr_dcp < 1) + return -ENODEV; + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + apple = devm_drm_dev_alloc(&pdev->dev, &apple_drm_driver, + struct apple_drm_private, drm); + if (IS_ERR(apple)) + return PTR_ERR(apple); + + ret = drm_vblank_init(&apple->drm, 1); + if (ret) + return ret; + + ret = drmm_mode_config_init(&apple->drm); + if (ret) + goto err_unload; + + /* + * IOMFB::UPPipeDCP_H13P::verify_surfaces produces the error "plane + * requires a minimum of 32x32 for the source buffer" if smaller + */ + apple->drm.mode_config.min_width = 32; + apple->drm.mode_config.min_height = 32; + + /* Unknown maximum, use the iMac (24-inch, 2021) display resolution as + * maximum. + */ + apple->drm.mode_config.max_width = 4480; + apple->drm.mode_config.max_height = 2520; + + apple->drm.mode_config.funcs = &apple_mode_config_funcs; + apple->drm.mode_config.helper_private = &apple_mode_config_helpers; + + for (i = 0; i < nr_dcp; ++i) { + ret = apple_probe_per_dcp(&pdev->dev, &apple->drm, dcp[i]); + + if (ret) + goto err_unload; + } + + drm_mode_config_reset(&apple->drm); + + ret = drm_aperture_remove_framebuffers(false, &apple_drm_driver); + if (ret) + return ret; + + ret = drm_dev_register(&apple->drm, 0); + if (ret) + goto err_unload; + + drm_fbdev_generic_setup(&apple->drm, 32); + + return 0; + +err_unload: + drm_dev_put(&apple->drm); + return ret; +} + +static int apple_platform_remove(struct platform_device *pdev) +{ + struct drm_device *drm = platform_get_drvdata(pdev); + + drm_dev_unregister(drm); + + return 0; +} + +static const struct of_device_id of_match[] = { + { .compatible = "apple,display-subsystem" }, + {} +}; +MODULE_DEVICE_TABLE(of, of_match); + +static struct platform_driver apple_platform_driver = { + .driver = { + .name = "apple-drm", + .of_match_table = of_match, + }, + .probe = apple_platform_probe, + .remove = apple_platform_remove, +}; + +module_platform_driver(apple_platform_driver); + +MODULE_AUTHOR("Alyssa Rosenzweig "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c new file mode 100644 index 00000000000000..d43ed9021bd64c --- /dev/null +++ b/drivers/gpu/drm/apple/dcp.c @@ -0,0 +1,1393 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright 2021 Alyssa Rosenzweig */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dcpep.h" +#include "dcp.h" +#include "parser.h" + +struct apple_dcp; + +/* Register defines used in bandwidth setup structure */ +#define REG_SCRATCH (0x14) +#define REG_DOORBELL (0x0) +#define REG_DOORBELL_BIT (2) + +#define DCP_BOOT_TIMEOUT msecs_to_jiffies(1000) + +/* Limit on call stack depth (arbitrary). Some nesting is required */ +#define DCP_MAX_CALL_DEPTH 8 + +typedef void (*dcp_callback_t)(struct apple_dcp *, void *, void *); + +struct dcp_call_channel { + dcp_callback_t callbacks[DCP_MAX_CALL_DEPTH]; + void *cookies[DCP_MAX_CALL_DEPTH]; + void *output[DCP_MAX_CALL_DEPTH]; + u16 end[DCP_MAX_CALL_DEPTH]; + + /* Current depth of the call stack. Less than DCP_MAX_CALL_DEPTH */ + u8 depth; +}; + +struct dcp_cb_channel { + u8 depth; + void *output[DCP_MAX_CALL_DEPTH]; +}; + +/* Temporary backing for a chunked transfer via setDCPAVPropStart/Chunk/End */ +struct dcp_chunks { + size_t length; + void *data; +}; + +#define DCP_MAX_MAPPINGS (128) /* should be enough */ +#define MAX_DISP_REGISTERS (7) + +struct apple_dcp { + struct device *dev; + struct platform_device *piodma; + struct apple_rtkit *rtk; + struct apple_crtc *crtc; + struct apple_connector *connector; + + /* DCP has crashed */ + bool crashed; + + /* DCP shared memory */ + void *shmem; + + /* Display registers mappable to the DCP */ + struct resource *disp_registers[MAX_DISP_REGISTERS]; + unsigned int nr_disp_registers; + + /* Number of memory mappings made by the DCP, used as an ID */ + u32 nr_mappings; + + /* Indexed table of mappings */ + struct sg_table mappings[DCP_MAX_MAPPINGS]; + + struct dcp_call_channel ch_cmd, ch_oobcmd; + struct dcp_cb_channel ch_cb, ch_oobcb, ch_async; + + /* Active chunked transfer. There can only be one at a time. */ + struct dcp_chunks chunks; + + /* Queued swap. Owned by the DCP to avoid per-swap memory allocation */ + struct dcp_swap_submit_req swap; + + /* Current display mode */ + bool valid_mode; + struct dcp_set_digital_out_mode_req mode; + + /* Is the DCP booted? */ + bool active; + + /* Modes valid for the connected display */ + struct dcp_display_mode *modes; + unsigned int nr_modes; + + /* Attributes of the connected display */ + int width_mm, height_mm; +}; + +/* + * A channel is busy if we have sent a message that has yet to be + * acked. The driver must not sent a message to a busy channel. + */ +static bool dcp_channel_busy(struct dcp_call_channel *ch) +{ + return (ch->depth != 0); +} + +/* Get a call channel for a context */ +static struct dcp_call_channel * +dcp_get_call_channel(struct apple_dcp *dcp, enum dcp_context_id context) +{ + switch (context) { + case DCP_CONTEXT_CMD: + case DCP_CONTEXT_CB: + return &dcp->ch_cmd; + case DCP_CONTEXT_OOBCMD: + case DCP_CONTEXT_OOBCB: + return &dcp->ch_oobcmd; + default: + return NULL; + } +} + +/* + * Get the context ID passed to the DCP for a command we push. The rule is + * simple: callback contexts are used when replying to the DCP, command + * contexts are used otherwise. That corresponds to a non/zero call stack + * depth. This rule frees the caller from tracking the call context manually. + */ +static enum dcp_context_id dcp_call_context(struct apple_dcp *dcp, bool oob) +{ + u8 depth = oob ? dcp->ch_oobcmd.depth : dcp->ch_cmd.depth; + + if (depth) + return oob ? DCP_CONTEXT_OOBCB : DCP_CONTEXT_CB; + else + return oob ? DCP_CONTEXT_OOBCMD : DCP_CONTEXT_CMD; +} + +/* Get a callback channel for a context */ +static struct dcp_cb_channel *dcp_get_cb_channel(struct apple_dcp *dcp, + enum dcp_context_id context) +{ + switch (context) { + case DCP_CONTEXT_CB: + return &dcp->ch_cb; + case DCP_CONTEXT_OOBCB: + return &dcp->ch_oobcb; + case DCP_CONTEXT_ASYNC: + return &dcp->ch_async; + default: + return NULL; + } +} + +/* Get the start of a packet: after the end of the previous packet */ +static u16 dcp_packet_start(struct dcp_call_channel *ch, u8 depth) +{ + if (depth > 0) + return ch->end[depth - 1]; + else + return 0; +} + +/* Pushes and pops the depth of the call stack with safety checks */ +static u8 dcp_push_depth(u8 *depth) +{ + u8 ret = (*depth)++; + + WARN_ON(ret >= DCP_MAX_CALL_DEPTH); + return ret; +} + +static u8 dcp_pop_depth(u8 *depth) +{ + WARN_ON((*depth) == 0); + + return --(*depth); +} + +#define DCP_METHOD(tag, name) [name] = { #name, tag } + +const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { + DCP_METHOD("A000", dcpep_late_init_signal), + DCP_METHOD("A029", dcpep_setup_video_limits), + DCP_METHOD("A034", dcpep_update_notify_clients_dcp), + DCP_METHOD("A357", dcpep_set_create_dfb), + DCP_METHOD("A401", dcpep_start_signal), + DCP_METHOD("A407", dcpep_swap_start), + DCP_METHOD("A408", dcpep_swap_submit), + DCP_METHOD("A410", dcpep_set_display_device), + DCP_METHOD("A412", dcpep_set_digital_out_mode), + DCP_METHOD("A443", dcpep_create_default_fb), + DCP_METHOD("A454", dcpep_first_client_open), + DCP_METHOD("A460", dcpep_set_display_refresh_properties), + DCP_METHOD("A463", dcpep_flush_supports_power), + DCP_METHOD("A468", dcpep_set_power_state), +}; + +/* Call a DCP function given by a tag */ +static void dcp_push(struct apple_dcp *dcp, bool oob, enum dcpep_method method, + u32 in_len, u32 out_len, void *data, dcp_callback_t cb, + void *cookie) +{ + struct dcp_call_channel *ch = oob ? &dcp->ch_oobcmd : &dcp->ch_cmd; + enum dcp_context_id context = dcp_call_context(dcp, oob); + + struct dcp_packet_header header = { + .in_len = in_len, + .out_len = out_len, + + /* Tag is reversed due to endianness of the fourcc */ + .tag[0] = dcp_methods[method].tag[3], + .tag[1] = dcp_methods[method].tag[2], + .tag[2] = dcp_methods[method].tag[1], + .tag[3] = dcp_methods[method].tag[0], + }; + + u8 depth = dcp_push_depth(&ch->depth); + u16 offset = dcp_packet_start(ch, depth); + + void *out = dcp->shmem + dcp_tx_offset(context) + offset; + void *out_data = out + sizeof(header); + size_t data_len = sizeof(header) + in_len + out_len; + + memcpy(out, &header, sizeof(header)); + + if (in_len > 0) + memcpy(out_data, data, in_len); + + dev_dbg(dcp->dev, "---> %s: context %u, offset %u, depth %u\n", + dcp_methods[method].name, context, offset, depth); + + ch->callbacks[depth] = cb; + ch->cookies[depth] = cookie; + ch->output[depth] = out + sizeof(header) + in_len; + ch->end[depth] = offset + ALIGN(data_len, DCP_PACKET_ALIGNMENT); + + apple_rtkit_send_message(dcp->rtk, DCP_ENDPOINT, + dcpep_msg(context, data_len, offset), + NULL, false); +} + +#define DCP_THUNK_VOID(func, handle) \ + static void func(struct apple_dcp *dcp, bool oob, dcp_callback_t cb, \ + void *cookie) \ + { \ + dcp_push(dcp, oob, handle, 0, 0, NULL, cb, cookie); \ + } + +#define DCP_THUNK_OUT(func, handle, T) \ + static void func(struct apple_dcp *dcp, bool oob, dcp_callback_t cb, \ + void *cookie) \ + { \ + dcp_push(dcp, oob, handle, 0, sizeof(T), NULL, cb, cookie); \ + } + +#define DCP_THUNK_IN(func, handle, T) \ + static void func(struct apple_dcp *dcp, bool oob, T *data, \ + dcp_callback_t cb, void *cookie) \ + { \ + dcp_push(dcp, oob, handle, sizeof(T), 0, data, cb, cookie); \ + } + +#define DCP_THUNK_INOUT(func, handle, T_in, T_out) \ + static void func(struct apple_dcp *dcp, bool oob, T_in *data, \ + dcp_callback_t cb, void *cookie) \ + { \ + dcp_push(dcp, oob, handle, sizeof(T_in), sizeof(T_out), data, \ + cb, cookie); \ + } + +DCP_THUNK_INOUT(dcp_swap_submit, dcpep_swap_submit, struct dcp_swap_submit_req, + struct dcp_swap_submit_resp); + +DCP_THUNK_INOUT(dcp_swap_start, dcpep_swap_start, struct dcp_swap_start_req, + struct dcp_swap_start_resp); + +DCP_THUNK_INOUT(dcp_set_power_state, dcpep_set_power_state, + struct dcp_set_power_state_req, + struct dcp_set_power_state_resp); + +DCP_THUNK_INOUT(dcp_set_digital_out_mode, dcpep_set_digital_out_mode, + struct dcp_set_digital_out_mode_req, u32); + +DCP_THUNK_INOUT(dcp_set_display_device, dcpep_set_display_device, u32, u32); + +DCP_THUNK_OUT(dcp_set_display_refresh_properties, + dcpep_set_display_refresh_properties, u32); + +DCP_THUNK_OUT(dcp_late_init_signal, dcpep_late_init_signal, u32); +DCP_THUNK_IN(dcp_flush_supports_power, dcpep_flush_supports_power, u32); +DCP_THUNK_OUT(dcp_create_default_fb, dcpep_create_default_fb, u32); +DCP_THUNK_OUT(dcp_start_signal, dcpep_start_signal, u32); +DCP_THUNK_VOID(dcp_setup_video_limits, dcpep_setup_video_limits); +DCP_THUNK_VOID(dcp_set_create_dfb, dcpep_set_create_dfb); +DCP_THUNK_VOID(dcp_first_client_open, dcpep_first_client_open); + +__attribute__((unused)) +DCP_THUNK_IN(dcp_update_notify_clients_dcp, dcpep_update_notify_clients_dcp, + struct dcp_update_notify_clients_dcp); + +/* Parse a callback tag "D123" into the ID 123. Returns -EINVAL on failure. */ +static int dcp_parse_tag(char tag[4]) +{ + u32 d[3]; + int i; + + if (tag[3] != 'D') + return -EINVAL; + + for (i = 0; i < 3; ++i) { + d[i] = (u32)(tag[i] - '0'); + + if (d[i] > 9) + return -EINVAL; + } + + return d[0] + (d[1] * 10) + (d[2] * 100); +} + +/* Ack a callback from the DCP */ +static void dcp_ack(struct apple_dcp *dcp, enum dcp_context_id context) +{ + struct dcp_cb_channel *ch = dcp_get_cb_channel(dcp, context); + + dcp_pop_depth(&ch->depth); + apple_rtkit_send_message(dcp->rtk, DCP_ENDPOINT, dcpep_ack(context), + NULL, false); +} + +/* DCP callback handlers */ +static void dcpep_cb_nop(struct apple_dcp *dcp) +{ + /* No operation */ +} + +static u8 dcpep_cb_true(struct apple_dcp *dcp) +{ + return true; +} + +static u8 dcpep_cb_false(struct apple_dcp *dcp) +{ + return false; +} + +static u32 dcpep_cb_zero(struct apple_dcp *dcp) +{ + return 0; +} + +static void dcpep_cb_swap_complete(struct apple_dcp *dcp) +{ + apple_crtc_vblank(dcp->crtc); +} + +static struct dcp_get_uint_prop_resp +dcpep_cb_get_uint_prop(struct apple_dcp *dcp, struct dcp_get_uint_prop_req *req) +{ + /* unimplemented for now */ + return (struct dcp_get_uint_prop_resp) { + .value = 0 + }; +} + +/* + * Callback to map a buffer allocated with allocate_buf for PIODMA usage. + * PIODMA is separate from the main DCP and uses own IOVA space on a dedicated + * stream of the display DART, rather than the expected DCP DART. + * + * XXX: This relies on dma_get_sgtable in concert with dma_map_sgtable, which + * is a "fundamentally unsafe" operation according to the docs. And yet + * everyone does it... + */ +static struct dcp_map_buf_resp +dcpep_cb_map_piodma(struct apple_dcp *dcp, struct dcp_map_buf_req *req) +{ + struct sg_table *map; + int ret; + + if (req->buffer >= ARRAY_SIZE(dcp->mappings)) + goto reject; + + map = &dcp->mappings[req->buffer]; + + if (!map->sgl) + goto reject; + + /* Use PIODMA device instead of DCP to map against the right IOMMU. */ + ret = dma_map_sgtable(&dcp->piodma->dev, map, DMA_BIDIRECTIONAL, 0); + + if (ret) + goto reject; + + return (struct dcp_map_buf_resp) { + .dva = sg_dma_address(map->sgl) + }; + +reject: + dev_err(dcp->dev, "denying map of invalid buffer %llx for pidoma\n", + req->buffer); + return (struct dcp_map_buf_resp) { + .ret = EINVAL + }; +} + +/* + * Allocate an IOVA contiguous buffer mapped to the DCP. The buffer need not be + * physically contigiuous, however we should save the sgtable in case the + * buffer needs to be later mapped for PIODMA. + */ +static struct dcp_allocate_buffer_resp +dcpep_cb_allocate_buffer(struct apple_dcp *dcp, struct dcp_allocate_buffer_req *req) +{ + struct dcp_allocate_buffer_resp resp = { 0 }; + void *buf; + + resp.dva_size = ALIGN(req->size, 4096); + resp.mem_desc_id = ++dcp->nr_mappings; + + if (resp.mem_desc_id >= ARRAY_SIZE(dcp->mappings)) { + dev_warn(dcp->dev, "DCP overflowed mapping table, ignoring"); + return resp; + } + + buf = dma_alloc_coherent(dcp->dev, resp.dva_size, &resp.dva, + GFP_KERNEL); + + dma_get_sgtable(dcp->dev, &dcp->mappings[resp.mem_desc_id], buf, + resp.dva, resp.dva_size); + return resp; +} + +/* Validate that the specified region is a display register */ +static bool is_disp_register(struct apple_dcp *dcp, u64 start, u64 end) +{ + int i; + + for (i = 0; i < dcp->nr_disp_registers; ++i) { + struct resource *r = dcp->disp_registers[i]; + + if ((start >= r->start) && (end <= r->end)) + return true; + } + + return false; +} + +/* + * Map contiguous physical memory into the DCP's address space. The firmware + * uses this to map the display registers we advertise in + * sr_map_device_memory_with_index, so we bounds check against that to guard + * safe against malicious coprocessors. + */ +static struct dcp_map_physical_resp +dcpep_cb_map_physical(struct apple_dcp *dcp, struct dcp_map_physical_req *req) +{ + int size = ALIGN(req->size, 4096); + + if (!is_disp_register(dcp, req->paddr, req->paddr + size - 1)) { + dev_err(dcp->dev, "refusing to map phys address %llx size %llx", + req->paddr, req->size); + return (struct dcp_map_physical_resp) { }; + } + + return (struct dcp_map_physical_resp) { + .dva_size = size, + .mem_desc_id = ++dcp->nr_mappings, + .dva = dma_map_resource(dcp->dev, req->paddr, size, + DMA_BIDIRECTIONAL, 0), + }; +} + +static u64 dcpep_cb_get_frequency(struct apple_dcp *dcp) +{ + /* Pixel clock frequency in Hz (compare: 4K@60 VGA clock 533.250 MHz) */ + return 533333328; +} + +static struct dcp_map_reg_resp +dcpep_cb_map_reg(struct apple_dcp *dcp, struct dcp_map_reg_req *req) +{ + if (req->index >= dcp->nr_disp_registers) { + dev_warn(dcp->dev, "attempted to read invalid reg index %u", + req->index); + + return (struct dcp_map_reg_resp) { + .ret = 1 + }; + } else { + struct resource *rsrc = dcp->disp_registers[req->index]; + + return (struct dcp_map_reg_resp) { + .addr = rsrc->start, + .length = resource_size(rsrc) + }; + } +} + +/* Chunked data transfer for property dictionaries */ +static u8 dcpep_cb_prop_start(struct apple_dcp *dcp, u32 *length) +{ + if (dcp->chunks.data != NULL) { + dev_warn(dcp->dev, "ignoring spurious transfer start\n"); + return false; + } + + dcp->chunks.length = *length; + dcp->chunks.data = devm_kzalloc(dcp->dev, *length, GFP_KERNEL); + + if (!dcp->chunks.data) { + dev_warn(dcp->dev, "failed to allocate chunks\n"); + return false; + } + + return true; +} + +static u8 dcpep_cb_prop_chunk(struct apple_dcp *dcp, + struct dcp_set_dcpav_prop_chunk_req *req) +{ + if (!dcp->chunks.data) { + dev_warn(dcp->dev, "ignoring spurious chunk\n"); + return false; + } + + if (req->offset + req->length > dcp->chunks.length) { + dev_warn(dcp->dev, "ignoring overflowing chunk\n"); + return false; + } + + memcpy(dcp->chunks.data + req->offset, req->data, req->length); + return true; +} + +static void dcp_set_dimensions(struct apple_dcp *dcp) +{ + int i; + + /* Set the connector info */ + if (dcp->connector) { + struct drm_connector *connector = &dcp->connector->base; + + mutex_lock(&connector->dev->mode_config.mutex); + connector->display_info.width_mm = dcp->width_mm; + connector->display_info.height_mm = dcp->height_mm; + mutex_unlock(&connector->dev->mode_config.mutex); + } + + /* + * Fix up any probed modes. Modes are created when parsing + * TimingElements, dimensions are calculated when parsing + * DisplayAttributes, and TimingElements may be sent first + */ + for (i = 0; i < dcp->nr_modes; ++i) { + dcp->modes[i].mode.width_mm = dcp->width_mm; + dcp->modes[i].mode.height_mm = dcp->height_mm; + } +} + +static bool dcpep_process_chunks(struct apple_dcp *dcp, + struct dcp_set_dcpav_prop_end_req *req) +{ + struct dcp_parse_ctx ctx; + int ret; + + if (!dcp->chunks.data) { + dev_warn(dcp->dev, "ignoring spurious end\n"); + return false; + } + + ret = parse(dcp->chunks.data, dcp->chunks.length, &ctx); + + if (ret) { + dev_warn(dcp->dev, "bad header on dcpav props\n"); + return false; + } + + if (!strcmp(req->key, "TimingElements")) { + dcp->modes = enumerate_modes(&ctx, &dcp->nr_modes, + dcp->width_mm, dcp->height_mm); + + if (IS_ERR(dcp->modes)) { + dev_warn(dcp->dev, "failed to parse modes\n"); + dcp->modes = NULL; + dcp->nr_modes = 0; + return false; + } + } else if (!strcmp(req->key, "DisplayAttributes")) { + ret = parse_display_attributes(&ctx, &dcp->width_mm, + &dcp->height_mm); + + if (ret) { + dev_warn(dcp->dev, "failed to parse display attribs\n"); + return false; + } + + dcp_set_dimensions(dcp); + } + + return true; +} + +static u8 dcpep_cb_prop_end(struct apple_dcp *dcp, + struct dcp_set_dcpav_prop_end_req *req) +{ + u8 resp = dcpep_process_chunks(dcp, req); + + /* Reset for the next transfer */ + devm_kfree(dcp->dev, dcp->chunks.data); + dcp->chunks.data = NULL; + + return resp; +} + +/* Boot sequence */ +static void boot_done(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct dcp_cb_channel *ch = &dcp->ch_cb; + u8 *succ = ch->output[ch->depth - 1]; + + *succ = true; + dcp_ack(dcp, DCP_CONTEXT_CB); +} + +static void boot_5(struct apple_dcp *dcp, void *out, void *cookie) +{ + dcp_set_display_refresh_properties(dcp, false, boot_done, NULL); +} + +static void boot_4(struct apple_dcp *dcp, void *out, void *cookie) +{ + dcp_late_init_signal(dcp, false, boot_5, NULL); +} + +static void boot_3(struct apple_dcp *dcp, void *out, void *cookie) +{ + u32 v_true = true; + + dcp_flush_supports_power(dcp, false, &v_true, boot_4, NULL); +} + +static void boot_2(struct apple_dcp *dcp, void *out, void *cookie) +{ + dcp_setup_video_limits(dcp, false, boot_3, NULL); +} + +static void boot_1_5(struct apple_dcp *dcp, void *out, void *cookie) +{ + dcp_create_default_fb(dcp, false, boot_2, NULL); +} + +/* Use special function signature to defer the ACK */ +static bool dcpep_cb_boot_1(struct apple_dcp *dcp, void *out, void *in) +{ + dcp_set_create_dfb(dcp, false, boot_1_5, NULL); + return false; +} + +static struct dcp_rt_bandwidth dcpep_cb_rt_bandwidth(struct apple_dcp *dcp) +{ + return (struct dcp_rt_bandwidth) { + .reg_scratch = dcp->disp_registers[5]->start + REG_SCRATCH, + .reg_doorbell = dcp->disp_registers[6]->start + REG_DOORBELL, + .doorbell_bit = REG_DOORBELL_BIT, + + .padding[3] = 0x4, // XXX: required by 11.x firmware + }; +} + +/* Callback to get the current time as milliseconds since the UNIX epoch */ +static u64 dcpep_cb_get_time(struct apple_dcp *dcp) +{ + return ktime_to_ms(ktime_get_real()); +} + +/* + * Helper to send a DRM hotplug event. The DCP is accessed from a single + * (RTKit) thread. To handle hotplug callbacks, we need to call + * drm_kms_helper_hotplug_event, which does an atomic commit (via DCP) and + * waits for vblank (a DCP callback). That means we deadlock if we call from + * the RTKit thread! Instead, move the call to another thread via a workqueue. + */ +void dcp_hotplug(struct work_struct *work) +{ + struct apple_connector *connector; + struct drm_device *dev; + + connector = container_of(work, struct apple_connector, hotplug_wq); + dev = connector->base.dev; + + /* + * DCP defers link training until we set a display mode. But we set + * display modes from atomic_flush, so userspace needs to trigger a + * flush, or the CRTC gets no signal. + */ + if (connector->connected) { + drm_connector_set_link_status_property( + &connector->base, DRM_MODE_LINK_STATUS_BAD); + } + + if (dev && dev->registered) + drm_kms_helper_hotplug_event(dev); +} +EXPORT_SYMBOL_GPL(dcp_hotplug); + +static void dcpep_cb_hotplug(struct apple_dcp *dcp, u64 *connected) +{ + struct apple_connector *connector = dcp->connector; + + /* Hotplug invalidates mode. DRM doesn't always handle this. */ + dcp->valid_mode = false; + + if (connector) { + connector->connected = !!(*connected); + schedule_work(&connector->hotplug_wq); + } +} + +#define DCPEP_MAX_CB (1000) + +/* + * Define type-safe trampolines. Define typedefs to enforce type-safety on the + * input data (so if the types don't match, gcc errors out). + */ + +#define TRAMPOLINE_VOID(func, handler) \ + static bool func(struct apple_dcp *dcp, void *out, void *in) \ + { \ + dev_dbg(dcp->dev, "received callback %s\n", #handler); \ + handler(dcp); \ + return true; \ + } + +#define TRAMPOLINE_IN(func, handler, T_in) \ + typedef void (*callback_##name)(struct apple_dcp *, T_in *); \ + \ + static bool func(struct apple_dcp *dcp, void *out, void *in) \ + { \ + callback_##name cb = handler; \ + \ + dev_dbg(dcp->dev, "received callback %s\n", #handler); \ + cb(dcp, in); \ + return true; \ + } + +#define TRAMPOLINE_INOUT(func, handler, T_in, T_out) \ + typedef T_out (*callback_##handler)(struct apple_dcp *, T_in *); \ + \ + static bool func(struct apple_dcp *dcp, void *out, void *in) \ + { \ + T_out *typed_out = out; \ + callback_##handler cb = handler; \ + \ + dev_dbg(dcp->dev, "received callback %s\n", #handler); \ + *typed_out = cb(dcp, in); \ + return true; \ + } + +#define TRAMPOLINE_OUT(func, handler, T_out) \ + static bool func(struct apple_dcp *dcp, void *out, void *in) \ + { \ + T_out *typed_out = out; \ + \ + dev_dbg(dcp->dev, "received callback %s\n", #handler); \ + *typed_out = handler(dcp); \ + return true; \ + } + +TRAMPOLINE_VOID(trampoline_nop, dcpep_cb_nop); +TRAMPOLINE_OUT(trampoline_true, dcpep_cb_true, u8); +TRAMPOLINE_OUT(trampoline_false, dcpep_cb_false, u8); +TRAMPOLINE_OUT(trampoline_zero, dcpep_cb_zero, u32); +TRAMPOLINE_VOID(trampoline_swap_complete, dcpep_cb_swap_complete); +TRAMPOLINE_INOUT(trampoline_get_uint_prop, dcpep_cb_get_uint_prop, + struct dcp_get_uint_prop_req, struct dcp_get_uint_prop_resp); +TRAMPOLINE_INOUT(trampoline_map_piodma, dcpep_cb_map_piodma, + struct dcp_map_buf_req, struct dcp_map_buf_resp); +TRAMPOLINE_INOUT(trampoline_allocate_buffer, dcpep_cb_allocate_buffer, + struct dcp_allocate_buffer_req, + struct dcp_allocate_buffer_resp); +TRAMPOLINE_INOUT(trampoline_map_physical, dcpep_cb_map_physical, + struct dcp_map_physical_req, struct dcp_map_physical_resp); +TRAMPOLINE_INOUT(trampoline_map_reg, dcpep_cb_map_reg, struct dcp_map_reg_req, + struct dcp_map_reg_resp); +TRAMPOLINE_INOUT(trampoline_prop_start, dcpep_cb_prop_start, u32, u8); +TRAMPOLINE_INOUT(trampoline_prop_chunk, dcpep_cb_prop_chunk, + struct dcp_set_dcpav_prop_chunk_req, u8); +TRAMPOLINE_INOUT(trampoline_prop_end, dcpep_cb_prop_end, + struct dcp_set_dcpav_prop_end_req, u8); +TRAMPOLINE_OUT(trampoline_rt_bandwidth, dcpep_cb_rt_bandwidth, + struct dcp_rt_bandwidth); +TRAMPOLINE_OUT(trampoline_get_frequency, dcpep_cb_get_frequency, u64); +TRAMPOLINE_OUT(trampoline_get_time, dcpep_cb_get_time, u64); +TRAMPOLINE_IN(trampoline_hotplug, dcpep_cb_hotplug, u64); + +bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, void *, void *) = { + [0] = trampoline_true, /* did_boot_signal */ + [1] = trampoline_true, /* did_power_on_signal */ + [2] = trampoline_nop, /* will_power_off_signal */ + [3] = trampoline_rt_bandwidth, + [100] = trampoline_nop, /* match_pmu_service */ + [101] = trampoline_zero, /* get_display_default_stride */ + [103] = trampoline_nop, /* set_boolean_property */ + [106] = trampoline_nop, /* remove_property */ + [107] = trampoline_true, /* create_provider_service */ + [108] = trampoline_true, /* create_product_service */ + [109] = trampoline_true, /* create_pmu_service */ + [110] = trampoline_true, /* create_iomfb_service */ + [111] = trampoline_false, /* create_backlight_service */ + [116] = dcpep_cb_boot_1, + [118] = trampoline_false, /* is_dark_boot / is_waking_from_hibernate*/ + [120] = trampoline_false, /* read_edt_data */ + [122] = trampoline_prop_start, + [123] = trampoline_prop_chunk, + [124] = trampoline_prop_end, + [201] = trampoline_map_piodma, + [206] = trampoline_true, /* match_pmu_service_2 */ + [207] = trampoline_true, /* match_backlight_service */ + [208] = trampoline_get_time, + [211] = trampoline_nop, /* update_backlight_factor_prop */ + [300] = trampoline_nop, /* pr_publish */ + [401] = trampoline_get_uint_prop, + [406] = trampoline_nop, /* set_fx_prop */ + [408] = trampoline_get_frequency, + [411] = trampoline_map_reg, + [413] = trampoline_true, /* sr_set_property_dict */ + [414] = trampoline_true, /* sr_set_property_int */ + [415] = trampoline_true, /* sr_set_property_bool */ + [451] = trampoline_allocate_buffer, + [452] = trampoline_map_physical, + [552] = trampoline_true, /* set_property_dict_0 */ + [561] = trampoline_true, /* set_property_dict */ + [563] = trampoline_true, /* set_property_int */ + [565] = trampoline_true, /* set_property_bool */ + [567] = trampoline_true, /* set_property_str */ + [574] = trampoline_zero, /* power_up_dart */ + [576] = trampoline_hotplug, + [577] = trampoline_nop, /* powerstate_notify */ + [582] = trampoline_true, /* create_default_fb_surface */ + [589] = trampoline_swap_complete, + [591] = trampoline_nop, /* swap_complete_intent_gated */ + [598] = trampoline_nop, /* find_swap_function_gated */ +}; + +static void dcpep_handle_cb(struct apple_dcp *dcp, enum dcp_context_id context, + void *data, u32 length) +{ + struct device *dev = dcp->dev; + struct dcp_packet_header *hdr = data; + void *in, *out; + int tag = dcp_parse_tag(hdr->tag); + struct dcp_cb_channel *ch = dcp_get_cb_channel(dcp, context); + u8 depth; + + if (tag < 0 || tag >= DCPEP_MAX_CB || !dcpep_cb_handlers[tag]) { + dev_warn(dev, "received unknown callback %c%c%c%c\n", + hdr->tag[3], hdr->tag[2], hdr->tag[1], hdr->tag[0]); + return; + } + + in = data + sizeof(*hdr); + out = in + hdr->in_len; + + depth = dcp_push_depth(&ch->depth); + ch->output[depth] = out; + + if (dcpep_cb_handlers[tag](dcp, out, in)) + dcp_ack(dcp, context); +} + +static void dcpep_handle_ack(struct apple_dcp *dcp, enum dcp_context_id context, + void *data, u32 length) +{ + struct dcp_packet_header *header = data; + struct dcp_call_channel *ch = dcp_get_call_channel(dcp, context); + void *cookie; + dcp_callback_t cb; + + if (!ch) { + dev_warn(dcp->dev, "ignoring ack on context %X\n", context); + return; + } + + dcp_pop_depth(&ch->depth); + + cb = ch->callbacks[ch->depth]; + cookie = ch->cookies[ch->depth]; + + if (cb) + cb(dcp, data + sizeof(*header) + header->in_len, cookie); +} + +static void dcpep_got_msg(struct apple_dcp *dcp, u64 message) +{ + enum dcp_context_id ctx_id; + u16 offset; + u32 length; + int channel_offset; + void *data; + + ctx_id = (message & DCPEP_CONTEXT_MASK) >> DCPEP_CONTEXT_SHIFT; + offset = (message & DCPEP_OFFSET_MASK) >> DCPEP_OFFSET_SHIFT; + length = (message >> DCPEP_LENGTH_SHIFT); + + channel_offset = dcp_channel_offset(ctx_id); + + if (channel_offset < 0) { + dev_warn(dcp->dev, "invalid context received %u", ctx_id); + return; + } + + data = dcp->shmem + channel_offset + offset; + + if (message & DCPEP_ACK) + dcpep_handle_ack(dcp, ctx_id, data, length); + else + dcpep_handle_cb(dcp, ctx_id, data, length); +} + +/* + * Callback for swap requests. If a swap failed, we'll never get a swap + * complete event so we need to fake a vblank event early to avoid a hang. + */ + +static void dcp_swapped(struct apple_dcp *dcp, void *data, void *cookie) +{ + struct dcp_swap_submit_resp *resp = data; + + if (resp->ret) { + dev_err(dcp->dev, "swap failed! status %u\n", resp->ret); + apple_crtc_vblank(dcp->crtc); + } +} + +static void dcp_swap_started(struct apple_dcp *dcp, void *data, void *cookie) +{ + struct dcp_swap_start_resp *resp = data; + + dcp->swap.swap.swap_id = resp->swap_id; + + dcp_swap_submit(dcp, false, &dcp->swap, dcp_swapped, NULL); +} + +/* + * DRM specifies rectangles as start and end coordinates. DCP specifies + * rectangles as a start coordinate and a width/height. Convert a DRM rectangle + * to a DCP rectangle. + */ +static struct dcp_rect drm_to_dcp_rect(struct drm_rect *rect) +{ + return (struct dcp_rect) { + .x = rect->x1, + .y = rect->y1, + .w = drm_rect_width(rect), + .h = drm_rect_height(rect) + }; +} + +static u32 drm_format_to_dcp(u32 drm) +{ + switch (drm) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + return fourcc_code('A', 'R', 'G', 'B'); + + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + return fourcc_code('A', 'B', 'G', 'R'); + } + + pr_warn("DRM format %X not supported in DCP\n", drm); + return 0; +} + +int dcp_get_modes(struct drm_connector *connector) +{ + struct apple_connector *apple_connector = to_apple_connector(connector); + struct platform_device *pdev = apple_connector->dcp; + struct apple_dcp *dcp = platform_get_drvdata(pdev); + + struct drm_device *dev = connector->dev; + struct drm_display_mode *mode; + int i; + + for (i = 0; i < dcp->nr_modes; ++i) { + mode = drm_mode_duplicate(dev, &dcp->modes[i].mode); + + if (!mode) { + dev_err(dev->dev, "Failed to duplicate display mode\n"); + return 0; + } + + drm_mode_probed_add(connector, mode); + } + + return dcp->nr_modes; +} +EXPORT_SYMBOL_GPL(dcp_get_modes); + +/* The user may own drm_display_mode, so we need to search for our copy */ +static struct dcp_display_mode *lookup_mode(struct apple_dcp *dcp, + struct drm_display_mode *mode) +{ + int i; + + for (i = 0; i < dcp->nr_modes; ++i) { + if (drm_mode_match(mode, &dcp->modes[i].mode, + DRM_MODE_MATCH_TIMINGS | + DRM_MODE_MATCH_CLOCK)) + return &dcp->modes[i]; + } + + return NULL; +} + +int dcp_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct apple_connector *apple_connector = to_apple_connector(connector); + struct platform_device *pdev = apple_connector->dcp; + struct apple_dcp *dcp = platform_get_drvdata(pdev); + + return lookup_mode(dcp, mode) ? MODE_OK : MODE_BAD; +} +EXPORT_SYMBOL_GPL(dcp_mode_valid); + +/* Helpers to modeset and swap, used to flush */ +static void do_swap(struct apple_dcp *dcp, void *data, void *cookie) +{ + struct dcp_swap_start_req start_req = { 0 }; + + dcp_swap_start(dcp, false, &start_req, dcp_swap_started, NULL); +} + +static void dcp_modeset(struct apple_dcp *dcp, void *out, void *cookie) +{ + dcp_set_digital_out_mode(dcp, false, &dcp->mode, do_swap, NULL); +} + +void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) +{ + struct platform_device *pdev = to_apple_crtc(crtc)->dcp; + struct apple_dcp *dcp = platform_get_drvdata(pdev); + struct drm_plane *plane; + struct drm_plane_state *new_state, *old_state; + struct drm_crtc_state *crtc_state; + struct dcp_swap_submit_req *req = &dcp->swap; + int l; + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + if (WARN(dcp_channel_busy(&dcp->ch_cmd), "unexpected busy channel") || + WARN(!dcp->connector->connected, "can't flush if disconnected")) { + apple_crtc_vblank(dcp->crtc); + return; + } + + /* Reset to defaults */ + memset(req, 0, sizeof(*req)); + for (l = 0; l < SWAP_SURFACES; l++) + req->surf_null[l] = true; + + for_each_oldnew_plane_in_state(state, plane, old_state, new_state, l) { + struct drm_framebuffer *fb = new_state->fb; + struct drm_rect src_rect; + + WARN_ON(l >= SWAP_SURFACES); + + req->swap.swap_enabled |= BIT(l); + + if (!new_state->fb) { + if (old_state->fb) + req->swap.swap_enabled |= DCP_REMOVE_LAYERS; + + continue; + } + req->surf_null[l] = false; + + // XXX: awful hack! race condition between a framebuffer unbind + // getting swapped out and GEM unreferencing a framebuffer. If + // we lose the race, the display gets IOVA faults and the DCP + // crashes. We need to extend the lifetime of the + // drm_framebuffer (and hence the GEM object) until after we + // get a swap complete for the swap unbinding it. + drm_framebuffer_get(fb); + + drm_rect_fp_to_int(&src_rect, &new_state->src); + + req->swap.src_rect[l] = drm_to_dcp_rect(&src_rect); + req->swap.dst_rect[l] = drm_to_dcp_rect(&new_state->dst); + + req->surf_iova[l] = drm_fb_dma_get_gem_addr(fb, new_state, 0); + + req->surf[l] = (struct dcp_surface) { + .format = drm_format_to_dcp(fb->format->format), + .xfer_func = 13, + .colorspace = 1, + .stride = fb->pitches[0], + .width = fb->width, + .height = fb->height, + .buf_size = fb->height * fb->pitches[0], + .surface_id = req->swap.surf_ids[l], + + /* Only used for compressed or multiplanar surfaces */ + .pix_size = 1, + .pel_w = 1, + .pel_h = 1, + .has_comp = 1, + .has_planes = 1, + }; + } + + /* These fields should be set together */ + req->swap.swap_completed = req->swap.swap_enabled; + + if (drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode) { + struct dcp_display_mode *mode; + u32 handle = 2; + + mode = lookup_mode(dcp, &crtc_state->mode); + if (!mode) { + dev_warn(dcp->dev, "no match for " DRM_MODE_FMT, + DRM_MODE_ARG(&crtc_state->mode)); + schedule_work(&dcp->vblank_wq); + return; + } + + dcp->mode = (struct dcp_set_digital_out_mode_req) { + .color_mode_id = mode->color_mode_id, + .timing_mode_id = mode->timing_mode_id + }; + + dcp->valid_mode = true; + + dcp_set_display_device(dcp, false, &handle, dcp_modeset, NULL); + } else + do_swap(dcp, NULL, NULL); +} +EXPORT_SYMBOL_GPL(dcp_flush); + +bool dcp_is_initialized(struct platform_device *pdev) +{ + struct apple_dcp *dcp = platform_get_drvdata(pdev); + + return dcp->active; +} +EXPORT_SYMBOL_GPL(dcp_is_initialized); + +static void init_done(struct apple_dcp *dcp, void *out, void *cookie) +{ +} + +static void init_3(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct dcp_set_power_state_req req = { + .unklong = 1, + }; + dcp_set_power_state(dcp, false, &req, init_done, NULL); +} + +static void init_2(struct apple_dcp *dcp, void *out, void *cookie) +{ + dcp_first_client_open(dcp, false, init_3, NULL); +} + +static void dcp_started(struct apple_dcp *dcp, void *data, void *cookie) +{ + dev_info(dcp->dev, "DCP booted\n"); + + init_2(dcp, data, cookie); + + dcp->active = true; +} + +static void dcp_got_msg(void *cookie, u8 endpoint, u64 message) +{ + struct apple_dcp *dcp = cookie; + enum dcpep_type type = (message >> DCPEP_TYPE_SHIFT) & DCPEP_TYPE_MASK; + + WARN_ON(endpoint != DCP_ENDPOINT); + + if (type == DCPEP_TYPE_INITIALIZED) + dcp_start_signal(dcp, false, dcp_started, NULL); + else if (type == DCPEP_TYPE_MESSAGE) + dcpep_got_msg(dcp, message); + else + dev_warn(dcp->dev, "Ignoring unknown message %llx\n", message); +} + +static void dcp_rtk_crashed(void *cookie) +{ + struct apple_dcp *dcp = cookie; + + dcp->crashed = true; + dev_err(dcp->dev, "DCP has crashed"); +} + +static int dcp_rtk_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr) +{ + struct apple_dcp *dcp = cookie; + + if (bfr->iova) { + struct iommu_domain *domain = iommu_get_domain_for_dev(dcp->dev); + phys_addr_t phy_addr; + + if (!domain) + return -ENOMEM; + + // TODO: get map from device-tree + phy_addr = iommu_iova_to_phys(domain, bfr->iova & 0xFFFFFFFF); + if (!phy_addr) + return -ENOMEM; + + // TODO: verify phy_addr, cache attribute + bfr->buffer = memremap(phy_addr, bfr->size, MEMREMAP_WB); + if (!bfr->buffer) + return -ENOMEM; + + bfr->is_mapped = true; + dev_info(dcp->dev, "shmem_setup: iova: %lx -> pa: %lx -> iomem: %lx", + (uintptr_t)bfr->iova, (uintptr_t)phy_addr, (uintptr_t)bfr->buffer); + } else { + bfr->buffer = dma_alloc_coherent(dcp->dev, bfr->size, &bfr->iova, GFP_KERNEL); + if (!bfr->buffer) + return -ENOMEM; + + dev_info(dcp->dev, "shmem_setup: iova: %lx, buffer: %lx", + (uintptr_t)bfr->iova, (uintptr_t)bfr->buffer); + } + + return 0; +} + +static void dcp_rtk_shmem_destroy(void *cookie, struct apple_rtkit_shmem *bfr) +{ + struct apple_dcp *dcp = cookie; + + if (bfr->is_mapped) + memunmap(bfr->buffer); + else + dma_free_coherent(dcp->dev, bfr->size, bfr->buffer, bfr->iova); +} + +static struct apple_rtkit_ops rtkit_ops = { + .crashed = dcp_rtk_crashed, + .recv_message = dcp_got_msg, + .shmem_setup = dcp_rtk_shmem_setup, + .shmem_destroy = dcp_rtk_shmem_destroy, +}; + +void dcp_link(struct platform_device *pdev, struct apple_crtc *crtc, + struct apple_connector *connector) +{ + struct apple_dcp *dcp = platform_get_drvdata(pdev); + + dcp->crtc = crtc; + dcp->connector = connector; + + /* Dimensions might already be parsed */ + dcp_set_dimensions(dcp); +} +EXPORT_SYMBOL_GPL(dcp_link); + +static struct platform_device *dcp_get_dev(struct device *dev, const char *name) +{ + struct device_node *node = of_get_child_by_name(dev->of_node, name); + + if (!node) + return NULL; + + return of_find_device_by_node(node); +} + +static int dcp_get_disp_regs(struct apple_dcp *dcp) +{ + struct platform_device *pdev = to_platform_device(dcp->dev); + int count = pdev->num_resources - 1; + int i; + + if (count <= 0 || count > MAX_DISP_REGISTERS) + return -EINVAL; + + for (i = 0; i < count; ++i) { + dcp->disp_registers[i] = + platform_get_resource(pdev, IORESOURCE_MEM, 1 + i); + } + + dcp->nr_disp_registers = count; + return 0; +} + +static int dcp_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct apple_dcp *dcp; + dma_addr_t shmem_iova; + int ret; + + dcp = devm_kzalloc(dev, sizeof(*dcp), GFP_KERNEL); + if (!dcp) + return -ENOMEM; + + platform_set_drvdata(pdev, dcp); + dcp->dev = dev; + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36)); + if (ret) + return ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "coproc"); + if (!res) + return -EINVAL; + + of_platform_default_populate(dev->of_node, NULL, dev); + + dcp->piodma = dcp_get_dev(dev, "piodma"); + if (!dcp->piodma) { + dev_err(dev, "failed to find piodma\n"); + return -ENODEV; + } + + ret = dcp_get_disp_regs(dcp); + if (ret) { + dev_err(dev, "failed to find display registers\n"); + return ret; + } + + dcp->rtk = devm_apple_rtkit_init(dev, dcp, "mbox", 0, &rtkit_ops); + if (IS_ERR(dcp->rtk)) + return dev_err_probe(dev, PTR_ERR(dcp->rtk), + "Failed to intialize RTKit"); + + ret = apple_rtkit_wake(dcp->rtk); + if (ret) + return dev_err_probe(dev, ret, + "Failed to boot RTKit: %d", ret); + + apple_rtkit_start_ep(dcp->rtk, DCP_ENDPOINT); + + dcp->shmem = dma_alloc_coherent(dev, DCP_SHMEM_SIZE, &shmem_iova, + GFP_KERNEL); + + apple_rtkit_send_message(dcp->rtk, DCP_ENDPOINT, + dcpep_set_shmem(shmem_iova), NULL, false); + + return ret; +} + +/* + * We need to shutdown DCP before tearing down the display subsystem. Otherwise + * the DCP will crash and briefly flash a green screen of death. + */ +static void dcp_platform_shutdown(struct platform_device *pdev) +{ + struct apple_dcp *dcp = platform_get_drvdata(pdev); + + struct dcp_set_power_state_req req = { + /* defaults are ok */ + }; + + dcp_set_power_state(dcp, false, &req, NULL, NULL); +} + +static const struct of_device_id of_match[] = { + { .compatible = "apple,dcp" }, + {} +}; +MODULE_DEVICE_TABLE(of, of_match); + +static struct platform_driver apple_platform_driver = { + .probe = dcp_platform_probe, + .shutdown = dcp_platform_shutdown, + .driver = { + .name = "apple-dcp", + .of_match_table = of_match, + }, +}; + +module_platform_driver(apple_platform_driver); + +MODULE_AUTHOR("Alyssa Rosenzweig "); +MODULE_DESCRIPTION("Apple Display Controller DRM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h new file mode 100644 index 00000000000000..4582fe984c8aa5 --- /dev/null +++ b/drivers/gpu/drm/apple/dcp.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2021 Alyssa Rosenzweig */ + +#ifndef __APPLE_DCP_H__ +#define __APPLE_DCP_H__ + +#include +#include "parser.h" + +struct apple_crtc { + struct drm_crtc base; + struct drm_pending_vblank_event *event; + bool vsync_disabled; + + /* Reference to the DCP device owning this CRTC */ + struct platform_device *dcp; +}; + +#define to_apple_crtc(x) container_of(x, struct apple_crtc, base) + +void dcp_hotplug(struct work_struct *work); + +struct apple_connector { + struct drm_connector base; + bool connected; + + struct platform_device *dcp; + + /* Workqueue for sending hotplug events to the associated device */ + struct work_struct hotplug_wq; +}; + +#define to_apple_connector(x) container_of(x, struct apple_connector, base) + +void dcp_link(struct platform_device *pdev, struct apple_crtc *apple, + struct apple_connector *connector); +void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state); +bool dcp_is_initialized(struct platform_device *pdev); +void apple_crtc_vblank(struct apple_crtc *apple); +int dcp_get_modes(struct drm_connector *connector); +int dcp_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode); + +#endif diff --git a/drivers/gpu/drm/apple/dcpep.h b/drivers/gpu/drm/apple/dcpep.h new file mode 100644 index 00000000000000..6301bf8f8c21d1 --- /dev/null +++ b/drivers/gpu/drm/apple/dcpep.h @@ -0,0 +1,406 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2021 Alyssa Rosenzweig */ + +#ifndef __APPLE_DCPEP_H__ +#define __APPLE_DCPEP_H__ + +/* Endpoint for general DCP traffic (dcpep in macOS) */ +#define DCP_ENDPOINT 0x37 + +/* Fixed size of shared memory between DCP and AP */ +#define DCP_SHMEM_SIZE 0x100000 + +/* DCP message contexts */ +enum dcp_context_id { + /* Callback */ + DCP_CONTEXT_CB = 0, + + /* Command */ + DCP_CONTEXT_CMD = 2, + + /* Asynchronous */ + DCP_CONTEXT_ASYNC = 3, + + /* Out-of-band callback */ + DCP_CONTEXT_OOBCB = 4, + + /* Out-of-band command */ + DCP_CONTEXT_OOBCMD = 6, + + DCP_NUM_CONTEXTS +}; + +static int dcp_tx_offset(enum dcp_context_id id) +{ + switch (id) { + case DCP_CONTEXT_CB: + case DCP_CONTEXT_CMD: return 0x00000; + case DCP_CONTEXT_OOBCB: + case DCP_CONTEXT_OOBCMD: return 0x08000; + default: return -EINVAL; + } +} + +static int dcp_channel_offset(enum dcp_context_id id) +{ + switch (id) { + case DCP_CONTEXT_ASYNC: return 0x40000; + case DCP_CONTEXT_CB: return 0x60000; + case DCP_CONTEXT_OOBCB: return 0x68000; + default: return dcp_tx_offset(id); + } +} + +/* RTKit endpoint message types */ +enum dcpep_type { + /* Set shared memory */ + DCPEP_TYPE_SET_SHMEM = 0, + + /* DCP is initialized */ + DCPEP_TYPE_INITIALIZED = 1, + + /* Remote procedure call */ + DCPEP_TYPE_MESSAGE = 2, +}; + +/* Message */ +#define DCPEP_TYPE_SHIFT (0) +#define DCPEP_TYPE_MASK GENMASK(1, 0) +#define DCPEP_ACK BIT_ULL(6) +#define DCPEP_CONTEXT_SHIFT (8) +#define DCPEP_CONTEXT_MASK GENMASK(11, 8) +#define DCPEP_OFFSET_SHIFT (16) +#define DCPEP_OFFSET_MASK GENMASK(31, 16) +#define DCPEP_LENGTH_SHIFT (32) + +/* Set shmem */ +#define DCPEP_DVA_SHIFT (16) +#define DCPEP_FLAG_SHIFT (4) +#define DCPEP_FLAG_VALUE (4) + +struct dcp_packet_header { + char tag[4]; + u32 in_len; + u32 out_len; +} __packed; + +#define DCP_IS_NULL(ptr) ((ptr) ? 1 : 0) +#define DCP_PACKET_ALIGNMENT (0x40) + +static inline u64 +dcpep_set_shmem(u64 dart_va) +{ + return (DCPEP_TYPE_SET_SHMEM << DCPEP_TYPE_SHIFT) | + (DCPEP_FLAG_VALUE << DCPEP_FLAG_SHIFT) | + (dart_va << DCPEP_DVA_SHIFT); +} + +static inline u64 +dcpep_msg(enum dcp_context_id id, u32 length, u16 offset) +{ + return (DCPEP_TYPE_MESSAGE << DCPEP_TYPE_SHIFT) | + ((u64) id << DCPEP_CONTEXT_SHIFT) | + ((u64) offset << DCPEP_OFFSET_SHIFT) | + ((u64) length << DCPEP_LENGTH_SHIFT); +} + +static inline u64 +dcpep_ack(enum dcp_context_id id) +{ + return dcpep_msg(id, 0, 0) | DCPEP_ACK; +} + +/* Structures used in v12.0 firmware */ + +#define SWAP_SURFACES 4 +#define MAX_PLANES 3 + +struct dcp_iouserclient { + /* Handle for the IOUserClient. macOS sets this to a kernel VA. */ + u64 handle; + u32 unk; + u8 flag1; + u8 flag2; + u8 padding[2]; +} __packed; + +struct dcp_rect { + u32 x; + u32 y; + u32 w; + u32 h; +} __packed; + +/* + * Set in the swap_{enabled,completed} field to remove missing + * layers. Without this flag, the DCP will assume missing layers have + * not changed since the previous frame and will preserve their + * content. + */ +#define DCP_REMOVE_LAYERS BIT(31) + +struct dcp_swap { + u64 ts1; + u64 ts2; + u64 unk_10[6]; + u64 flags1; + u64 flags2; + + u32 swap_id; + + u32 surf_ids[SWAP_SURFACES]; + struct dcp_rect src_rect[SWAP_SURFACES]; + u32 surf_flags[SWAP_SURFACES]; + u32 surf_unk[SWAP_SURFACES]; + struct dcp_rect dst_rect[SWAP_SURFACES]; + u32 swap_enabled; + u32 swap_completed; + + u32 unk_10c; + u8 unk_110[0x1b8]; + u32 unk_2c8; + u8 unk_2cc[0x14]; + u32 unk_2e0; + u8 unk_2e4[0x3c]; +} __packed; + +/* Information describing a plane of a planar compressed surface */ +struct dcp_plane_info { + u32 width; + u32 height; + u32 base; + u32 offset; + u32 stride; + u32 size; + u16 tile_size; + u8 tile_w; + u8 tile_h; + u32 unk[13]; +} __packed; + +struct dcp_component_types { + u8 count; + u8 types[7]; +} __packed; + +/* Information describing a surface */ +struct dcp_surface { + u8 is_tiled; + u8 unk_1; + u8 unk_2; + u32 plane_cnt; + u32 plane_cnt2; + u32 format; /* DCP fourcc */ + u32 unk_f; + u8 xfer_func; + u8 colorspace; + u32 stride; + u16 pix_size; + u8 pel_w; + u8 pel_h; + u32 offset; + u32 width; + u32 height; + u32 buf_size; + u32 unk_2d; + u32 unk_31; + u32 surface_id; + struct dcp_component_types comp_types[MAX_PLANES]; + u64 has_comp; + struct dcp_plane_info planes[MAX_PLANES]; + u64 has_planes; + u32 compression_info[MAX_PLANES][13]; + u64 has_compr_info; + u64 unk_1f5; + u8 padding[7]; +} __packed; + +struct dcp_rt_bandwidth { + u64 unk1; + u64 reg_scratch; + u64 reg_doorbell; + u32 unk2; + u32 doorbell_bit; + u32 padding[7]; +} __packed; + +/* Method calls */ + +enum dcpep_method { + dcpep_late_init_signal, + dcpep_setup_video_limits, + dcpep_set_create_dfb, + dcpep_start_signal, + dcpep_swap_start, + dcpep_swap_submit, + dcpep_set_display_device, + dcpep_set_digital_out_mode, + dcpep_create_default_fb, + dcpep_set_display_refresh_properties, + dcpep_flush_supports_power, + dcpep_set_power_state, + dcpep_first_client_open, + dcpep_update_notify_clients_dcp, + dcpep_num_methods +}; + +struct dcp_method_entry { + const char *name; + char tag[4]; +}; + +/* Prototypes */ + +struct dcp_set_digital_out_mode_req { + u32 color_mode_id; + u32 timing_mode_id; +} __packed; + +struct dcp_map_buf_req { + u64 buffer; + u8 unk; + u8 buf_null; + u8 vaddr_null; + u8 dva_null; +} __packed; + +struct dcp_map_buf_resp { + u64 vaddr; + u64 dva; + u32 ret; +} __packed; + +struct dcp_allocate_buffer_req { + u32 unk0; + u64 size; + u32 unk2; + u8 paddr_null; + u8 dva_null; + u8 dva_size_null; + u8 padding; +} __packed; + +struct dcp_allocate_buffer_resp { + u64 paddr; + u64 dva; + u64 dva_size; + u32 mem_desc_id; +} __packed; + +struct dcp_map_physical_req { + u64 paddr; + u64 size; + u32 flags; + u8 dva_null; + u8 dva_size_null; + u8 padding[2]; +} __packed; + +struct dcp_map_physical_resp { + u64 dva; + u64 dva_size; + u32 mem_desc_id; +} __packed; + +struct dcp_map_reg_req { + char obj[4]; + u32 index; + u32 flags; + u8 addr_null; + u8 length_null; + u8 padding[2]; +} __packed; + +struct dcp_map_reg_resp { + u64 addr; + u64 length; + u32 ret; +} __packed; + +struct dcp_swap_start_req { + u32 swap_id; + struct dcp_iouserclient client; + u8 swap_id_null; + u8 client_null; + u8 padding[2]; +} __packed; + +struct dcp_swap_start_resp { + u32 swap_id; + struct dcp_iouserclient client; + u32 ret; +} __packed; + +struct dcp_swap_submit_req { + struct dcp_swap swap; + struct dcp_surface surf[SWAP_SURFACES]; + u64 surf_iova[SWAP_SURFACES]; + u8 unkbool; + u64 unkdouble; + u32 unkint; + u8 swap_null; + u8 surf_null[SWAP_SURFACES]; + u8 unkoutbool_null; + u8 padding[1]; +} __packed; + +struct dcp_swap_submit_resp { + u8 unkoutbool; + u32 ret; + u8 padding[3]; +} __packed; + +struct dcp_get_uint_prop_req { + char obj[4]; + char key[0x40]; + u64 value; + u8 value_null; + u8 padding[3]; +} __packed; + +struct dcp_get_uint_prop_resp { + u64 value; + u8 ret; + u8 padding[3]; +} __packed; + +struct dcp_set_power_state_req { + u64 unklong; + u8 unkbool; + u8 unkint_null; + u8 padding[2]; +} __packed; + +struct dcp_set_power_state_resp { + u32 unkint; + u32 ret; +} __packed; + +struct dcp_set_dcpav_prop_chunk_req { + char data[0x1000]; + u32 offset; + u32 length; +} __packed; + +struct dcp_set_dcpav_prop_end_req { + char key[0x40]; +} __packed; + +struct dcp_update_notify_clients_dcp { + u32 client_0; + u32 client_1; + u32 client_2; + u32 client_3; + u32 client_4; + u32 client_5; + u32 client_6; + u32 client_7; + u32 client_8; + u32 client_9; + u32 client_a; + u32 client_b; + u32 client_c; + u32 client_d; +} __packed; + +#endif diff --git a/drivers/gpu/drm/apple/dummy-piodma.c b/drivers/gpu/drm/apple/dummy-piodma.c new file mode 100644 index 00000000000000..a84a28b4dc2ae0 --- /dev/null +++ b/drivers/gpu/drm/apple/dummy-piodma.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright 2021 Alyssa Rosenzweig */ + +#include +#include +#include + +static int dcp_piodma_probe(struct platform_device *pdev) +{ + return dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36)); +} + +static const struct of_device_id of_match[] = { + { .compatible = "apple,dcp-piodma" }, + {} +}; +MODULE_DEVICE_TABLE(of, of_match); + +static struct platform_driver dcp_piodma_platform_driver = { + .probe = dcp_piodma_probe, + .driver = { + .name = "apple,dcp-piodma", + .of_match_table = of_match, + }, +}; + +module_platform_driver(dcp_piodma_platform_driver); + +MODULE_AUTHOR("Alyssa Rosenzweig "); +MODULE_DESCRIPTION("[HACK] Apple DCP PIODMA shim"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c new file mode 100644 index 00000000000000..7205179e7785aa --- /dev/null +++ b/drivers/gpu/drm/apple/parser.c @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright 2021 Alyssa Rosenzweig */ + +#include +#include +#include +#include +#include +#include "parser.h" + +#define DCP_PARSE_HEADER 0xd3 + +enum dcp_parse_type { + DCP_TYPE_DICTIONARY = 1, + DCP_TYPE_ARRAY = 2, + DCP_TYPE_INT64 = 4, + DCP_TYPE_STRING = 9, + DCP_TYPE_BLOB = 10, + DCP_TYPE_BOOL = 11 +}; + +struct dcp_parse_tag { + unsigned int size : 24; + enum dcp_parse_type type : 5; + unsigned int padding : 2; + bool last : 1; +} __packed; + +static void *parse_bytes(struct dcp_parse_ctx *ctx, size_t count) +{ + void *ptr = ctx->blob + ctx->pos; + + if (ctx->pos + count > ctx->len) + return ERR_PTR(-EINVAL); + + ctx->pos += count; + return ptr; +} + +static u32 *parse_u32(struct dcp_parse_ctx *ctx) +{ + return parse_bytes(ctx, sizeof(u32)); +} + +static struct dcp_parse_tag *parse_tag(struct dcp_parse_ctx *ctx) +{ + struct dcp_parse_tag *tag; + + /* Align to 32-bits */ + ctx->pos = round_up(ctx->pos, 4); + + tag = parse_bytes(ctx, sizeof(struct dcp_parse_tag)); + + if (IS_ERR(tag)) + return tag; + + if (tag->padding) + return ERR_PTR(-EINVAL); + + return tag; +} + +static struct dcp_parse_tag *parse_tag_of_type(struct dcp_parse_ctx *ctx, + enum dcp_parse_type type) +{ + struct dcp_parse_tag *tag = parse_tag(ctx); + + if (IS_ERR(tag)) + return tag; + + if (tag->type != type) + return ERR_PTR(-EINVAL); + + return tag; +} + +static int skip(struct dcp_parse_ctx *handle) +{ + struct dcp_parse_tag *tag = parse_tag(handle); + int ret = 0; + int i; + + if (IS_ERR(tag)) + return PTR_ERR(tag); + + switch (tag->type) { + case DCP_TYPE_DICTIONARY: + for (i = 0; i < tag->size; ++i) { + ret |= skip(handle); /* key */ + ret |= skip(handle); /* value */ + } + + return ret; + + case DCP_TYPE_ARRAY: + for (i = 0; i < tag->size; ++i) + ret |= skip(handle); + + return ret; + + case DCP_TYPE_INT64: + handle->pos += sizeof(s64); + return 0; + + case DCP_TYPE_STRING: + case DCP_TYPE_BLOB: + handle->pos += tag->size; + return 0; + + case DCP_TYPE_BOOL: + return 0; + + default: + return -EINVAL; + } +} + +/* Caller must free the result */ +static char *parse_string(struct dcp_parse_ctx *handle) +{ + struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_STRING); + const char *in; + char *out; + + if (IS_ERR(tag)) + return (void *)tag; + + in = parse_bytes(handle, tag->size); + if (IS_ERR(in)) + return (void *)in; + + out = kmalloc(tag->size + 1, GFP_KERNEL); + + memcpy(out, in, tag->size); + out[tag->size] = '\0'; + return out; +} + +static int parse_int(struct dcp_parse_ctx *handle, s64 *value) +{ + void *tag = parse_tag_of_type(handle, DCP_TYPE_INT64); + s64 *in; + + if (IS_ERR(tag)) + return PTR_ERR(tag); + + in = parse_bytes(handle, sizeof(s64)); + + if (IS_ERR(in)) + return PTR_ERR(in); + + memcpy(value, in, sizeof(*value)); + return 0; +} + +static int parse_bool(struct dcp_parse_ctx *handle, bool *b) +{ + struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_BOOL); + + if (IS_ERR(tag)) + return PTR_ERR(tag); + + *b = !!tag->size; + return 0; +} + +struct iterator { + struct dcp_parse_ctx *handle; + u32 idx, len; +}; + +static int iterator_begin(struct dcp_parse_ctx *handle, struct iterator *it, + bool dict) +{ + struct dcp_parse_tag *tag; + enum dcp_parse_type type = dict ? DCP_TYPE_DICTIONARY : DCP_TYPE_ARRAY; + + *it = (struct iterator) { + .handle = handle, + .idx = 0 + }; + + tag = parse_tag_of_type(it->handle, type); + if (IS_ERR(tag)) + return PTR_ERR(tag); + + it->len = tag->size; + return 0; +} + +#define dcp_parse_foreach_in_array(handle, it) \ + for (iterator_begin(handle, &it, false); it.idx < it.len; ++it.idx) +#define dcp_parse_foreach_in_dict(handle, it) \ + for (iterator_begin(handle, &it, true); it.idx < it.len; ++it.idx) + +int parse(void *blob, size_t size, struct dcp_parse_ctx *ctx) +{ + u32 *header; + + *ctx = (struct dcp_parse_ctx) { + .blob = blob, + .len = size, + .pos = 0, + }; + + header = parse_u32(ctx); + if (IS_ERR(header)) + return PTR_ERR(header); + + if (*header != DCP_PARSE_HEADER) + return -EINVAL; + + return 0; +} + +struct dimension { + s64 total, front_porch, sync_width, active; + s64 precise_sync_rate; +}; + +static int parse_dimension(struct dcp_parse_ctx *handle, struct dimension *dim) +{ + struct iterator it; + int ret = 0; + + dcp_parse_foreach_in_dict(handle, it) { + char *key = parse_string(it.handle); + + if (IS_ERR(key)) + ret = PTR_ERR(handle); + else if (!strcmp(key, "Active")) + ret = parse_int(it.handle, &dim->active); + else if (!strcmp(key, "Total")) + ret = parse_int(it.handle, &dim->total); + else if (!strcmp(key, "FrontPorch")) + ret = parse_int(it.handle, &dim->front_porch); + else if (!strcmp(key, "SyncWidth")) + ret = parse_int(it.handle, &dim->sync_width); + else if (!strcmp(key, "PreciseSyncRate")) + ret = parse_int(it.handle, &dim->precise_sync_rate); + else + skip(it.handle); + + if (ret) + return ret; + } + + return 0; +} + +static int parse_color_modes(struct dcp_parse_ctx *handle, s64 *best_id) +{ + struct iterator outer_it; + int ret = 0; + s64 best_score = -1; + + *best_id = -1; + + dcp_parse_foreach_in_array(handle, outer_it) { + struct iterator it; + s64 score = -1, id = -1; + + dcp_parse_foreach_in_dict(handle, it) { + char *key = parse_string(it.handle); + + if (IS_ERR(key)) + ret = PTR_ERR(key); + else if (!strcmp(key, "Score")) + ret = parse_int(it.handle, &score); + else if (!strcmp(key, "ID")) + ret = parse_int(it.handle, &id); + else + skip(it.handle); + + if (ret) + return ret; + } + + /* Skip partial entries */ + if (score < 0 || id < 0) + continue; + + if (score > best_score) { + best_score = score; + *best_id = id; + } + } + + return 0; +} + +/* + * Calculate the pixel clock for a mode given the 16:16 fixed-point refresh + * rate. The pixel clock is the refresh rate times the pixel count. DRM + * specifies the clock in kHz. The intermediate result may overflow a u32, so + * use a u64 where required. + */ +static u32 calculate_clock(struct dimension *horiz, struct dimension *vert) +{ + u32 pixels = horiz->total * vert->total; + u64 clock = mul_u32_u32(pixels, vert->precise_sync_rate); + + return DIV_ROUND_CLOSEST_ULL(clock >> 16, 1000); +} + +static int parse_mode(struct dcp_parse_ctx *handle, + struct dcp_display_mode *out, s64 *score, int width_mm, + int height_mm) +{ + int ret = 0; + struct iterator it; + struct dimension horiz, vert; + s64 id = -1; + s64 best_color_mode = -1; + bool is_virtual = false; + struct drm_display_mode *mode = &out->mode; + + dcp_parse_foreach_in_dict(handle, it) { + char *key = parse_string(it.handle); + + if (IS_ERR(key)) + ret = PTR_ERR(key); + else if (!strcmp(key, "HorizontalAttributes")) + ret = parse_dimension(it.handle, &horiz); + else if (!strcmp(key, "VerticalAttributes")) + ret = parse_dimension(it.handle, &vert); + else if (!strcmp(key, "ColorModes")) + ret = parse_color_modes(it.handle, &best_color_mode); + else if (!strcmp(key, "ID")) + ret = parse_int(it.handle, &id); + else if (!strcmp(key, "IsVirtual")) + ret = parse_bool(it.handle, &is_virtual); + else if (!strcmp(key, "Score")) + ret = parse_int(it.handle, score); + else + skip(it.handle); + + if (ret) + return ret; + } + + /* + * We need to skip virtual modes. In some cases, virtual modes are "too + * big" for the monitor and can cause breakage. It is unclear why the + * DCP reports these modes at all. Treat as a recoverable error. + */ + if (is_virtual) + return -EINVAL; + + /* From here we must succeed. Start filling out the mode. */ + *mode = (struct drm_display_mode) { + .type = DRM_MODE_TYPE_DRIVER, + .clock = calculate_clock(&horiz, &vert), + + .vdisplay = vert.active, + .vsync_start = vert.active + vert.front_porch, + .vsync_end = vert.active + vert.front_porch + vert.sync_width, + .vtotal = vert.total, + + .hdisplay = horiz.active, + .hsync_start = horiz.active + horiz.front_porch, + .hsync_end = horiz.active + horiz.front_porch + + horiz.sync_width, + .htotal = horiz.total, + + .width_mm = width_mm, + .height_mm = height_mm, + }; + + drm_mode_set_name(mode); + + out->timing_mode_id = id; + out->color_mode_id = best_color_mode; + + return 0; +} + +struct dcp_display_mode *enumerate_modes(struct dcp_parse_ctx *handle, + unsigned int *count, int width_mm, + int height_mm) +{ + struct iterator it; + int ret; + struct dcp_display_mode *mode, *modes; + struct dcp_display_mode *best_mode = NULL; + s64 score, best_score = -1; + + ret = iterator_begin(handle, &it, false); + + if (ret) + return ERR_PTR(ret); + + /* Start with a worst case allocation */ + modes = kmalloc_array(it.len, sizeof(*modes), GFP_KERNEL); + *count = 0; + + if (!modes) + return ERR_PTR(-ENOMEM); + + for (; it.idx < it.len; ++it.idx) { + mode = &modes[*count]; + ret = parse_mode(it.handle, mode, &score, width_mm, height_mm); + + /* Errors for a single mode are recoverable -- just skip it. */ + if (ret) + continue; + + /* Process a successful mode */ + (*count)++; + + if (score > best_score) { + best_score = score; + best_mode = mode; + } + } + + if (best_mode != NULL) + best_mode->mode.type |= DRM_MODE_TYPE_PREFERRED; + + return modes; +} + +int parse_display_attributes(struct dcp_parse_ctx *handle, int *width_mm, + int *height_mm) +{ + int ret = 0; + struct iterator it; + s64 width_cm = 0, height_cm = 0; + + dcp_parse_foreach_in_dict(handle, it) { + char *key = parse_string(it.handle); + + if (IS_ERR(key)) + ret = PTR_ERR(key); + else if (!strcmp(key, "MaxHorizontalImageSize")) + ret = parse_int(it.handle, &width_cm); + else if (!strcmp(key, "MaxVerticalImageSize")) + ret = parse_int(it.handle, &height_cm); + else + skip(it.handle); + + if (ret) + return ret; + } + + /* 1cm = 10mm */ + *width_mm = 10 * width_cm; + *height_mm = 10 * height_cm; + + return 0; +} diff --git a/drivers/gpu/drm/apple/parser.h b/drivers/gpu/drm/apple/parser.h new file mode 100644 index 00000000000000..afe3ed4700add7 --- /dev/null +++ b/drivers/gpu/drm/apple/parser.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2021 Alyssa Rosenzweig */ + +#ifndef __APPLE_DCP_PARSER_H__ +#define __APPLE_DCP_PARSER_H__ + +/* For mode parsing */ +#include + +struct dcp_parse_ctx { + void *blob; + u32 pos, len; +}; + +/* + * Represents a single display mode. These mode objects are populated at + * runtime based on the TimingElements dictionary sent by the DCP. + */ +struct dcp_display_mode { + struct drm_display_mode mode; + u32 color_mode_id; + u32 timing_mode_id; +}; + +int parse(void *blob, size_t size, struct dcp_parse_ctx *ctx); +struct dcp_display_mode *enumerate_modes(struct dcp_parse_ctx *handle, + unsigned int *count, int width_mm, + int height_mm); +int parse_display_attributes(struct dcp_parse_ctx *handle, int *width_mm, + int *height_mm); + +#endif From cd6fbd5129e8241d9b15a07f534137311d74a4ad Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 20 Mar 2022 18:44:00 +0100 Subject: [PATCH 0423/1020] drm: apple: Relicense DCP driver as dual MIT / GPL v2.0 Link: https://oftc.irclog.whitequark.org/asahi-dev/2022-03-20#30747564 Link: https://oftc.irclog.whitequark.org/asahi-dev/2022-03-20#30747570 Signed-off-by: Alyssa Rosenzweig Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/Kconfig | 2 +- drivers/gpu/drm/apple/Makefile | 2 +- drivers/gpu/drm/apple/apple_drv.c | 4 ++-- drivers/gpu/drm/apple/dcp.c | 4 ++-- drivers/gpu/drm/apple/dcp.h | 2 +- drivers/gpu/drm/apple/dcpep.h | 2 +- drivers/gpu/drm/apple/dummy-piodma.c | 4 ++-- drivers/gpu/drm/apple/parser.c | 2 +- drivers/gpu/drm/apple/parser.h | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/apple/Kconfig b/drivers/gpu/drm/apple/Kconfig index 53c7d4edfd017e..9b9bcb7b5433e0 100644 --- a/drivers/gpu/drm/apple/Kconfig +++ b/drivers/gpu/drm/apple/Kconfig @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0-only +# SPDX-License-Identifier: GPL-2.0-only OR MIT config DRM_APPLE tristate "DRM Support for Apple display controllers" depends on DRM && OF && ARM64 diff --git a/drivers/gpu/drm/apple/Makefile b/drivers/gpu/drm/apple/Makefile index db7ef359987d3d..8c758d5720b642 100644 --- a/drivers/gpu/drm/apple/Makefile +++ b/drivers/gpu/drm/apple/Makefile @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0-only +# SPDX-License-Identifier: GPL-2.0-only OR MIT appledrm-y := apple_drv.o apple_dcp-y := dcp.o parser.o diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index c0ad2e3d426909..356cf39cc1e7ae 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2021 Alyssa Rosenzweig */ /* Based on meson driver which is * Copyright (C) 2016 BayLibre, SAS @@ -437,4 +437,4 @@ module_platform_driver(apple_platform_driver); MODULE_AUTHOR("Alyssa Rosenzweig "); MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index d43ed9021bd64c..9f4f13e01c138a 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2021 Alyssa Rosenzweig */ #include @@ -1390,4 +1390,4 @@ module_platform_driver(apple_platform_driver); MODULE_AUTHOR("Alyssa Rosenzweig "); MODULE_DESCRIPTION("Apple Display Controller DRM driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h index 4582fe984c8aa5..794544456df963 100644 --- a/drivers/gpu/drm/apple/dcp.h +++ b/drivers/gpu/drm/apple/dcp.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +// SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2021 Alyssa Rosenzweig */ #ifndef __APPLE_DCP_H__ diff --git a/drivers/gpu/drm/apple/dcpep.h b/drivers/gpu/drm/apple/dcpep.h index 6301bf8f8c21d1..f3d62d1d5f0328 100644 --- a/drivers/gpu/drm/apple/dcpep.h +++ b/drivers/gpu/drm/apple/dcpep.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +// SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2021 Alyssa Rosenzweig */ #ifndef __APPLE_DCPEP_H__ diff --git a/drivers/gpu/drm/apple/dummy-piodma.c b/drivers/gpu/drm/apple/dummy-piodma.c index a84a28b4dc2ae0..3d4454df4a25da 100644 --- a/drivers/gpu/drm/apple/dummy-piodma.c +++ b/drivers/gpu/drm/apple/dummy-piodma.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2021 Alyssa Rosenzweig */ #include @@ -28,4 +28,4 @@ module_platform_driver(dcp_piodma_platform_driver); MODULE_AUTHOR("Alyssa Rosenzweig "); MODULE_DESCRIPTION("[HACK] Apple DCP PIODMA shim"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index 7205179e7785aa..f0cd38c2a81048 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2021 Alyssa Rosenzweig */ #include diff --git a/drivers/gpu/drm/apple/parser.h b/drivers/gpu/drm/apple/parser.h index afe3ed4700add7..66a675079dc164 100644 --- a/drivers/gpu/drm/apple/parser.h +++ b/drivers/gpu/drm/apple/parser.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +// SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2021 Alyssa Rosenzweig */ #ifndef __APPLE_DCP_PARSER_H__ From 2dae1b58f3d6d960bb8c4b6666a69e2bb28d51b5 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 25 Jul 2022 21:36:38 +0200 Subject: [PATCH 0424/1020] drm/apple: Start coprocessor on probe Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 9f4f13e01c138a..6f36138e13cebc 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -20,6 +20,9 @@ struct apple_dcp; +#define APPLE_DCP_COPROC_CPU_CONTROL 0x44 +#define APPLE_DCP_COPROC_CPU_CONTROL_RUN BIT(4) + /* Register defines used in bandwidth setup structure */ #define REG_SCRATCH (0x14) #define REG_DOORBELL (0x0) @@ -69,6 +72,9 @@ struct apple_dcp { /* DCP shared memory */ void *shmem; + /* Coprocessor control register */ + void __iomem *coproc_reg; + /* Display registers mappable to the DCP */ struct resource *disp_registers[MAX_DISP_REGISTERS]; unsigned int nr_disp_registers; @@ -1301,9 +1307,9 @@ static int dcp_get_disp_regs(struct apple_dcp *dcp) static int dcp_platform_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct resource *res; struct apple_dcp *dcp; dma_addr_t shmem_iova; + u32 cpu_ctrl; int ret; dcp = devm_kzalloc(dev, sizeof(*dcp), GFP_KERNEL); @@ -1317,9 +1323,9 @@ static int dcp_platform_probe(struct platform_device *pdev) if (ret) return ret; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "coproc"); - if (!res) - return -EINVAL; + dcp->coproc_reg = devm_platform_ioremap_resource_byname(pdev, "coproc"); + if (IS_ERR(dcp->coproc_reg)) + return PTR_ERR(dcp->coproc_reg); of_platform_default_populate(dev->of_node, NULL, dev); @@ -1335,6 +1341,10 @@ static int dcp_platform_probe(struct platform_device *pdev) return ret; } + cpu_ctrl = readl_relaxed(dcp->coproc_reg + APPLE_DCP_COPROC_CPU_CONTROL); + writel_relaxed(cpu_ctrl | APPLE_DCP_COPROC_CPU_CONTROL_RUN, + dcp->coproc_reg + APPLE_DCP_COPROC_CPU_CONTROL); + dcp->rtk = devm_apple_rtkit_init(dev, dcp, "mbox", 0, &rtkit_ops); if (IS_ERR(dcp->rtk)) return dev_err_probe(dev, PTR_ERR(dcp->rtk), From d777435b6ab2864a8e3a570c88f7b408e1137f60 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 15 Jan 2022 18:29:55 +0100 Subject: [PATCH 0425/1020] HACK: drm/apple: avoid DCP swaps without attached surfaces Xorg startup with modesetting driver triggers this. Move vblank signalling to dcp to avoid a circular dependency between apple_drv and dcp. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 18 ---------- drivers/gpu/drm/apple/dcp.c | 60 +++++++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 356cf39cc1e7ae..660355261d04d2 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -208,24 +208,6 @@ static void apple_crtc_atomic_begin(struct drm_crtc *crtc, } } -void apple_crtc_vblank(struct apple_crtc *crtc) -{ - unsigned long flags; - - if (crtc->vsync_disabled) - return; - - drm_crtc_handle_vblank(&crtc->base); - - spin_lock_irqsave(&crtc->base.dev->event_lock, flags); - if (crtc->event) { - drm_crtc_send_vblank_event(&crtc->base, crtc->event); - drm_crtc_vblank_put(&crtc->base); - crtc->event = NULL; - } - spin_unlock_irqrestore(&crtc->base.dev->event_lock, flags); -} - static const struct drm_crtc_funcs apple_crtc_funcs = { .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 6f36138e13cebc..5b2edf2579f4bd 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include "dcpep.h" #include "dcp.h" @@ -107,6 +109,9 @@ struct apple_dcp { /* Attributes of the connected display */ int width_mm, height_mm; + + /* Workqueue for sending vblank events when a dcp swap is not possible */ + struct work_struct vblank_wq; }; /* @@ -363,9 +368,28 @@ static u32 dcpep_cb_zero(struct apple_dcp *dcp) return 0; } +/* HACK: moved here to avoid circular dependency between apple_drv and dcp */ +void dcp_drm_crtc_vblank(struct apple_crtc *crtc) +{ + unsigned long flags; + + if (crtc->vsync_disabled) + return; + + drm_crtc_handle_vblank(&crtc->base); + + spin_lock_irqsave(&crtc->base.dev->event_lock, flags); + if (crtc->event) { + drm_crtc_send_vblank_event(&crtc->base, crtc->event); + drm_crtc_vblank_put(&crtc->base); + crtc->event = NULL; + } + spin_unlock_irqrestore(&crtc->base.dev->event_lock, flags); +} + static void dcpep_cb_swap_complete(struct apple_dcp *dcp) { - apple_crtc_vblank(dcp->crtc); + dcp_drm_crtc_vblank(dcp->crtc); } static struct dcp_get_uint_prop_resp @@ -731,6 +755,21 @@ static void dcpep_cb_hotplug(struct apple_dcp *dcp, u64 *connected) } } +/* + * Helper to send a DRM vblank event. We do not know how call swap_submit_dcp + * without surfaces. To avoid timeouts in drm_atomic_helper_wait_for_vblanks + * send a vblank event via a workqueue. + */ +static void dcp_delayed_vblank(struct work_struct *work) +{ + struct apple_dcp *dcp; + + dcp = container_of(work, struct apple_dcp, vblank_wq); + mdelay(5); + dcp_drm_crtc_vblank(dcp->crtc); +} + + #define DCPEP_MAX_CB (1000) /* @@ -943,7 +982,7 @@ static void dcp_swapped(struct apple_dcp *dcp, void *data, void *cookie) if (resp->ret) { dev_err(dcp->dev, "swap failed! status %u\n", resp->ret); - apple_crtc_vblank(dcp->crtc); + dcp_drm_crtc_vblank(dcp->crtc); } } @@ -1061,12 +1100,16 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) struct drm_crtc_state *crtc_state; struct dcp_swap_submit_req *req = &dcp->swap; int l; + int has_surface = 0; crtc_state = drm_atomic_get_new_crtc_state(state, crtc); if (WARN(dcp_channel_busy(&dcp->ch_cmd), "unexpected busy channel") || WARN(!dcp->connector->connected, "can't flush if disconnected")) { - apple_crtc_vblank(dcp->crtc); + /* HACK: issue a delayed vblank event to avoid timeouts in + * drm_atomic_helper_wait_for_vblanks(). + */ + schedule_work(&dcp->vblank_wq); return; } @@ -1090,6 +1133,7 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) continue; } req->surf_null[l] = false; + has_surface = 1; // XXX: awful hack! race condition between a framebuffer unbind // getting swapped out and GEM unreferencing a framebuffer. If @@ -1148,6 +1192,14 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) dcp->valid_mode = true; dcp_set_display_device(dcp, false, &handle, dcp_modeset, NULL); + } + else if (!has_surface) { + dev_warn(dcp->dev, "can't flush without surfaces, vsync:%d", dcp->crtc->vsync_disabled); + /* HACK: issue a delayed vblank event to avoid timeouts in + * drm_atomic_helper_wait_for_vblanks(). It's currently unkown + * if and how DCP supports swaps without attached surfaces. + */ + schedule_work(&dcp->vblank_wq); } else do_swap(dcp, NULL, NULL); } @@ -1341,6 +1393,8 @@ static int dcp_platform_probe(struct platform_device *pdev) return ret; } + INIT_WORK(&dcp->vblank_wq, dcp_delayed_vblank); + cpu_ctrl = readl_relaxed(dcp->coproc_reg + APPLE_DCP_COPROC_CPU_CONTROL); writel_relaxed(cpu_ctrl | APPLE_DCP_COPROC_CPU_CONTROL_RUN, dcp->coproc_reg + APPLE_DCP_COPROC_CPU_CONTROL); From afdd5cad15dac5ebb205afb62a2798c54482af1a Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 31 Jul 2022 18:34:58 +0200 Subject: [PATCH 0426/1020] drm/apple: Use a device tree defined clock for dcpep_cb_get_frequency Frequency differs between M1 and M1 Pro/Max/Ultra. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 5b2edf2579f4bd..7535e817f90846 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2021 Alyssa Rosenzweig */ +#include #include #include #include @@ -71,6 +72,9 @@ struct apple_dcp { /* DCP has crashed */ bool crashed; + /* clock rate request by dcp in */ + struct clk *clk; + /* DCP shared memory */ void *shmem; @@ -511,8 +515,7 @@ dcpep_cb_map_physical(struct apple_dcp *dcp, struct dcp_map_physical_req *req) static u64 dcpep_cb_get_frequency(struct apple_dcp *dcp) { - /* Pixel clock frequency in Hz (compare: 4K@60 VGA clock 533.250 MHz) */ - return 533333328; + return clk_get_rate(dcp->clk); } static struct dcp_map_reg_resp @@ -1393,6 +1396,10 @@ static int dcp_platform_probe(struct platform_device *pdev) return ret; } + dcp->clk = devm_clk_get(dev, NULL); + if (IS_ERR(dcp->clk)) + return dev_err_probe(dev, PTR_ERR(dcp->clk), "Unable to find clock\n"); + INIT_WORK(&dcp->vblank_wq, dcp_delayed_vblank); cpu_ctrl = readl_relaxed(dcp->coproc_reg + APPLE_DCP_COPROC_CPU_CONTROL); From 740c4649dd4352ccc5fdc585e8e4fa73e0fb4a95 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 12 Mar 2022 11:40:32 +0100 Subject: [PATCH 0427/1020] drm/apple: Fix rt_bandwidth for t600x Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 7535e817f90846..009f6a3880e3dd 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -28,6 +28,7 @@ struct apple_dcp; /* Register defines used in bandwidth setup structure */ #define REG_SCRATCH (0x14) +#define REG_SCRATCH_T600X (0x988) #define REG_DOORBELL (0x0) #define REG_DOORBELL_BIT (2) @@ -700,13 +701,26 @@ static bool dcpep_cb_boot_1(struct apple_dcp *dcp, void *out, void *in) static struct dcp_rt_bandwidth dcpep_cb_rt_bandwidth(struct apple_dcp *dcp) { - return (struct dcp_rt_bandwidth) { - .reg_scratch = dcp->disp_registers[5]->start + REG_SCRATCH, - .reg_doorbell = dcp->disp_registers[6]->start + REG_DOORBELL, - .doorbell_bit = REG_DOORBELL_BIT, + if (dcp->disp_registers[5] && dcp->disp_registers[6]) + return (struct dcp_rt_bandwidth) { + .reg_scratch = dcp->disp_registers[5]->start + REG_SCRATCH, + .reg_doorbell = dcp->disp_registers[6]->start + REG_DOORBELL, + .doorbell_bit = REG_DOORBELL_BIT, - .padding[3] = 0x4, // XXX: required by 11.x firmware - }; + .padding[3] = 0x4, // XXX: required by 11.x firmware + }; + else if (dcp->disp_registers[4]) + return (struct dcp_rt_bandwidth) { + .reg_scratch = dcp->disp_registers[4]->start + REG_SCRATCH_T600X, + .reg_doorbell = 0, + .doorbell_bit = 0, + }; + else + return (struct dcp_rt_bandwidth) { + .reg_scratch = 0, + .reg_doorbell = 0, + .doorbell_bit = 0, + }; } /* Callback to get the current time as milliseconds since the UNIX epoch */ From 017da76918e3513d30a3ec3a72a92709b0336774 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 12 Mar 2022 11:43:04 +0100 Subject: [PATCH 0428/1020] drm/apple: Add nop sr_set_uint_prop callback for t600x-dcp Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 009f6a3880e3dd..9752c9f8abdd19 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -891,6 +891,7 @@ bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, void *, void * [211] = trampoline_nop, /* update_backlight_factor_prop */ [300] = trampoline_nop, /* pr_publish */ [401] = trampoline_get_uint_prop, + [404] = trampoline_nop, /* sr_set_uint_prop */ [406] = trampoline_nop, /* set_fx_prop */ [408] = trampoline_get_frequency, [411] = trampoline_map_reg, From d71a8dbfac15eb18b42a4e9d273aa6169d59a43c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 12 Mar 2022 11:46:11 +0100 Subject: [PATCH 0429/1020] drm/apple: Reference only swapped out framebuffers The framebuffer can be unreferenced by GEM while the display controller is still using it for scanout resulting in IOVA faults and crashed dcp. dcp has to hold a reference until the swap is complete. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 48 +++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 9752c9f8abdd19..e9db4474028dd3 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -117,6 +117,16 @@ struct apple_dcp { /* Workqueue for sending vblank events when a dcp swap is not possible */ struct work_struct vblank_wq; + + /* List of referenced drm_framebuffers which can be unreferenced + * on the next successfully completed swap. + */ + struct list_head swapped_out_fbs; +}; + +struct dcp_fb_reference { + struct list_head head; + struct drm_framebuffer *fb; }; /* @@ -1001,6 +1011,17 @@ static void dcp_swapped(struct apple_dcp *dcp, void *data, void *cookie) if (resp->ret) { dev_err(dcp->dev, "swap failed! status %u\n", resp->ret); dcp_drm_crtc_vblank(dcp->crtc); + return; + } + + while (!list_empty(&dcp->swapped_out_fbs)) { + struct dcp_fb_reference *entry; + entry = list_first_entry(&dcp->swapped_out_fbs, + struct dcp_fb_reference, head); + if (entry->fb) + drm_framebuffer_put(entry->fb); + list_del(&entry->head); + kfree(entry); } } @@ -1144,6 +1165,24 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) req->swap.swap_enabled |= BIT(l); + if (old_state->fb && fb != old_state->fb) { + /* + * Race condition between a framebuffer unbind getting + * swapped out and GEM unreferencing a framebuffer. If + * we lose the race, the display gets IOVA faults and + * the DCP crashes. We need to extend the lifetime of + * the drm_framebuffer (and hence the GEM object) until + * after we get a swap complete for the swap unbinding + * it. + */ + struct dcp_fb_reference *entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (entry) { + entry->fb = old_state->fb; + list_add_tail(&entry->head, &dcp->swapped_out_fbs); + } + drm_framebuffer_get(old_state->fb); + } + if (!new_state->fb) { if (old_state->fb) req->swap.swap_enabled |= DCP_REMOVE_LAYERS; @@ -1153,13 +1192,6 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) req->surf_null[l] = false; has_surface = 1; - // XXX: awful hack! race condition between a framebuffer unbind - // getting swapped out and GEM unreferencing a framebuffer. If - // we lose the race, the display gets IOVA faults and the DCP - // crashes. We need to extend the lifetime of the - // drm_framebuffer (and hence the GEM object) until after we - // get a swap complete for the swap unbinding it. - drm_framebuffer_get(fb); drm_rect_fp_to_int(&src_rect, &new_state->src); @@ -1417,6 +1449,8 @@ static int dcp_platform_probe(struct platform_device *pdev) INIT_WORK(&dcp->vblank_wq, dcp_delayed_vblank); + dcp->swapped_out_fbs = (struct list_head)LIST_HEAD_INIT(dcp->swapped_out_fbs); + cpu_ctrl = readl_relaxed(dcp->coproc_reg + APPLE_DCP_COPROC_CPU_CONTROL); writel_relaxed(cpu_ctrl | APPLE_DCP_COPROC_CPU_CONTROL_RUN, dcp->coproc_reg + APPLE_DCP_COPROC_CPU_CONTROL); From 3c569d06df05864f11955e323c76234606965358 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 12 Mar 2022 12:48:34 +0100 Subject: [PATCH 0430/1020] drm/apple: Use "apple,asc-dram-mask" for rtkit iovas Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index e9db4474028dd3..7f5ce8db92620a 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -76,6 +76,9 @@ struct apple_dcp { /* clock rate request by dcp in */ struct clk *clk; + /* mask for DCP IO virtual addresses shared over rtkit */ + u64 asc_dram_mask; + /* DCP shared memory */ void *shmem; @@ -481,6 +484,7 @@ dcpep_cb_allocate_buffer(struct apple_dcp *dcp, struct dcp_allocate_buffer_req * dma_get_sgtable(dcp->dev, &dcp->mappings[resp.mem_desc_id], buf, resp.dva, resp.dva_size); + resp.dva |= dcp->asc_dram_mask; return resp; } @@ -520,7 +524,7 @@ dcpep_cb_map_physical(struct apple_dcp *dcp, struct dcp_map_physical_req *req) .dva_size = size, .mem_desc_id = ++dcp->nr_mappings, .dva = dma_map_resource(dcp->dev, req->paddr, size, - DMA_BIDIRECTIONAL, 0), + DMA_BIDIRECTIONAL, 0) | dcp->asc_dram_mask, }; } @@ -1324,7 +1328,7 @@ static int dcp_rtk_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr) return -ENOMEM; // TODO: get map from device-tree - phy_addr = iommu_iova_to_phys(domain, bfr->iova & 0xFFFFFFFF); + phy_addr = iommu_iova_to_phys(domain, bfr->iova & ~dcp->asc_dram_mask); if (!phy_addr) return -ENOMEM; @@ -1341,6 +1345,8 @@ static int dcp_rtk_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr) if (!bfr->buffer) return -ENOMEM; + bfr->iova |= dcp->asc_dram_mask; + dev_info(dcp->dev, "shmem_setup: iova: %lx, buffer: %lx", (uintptr_t)bfr->iova, (uintptr_t)bfr->buffer); } @@ -1355,7 +1361,7 @@ static void dcp_rtk_shmem_destroy(void *cookie, struct apple_rtkit_shmem *bfr) if (bfr->is_mapped) memunmap(bfr->buffer); else - dma_free_coherent(dcp->dev, bfr->size, bfr->buffer, bfr->iova); + dma_free_coherent(dcp->dev, bfr->size, bfr->buffer, bfr->iova & ~dcp->asc_dram_mask); } static struct apple_rtkit_ops rtkit_ops = { @@ -1447,6 +1453,12 @@ static int dcp_platform_probe(struct platform_device *pdev) if (IS_ERR(dcp->clk)) return dev_err_probe(dev, PTR_ERR(dcp->clk), "Unable to find clock\n"); + ret = of_property_read_u64(dev->of_node, "apple,asc-dram-mask", + &dcp->asc_dram_mask); + if (ret) + dev_warn(dev, "failed read 'apple,asc-dram-mask': %d\n", ret); + dev_dbg(dev, "'apple,asc-dram-mask': 0x%011llx\n", dcp->asc_dram_mask); + INIT_WORK(&dcp->vblank_wq, dcp_delayed_vblank); dcp->swapped_out_fbs = (struct list_head)LIST_HEAD_INIT(dcp->swapped_out_fbs); @@ -1470,6 +1482,7 @@ static int dcp_platform_probe(struct platform_device *pdev) dcp->shmem = dma_alloc_coherent(dev, DCP_SHMEM_SIZE, &shmem_iova, GFP_KERNEL); + shmem_iova |= dcp->asc_dram_mask; apple_rtkit_send_message(dcp->rtk, DCP_ENDPOINT, dcpep_set_shmem(shmem_iova), NULL, false); From 140537fa970f6a03731a4b89e74afa937e29d516 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Tue, 22 Mar 2022 16:24:53 -0400 Subject: [PATCH 0431/1020] drm/apple: Implement suspend/resume for DCP Use the firmware setPowerState callback. Signed-off-by: Alyssa Rosenzweig --- drivers/gpu/drm/apple/dcp.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 7f5ce8db92620a..d976b1c08cf494 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -1501,6 +1501,10 @@ static void dcp_platform_shutdown(struct platform_device *pdev) /* defaults are ok */ }; + /* We're going down */ + dcp->active = false; + dcp->valid_mode = false; + dcp_set_power_state(dcp, false, &req, NULL, NULL); } @@ -1510,12 +1514,41 @@ static const struct of_device_id of_match[] = { }; MODULE_DEVICE_TABLE(of, of_match); +#ifdef CONFIG_PM_SLEEP +/* + * We don't hold any useful persistent state, so for suspend/resume it suffices + * to power off/on the entire DCP. The firmware will sort out the details for + * us. + */ +static int dcp_suspend(struct device *dev) +{ + dcp_platform_shutdown(to_platform_device(dev)); + return 0; +} + +static int dcp_resume(struct device *dev) +{ + struct apple_dcp *dcp = platform_get_drvdata(to_platform_device(dev)); + + dcp_start_signal(dcp, false, dcp_started, NULL); + return 0; +} + +static const struct dev_pm_ops dcp_pm_ops = { + .suspend = dcp_suspend, + .resume = dcp_resume, +}; +#endif + static struct platform_driver apple_platform_driver = { .probe = dcp_platform_probe, .shutdown = dcp_platform_shutdown, .driver = { .name = "apple-dcp", .of_match_table = of_match, +#ifdef CONFIG_PM_SLEEP + .pm = &dcp_pm_ops, +#endif }, }; From d519e7012ca59042b43a7233ab3b95ea5bd9a737 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 14 Apr 2022 18:57:20 +0200 Subject: [PATCH 0432/1020] drm/apple: dcp: fix TRAMPOLINE_IN macro fixup! WIP: drm/apple: Add DCP display driver Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index d976b1c08cf494..eac5a201d29271 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -817,11 +817,11 @@ static void dcp_delayed_vblank(struct work_struct *work) } #define TRAMPOLINE_IN(func, handler, T_in) \ - typedef void (*callback_##name)(struct apple_dcp *, T_in *); \ + typedef void (*callback_##handler)(struct apple_dcp *, T_in *); \ \ static bool func(struct apple_dcp *dcp, void *out, void *in) \ { \ - callback_##name cb = handler; \ + callback_##handler cb = handler; \ \ dev_dbg(dcp->dev, "received callback %s\n", #handler); \ cb(dcp, in); \ From c1b5f851b48fcacb1cd715764b60bfd9c4cdccbe Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 31 Jul 2022 18:02:23 +0200 Subject: [PATCH 0433/1020] drm/apple: Switch to nonblocking commit handling The swap completes only after the async reply from DCP. Uses drm_atomic_helper_wait_for_flip_done instead of drm_atomic_helper_wait_for_vblanks. This should allow ius to get rid of the scheduled fake vblanks. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 660355261d04d2..f6773c8eb0c5af 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -208,6 +208,27 @@ static void apple_crtc_atomic_begin(struct drm_crtc *crtc, } } +static void dcp_atomic_commit_tail(struct drm_atomic_state *old_state) +{ + struct drm_device *dev = old_state->dev; + + drm_atomic_helper_commit_modeset_disables(dev, old_state); + + drm_atomic_helper_commit_modeset_enables(dev, old_state); + + drm_atomic_helper_commit_planes(dev, old_state, + DRM_PLANE_COMMIT_ACTIVE_ONLY); + + drm_atomic_helper_fake_vblank(old_state); + + drm_atomic_helper_commit_hw_done(old_state); + + drm_atomic_helper_wait_for_flip_done(dev, old_state); + + drm_atomic_helper_cleanup_planes(dev, old_state); +} + + static const struct drm_crtc_funcs apple_crtc_funcs = { .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, @@ -226,7 +247,7 @@ static const struct drm_mode_config_funcs apple_mode_config_funcs = { }; static const struct drm_mode_config_helper_funcs apple_mode_config_helpers = { - .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, + .atomic_commit_tail = dcp_atomic_commit_tail, }; static const struct drm_connector_funcs apple_connector_funcs = { From de360cf7c41c8cd0e476218e21be8b1f8c3e9f02 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 31 Jul 2022 18:15:30 +0200 Subject: [PATCH 0434/1020] drm/apple: Log callbacks with their tag as debug output Mostly for the generic callbacks nop, true, false. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index eac5a201d29271..d314abc60ef630 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -707,8 +707,9 @@ static void boot_1_5(struct apple_dcp *dcp, void *out, void *cookie) } /* Use special function signature to defer the ACK */ -static bool dcpep_cb_boot_1(struct apple_dcp *dcp, void *out, void *in) +static bool dcpep_cb_boot_1(struct apple_dcp *dcp, int tag, void *out, void *in) { + dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, __func__); dcp_set_create_dfb(dcp, false, boot_1_5, NULL); return false; } @@ -809,9 +810,9 @@ static void dcp_delayed_vblank(struct work_struct *work) */ #define TRAMPOLINE_VOID(func, handler) \ - static bool func(struct apple_dcp *dcp, void *out, void *in) \ + static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ { \ - dev_dbg(dcp->dev, "received callback %s\n", #handler); \ + dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, #handler); \ handler(dcp); \ return true; \ } @@ -819,11 +820,11 @@ static void dcp_delayed_vblank(struct work_struct *work) #define TRAMPOLINE_IN(func, handler, T_in) \ typedef void (*callback_##handler)(struct apple_dcp *, T_in *); \ \ - static bool func(struct apple_dcp *dcp, void *out, void *in) \ + static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ { \ callback_##handler cb = handler; \ \ - dev_dbg(dcp->dev, "received callback %s\n", #handler); \ + dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, #handler); \ cb(dcp, in); \ return true; \ } @@ -831,22 +832,22 @@ static void dcp_delayed_vblank(struct work_struct *work) #define TRAMPOLINE_INOUT(func, handler, T_in, T_out) \ typedef T_out (*callback_##handler)(struct apple_dcp *, T_in *); \ \ - static bool func(struct apple_dcp *dcp, void *out, void *in) \ + static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ { \ T_out *typed_out = out; \ callback_##handler cb = handler; \ \ - dev_dbg(dcp->dev, "received callback %s\n", #handler); \ + dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, #handler); \ *typed_out = cb(dcp, in); \ return true; \ } #define TRAMPOLINE_OUT(func, handler, T_out) \ - static bool func(struct apple_dcp *dcp, void *out, void *in) \ + static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ { \ T_out *typed_out = out; \ \ - dev_dbg(dcp->dev, "received callback %s\n", #handler); \ + dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, #handler); \ *typed_out = handler(dcp); \ return true; \ } @@ -878,7 +879,7 @@ TRAMPOLINE_OUT(trampoline_get_frequency, dcpep_cb_get_frequency, u64); TRAMPOLINE_OUT(trampoline_get_time, dcpep_cb_get_time, u64); TRAMPOLINE_IN(trampoline_hotplug, dcpep_cb_hotplug, u64); -bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, void *, void *) = { +bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, void *) = { [0] = trampoline_true, /* did_boot_signal */ [1] = trampoline_true, /* did_power_on_signal */ [2] = trampoline_nop, /* will_power_off_signal */ @@ -950,7 +951,7 @@ static void dcpep_handle_cb(struct apple_dcp *dcp, enum dcp_context_id context, depth = dcp_push_depth(&ch->depth); ch->output[depth] = out; - if (dcpep_cb_handlers[tag](dcp, out, in)) + if (dcpep_cb_handlers[tag](dcp, tag, out, in)) dcp_ack(dcp, context); } From a96f4e84385b0bbb5892d8092740788fd709f8e2 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 31 Jul 2022 18:19:28 +0200 Subject: [PATCH 0435/1020] drm/apple: Add DCP interface definitions used on t600x Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 12 ++++++++++++ drivers/gpu/drm/apple/dcpep.h | 26 ++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index d314abc60ef630..f94f3e1a923632 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -225,8 +225,11 @@ const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { DCP_METHOD("A407", dcpep_swap_start), DCP_METHOD("A408", dcpep_swap_submit), DCP_METHOD("A410", dcpep_set_display_device), + DCP_METHOD("A411", dcpep_is_main_display), DCP_METHOD("A412", dcpep_set_digital_out_mode), + DCP_METHOD("A439", dcpep_set_parameter_dcp), DCP_METHOD("A443", dcpep_create_default_fb), + DCP_METHOD("A447", dcpep_enable_disable_video_power_savings), DCP_METHOD("A454", dcpep_first_client_open), DCP_METHOD("A460", dcpep_set_display_refresh_properties), DCP_METHOD("A463", dcpep_flush_supports_power), @@ -336,6 +339,15 @@ __attribute__((unused)) DCP_THUNK_IN(dcp_update_notify_clients_dcp, dcpep_update_notify_clients_dcp, struct dcp_update_notify_clients_dcp); +DCP_THUNK_INOUT(dcp_set_parameter_dcp, dcpep_set_parameter_dcp, + struct dcp_set_parameter_dcp, u32); + +DCP_THUNK_INOUT(dcp_enable_disable_video_power_savings, + dcpep_enable_disable_video_power_savings, + u32, int); + +DCP_THUNK_OUT(dcp_is_main_display, dcpep_is_main_display, u32); + /* Parse a callback tag "D123" into the ID 123. Returns -EINVAL on failure. */ static int dcp_parse_tag(char tag[4]) { diff --git a/drivers/gpu/drm/apple/dcpep.h b/drivers/gpu/drm/apple/dcpep.h index f3d62d1d5f0328..d04a1b9ca9c55f 100644 --- a/drivers/gpu/drm/apple/dcpep.h +++ b/drivers/gpu/drm/apple/dcpep.h @@ -241,6 +241,9 @@ enum dcpep_method { dcpep_set_power_state, dcpep_first_client_open, dcpep_update_notify_clients_dcp, + dcpep_set_parameter_dcp, + dcpep_enable_disable_video_power_savings, + dcpep_is_main_display, dcpep_num_methods }; @@ -350,6 +353,15 @@ struct dcp_swap_submit_resp { u8 padding[3]; } __packed; +struct dc_swap_complete_resp { + u32 swap_id; + u8 unkbool; + u64 swap_data; + u8 swap_info[0x6c4]; + u32 unkint; + u8 swap_info_null; +} __packed; + struct dcp_get_uint_prop_req { char obj[4]; char key[0x40]; @@ -403,4 +415,18 @@ struct dcp_update_notify_clients_dcp { u32 client_d; } __packed; +struct dcp_set_parameter_dcp { + u32 param; + u32 value[8]; + u32 count; +} __packed; + +struct dcp_swap_complete_intent_gated { + u32 swap_id; + u8 unkBool; + u32 unkInt; + u32 width; + u32 height; +} __packed; + #endif From 5a572b61ced4e4fb70dd8b7a8c56c5b6ea677eb2 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 31 Jul 2022 18:43:01 +0200 Subject: [PATCH 0436/1020] drm/apple: Clear used callback/cookie on dcp_ack Avoids unexpected callbacks on nesting state errors. Encountered when making DCP calls from a second thread for the backlight. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index f94f3e1a923632..7e242e0f7ad747 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -985,6 +985,9 @@ static void dcpep_handle_ack(struct apple_dcp *dcp, enum dcp_context_id context, cb = ch->callbacks[ch->depth]; cookie = ch->cookies[ch->depth]; + ch->callbacks[ch->depth] = NULL; + ch->cookies[ch->depth] = NULL; + if (cb) cb(dcp, data + sizeof(*header) + header->in_len, cookie); } From 666951c99738cb1fb9d7ab24d14fe371331c7934 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 31 Jul 2022 19:31:16 +0200 Subject: [PATCH 0437/1020] drm/apple: Add t600x support Call power-on/-off handling explicitly from apple_crtc_atomic_enable / apple_crtc_atomic_disable. This makes DPMS work. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 4 + drivers/gpu/drm/apple/dcp.c | 302 +++++++++++++++++++++++++++--- drivers/gpu/drm/apple/dcp.h | 2 + 3 files changed, 280 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index f6773c8eb0c5af..cff24df268c6a5 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -175,13 +175,17 @@ apple_connector_detect(struct drm_connector *connector, bool force) static void apple_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *state) { + struct apple_crtc *apple_crtc = to_apple_crtc(crtc); + dcp_poweron(apple_crtc->dcp); drm_crtc_vblank_on(crtc); } static void apple_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *state) { + struct apple_crtc *apple_crtc = to_apple_crtc(crtc); drm_crtc_vblank_off(crtc); + dcp_poweroff(apple_crtc->dcp); if (crtc->state->event && !crtc->state->active) { spin_lock_irq(&crtc->dev->event_lock); diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 7e242e0f7ad747..e766591461fbd3 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -111,6 +112,11 @@ struct apple_dcp { /* Is the DCP booted? */ bool active; + /* eDP display without DP-HDMI conversion */ + bool main_display; + + bool ignore_swap_complete; + /* Modes valid for the connected display */ struct dcp_display_mode *modes; unsigned int nr_modes; @@ -132,6 +138,11 @@ struct dcp_fb_reference { struct drm_framebuffer *fb; }; +struct dcp_wait_cookie { + struct completion done; + atomic_t refcount; +}; + /* * A channel is busy if we have sent a message that has yet to be * acked. The driver must not sent a message to a busy channel. @@ -417,9 +428,11 @@ void dcp_drm_crtc_vblank(struct apple_crtc *crtc) spin_unlock_irqrestore(&crtc->base.dev->event_lock, flags); } -static void dcpep_cb_swap_complete(struct apple_dcp *dcp) +static void dcpep_cb_swap_complete(struct apple_dcp *dcp, + struct dc_swap_complete_resp *resp) { - dcp_drm_crtc_vblank(dcp->crtc); + if (!dcp->ignore_swap_complete) + dcp_drm_crtc_vblank(dcp->crtc); } static struct dcp_get_uint_prop_resp @@ -756,6 +769,182 @@ static u64 dcpep_cb_get_time(struct apple_dcp *dcp) return ktime_to_ms(ktime_get_real()); } +struct dcp_swap_cookie { + struct completion done; + atomic_t refcount; + u32 swap_id; +}; + +static void dcp_swap_cleared(struct apple_dcp *dcp, void *data, void *cookie) +{ + struct dcp_swap_submit_resp *resp = data; + + if (cookie) { + struct dcp_swap_cookie *info = cookie; + complete(&info->done); + if (atomic_dec_and_test(&info->refcount)) + kfree(info); + } + + if (resp->ret) { + dev_err(dcp->dev, "swap_clear failed! status %u\n", resp->ret); + dcp_drm_crtc_vblank(dcp->crtc); + return; + } + + while (!list_empty(&dcp->swapped_out_fbs)) { + struct dcp_fb_reference *entry; + entry = list_first_entry(&dcp->swapped_out_fbs, + struct dcp_fb_reference, head); + if (entry->fb) + drm_framebuffer_put(entry->fb); + list_del(&entry->head); + kfree(entry); + } +} + +static void dcp_swap_clear_started(struct apple_dcp *dcp, void *data, + void *cookie) +{ + struct dcp_swap_start_resp *resp = data; + dcp->swap.swap.swap_id = resp->swap_id; + + if (cookie) { + struct dcp_swap_cookie *info = cookie; + info->swap_id = resp->swap_id; + } + + dcp_swap_submit(dcp, false, &dcp->swap, dcp_swap_cleared, cookie); +} + +static void dcp_on_final(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct dcp_wait_cookie *wait = cookie; + + if (wait) { + complete(&wait->done); + if (atomic_dec_and_test(&wait->refcount)) + kfree(wait); + } +} + +static void dcp_on_set_parameter(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct dcp_set_parameter_dcp param = { + .param = 14, + .value = { 0 }, + .count = 1, + }; + + dcp_set_parameter_dcp(dcp, false, ¶m, dcp_on_final, cookie); +} + +void dcp_poweron(struct platform_device *pdev) +{ + struct apple_dcp *dcp = platform_get_drvdata(pdev); + struct dcp_wait_cookie * cookie; + struct dcp_set_power_state_req req = { + .unklong = 1, + }; + int ret; + u32 handle; + + cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); + if (!cookie) + return; + + init_completion(&cookie->done); + atomic_set(&cookie->refcount, 2); + + if (dcp->main_display) { + handle = 0; + dcp_set_display_device(dcp, false, &handle, dcp_on_final, cookie); + } else { + handle = 2; + dcp_set_display_device(dcp, false, &handle, dcp_on_set_parameter, cookie); + } + dcp_set_power_state(dcp, true, &req, NULL, NULL); + + ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(500)); + + if (ret == 0) + dev_warn(dcp->dev, "wait for power timed out"); + + if (atomic_dec_and_test(&cookie->refcount)) + kfree(cookie); +} +EXPORT_SYMBOL(dcp_poweron); + +static void complete_set_powerstate(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct dcp_wait_cookie *wait = cookie; + + if (wait) { + complete(&wait->done); + if (atomic_dec_and_test(&wait->refcount)) + kfree(wait); + } +} + +void dcp_poweroff(struct platform_device *pdev) +{ + struct apple_dcp *dcp = platform_get_drvdata(pdev); + int ret, swap_id; + struct dcp_set_power_state_req power_req = { + .unklong = 0, + }; + struct dcp_swap_cookie *cookie; + struct dcp_wait_cookie *poff_cookie; + struct dcp_swap_start_req swap_req= { 0 }; + + cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); + if (!cookie) + return; + init_completion(&cookie->done); + atomic_set(&cookie->refcount, 2); + + // clear surfaces + memset(&dcp->swap, 0, sizeof(dcp->swap)); + + dcp->swap.swap.swap_enabled = DCP_REMOVE_LAYERS | 0x7; + dcp->swap.swap.swap_completed = DCP_REMOVE_LAYERS | 0x7; + dcp->swap.swap.unk_10c = 0xFF000000; + + for (int l = 0; l < SWAP_SURFACES; l++) + dcp->swap.surf_null[l] = true; + + dcp_swap_start(dcp, false, &swap_req, dcp_swap_clear_started, cookie); + + ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(50)); + swap_id = cookie->swap_id; + if (atomic_dec_and_test(&cookie->refcount)) + kfree(cookie); + if (ret <= 0) { + dcp->crashed = true; + return; + } + + poff_cookie = kzalloc(sizeof(*poff_cookie), GFP_KERNEL); + if (!poff_cookie) + return; + init_completion(&poff_cookie->done); + atomic_set(&poff_cookie->refcount, 2); + + dcp_set_power_state(dcp, false, &power_req, complete_set_powerstate, poff_cookie); + ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(1000)); + + if (ret == 0) + dev_warn(dcp->dev, "setPowerState(0) timeout %u ms", 1000); + else if (ret > 0) + dev_dbg(dcp->dev, "setPowerState(0) finished with %d ms to spare", + jiffies_to_msecs(ret)); + + if (atomic_dec_and_test(&poff_cookie->refcount)) + kfree(poff_cookie); + dev_dbg(dcp->dev, "%s: setPowerState(0) done", __func__); +} +EXPORT_SYMBOL(dcp_poweroff); + /* * Helper to send a DRM hotplug event. The DCP is accessed from a single * (RTKit) thread. To handle hotplug callbacks, we need to call @@ -767,16 +956,19 @@ void dcp_hotplug(struct work_struct *work) { struct apple_connector *connector; struct drm_device *dev; + struct apple_dcp *dcp; connector = container_of(work, struct apple_connector, hotplug_wq); dev = connector->base.dev; + dcp = platform_get_drvdata(connector->dcp); + /* * DCP defers link training until we set a display mode. But we set * display modes from atomic_flush, so userspace needs to trigger a * flush, or the CRTC gets no signal. */ - if (connector->connected) { + if (!dcp->valid_mode && connector->connected) { drm_connector_set_link_status_property( &connector->base, DRM_MODE_LINK_STATUS_BAD); } @@ -791,10 +983,16 @@ static void dcpep_cb_hotplug(struct apple_dcp *dcp, u64 *connected) struct apple_connector *connector = dcp->connector; /* Hotplug invalidates mode. DRM doesn't always handle this. */ - dcp->valid_mode = false; + if (!(*connected)) { + dcp->valid_mode = false; + /* after unplug swap will not complete until the next + * set_digital_out_mode */ + schedule_work(&dcp->vblank_wq); + } - if (connector) { + if (connector && connector->connected != !!(*connected)) { connector->connected = !!(*connected); + dcp->valid_mode = false; schedule_work(&connector->hotplug_wq); } } @@ -868,7 +1066,8 @@ TRAMPOLINE_VOID(trampoline_nop, dcpep_cb_nop); TRAMPOLINE_OUT(trampoline_true, dcpep_cb_true, u8); TRAMPOLINE_OUT(trampoline_false, dcpep_cb_false, u8); TRAMPOLINE_OUT(trampoline_zero, dcpep_cb_zero, u32); -TRAMPOLINE_VOID(trampoline_swap_complete, dcpep_cb_swap_complete); +TRAMPOLINE_IN(trampoline_swap_complete, dcpep_cb_swap_complete, + struct dc_swap_complete_resp); TRAMPOLINE_INOUT(trampoline_get_uint_prop, dcpep_cb_get_uint_prop, struct dcp_get_uint_prop_req, struct dcp_get_uint_prop_resp); TRAMPOLINE_INOUT(trampoline_map_piodma, dcpep_cb_map_piodma, @@ -906,6 +1105,7 @@ bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, v [110] = trampoline_true, /* create_iomfb_service */ [111] = trampoline_false, /* create_backlight_service */ [116] = dcpep_cb_boot_1, + [117] = trampoline_false, /* is_dark_boot */ [118] = trampoline_false, /* is_dark_boot / is_waking_from_hibernate*/ [120] = trampoline_false, /* read_edt_data */ [122] = trampoline_prop_start, @@ -938,6 +1138,7 @@ bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, v [582] = trampoline_true, /* create_default_fb_surface */ [589] = trampoline_swap_complete, [591] = trampoline_nop, /* swap_complete_intent_gated */ + [593] = trampoline_nop, /* enable_backlight_message_ap_gated */ [598] = trampoline_nop, /* find_swap_function_gated */ }; @@ -1142,12 +1343,24 @@ static void do_swap(struct apple_dcp *dcp, void *data, void *cookie) { struct dcp_swap_start_req start_req = { 0 }; - dcp_swap_start(dcp, false, &start_req, dcp_swap_started, NULL); + if (dcp->connector && dcp->connector->connected) + dcp_swap_start(dcp, false, &start_req, dcp_swap_started, NULL); + else + dcp_drm_crtc_vblank(dcp->crtc); } -static void dcp_modeset(struct apple_dcp *dcp, void *out, void *cookie) +static void complete_set_digital_out_mode(struct apple_dcp *dcp, void *data, + void *cookie) { - dcp_set_digital_out_mode(dcp, false, &dcp->mode, do_swap, NULL); + struct dcp_wait_cookie *wait = cookie; + + dcp->ignore_swap_complete = false; + + if (wait) { + complete(&wait->done); + if (atomic_dec_and_test(&wait->refcount)) + kfree(wait); + } } void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) @@ -1244,7 +1457,8 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) if (drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode) { struct dcp_display_mode *mode; - u32 handle = 2; + struct dcp_wait_cookie *cookie; + int ret; mode = lookup_mode(dcp, &crtc_state->mode); if (!mode) { @@ -1259,19 +1473,43 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) .timing_mode_id = mode->timing_mode_id }; - dcp->valid_mode = true; + cookie = kzalloc(sizeof(cookie), GFP_KERNEL); + if (!cookie) { + schedule_work(&dcp->vblank_wq); + return; + } + + init_completion(&cookie->done); + atomic_set(&cookie->refcount, 2); + + dcp_set_digital_out_mode(dcp, false, &dcp->mode, + complete_set_digital_out_mode, cookie); + + ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(500)); - dcp_set_display_device(dcp, false, &handle, dcp_modeset, NULL); + if (atomic_dec_and_test(&cookie->refcount)) + kfree(cookie); + + if (ret == 0) { + dev_dbg(dcp->dev, "set_digital_out_mode 200 ms"); + schedule_work(&dcp->vblank_wq); + return; + } + else if (ret > 0) { + dev_dbg(dcp->dev, "set_digital_out_mode finished with %d to spare", + jiffies_to_msecs(ret)); + } + + dcp->valid_mode = true; } - else if (!has_surface) { - dev_warn(dcp->dev, "can't flush without surfaces, vsync:%d", dcp->crtc->vsync_disabled); - /* HACK: issue a delayed vblank event to avoid timeouts in - * drm_atomic_helper_wait_for_vblanks(). It's currently unkown - * if and how DCP supports swaps without attached surfaces. - */ + + if (!has_surface) { + dev_warn(dcp->dev, "flush without surfaces, vsync:%d", + dcp->crtc->vsync_disabled); schedule_work(&dcp->vblank_wq); - } else - do_swap(dcp, NULL, NULL); + return; + } + do_swap(dcp, NULL, NULL); } EXPORT_SYMBOL_GPL(dcp_flush); @@ -1283,16 +1521,20 @@ bool dcp_is_initialized(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(dcp_is_initialized); -static void init_done(struct apple_dcp *dcp, void *out, void *cookie) + +static void res_is_main_display(struct apple_dcp *dcp, void *out, void *cookie) { + int result = *(int *)out; + dev_info(dcp->dev, "DCP is_main_display: %d\n", result); + + dcp->main_display = result != 0; + + dcp->active = true; } static void init_3(struct apple_dcp *dcp, void *out, void *cookie) { - struct dcp_set_power_state_req req = { - .unklong = 1, - }; - dcp_set_power_state(dcp, false, &req, init_done, NULL); + dcp_is_main_display(dcp, false, res_is_main_display, NULL); } static void init_2(struct apple_dcp *dcp, void *out, void *cookie) @@ -1300,13 +1542,17 @@ static void init_2(struct apple_dcp *dcp, void *out, void *cookie) dcp_first_client_open(dcp, false, init_3, NULL); } +static void init_1(struct apple_dcp *dcp, void *out, void *cookie) +{ + u32 val = 0; + dcp_enable_disable_video_power_savings(dcp, false, &val, init_2, NULL); +} + static void dcp_started(struct apple_dcp *dcp, void *data, void *cookie) { dev_info(dcp->dev, "DCP booted\n"); - init_2(dcp, data, cookie); - - dcp->active = true; + init_1(dcp, data, cookie); } static void dcp_got_msg(void *cookie, u8 endpoint, u64 message) diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h index 794544456df963..9e3e3738a39377 100644 --- a/drivers/gpu/drm/apple/dcp.h +++ b/drivers/gpu/drm/apple/dcp.h @@ -32,6 +32,8 @@ struct apple_connector { #define to_apple_connector(x) container_of(x, struct apple_connector, base) +void dcp_poweroff(struct platform_device *pdev); +void dcp_poweron(struct platform_device *pdev); void dcp_link(struct platform_device *pdev, struct apple_crtc *apple, struct apple_connector *connector); void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state); From e117e0230f98bb9042af88a3a4eb49871b08d304 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 6 Jun 2022 22:14:27 +0200 Subject: [PATCH 0438/1020] drm/apple: toggle power only when active state changes squash! WIP: GPU/apple: t600x work in progress Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index cff24df268c6a5..bcac8bb9f476a7 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -175,17 +175,32 @@ apple_connector_detect(struct drm_connector *connector, bool force) static void apple_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *state) { - struct apple_crtc *apple_crtc = to_apple_crtc(crtc); - dcp_poweron(apple_crtc->dcp); + struct drm_crtc_state *crtc_state; + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + if (crtc_state->active_changed && crtc_state->active) { + struct apple_crtc *apple_crtc = to_apple_crtc(crtc); + dev_dbg(&apple_crtc->dcp->dev, "%s", __func__); + dcp_poweron(apple_crtc->dcp); + dev_dbg(&apple_crtc->dcp->dev, "%s finished", __func__); + } drm_crtc_vblank_on(crtc); } static void apple_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *state) { - struct apple_crtc *apple_crtc = to_apple_crtc(crtc); + struct drm_crtc_state *crtc_state; + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + drm_crtc_vblank_off(crtc); - dcp_poweroff(apple_crtc->dcp); + + if (crtc_state->active_changed && !crtc_state->active) { + struct apple_crtc *apple_crtc = to_apple_crtc(crtc); + dev_dbg(&apple_crtc->dcp->dev, "%s", __func__); + dcp_poweroff(apple_crtc->dcp); + dev_dbg(&apple_crtc->dcp->dev, "%s finished", __func__); + } if (crtc->state->event && !crtc->state->active) { spin_lock_irq(&crtc->dev->event_lock); From 3c0b2e7fa61c98d1468b253b18af4bc374c940a0 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 31 Jul 2022 19:39:10 +0200 Subject: [PATCH 0439/1020] drm/apple: Add somewhat useful debug prints Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index e766591461fbd3..af4ffb18c09d0b 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -431,6 +431,9 @@ void dcp_drm_crtc_vblank(struct apple_crtc *crtc) static void dcpep_cb_swap_complete(struct apple_dcp *dcp, struct dc_swap_complete_resp *resp) { + dev_dbg(dcp->dev, "swap complete for swap_id: %u vblank: %u", + resp->swap_id, dcp->ignore_swap_complete); + if (!dcp->ignore_swap_complete) dcp_drm_crtc_vblank(dcp->crtc); } @@ -699,6 +702,7 @@ static void boot_done(struct apple_dcp *dcp, void *out, void *cookie) { struct dcp_cb_channel *ch = &dcp->ch_cb; u8 *succ = ch->output[ch->depth - 1]; + dev_dbg(dcp->dev, "boot done"); *succ = true; dcp_ack(dcp, DCP_CONTEXT_CB); @@ -807,6 +811,7 @@ static void dcp_swap_clear_started(struct apple_dcp *dcp, void *data, void *cookie) { struct dcp_swap_start_resp *resp = data; + dev_dbg(dcp->dev, "%s swap_id: %u", __func__, resp->swap_id); dcp->swap.swap.swap_id = resp->swap_id; if (cookie) { @@ -924,6 +929,8 @@ void dcp_poweroff(struct platform_device *pdev) return; } + dev_dbg(dcp->dev, "%s: clear swap submitted: %u", __func__, swap_id); + poff_cookie = kzalloc(sizeof(*poff_cookie), GFP_KERNEL); if (!poff_cookie) return; @@ -962,6 +969,7 @@ void dcp_hotplug(struct work_struct *work) dev = connector->base.dev; dcp = platform_get_drvdata(connector->dcp); + dev_info(dcp->dev, "%s: connected: %d", __func__, connector->connected); /* * DCP defers link training until we set a display mode. But we set @@ -997,6 +1005,14 @@ static void dcpep_cb_hotplug(struct apple_dcp *dcp, u64 *connected) } } +static void +dcpep_cb_swap_complete_intent_gated(struct apple_dcp *dcp, + struct dcp_swap_complete_intent_gated *info) +{ + dev_dbg(dcp->dev, "swap_id:%u width:%u height:%u", info->swap_id, + info->width, info->height); +} + /* * Helper to send a DRM vblank event. We do not know how call swap_submit_dcp * without surfaces. To avoid timeouts in drm_atomic_helper_wait_for_vblanks @@ -1089,6 +1105,9 @@ TRAMPOLINE_OUT(trampoline_rt_bandwidth, dcpep_cb_rt_bandwidth, TRAMPOLINE_OUT(trampoline_get_frequency, dcpep_cb_get_frequency, u64); TRAMPOLINE_OUT(trampoline_get_time, dcpep_cb_get_time, u64); TRAMPOLINE_IN(trampoline_hotplug, dcpep_cb_hotplug, u64); +TRAMPOLINE_IN(trampoline_swap_complete_intent_gated, + dcpep_cb_swap_complete_intent_gated, + struct dcp_swap_complete_intent_gated); bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, void *) = { [0] = trampoline_true, /* did_boot_signal */ @@ -1137,7 +1156,7 @@ bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, v [577] = trampoline_nop, /* powerstate_notify */ [582] = trampoline_true, /* create_default_fb_surface */ [589] = trampoline_swap_complete, - [591] = trampoline_nop, /* swap_complete_intent_gated */ + [591] = trampoline_swap_complete_intent_gated, [593] = trampoline_nop, /* enable_backlight_message_ap_gated */ [598] = trampoline_nop, /* find_swap_function_gated */ }; @@ -1468,6 +1487,8 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) return; } + dev_info(dcp->dev, "set_digital_out_mode(color:%d timing:%d)", + mode->color_mode_id, mode->timing_mode_id); dcp->mode = (struct dcp_set_digital_out_mode_req) { .color_mode_id = mode->color_mode_id, .timing_mode_id = mode->timing_mode_id @@ -1485,6 +1506,7 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) dcp_set_digital_out_mode(dcp, false, &dcp->mode, complete_set_digital_out_mode, cookie); + dev_dbg(dcp->dev, "%s - wait for modeset", __func__); ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(500)); if (atomic_dec_and_test(&cookie->refcount)) From a9dea94aa7cae9d86d3bba89e9bb810f3740dfc4 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 31 Jul 2022 19:40:50 +0200 Subject: [PATCH 0440/1020] drm/apple: Add less tons of questionable debug prints Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index af4ffb18c09d0b..d05ae66e35748d 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -782,6 +782,7 @@ struct dcp_swap_cookie { static void dcp_swap_cleared(struct apple_dcp *dcp, void *data, void *cookie) { struct dcp_swap_submit_resp *resp = data; + dev_dbg(dcp->dev, "%s", __func__); if (cookie) { struct dcp_swap_cookie *info = cookie; @@ -825,6 +826,7 @@ static void dcp_swap_clear_started(struct apple_dcp *dcp, void *data, static void dcp_on_final(struct apple_dcp *dcp, void *out, void *cookie) { struct dcp_wait_cookie *wait = cookie; + dev_dbg(dcp->dev, "%s", __func__); if (wait) { complete(&wait->done); @@ -840,6 +842,7 @@ static void dcp_on_set_parameter(struct apple_dcp *dcp, void *out, void *cookie) .value = { 0 }, .count = 1, }; + dev_dbg(dcp->dev, "%s", __func__); dcp_set_parameter_dcp(dcp, false, ¶m, dcp_on_final, cookie); } @@ -853,6 +856,7 @@ void dcp_poweron(struct platform_device *pdev) }; int ret; u32 handle; + dev_dbg(dcp->dev, "%s", __func__); cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); if (!cookie) @@ -902,6 +906,8 @@ void dcp_poweroff(struct platform_device *pdev) struct dcp_wait_cookie *poff_cookie; struct dcp_swap_start_req swap_req= { 0 }; + dev_dbg(dcp->dev, "%s", __func__); + cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); if (!cookie) return; @@ -1361,6 +1367,7 @@ EXPORT_SYMBOL_GPL(dcp_mode_valid); static void do_swap(struct apple_dcp *dcp, void *data, void *cookie) { struct dcp_swap_start_req start_req = { 0 }; + dev_dbg(dcp->dev, "%s", __func__); if (dcp->connector && dcp->connector->connected) dcp_swap_start(dcp, false, &start_req, dcp_swap_started, NULL); @@ -1372,6 +1379,7 @@ static void complete_set_digital_out_mode(struct apple_dcp *dcp, void *data, void *cookie) { struct dcp_wait_cookie *wait = cookie; + dev_dbg(dcp->dev, "%s", __func__); dcp->ignore_swap_complete = false; @@ -1392,6 +1400,7 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) struct dcp_swap_submit_req *req = &dcp->swap; int l; int has_surface = 0; + dev_dbg(dcp->dev, "%s", __func__); crtc_state = drm_atomic_get_new_crtc_state(state, crtc); From ba2e3cd4c9f0b51d24c13ce2d34bcd7f059d1e52 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 5 Jun 2022 19:32:01 +0200 Subject: [PATCH 0441/1020] drm/apple: implement read_edt_data Handling it with trampoline_false can result in errors due to uninitialized data. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 13 ++++++++++++- drivers/gpu/drm/apple/dcpep.h | 11 +++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index d05ae66e35748d..a36217145ec6e9 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -581,6 +581,15 @@ dcpep_cb_map_reg(struct apple_dcp *dcp, struct dcp_map_reg_req *req) } } +static struct dcp_read_edt_data_resp +dcpep_cb_read_edt_data(struct apple_dcp *dcp, struct dcp_read_edt_data_req *req) +{ + return (struct dcp_read_edt_data_resp) { + .value[0] = req->value[0], + .ret = 0, + }; +} + /* Chunked data transfer for property dictionaries */ static u8 dcpep_cb_prop_start(struct apple_dcp *dcp, u32 *length) { @@ -1101,6 +1110,8 @@ TRAMPOLINE_INOUT(trampoline_map_physical, dcpep_cb_map_physical, struct dcp_map_physical_req, struct dcp_map_physical_resp); TRAMPOLINE_INOUT(trampoline_map_reg, dcpep_cb_map_reg, struct dcp_map_reg_req, struct dcp_map_reg_resp); +TRAMPOLINE_INOUT(trampoline_read_edt_data, dcpep_cb_read_edt_data, + struct dcp_read_edt_data_req, struct dcp_read_edt_data_resp); TRAMPOLINE_INOUT(trampoline_prop_start, dcpep_cb_prop_start, u32, u8); TRAMPOLINE_INOUT(trampoline_prop_chunk, dcpep_cb_prop_chunk, struct dcp_set_dcpav_prop_chunk_req, u8); @@ -1132,7 +1143,7 @@ bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, v [116] = dcpep_cb_boot_1, [117] = trampoline_false, /* is_dark_boot */ [118] = trampoline_false, /* is_dark_boot / is_waking_from_hibernate*/ - [120] = trampoline_false, /* read_edt_data */ + [120] = trampoline_read_edt_data, [122] = trampoline_prop_start, [123] = trampoline_prop_chunk, [124] = trampoline_prop_end, diff --git a/drivers/gpu/drm/apple/dcpep.h b/drivers/gpu/drm/apple/dcpep.h index d04a1b9ca9c55f..12d81c7b4e2734 100644 --- a/drivers/gpu/drm/apple/dcpep.h +++ b/drivers/gpu/drm/apple/dcpep.h @@ -429,4 +429,15 @@ struct dcp_swap_complete_intent_gated { u32 height; } __packed; +struct dcp_read_edt_data_req { + char key[0x40]; + u32 count; + u32 value[8]; +} __packed; + +struct dcp_read_edt_data_resp { + u32 value[8]; + u8 ret; +} __packed; + #endif From a60341a75f815e1e01d85725eb5864f9bb09e8ee Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 5 Jun 2022 19:47:25 +0200 Subject: [PATCH 0442/1020] drm/apple: clear callback's output data If there is a mismatch in the output size this way we have at leas consistant results. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index a36217145ec6e9..5250eb5da0d4a3 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -1197,6 +1197,11 @@ static void dcpep_handle_cb(struct apple_dcp *dcp, enum dcp_context_id context, in = data + sizeof(*hdr); out = in + hdr->in_len; + // TODO: verify that in_len and out_len match our prototypes + // for now just clear the out data to have at least consistant results + if (hdr->out_len) + memset(out, 0, hdr->out_len); + depth = dcp_push_depth(&ch->depth); ch->output[depth] = out; From ce16dd019ab8cc564bc132071688d47a64d2d858 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 5 Jun 2022 20:00:13 +0200 Subject: [PATCH 0443/1020] drm/apple: Support memory unmapping/freeing Used by dcp on mode changes on the Macbook Pro 14" (M1 Max). Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 129 ++++++++++++++++++++++++++++++---- drivers/gpu/drm/apple/dcpep.h | 8 +++ 2 files changed, 122 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 5250eb5da0d4a3..913664aa1f97c8 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2021 Alyssa Rosenzweig */ +#include #include #include #include @@ -64,6 +65,14 @@ struct dcp_chunks { #define DCP_MAX_MAPPINGS (128) /* should be enough */ #define MAX_DISP_REGISTERS (7) +struct dcp_mem_descriptor { + size_t size; + void *buf; + dma_addr_t dva; + struct sg_table map; + u64 reg; +}; + struct apple_dcp { struct device *dev; struct platform_device *piodma; @@ -90,11 +99,11 @@ struct apple_dcp { struct resource *disp_registers[MAX_DISP_REGISTERS]; unsigned int nr_disp_registers; - /* Number of memory mappings made by the DCP, used as an ID */ - u32 nr_mappings; + /* Bitmap of memory descriptors used for mappings made by the DCP */ + DECLARE_BITMAP(memdesc_map, DCP_MAX_MAPPINGS); - /* Indexed table of mappings */ - struct sg_table mappings[DCP_MAX_MAPPINGS]; + /* Indexed table of memory descriptors */ + struct dcp_mem_descriptor memdesc[DCP_MAX_MAPPINGS]; struct dcp_call_channel ch_cmd, ch_oobcmd; struct dcp_cb_channel ch_cb, ch_oobcb, ch_async; @@ -462,10 +471,10 @@ dcpep_cb_map_piodma(struct apple_dcp *dcp, struct dcp_map_buf_req *req) struct sg_table *map; int ret; - if (req->buffer >= ARRAY_SIZE(dcp->mappings)) + if (req->buffer >= ARRAY_SIZE(dcp->memdesc)) goto reject; - map = &dcp->mappings[req->buffer]; + map = &dcp->memdesc[req->buffer].map; if (!map->sgl) goto reject; @@ -488,6 +497,37 @@ dcpep_cb_map_piodma(struct apple_dcp *dcp, struct dcp_map_buf_req *req) }; } +static void +dcpep_cb_unmap_piodma(struct apple_dcp *dcp, struct dcp_unmap_buf_resp *resp) +{ + struct sg_table *map; + dma_addr_t dma_addr; + + if (resp->buffer >= ARRAY_SIZE(dcp->memdesc)) { + dev_warn(dcp->dev, "unmap request for out of range buffer %llu", + resp->buffer); + return; + } + + map = &dcp->memdesc[resp->buffer].map; + + if (!map->sgl) { + dev_warn(dcp->dev, "unmap for non-mapped buffer %llu iova:0x%08llx", + resp->buffer, resp->dva); + return; + } + + dma_addr = sg_dma_address(map->sgl); + if (dma_addr != resp->dva) { + dev_warn(dcp->dev, "unmap buffer %llu address mismatch dma_addr:%llx dva:%llx", + resp->buffer, dma_addr, resp->dva); + return; + } + + /* Use PIODMA device instead of DCP to unmap from the right IOMMU. */ + dma_unmap_sgtable(&dcp->piodma->dev, map, DMA_BIDIRECTIONAL, 0); +} + /* * Allocate an IOVA contiguous buffer mapped to the DCP. The buffer need not be * physically contigiuous, however we should save the sgtable in case the @@ -497,25 +537,68 @@ static struct dcp_allocate_buffer_resp dcpep_cb_allocate_buffer(struct apple_dcp *dcp, struct dcp_allocate_buffer_req *req) { struct dcp_allocate_buffer_resp resp = { 0 }; - void *buf; + struct dcp_mem_descriptor *memdesc; + u32 id; resp.dva_size = ALIGN(req->size, 4096); - resp.mem_desc_id = ++dcp->nr_mappings; + resp.mem_desc_id = find_first_zero_bit(dcp->memdesc_map, DCP_MAX_MAPPINGS); - if (resp.mem_desc_id >= ARRAY_SIZE(dcp->mappings)) { + if (resp.mem_desc_id >= DCP_MAX_MAPPINGS) { dev_warn(dcp->dev, "DCP overflowed mapping table, ignoring"); + resp.dva_size = 0; + resp.mem_desc_id = 0; return resp; } + id = resp.mem_desc_id; + set_bit(id, dcp->memdesc_map); - buf = dma_alloc_coherent(dcp->dev, resp.dva_size, &resp.dva, + memdesc = &dcp->memdesc[id]; + + memdesc->size = resp.dva_size; + memdesc->buf = dma_alloc_coherent(dcp->dev, memdesc->size, &memdesc->dva, GFP_KERNEL); - dma_get_sgtable(dcp->dev, &dcp->mappings[resp.mem_desc_id], buf, - resp.dva, resp.dva_size); - resp.dva |= dcp->asc_dram_mask; + dma_get_sgtable(dcp->dev, &memdesc->map, memdesc->buf, + memdesc->dva, memdesc->size); + resp.dva = memdesc->dva; + return resp; } +static u8 dcpep_cb_release_mem_desc(struct apple_dcp *dcp, u32 *mem_desc_id) +{ + struct dcp_mem_descriptor *memdesc; + u32 id = *mem_desc_id; + + if (id >= DCP_MAX_MAPPINGS) { + dev_warn(dcp->dev, "unmap request for out of range mem_desc_id %u", + id); + return 0; + } + + if (!test_and_clear_bit(id, dcp->memdesc_map)) { + dev_warn(dcp->dev, "unmap request for unused mem_desc_id %u", + id); + return 0; + } + + memdesc = &dcp->memdesc[id]; + if (memdesc->buf) { + + dma_free_coherent(dcp->dev, memdesc->size, memdesc->buf, + memdesc->dva); + + memdesc->buf = NULL; + memset(&memdesc->map, 0, sizeof(memdesc->map)); + } else { + memdesc->reg = 0; + } + + memdesc->size = 0; + + return 1; +} + /* Validate that the specified region is a display register */ static bool is_disp_register(struct apple_dcp *dcp, u64 start, u64 end) { @@ -541,6 +624,7 @@ static struct dcp_map_physical_resp dcpep_cb_map_physical(struct apple_dcp *dcp, struct dcp_map_physical_req *req) { int size = ALIGN(req->size, 4096); + u32 id; if (!is_disp_register(dcp, req->paddr, req->paddr + size - 1)) { dev_err(dcp->dev, "refusing to map phys address %llx size %llx", @@ -548,11 +632,16 @@ dcpep_cb_map_physical(struct apple_dcp *dcp, struct dcp_map_physical_req *req) return (struct dcp_map_physical_resp) { }; } + id = find_first_zero_bit(dcp->memdesc_map, DCP_MAX_MAPPINGS); + set_bit(id, dcp->memdesc_map); + dcp->memdesc[id].size = size; + dcp->memdesc[id].reg = req->paddr; + return (struct dcp_map_physical_resp) { .dva_size = size, - .mem_desc_id = ++dcp->nr_mappings, + .mem_desc_id = id, .dva = dma_map_resource(dcp->dev, req->paddr, size, - DMA_BIDIRECTIONAL, 0) | dcp->asc_dram_mask, + DMA_BIDIRECTIONAL, 0), }; } @@ -1103,11 +1192,15 @@ TRAMPOLINE_INOUT(trampoline_get_uint_prop, dcpep_cb_get_uint_prop, struct dcp_get_uint_prop_req, struct dcp_get_uint_prop_resp); TRAMPOLINE_INOUT(trampoline_map_piodma, dcpep_cb_map_piodma, struct dcp_map_buf_req, struct dcp_map_buf_resp); +TRAMPOLINE_IN(trampoline_unmap_piodma, dcpep_cb_unmap_piodma, + struct dcp_unmap_buf_resp); TRAMPOLINE_INOUT(trampoline_allocate_buffer, dcpep_cb_allocate_buffer, struct dcp_allocate_buffer_req, struct dcp_allocate_buffer_resp); TRAMPOLINE_INOUT(trampoline_map_physical, dcpep_cb_map_physical, struct dcp_map_physical_req, struct dcp_map_physical_resp); +TRAMPOLINE_INOUT(trampoline_release_mem_desc, dcpep_cb_release_mem_desc, + u32, u8); TRAMPOLINE_INOUT(trampoline_map_reg, dcpep_cb_map_reg, struct dcp_map_reg_req, struct dcp_map_reg_resp); TRAMPOLINE_INOUT(trampoline_read_edt_data, dcpep_cb_read_edt_data, @@ -1148,6 +1241,7 @@ bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, v [123] = trampoline_prop_chunk, [124] = trampoline_prop_end, [201] = trampoline_map_piodma, + [202] = trampoline_unmap_piodma, [206] = trampoline_true, /* match_pmu_service_2 */ [207] = trampoline_true, /* match_backlight_service */ [208] = trampoline_get_time, @@ -1163,6 +1257,7 @@ bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, v [415] = trampoline_true, /* sr_set_property_bool */ [451] = trampoline_allocate_buffer, [452] = trampoline_map_physical, + [456] = trampoline_release_mem_desc, [552] = trampoline_true, /* set_property_dict_0 */ [561] = trampoline_true, /* set_property_dict */ [563] = trampoline_true, /* set_property_int */ @@ -1768,6 +1863,10 @@ static int dcp_platform_probe(struct platform_device *pdev) dev_warn(dev, "failed read 'apple,asc-dram-mask': %d\n", ret); dev_dbg(dev, "'apple,asc-dram-mask': 0x%011llx\n", dcp->asc_dram_mask); + bitmap_zero(dcp->memdesc_map, DCP_MAX_MAPPINGS); + // TDOD: mem_desc IDs start at 1, for simplicity just skip '0' entry + set_bit(0, dcp->memdesc_map); + INIT_WORK(&dcp->vblank_wq, dcp_delayed_vblank); dcp->swapped_out_fbs = (struct list_head)LIST_HEAD_INIT(dcp->swapped_out_fbs); diff --git a/drivers/gpu/drm/apple/dcpep.h b/drivers/gpu/drm/apple/dcpep.h index 12d81c7b4e2734..7c4fd97c45e54e 100644 --- a/drivers/gpu/drm/apple/dcpep.h +++ b/drivers/gpu/drm/apple/dcpep.h @@ -273,6 +273,14 @@ struct dcp_map_buf_resp { u32 ret; } __packed; +struct dcp_unmap_buf_resp { + u64 buffer; + u64 vaddr; + u64 dva; + u8 unk; + u8 buf_null; +} __packed; + struct dcp_allocate_buffer_req { u32 unk0; u64 size; From 1018243f2dab647f2864c9cef507a0b8446b5c66 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 6 Jun 2022 12:53:18 +0200 Subject: [PATCH 0444/1020] WIP: drm/apple: Change the way to clear unused surfaces This seems to be incorrect but the flag seems to be related to clearing. Allows unmapping surfaces after the swap finished. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 10 ++++++---- drivers/gpu/drm/apple/dcpep.h | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 913664aa1f97c8..4b3a37fbf1e938 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -1646,10 +1646,12 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) } if (!has_surface) { - dev_warn(dcp->dev, "flush without surfaces, vsync:%d", - dcp->crtc->vsync_disabled); - schedule_work(&dcp->vblank_wq); - return; + if (crtc_state->enable && crtc_state->active && !crtc_state->planes_changed) { + schedule_work(&dcp->vblank_wq); + return; + } + + req->clear = 1; } do_swap(dcp, NULL, NULL); } diff --git a/drivers/gpu/drm/apple/dcpep.h b/drivers/gpu/drm/apple/dcpep.h index 7c4fd97c45e54e..a796b16bd55ad7 100644 --- a/drivers/gpu/drm/apple/dcpep.h +++ b/drivers/gpu/drm/apple/dcpep.h @@ -348,7 +348,7 @@ struct dcp_swap_submit_req { u64 surf_iova[SWAP_SURFACES]; u8 unkbool; u64 unkdouble; - u32 unkint; + u32 clear; // or maybe switch to default fb? u8 swap_null; u8 surf_null[SWAP_SURFACES]; u8 unkoutbool_null; From 00cbe816c9997130534d1db089bb3828a6564567 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 27 Sep 2022 00:02:20 +0200 Subject: [PATCH 0445/1020] drm/apple: laod piodma dev via explicit phandle Use a device_link to ensure piodma has is bound to its DART. fixup! WIP: drm/apple: Add DCP display driver Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 4b3a37fbf1e938..d2ac6ee0117604 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -76,6 +76,7 @@ struct dcp_mem_descriptor { struct apple_dcp { struct device *dev; struct platform_device *piodma; + struct device_link *piodma_link; struct apple_rtkit *rtk; struct apple_crtc *crtc; struct apple_connector *connector; @@ -1792,12 +1793,15 @@ EXPORT_SYMBOL_GPL(dcp_link); static struct platform_device *dcp_get_dev(struct device *dev, const char *name) { - struct device_node *node = of_get_child_by_name(dev->of_node, name); + struct platform_device *pdev; + struct device_node *node = of_parse_phandle(dev->of_node, name, 0); if (!node) return NULL; - return of_find_device_by_node(node); + pdev = of_find_device_by_node(node); + of_node_put(node); + return pdev; } static int dcp_get_disp_regs(struct apple_dcp *dcp) @@ -1843,12 +1847,22 @@ static int dcp_platform_probe(struct platform_device *pdev) of_platform_default_populate(dev->of_node, NULL, dev); - dcp->piodma = dcp_get_dev(dev, "piodma"); + dcp->piodma = dcp_get_dev(dev, "apple,piodma-mapper"); if (!dcp->piodma) { dev_err(dev, "failed to find piodma\n"); return -ENODEV; } + dcp->piodma_link = device_link_add(dev, &dcp->piodma->dev, + DL_FLAG_AUTOREMOVE_CONSUMER); + if (!dcp->piodma_link) { + dev_err(dev, "Failed to link to piodma device"); + return -EINVAL; + } + + if (dcp->piodma_link->supplier->links.status != DL_DEV_DRIVER_BOUND) + return -EPROBE_DEFER; + ret = dcp_get_disp_regs(dcp); if (ret) { dev_err(dev, "failed to find display registers\n"); From d492ef499da32647b15fe52ac54c946de963b531 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Wed, 28 Sep 2022 17:47:01 +0900 Subject: [PATCH 0446/1020] drm/apple: Fix kzalloc in dcp_flush() Signed-off-by: Asahi Lina --- drivers/gpu/drm/apple/dcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index d2ac6ee0117604..06745116838a6d 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -1615,7 +1615,7 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) .timing_mode_id = mode->timing_mode_id }; - cookie = kzalloc(sizeof(cookie), GFP_KERNEL); + cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); if (!cookie) { schedule_work(&dcp->vblank_wq); return; From 4dcc9fded94851b08bec31215bd9710970b54fb3 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 1 Oct 2022 09:48:08 +0200 Subject: [PATCH 0447/1020] drm/apple: Allow modesets even when disconnected Fixes a display wakeup issue seen with Plasma/X11. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 06745116838a6d..0f0c7bff31623b 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -1512,12 +1512,15 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) struct dcp_swap_submit_req *req = &dcp->swap; int l; int has_surface = 0; + bool modeset; dev_dbg(dcp->dev, "%s", __func__); crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + modeset = drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode; + if (WARN(dcp_channel_busy(&dcp->ch_cmd), "unexpected busy channel") || - WARN(!dcp->connector->connected, "can't flush if disconnected")) { + WARN(!modeset && !dcp->connector->connected, "can't flush if disconnected")) { /* HACK: issue a delayed vblank event to avoid timeouts in * drm_atomic_helper_wait_for_vblanks(). */ @@ -1595,7 +1598,7 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) /* These fields should be set together */ req->swap.swap_completed = req->swap.swap_enabled; - if (drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode) { + if (modeset) { struct dcp_display_mode *mode; struct dcp_wait_cookie *cookie; int ret; From 6587f212082141d3be85ee154d34eaaabd7dce1d Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 1 Oct 2022 11:05:56 +0200 Subject: [PATCH 0448/1020] drm/apple: Mark the connecter on init only with modes as connected Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 2 +- drivers/gpu/drm/apple/dcp.c | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index bcac8bb9f476a7..378be6237d9db6 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -329,7 +329,7 @@ static int apple_probe_per_dcp(struct device *dev, return ret; connector->base.polled = DRM_CONNECTOR_POLL_HPD; - connector->connected = true; /* XXX */ + connector->connected = false; connector->dcp = dcp; INIT_WORK(&connector->hotplug_wq, dcp_hotplug); diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 0f0c7bff31623b..4d2f835d3d338b 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -1672,12 +1672,19 @@ EXPORT_SYMBOL_GPL(dcp_is_initialized); static void res_is_main_display(struct apple_dcp *dcp, void *out, void *cookie) { + struct apple_connector *connector; int result = *(int *)out; dev_info(dcp->dev, "DCP is_main_display: %d\n", result); dcp->main_display = result != 0; dcp->active = true; + + connector = dcp->connector; + if (connector) { + connector->connected = dcp->nr_modes > 0; + schedule_work(&connector->hotplug_wq); + } } static void init_3(struct apple_dcp *dcp, void *out, void *cookie) @@ -1789,6 +1796,9 @@ void dcp_link(struct platform_device *pdev, struct apple_crtc *crtc, dcp->crtc = crtc; dcp->connector = connector; + /* init connector status by modes offered by dcp */ + connector->connected = dcp->nr_modes > 0; + /* Dimensions might already be parsed */ dcp_set_dimensions(dcp); } From 08a4782f6bf4a5001cc5ef374b10fe3edb2939dd Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 1 Oct 2022 14:46:11 +0200 Subject: [PATCH 0449/1020] drm/apple: make note about drm.mode_config.max_width/height Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 378be6237d9db6..f576cefd8c3353 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -398,6 +398,8 @@ static int apple_platform_probe(struct platform_device *pdev) /* Unknown maximum, use the iMac (24-inch, 2021) display resolution as * maximum. + * TODO: this is the max framebuffer size not the maximal supported output + * resolution. DCP reports the maximal framebuffer size take it from there. */ apple->drm.mode_config.max_width = 4480; apple->drm.mode_config.max_height = 2520; From 8c7ed2fbede03842a5fe00e66c6db5c686429a31 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 2 Oct 2022 18:22:32 +0200 Subject: [PATCH 0450/1020] drm/apple: Split dcpep/iomfb out of dcp.c For external display support DCP will use more endpoints. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/Makefile | 2 +- drivers/gpu/drm/apple/dcp-internal.h | 133 ++ drivers/gpu/drm/apple/dcp.c | 1758 +------------------- drivers/gpu/drm/apple/dcp.h | 11 + drivers/gpu/drm/apple/iomfb.c | 1626 ++++++++++++++++++ drivers/gpu/drm/apple/{dcpep.h => iomfb.h} | 48 +- 6 files changed, 1837 insertions(+), 1741 deletions(-) create mode 100644 drivers/gpu/drm/apple/dcp-internal.h create mode 100644 drivers/gpu/drm/apple/iomfb.c rename drivers/gpu/drm/apple/{dcpep.h => iomfb.h} (86%) diff --git a/drivers/gpu/drm/apple/Makefile b/drivers/gpu/drm/apple/Makefile index 8c758d5720b642..d1f909792229e5 100644 --- a/drivers/gpu/drm/apple/Makefile +++ b/drivers/gpu/drm/apple/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only OR MIT appledrm-y := apple_drv.o -apple_dcp-y := dcp.o parser.o +apple_dcp-y := dcp.o iomfb.o parser.o apple_piodma-y := dummy-piodma.o obj-$(CONFIG_DRM_APPLE) += appledrm.o diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h new file mode 100644 index 00000000000000..648d543b9cd91a --- /dev/null +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright 2021 Alyssa Rosenzweig */ + +#ifndef __APPLE_DCP_INTERNAL_H__ +#define __APPLE_DCP_INTERNAL_H__ + +#include +#include +#include + +#include "iomfb.h" + +struct apple_dcp; + +/* Temporary backing for a chunked transfer via setDCPAVPropStart/Chunk/End */ +struct dcp_chunks { + size_t length; + void *data; +}; + +#define DCP_MAX_MAPPINGS (128) /* should be enough */ +#define MAX_DISP_REGISTERS (7) + +struct dcp_mem_descriptor { + size_t size; + void *buf; + dma_addr_t dva; + struct sg_table map; + u64 reg; +}; + +/* Limit on call stack depth (arbitrary). Some nesting is required */ +#define DCP_MAX_CALL_DEPTH 8 + +typedef void (*dcp_callback_t)(struct apple_dcp *, void *, void *); + +struct dcp_call_channel { + dcp_callback_t callbacks[DCP_MAX_CALL_DEPTH]; + void *cookies[DCP_MAX_CALL_DEPTH]; + void *output[DCP_MAX_CALL_DEPTH]; + u16 end[DCP_MAX_CALL_DEPTH]; + + /* Current depth of the call stack. Less than DCP_MAX_CALL_DEPTH */ + u8 depth; +}; + +struct dcp_cb_channel { + u8 depth; + void *output[DCP_MAX_CALL_DEPTH]; +}; + +struct dcp_fb_reference { + struct list_head head; + struct drm_framebuffer *fb; +}; + +/* TODO: move IOMFB members to its own struct */ +struct apple_dcp { + struct device *dev; + struct platform_device *piodma; + struct device_link *piodma_link; + struct apple_rtkit *rtk; + struct apple_crtc *crtc; + struct apple_connector *connector; + + /* Coprocessor control register */ + void __iomem *coproc_reg; + + /* mask for DCP IO virtual addresses shared over rtkit */ + u64 asc_dram_mask; + + /* DCP has crashed */ + bool crashed; + + /************* IOMFB ************************************************** + * everything below is mostly used inside IOMFB but it could make * + * sense keep some of the the members in apple_dcp. * + **********************************************************************/ + + /* clock rate request by dcp in */ + struct clk *clk; + + /* DCP shared memory */ + void *shmem; + + /* Display registers mappable to the DCP */ + struct resource *disp_registers[MAX_DISP_REGISTERS]; + unsigned int nr_disp_registers; + + /* Bitmap of memory descriptors used for mappings made by the DCP */ + DECLARE_BITMAP(memdesc_map, DCP_MAX_MAPPINGS); + + /* Indexed table of memory descriptors */ + struct dcp_mem_descriptor memdesc[DCP_MAX_MAPPINGS]; + + struct dcp_call_channel ch_cmd, ch_oobcmd; + struct dcp_cb_channel ch_cb, ch_oobcb, ch_async; + + /* Active chunked transfer. There can only be one at a time. */ + struct dcp_chunks chunks; + + /* Queued swap. Owned by the DCP to avoid per-swap memory allocation */ + struct dcp_swap_submit_req swap; + + /* Current display mode */ + bool valid_mode; + struct dcp_set_digital_out_mode_req mode; + + /* Is the DCP booted? */ + bool active; + + /* eDP display without DP-HDMI conversion */ + bool main_display; + + bool ignore_swap_complete; + + /* Modes valid for the connected display */ + struct dcp_display_mode *modes; + unsigned int nr_modes; + + /* Attributes of the connected display */ + int width_mm, height_mm; + + /* Workqueue for sending vblank events when a dcp swap is not possible */ + struct work_struct vblank_wq; + + /* List of referenced drm_framebuffers which can be unreferenced + * on the next successfully completed swap. + */ + struct list_head swapped_out_fbs; +}; + +#endif /* __APPLE_DCP_INTERNAL_H__ */ diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 4d2f835d3d338b..6ba5b6e93bddf2 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -19,1105 +19,59 @@ #include #include -#include "dcpep.h" #include "dcp.h" +#include "dcp-internal.h" #include "parser.h" -struct apple_dcp; - -#define APPLE_DCP_COPROC_CPU_CONTROL 0x44 -#define APPLE_DCP_COPROC_CPU_CONTROL_RUN BIT(4) - -/* Register defines used in bandwidth setup structure */ -#define REG_SCRATCH (0x14) -#define REG_SCRATCH_T600X (0x988) -#define REG_DOORBELL (0x0) -#define REG_DOORBELL_BIT (2) - -#define DCP_BOOT_TIMEOUT msecs_to_jiffies(1000) - -/* Limit on call stack depth (arbitrary). Some nesting is required */ -#define DCP_MAX_CALL_DEPTH 8 - -typedef void (*dcp_callback_t)(struct apple_dcp *, void *, void *); - -struct dcp_call_channel { - dcp_callback_t callbacks[DCP_MAX_CALL_DEPTH]; - void *cookies[DCP_MAX_CALL_DEPTH]; - void *output[DCP_MAX_CALL_DEPTH]; - u16 end[DCP_MAX_CALL_DEPTH]; - - /* Current depth of the call stack. Less than DCP_MAX_CALL_DEPTH */ - u8 depth; -}; - -struct dcp_cb_channel { - u8 depth; - void *output[DCP_MAX_CALL_DEPTH]; -}; - -/* Temporary backing for a chunked transfer via setDCPAVPropStart/Chunk/End */ -struct dcp_chunks { - size_t length; - void *data; -}; - -#define DCP_MAX_MAPPINGS (128) /* should be enough */ -#define MAX_DISP_REGISTERS (7) - -struct dcp_mem_descriptor { - size_t size; - void *buf; - dma_addr_t dva; - struct sg_table map; - u64 reg; -}; - -struct apple_dcp { - struct device *dev; - struct platform_device *piodma; - struct device_link *piodma_link; - struct apple_rtkit *rtk; - struct apple_crtc *crtc; - struct apple_connector *connector; - - /* DCP has crashed */ - bool crashed; - - /* clock rate request by dcp in */ - struct clk *clk; - - /* mask for DCP IO virtual addresses shared over rtkit */ - u64 asc_dram_mask; - - /* DCP shared memory */ - void *shmem; - - /* Coprocessor control register */ - void __iomem *coproc_reg; - - /* Display registers mappable to the DCP */ - struct resource *disp_registers[MAX_DISP_REGISTERS]; - unsigned int nr_disp_registers; - - /* Bitmap of memory descriptors used for mappings made by the DCP */ - DECLARE_BITMAP(memdesc_map, DCP_MAX_MAPPINGS); - - /* Indexed table of memory descriptors */ - struct dcp_mem_descriptor memdesc[DCP_MAX_MAPPINGS]; - - struct dcp_call_channel ch_cmd, ch_oobcmd; - struct dcp_cb_channel ch_cb, ch_oobcb, ch_async; - - /* Active chunked transfer. There can only be one at a time. */ - struct dcp_chunks chunks; - - /* Queued swap. Owned by the DCP to avoid per-swap memory allocation */ - struct dcp_swap_submit_req swap; - - /* Current display mode */ - bool valid_mode; - struct dcp_set_digital_out_mode_req mode; - - /* Is the DCP booted? */ - bool active; - - /* eDP display without DP-HDMI conversion */ - bool main_display; - - bool ignore_swap_complete; - - /* Modes valid for the connected display */ - struct dcp_display_mode *modes; - unsigned int nr_modes; - - /* Attributes of the connected display */ - int width_mm, height_mm; - - /* Workqueue for sending vblank events when a dcp swap is not possible */ - struct work_struct vblank_wq; - - /* List of referenced drm_framebuffers which can be unreferenced - * on the next successfully completed swap. - */ - struct list_head swapped_out_fbs; -}; - -struct dcp_fb_reference { - struct list_head head; - struct drm_framebuffer *fb; -}; - -struct dcp_wait_cookie { - struct completion done; - atomic_t refcount; -}; - -/* - * A channel is busy if we have sent a message that has yet to be - * acked. The driver must not sent a message to a busy channel. - */ -static bool dcp_channel_busy(struct dcp_call_channel *ch) -{ - return (ch->depth != 0); -} - -/* Get a call channel for a context */ -static struct dcp_call_channel * -dcp_get_call_channel(struct apple_dcp *dcp, enum dcp_context_id context) -{ - switch (context) { - case DCP_CONTEXT_CMD: - case DCP_CONTEXT_CB: - return &dcp->ch_cmd; - case DCP_CONTEXT_OOBCMD: - case DCP_CONTEXT_OOBCB: - return &dcp->ch_oobcmd; - default: - return NULL; - } -} - -/* - * Get the context ID passed to the DCP for a command we push. The rule is - * simple: callback contexts are used when replying to the DCP, command - * contexts are used otherwise. That corresponds to a non/zero call stack - * depth. This rule frees the caller from tracking the call context manually. - */ -static enum dcp_context_id dcp_call_context(struct apple_dcp *dcp, bool oob) -{ - u8 depth = oob ? dcp->ch_oobcmd.depth : dcp->ch_cmd.depth; - - if (depth) - return oob ? DCP_CONTEXT_OOBCB : DCP_CONTEXT_CB; - else - return oob ? DCP_CONTEXT_OOBCMD : DCP_CONTEXT_CMD; -} - -/* Get a callback channel for a context */ -static struct dcp_cb_channel *dcp_get_cb_channel(struct apple_dcp *dcp, - enum dcp_context_id context) -{ - switch (context) { - case DCP_CONTEXT_CB: - return &dcp->ch_cb; - case DCP_CONTEXT_OOBCB: - return &dcp->ch_oobcb; - case DCP_CONTEXT_ASYNC: - return &dcp->ch_async; - default: - return NULL; - } -} - -/* Get the start of a packet: after the end of the previous packet */ -static u16 dcp_packet_start(struct dcp_call_channel *ch, u8 depth) -{ - if (depth > 0) - return ch->end[depth - 1]; - else - return 0; -} - -/* Pushes and pops the depth of the call stack with safety checks */ -static u8 dcp_push_depth(u8 *depth) -{ - u8 ret = (*depth)++; - - WARN_ON(ret >= DCP_MAX_CALL_DEPTH); - return ret; -} - -static u8 dcp_pop_depth(u8 *depth) -{ - WARN_ON((*depth) == 0); - - return --(*depth); -} - -#define DCP_METHOD(tag, name) [name] = { #name, tag } - -const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { - DCP_METHOD("A000", dcpep_late_init_signal), - DCP_METHOD("A029", dcpep_setup_video_limits), - DCP_METHOD("A034", dcpep_update_notify_clients_dcp), - DCP_METHOD("A357", dcpep_set_create_dfb), - DCP_METHOD("A401", dcpep_start_signal), - DCP_METHOD("A407", dcpep_swap_start), - DCP_METHOD("A408", dcpep_swap_submit), - DCP_METHOD("A410", dcpep_set_display_device), - DCP_METHOD("A411", dcpep_is_main_display), - DCP_METHOD("A412", dcpep_set_digital_out_mode), - DCP_METHOD("A439", dcpep_set_parameter_dcp), - DCP_METHOD("A443", dcpep_create_default_fb), - DCP_METHOD("A447", dcpep_enable_disable_video_power_savings), - DCP_METHOD("A454", dcpep_first_client_open), - DCP_METHOD("A460", dcpep_set_display_refresh_properties), - DCP_METHOD("A463", dcpep_flush_supports_power), - DCP_METHOD("A468", dcpep_set_power_state), -}; - -/* Call a DCP function given by a tag */ -static void dcp_push(struct apple_dcp *dcp, bool oob, enum dcpep_method method, - u32 in_len, u32 out_len, void *data, dcp_callback_t cb, - void *cookie) -{ - struct dcp_call_channel *ch = oob ? &dcp->ch_oobcmd : &dcp->ch_cmd; - enum dcp_context_id context = dcp_call_context(dcp, oob); - - struct dcp_packet_header header = { - .in_len = in_len, - .out_len = out_len, - - /* Tag is reversed due to endianness of the fourcc */ - .tag[0] = dcp_methods[method].tag[3], - .tag[1] = dcp_methods[method].tag[2], - .tag[2] = dcp_methods[method].tag[1], - .tag[3] = dcp_methods[method].tag[0], - }; - - u8 depth = dcp_push_depth(&ch->depth); - u16 offset = dcp_packet_start(ch, depth); - - void *out = dcp->shmem + dcp_tx_offset(context) + offset; - void *out_data = out + sizeof(header); - size_t data_len = sizeof(header) + in_len + out_len; - - memcpy(out, &header, sizeof(header)); - - if (in_len > 0) - memcpy(out_data, data, in_len); - - dev_dbg(dcp->dev, "---> %s: context %u, offset %u, depth %u\n", - dcp_methods[method].name, context, offset, depth); - - ch->callbacks[depth] = cb; - ch->cookies[depth] = cookie; - ch->output[depth] = out + sizeof(header) + in_len; - ch->end[depth] = offset + ALIGN(data_len, DCP_PACKET_ALIGNMENT); - - apple_rtkit_send_message(dcp->rtk, DCP_ENDPOINT, - dcpep_msg(context, data_len, offset), - NULL, false); -} - -#define DCP_THUNK_VOID(func, handle) \ - static void func(struct apple_dcp *dcp, bool oob, dcp_callback_t cb, \ - void *cookie) \ - { \ - dcp_push(dcp, oob, handle, 0, 0, NULL, cb, cookie); \ - } - -#define DCP_THUNK_OUT(func, handle, T) \ - static void func(struct apple_dcp *dcp, bool oob, dcp_callback_t cb, \ - void *cookie) \ - { \ - dcp_push(dcp, oob, handle, 0, sizeof(T), NULL, cb, cookie); \ - } - -#define DCP_THUNK_IN(func, handle, T) \ - static void func(struct apple_dcp *dcp, bool oob, T *data, \ - dcp_callback_t cb, void *cookie) \ - { \ - dcp_push(dcp, oob, handle, sizeof(T), 0, data, cb, cookie); \ - } - -#define DCP_THUNK_INOUT(func, handle, T_in, T_out) \ - static void func(struct apple_dcp *dcp, bool oob, T_in *data, \ - dcp_callback_t cb, void *cookie) \ - { \ - dcp_push(dcp, oob, handle, sizeof(T_in), sizeof(T_out), data, \ - cb, cookie); \ - } - -DCP_THUNK_INOUT(dcp_swap_submit, dcpep_swap_submit, struct dcp_swap_submit_req, - struct dcp_swap_submit_resp); - -DCP_THUNK_INOUT(dcp_swap_start, dcpep_swap_start, struct dcp_swap_start_req, - struct dcp_swap_start_resp); - -DCP_THUNK_INOUT(dcp_set_power_state, dcpep_set_power_state, - struct dcp_set_power_state_req, - struct dcp_set_power_state_resp); - -DCP_THUNK_INOUT(dcp_set_digital_out_mode, dcpep_set_digital_out_mode, - struct dcp_set_digital_out_mode_req, u32); - -DCP_THUNK_INOUT(dcp_set_display_device, dcpep_set_display_device, u32, u32); - -DCP_THUNK_OUT(dcp_set_display_refresh_properties, - dcpep_set_display_refresh_properties, u32); - -DCP_THUNK_OUT(dcp_late_init_signal, dcpep_late_init_signal, u32); -DCP_THUNK_IN(dcp_flush_supports_power, dcpep_flush_supports_power, u32); -DCP_THUNK_OUT(dcp_create_default_fb, dcpep_create_default_fb, u32); -DCP_THUNK_OUT(dcp_start_signal, dcpep_start_signal, u32); -DCP_THUNK_VOID(dcp_setup_video_limits, dcpep_setup_video_limits); -DCP_THUNK_VOID(dcp_set_create_dfb, dcpep_set_create_dfb); -DCP_THUNK_VOID(dcp_first_client_open, dcpep_first_client_open); - -__attribute__((unused)) -DCP_THUNK_IN(dcp_update_notify_clients_dcp, dcpep_update_notify_clients_dcp, - struct dcp_update_notify_clients_dcp); - -DCP_THUNK_INOUT(dcp_set_parameter_dcp, dcpep_set_parameter_dcp, - struct dcp_set_parameter_dcp, u32); - -DCP_THUNK_INOUT(dcp_enable_disable_video_power_savings, - dcpep_enable_disable_video_power_savings, - u32, int); - -DCP_THUNK_OUT(dcp_is_main_display, dcpep_is_main_display, u32); - -/* Parse a callback tag "D123" into the ID 123. Returns -EINVAL on failure. */ -static int dcp_parse_tag(char tag[4]) -{ - u32 d[3]; - int i; - - if (tag[3] != 'D') - return -EINVAL; - - for (i = 0; i < 3; ++i) { - d[i] = (u32)(tag[i] - '0'); - - if (d[i] > 9) - return -EINVAL; - } - - return d[0] + (d[1] * 10) + (d[2] * 100); -} - -/* Ack a callback from the DCP */ -static void dcp_ack(struct apple_dcp *dcp, enum dcp_context_id context) -{ - struct dcp_cb_channel *ch = dcp_get_cb_channel(dcp, context); - - dcp_pop_depth(&ch->depth); - apple_rtkit_send_message(dcp->rtk, DCP_ENDPOINT, dcpep_ack(context), - NULL, false); -} - -/* DCP callback handlers */ -static void dcpep_cb_nop(struct apple_dcp *dcp) -{ - /* No operation */ -} - -static u8 dcpep_cb_true(struct apple_dcp *dcp) -{ - return true; -} - -static u8 dcpep_cb_false(struct apple_dcp *dcp) -{ - return false; -} - -static u32 dcpep_cb_zero(struct apple_dcp *dcp) -{ - return 0; -} - -/* HACK: moved here to avoid circular dependency between apple_drv and dcp */ -void dcp_drm_crtc_vblank(struct apple_crtc *crtc) -{ - unsigned long flags; - - if (crtc->vsync_disabled) - return; - - drm_crtc_handle_vblank(&crtc->base); - - spin_lock_irqsave(&crtc->base.dev->event_lock, flags); - if (crtc->event) { - drm_crtc_send_vblank_event(&crtc->base, crtc->event); - drm_crtc_vblank_put(&crtc->base); - crtc->event = NULL; - } - spin_unlock_irqrestore(&crtc->base.dev->event_lock, flags); -} - -static void dcpep_cb_swap_complete(struct apple_dcp *dcp, - struct dc_swap_complete_resp *resp) -{ - dev_dbg(dcp->dev, "swap complete for swap_id: %u vblank: %u", - resp->swap_id, dcp->ignore_swap_complete); - - if (!dcp->ignore_swap_complete) - dcp_drm_crtc_vblank(dcp->crtc); -} - -static struct dcp_get_uint_prop_resp -dcpep_cb_get_uint_prop(struct apple_dcp *dcp, struct dcp_get_uint_prop_req *req) -{ - /* unimplemented for now */ - return (struct dcp_get_uint_prop_resp) { - .value = 0 - }; -} - -/* - * Callback to map a buffer allocated with allocate_buf for PIODMA usage. - * PIODMA is separate from the main DCP and uses own IOVA space on a dedicated - * stream of the display DART, rather than the expected DCP DART. - * - * XXX: This relies on dma_get_sgtable in concert with dma_map_sgtable, which - * is a "fundamentally unsafe" operation according to the docs. And yet - * everyone does it... - */ -static struct dcp_map_buf_resp -dcpep_cb_map_piodma(struct apple_dcp *dcp, struct dcp_map_buf_req *req) -{ - struct sg_table *map; - int ret; - - if (req->buffer >= ARRAY_SIZE(dcp->memdesc)) - goto reject; - - map = &dcp->memdesc[req->buffer].map; - - if (!map->sgl) - goto reject; - - /* Use PIODMA device instead of DCP to map against the right IOMMU. */ - ret = dma_map_sgtable(&dcp->piodma->dev, map, DMA_BIDIRECTIONAL, 0); - - if (ret) - goto reject; - - return (struct dcp_map_buf_resp) { - .dva = sg_dma_address(map->sgl) - }; - -reject: - dev_err(dcp->dev, "denying map of invalid buffer %llx for pidoma\n", - req->buffer); - return (struct dcp_map_buf_resp) { - .ret = EINVAL - }; -} - -static void -dcpep_cb_unmap_piodma(struct apple_dcp *dcp, struct dcp_unmap_buf_resp *resp) -{ - struct sg_table *map; - dma_addr_t dma_addr; - - if (resp->buffer >= ARRAY_SIZE(dcp->memdesc)) { - dev_warn(dcp->dev, "unmap request for out of range buffer %llu", - resp->buffer); - return; - } - - map = &dcp->memdesc[resp->buffer].map; - - if (!map->sgl) { - dev_warn(dcp->dev, "unmap for non-mapped buffer %llu iova:0x%08llx", - resp->buffer, resp->dva); - return; - } - - dma_addr = sg_dma_address(map->sgl); - if (dma_addr != resp->dva) { - dev_warn(dcp->dev, "unmap buffer %llu address mismatch dma_addr:%llx dva:%llx", - resp->buffer, dma_addr, resp->dva); - return; - } - - /* Use PIODMA device instead of DCP to unmap from the right IOMMU. */ - dma_unmap_sgtable(&dcp->piodma->dev, map, DMA_BIDIRECTIONAL, 0); -} - -/* - * Allocate an IOVA contiguous buffer mapped to the DCP. The buffer need not be - * physically contigiuous, however we should save the sgtable in case the - * buffer needs to be later mapped for PIODMA. - */ -static struct dcp_allocate_buffer_resp -dcpep_cb_allocate_buffer(struct apple_dcp *dcp, struct dcp_allocate_buffer_req *req) -{ - struct dcp_allocate_buffer_resp resp = { 0 }; - struct dcp_mem_descriptor *memdesc; - u32 id; - - resp.dva_size = ALIGN(req->size, 4096); - resp.mem_desc_id = find_first_zero_bit(dcp->memdesc_map, DCP_MAX_MAPPINGS); - - if (resp.mem_desc_id >= DCP_MAX_MAPPINGS) { - dev_warn(dcp->dev, "DCP overflowed mapping table, ignoring"); - resp.dva_size = 0; - resp.mem_desc_id = 0; - return resp; - } - id = resp.mem_desc_id; - set_bit(id, dcp->memdesc_map); - - memdesc = &dcp->memdesc[id]; - - memdesc->size = resp.dva_size; - memdesc->buf = dma_alloc_coherent(dcp->dev, memdesc->size, &memdesc->dva, - GFP_KERNEL); - - dma_get_sgtable(dcp->dev, &memdesc->map, memdesc->buf, - memdesc->dva, memdesc->size); - resp.dva = memdesc->dva; - - return resp; -} - -static u8 dcpep_cb_release_mem_desc(struct apple_dcp *dcp, u32 *mem_desc_id) -{ - struct dcp_mem_descriptor *memdesc; - u32 id = *mem_desc_id; - - if (id >= DCP_MAX_MAPPINGS) { - dev_warn(dcp->dev, "unmap request for out of range mem_desc_id %u", - id); - return 0; - } - - if (!test_and_clear_bit(id, dcp->memdesc_map)) { - dev_warn(dcp->dev, "unmap request for unused mem_desc_id %u", - id); - return 0; - } - - memdesc = &dcp->memdesc[id]; - if (memdesc->buf) { - - dma_free_coherent(dcp->dev, memdesc->size, memdesc->buf, - memdesc->dva); - - memdesc->buf = NULL; - memset(&memdesc->map, 0, sizeof(memdesc->map)); - } else { - memdesc->reg = 0; - } - - memdesc->size = 0; - - return 1; -} - -/* Validate that the specified region is a display register */ -static bool is_disp_register(struct apple_dcp *dcp, u64 start, u64 end) -{ - int i; - - for (i = 0; i < dcp->nr_disp_registers; ++i) { - struct resource *r = dcp->disp_registers[i]; - - if ((start >= r->start) && (end <= r->end)) - return true; - } - - return false; -} - -/* - * Map contiguous physical memory into the DCP's address space. The firmware - * uses this to map the display registers we advertise in - * sr_map_device_memory_with_index, so we bounds check against that to guard - * safe against malicious coprocessors. - */ -static struct dcp_map_physical_resp -dcpep_cb_map_physical(struct apple_dcp *dcp, struct dcp_map_physical_req *req) -{ - int size = ALIGN(req->size, 4096); - u32 id; - - if (!is_disp_register(dcp, req->paddr, req->paddr + size - 1)) { - dev_err(dcp->dev, "refusing to map phys address %llx size %llx", - req->paddr, req->size); - return (struct dcp_map_physical_resp) { }; - } - - id = find_first_zero_bit(dcp->memdesc_map, DCP_MAX_MAPPINGS); - set_bit(id, dcp->memdesc_map); - dcp->memdesc[id].size = size; - dcp->memdesc[id].reg = req->paddr; - - return (struct dcp_map_physical_resp) { - .dva_size = size, - .mem_desc_id = id, - .dva = dma_map_resource(dcp->dev, req->paddr, size, - DMA_BIDIRECTIONAL, 0), - }; -} - -static u64 dcpep_cb_get_frequency(struct apple_dcp *dcp) -{ - return clk_get_rate(dcp->clk); -} - -static struct dcp_map_reg_resp -dcpep_cb_map_reg(struct apple_dcp *dcp, struct dcp_map_reg_req *req) -{ - if (req->index >= dcp->nr_disp_registers) { - dev_warn(dcp->dev, "attempted to read invalid reg index %u", - req->index); - - return (struct dcp_map_reg_resp) { - .ret = 1 - }; - } else { - struct resource *rsrc = dcp->disp_registers[req->index]; - - return (struct dcp_map_reg_resp) { - .addr = rsrc->start, - .length = resource_size(rsrc) - }; - } -} - -static struct dcp_read_edt_data_resp -dcpep_cb_read_edt_data(struct apple_dcp *dcp, struct dcp_read_edt_data_req *req) -{ - return (struct dcp_read_edt_data_resp) { - .value[0] = req->value[0], - .ret = 0, - }; -} - -/* Chunked data transfer for property dictionaries */ -static u8 dcpep_cb_prop_start(struct apple_dcp *dcp, u32 *length) -{ - if (dcp->chunks.data != NULL) { - dev_warn(dcp->dev, "ignoring spurious transfer start\n"); - return false; - } - - dcp->chunks.length = *length; - dcp->chunks.data = devm_kzalloc(dcp->dev, *length, GFP_KERNEL); - - if (!dcp->chunks.data) { - dev_warn(dcp->dev, "failed to allocate chunks\n"); - return false; - } - - return true; -} - -static u8 dcpep_cb_prop_chunk(struct apple_dcp *dcp, - struct dcp_set_dcpav_prop_chunk_req *req) -{ - if (!dcp->chunks.data) { - dev_warn(dcp->dev, "ignoring spurious chunk\n"); - return false; - } - - if (req->offset + req->length > dcp->chunks.length) { - dev_warn(dcp->dev, "ignoring overflowing chunk\n"); - return false; - } - - memcpy(dcp->chunks.data + req->offset, req->data, req->length); - return true; -} - -static void dcp_set_dimensions(struct apple_dcp *dcp) -{ - int i; - - /* Set the connector info */ - if (dcp->connector) { - struct drm_connector *connector = &dcp->connector->base; - - mutex_lock(&connector->dev->mode_config.mutex); - connector->display_info.width_mm = dcp->width_mm; - connector->display_info.height_mm = dcp->height_mm; - mutex_unlock(&connector->dev->mode_config.mutex); - } - - /* - * Fix up any probed modes. Modes are created when parsing - * TimingElements, dimensions are calculated when parsing - * DisplayAttributes, and TimingElements may be sent first - */ - for (i = 0; i < dcp->nr_modes; ++i) { - dcp->modes[i].mode.width_mm = dcp->width_mm; - dcp->modes[i].mode.height_mm = dcp->height_mm; - } -} - -static bool dcpep_process_chunks(struct apple_dcp *dcp, - struct dcp_set_dcpav_prop_end_req *req) -{ - struct dcp_parse_ctx ctx; - int ret; - - if (!dcp->chunks.data) { - dev_warn(dcp->dev, "ignoring spurious end\n"); - return false; - } - - ret = parse(dcp->chunks.data, dcp->chunks.length, &ctx); - - if (ret) { - dev_warn(dcp->dev, "bad header on dcpav props\n"); - return false; - } - - if (!strcmp(req->key, "TimingElements")) { - dcp->modes = enumerate_modes(&ctx, &dcp->nr_modes, - dcp->width_mm, dcp->height_mm); - - if (IS_ERR(dcp->modes)) { - dev_warn(dcp->dev, "failed to parse modes\n"); - dcp->modes = NULL; - dcp->nr_modes = 0; - return false; - } - } else if (!strcmp(req->key, "DisplayAttributes")) { - ret = parse_display_attributes(&ctx, &dcp->width_mm, - &dcp->height_mm); - - if (ret) { - dev_warn(dcp->dev, "failed to parse display attribs\n"); - return false; - } - - dcp_set_dimensions(dcp); - } - - return true; -} - -static u8 dcpep_cb_prop_end(struct apple_dcp *dcp, - struct dcp_set_dcpav_prop_end_req *req) -{ - u8 resp = dcpep_process_chunks(dcp, req); - - /* Reset for the next transfer */ - devm_kfree(dcp->dev, dcp->chunks.data); - dcp->chunks.data = NULL; - - return resp; -} - -/* Boot sequence */ -static void boot_done(struct apple_dcp *dcp, void *out, void *cookie) -{ - struct dcp_cb_channel *ch = &dcp->ch_cb; - u8 *succ = ch->output[ch->depth - 1]; - dev_dbg(dcp->dev, "boot done"); - - *succ = true; - dcp_ack(dcp, DCP_CONTEXT_CB); -} - -static void boot_5(struct apple_dcp *dcp, void *out, void *cookie) -{ - dcp_set_display_refresh_properties(dcp, false, boot_done, NULL); -} - -static void boot_4(struct apple_dcp *dcp, void *out, void *cookie) -{ - dcp_late_init_signal(dcp, false, boot_5, NULL); -} - -static void boot_3(struct apple_dcp *dcp, void *out, void *cookie) -{ - u32 v_true = true; - - dcp_flush_supports_power(dcp, false, &v_true, boot_4, NULL); -} - -static void boot_2(struct apple_dcp *dcp, void *out, void *cookie) -{ - dcp_setup_video_limits(dcp, false, boot_3, NULL); -} - -static void boot_1_5(struct apple_dcp *dcp, void *out, void *cookie) -{ - dcp_create_default_fb(dcp, false, boot_2, NULL); -} - -/* Use special function signature to defer the ACK */ -static bool dcpep_cb_boot_1(struct apple_dcp *dcp, int tag, void *out, void *in) -{ - dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, __func__); - dcp_set_create_dfb(dcp, false, boot_1_5, NULL); - return false; -} - -static struct dcp_rt_bandwidth dcpep_cb_rt_bandwidth(struct apple_dcp *dcp) -{ - if (dcp->disp_registers[5] && dcp->disp_registers[6]) - return (struct dcp_rt_bandwidth) { - .reg_scratch = dcp->disp_registers[5]->start + REG_SCRATCH, - .reg_doorbell = dcp->disp_registers[6]->start + REG_DOORBELL, - .doorbell_bit = REG_DOORBELL_BIT, - - .padding[3] = 0x4, // XXX: required by 11.x firmware - }; - else if (dcp->disp_registers[4]) - return (struct dcp_rt_bandwidth) { - .reg_scratch = dcp->disp_registers[4]->start + REG_SCRATCH_T600X, - .reg_doorbell = 0, - .doorbell_bit = 0, - }; - else - return (struct dcp_rt_bandwidth) { - .reg_scratch = 0, - .reg_doorbell = 0, - .doorbell_bit = 0, - }; -} - -/* Callback to get the current time as milliseconds since the UNIX epoch */ -static u64 dcpep_cb_get_time(struct apple_dcp *dcp) -{ - return ktime_to_ms(ktime_get_real()); -} - -struct dcp_swap_cookie { - struct completion done; - atomic_t refcount; - u32 swap_id; -}; - -static void dcp_swap_cleared(struct apple_dcp *dcp, void *data, void *cookie) -{ - struct dcp_swap_submit_resp *resp = data; - dev_dbg(dcp->dev, "%s", __func__); - - if (cookie) { - struct dcp_swap_cookie *info = cookie; - complete(&info->done); - if (atomic_dec_and_test(&info->refcount)) - kfree(info); - } - - if (resp->ret) { - dev_err(dcp->dev, "swap_clear failed! status %u\n", resp->ret); - dcp_drm_crtc_vblank(dcp->crtc); - return; - } - - while (!list_empty(&dcp->swapped_out_fbs)) { - struct dcp_fb_reference *entry; - entry = list_first_entry(&dcp->swapped_out_fbs, - struct dcp_fb_reference, head); - if (entry->fb) - drm_framebuffer_put(entry->fb); - list_del(&entry->head); - kfree(entry); - } -} - -static void dcp_swap_clear_started(struct apple_dcp *dcp, void *data, - void *cookie) -{ - struct dcp_swap_start_resp *resp = data; - dev_dbg(dcp->dev, "%s swap_id: %u", __func__, resp->swap_id); - dcp->swap.swap.swap_id = resp->swap_id; - - if (cookie) { - struct dcp_swap_cookie *info = cookie; - info->swap_id = resp->swap_id; - } - - dcp_swap_submit(dcp, false, &dcp->swap, dcp_swap_cleared, cookie); -} - -static void dcp_on_final(struct apple_dcp *dcp, void *out, void *cookie) -{ - struct dcp_wait_cookie *wait = cookie; - dev_dbg(dcp->dev, "%s", __func__); - - if (wait) { - complete(&wait->done); - if (atomic_dec_and_test(&wait->refcount)) - kfree(wait); - } -} +#define APPLE_DCP_COPROC_CPU_CONTROL 0x44 +#define APPLE_DCP_COPROC_CPU_CONTROL_RUN BIT(4) -static void dcp_on_set_parameter(struct apple_dcp *dcp, void *out, void *cookie) -{ - struct dcp_set_parameter_dcp param = { - .param = 14, - .value = { 0 }, - .count = 1, - }; - dev_dbg(dcp->dev, "%s", __func__); - - dcp_set_parameter_dcp(dcp, false, ¶m, dcp_on_final, cookie); -} +#define DCP_BOOT_TIMEOUT msecs_to_jiffies(1000) -void dcp_poweron(struct platform_device *pdev) +/* HACK: moved here to avoid circular dependency between apple_drv and dcp */ +void dcp_drm_crtc_vblank(struct apple_crtc *crtc) { - struct apple_dcp *dcp = platform_get_drvdata(pdev); - struct dcp_wait_cookie * cookie; - struct dcp_set_power_state_req req = { - .unklong = 1, - }; - int ret; - u32 handle; - dev_dbg(dcp->dev, "%s", __func__); + unsigned long flags; - cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); - if (!cookie) + if (crtc->vsync_disabled) return; - init_completion(&cookie->done); - atomic_set(&cookie->refcount, 2); - - if (dcp->main_display) { - handle = 0; - dcp_set_display_device(dcp, false, &handle, dcp_on_final, cookie); - } else { - handle = 2; - dcp_set_display_device(dcp, false, &handle, dcp_on_set_parameter, cookie); - } - dcp_set_power_state(dcp, true, &req, NULL, NULL); - - ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(500)); - - if (ret == 0) - dev_warn(dcp->dev, "wait for power timed out"); - - if (atomic_dec_and_test(&cookie->refcount)) - kfree(cookie); -} -EXPORT_SYMBOL(dcp_poweron); - -static void complete_set_powerstate(struct apple_dcp *dcp, void *out, void *cookie) -{ - struct dcp_wait_cookie *wait = cookie; + drm_crtc_handle_vblank(&crtc->base); - if (wait) { - complete(&wait->done); - if (atomic_dec_and_test(&wait->refcount)) - kfree(wait); + spin_lock_irqsave(&crtc->base.dev->event_lock, flags); + if (crtc->event) { + drm_crtc_send_vblank_event(&crtc->base, crtc->event); + drm_crtc_vblank_put(&crtc->base); + crtc->event = NULL; } + spin_unlock_irqrestore(&crtc->base.dev->event_lock, flags); } -void dcp_poweroff(struct platform_device *pdev) +void dcp_set_dimensions(struct apple_dcp *dcp) { - struct apple_dcp *dcp = platform_get_drvdata(pdev); - int ret, swap_id; - struct dcp_set_power_state_req power_req = { - .unklong = 0, - }; - struct dcp_swap_cookie *cookie; - struct dcp_wait_cookie *poff_cookie; - struct dcp_swap_start_req swap_req= { 0 }; - - dev_dbg(dcp->dev, "%s", __func__); - - cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); - if (!cookie) - return; - init_completion(&cookie->done); - atomic_set(&cookie->refcount, 2); - - // clear surfaces - memset(&dcp->swap, 0, sizeof(dcp->swap)); - - dcp->swap.swap.swap_enabled = DCP_REMOVE_LAYERS | 0x7; - dcp->swap.swap.swap_completed = DCP_REMOVE_LAYERS | 0x7; - dcp->swap.swap.unk_10c = 0xFF000000; - - for (int l = 0; l < SWAP_SURFACES; l++) - dcp->swap.surf_null[l] = true; + int i; - dcp_swap_start(dcp, false, &swap_req, dcp_swap_clear_started, cookie); + /* Set the connector info */ + if (dcp->connector) { + struct drm_connector *connector = &dcp->connector->base; - ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(50)); - swap_id = cookie->swap_id; - if (atomic_dec_and_test(&cookie->refcount)) - kfree(cookie); - if (ret <= 0) { - dcp->crashed = true; - return; + mutex_lock(&connector->dev->mode_config.mutex); + connector->display_info.width_mm = dcp->width_mm; + connector->display_info.height_mm = dcp->height_mm; + mutex_unlock(&connector->dev->mode_config.mutex); } - dev_dbg(dcp->dev, "%s: clear swap submitted: %u", __func__, swap_id); - - poff_cookie = kzalloc(sizeof(*poff_cookie), GFP_KERNEL); - if (!poff_cookie) - return; - init_completion(&poff_cookie->done); - atomic_set(&poff_cookie->refcount, 2); - - dcp_set_power_state(dcp, false, &power_req, complete_set_powerstate, poff_cookie); - ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(1000)); - - if (ret == 0) - dev_warn(dcp->dev, "setPowerState(0) timeout %u ms", 1000); - else if (ret > 0) - dev_dbg(dcp->dev, "setPowerState(0) finished with %d ms to spare", - jiffies_to_msecs(ret)); - - if (atomic_dec_and_test(&poff_cookie->refcount)) - kfree(poff_cookie); - dev_dbg(dcp->dev, "%s: setPowerState(0) done", __func__); -} -EXPORT_SYMBOL(dcp_poweroff); - -/* - * Helper to send a DRM hotplug event. The DCP is accessed from a single - * (RTKit) thread. To handle hotplug callbacks, we need to call - * drm_kms_helper_hotplug_event, which does an atomic commit (via DCP) and - * waits for vblank (a DCP callback). That means we deadlock if we call from - * the RTKit thread! Instead, move the call to another thread via a workqueue. - */ -void dcp_hotplug(struct work_struct *work) -{ - struct apple_connector *connector; - struct drm_device *dev; - struct apple_dcp *dcp; - - connector = container_of(work, struct apple_connector, hotplug_wq); - dev = connector->base.dev; - - dcp = platform_get_drvdata(connector->dcp); - dev_info(dcp->dev, "%s: connected: %d", __func__, connector->connected); - /* - * DCP defers link training until we set a display mode. But we set - * display modes from atomic_flush, so userspace needs to trigger a - * flush, or the CRTC gets no signal. + * Fix up any probed modes. Modes are created when parsing + * TimingElements, dimensions are calculated when parsing + * DisplayAttributes, and TimingElements may be sent first */ - if (!dcp->valid_mode && connector->connected) { - drm_connector_set_link_status_property( - &connector->base, DRM_MODE_LINK_STATUS_BAD); - } - - if (dev && dev->registered) - drm_kms_helper_hotplug_event(dev); -} -EXPORT_SYMBOL_GPL(dcp_hotplug); - -static void dcpep_cb_hotplug(struct apple_dcp *dcp, u64 *connected) -{ - struct apple_connector *connector = dcp->connector; - - /* Hotplug invalidates mode. DRM doesn't always handle this. */ - if (!(*connected)) { - dcp->valid_mode = false; - /* after unplug swap will not complete until the next - * set_digital_out_mode */ - schedule_work(&dcp->vblank_wq); - } - - if (connector && connector->connected != !!(*connected)) { - connector->connected = !!(*connected); - dcp->valid_mode = false; - schedule_work(&connector->hotplug_wq); + for (i = 0; i < dcp->nr_modes; ++i) { + dcp->modes[i].mode.width_mm = dcp->width_mm; + dcp->modes[i].mode.height_mm = dcp->height_mm; } } -static void -dcpep_cb_swap_complete_intent_gated(struct apple_dcp *dcp, - struct dcp_swap_complete_intent_gated *info) -{ - dev_dbg(dcp->dev, "swap_id:%u width:%u height:%u", info->swap_id, - info->width, info->height); -} - /* * Helper to send a DRM vblank event. We do not know how call swap_submit_dcp * without surfaces. To avoid timeouts in drm_atomic_helper_wait_for_vblanks @@ -1132,597 +86,16 @@ static void dcp_delayed_vblank(struct work_struct *work) dcp_drm_crtc_vblank(dcp->crtc); } - -#define DCPEP_MAX_CB (1000) - -/* - * Define type-safe trampolines. Define typedefs to enforce type-safety on the - * input data (so if the types don't match, gcc errors out). - */ - -#define TRAMPOLINE_VOID(func, handler) \ - static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ - { \ - dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, #handler); \ - handler(dcp); \ - return true; \ - } - -#define TRAMPOLINE_IN(func, handler, T_in) \ - typedef void (*callback_##handler)(struct apple_dcp *, T_in *); \ - \ - static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ - { \ - callback_##handler cb = handler; \ - \ - dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, #handler); \ - cb(dcp, in); \ - return true; \ - } - -#define TRAMPOLINE_INOUT(func, handler, T_in, T_out) \ - typedef T_out (*callback_##handler)(struct apple_dcp *, T_in *); \ - \ - static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ - { \ - T_out *typed_out = out; \ - callback_##handler cb = handler; \ - \ - dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, #handler); \ - *typed_out = cb(dcp, in); \ - return true; \ - } - -#define TRAMPOLINE_OUT(func, handler, T_out) \ - static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ - { \ - T_out *typed_out = out; \ - \ - dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, #handler); \ - *typed_out = handler(dcp); \ - return true; \ - } - -TRAMPOLINE_VOID(trampoline_nop, dcpep_cb_nop); -TRAMPOLINE_OUT(trampoline_true, dcpep_cb_true, u8); -TRAMPOLINE_OUT(trampoline_false, dcpep_cb_false, u8); -TRAMPOLINE_OUT(trampoline_zero, dcpep_cb_zero, u32); -TRAMPOLINE_IN(trampoline_swap_complete, dcpep_cb_swap_complete, - struct dc_swap_complete_resp); -TRAMPOLINE_INOUT(trampoline_get_uint_prop, dcpep_cb_get_uint_prop, - struct dcp_get_uint_prop_req, struct dcp_get_uint_prop_resp); -TRAMPOLINE_INOUT(trampoline_map_piodma, dcpep_cb_map_piodma, - struct dcp_map_buf_req, struct dcp_map_buf_resp); -TRAMPOLINE_IN(trampoline_unmap_piodma, dcpep_cb_unmap_piodma, - struct dcp_unmap_buf_resp); -TRAMPOLINE_INOUT(trampoline_allocate_buffer, dcpep_cb_allocate_buffer, - struct dcp_allocate_buffer_req, - struct dcp_allocate_buffer_resp); -TRAMPOLINE_INOUT(trampoline_map_physical, dcpep_cb_map_physical, - struct dcp_map_physical_req, struct dcp_map_physical_resp); -TRAMPOLINE_INOUT(trampoline_release_mem_desc, dcpep_cb_release_mem_desc, - u32, u8); -TRAMPOLINE_INOUT(trampoline_map_reg, dcpep_cb_map_reg, struct dcp_map_reg_req, - struct dcp_map_reg_resp); -TRAMPOLINE_INOUT(trampoline_read_edt_data, dcpep_cb_read_edt_data, - struct dcp_read_edt_data_req, struct dcp_read_edt_data_resp); -TRAMPOLINE_INOUT(trampoline_prop_start, dcpep_cb_prop_start, u32, u8); -TRAMPOLINE_INOUT(trampoline_prop_chunk, dcpep_cb_prop_chunk, - struct dcp_set_dcpav_prop_chunk_req, u8); -TRAMPOLINE_INOUT(trampoline_prop_end, dcpep_cb_prop_end, - struct dcp_set_dcpav_prop_end_req, u8); -TRAMPOLINE_OUT(trampoline_rt_bandwidth, dcpep_cb_rt_bandwidth, - struct dcp_rt_bandwidth); -TRAMPOLINE_OUT(trampoline_get_frequency, dcpep_cb_get_frequency, u64); -TRAMPOLINE_OUT(trampoline_get_time, dcpep_cb_get_time, u64); -TRAMPOLINE_IN(trampoline_hotplug, dcpep_cb_hotplug, u64); -TRAMPOLINE_IN(trampoline_swap_complete_intent_gated, - dcpep_cb_swap_complete_intent_gated, - struct dcp_swap_complete_intent_gated); - -bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, void *) = { - [0] = trampoline_true, /* did_boot_signal */ - [1] = trampoline_true, /* did_power_on_signal */ - [2] = trampoline_nop, /* will_power_off_signal */ - [3] = trampoline_rt_bandwidth, - [100] = trampoline_nop, /* match_pmu_service */ - [101] = trampoline_zero, /* get_display_default_stride */ - [103] = trampoline_nop, /* set_boolean_property */ - [106] = trampoline_nop, /* remove_property */ - [107] = trampoline_true, /* create_provider_service */ - [108] = trampoline_true, /* create_product_service */ - [109] = trampoline_true, /* create_pmu_service */ - [110] = trampoline_true, /* create_iomfb_service */ - [111] = trampoline_false, /* create_backlight_service */ - [116] = dcpep_cb_boot_1, - [117] = trampoline_false, /* is_dark_boot */ - [118] = trampoline_false, /* is_dark_boot / is_waking_from_hibernate*/ - [120] = trampoline_read_edt_data, - [122] = trampoline_prop_start, - [123] = trampoline_prop_chunk, - [124] = trampoline_prop_end, - [201] = trampoline_map_piodma, - [202] = trampoline_unmap_piodma, - [206] = trampoline_true, /* match_pmu_service_2 */ - [207] = trampoline_true, /* match_backlight_service */ - [208] = trampoline_get_time, - [211] = trampoline_nop, /* update_backlight_factor_prop */ - [300] = trampoline_nop, /* pr_publish */ - [401] = trampoline_get_uint_prop, - [404] = trampoline_nop, /* sr_set_uint_prop */ - [406] = trampoline_nop, /* set_fx_prop */ - [408] = trampoline_get_frequency, - [411] = trampoline_map_reg, - [413] = trampoline_true, /* sr_set_property_dict */ - [414] = trampoline_true, /* sr_set_property_int */ - [415] = trampoline_true, /* sr_set_property_bool */ - [451] = trampoline_allocate_buffer, - [452] = trampoline_map_physical, - [456] = trampoline_release_mem_desc, - [552] = trampoline_true, /* set_property_dict_0 */ - [561] = trampoline_true, /* set_property_dict */ - [563] = trampoline_true, /* set_property_int */ - [565] = trampoline_true, /* set_property_bool */ - [567] = trampoline_true, /* set_property_str */ - [574] = trampoline_zero, /* power_up_dart */ - [576] = trampoline_hotplug, - [577] = trampoline_nop, /* powerstate_notify */ - [582] = trampoline_true, /* create_default_fb_surface */ - [589] = trampoline_swap_complete, - [591] = trampoline_swap_complete_intent_gated, - [593] = trampoline_nop, /* enable_backlight_message_ap_gated */ - [598] = trampoline_nop, /* find_swap_function_gated */ -}; - -static void dcpep_handle_cb(struct apple_dcp *dcp, enum dcp_context_id context, - void *data, u32 length) -{ - struct device *dev = dcp->dev; - struct dcp_packet_header *hdr = data; - void *in, *out; - int tag = dcp_parse_tag(hdr->tag); - struct dcp_cb_channel *ch = dcp_get_cb_channel(dcp, context); - u8 depth; - - if (tag < 0 || tag >= DCPEP_MAX_CB || !dcpep_cb_handlers[tag]) { - dev_warn(dev, "received unknown callback %c%c%c%c\n", - hdr->tag[3], hdr->tag[2], hdr->tag[1], hdr->tag[0]); - return; - } - - in = data + sizeof(*hdr); - out = in + hdr->in_len; - - // TODO: verify that in_len and out_len match our prototypes - // for now just clear the out data to have at least consistant results - if (hdr->out_len) - memset(out, 0, hdr->out_len); - - depth = dcp_push_depth(&ch->depth); - ch->output[depth] = out; - - if (dcpep_cb_handlers[tag](dcp, tag, out, in)) - dcp_ack(dcp, context); -} - -static void dcpep_handle_ack(struct apple_dcp *dcp, enum dcp_context_id context, - void *data, u32 length) -{ - struct dcp_packet_header *header = data; - struct dcp_call_channel *ch = dcp_get_call_channel(dcp, context); - void *cookie; - dcp_callback_t cb; - - if (!ch) { - dev_warn(dcp->dev, "ignoring ack on context %X\n", context); - return; - } - - dcp_pop_depth(&ch->depth); - - cb = ch->callbacks[ch->depth]; - cookie = ch->cookies[ch->depth]; - - ch->callbacks[ch->depth] = NULL; - ch->cookies[ch->depth] = NULL; - - if (cb) - cb(dcp, data + sizeof(*header) + header->in_len, cookie); -} - -static void dcpep_got_msg(struct apple_dcp *dcp, u64 message) -{ - enum dcp_context_id ctx_id; - u16 offset; - u32 length; - int channel_offset; - void *data; - - ctx_id = (message & DCPEP_CONTEXT_MASK) >> DCPEP_CONTEXT_SHIFT; - offset = (message & DCPEP_OFFSET_MASK) >> DCPEP_OFFSET_SHIFT; - length = (message >> DCPEP_LENGTH_SHIFT); - - channel_offset = dcp_channel_offset(ctx_id); - - if (channel_offset < 0) { - dev_warn(dcp->dev, "invalid context received %u", ctx_id); - return; - } - - data = dcp->shmem + channel_offset + offset; - - if (message & DCPEP_ACK) - dcpep_handle_ack(dcp, ctx_id, data, length); - else - dcpep_handle_cb(dcp, ctx_id, data, length); -} - -/* - * Callback for swap requests. If a swap failed, we'll never get a swap - * complete event so we need to fake a vblank event early to avoid a hang. - */ - -static void dcp_swapped(struct apple_dcp *dcp, void *data, void *cookie) -{ - struct dcp_swap_submit_resp *resp = data; - - if (resp->ret) { - dev_err(dcp->dev, "swap failed! status %u\n", resp->ret); - dcp_drm_crtc_vblank(dcp->crtc); - return; - } - - while (!list_empty(&dcp->swapped_out_fbs)) { - struct dcp_fb_reference *entry; - entry = list_first_entry(&dcp->swapped_out_fbs, - struct dcp_fb_reference, head); - if (entry->fb) - drm_framebuffer_put(entry->fb); - list_del(&entry->head); - kfree(entry); - } -} - -static void dcp_swap_started(struct apple_dcp *dcp, void *data, void *cookie) -{ - struct dcp_swap_start_resp *resp = data; - - dcp->swap.swap.swap_id = resp->swap_id; - - dcp_swap_submit(dcp, false, &dcp->swap, dcp_swapped, NULL); -} - -/* - * DRM specifies rectangles as start and end coordinates. DCP specifies - * rectangles as a start coordinate and a width/height. Convert a DRM rectangle - * to a DCP rectangle. - */ -static struct dcp_rect drm_to_dcp_rect(struct drm_rect *rect) -{ - return (struct dcp_rect) { - .x = rect->x1, - .y = rect->y1, - .w = drm_rect_width(rect), - .h = drm_rect_height(rect) - }; -} - -static u32 drm_format_to_dcp(u32 drm) -{ - switch (drm) { - case DRM_FORMAT_XRGB8888: - case DRM_FORMAT_ARGB8888: - return fourcc_code('A', 'R', 'G', 'B'); - - case DRM_FORMAT_XBGR8888: - case DRM_FORMAT_ABGR8888: - return fourcc_code('A', 'B', 'G', 'R'); - } - - pr_warn("DRM format %X not supported in DCP\n", drm); - return 0; -} - -int dcp_get_modes(struct drm_connector *connector) -{ - struct apple_connector *apple_connector = to_apple_connector(connector); - struct platform_device *pdev = apple_connector->dcp; - struct apple_dcp *dcp = platform_get_drvdata(pdev); - - struct drm_device *dev = connector->dev; - struct drm_display_mode *mode; - int i; - - for (i = 0; i < dcp->nr_modes; ++i) { - mode = drm_mode_duplicate(dev, &dcp->modes[i].mode); - - if (!mode) { - dev_err(dev->dev, "Failed to duplicate display mode\n"); - return 0; - } - - drm_mode_probed_add(connector, mode); - } - - return dcp->nr_modes; -} -EXPORT_SYMBOL_GPL(dcp_get_modes); - -/* The user may own drm_display_mode, so we need to search for our copy */ -static struct dcp_display_mode *lookup_mode(struct apple_dcp *dcp, - struct drm_display_mode *mode) -{ - int i; - - for (i = 0; i < dcp->nr_modes; ++i) { - if (drm_mode_match(mode, &dcp->modes[i].mode, - DRM_MODE_MATCH_TIMINGS | - DRM_MODE_MATCH_CLOCK)) - return &dcp->modes[i]; - } - - return NULL; -} - -int dcp_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - struct apple_connector *apple_connector = to_apple_connector(connector); - struct platform_device *pdev = apple_connector->dcp; - struct apple_dcp *dcp = platform_get_drvdata(pdev); - - return lookup_mode(dcp, mode) ? MODE_OK : MODE_BAD; -} -EXPORT_SYMBOL_GPL(dcp_mode_valid); - -/* Helpers to modeset and swap, used to flush */ -static void do_swap(struct apple_dcp *dcp, void *data, void *cookie) -{ - struct dcp_swap_start_req start_req = { 0 }; - dev_dbg(dcp->dev, "%s", __func__); - - if (dcp->connector && dcp->connector->connected) - dcp_swap_start(dcp, false, &start_req, dcp_swap_started, NULL); - else - dcp_drm_crtc_vblank(dcp->crtc); -} - -static void complete_set_digital_out_mode(struct apple_dcp *dcp, void *data, - void *cookie) -{ - struct dcp_wait_cookie *wait = cookie; - dev_dbg(dcp->dev, "%s", __func__); - - dcp->ignore_swap_complete = false; - - if (wait) { - complete(&wait->done); - if (atomic_dec_and_test(&wait->refcount)) - kfree(wait); - } -} - -void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) -{ - struct platform_device *pdev = to_apple_crtc(crtc)->dcp; - struct apple_dcp *dcp = platform_get_drvdata(pdev); - struct drm_plane *plane; - struct drm_plane_state *new_state, *old_state; - struct drm_crtc_state *crtc_state; - struct dcp_swap_submit_req *req = &dcp->swap; - int l; - int has_surface = 0; - bool modeset; - dev_dbg(dcp->dev, "%s", __func__); - - crtc_state = drm_atomic_get_new_crtc_state(state, crtc); - - modeset = drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode; - - if (WARN(dcp_channel_busy(&dcp->ch_cmd), "unexpected busy channel") || - WARN(!modeset && !dcp->connector->connected, "can't flush if disconnected")) { - /* HACK: issue a delayed vblank event to avoid timeouts in - * drm_atomic_helper_wait_for_vblanks(). - */ - schedule_work(&dcp->vblank_wq); - return; - } - - /* Reset to defaults */ - memset(req, 0, sizeof(*req)); - for (l = 0; l < SWAP_SURFACES; l++) - req->surf_null[l] = true; - - for_each_oldnew_plane_in_state(state, plane, old_state, new_state, l) { - struct drm_framebuffer *fb = new_state->fb; - struct drm_rect src_rect; - - WARN_ON(l >= SWAP_SURFACES); - - req->swap.swap_enabled |= BIT(l); - - if (old_state->fb && fb != old_state->fb) { - /* - * Race condition between a framebuffer unbind getting - * swapped out and GEM unreferencing a framebuffer. If - * we lose the race, the display gets IOVA faults and - * the DCP crashes. We need to extend the lifetime of - * the drm_framebuffer (and hence the GEM object) until - * after we get a swap complete for the swap unbinding - * it. - */ - struct dcp_fb_reference *entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (entry) { - entry->fb = old_state->fb; - list_add_tail(&entry->head, &dcp->swapped_out_fbs); - } - drm_framebuffer_get(old_state->fb); - } - - if (!new_state->fb) { - if (old_state->fb) - req->swap.swap_enabled |= DCP_REMOVE_LAYERS; - - continue; - } - req->surf_null[l] = false; - has_surface = 1; - - - drm_rect_fp_to_int(&src_rect, &new_state->src); - - req->swap.src_rect[l] = drm_to_dcp_rect(&src_rect); - req->swap.dst_rect[l] = drm_to_dcp_rect(&new_state->dst); - - req->surf_iova[l] = drm_fb_dma_get_gem_addr(fb, new_state, 0); - - req->surf[l] = (struct dcp_surface) { - .format = drm_format_to_dcp(fb->format->format), - .xfer_func = 13, - .colorspace = 1, - .stride = fb->pitches[0], - .width = fb->width, - .height = fb->height, - .buf_size = fb->height * fb->pitches[0], - .surface_id = req->swap.surf_ids[l], - - /* Only used for compressed or multiplanar surfaces */ - .pix_size = 1, - .pel_w = 1, - .pel_h = 1, - .has_comp = 1, - .has_planes = 1, - }; - } - - /* These fields should be set together */ - req->swap.swap_completed = req->swap.swap_enabled; - - if (modeset) { - struct dcp_display_mode *mode; - struct dcp_wait_cookie *cookie; - int ret; - - mode = lookup_mode(dcp, &crtc_state->mode); - if (!mode) { - dev_warn(dcp->dev, "no match for " DRM_MODE_FMT, - DRM_MODE_ARG(&crtc_state->mode)); - schedule_work(&dcp->vblank_wq); - return; - } - - dev_info(dcp->dev, "set_digital_out_mode(color:%d timing:%d)", - mode->color_mode_id, mode->timing_mode_id); - dcp->mode = (struct dcp_set_digital_out_mode_req) { - .color_mode_id = mode->color_mode_id, - .timing_mode_id = mode->timing_mode_id - }; - - cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); - if (!cookie) { - schedule_work(&dcp->vblank_wq); - return; - } - - init_completion(&cookie->done); - atomic_set(&cookie->refcount, 2); - - dcp_set_digital_out_mode(dcp, false, &dcp->mode, - complete_set_digital_out_mode, cookie); - - dev_dbg(dcp->dev, "%s - wait for modeset", __func__); - ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(500)); - - if (atomic_dec_and_test(&cookie->refcount)) - kfree(cookie); - - if (ret == 0) { - dev_dbg(dcp->dev, "set_digital_out_mode 200 ms"); - schedule_work(&dcp->vblank_wq); - return; - } - else if (ret > 0) { - dev_dbg(dcp->dev, "set_digital_out_mode finished with %d to spare", - jiffies_to_msecs(ret)); - } - - dcp->valid_mode = true; - } - - if (!has_surface) { - if (crtc_state->enable && crtc_state->active && !crtc_state->planes_changed) { - schedule_work(&dcp->vblank_wq); - return; - } - - req->clear = 1; - } - do_swap(dcp, NULL, NULL); -} -EXPORT_SYMBOL_GPL(dcp_flush); - -bool dcp_is_initialized(struct platform_device *pdev) -{ - struct apple_dcp *dcp = platform_get_drvdata(pdev); - - return dcp->active; -} -EXPORT_SYMBOL_GPL(dcp_is_initialized); - - -static void res_is_main_display(struct apple_dcp *dcp, void *out, void *cookie) -{ - struct apple_connector *connector; - int result = *(int *)out; - dev_info(dcp->dev, "DCP is_main_display: %d\n", result); - - dcp->main_display = result != 0; - - dcp->active = true; - - connector = dcp->connector; - if (connector) { - connector->connected = dcp->nr_modes > 0; - schedule_work(&connector->hotplug_wq); - } -} - -static void init_3(struct apple_dcp *dcp, void *out, void *cookie) -{ - dcp_is_main_display(dcp, false, res_is_main_display, NULL); -} - -static void init_2(struct apple_dcp *dcp, void *out, void *cookie) -{ - dcp_first_client_open(dcp, false, init_3, NULL); -} - -static void init_1(struct apple_dcp *dcp, void *out, void *cookie) -{ - u32 val = 0; - dcp_enable_disable_video_power_savings(dcp, false, &val, init_2, NULL); -} - -static void dcp_started(struct apple_dcp *dcp, void *data, void *cookie) -{ - dev_info(dcp->dev, "DCP booted\n"); - - init_1(dcp, data, cookie); -} - -static void dcp_got_msg(void *cookie, u8 endpoint, u64 message) +static void dcp_recv_msg(void *cookie, u8 endpoint, u64 message) { struct apple_dcp *dcp = cookie; - enum dcpep_type type = (message >> DCPEP_TYPE_SHIFT) & DCPEP_TYPE_MASK; - - WARN_ON(endpoint != DCP_ENDPOINT); - if (type == DCPEP_TYPE_INITIALIZED) - dcp_start_signal(dcp, false, dcp_started, NULL); - else if (type == DCPEP_TYPE_MESSAGE) - dcpep_got_msg(dcp, message); - else - dev_warn(dcp->dev, "Ignoring unknown message %llx\n", message); + switch (endpoint) { + case IOMFB_ENDPOINT: + return iomfb_recv_msg(dcp, message); + default: + WARN(endpoint, "unknown DCP endpoint %hhu", endpoint); + } } static void dcp_rtk_crashed(void *cookie) @@ -1738,14 +111,16 @@ static int dcp_rtk_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr) struct apple_dcp *dcp = cookie; if (bfr->iova) { - struct iommu_domain *domain = iommu_get_domain_for_dev(dcp->dev); + struct iommu_domain *domain = + iommu_get_domain_for_dev(dcp->dev); phys_addr_t phy_addr; if (!domain) return -ENOMEM; // TODO: get map from device-tree - phy_addr = iommu_iova_to_phys(domain, bfr->iova & ~dcp->asc_dram_mask); + phy_addr = iommu_iova_to_phys(domain, + bfr->iova & ~dcp->asc_dram_mask); if (!phy_addr) return -ENOMEM; @@ -1755,10 +130,13 @@ static int dcp_rtk_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr) return -ENOMEM; bfr->is_mapped = true; - dev_info(dcp->dev, "shmem_setup: iova: %lx -> pa: %lx -> iomem: %lx", - (uintptr_t)bfr->iova, (uintptr_t)phy_addr, (uintptr_t)bfr->buffer); + dev_info(dcp->dev, + "shmem_setup: iova: %lx -> pa: %lx -> iomem: %lx", + (uintptr_t)bfr->iova, (uintptr_t)phy_addr, + (uintptr_t)bfr->buffer); } else { - bfr->buffer = dma_alloc_coherent(dcp->dev, bfr->size, &bfr->iova, GFP_KERNEL); + bfr->buffer = dma_alloc_coherent(dcp->dev, bfr->size, + &bfr->iova, GFP_KERNEL); if (!bfr->buffer) return -ENOMEM; @@ -1778,12 +156,13 @@ static void dcp_rtk_shmem_destroy(void *cookie, struct apple_rtkit_shmem *bfr) if (bfr->is_mapped) memunmap(bfr->buffer); else - dma_free_coherent(dcp->dev, bfr->size, bfr->buffer, bfr->iova & ~dcp->asc_dram_mask); + dma_free_coherent(dcp->dev, bfr->size, bfr->buffer, + bfr->iova & ~dcp->asc_dram_mask); } static struct apple_rtkit_ops rtkit_ops = { .crashed = dcp_rtk_crashed, - .recv_message = dcp_got_msg, + .recv_message = dcp_recv_msg, .shmem_setup = dcp_rtk_shmem_setup, .shmem_destroy = dcp_rtk_shmem_destroy, }; @@ -1839,7 +218,6 @@ static int dcp_platform_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct apple_dcp *dcp; - dma_addr_t shmem_iova; u32 cpu_ctrl; int ret; @@ -1884,7 +262,8 @@ static int dcp_platform_probe(struct platform_device *pdev) dcp->clk = devm_clk_get(dev, NULL); if (IS_ERR(dcp->clk)) - return dev_err_probe(dev, PTR_ERR(dcp->clk), "Unable to find clock\n"); + return dev_err_probe(dev, PTR_ERR(dcp->clk), + "Unable to find clock\n"); ret = of_property_read_u64(dev->of_node, "apple,asc-dram-mask", &dcp->asc_dram_mask); @@ -1898,9 +277,11 @@ static int dcp_platform_probe(struct platform_device *pdev) INIT_WORK(&dcp->vblank_wq, dcp_delayed_vblank); - dcp->swapped_out_fbs = (struct list_head)LIST_HEAD_INIT(dcp->swapped_out_fbs); + dcp->swapped_out_fbs = + (struct list_head)LIST_HEAD_INIT(dcp->swapped_out_fbs); - cpu_ctrl = readl_relaxed(dcp->coproc_reg + APPLE_DCP_COPROC_CPU_CONTROL); + cpu_ctrl = + readl_relaxed(dcp->coproc_reg + APPLE_DCP_COPROC_CPU_CONTROL); writel_relaxed(cpu_ctrl | APPLE_DCP_COPROC_CPU_CONTROL_RUN, dcp->coproc_reg + APPLE_DCP_COPROC_CPU_CONTROL); @@ -1914,14 +295,11 @@ static int dcp_platform_probe(struct platform_device *pdev) return dev_err_probe(dev, ret, "Failed to boot RTKit: %d", ret); - apple_rtkit_start_ep(dcp->rtk, DCP_ENDPOINT); - - dcp->shmem = dma_alloc_coherent(dev, DCP_SHMEM_SIZE, &shmem_iova, - GFP_KERNEL); - - shmem_iova |= dcp->asc_dram_mask; - apple_rtkit_send_message(dcp->rtk, DCP_ENDPOINT, - dcpep_set_shmem(shmem_iova), NULL, false); + /* start RTKit endpoints */ + ret = iomfb_start_rtkit(dcp); + if (ret) + return dev_err_probe(dev, ret, + "Failed to start IOMFB endpoint: %d", ret); return ret; } @@ -1934,15 +312,7 @@ static void dcp_platform_shutdown(struct platform_device *pdev) { struct apple_dcp *dcp = platform_get_drvdata(pdev); - struct dcp_set_power_state_req req = { - /* defaults are ok */ - }; - - /* We're going down */ - dcp->active = false; - dcp->valid_mode = false; - - dcp_set_power_state(dcp, false, &req, NULL, NULL); + iomfb_shutdown(dcp); } static const struct of_device_id of_match[] = { @@ -1965,9 +335,7 @@ static int dcp_suspend(struct device *dev) static int dcp_resume(struct device *dev) { - struct apple_dcp *dcp = platform_get_drvdata(to_platform_device(dev)); - - dcp_start_signal(dcp, false, dcp_started, NULL); + dcp_poweron(to_platform_device(dev)); return 0; } diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h index 9e3e3738a39377..18d71afaf6b72e 100644 --- a/drivers/gpu/drm/apple/dcp.h +++ b/drivers/gpu/drm/apple/dcp.h @@ -5,6 +5,9 @@ #define __APPLE_DCP_H__ #include +#include + +#include "dcp-internal.h" #include "parser.h" struct apple_crtc { @@ -39,8 +42,16 @@ void dcp_link(struct platform_device *pdev, struct apple_crtc *apple, void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state); bool dcp_is_initialized(struct platform_device *pdev); void apple_crtc_vblank(struct apple_crtc *apple); +void dcp_drm_crtc_vblank(struct apple_crtc *crtc); int dcp_get_modes(struct drm_connector *connector); int dcp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode); +void dcp_set_dimensions(struct apple_dcp *dcp); + + +int iomfb_start_rtkit(struct apple_dcp *dcp); +void iomfb_shutdown(struct apple_dcp *dcp); +/* rtkit message handler for IOMFB messages */ +void iomfb_recv_msg(struct apple_dcp *dcp, u64 message); #endif diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c new file mode 100644 index 00000000000000..0da3de1aa27e78 --- /dev/null +++ b/drivers/gpu/drm/apple/iomfb.c @@ -0,0 +1,1626 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright 2021 Alyssa Rosenzweig */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "dcp.h" +#include "dcp-internal.h" +#include "iomfb.h" +#include "parser.h" + +/* Register defines used in bandwidth setup structure */ +#define REG_SCRATCH (0x14) +#define REG_SCRATCH_T600X (0x988) +#define REG_DOORBELL (0x0) +#define REG_DOORBELL_BIT (2) + +struct dcp_wait_cookie { + struct completion done; + atomic_t refcount; +}; + +static int dcp_tx_offset(enum dcp_context_id id) +{ + switch (id) { + case DCP_CONTEXT_CB: + case DCP_CONTEXT_CMD: + return 0x00000; + case DCP_CONTEXT_OOBCB: + case DCP_CONTEXT_OOBCMD: + return 0x08000; + default: + return -EINVAL; + } +} + +static int dcp_channel_offset(enum dcp_context_id id) +{ + switch (id) { + case DCP_CONTEXT_ASYNC: + return 0x40000; + case DCP_CONTEXT_CB: + return 0x60000; + case DCP_CONTEXT_OOBCB: + return 0x68000; + default: + return dcp_tx_offset(id); + } +} + +static inline u64 dcpep_set_shmem(u64 dart_va) +{ + return (DCPEP_TYPE_SET_SHMEM << DCPEP_TYPE_SHIFT) | + (DCPEP_FLAG_VALUE << DCPEP_FLAG_SHIFT) | + (dart_va << DCPEP_DVA_SHIFT); +} + +static inline u64 dcpep_msg(enum dcp_context_id id, u32 length, u16 offset) +{ + return (DCPEP_TYPE_MESSAGE << DCPEP_TYPE_SHIFT) | + ((u64)id << DCPEP_CONTEXT_SHIFT) | + ((u64)offset << DCPEP_OFFSET_SHIFT) | + ((u64)length << DCPEP_LENGTH_SHIFT); +} + +static inline u64 dcpep_ack(enum dcp_context_id id) +{ + return dcpep_msg(id, 0, 0) | DCPEP_ACK; +} + +/* + * A channel is busy if we have sent a message that has yet to be + * acked. The driver must not sent a message to a busy channel. + */ +static bool dcp_channel_busy(struct dcp_call_channel *ch) +{ + return (ch->depth != 0); +} + +/* Get a call channel for a context */ +static struct dcp_call_channel * +dcp_get_call_channel(struct apple_dcp *dcp, enum dcp_context_id context) +{ + switch (context) { + case DCP_CONTEXT_CMD: + case DCP_CONTEXT_CB: + return &dcp->ch_cmd; + case DCP_CONTEXT_OOBCMD: + case DCP_CONTEXT_OOBCB: + return &dcp->ch_oobcmd; + default: + return NULL; + } +} + +/* + * Get the context ID passed to the DCP for a command we push. The rule is + * simple: callback contexts are used when replying to the DCP, command + * contexts are used otherwise. That corresponds to a non/zero call stack + * depth. This rule frees the caller from tracking the call context manually. + */ +static enum dcp_context_id dcp_call_context(struct apple_dcp *dcp, bool oob) +{ + u8 depth = oob ? dcp->ch_oobcmd.depth : dcp->ch_cmd.depth; + + if (depth) + return oob ? DCP_CONTEXT_OOBCB : DCP_CONTEXT_CB; + else + return oob ? DCP_CONTEXT_OOBCMD : DCP_CONTEXT_CMD; +} + +/* Get a callback channel for a context */ +static struct dcp_cb_channel *dcp_get_cb_channel(struct apple_dcp *dcp, + enum dcp_context_id context) +{ + switch (context) { + case DCP_CONTEXT_CB: + return &dcp->ch_cb; + case DCP_CONTEXT_OOBCB: + return &dcp->ch_oobcb; + case DCP_CONTEXT_ASYNC: + return &dcp->ch_async; + default: + return NULL; + } +} + +/* Get the start of a packet: after the end of the previous packet */ +static u16 dcp_packet_start(struct dcp_call_channel *ch, u8 depth) +{ + if (depth > 0) + return ch->end[depth - 1]; + else + return 0; +} + +/* Pushes and pops the depth of the call stack with safety checks */ +static u8 dcp_push_depth(u8 *depth) +{ + u8 ret = (*depth)++; + + WARN_ON(ret >= DCP_MAX_CALL_DEPTH); + return ret; +} + +static u8 dcp_pop_depth(u8 *depth) +{ + WARN_ON((*depth) == 0); + + return --(*depth); +} + +#define DCP_METHOD(tag, name) [name] = { #name, tag } + +const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { + DCP_METHOD("A000", dcpep_late_init_signal), + DCP_METHOD("A029", dcpep_setup_video_limits), + DCP_METHOD("A034", dcpep_update_notify_clients_dcp), + DCP_METHOD("A357", dcpep_set_create_dfb), + DCP_METHOD("A401", dcpep_start_signal), + DCP_METHOD("A407", dcpep_swap_start), + DCP_METHOD("A408", dcpep_swap_submit), + DCP_METHOD("A410", dcpep_set_display_device), + DCP_METHOD("A411", dcpep_is_main_display), + DCP_METHOD("A412", dcpep_set_digital_out_mode), + DCP_METHOD("A439", dcpep_set_parameter_dcp), + DCP_METHOD("A443", dcpep_create_default_fb), + DCP_METHOD("A447", dcpep_enable_disable_video_power_savings), + DCP_METHOD("A454", dcpep_first_client_open), + DCP_METHOD("A460", dcpep_set_display_refresh_properties), + DCP_METHOD("A463", dcpep_flush_supports_power), + DCP_METHOD("A468", dcpep_set_power_state), +}; + +/* Call a DCP function given by a tag */ +static void dcp_push(struct apple_dcp *dcp, bool oob, enum dcpep_method method, + u32 in_len, u32 out_len, void *data, dcp_callback_t cb, + void *cookie) +{ + struct dcp_call_channel *ch = oob ? &dcp->ch_oobcmd : &dcp->ch_cmd; + enum dcp_context_id context = dcp_call_context(dcp, oob); + + struct dcp_packet_header header = { + .in_len = in_len, + .out_len = out_len, + + /* Tag is reversed due to endianness of the fourcc */ + .tag[0] = dcp_methods[method].tag[3], + .tag[1] = dcp_methods[method].tag[2], + .tag[2] = dcp_methods[method].tag[1], + .tag[3] = dcp_methods[method].tag[0], + }; + + u8 depth = dcp_push_depth(&ch->depth); + u16 offset = dcp_packet_start(ch, depth); + + void *out = dcp->shmem + dcp_tx_offset(context) + offset; + void *out_data = out + sizeof(header); + size_t data_len = sizeof(header) + in_len + out_len; + + memcpy(out, &header, sizeof(header)); + + if (in_len > 0) + memcpy(out_data, data, in_len); + + dev_dbg(dcp->dev, "---> %s: context %u, offset %u, depth %u\n", + dcp_methods[method].name, context, offset, depth); + + ch->callbacks[depth] = cb; + ch->cookies[depth] = cookie; + ch->output[depth] = out + sizeof(header) + in_len; + ch->end[depth] = offset + ALIGN(data_len, DCP_PACKET_ALIGNMENT); + + apple_rtkit_send_message(dcp->rtk, IOMFB_ENDPOINT, + dcpep_msg(context, data_len, offset), NULL, + false); +} + +#define DCP_THUNK_VOID(func, handle) \ + static void func(struct apple_dcp *dcp, bool oob, dcp_callback_t cb, \ + void *cookie) \ + { \ + dcp_push(dcp, oob, handle, 0, 0, NULL, cb, cookie); \ + } + +#define DCP_THUNK_OUT(func, handle, T) \ + static void func(struct apple_dcp *dcp, bool oob, dcp_callback_t cb, \ + void *cookie) \ + { \ + dcp_push(dcp, oob, handle, 0, sizeof(T), NULL, cb, cookie); \ + } + +#define DCP_THUNK_IN(func, handle, T) \ + static void func(struct apple_dcp *dcp, bool oob, T *data, \ + dcp_callback_t cb, void *cookie) \ + { \ + dcp_push(dcp, oob, handle, sizeof(T), 0, data, cb, cookie); \ + } + +#define DCP_THUNK_INOUT(func, handle, T_in, T_out) \ + static void func(struct apple_dcp *dcp, bool oob, T_in *data, \ + dcp_callback_t cb, void *cookie) \ + { \ + dcp_push(dcp, oob, handle, sizeof(T_in), sizeof(T_out), data, \ + cb, cookie); \ + } + +DCP_THUNK_INOUT(dcp_swap_submit, dcpep_swap_submit, struct dcp_swap_submit_req, + struct dcp_swap_submit_resp); + +DCP_THUNK_INOUT(dcp_swap_start, dcpep_swap_start, struct dcp_swap_start_req, + struct dcp_swap_start_resp); + +DCP_THUNK_INOUT(dcp_set_power_state, dcpep_set_power_state, + struct dcp_set_power_state_req, + struct dcp_set_power_state_resp); + +DCP_THUNK_INOUT(dcp_set_digital_out_mode, dcpep_set_digital_out_mode, + struct dcp_set_digital_out_mode_req, u32); + +DCP_THUNK_INOUT(dcp_set_display_device, dcpep_set_display_device, u32, u32); + +DCP_THUNK_OUT(dcp_set_display_refresh_properties, + dcpep_set_display_refresh_properties, u32); + +DCP_THUNK_OUT(dcp_late_init_signal, dcpep_late_init_signal, u32); +DCP_THUNK_IN(dcp_flush_supports_power, dcpep_flush_supports_power, u32); +DCP_THUNK_OUT(dcp_create_default_fb, dcpep_create_default_fb, u32); +DCP_THUNK_OUT(dcp_start_signal, dcpep_start_signal, u32); +DCP_THUNK_VOID(dcp_setup_video_limits, dcpep_setup_video_limits); +DCP_THUNK_VOID(dcp_set_create_dfb, dcpep_set_create_dfb); +DCP_THUNK_VOID(dcp_first_client_open, dcpep_first_client_open); + +__attribute__((unused)) +DCP_THUNK_IN(dcp_update_notify_clients_dcp, dcpep_update_notify_clients_dcp, + struct dcp_update_notify_clients_dcp); + +DCP_THUNK_INOUT(dcp_set_parameter_dcp, dcpep_set_parameter_dcp, + struct dcp_set_parameter_dcp, u32); + +DCP_THUNK_INOUT(dcp_enable_disable_video_power_savings, + dcpep_enable_disable_video_power_savings, u32, int); + +DCP_THUNK_OUT(dcp_is_main_display, dcpep_is_main_display, u32); + +/* Parse a callback tag "D123" into the ID 123. Returns -EINVAL on failure. */ +static int dcp_parse_tag(char tag[4]) +{ + u32 d[3]; + int i; + + if (tag[3] != 'D') + return -EINVAL; + + for (i = 0; i < 3; ++i) { + d[i] = (u32)(tag[i] - '0'); + + if (d[i] > 9) + return -EINVAL; + } + + return d[0] + (d[1] * 10) + (d[2] * 100); +} + +/* Ack a callback from the DCP */ +static void dcp_ack(struct apple_dcp *dcp, enum dcp_context_id context) +{ + struct dcp_cb_channel *ch = dcp_get_cb_channel(dcp, context); + + dcp_pop_depth(&ch->depth); + apple_rtkit_send_message(dcp->rtk, IOMFB_ENDPOINT, dcpep_ack(context), + NULL, false); +} + +/* DCP callback handlers */ +static void dcpep_cb_nop(struct apple_dcp *dcp) +{ + /* No operation */ +} + +static u8 dcpep_cb_true(struct apple_dcp *dcp) +{ + return true; +} + +static u8 dcpep_cb_false(struct apple_dcp *dcp) +{ + return false; +} + +static u32 dcpep_cb_zero(struct apple_dcp *dcp) +{ + return 0; +} + +static void dcpep_cb_swap_complete(struct apple_dcp *dcp, + struct dc_swap_complete_resp *resp) +{ + dev_dbg(dcp->dev, "swap complete for swap_id: %u vblank: %u", + resp->swap_id, dcp->ignore_swap_complete); + + if (!dcp->ignore_swap_complete) + dcp_drm_crtc_vblank(dcp->crtc); +} + +static struct dcp_get_uint_prop_resp +dcpep_cb_get_uint_prop(struct apple_dcp *dcp, struct dcp_get_uint_prop_req *req) +{ + /* unimplemented for now */ + return (struct dcp_get_uint_prop_resp){ .value = 0 }; +} + +/* + * Callback to map a buffer allocated with allocate_buf for PIODMA usage. + * PIODMA is separate from the main DCP and uses own IOVA space on a dedicated + * stream of the display DART, rather than the expected DCP DART. + * + * XXX: This relies on dma_get_sgtable in concert with dma_map_sgtable, which + * is a "fundamentally unsafe" operation according to the docs. And yet + * everyone does it... + */ +static struct dcp_map_buf_resp dcpep_cb_map_piodma(struct apple_dcp *dcp, + struct dcp_map_buf_req *req) +{ + struct sg_table *map; + int ret; + + if (req->buffer >= ARRAY_SIZE(dcp->memdesc)) + goto reject; + + map = &dcp->memdesc[req->buffer].map; + + if (!map->sgl) + goto reject; + + /* Use PIODMA device instead of DCP to map against the right IOMMU. */ + ret = dma_map_sgtable(&dcp->piodma->dev, map, DMA_BIDIRECTIONAL, 0); + + if (ret) + goto reject; + + return (struct dcp_map_buf_resp){ .dva = sg_dma_address(map->sgl) }; + +reject: + dev_err(dcp->dev, "denying map of invalid buffer %llx for pidoma\n", + req->buffer); + return (struct dcp_map_buf_resp){ .ret = EINVAL }; +} + +static void dcpep_cb_unmap_piodma(struct apple_dcp *dcp, + struct dcp_unmap_buf_resp *resp) +{ + struct sg_table *map; + dma_addr_t dma_addr; + + if (resp->buffer >= ARRAY_SIZE(dcp->memdesc)) { + dev_warn(dcp->dev, "unmap request for out of range buffer %llu", + resp->buffer); + return; + } + + map = &dcp->memdesc[resp->buffer].map; + + if (!map->sgl) { + dev_warn(dcp->dev, + "unmap for non-mapped buffer %llu iova:0x%08llx", + resp->buffer, resp->dva); + return; + } + + dma_addr = sg_dma_address(map->sgl); + if (dma_addr != resp->dva) { + dev_warn(dcp->dev, "unmap buffer %llu address mismatch dma_addr:%llx dva:%llx", + resp->buffer, dma_addr, resp->dva); + return; + } + + /* Use PIODMA device instead of DCP to unmap from the right IOMMU. */ + dma_unmap_sgtable(&dcp->piodma->dev, map, DMA_BIDIRECTIONAL, 0); +} + +/* + * Allocate an IOVA contiguous buffer mapped to the DCP. The buffer need not be + * physically contigiuous, however we should save the sgtable in case the + * buffer needs to be later mapped for PIODMA. + */ +static struct dcp_allocate_buffer_resp +dcpep_cb_allocate_buffer(struct apple_dcp *dcp, + struct dcp_allocate_buffer_req *req) +{ + struct dcp_allocate_buffer_resp resp = { 0 }; + struct dcp_mem_descriptor *memdesc; + u32 id; + + resp.dva_size = ALIGN(req->size, 4096); + resp.mem_desc_id = + find_first_zero_bit(dcp->memdesc_map, DCP_MAX_MAPPINGS); + + if (resp.mem_desc_id >= DCP_MAX_MAPPINGS) { + dev_warn(dcp->dev, "DCP overflowed mapping table, ignoring"); + resp.dva_size = 0; + resp.mem_desc_id = 0; + return resp; + } + id = resp.mem_desc_id; + set_bit(id, dcp->memdesc_map); + + memdesc = &dcp->memdesc[id]; + + memdesc->size = resp.dva_size; + memdesc->buf = dma_alloc_coherent(dcp->dev, memdesc->size, + &memdesc->dva, GFP_KERNEL); + + dma_get_sgtable(dcp->dev, &memdesc->map, memdesc->buf, memdesc->dva, + memdesc->size); + resp.dva = memdesc->dva; + + return resp; +} + +static u8 dcpep_cb_release_mem_desc(struct apple_dcp *dcp, u32 *mem_desc_id) +{ + struct dcp_mem_descriptor *memdesc; + u32 id = *mem_desc_id; + + if (id >= DCP_MAX_MAPPINGS) { + dev_warn(dcp->dev, + "unmap request for out of range mem_desc_id %u", id); + return 0; + } + + if (!test_and_clear_bit(id, dcp->memdesc_map)) { + dev_warn(dcp->dev, "unmap request for unused mem_desc_id %u", + id); + return 0; + } + + memdesc = &dcp->memdesc[id]; + if (memdesc->buf) { + dma_free_coherent(dcp->dev, memdesc->size, memdesc->buf, + memdesc->dva); + + memdesc->buf = NULL; + memset(&memdesc->map, 0, sizeof(memdesc->map)); + } else { + memdesc->reg = 0; + } + + memdesc->size = 0; + + return 1; +} + +/* Validate that the specified region is a display register */ +static bool is_disp_register(struct apple_dcp *dcp, u64 start, u64 end) +{ + int i; + + for (i = 0; i < dcp->nr_disp_registers; ++i) { + struct resource *r = dcp->disp_registers[i]; + + if ((start >= r->start) && (end <= r->end)) + return true; + } + + return false; +} + +/* + * Map contiguous physical memory into the DCP's address space. The firmware + * uses this to map the display registers we advertise in + * sr_map_device_memory_with_index, so we bounds check against that to guard + * safe against malicious coprocessors. + */ +static struct dcp_map_physical_resp +dcpep_cb_map_physical(struct apple_dcp *dcp, struct dcp_map_physical_req *req) +{ + int size = ALIGN(req->size, 4096); + u32 id; + + if (!is_disp_register(dcp, req->paddr, req->paddr + size - 1)) { + dev_err(dcp->dev, "refusing to map phys address %llx size %llx", + req->paddr, req->size); + return (struct dcp_map_physical_resp){}; + } + + id = find_first_zero_bit(dcp->memdesc_map, DCP_MAX_MAPPINGS); + set_bit(id, dcp->memdesc_map); + dcp->memdesc[id].size = size; + dcp->memdesc[id].reg = req->paddr; + + return (struct dcp_map_physical_resp){ + .dva_size = size, + .mem_desc_id = id, + .dva = dma_map_resource(dcp->dev, req->paddr, size, + DMA_BIDIRECTIONAL, 0), + }; +} + +static u64 dcpep_cb_get_frequency(struct apple_dcp *dcp) +{ + return clk_get_rate(dcp->clk); +} + +static struct dcp_map_reg_resp dcpep_cb_map_reg(struct apple_dcp *dcp, + struct dcp_map_reg_req *req) +{ + if (req->index >= dcp->nr_disp_registers) { + dev_warn(dcp->dev, "attempted to read invalid reg index %u", + req->index); + + return (struct dcp_map_reg_resp){ .ret = 1 }; + } else { + struct resource *rsrc = dcp->disp_registers[req->index]; + + return (struct dcp_map_reg_resp){ + .addr = rsrc->start, .length = resource_size(rsrc) + }; + } +} + +static struct dcp_read_edt_data_resp +dcpep_cb_read_edt_data(struct apple_dcp *dcp, struct dcp_read_edt_data_req *req) +{ + return (struct dcp_read_edt_data_resp){ + .value[0] = req->value[0], + .ret = 0, + }; +} + +/* Chunked data transfer for property dictionaries */ +static u8 dcpep_cb_prop_start(struct apple_dcp *dcp, u32 *length) +{ + if (dcp->chunks.data != NULL) { + dev_warn(dcp->dev, "ignoring spurious transfer start\n"); + return false; + } + + dcp->chunks.length = *length; + dcp->chunks.data = devm_kzalloc(dcp->dev, *length, GFP_KERNEL); + + if (!dcp->chunks.data) { + dev_warn(dcp->dev, "failed to allocate chunks\n"); + return false; + } + + return true; +} + +static u8 dcpep_cb_prop_chunk(struct apple_dcp *dcp, + struct dcp_set_dcpav_prop_chunk_req *req) +{ + if (!dcp->chunks.data) { + dev_warn(dcp->dev, "ignoring spurious chunk\n"); + return false; + } + + if (req->offset + req->length > dcp->chunks.length) { + dev_warn(dcp->dev, "ignoring overflowing chunk\n"); + return false; + } + + memcpy(dcp->chunks.data + req->offset, req->data, req->length); + return true; +} + +static bool dcpep_process_chunks(struct apple_dcp *dcp, + struct dcp_set_dcpav_prop_end_req *req) +{ + struct dcp_parse_ctx ctx; + int ret; + + if (!dcp->chunks.data) { + dev_warn(dcp->dev, "ignoring spurious end\n"); + return false; + } + + ret = parse(dcp->chunks.data, dcp->chunks.length, &ctx); + + if (ret) { + dev_warn(dcp->dev, "bad header on dcpav props\n"); + return false; + } + + if (!strcmp(req->key, "TimingElements")) { + dcp->modes = enumerate_modes(&ctx, &dcp->nr_modes, + dcp->width_mm, dcp->height_mm); + + if (IS_ERR(dcp->modes)) { + dev_warn(dcp->dev, "failed to parse modes\n"); + dcp->modes = NULL; + dcp->nr_modes = 0; + return false; + } + } else if (!strcmp(req->key, "DisplayAttributes")) { + ret = parse_display_attributes(&ctx, &dcp->width_mm, + &dcp->height_mm); + + if (ret) { + dev_warn(dcp->dev, "failed to parse display attribs\n"); + return false; + } + + dcp_set_dimensions(dcp); + } + + return true; +} + +static u8 dcpep_cb_prop_end(struct apple_dcp *dcp, + struct dcp_set_dcpav_prop_end_req *req) +{ + u8 resp = dcpep_process_chunks(dcp, req); + + /* Reset for the next transfer */ + devm_kfree(dcp->dev, dcp->chunks.data); + dcp->chunks.data = NULL; + + return resp; +} + +/* Boot sequence */ +static void boot_done(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct dcp_cb_channel *ch = &dcp->ch_cb; + u8 *succ = ch->output[ch->depth - 1]; + dev_dbg(dcp->dev, "boot done"); + + *succ = true; + dcp_ack(dcp, DCP_CONTEXT_CB); +} + +static void boot_5(struct apple_dcp *dcp, void *out, void *cookie) +{ + dcp_set_display_refresh_properties(dcp, false, boot_done, NULL); +} + +static void boot_4(struct apple_dcp *dcp, void *out, void *cookie) +{ + dcp_late_init_signal(dcp, false, boot_5, NULL); +} + +static void boot_3(struct apple_dcp *dcp, void *out, void *cookie) +{ + u32 v_true = true; + + dcp_flush_supports_power(dcp, false, &v_true, boot_4, NULL); +} + +static void boot_2(struct apple_dcp *dcp, void *out, void *cookie) +{ + dcp_setup_video_limits(dcp, false, boot_3, NULL); +} + +static void boot_1_5(struct apple_dcp *dcp, void *out, void *cookie) +{ + dcp_create_default_fb(dcp, false, boot_2, NULL); +} + +/* Use special function signature to defer the ACK */ +static bool dcpep_cb_boot_1(struct apple_dcp *dcp, int tag, void *out, void *in) +{ + dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, __func__); + dcp_set_create_dfb(dcp, false, boot_1_5, NULL); + return false; +} + +static struct dcp_rt_bandwidth dcpep_cb_rt_bandwidth(struct apple_dcp *dcp) +{ + if (dcp->disp_registers[5] && dcp->disp_registers[6]) + return (struct dcp_rt_bandwidth){ + .reg_scratch = + dcp->disp_registers[5]->start + REG_SCRATCH, + .reg_doorbell = + dcp->disp_registers[6]->start + REG_DOORBELL, + .doorbell_bit = REG_DOORBELL_BIT, + + .padding[3] = 0x4, // XXX: required by 11.x firmware + }; + else if (dcp->disp_registers[4]) + return (struct dcp_rt_bandwidth){ + .reg_scratch = dcp->disp_registers[4]->start + + REG_SCRATCH_T600X, + .reg_doorbell = 0, + .doorbell_bit = 0, + }; + else + return (struct dcp_rt_bandwidth){ + .reg_scratch = 0, + .reg_doorbell = 0, + .doorbell_bit = 0, + }; +} + +/* Callback to get the current time as milliseconds since the UNIX epoch */ +static u64 dcpep_cb_get_time(struct apple_dcp *dcp) +{ + return ktime_to_ms(ktime_get_real()); +} + +struct dcp_swap_cookie { + struct completion done; + atomic_t refcount; + u32 swap_id; +}; + +static void dcp_swap_cleared(struct apple_dcp *dcp, void *data, void *cookie) +{ + struct dcp_swap_submit_resp *resp = data; + dev_dbg(dcp->dev, "%s", __func__); + + if (cookie) { + struct dcp_swap_cookie *info = cookie; + complete(&info->done); + if (atomic_dec_and_test(&info->refcount)) + kfree(info); + } + + if (resp->ret) { + dev_err(dcp->dev, "swap_clear failed! status %u\n", resp->ret); + dcp_drm_crtc_vblank(dcp->crtc); + return; + } + + while (!list_empty(&dcp->swapped_out_fbs)) { + struct dcp_fb_reference *entry; + entry = list_first_entry(&dcp->swapped_out_fbs, + struct dcp_fb_reference, head); + if (entry->fb) + drm_framebuffer_put(entry->fb); + list_del(&entry->head); + kfree(entry); + } +} + +static void dcp_swap_clear_started(struct apple_dcp *dcp, void *data, + void *cookie) +{ + struct dcp_swap_start_resp *resp = data; + dev_dbg(dcp->dev, "%s swap_id: %u", __func__, resp->swap_id); + dcp->swap.swap.swap_id = resp->swap_id; + + if (cookie) { + struct dcp_swap_cookie *info = cookie; + info->swap_id = resp->swap_id; + } + + dcp_swap_submit(dcp, false, &dcp->swap, dcp_swap_cleared, cookie); +} + +static void dcp_on_final(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct dcp_wait_cookie *wait = cookie; + dev_dbg(dcp->dev, "%s", __func__); + + if (wait) { + complete(&wait->done); + if (atomic_dec_and_test(&wait->refcount)) + kfree(wait); + } +} + +static void dcp_on_set_parameter(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct dcp_set_parameter_dcp param = { + .param = 14, + .value = { 0 }, + .count = 1, + }; + dev_dbg(dcp->dev, "%s", __func__); + + dcp_set_parameter_dcp(dcp, false, ¶m, dcp_on_final, cookie); +} + +void dcp_poweron(struct platform_device *pdev) +{ + struct apple_dcp *dcp = platform_get_drvdata(pdev); + struct dcp_wait_cookie *cookie; + struct dcp_set_power_state_req req = { + .unklong = 1, + }; + int ret; + u32 handle; + dev_dbg(dcp->dev, "%s", __func__); + + cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); + if (!cookie) + return; + + init_completion(&cookie->done); + atomic_set(&cookie->refcount, 2); + + if (dcp->main_display) { + handle = 0; + dcp_set_display_device(dcp, false, &handle, dcp_on_final, + cookie); + } else { + handle = 2; + dcp_set_display_device(dcp, false, &handle, + dcp_on_set_parameter, cookie); + } + dcp_set_power_state(dcp, true, &req, NULL, NULL); + + ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(500)); + + if (ret == 0) + dev_warn(dcp->dev, "wait for power timed out"); + + if (atomic_dec_and_test(&cookie->refcount)) + kfree(cookie); +} +EXPORT_SYMBOL(dcp_poweron); + +static void complete_set_powerstate(struct apple_dcp *dcp, void *out, + void *cookie) +{ + struct dcp_wait_cookie *wait = cookie; + + if (wait) { + complete(&wait->done); + if (atomic_dec_and_test(&wait->refcount)) + kfree(wait); + } +} + +void dcp_poweroff(struct platform_device *pdev) +{ + struct apple_dcp *dcp = platform_get_drvdata(pdev); + int ret, swap_id; + struct dcp_set_power_state_req power_req = { + .unklong = 0, + }; + struct dcp_swap_cookie *cookie; + struct dcp_wait_cookie *poff_cookie; + struct dcp_swap_start_req swap_req = { 0 }; + + dev_dbg(dcp->dev, "%s", __func__); + + cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); + if (!cookie) + return; + init_completion(&cookie->done); + atomic_set(&cookie->refcount, 2); + + // clear surfaces + memset(&dcp->swap, 0, sizeof(dcp->swap)); + + dcp->swap.swap.swap_enabled = DCP_REMOVE_LAYERS | 0x7; + dcp->swap.swap.swap_completed = DCP_REMOVE_LAYERS | 0x7; + dcp->swap.swap.unk_10c = 0xFF000000; + + for (int l = 0; l < SWAP_SURFACES; l++) + dcp->swap.surf_null[l] = true; + + dcp_swap_start(dcp, false, &swap_req, dcp_swap_clear_started, cookie); + + ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(50)); + swap_id = cookie->swap_id; + if (atomic_dec_and_test(&cookie->refcount)) + kfree(cookie); + if (ret <= 0) { + dcp->crashed = true; + return; + } + + dev_dbg(dcp->dev, "%s: clear swap submitted: %u", __func__, swap_id); + + poff_cookie = kzalloc(sizeof(*poff_cookie), GFP_KERNEL); + if (!poff_cookie) + return; + init_completion(&poff_cookie->done); + atomic_set(&poff_cookie->refcount, 2); + + dcp_set_power_state(dcp, false, &power_req, complete_set_powerstate, + poff_cookie); + ret = wait_for_completion_timeout(&poff_cookie->done, + msecs_to_jiffies(1000)); + + if (ret == 0) + dev_warn(dcp->dev, "setPowerState(0) timeout %u ms", 1000); + else if (ret > 0) + dev_dbg(dcp->dev, + "setPowerState(0) finished with %d ms to spare", + jiffies_to_msecs(ret)); + + if (atomic_dec_and_test(&poff_cookie->refcount)) + kfree(poff_cookie); + dev_dbg(dcp->dev, "%s: setPowerState(0) done", __func__); +} +EXPORT_SYMBOL(dcp_poweroff); + +/* + * Helper to send a DRM hotplug event. The DCP is accessed from a single + * (RTKit) thread. To handle hotplug callbacks, we need to call + * drm_kms_helper_hotplug_event, which does an atomic commit (via DCP) and + * waits for vblank (a DCP callback). That means we deadlock if we call from + * the RTKit thread! Instead, move the call to another thread via a workqueue. + */ +void dcp_hotplug(struct work_struct *work) +{ + struct apple_connector *connector; + struct drm_device *dev; + struct apple_dcp *dcp; + + connector = container_of(work, struct apple_connector, hotplug_wq); + dev = connector->base.dev; + + dcp = platform_get_drvdata(connector->dcp); + dev_info(dcp->dev, "%s: connected: %d", __func__, connector->connected); + + /* + * DCP defers link training until we set a display mode. But we set + * display modes from atomic_flush, so userspace needs to trigger a + * flush, or the CRTC gets no signal. + */ + if (!dcp->valid_mode && connector->connected) { + drm_connector_set_link_status_property( + &connector->base, DRM_MODE_LINK_STATUS_BAD); + } + + if (dev && dev->registered) + drm_kms_helper_hotplug_event(dev); +} +EXPORT_SYMBOL_GPL(dcp_hotplug); + +static void dcpep_cb_hotplug(struct apple_dcp *dcp, u64 *connected) +{ + struct apple_connector *connector = dcp->connector; + + /* Hotplug invalidates mode. DRM doesn't always handle this. */ + if (!(*connected)) { + dcp->valid_mode = false; + /* after unplug swap will not complete until the next + * set_digital_out_mode */ + schedule_work(&dcp->vblank_wq); + } + + if (connector && connector->connected != !!(*connected)) { + connector->connected = !!(*connected); + dcp->valid_mode = false; + schedule_work(&connector->hotplug_wq); + } +} + +static void +dcpep_cb_swap_complete_intent_gated(struct apple_dcp *dcp, + struct dcp_swap_complete_intent_gated *info) +{ + dev_dbg(dcp->dev, "swap_id:%u width:%u height:%u", info->swap_id, + info->width, info->height); +} + +#define DCPEP_MAX_CB (1000) + +/* + * Define type-safe trampolines. Define typedefs to enforce type-safety on the + * input data (so if the types don't match, gcc errors out). + */ + +#define TRAMPOLINE_VOID(func, handler) \ + static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ + { \ + dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, #handler); \ + handler(dcp); \ + return true; \ + } + +#define TRAMPOLINE_IN(func, handler, T_in) \ + typedef void (*callback_##handler)(struct apple_dcp *, T_in *); \ + \ + static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ + { \ + callback_##handler cb = handler; \ + \ + dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, #handler); \ + cb(dcp, in); \ + return true; \ + } + +#define TRAMPOLINE_INOUT(func, handler, T_in, T_out) \ + typedef T_out (*callback_##handler)(struct apple_dcp *, T_in *); \ + \ + static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ + { \ + T_out *typed_out = out; \ + callback_##handler cb = handler; \ + \ + dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, #handler); \ + *typed_out = cb(dcp, in); \ + return true; \ + } + +#define TRAMPOLINE_OUT(func, handler, T_out) \ + static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ + { \ + T_out *typed_out = out; \ + \ + dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, #handler); \ + *typed_out = handler(dcp); \ + return true; \ + } + +TRAMPOLINE_VOID(trampoline_nop, dcpep_cb_nop); +TRAMPOLINE_OUT(trampoline_true, dcpep_cb_true, u8); +TRAMPOLINE_OUT(trampoline_false, dcpep_cb_false, u8); +TRAMPOLINE_OUT(trampoline_zero, dcpep_cb_zero, u32); +TRAMPOLINE_IN(trampoline_swap_complete, dcpep_cb_swap_complete, + struct dc_swap_complete_resp); +TRAMPOLINE_INOUT(trampoline_get_uint_prop, dcpep_cb_get_uint_prop, + struct dcp_get_uint_prop_req, struct dcp_get_uint_prop_resp); +TRAMPOLINE_INOUT(trampoline_map_piodma, dcpep_cb_map_piodma, + struct dcp_map_buf_req, struct dcp_map_buf_resp); +TRAMPOLINE_IN(trampoline_unmap_piodma, dcpep_cb_unmap_piodma, + struct dcp_unmap_buf_resp); +TRAMPOLINE_INOUT(trampoline_allocate_buffer, dcpep_cb_allocate_buffer, + struct dcp_allocate_buffer_req, + struct dcp_allocate_buffer_resp); +TRAMPOLINE_INOUT(trampoline_map_physical, dcpep_cb_map_physical, + struct dcp_map_physical_req, struct dcp_map_physical_resp); +TRAMPOLINE_INOUT(trampoline_release_mem_desc, dcpep_cb_release_mem_desc, u32, + u8); +TRAMPOLINE_INOUT(trampoline_map_reg, dcpep_cb_map_reg, struct dcp_map_reg_req, + struct dcp_map_reg_resp); +TRAMPOLINE_INOUT(trampoline_read_edt_data, dcpep_cb_read_edt_data, + struct dcp_read_edt_data_req, struct dcp_read_edt_data_resp); +TRAMPOLINE_INOUT(trampoline_prop_start, dcpep_cb_prop_start, u32, u8); +TRAMPOLINE_INOUT(trampoline_prop_chunk, dcpep_cb_prop_chunk, + struct dcp_set_dcpav_prop_chunk_req, u8); +TRAMPOLINE_INOUT(trampoline_prop_end, dcpep_cb_prop_end, + struct dcp_set_dcpav_prop_end_req, u8); +TRAMPOLINE_OUT(trampoline_rt_bandwidth, dcpep_cb_rt_bandwidth, + struct dcp_rt_bandwidth); +TRAMPOLINE_OUT(trampoline_get_frequency, dcpep_cb_get_frequency, u64); +TRAMPOLINE_OUT(trampoline_get_time, dcpep_cb_get_time, u64); +TRAMPOLINE_IN(trampoline_hotplug, dcpep_cb_hotplug, u64); +TRAMPOLINE_IN(trampoline_swap_complete_intent_gated, + dcpep_cb_swap_complete_intent_gated, + struct dcp_swap_complete_intent_gated); + +bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, + void *) = { + [0] = trampoline_true, /* did_boot_signal */ + [1] = trampoline_true, /* did_power_on_signal */ + [2] = trampoline_nop, /* will_power_off_signal */ + [3] = trampoline_rt_bandwidth, + [100] = trampoline_nop, /* match_pmu_service */ + [101] = trampoline_zero, /* get_display_default_stride */ + [103] = trampoline_nop, /* set_boolean_property */ + [106] = trampoline_nop, /* remove_property */ + [107] = trampoline_true, /* create_provider_service */ + [108] = trampoline_true, /* create_product_service */ + [109] = trampoline_true, /* create_pmu_service */ + [110] = trampoline_true, /* create_iomfb_service */ + [111] = trampoline_false, /* create_backlight_service */ + [116] = dcpep_cb_boot_1, + [117] = trampoline_false, /* is_dark_boot */ + [118] = trampoline_false, /* is_dark_boot / is_waking_from_hibernate*/ + [120] = trampoline_read_edt_data, + [122] = trampoline_prop_start, + [123] = trampoline_prop_chunk, + [124] = trampoline_prop_end, + [201] = trampoline_map_piodma, + [202] = trampoline_unmap_piodma, + [206] = trampoline_true, /* match_pmu_service_2 */ + [207] = trampoline_true, /* match_backlight_service */ + [208] = trampoline_get_time, + [211] = trampoline_nop, /* update_backlight_factor_prop */ + [300] = trampoline_nop, /* pr_publish */ + [401] = trampoline_get_uint_prop, + [404] = trampoline_nop, /* sr_set_uint_prop */ + [406] = trampoline_nop, /* set_fx_prop */ + [408] = trampoline_get_frequency, + [411] = trampoline_map_reg, + [413] = trampoline_true, /* sr_set_property_dict */ + [414] = trampoline_true, /* sr_set_property_int */ + [415] = trampoline_true, /* sr_set_property_bool */ + [451] = trampoline_allocate_buffer, + [452] = trampoline_map_physical, + [456] = trampoline_release_mem_desc, + [552] = trampoline_true, /* set_property_dict_0 */ + [561] = trampoline_true, /* set_property_dict */ + [563] = trampoline_true, /* set_property_int */ + [565] = trampoline_true, /* set_property_bool */ + [567] = trampoline_true, /* set_property_str */ + [574] = trampoline_zero, /* power_up_dart */ + [576] = trampoline_hotplug, + [577] = trampoline_nop, /* powerstate_notify */ + [582] = trampoline_true, /* create_default_fb_surface */ + [589] = trampoline_swap_complete, + [591] = trampoline_swap_complete_intent_gated, + [593] = trampoline_nop, /* enable_backlight_message_ap_gated */ + [598] = trampoline_nop, /* find_swap_function_gated */ +}; + +static void dcpep_handle_cb(struct apple_dcp *dcp, enum dcp_context_id context, + void *data, u32 length) +{ + struct device *dev = dcp->dev; + struct dcp_packet_header *hdr = data; + void *in, *out; + int tag = dcp_parse_tag(hdr->tag); + struct dcp_cb_channel *ch = dcp_get_cb_channel(dcp, context); + u8 depth; + + if (tag < 0 || tag >= DCPEP_MAX_CB || !dcpep_cb_handlers[tag]) { + dev_warn(dev, "received unknown callback %c%c%c%c\n", + hdr->tag[3], hdr->tag[2], hdr->tag[1], hdr->tag[0]); + return; + } + + in = data + sizeof(*hdr); + out = in + hdr->in_len; + + // TODO: verify that in_len and out_len match our prototypes + // for now just clear the out data to have at least consistant results + if (hdr->out_len) + memset(out, 0, hdr->out_len); + + depth = dcp_push_depth(&ch->depth); + ch->output[depth] = out; + + if (dcpep_cb_handlers[tag](dcp, tag, out, in)) + dcp_ack(dcp, context); +} + +static void dcpep_handle_ack(struct apple_dcp *dcp, enum dcp_context_id context, + void *data, u32 length) +{ + struct dcp_packet_header *header = data; + struct dcp_call_channel *ch = dcp_get_call_channel(dcp, context); + void *cookie; + dcp_callback_t cb; + + if (!ch) { + dev_warn(dcp->dev, "ignoring ack on context %X\n", context); + return; + } + + dcp_pop_depth(&ch->depth); + + cb = ch->callbacks[ch->depth]; + cookie = ch->cookies[ch->depth]; + + ch->callbacks[ch->depth] = NULL; + ch->cookies[ch->depth] = NULL; + + if (cb) + cb(dcp, data + sizeof(*header) + header->in_len, cookie); +} + +static void dcpep_got_msg(struct apple_dcp *dcp, u64 message) +{ + enum dcp_context_id ctx_id; + u16 offset; + u32 length; + int channel_offset; + void *data; + + ctx_id = (message & DCPEP_CONTEXT_MASK) >> DCPEP_CONTEXT_SHIFT; + offset = (message & DCPEP_OFFSET_MASK) >> DCPEP_OFFSET_SHIFT; + length = (message >> DCPEP_LENGTH_SHIFT); + + channel_offset = dcp_channel_offset(ctx_id); + + if (channel_offset < 0) { + dev_warn(dcp->dev, "invalid context received %u", ctx_id); + return; + } + + data = dcp->shmem + channel_offset + offset; + + if (message & DCPEP_ACK) + dcpep_handle_ack(dcp, ctx_id, data, length); + else + dcpep_handle_cb(dcp, ctx_id, data, length); +} + +/* + * Callback for swap requests. If a swap failed, we'll never get a swap + * complete event so we need to fake a vblank event early to avoid a hang. + */ + +static void dcp_swapped(struct apple_dcp *dcp, void *data, void *cookie) +{ + struct dcp_swap_submit_resp *resp = data; + + if (resp->ret) { + dev_err(dcp->dev, "swap failed! status %u\n", resp->ret); + dcp_drm_crtc_vblank(dcp->crtc); + return; + } + + while (!list_empty(&dcp->swapped_out_fbs)) { + struct dcp_fb_reference *entry; + entry = list_first_entry(&dcp->swapped_out_fbs, + struct dcp_fb_reference, head); + if (entry->fb) + drm_framebuffer_put(entry->fb); + list_del(&entry->head); + kfree(entry); + } +} + +static void dcp_swap_started(struct apple_dcp *dcp, void *data, void *cookie) +{ + struct dcp_swap_start_resp *resp = data; + + dcp->swap.swap.swap_id = resp->swap_id; + + dcp_swap_submit(dcp, false, &dcp->swap, dcp_swapped, NULL); +} + +/* + * DRM specifies rectangles as start and end coordinates. DCP specifies + * rectangles as a start coordinate and a width/height. Convert a DRM rectangle + * to a DCP rectangle. + */ +static struct dcp_rect drm_to_dcp_rect(struct drm_rect *rect) +{ + return (struct dcp_rect){ .x = rect->x1, + .y = rect->y1, + .w = drm_rect_width(rect), + .h = drm_rect_height(rect) }; +} + +static u32 drm_format_to_dcp(u32 drm) +{ + switch (drm) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + return fourcc_code('A', 'R', 'G', 'B'); + + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + return fourcc_code('A', 'B', 'G', 'R'); + } + + pr_warn("DRM format %X not supported in DCP\n", drm); + return 0; +} + +int dcp_get_modes(struct drm_connector *connector) +{ + struct apple_connector *apple_connector = to_apple_connector(connector); + struct platform_device *pdev = apple_connector->dcp; + struct apple_dcp *dcp = platform_get_drvdata(pdev); + + struct drm_device *dev = connector->dev; + struct drm_display_mode *mode; + int i; + + for (i = 0; i < dcp->nr_modes; ++i) { + mode = drm_mode_duplicate(dev, &dcp->modes[i].mode); + + if (!mode) { + dev_err(dev->dev, "Failed to duplicate display mode\n"); + return 0; + } + + drm_mode_probed_add(connector, mode); + } + + return dcp->nr_modes; +} +EXPORT_SYMBOL_GPL(dcp_get_modes); + +/* The user may own drm_display_mode, so we need to search for our copy */ +static struct dcp_display_mode *lookup_mode(struct apple_dcp *dcp, + struct drm_display_mode *mode) +{ + int i; + + for (i = 0; i < dcp->nr_modes; ++i) { + if (drm_mode_match(mode, &dcp->modes[i].mode, + DRM_MODE_MATCH_TIMINGS | + DRM_MODE_MATCH_CLOCK)) + return &dcp->modes[i]; + } + + return NULL; +} + +int dcp_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct apple_connector *apple_connector = to_apple_connector(connector); + struct platform_device *pdev = apple_connector->dcp; + struct apple_dcp *dcp = platform_get_drvdata(pdev); + + return lookup_mode(dcp, mode) ? MODE_OK : MODE_BAD; +} +EXPORT_SYMBOL_GPL(dcp_mode_valid); + +/* Helpers to modeset and swap, used to flush */ +static void do_swap(struct apple_dcp *dcp, void *data, void *cookie) +{ + struct dcp_swap_start_req start_req = { 0 }; + dev_dbg(dcp->dev, "%s", __func__); + + if (dcp->connector && dcp->connector->connected) + dcp_swap_start(dcp, false, &start_req, dcp_swap_started, NULL); + else + dcp_drm_crtc_vblank(dcp->crtc); +} + +static void complete_set_digital_out_mode(struct apple_dcp *dcp, void *data, + void *cookie) +{ + struct dcp_wait_cookie *wait = cookie; + dev_dbg(dcp->dev, "%s", __func__); + + dcp->ignore_swap_complete = false; + + if (wait) { + complete(&wait->done); + if (atomic_dec_and_test(&wait->refcount)) + kfree(wait); + } +} + +void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) +{ + struct platform_device *pdev = to_apple_crtc(crtc)->dcp; + struct apple_dcp *dcp = platform_get_drvdata(pdev); + struct drm_plane *plane; + struct drm_plane_state *new_state, *old_state; + struct drm_crtc_state *crtc_state; + struct dcp_swap_submit_req *req = &dcp->swap; + int l; + int has_surface = 0; + bool modeset; + dev_dbg(dcp->dev, "%s", __func__); + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + modeset = drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode; + + if (WARN(dcp_channel_busy(&dcp->ch_cmd), "unexpected busy channel") || + WARN(!modeset && !dcp->connector->connected, + "can't flush if disconnected")) { + /* HACK: issue a delayed vblank event to avoid timeouts in + * drm_atomic_helper_wait_for_vblanks(). + */ + schedule_work(&dcp->vblank_wq); + return; + } + + /* Reset to defaults */ + memset(req, 0, sizeof(*req)); + for (l = 0; l < SWAP_SURFACES; l++) + req->surf_null[l] = true; + + for_each_oldnew_plane_in_state(state, plane, old_state, new_state, l) { + struct drm_framebuffer *fb = new_state->fb; + struct drm_rect src_rect; + + WARN_ON(l >= SWAP_SURFACES); + + req->swap.swap_enabled |= BIT(l); + + if (old_state->fb && fb != old_state->fb) { + /* + * Race condition between a framebuffer unbind getting + * swapped out and GEM unreferencing a framebuffer. If + * we lose the race, the display gets IOVA faults and + * the DCP crashes. We need to extend the lifetime of + * the drm_framebuffer (and hence the GEM object) until + * after we get a swap complete for the swap unbinding + * it. + */ + struct dcp_fb_reference *entry = + kzalloc(sizeof(*entry), GFP_KERNEL); + if (entry) { + entry->fb = old_state->fb; + list_add_tail(&entry->head, + &dcp->swapped_out_fbs); + } + drm_framebuffer_get(old_state->fb); + } + + if (!new_state->fb) { + if (old_state->fb) + req->swap.swap_enabled |= DCP_REMOVE_LAYERS; + + continue; + } + req->surf_null[l] = false; + has_surface = 1; + + drm_rect_fp_to_int(&src_rect, &new_state->src); + + req->swap.src_rect[l] = drm_to_dcp_rect(&src_rect); + req->swap.dst_rect[l] = drm_to_dcp_rect(&new_state->dst); + + req->surf_iova[l] = drm_fb_dma_get_gem_addr(fb, new_state, 0); + + req->surf[l] = (struct dcp_surface){ + .format = drm_format_to_dcp(fb->format->format), + .xfer_func = 13, + .colorspace = 1, + .stride = fb->pitches[0], + .width = fb->width, + .height = fb->height, + .buf_size = fb->height * fb->pitches[0], + .surface_id = req->swap.surf_ids[l], + + /* Only used for compressed or multiplanar surfaces */ + .pix_size = 1, + .pel_w = 1, + .pel_h = 1, + .has_comp = 1, + .has_planes = 1, + }; + } + + /* These fields should be set together */ + req->swap.swap_completed = req->swap.swap_enabled; + + if (modeset) { + struct dcp_display_mode *mode; + struct dcp_wait_cookie *cookie; + int ret; + + mode = lookup_mode(dcp, &crtc_state->mode); + if (!mode) { + dev_warn(dcp->dev, "no match for " DRM_MODE_FMT, + DRM_MODE_ARG(&crtc_state->mode)); + schedule_work(&dcp->vblank_wq); + return; + } + + dev_info(dcp->dev, "set_digital_out_mode(color:%d timing:%d)", + mode->color_mode_id, mode->timing_mode_id); + dcp->mode = (struct dcp_set_digital_out_mode_req){ + .color_mode_id = mode->color_mode_id, + .timing_mode_id = mode->timing_mode_id + }; + + cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); + if (!cookie) { + schedule_work(&dcp->vblank_wq); + return; + } + + init_completion(&cookie->done); + atomic_set(&cookie->refcount, 2); + + dcp_set_digital_out_mode(dcp, false, &dcp->mode, + complete_set_digital_out_mode, cookie); + + dev_dbg(dcp->dev, "%s - wait for modeset", __func__); + ret = wait_for_completion_timeout(&cookie->done, + msecs_to_jiffies(500)); + + if (atomic_dec_and_test(&cookie->refcount)) + kfree(cookie); + + if (ret == 0) { + dev_dbg(dcp->dev, "set_digital_out_mode 200 ms"); + schedule_work(&dcp->vblank_wq); + return; + } else if (ret > 0) { + dev_dbg(dcp->dev, + "set_digital_out_mode finished with %d to spare", + jiffies_to_msecs(ret)); + } + + dcp->valid_mode = true; + } + + if (!has_surface) { + if (crtc_state->enable && crtc_state->active && + !crtc_state->planes_changed) { + schedule_work(&dcp->vblank_wq); + return; + } + + req->clear = 1; + } + do_swap(dcp, NULL, NULL); +} +EXPORT_SYMBOL_GPL(dcp_flush); + +bool dcp_is_initialized(struct platform_device *pdev) +{ + struct apple_dcp *dcp = platform_get_drvdata(pdev); + + return dcp->active; +} +EXPORT_SYMBOL_GPL(dcp_is_initialized); + +static void res_is_main_display(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct apple_connector *connector; + int result = *(int *)out; + dev_info(dcp->dev, "DCP is_main_display: %d\n", result); + + dcp->main_display = result != 0; + + dcp->active = true; + + connector = dcp->connector; + if (connector) { + connector->connected = dcp->nr_modes > 0; + schedule_work(&connector->hotplug_wq); + } +} + +static void init_3(struct apple_dcp *dcp, void *out, void *cookie) +{ + dcp_is_main_display(dcp, false, res_is_main_display, NULL); +} + +static void init_2(struct apple_dcp *dcp, void *out, void *cookie) +{ + dcp_first_client_open(dcp, false, init_3, NULL); +} + +static void init_1(struct apple_dcp *dcp, void *out, void *cookie) +{ + u32 val = 0; + dcp_enable_disable_video_power_savings(dcp, false, &val, init_2, NULL); +} + +static void dcp_started(struct apple_dcp *dcp, void *data, void *cookie) +{ + dev_info(dcp->dev, "DCP booted\n"); + + init_1(dcp, data, cookie); +} + +void iomfb_recv_msg(struct apple_dcp *dcp, u64 message) +{ + enum dcpep_type type = (message >> DCPEP_TYPE_SHIFT) & DCPEP_TYPE_MASK; + + if (type == DCPEP_TYPE_INITIALIZED) + dcp_start_signal(dcp, false, dcp_started, NULL); + else if (type == DCPEP_TYPE_MESSAGE) + dcpep_got_msg(dcp, message); + else + dev_warn(dcp->dev, "Ignoring unknown message %llx\n", message); +} + +int iomfb_start_rtkit(struct apple_dcp *dcp) +{ + dma_addr_t shmem_iova; + apple_rtkit_start_ep(dcp->rtk, IOMFB_ENDPOINT); + + dcp->shmem = dma_alloc_coherent(dcp->dev, DCP_SHMEM_SIZE, &shmem_iova, + GFP_KERNEL); + + shmem_iova |= dcp->asc_dram_mask; + apple_rtkit_send_message(dcp->rtk, IOMFB_ENDPOINT, + dcpep_set_shmem(shmem_iova), NULL, false); + + return 0; +} + +void iomfb_shutdown(struct apple_dcp *dcp) +{ + struct dcp_set_power_state_req req = { + /* defaults are ok */ + }; + + /* We're going down */ + dcp->active = false; + dcp->valid_mode = false; + + dcp_set_power_state(dcp, false, &req, NULL, NULL); +} diff --git a/drivers/gpu/drm/apple/dcpep.h b/drivers/gpu/drm/apple/iomfb.h similarity index 86% rename from drivers/gpu/drm/apple/dcpep.h rename to drivers/gpu/drm/apple/iomfb.h index a796b16bd55ad7..96dfd170b8e330 100644 --- a/drivers/gpu/drm/apple/dcpep.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -4,8 +4,10 @@ #ifndef __APPLE_DCPEP_H__ #define __APPLE_DCPEP_H__ +#include + /* Endpoint for general DCP traffic (dcpep in macOS) */ -#define DCP_ENDPOINT 0x37 +#define IOMFB_ENDPOINT 0x37 /* Fixed size of shared memory between DCP and AP */ #define DCP_SHMEM_SIZE 0x100000 @@ -30,27 +32,6 @@ enum dcp_context_id { DCP_NUM_CONTEXTS }; -static int dcp_tx_offset(enum dcp_context_id id) -{ - switch (id) { - case DCP_CONTEXT_CB: - case DCP_CONTEXT_CMD: return 0x00000; - case DCP_CONTEXT_OOBCB: - case DCP_CONTEXT_OOBCMD: return 0x08000; - default: return -EINVAL; - } -} - -static int dcp_channel_offset(enum dcp_context_id id) -{ - switch (id) { - case DCP_CONTEXT_ASYNC: return 0x40000; - case DCP_CONTEXT_CB: return 0x60000; - case DCP_CONTEXT_OOBCB: return 0x68000; - default: return dcp_tx_offset(id); - } -} - /* RTKit endpoint message types */ enum dcpep_type { /* Set shared memory */ @@ -87,29 +68,6 @@ struct dcp_packet_header { #define DCP_IS_NULL(ptr) ((ptr) ? 1 : 0) #define DCP_PACKET_ALIGNMENT (0x40) -static inline u64 -dcpep_set_shmem(u64 dart_va) -{ - return (DCPEP_TYPE_SET_SHMEM << DCPEP_TYPE_SHIFT) | - (DCPEP_FLAG_VALUE << DCPEP_FLAG_SHIFT) | - (dart_va << DCPEP_DVA_SHIFT); -} - -static inline u64 -dcpep_msg(enum dcp_context_id id, u32 length, u16 offset) -{ - return (DCPEP_TYPE_MESSAGE << DCPEP_TYPE_SHIFT) | - ((u64) id << DCPEP_CONTEXT_SHIFT) | - ((u64) offset << DCPEP_OFFSET_SHIFT) | - ((u64) length << DCPEP_LENGTH_SHIFT); -} - -static inline u64 -dcpep_ack(enum dcp_context_id id) -{ - return dcpep_msg(id, 0, 0) | DCPEP_ACK; -} - /* Structures used in v12.0 firmware */ #define SWAP_SURFACES 4 From 5b85f32bb5dbb9b23ec988b0db7d15ea067e1e25 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 3 Oct 2022 10:43:02 +0200 Subject: [PATCH 0451/1020] WIP: add header test target copied from i915 Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/.gitignore | 1 + drivers/gpu/drm/apple/Makefile | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 drivers/gpu/drm/apple/.gitignore diff --git a/drivers/gpu/drm/apple/.gitignore b/drivers/gpu/drm/apple/.gitignore new file mode 100644 index 00000000000000..d9a77f3b59b21a --- /dev/null +++ b/drivers/gpu/drm/apple/.gitignore @@ -0,0 +1 @@ +*.hdrtest diff --git a/drivers/gpu/drm/apple/Makefile b/drivers/gpu/drm/apple/Makefile index d1f909792229e5..288c89739af196 100644 --- a/drivers/gpu/drm/apple/Makefile +++ b/drivers/gpu/drm/apple/Makefile @@ -7,3 +7,18 @@ apple_piodma-y := dummy-piodma.o obj-$(CONFIG_DRM_APPLE) += appledrm.o obj-$(CONFIG_DRM_APPLE) += apple_dcp.o obj-$(CONFIG_DRM_APPLE) += apple_piodma.o + +# header test + +# exclude some broken headers from the test coverage +no-header-test := \ + +always-y += \ + $(patsubst %.h,%.hdrtest, $(filter-out $(no-header-test), \ + $(shell cd $(srctree)/$(src) && find * -name '*.h'))) + +quiet_cmd_hdrtest = HDRTEST $(patsubst %.hdrtest,%.h,$@) + cmd_hdrtest = $(CC) $(filter-out $(CFLAGS_GCOV), $(c_flags)) -S -o /dev/null -x c /dev/null -include $<; touch $@ + +$(obj)/%.hdrtest: $(src)/%.h FORCE + $(call if_changed_dep,hdrtest) From cc8c8a193041aefed886fd143fc99aa54c39f5e5 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 22 Oct 2022 09:27:51 +0200 Subject: [PATCH 0452/1020] gpu: drm: apple: Use connector types from devicetree Needs to be re-done for upstream submission but the exisiting port, bridge, connector and display bindings are a bad fit since DCP manages everything. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index f576cefd8c3353..fa966dcc7e9447 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -298,6 +299,7 @@ static int apple_probe_per_dcp(struct device *dev, struct apple_connector *connector; struct drm_encoder *encoder; struct drm_plane *primary; + int con_type; int ret; primary = apple_plane_init(drm, DRM_PLANE_TYPE_PRIMARY); @@ -323,8 +325,17 @@ static int apple_probe_per_dcp(struct device *dev, drm_connector_helper_add(&connector->base, &apple_connector_helper_funcs); + if (of_property_match_string(dcp->dev.of_node, "apple,connector-type", "eDP") >= 0) + con_type = DRM_MODE_CONNECTOR_eDP; + else if (of_property_match_string(dcp->dev.of_node, "apple,connector-type", "HDMI-A") >= 0) + con_type = DRM_MODE_CONNECTOR_HDMIA; + else if (of_property_match_string(dcp->dev.of_node, "apple,connector-type", "USB-C") >= 0) + con_type = DRM_MODE_CONNECTOR_USB; + else + con_type = DRM_MODE_CONNECTOR_Unknown; + ret = drm_connector_init(drm, &connector->base, &apple_connector_funcs, - DRM_MODE_CONNECTOR_HDMIA); + con_type); if (ret) return ret; From 427dc00cf5d85a54bea380cebb5877174abc6588 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 5 Oct 2022 22:30:58 +0200 Subject: [PATCH 0453/1020] drm: apple: Fix connector state on devices with integrated display DCP issues hotplug_gated callbacks after SetPowerState() calls on devices with display (macbooks, imacs). This must not result in connector state changes on DRM side. Weston will not re-enable the CRTC after DPMS off if the connector is not in connected state. DCP provides with dcp_is_main_display() a call to query if the device has an integrated display. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 0da3de1aa27e78..3c930209382685 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -983,6 +983,16 @@ static void dcpep_cb_hotplug(struct apple_dcp *dcp, u64 *connected) { struct apple_connector *connector = dcp->connector; + /* DCP issues hotplug_gated callbacks after SetPowerState() calls on + * devices with display (macbooks, imacs). This must not result in + * connector state changes on DRM side. Some applications won't enable + * a CRTC with a connector in disconnected state. Weston after DPMS off + * is one example. dcp_is_main_display() returns true on devices with + * integrated display. Ignore the hotplug_gated() callbacks there. + */ + if (dcp->main_display) + return; + /* Hotplug invalidates mode. DRM doesn't always handle this. */ if (!(*connected)) { dcp->valid_mode = false; From 62f60804507edb564d4c283124cde745a25831b4 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 5 Oct 2022 22:59:54 +0200 Subject: [PATCH 0454/1020] drm: apple: Replace atomic refcount with kref fixup! drm/apple: Add t600x support Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 61 ++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 3c930209382685..68ca1c2aa8354e 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -32,10 +33,18 @@ #define REG_DOORBELL_BIT (2) struct dcp_wait_cookie { + struct kref refcount; struct completion done; - atomic_t refcount; }; +static void release_wait_cookie(struct kref *ref) +{ + struct dcp_wait_cookie *cookie; + cookie = container_of(ref, struct dcp_wait_cookie, refcount); + + kfree(cookie); +} + static int dcp_tx_offset(enum dcp_context_id id) { switch (id) { @@ -755,11 +764,19 @@ static u64 dcpep_cb_get_time(struct apple_dcp *dcp) } struct dcp_swap_cookie { + struct kref refcount; struct completion done; - atomic_t refcount; u32 swap_id; }; +static void release_swap_cookie(struct kref *ref) +{ + struct dcp_swap_cookie *cookie; + cookie = container_of(ref, struct dcp_swap_cookie, refcount); + + kfree(cookie); +} + static void dcp_swap_cleared(struct apple_dcp *dcp, void *data, void *cookie) { struct dcp_swap_submit_resp *resp = data; @@ -768,8 +785,7 @@ static void dcp_swap_cleared(struct apple_dcp *dcp, void *data, void *cookie) if (cookie) { struct dcp_swap_cookie *info = cookie; complete(&info->done); - if (atomic_dec_and_test(&info->refcount)) - kfree(info); + kref_put(&info->refcount, release_swap_cookie); } if (resp->ret) { @@ -811,8 +827,7 @@ static void dcp_on_final(struct apple_dcp *dcp, void *out, void *cookie) if (wait) { complete(&wait->done); - if (atomic_dec_and_test(&wait->refcount)) - kfree(wait); + kref_put(&wait->refcount, release_wait_cookie); } } @@ -844,7 +859,9 @@ void dcp_poweron(struct platform_device *pdev) return; init_completion(&cookie->done); - atomic_set(&cookie->refcount, 2); + kref_init(&cookie->refcount); + /* increase refcount to ensure the receiver has a reference */ + kref_get(&cookie->refcount); if (dcp->main_display) { handle = 0; @@ -862,8 +879,7 @@ void dcp_poweron(struct platform_device *pdev) if (ret == 0) dev_warn(dcp->dev, "wait for power timed out"); - if (atomic_dec_and_test(&cookie->refcount)) - kfree(cookie); + kref_put(&cookie->refcount, release_wait_cookie);; } EXPORT_SYMBOL(dcp_poweron); @@ -874,8 +890,7 @@ static void complete_set_powerstate(struct apple_dcp *dcp, void *out, if (wait) { complete(&wait->done); - if (atomic_dec_and_test(&wait->refcount)) - kfree(wait); + kref_put(&wait->refcount, release_wait_cookie); } } @@ -896,7 +911,9 @@ void dcp_poweroff(struct platform_device *pdev) if (!cookie) return; init_completion(&cookie->done); - atomic_set(&cookie->refcount, 2); + kref_init(&cookie->refcount); + /* increase refcount to ensure the receiver has a reference */ + kref_get(&cookie->refcount); // clear surfaces memset(&dcp->swap, 0, sizeof(dcp->swap)); @@ -912,8 +929,7 @@ void dcp_poweroff(struct platform_device *pdev) ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(50)); swap_id = cookie->swap_id; - if (atomic_dec_and_test(&cookie->refcount)) - kfree(cookie); + kref_put(&cookie->refcount, release_swap_cookie); if (ret <= 0) { dcp->crashed = true; return; @@ -925,7 +941,9 @@ void dcp_poweroff(struct platform_device *pdev) if (!poff_cookie) return; init_completion(&poff_cookie->done); - atomic_set(&poff_cookie->refcount, 2); + kref_init(&poff_cookie->refcount); + /* increase refcount to ensure the receiver has a reference */ + kref_get(&poff_cookie->refcount); dcp_set_power_state(dcp, false, &power_req, complete_set_powerstate, poff_cookie); @@ -939,8 +957,7 @@ void dcp_poweroff(struct platform_device *pdev) "setPowerState(0) finished with %d ms to spare", jiffies_to_msecs(ret)); - if (atomic_dec_and_test(&poff_cookie->refcount)) - kfree(poff_cookie); + kref_put(&poff_cookie->refcount, release_wait_cookie); dev_dbg(dcp->dev, "%s: setPowerState(0) done", __func__); } EXPORT_SYMBOL(dcp_poweroff); @@ -1379,8 +1396,7 @@ static void complete_set_digital_out_mode(struct apple_dcp *dcp, void *data, if (wait) { complete(&wait->done); - if (atomic_dec_and_test(&wait->refcount)) - kfree(wait); + kref_put(&wait->refcount, release_wait_cookie); } } @@ -1509,7 +1525,9 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) } init_completion(&cookie->done); - atomic_set(&cookie->refcount, 2); + kref_init(&cookie->refcount); + /* increase refcount to ensure the receiver has a reference */ + kref_get(&cookie->refcount); dcp_set_digital_out_mode(dcp, false, &dcp->mode, complete_set_digital_out_mode, cookie); @@ -1518,8 +1536,7 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(500)); - if (atomic_dec_and_test(&cookie->refcount)) - kfree(cookie); + kref_put(&cookie->refcount, release_wait_cookie); if (ret == 0) { dev_dbg(dcp->dev, "set_digital_out_mode 200 ms"); From 8eb73b2941826cb2d9fc497666db9bb331e3cfb1 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 8 Oct 2022 18:30:41 +0200 Subject: [PATCH 0455/1020] gpu: drm: apple: Start using tracepoints Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/Makefile | 5 + drivers/gpu/drm/apple/dcp-internal.h | 11 ++ drivers/gpu/drm/apple/dcp.c | 10 ++ drivers/gpu/drm/apple/dcp.h | 2 +- drivers/gpu/drm/apple/iomfb.c | 32 +++--- drivers/gpu/drm/apple/iomfb.h | 3 - drivers/gpu/drm/apple/trace.c | 9 ++ drivers/gpu/drm/apple/trace.h | 166 +++++++++++++++++++++++++++ 8 files changed, 217 insertions(+), 21 deletions(-) create mode 100644 drivers/gpu/drm/apple/trace.c create mode 100644 drivers/gpu/drm/apple/trace.h diff --git a/drivers/gpu/drm/apple/Makefile b/drivers/gpu/drm/apple/Makefile index 288c89739af196..2502f781a5dcef 100644 --- a/drivers/gpu/drm/apple/Makefile +++ b/drivers/gpu/drm/apple/Makefile @@ -1,7 +1,12 @@ # SPDX-License-Identifier: GPL-2.0-only OR MIT +CFLAGS_trace.o = -I$(src) + appledrm-y := apple_drv.o + apple_dcp-y := dcp.o iomfb.o parser.o +apple_dcp-$(CONFIG_TRACING) += trace.o + apple_piodma-y := dummy-piodma.o obj-$(CONFIG_DRM_APPLE) += appledrm.o diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 648d543b9cd91a..fd7c3aac603e35 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -12,6 +12,17 @@ struct apple_dcp; +enum { + SYSTEM_ENDPOINT = 0x20, + TEST_ENDPOINT = 0x21, + DCP_EXPERT_ENDPOINT = 0x22, + DISP0_ENDPOINT = 0x23, + DPTX_ENDPOINT = 0x2a, + HDCP_ENDPOINT = 0x2b, + REMOTE_ALLOC_ENDPOINT = 0x2d, + IOMFB_ENDPOINT = 0x37, +}; + /* Temporary backing for a chunked transfer via setDCPAVPropStart/Chunk/End */ struct dcp_chunks { size_t length; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 6ba5b6e93bddf2..10edc9e6377d09 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -22,6 +22,7 @@ #include "dcp.h" #include "dcp-internal.h" #include "parser.h" +#include "trace.h" #define APPLE_DCP_COPROC_CPU_CONTROL 0x44 #define APPLE_DCP_COPROC_CPU_CONTROL_RUN BIT(4) @@ -90,6 +91,8 @@ static void dcp_recv_msg(void *cookie, u8 endpoint, u64 message) { struct apple_dcp *dcp = cookie; + trace_dcp_recv_msg(dcp, endpoint, message); + switch (endpoint) { case IOMFB_ENDPOINT: return iomfb_recv_msg(dcp, message); @@ -167,6 +170,13 @@ static struct apple_rtkit_ops rtkit_ops = { .shmem_destroy = dcp_rtk_shmem_destroy, }; +void dcp_send_message(struct apple_dcp *dcp, u8 endpoint, u64 message) +{ + trace_dcp_send_msg(dcp, endpoint, message); + apple_rtkit_send_message(dcp->rtk, endpoint, message, NULL, + false); +} + void dcp_link(struct platform_device *pdev, struct apple_crtc *crtc, struct apple_connector *connector) { diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h index 18d71afaf6b72e..047c1b3a160457 100644 --- a/drivers/gpu/drm/apple/dcp.h +++ b/drivers/gpu/drm/apple/dcp.h @@ -47,7 +47,7 @@ int dcp_get_modes(struct drm_connector *connector); int dcp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode); void dcp_set_dimensions(struct apple_dcp *dcp); - +void dcp_send_message(struct apple_dcp *dcp, u8 endpoint, u64 message); int iomfb_start_rtkit(struct apple_dcp *dcp); void iomfb_shutdown(struct apple_dcp *dcp); diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 68ca1c2aa8354e..5bab8825df5a60 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -25,6 +25,7 @@ #include "dcp-internal.h" #include "iomfb.h" #include "parser.h" +#include "trace.h" /* Register defines used in bandwidth setup structure */ #define REG_SCRATCH (0x14) @@ -228,17 +229,15 @@ static void dcp_push(struct apple_dcp *dcp, bool oob, enum dcpep_method method, if (in_len > 0) memcpy(out_data, data, in_len); - dev_dbg(dcp->dev, "---> %s: context %u, offset %u, depth %u\n", - dcp_methods[method].name, context, offset, depth); + trace_iomfb_push(dcp, &dcp_methods[method], context, offset, depth); ch->callbacks[depth] = cb; ch->cookies[depth] = cookie; ch->output[depth] = out + sizeof(header) + in_len; ch->end[depth] = offset + ALIGN(data_len, DCP_PACKET_ALIGNMENT); - apple_rtkit_send_message(dcp->rtk, IOMFB_ENDPOINT, - dcpep_msg(context, data_len, offset), NULL, - false); + dcp_send_message(dcp, IOMFB_ENDPOINT, + dcpep_msg(context, data_len, offset)); } #define DCP_THUNK_VOID(func, handle) \ @@ -333,8 +332,8 @@ static void dcp_ack(struct apple_dcp *dcp, enum dcp_context_id context) struct dcp_cb_channel *ch = dcp_get_cb_channel(dcp, context); dcp_pop_depth(&ch->depth); - apple_rtkit_send_message(dcp->rtk, IOMFB_ENDPOINT, dcpep_ack(context), - NULL, false); + dcp_send_message(dcp, IOMFB_ENDPOINT, + dcpep_ack(context)); } /* DCP callback handlers */ @@ -361,8 +360,7 @@ static u32 dcpep_cb_zero(struct apple_dcp *dcp) static void dcpep_cb_swap_complete(struct apple_dcp *dcp, struct dc_swap_complete_resp *resp) { - dev_dbg(dcp->dev, "swap complete for swap_id: %u vblank: %u", - resp->swap_id, dcp->ignore_swap_complete); + trace_iomfb_swap_complete(dcp, resp->swap_id); if (!dcp->ignore_swap_complete) dcp_drm_crtc_vblank(dcp->crtc); @@ -725,7 +723,7 @@ static void boot_1_5(struct apple_dcp *dcp, void *out, void *cookie) /* Use special function signature to defer the ACK */ static bool dcpep_cb_boot_1(struct apple_dcp *dcp, int tag, void *out, void *in) { - dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, __func__); + trace_iomfb_callback(dcp, tag, __func__); dcp_set_create_dfb(dcp, false, boot_1_5, NULL); return false; } @@ -1029,7 +1027,7 @@ static void dcpep_cb_swap_complete_intent_gated(struct apple_dcp *dcp, struct dcp_swap_complete_intent_gated *info) { - dev_dbg(dcp->dev, "swap_id:%u width:%u height:%u", info->swap_id, + trace_iomfb_swap_complete_intent_gated(dcp, info->swap_id, info->width, info->height); } @@ -1043,7 +1041,7 @@ dcpep_cb_swap_complete_intent_gated(struct apple_dcp *dcp, #define TRAMPOLINE_VOID(func, handler) \ static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ { \ - dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, #handler); \ + trace_iomfb_callback(dcp, tag, #handler); \ handler(dcp); \ return true; \ } @@ -1055,7 +1053,7 @@ dcpep_cb_swap_complete_intent_gated(struct apple_dcp *dcp, { \ callback_##handler cb = handler; \ \ - dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, #handler); \ + trace_iomfb_callback(dcp, tag, #handler); \ cb(dcp, in); \ return true; \ } @@ -1068,7 +1066,7 @@ dcpep_cb_swap_complete_intent_gated(struct apple_dcp *dcp, T_out *typed_out = out; \ callback_##handler cb = handler; \ \ - dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, #handler); \ + trace_iomfb_callback(dcp, tag, #handler); \ *typed_out = cb(dcp, in); \ return true; \ } @@ -1078,7 +1076,7 @@ dcpep_cb_swap_complete_intent_gated(struct apple_dcp *dcp, { \ T_out *typed_out = out; \ \ - dev_dbg(dcp->dev, "Callback D%03d %s\n", tag, #handler); \ + trace_iomfb_callback(dcp, tag, #handler); \ *typed_out = handler(dcp); \ return true; \ } @@ -1290,6 +1288,7 @@ static void dcp_swap_started(struct apple_dcp *dcp, void *data, void *cookie) dcp->swap.swap.swap_id = resp->swap_id; + trace_iomfb_swap_submit(dcp, resp->swap_id); dcp_swap_submit(dcp, false, &dcp->swap, dcp_swapped, NULL); } @@ -1633,8 +1632,7 @@ int iomfb_start_rtkit(struct apple_dcp *dcp) GFP_KERNEL); shmem_iova |= dcp->asc_dram_mask; - apple_rtkit_send_message(dcp->rtk, IOMFB_ENDPOINT, - dcpep_set_shmem(shmem_iova), NULL, false); + dcp_send_message(dcp, IOMFB_ENDPOINT, dcpep_set_shmem(shmem_iova)); return 0; } diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index 96dfd170b8e330..5b1f4ba789bccf 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -6,9 +6,6 @@ #include -/* Endpoint for general DCP traffic (dcpep in macOS) */ -#define IOMFB_ENDPOINT 0x37 - /* Fixed size of shared memory between DCP and AP */ #define DCP_SHMEM_SIZE 0x100000 diff --git a/drivers/gpu/drm/apple/trace.c b/drivers/gpu/drm/apple/trace.c new file mode 100644 index 00000000000000..6f40d5a583df01 --- /dev/null +++ b/drivers/gpu/drm/apple/trace.c @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * Tracepoints for Apple DCP driver + * + * Copyright (C) The Asahi Linux Contributors + */ + +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/drivers/gpu/drm/apple/trace.h b/drivers/gpu/drm/apple/trace.h new file mode 100644 index 00000000000000..d6a4742fcf470d --- /dev/null +++ b/drivers/gpu/drm/apple/trace.h @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (C) The Asahi Linux Contributors */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM dcp + +#if !defined(_TRACE_DCP_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_DCP_H + +#include "dcp-internal.h" + +#include +#include +#include + +#define show_dcp_endpoint(ep) \ + __print_symbolic(ep, { SYSTEM_ENDPOINT, "system" }, \ + { TEST_ENDPOINT, "test" }, \ + { DCP_EXPERT_ENDPOINT, "dcpexpert" }, \ + { DISP0_ENDPOINT, "disp0" }, \ + { DPTX_ENDPOINT, "dptxport" }, \ + { HDCP_ENDPOINT, "hdcp" }, \ + { REMOTE_ALLOC_ENDPOINT, "remotealloc" }, \ + { IOMFB_ENDPOINT, "iomfb" }) + +TRACE_EVENT(dcp_recv_msg, + TP_PROTO(struct apple_dcp *dcp, u8 endpoint, u64 message), + TP_ARGS(dcp, endpoint, message), + + TP_STRUCT__entry(__string(devname, dev_name(dcp->dev)) + __field(u8, endpoint) + __field(u64, message)), + + TP_fast_assign(__assign_str(devname, dev_name(dcp->dev)); + __entry->endpoint = endpoint; + __entry->message = message;), + + TP_printk("%s: endpoint 0x%x (%s): received message 0x%016llx", + __get_str(devname), __entry->endpoint, + show_dcp_endpoint(__entry->endpoint), __entry->message)); + +TRACE_EVENT(dcp_send_msg, + TP_PROTO(struct apple_dcp *dcp, u8 endpoint, u64 message), + TP_ARGS(dcp, endpoint, message), + + TP_STRUCT__entry(__string(devname, dev_name(dcp->dev)) + __field(u8, endpoint) + __field(u64, message)), + + TP_fast_assign(__assign_str(devname, dev_name(dcp->dev)); + __entry->endpoint = endpoint; + __entry->message = message;), + + TP_printk("%s: endpoint 0x%x (%s): will send message 0x%016llx", + __get_str(devname), __entry->endpoint, + show_dcp_endpoint(__entry->endpoint), __entry->message)); + +TRACE_EVENT(iomfb_callback, + TP_PROTO(struct apple_dcp *dcp, int tag, const char *name), + TP_ARGS(dcp, tag, name), + + TP_STRUCT__entry( + __string(devname, dev_name(dcp->dev)) + __field(int, tag) + __field(const char *, name) + ), + + TP_fast_assign( + __assign_str(devname, dev_name(dcp->dev)); + __entry->tag = tag; __entry->name = name; + ), + + TP_printk("%s: Callback D%03d %s", __get_str(devname), __entry->tag, + __entry->name)); + +TRACE_EVENT(iomfb_push, + TP_PROTO(struct apple_dcp *dcp, + const struct dcp_method_entry *method, int context, + int offset, int depth), + TP_ARGS(dcp, method, context, offset, depth), + + TP_STRUCT__entry( + __string(devname, dev_name(dcp->dev)) + __string(name, method->name) + __field(int, context) + __field(int, offset) + __field(int, depth)), + + TP_fast_assign( + __assign_str(devname, dev_name(dcp->dev)); + __assign_str(name, method->name); + __entry->context = context; __entry->offset = offset; + __entry->depth = depth; + ), + + TP_printk("%s: Method %s: context %u, offset %u, depth %u", + __get_str(devname), __get_str(name), __entry->context, + __entry->offset, __entry->depth)); + +TRACE_EVENT(iomfb_swap_submit, + TP_PROTO(struct apple_dcp *dcp, u32 swap_id), + TP_ARGS(dcp, swap_id), + TP_STRUCT__entry( + __field(u64, dcp) + __field(u32, swap_id) + ), + TP_fast_assign( + __entry->dcp = (u64)dcp; + __entry->swap_id = swap_id; + ), + TP_printk("dcp=%llx, swap_id=%d", + __entry->dcp, + __entry->swap_id) +); + +TRACE_EVENT(iomfb_swap_complete, + TP_PROTO(struct apple_dcp *dcp, u32 swap_id), + TP_ARGS(dcp, swap_id), + TP_STRUCT__entry( + __field(u64, dcp) + __field(u32, swap_id) + ), + TP_fast_assign( + __entry->dcp = (u64)dcp; + __entry->swap_id = swap_id; + ), + TP_printk("dcp=%llx, swap_id=%d", + __entry->dcp, + __entry->swap_id + ) +); + +TRACE_EVENT(iomfb_swap_complete_intent_gated, + TP_PROTO(struct apple_dcp *dcp, u32 swap_id, u32 width, u32 height), + TP_ARGS(dcp, swap_id, width, height), + TP_STRUCT__entry( + __field(u64, dcp) + __field(u32, swap_id) + __field(u32, width) + __field(u32, height) + ), + TP_fast_assign( + __entry->dcp = (u64)dcp; + __entry->swap_id = swap_id; + __entry->height = height; + __entry->width = width; + ), + TP_printk("dcp=%llx, swap_id=%u %ux%u", + __entry->dcp, + __entry->swap_id, + __entry->width, + __entry->height + ) +); + +#endif /* _TRACE_DCP_H */ + +/* This part must be outside protection */ + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#include From 410b525f7b7a51960e935912d7b8343891dc1a06 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 11 Oct 2022 08:34:03 +0200 Subject: [PATCH 0456/1020] gpu: drm: apple: Unbreak multiple DCP plane <-> crtc matching Still untested so this changes the status from definitively broken to probably broken. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 11 +++++++---- drivers/gpu/drm/apple/iomfb.c | 12 ++++++++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index fa966dcc7e9447..ade8eeb9cfb91c 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -134,6 +134,7 @@ u64 apple_format_modifiers[] = { }; static struct drm_plane *apple_plane_init(struct drm_device *dev, + unsigned long possible_crtcs, enum drm_plane_type type) { int ret; @@ -141,7 +142,8 @@ static struct drm_plane *apple_plane_init(struct drm_device *dev, plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL); - ret = drm_universal_plane_init(dev, plane, 0x1, &apple_plane_funcs, + ret = drm_universal_plane_init(dev, plane, possible_crtcs, + &apple_plane_funcs, dcp_formats, ARRAY_SIZE(dcp_formats), apple_format_modifiers, type, NULL); if (ret) @@ -293,7 +295,8 @@ static const struct drm_crtc_helper_funcs apple_crtc_helper_funcs = { static int apple_probe_per_dcp(struct device *dev, struct drm_device *drm, - struct platform_device *dcp) + struct platform_device *dcp, + int num) { struct apple_crtc *crtc; struct apple_connector *connector; @@ -302,7 +305,7 @@ static int apple_probe_per_dcp(struct device *dev, int con_type; int ret; - primary = apple_plane_init(drm, DRM_PLANE_TYPE_PRIMARY); + primary = apple_plane_init(drm, 1U << num, DRM_PLANE_TYPE_PRIMARY); if (IS_ERR(primary)) return PTR_ERR(primary); @@ -419,7 +422,7 @@ static int apple_platform_probe(struct platform_device *pdev) apple->drm.mode_config.helper_private = &apple_mode_config_helpers; for (i = 0; i < nr_dcp; ++i) { - ret = apple_probe_per_dcp(&pdev->dev, &apple->drm, dcp[i]); + ret = apple_probe_per_dcp(&pdev->dev, &apple->drm, dcp[i], i); if (ret) goto err_unload; diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 5bab8825df5a60..d4b3dc8d3e6dd5 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -1407,7 +1407,7 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) struct drm_plane_state *new_state, *old_state; struct drm_crtc_state *crtc_state; struct dcp_swap_submit_req *req = &dcp->swap; - int l; + int plane_idx, l; int has_surface = 0; bool modeset; dev_dbg(dcp->dev, "%s", __func__); @@ -1431,10 +1431,15 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) for (l = 0; l < SWAP_SURFACES; l++) req->surf_null[l] = true; - for_each_oldnew_plane_in_state(state, plane, old_state, new_state, l) { + l = 0; + for_each_oldnew_plane_in_state(state, plane, old_state, new_state, plane_idx) { struct drm_framebuffer *fb = new_state->fb; struct drm_rect src_rect; + /* skip planes not for this crtc */ + if (old_state->crtc != crtc && new_state->crtc != crtc) + continue; + WARN_ON(l >= SWAP_SURFACES); req->swap.swap_enabled |= BIT(l); @@ -1463,6 +1468,7 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) if (old_state->fb) req->swap.swap_enabled |= DCP_REMOVE_LAYERS; + l += 1; continue; } req->surf_null[l] = false; @@ -1492,6 +1498,8 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) .has_comp = 1, .has_planes = 1, }; + + l += 1; } /* These fields should be set together */ From 1fed810daa5e742cd36a48f6370b46eb8a8cd32c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 11 Oct 2022 08:38:32 +0200 Subject: [PATCH 0457/1020] gpu: drm: apple: Add support for DRM_FORMAT_XRGB2101010 iboot uses DRM_FORMAT_XRGB2101010 at boot announce preference by listing it as first format. Disabled for now since it results in oversaturated colors. Might be fixed by using "IOMFB::UPPipe2::set_matrix()". Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 2 ++ drivers/gpu/drm/apple/iomfb.c | 23 ++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index ade8eeb9cfb91c..b3518f47306660 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -122,6 +122,8 @@ static const struct drm_plane_funcs apple_plane_funcs = { * advertise formats without alpha. */ static const u32 dcp_formats[] = { + // DRM_FORMAT_XRGB2101010, + // DRM_FORMAT_ARGB2101010, DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_XBGR8888, diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index d4b3dc8d3e6dd5..56622c9e463173 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -1315,12 +1315,33 @@ static u32 drm_format_to_dcp(u32 drm) case DRM_FORMAT_XBGR8888: case DRM_FORMAT_ABGR8888: return fourcc_code('A', 'B', 'G', 'R'); + + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_XRGB2101010: + return fourcc_code('r', '0', '3', 'w'); } pr_warn("DRM format %X not supported in DCP\n", drm); return 0; } +static u8 drm_format_to_colorspace(u32 drm) +{ + switch (drm) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + return 1; + + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_XRGB2101010: + return 2; + } + + return 1; +} + int dcp_get_modes(struct drm_connector *connector) { struct apple_connector *apple_connector = to_apple_connector(connector); @@ -1484,7 +1505,7 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) req->surf[l] = (struct dcp_surface){ .format = drm_format_to_dcp(fb->format->format), .xfer_func = 13, - .colorspace = 1, + .colorspace = drm_format_to_colorspace(fb->format->format), .stride = fb->pitches[0], .width = fb->width, .height = fb->height, From c92a73cc2f08a4fa3f877b3f1feb6c0abc3d664a Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 11 Oct 2022 08:40:46 +0200 Subject: [PATCH 0458/1020] gpu: drm: apple: Add apple_drm_gem_dumb_create() DCP needs a 64-byte aligned pitch, override drm_gem_dma_dumb_create() to align the pitch as necessary and use drm_gem_dma_dumb_create_internal() to allocate the GEM object. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index b3518f47306660..6eb67efb1febd5 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -45,8 +45,18 @@ struct apple_drm_private { DEFINE_DRM_GEM_DMA_FOPS(apple_fops); +static int apple_drm_gem_dumb_create(struct drm_file *file_priv, + struct drm_device *drm, + struct drm_mode_create_dumb *args) +{ + args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), 64); + args->size = args->pitch * args->height; + + return drm_gem_dma_dumb_create_internal(file_priv, drm, args); +} + static const struct drm_driver apple_drm_driver = { - DRM_GEM_DMA_DRIVER_OPS, + DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(apple_drm_gem_dumb_create), .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = "20210901", From bec50ad8d05fb631902f90682203d4b9f729e0e7 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 12 Oct 2022 23:12:07 +0200 Subject: [PATCH 0459/1020] gpu: drm: apple: Reject modes without valid color mode Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/parser.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index f0cd38c2a81048..bb7d57f272ddb9 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -339,6 +339,12 @@ static int parse_mode(struct dcp_parse_ctx *handle, return ret; } + /* + * Reject modes without valid color mode. + */ + if (best_color_mode < 0) + return -EINVAL; + /* * We need to skip virtual modes. In some cases, virtual modes are "too * big" for the monitor and can cause breakage. It is unclear why the From 31dc9470bdf99d0095c54c42015c8c6e7e6409c5 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 12 Oct 2022 23:25:49 +0200 Subject: [PATCH 0460/1020] gpu: drm: apple: Convert 2 non-assert WARN()s to dev_err() Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 56622c9e463173..3893c0f86ef296 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -1437,9 +1437,18 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) modeset = drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode; - if (WARN(dcp_channel_busy(&dcp->ch_cmd), "unexpected busy channel") || - WARN(!modeset && !dcp->connector->connected, - "can't flush if disconnected")) { + if (dcp_channel_busy(&dcp->ch_cmd)) + { + dev_err(dcp->dev, "unexpected busy command channel"); + /* HACK: issue a delayed vblank event to avoid timeouts in + * drm_atomic_helper_wait_for_vblanks(). + */ + schedule_work(&dcp->vblank_wq); + return; + } + if (!modeset && !dcp->connector->connected) + { + dev_err(dcp->dev, "dcp_flush while disconnected"); /* HACK: issue a delayed vblank event to avoid timeouts in * drm_atomic_helper_wait_for_vblanks(). */ From 4be8d3a37c47a1cda8cf4e47c5e4c52f59ede5ef Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 20 Oct 2022 20:42:09 +0200 Subject: [PATCH 0461/1020] gpu: drm: apple: Send an disconnected hotplug event on ASC crash Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 10edc9e6377d09..cfed9e53844c57 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -107,6 +107,10 @@ static void dcp_rtk_crashed(void *cookie) dcp->crashed = true; dev_err(dcp->dev, "DCP has crashed"); + if (dcp->connector) { + dcp->connector->connected = 0; + schedule_work(&dcp->connector->hotplug_wq); + } } static int dcp_rtk_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr) From b1cd62a56ab5c36259c994b614c67b9532bb7df0 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 20 Oct 2022 23:02:50 +0200 Subject: [PATCH 0462/1020] gpu: drm: apple: Add dcp_crtc_atomic_check Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 1 + drivers/gpu/drm/apple/dcp-internal.h | 2 ++ drivers/gpu/drm/apple/dcp.c | 38 ++++++++++++++++++++++++++++ drivers/gpu/drm/apple/dcp.h | 1 + drivers/gpu/drm/apple/iomfb.c | 9 ------- 5 files changed, 42 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 6eb67efb1febd5..8eb08bc4b53fc6 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -300,6 +300,7 @@ static const struct drm_connector_helper_funcs apple_connector_helper_funcs = { static const struct drm_crtc_helper_funcs apple_crtc_helper_funcs = { .atomic_begin = apple_crtc_atomic_begin, + .atomic_check = dcp_crtc_atomic_check, .atomic_flush = dcp_flush, .atomic_enable = apple_crtc_atomic_enable, .atomic_disable = apple_crtc_atomic_disable, diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index fd7c3aac603e35..c7a7b9563a156f 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -10,6 +10,8 @@ #include "iomfb.h" +#define DCP_MAX_PLANES 2 + struct apple_dcp; enum { diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index cfed9e53844c57..40f374a2ad680a 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -181,6 +181,44 @@ void dcp_send_message(struct apple_dcp *dcp, u8 endpoint, u64 message) false); } +int dcp_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) +{ + struct platform_device *pdev = to_apple_crtc(crtc)->dcp; + struct apple_dcp *dcp = platform_get_drvdata(pdev); + struct drm_plane_state *new_state; + struct drm_plane *plane; + struct drm_crtc_state *crtc_state; + int plane_idx, plane_count = 0; + bool needs_modeset; + + if (dcp->crashed) + return -EINVAL; + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + needs_modeset = drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode; + if (!needs_modeset && !dcp->connector->connected) { + dev_err(dcp->dev, "crtc_atomic_check: disconnected but no modeset"); + return -EINVAL; + } + + for_each_new_plane_in_state(state, plane, new_state, plane_idx) { + /* skip planes not for this crtc */ + if (new_state->crtc != crtc) + continue; + + plane_count += 1; + } + + if (plane_count > DCP_MAX_PLANES) { + dev_err(dcp->dev, "crtc_atomic_check: Blend supports only 2 layers!"); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(dcp_crtc_atomic_check); + void dcp_link(struct platform_device *pdev, struct apple_crtc *crtc, struct apple_connector *connector) { diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h index 047c1b3a160457..72ac1315372e5c 100644 --- a/drivers/gpu/drm/apple/dcp.h +++ b/drivers/gpu/drm/apple/dcp.h @@ -37,6 +37,7 @@ struct apple_connector { void dcp_poweroff(struct platform_device *pdev); void dcp_poweron(struct platform_device *pdev); +int dcp_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state); void dcp_link(struct platform_device *pdev, struct apple_crtc *apple, struct apple_connector *connector); void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state); diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 3893c0f86ef296..da6d34b6d7d146 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -1446,15 +1446,6 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) schedule_work(&dcp->vblank_wq); return; } - if (!modeset && !dcp->connector->connected) - { - dev_err(dcp->dev, "dcp_flush while disconnected"); - /* HACK: issue a delayed vblank event to avoid timeouts in - * drm_atomic_helper_wait_for_vblanks(). - */ - schedule_work(&dcp->vblank_wq); - return; - } /* Reset to defaults */ memset(req, 0, sizeof(*req)); From 0b82c202598c5cac1702e7b6eab9b7cc2ac5737d Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Fri, 21 Oct 2022 22:31:42 +0200 Subject: [PATCH 0463/1020] gpu: drm: apple: Fix DCP run time PM Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 40f374a2ad680a..62d3c9f6ec9812 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -381,7 +381,7 @@ MODULE_DEVICE_TABLE(of, of_match); */ static int dcp_suspend(struct device *dev) { - dcp_platform_shutdown(to_platform_device(dev)); + dcp_poweroff(to_platform_device(dev)); return 0; } From cf625720033cb77a2fd314e0106bb5586d648ae6 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Fri, 21 Oct 2022 22:34:23 +0200 Subject: [PATCH 0464/1020] gpu: drm: apple: Fix DCP initialisation Try to avoid races between DRM driver and DCP(ext) probing and initialisation: - do not start RTKit endpoints in dcp probe, iomfb excepts DRM objects to be already initialized on startup - ensure in apple_drv that all DCPs are probe via device links - initialize DRM planes, connectors, encoders - start DCP RTKit endpoints synchronously Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 26 ++++++++++++++++++-------- drivers/gpu/drm/apple/dcp.c | 24 +++++++++++++----------- drivers/gpu/drm/apple/dcp.h | 1 + 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 8eb08bc4b53fc6..526d976bb82dd4 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -369,14 +369,16 @@ static int apple_probe_per_dcp(struct device *dev, static int apple_platform_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct apple_drm_private *apple; struct platform_device *dcp[MAX_COPROCESSORS]; int ret, nr_dcp, i; for (nr_dcp = 0; nr_dcp < MAX_COPROCESSORS; ++nr_dcp) { struct device_node *np; + struct device_link *dcp_link; - np = of_parse_phandle(pdev->dev.of_node, "apple,coprocessors", + np = of_parse_phandle(dev->of_node, "apple,coprocessors", nr_dcp); if (!np) @@ -387,11 +389,14 @@ static int apple_platform_probe(struct platform_device *pdev) if (!dcp[nr_dcp]) return -ENODEV; - /* DCP needs to be initialized before KMS can come online */ - if (!platform_get_drvdata(dcp[nr_dcp])) - return -EPROBE_DEFER; + dcp_link = device_link_add(dev, &dcp[nr_dcp]->dev, + DL_FLAG_AUTOREMOVE_CONSUMER); + if (!dcp_link) { + dev_err(dev, "Failed to link to DCP %d device", nr_dcp); + return -EINVAL; + } - if (!dcp_is_initialized(dcp[nr_dcp])) + if (dcp_link->supplier->links.status != DL_DEV_DRIVER_BOUND) return -EPROBE_DEFER; } @@ -399,11 +404,11 @@ static int apple_platform_probe(struct platform_device *pdev) if (nr_dcp < 1) return -ENODEV; - ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36)); if (ret) return ret; - apple = devm_drm_dev_alloc(&pdev->dev, &apple_drm_driver, + apple = devm_drm_dev_alloc(dev, &apple_drm_driver, struct apple_drm_private, drm); if (IS_ERR(apple)) return PTR_ERR(apple); @@ -435,7 +440,12 @@ static int apple_platform_probe(struct platform_device *pdev) apple->drm.mode_config.helper_private = &apple_mode_config_helpers; for (i = 0; i < nr_dcp; ++i) { - ret = apple_probe_per_dcp(&pdev->dev, &apple->drm, dcp[i], i); + ret = apple_probe_per_dcp(dev, &apple->drm, dcp[i], i); + + if (ret) + goto err_unload; + + ret = dcp_start(dcp[i]); if (ret) goto err_unload; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 62d3c9f6ec9812..83782b8fda74ab 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -226,14 +226,22 @@ void dcp_link(struct platform_device *pdev, struct apple_crtc *crtc, dcp->crtc = crtc; dcp->connector = connector; +} +EXPORT_SYMBOL_GPL(dcp_link); + +int dcp_start(struct platform_device *pdev) +{ + struct apple_dcp *dcp = platform_get_drvdata(pdev); + int ret; - /* init connector status by modes offered by dcp */ - connector->connected = dcp->nr_modes > 0; + /* start RTKit endpoints */ + ret = iomfb_start_rtkit(dcp); + if (ret) + dev_err(dcp->dev, "Failed to start IOMFB endpoint: %d", ret); - /* Dimensions might already be parsed */ - dcp_set_dimensions(dcp); + return ret; } -EXPORT_SYMBOL_GPL(dcp_link); +EXPORT_SYMBOL(dcp_start); static struct platform_device *dcp_get_dev(struct device *dev, const char *name) { @@ -347,12 +355,6 @@ static int dcp_platform_probe(struct platform_device *pdev) return dev_err_probe(dev, ret, "Failed to boot RTKit: %d", ret); - /* start RTKit endpoints */ - ret = iomfb_start_rtkit(dcp); - if (ret) - return dev_err_probe(dev, ret, - "Failed to start IOMFB endpoint: %d", ret); - return ret; } diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h index 72ac1315372e5c..60e9bcfa4714e0 100644 --- a/drivers/gpu/drm/apple/dcp.h +++ b/drivers/gpu/drm/apple/dcp.h @@ -40,6 +40,7 @@ void dcp_poweron(struct platform_device *pdev); int dcp_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state); void dcp_link(struct platform_device *pdev, struct apple_crtc *apple, struct apple_connector *connector); +int dcp_start(struct platform_device *pdev); void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state); bool dcp_is_initialized(struct platform_device *pdev); void apple_crtc_vblank(struct apple_crtc *apple); From fb48efd60989452f2c6cb6e04b7ebc993a10dca2 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Fri, 21 Oct 2022 23:43:57 +0200 Subject: [PATCH 0465/1020] gpu: drm: apple: Specify correct number of DCP*s for drm_vblank_init Unbreaks dcpext a little further. fixup! WIP: drm/apple: Add DCP display driver Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 526d976bb82dd4..360675cb8ca438 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -413,7 +413,7 @@ static int apple_platform_probe(struct platform_device *pdev) if (IS_ERR(apple)) return PTR_ERR(apple); - ret = drm_vblank_init(&apple->drm, 1); + ret = drm_vblank_init(&apple->drm, nr_dcp); if (ret) return ret; From 98d696fbb48e7a2ed5f828a74bfbd5166bdd83aa Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 25 Oct 2022 08:17:10 +0200 Subject: [PATCH 0466/1020] gpu: drm: apple: Remove other framebuffers before DRM setup Allows taking over the device node from simpledrm and appaers to be the common pattern in DRM drivers replacing boot framebuffers. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 360675cb8ca438..7e05f48d2857e0 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -453,6 +453,7 @@ static int apple_platform_probe(struct platform_device *pdev) drm_mode_config_reset(&apple->drm); + // remove before registering our DRM device ret = drm_aperture_remove_framebuffers(false, &apple_drm_driver); if (ret) return ret; From 9ab90f612e65850b41337ad7893a132ac6207b7f Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 27 Oct 2022 02:09:45 +0200 Subject: [PATCH 0467/1020] gpu: drm: apple: Support opaque pixel formats Opaque pixel formats such as XRGB8888 or YUV formats require flag in struct dcp_surface to be set to be displayed properly. Fixes display issues with fbcon after the handover from simpledrm on j314c with 16:10 modes (notch hiding). Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 5 +++++ drivers/gpu/drm/apple/iomfb.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index da6d34b6d7d146..2358f17ab509bf 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -1456,6 +1456,7 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) for_each_oldnew_plane_in_state(state, plane, old_state, new_state, plane_idx) { struct drm_framebuffer *fb = new_state->fb; struct drm_rect src_rect; + bool opaque = false; /* skip planes not for this crtc */ if (old_state->crtc != crtc && new_state->crtc != crtc) @@ -1495,6 +1496,9 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) req->surf_null[l] = false; has_surface = 1; + if (!fb->format->has_alpha || + new_state->plane->type == DRM_PLANE_TYPE_PRIMARY) + opaque = true; drm_rect_fp_to_int(&src_rect, &new_state->src); req->swap.src_rect[l] = drm_to_dcp_rect(&src_rect); @@ -1503,6 +1507,7 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) req->surf_iova[l] = drm_fb_dma_get_gem_addr(fb, new_state, 0); req->surf[l] = (struct dcp_surface){ + .opaque = opaque, .format = drm_format_to_dcp(fb->format->format), .xfer_func = 13, .colorspace = drm_format_to_colorspace(fb->format->format), diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index 5b1f4ba789bccf..f9ead84c21f255 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -142,7 +142,7 @@ struct dcp_component_types { struct dcp_surface { u8 is_tiled; u8 unk_1; - u8 unk_2; + u8 opaque; /** ignore alpha, also required YUV overlays */ u32 plane_cnt; u32 plane_cnt2; u32 format; /* DCP fourcc */ From 06aabd6765116b1a87fc0c56f964d4bb761d3288 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 25 Oct 2022 08:24:01 +0200 Subject: [PATCH 0468/1020] gpu: drm: apple: Provide notch-less modes If the device tree caries a "apple,notch-height" property subtract it from all modes and render all framebuffers offsetted by it. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp-internal.h | 4 ++++ drivers/gpu/drm/apple/dcp.c | 7 +++++++ drivers/gpu/drm/apple/iomfb.c | 6 +++++- drivers/gpu/drm/apple/parser.c | 9 ++++++--- drivers/gpu/drm/apple/parser.h | 2 +- 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index c7a7b9563a156f..6624672109c33e 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -67,6 +67,8 @@ struct dcp_fb_reference { struct drm_framebuffer *fb; }; +#define MAX_NOTCH_HEIGHT 160 + /* TODO: move IOMFB members to its own struct */ struct apple_dcp { struct device *dev; @@ -134,6 +136,8 @@ struct apple_dcp { /* Attributes of the connected display */ int width_mm, height_mm; + unsigned notch_height; + /* Workqueue for sending vblank events when a dcp swap is not possible */ struct work_struct vblank_wq; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 83782b8fda74ab..fb7c0df2a85573 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -331,6 +331,13 @@ static int dcp_platform_probe(struct platform_device *pdev) dev_warn(dev, "failed read 'apple,asc-dram-mask': %d\n", ret); dev_dbg(dev, "'apple,asc-dram-mask': 0x%011llx\n", dcp->asc_dram_mask); + ret = of_property_read_u32(dev->of_node, "apple,notch-height", + &dcp->notch_height); + if (dcp->notch_height > MAX_NOTCH_HEIGHT) + dcp->notch_height = MAX_NOTCH_HEIGHT; + if (dcp->notch_height > 0) + dev_info(dev, "Detected display with notch of %u pixel\n", dcp->notch_height); + bitmap_zero(dcp->memdesc_map, DCP_MAX_MAPPINGS); // TDOD: mem_desc IDs start at 1, for simplicity just skip '0' entry set_bit(0, dcp->memdesc_map); diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 2358f17ab509bf..404f7638cfb2c0 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -647,7 +647,8 @@ static bool dcpep_process_chunks(struct apple_dcp *dcp, if (!strcmp(req->key, "TimingElements")) { dcp->modes = enumerate_modes(&ctx, &dcp->nr_modes, - dcp->width_mm, dcp->height_mm); + dcp->width_mm, dcp->height_mm, + dcp->notch_height); if (IS_ERR(dcp->modes)) { dev_warn(dcp->dev, "failed to parse modes\n"); @@ -1504,6 +1505,9 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) req->swap.src_rect[l] = drm_to_dcp_rect(&src_rect); req->swap.dst_rect[l] = drm_to_dcp_rect(&new_state->dst); + if (dcp->notch_height > 0) + req->swap.dst_rect[l].y += dcp->notch_height; + req->surf_iova[l] = drm_fb_dma_get_gem_addr(fb, new_state, 0); req->surf[l] = (struct dcp_surface){ diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index bb7d57f272ddb9..fc52c26490ec80 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -305,7 +305,7 @@ static u32 calculate_clock(struct dimension *horiz, struct dimension *vert) static int parse_mode(struct dcp_parse_ctx *handle, struct dcp_display_mode *out, s64 *score, int width_mm, - int height_mm) + int height_mm, unsigned notch_height) { int ret = 0; struct iterator it; @@ -353,6 +353,9 @@ static int parse_mode(struct dcp_parse_ctx *handle, if (is_virtual) return -EINVAL; + vert.active -= notch_height; + vert.sync_width += notch_height; + /* From here we must succeed. Start filling out the mode. */ *mode = (struct drm_display_mode) { .type = DRM_MODE_TYPE_DRIVER, @@ -383,7 +386,7 @@ static int parse_mode(struct dcp_parse_ctx *handle, struct dcp_display_mode *enumerate_modes(struct dcp_parse_ctx *handle, unsigned int *count, int width_mm, - int height_mm) + int height_mm, unsigned notch_height) { struct iterator it; int ret; @@ -405,7 +408,7 @@ struct dcp_display_mode *enumerate_modes(struct dcp_parse_ctx *handle, for (; it.idx < it.len; ++it.idx) { mode = &modes[*count]; - ret = parse_mode(it.handle, mode, &score, width_mm, height_mm); + ret = parse_mode(it.handle, mode, &score, width_mm, height_mm, notch_height); /* Errors for a single mode are recoverable -- just skip it. */ if (ret) diff --git a/drivers/gpu/drm/apple/parser.h b/drivers/gpu/drm/apple/parser.h index 66a675079dc164..a2d479258ed0eb 100644 --- a/drivers/gpu/drm/apple/parser.h +++ b/drivers/gpu/drm/apple/parser.h @@ -25,7 +25,7 @@ struct dcp_display_mode { int parse(void *blob, size_t size, struct dcp_parse_ctx *ctx); struct dcp_display_mode *enumerate_modes(struct dcp_parse_ctx *handle, unsigned int *count, int width_mm, - int height_mm); + int height_mm, unsigned notch_height); int parse_display_attributes(struct dcp_parse_ctx *handle, int *width_mm, int *height_mm); From f9c410e1871f82fd27225fdbb59d8d2e2aa0db72 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 6 Nov 2022 10:39:05 +0100 Subject: [PATCH 0469/1020] gpu: drm: apple: Fix shutdown of partially probed dcp No need to shut the co-processor down if it wasn't booted to begin with. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index fb7c0df2a85573..f4161faad6baf0 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -373,7 +373,8 @@ static void dcp_platform_shutdown(struct platform_device *pdev) { struct apple_dcp *dcp = platform_get_drvdata(pdev); - iomfb_shutdown(dcp); + if (dcp->shmem) + iomfb_shutdown(dcp); } static const struct of_device_id of_match[] = { From 52d8c7224988fdaafea9a83253367ab63fd3c143 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 6 Nov 2022 19:23:08 +0100 Subject: [PATCH 0470/1020] gpu: drm: apple: Set maximal framebuffer size correctly DCP reports this in the IOMFBMaxSrcPixels dictionary. Use that instead of the hardcoded values from M1 Max. Fixes multiscreen X11 setups. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 7e05f48d2857e0..bc84fd04697ef6 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -428,13 +428,15 @@ static int apple_platform_probe(struct platform_device *pdev) apple->drm.mode_config.min_width = 32; apple->drm.mode_config.min_height = 32; - /* Unknown maximum, use the iMac (24-inch, 2021) display resolution as - * maximum. - * TODO: this is the max framebuffer size not the maximal supported output - * resolution. DCP reports the maximal framebuffer size take it from there. + /* + * TODO: this is the max framebuffer size not the maximal supported + * output resolution. DCP reports the maximal framebuffer size take it + * from there. + * Hardcode it for now to the M1 Max DCP reported 'MaxSrcBufferWidth' + * and 'MaxSrcBufferHeight' of 16384. */ - apple->drm.mode_config.max_width = 4480; - apple->drm.mode_config.max_height = 2520; + apple->drm.mode_config.max_width = 16384; + apple->drm.mode_config.max_height = 16384; apple->drm.mode_config.funcs = &apple_mode_config_funcs; apple->drm.mode_config.helper_private = &apple_mode_config_helpers; From 77e140b8897b2d9108a4d938d6effae2cd7fbf3d Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 6 Nov 2022 20:35:41 +0100 Subject: [PATCH 0471/1020] gpu: drm: apple: Prevent NULL pointer in dcp_hotplug A bit hackish, probably missing something in the setup or simply calling this too early. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 404f7638cfb2c0..ee00db453da00b 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -985,7 +985,7 @@ void dcp_hotplug(struct work_struct *work) * display modes from atomic_flush, so userspace needs to trigger a * flush, or the CRTC gets no signal. */ - if (!dcp->valid_mode && connector->connected) { + if (connector->base.state && !dcp->valid_mode && connector->connected) { drm_connector_set_link_status_property( &connector->base, DRM_MODE_LINK_STATUS_BAD); } From 99abe7b28a8f943ea634b31475cb92417821f2b7 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 6 Nov 2022 23:02:54 +0100 Subject: [PATCH 0472/1020] gpu: drm: apple: Update date last update Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index bc84fd04697ef6..b01f2207faeba6 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -59,7 +59,7 @@ static const struct drm_driver apple_drm_driver = { DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(apple_drm_gem_dumb_create), .name = DRIVER_NAME, .desc = DRIVER_DESC, - .date = "20210901", + .date = "20221106", .major = 1, .minor = 0, .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, From 534ac871f45f6ec34fcdf3d29c92d235a3768b95 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 30 Oct 2022 13:11:06 +0100 Subject: [PATCH 0473/1020] gpu: drm: apple: iomfb: Use FIELD_{GET,PREP} Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 30 +++++++++++++++--------------- drivers/gpu/drm/apple/iomfb.h | 26 ++++++++++++-------------- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index ee00db453da00b..e9d500eded40b8 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -76,22 +76,22 @@ static int dcp_channel_offset(enum dcp_context_id id) static inline u64 dcpep_set_shmem(u64 dart_va) { - return (DCPEP_TYPE_SET_SHMEM << DCPEP_TYPE_SHIFT) | - (DCPEP_FLAG_VALUE << DCPEP_FLAG_SHIFT) | - (dart_va << DCPEP_DVA_SHIFT); + return FIELD_PREP(IOMFB_MESSAGE_TYPE, IOMFB_MESSAGE_TYPE_SET_SHMEM) | + FIELD_PREP(IOMFB_SHMEM_FLAG, IOMFB_SHMEM_FLAG_VALUE) | + FIELD_PREP(IOMFB_SHMEM_DVA, dart_va); } static inline u64 dcpep_msg(enum dcp_context_id id, u32 length, u16 offset) { - return (DCPEP_TYPE_MESSAGE << DCPEP_TYPE_SHIFT) | - ((u64)id << DCPEP_CONTEXT_SHIFT) | - ((u64)offset << DCPEP_OFFSET_SHIFT) | - ((u64)length << DCPEP_LENGTH_SHIFT); + return FIELD_PREP(IOMFB_MESSAGE_TYPE, IOMFB_MESSAGE_TYPE_MSG) | + FIELD_PREP(IOMFB_MSG_CONTEXT, id) | + FIELD_PREP(IOMFB_MSG_OFFSET, offset) | + FIELD_PREP(IOMFB_MSG_LENGTH, length); } static inline u64 dcpep_ack(enum dcp_context_id id) { - return dcpep_msg(id, 0, 0) | DCPEP_ACK; + return dcpep_msg(id, 0, 0) | IOMFB_MSG_ACK; } /* @@ -1238,9 +1238,9 @@ static void dcpep_got_msg(struct apple_dcp *dcp, u64 message) int channel_offset; void *data; - ctx_id = (message & DCPEP_CONTEXT_MASK) >> DCPEP_CONTEXT_SHIFT; - offset = (message & DCPEP_OFFSET_MASK) >> DCPEP_OFFSET_SHIFT; - length = (message >> DCPEP_LENGTH_SHIFT); + ctx_id = FIELD_GET(IOMFB_MSG_CONTEXT, message); + offset = FIELD_GET(IOMFB_MSG_OFFSET, message); + length = FIELD_GET(IOMFB_MSG_LENGTH, message); channel_offset = dcp_channel_offset(ctx_id); @@ -1251,7 +1251,7 @@ static void dcpep_got_msg(struct apple_dcp *dcp, u64 message) data = dcp->shmem + channel_offset + offset; - if (message & DCPEP_ACK) + if (FIELD_GET(IOMFB_MSG_ACK, message)) dcpep_handle_ack(dcp, ctx_id, data, length); else dcpep_handle_cb(dcp, ctx_id, data, length); @@ -1651,11 +1651,11 @@ static void dcp_started(struct apple_dcp *dcp, void *data, void *cookie) void iomfb_recv_msg(struct apple_dcp *dcp, u64 message) { - enum dcpep_type type = (message >> DCPEP_TYPE_SHIFT) & DCPEP_TYPE_MASK; + enum dcpep_type type = FIELD_GET(IOMFB_MESSAGE_TYPE, message); - if (type == DCPEP_TYPE_INITIALIZED) + if (type == IOMFB_MESSAGE_TYPE_INITIALIZED) dcp_start_signal(dcp, false, dcp_started, NULL); - else if (type == DCPEP_TYPE_MESSAGE) + else if (type == IOMFB_MESSAGE_TYPE_MSG) dcpep_got_msg(dcp, message); else dev_warn(dcp->dev, "Ignoring unknown message %llx\n", message); diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index f9ead84c21f255..a82d960512bfd1 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -32,29 +32,27 @@ enum dcp_context_id { /* RTKit endpoint message types */ enum dcpep_type { /* Set shared memory */ - DCPEP_TYPE_SET_SHMEM = 0, + IOMFB_MESSAGE_TYPE_SET_SHMEM = 0, /* DCP is initialized */ - DCPEP_TYPE_INITIALIZED = 1, + IOMFB_MESSAGE_TYPE_INITIALIZED = 1, /* Remote procedure call */ - DCPEP_TYPE_MESSAGE = 2, + IOMFB_MESSAGE_TYPE_MSG = 2, }; +#define IOMFB_MESSAGE_TYPE GENMASK_ULL( 3, 0) + /* Message */ -#define DCPEP_TYPE_SHIFT (0) -#define DCPEP_TYPE_MASK GENMASK(1, 0) -#define DCPEP_ACK BIT_ULL(6) -#define DCPEP_CONTEXT_SHIFT (8) -#define DCPEP_CONTEXT_MASK GENMASK(11, 8) -#define DCPEP_OFFSET_SHIFT (16) -#define DCPEP_OFFSET_MASK GENMASK(31, 16) -#define DCPEP_LENGTH_SHIFT (32) +#define IOMFB_MSG_LENGTH GENMASK_ULL(63, 32) +#define IOMFB_MSG_OFFSET GENMASK_ULL(31, 16) +#define IOMFB_MSG_CONTEXT GENMASK_ULL(11, 8) +#define IOMFB_MSG_ACK BIT_ULL(6) /* Set shmem */ -#define DCPEP_DVA_SHIFT (16) -#define DCPEP_FLAG_SHIFT (4) -#define DCPEP_FLAG_VALUE (4) +#define IOMFB_SHMEM_DVA GENMASK_ULL(63, 16) +#define IOMFB_SHMEM_FLAG GENMASK_ULL( 7, 4) +#define IOMFB_SHMEM_FLAG_VALUE 4 struct dcp_packet_header { char tag[4]; From 9516f2416335bc2674769282d6309bc599acda70 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 30 Oct 2022 13:17:28 +0100 Subject: [PATCH 0474/1020] gpu: drm: apple: iomfb: Unify call and callback channels AP calls initiated inside callbacks are using the callback channel and context. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp-internal.h | 11 ++----- drivers/gpu/drm/apple/iomfb.c | 45 +++++++++++----------------- 2 files changed, 20 insertions(+), 36 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 6624672109c33e..8c12382e281b42 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -47,7 +47,7 @@ struct dcp_mem_descriptor { typedef void (*dcp_callback_t)(struct apple_dcp *, void *, void *); -struct dcp_call_channel { +struct dcp_channel { dcp_callback_t callbacks[DCP_MAX_CALL_DEPTH]; void *cookies[DCP_MAX_CALL_DEPTH]; void *output[DCP_MAX_CALL_DEPTH]; @@ -57,11 +57,6 @@ struct dcp_call_channel { u8 depth; }; -struct dcp_cb_channel { - u8 depth; - void *output[DCP_MAX_CALL_DEPTH]; -}; - struct dcp_fb_reference { struct list_head head; struct drm_framebuffer *fb; @@ -108,8 +103,8 @@ struct apple_dcp { /* Indexed table of memory descriptors */ struct dcp_mem_descriptor memdesc[DCP_MAX_MAPPINGS]; - struct dcp_call_channel ch_cmd, ch_oobcmd; - struct dcp_cb_channel ch_cb, ch_oobcb, ch_async; + struct dcp_channel ch_cmd, ch_oobcmd; + struct dcp_channel ch_cb, ch_oobcb, ch_async; /* Active chunked transfer. There can only be one at a time. */ struct dcp_chunks chunks; diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index e9d500eded40b8..5dc5a6772e2482 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -98,27 +98,11 @@ static inline u64 dcpep_ack(enum dcp_context_id id) * A channel is busy if we have sent a message that has yet to be * acked. The driver must not sent a message to a busy channel. */ -static bool dcp_channel_busy(struct dcp_call_channel *ch) +static bool dcp_channel_busy(struct dcp_channel *ch) { return (ch->depth != 0); } -/* Get a call channel for a context */ -static struct dcp_call_channel * -dcp_get_call_channel(struct apple_dcp *dcp, enum dcp_context_id context) -{ - switch (context) { - case DCP_CONTEXT_CMD: - case DCP_CONTEXT_CB: - return &dcp->ch_cmd; - case DCP_CONTEXT_OOBCMD: - case DCP_CONTEXT_OOBCB: - return &dcp->ch_oobcmd; - default: - return NULL; - } -} - /* * Get the context ID passed to the DCP for a command we push. The rule is * simple: callback contexts are used when replying to the DCP, command @@ -135,15 +119,19 @@ static enum dcp_context_id dcp_call_context(struct apple_dcp *dcp, bool oob) return oob ? DCP_CONTEXT_OOBCMD : DCP_CONTEXT_CMD; } -/* Get a callback channel for a context */ -static struct dcp_cb_channel *dcp_get_cb_channel(struct apple_dcp *dcp, - enum dcp_context_id context) +/* Get a channel for a context */ +static struct dcp_channel *dcp_get_channel(struct apple_dcp *dcp, + enum dcp_context_id context) { switch (context) { case DCP_CONTEXT_CB: return &dcp->ch_cb; + case DCP_CONTEXT_CMD: + return &dcp->ch_cmd; case DCP_CONTEXT_OOBCB: return &dcp->ch_oobcb; + case DCP_CONTEXT_OOBCMD: + return &dcp->ch_oobcmd; case DCP_CONTEXT_ASYNC: return &dcp->ch_async; default: @@ -152,7 +140,7 @@ static struct dcp_cb_channel *dcp_get_cb_channel(struct apple_dcp *dcp, } /* Get the start of a packet: after the end of the previous packet */ -static u16 dcp_packet_start(struct dcp_call_channel *ch, u8 depth) +static u16 dcp_packet_start(struct dcp_channel *ch, u8 depth) { if (depth > 0) return ch->end[depth - 1]; @@ -203,8 +191,8 @@ static void dcp_push(struct apple_dcp *dcp, bool oob, enum dcpep_method method, u32 in_len, u32 out_len, void *data, dcp_callback_t cb, void *cookie) { - struct dcp_call_channel *ch = oob ? &dcp->ch_oobcmd : &dcp->ch_cmd; enum dcp_context_id context = dcp_call_context(dcp, oob); + struct dcp_channel *ch = dcp_get_channel(dcp, context); struct dcp_packet_header header = { .in_len = in_len, @@ -329,7 +317,7 @@ static int dcp_parse_tag(char tag[4]) /* Ack a callback from the DCP */ static void dcp_ack(struct apple_dcp *dcp, enum dcp_context_id context) { - struct dcp_cb_channel *ch = dcp_get_cb_channel(dcp, context); + struct dcp_channel *ch = dcp_get_channel(dcp, context); dcp_pop_depth(&ch->depth); dcp_send_message(dcp, IOMFB_ENDPOINT, @@ -686,7 +674,7 @@ static u8 dcpep_cb_prop_end(struct apple_dcp *dcp, /* Boot sequence */ static void boot_done(struct apple_dcp *dcp, void *out, void *cookie) { - struct dcp_cb_channel *ch = &dcp->ch_cb; + struct dcp_channel *ch = &dcp->ch_cb; u8 *succ = ch->output[ch->depth - 1]; dev_dbg(dcp->dev, "boot done"); @@ -1175,13 +1163,13 @@ bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, }; static void dcpep_handle_cb(struct apple_dcp *dcp, enum dcp_context_id context, - void *data, u32 length) + void *data, u32 length, u16 offset) { struct device *dev = dcp->dev; struct dcp_packet_header *hdr = data; void *in, *out; int tag = dcp_parse_tag(hdr->tag); - struct dcp_cb_channel *ch = dcp_get_cb_channel(dcp, context); + struct dcp_channel *ch = dcp_get_channel(dcp, context); u8 depth; if (tag < 0 || tag >= DCPEP_MAX_CB || !dcpep_cb_handlers[tag]) { @@ -1200,6 +1188,7 @@ static void dcpep_handle_cb(struct apple_dcp *dcp, enum dcp_context_id context, depth = dcp_push_depth(&ch->depth); ch->output[depth] = out; + ch->end[depth] = offset + ALIGN(length, DCP_PACKET_ALIGNMENT); if (dcpep_cb_handlers[tag](dcp, tag, out, in)) dcp_ack(dcp, context); @@ -1209,7 +1198,7 @@ static void dcpep_handle_ack(struct apple_dcp *dcp, enum dcp_context_id context, void *data, u32 length) { struct dcp_packet_header *header = data; - struct dcp_call_channel *ch = dcp_get_call_channel(dcp, context); + struct dcp_channel *ch = dcp_get_channel(dcp, context); void *cookie; dcp_callback_t cb; @@ -1254,7 +1243,7 @@ static void dcpep_got_msg(struct apple_dcp *dcp, u64 message) if (FIELD_GET(IOMFB_MSG_ACK, message)) dcpep_handle_ack(dcp, ctx_id, data, length); else - dcpep_handle_cb(dcp, ctx_id, data, length); + dcpep_handle_cb(dcp, ctx_id, data, length, offset); } /* From 4faef732132e16a610eb5824e87065d26d837d66 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 30 Oct 2022 13:29:23 +0100 Subject: [PATCH 0475/1020] gpu: drm: apple: "match" PMU/backlight services on init Verify that this still works on HDMI/USB-C. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 103 +++++++++++++++++++++++++++++++--- drivers/gpu/drm/apple/iomfb.h | 9 +++ 2 files changed, 105 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 5dc5a6772e2482..58d1a91d054c62 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -170,7 +170,10 @@ const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { DCP_METHOD("A000", dcpep_late_init_signal), DCP_METHOD("A029", dcpep_setup_video_limits), DCP_METHOD("A034", dcpep_update_notify_clients_dcp), + DCP_METHOD("A131", iomfbep_a131_pmu_service_matched), + DCP_METHOD("A132", iomfbep_a132_backlight_service_matched), DCP_METHOD("A357", dcpep_set_create_dfb), + DCP_METHOD("A358", iomfbep_a358_vi_set_temperature_hint), DCP_METHOD("A401", dcpep_start_signal), DCP_METHOD("A407", dcpep_swap_start), DCP_METHOD("A408", dcpep_swap_submit), @@ -257,6 +260,10 @@ static void dcp_push(struct apple_dcp *dcp, bool oob, enum dcpep_method method, cb, cookie); \ } +DCP_THUNK_OUT(iomfb_a131_pmu_service_matched, iomfbep_a131_pmu_service_matched, u32); +DCP_THUNK_OUT(iomfb_a132_backlight_service_matched, iomfbep_a132_backlight_service_matched, u32); +DCP_THUNK_OUT(iomfb_a358_vi_set_temperature_hint, iomfbep_a358_vi_set_temperature_hint, u32); + DCP_THUNK_INOUT(dcp_swap_submit, dcpep_swap_submit, struct dcp_swap_submit_req, struct dcp_swap_submit_resp); @@ -354,11 +361,91 @@ static void dcpep_cb_swap_complete(struct apple_dcp *dcp, dcp_drm_crtc_vblank(dcp->crtc); } +/* special */ +static void complete_vi_set_temperature_hint(struct apple_dcp *dcp, void *out, void *cookie) +{ + // ack D100 cb_match_pmu_service + dcp_ack(dcp, DCP_CONTEXT_CB); +} + +static bool iomfbep_cb_match_pmu_service(struct apple_dcp *dcp, int tag, void *out, void *in) +{ + trace_iomfb_callback(dcp, tag, __func__); + iomfb_a358_vi_set_temperature_hint(dcp, false, + complete_vi_set_temperature_hint, + NULL); + + // return false for deferred ACK + return false; +} + +static void complete_pmu_service_matched(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct dcp_channel *ch = &dcp->ch_cb; + u8 *succ = ch->output[ch->depth - 1]; + + *succ = true; + + // ack D206 cb_match_pmu_service_2 + dcp_ack(dcp, DCP_CONTEXT_CB); +} + +static bool iomfbep_cb_match_pmu_service_2(struct apple_dcp *dcp, int tag, void *out, void *in) +{ + trace_iomfb_callback(dcp, tag, __func__); + + iomfb_a131_pmu_service_matched(dcp, false, complete_pmu_service_matched, + out); + + // return false for deferred ACK + return false; +} + +static void complete_backlight_service_matched(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct dcp_channel *ch = &dcp->ch_cb; + u8 *succ = ch->output[ch->depth - 1]; + + *succ = true; + + // ack D206 cb_match_backlight_service + dcp_ack(dcp, DCP_CONTEXT_CB); +} + +static bool iomfbep_cb_match_backlight_service(struct apple_dcp *dcp, int tag, void *out, void *in) +{ + trace_iomfb_callback(dcp, tag, __func__); + + iomfb_a132_backlight_service_matched(dcp, false, complete_backlight_service_matched, out); + + // return false for deferred ACK + return false; +} + static struct dcp_get_uint_prop_resp dcpep_cb_get_uint_prop(struct apple_dcp *dcp, struct dcp_get_uint_prop_req *req) { - /* unimplemented for now */ - return (struct dcp_get_uint_prop_resp){ .value = 0 }; + struct dcp_get_uint_prop_resp resp = (struct dcp_get_uint_prop_resp){ + .value = 0 + }; + + if (memcmp(req->obj, "SUMP", sizeof(req->obj)) == 0) { /* "PMUS */ + if (strncmp(req->key, "Temperature", sizeof(req->key)) == 0) { + /* + * TODO: value from j314c, find out if it is temperature in + * centigrade C and which temperature sensor reports it + */ + resp.value = 3029; + resp.ret = true; + } + } + + return resp; +} + +static void iomfbep_cb_set_fx_prop(struct apple_dcp *dcp, struct iomfb_set_fx_prop_req *req) +{ + // TODO: trace this, see if there properties which needs to used later } /* @@ -1078,6 +1165,8 @@ TRAMPOLINE_IN(trampoline_swap_complete, dcpep_cb_swap_complete, struct dc_swap_complete_resp); TRAMPOLINE_INOUT(trampoline_get_uint_prop, dcpep_cb_get_uint_prop, struct dcp_get_uint_prop_req, struct dcp_get_uint_prop_resp); +TRAMPOLINE_IN(trampoline_set_fx_prop, iomfbep_cb_set_fx_prop, + struct iomfb_set_fx_prop_req) TRAMPOLINE_INOUT(trampoline_map_piodma, dcpep_cb_map_piodma, struct dcp_map_buf_req, struct dcp_map_buf_resp); TRAMPOLINE_IN(trampoline_unmap_piodma, dcpep_cb_unmap_piodma, @@ -1113,7 +1202,7 @@ bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, [1] = trampoline_true, /* did_power_on_signal */ [2] = trampoline_nop, /* will_power_off_signal */ [3] = trampoline_rt_bandwidth, - [100] = trampoline_nop, /* match_pmu_service */ + [100] = iomfbep_cb_match_pmu_service, [101] = trampoline_zero, /* get_display_default_stride */ [103] = trampoline_nop, /* set_boolean_property */ [106] = trampoline_nop, /* remove_property */ @@ -1121,7 +1210,7 @@ bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, [108] = trampoline_true, /* create_product_service */ [109] = trampoline_true, /* create_pmu_service */ [110] = trampoline_true, /* create_iomfb_service */ - [111] = trampoline_false, /* create_backlight_service */ + [111] = trampoline_true, /* create_backlight_service */ [116] = dcpep_cb_boot_1, [117] = trampoline_false, /* is_dark_boot */ [118] = trampoline_false, /* is_dark_boot / is_waking_from_hibernate*/ @@ -1131,14 +1220,14 @@ bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, [124] = trampoline_prop_end, [201] = trampoline_map_piodma, [202] = trampoline_unmap_piodma, - [206] = trampoline_true, /* match_pmu_service_2 */ - [207] = trampoline_true, /* match_backlight_service */ + [206] = iomfbep_cb_match_pmu_service_2, + [207] = iomfbep_cb_match_backlight_service, [208] = trampoline_get_time, [211] = trampoline_nop, /* update_backlight_factor_prop */ [300] = trampoline_nop, /* pr_publish */ [401] = trampoline_get_uint_prop, [404] = trampoline_nop, /* sr_set_uint_prop */ - [406] = trampoline_nop, /* set_fx_prop */ + [406] = trampoline_set_fx_prop, [408] = trampoline_get_frequency, [411] = trampoline_map_reg, [413] = trampoline_true, /* sr_set_property_dict */ diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index a82d960512bfd1..68fdc654d597f5 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -197,6 +197,9 @@ enum dcpep_method { dcpep_set_parameter_dcp, dcpep_enable_disable_video_power_savings, dcpep_is_main_display, + iomfbep_a131_pmu_service_matched, + iomfbep_a132_backlight_service_matched, + iomfbep_a358_vi_set_temperature_hint, dcpep_num_methods }; @@ -337,6 +340,12 @@ struct dcp_get_uint_prop_resp { u8 padding[3]; } __packed; +struct iomfb_set_fx_prop_req { + char obj[4]; + char key[0x40]; + u32 value; +} __packed; + struct dcp_set_power_state_req { u64 unklong; u8 unkbool; From 53597e4c2dce9e385b0e481a9c1dfad00938cb89 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 31 Oct 2022 01:11:36 +0100 Subject: [PATCH 0476/1020] gpu: drm: apple: Brightness control via atomic commits This abuses color_mgnt_change in drm_crtc_state and will be changed once phase 2 of the "drm/kms: control display brightness through drm_connector properties" RfC (linked below) is implemented. The lookup of DAC values from brightness (nits) is not fully understood. Since IOMFB reports te brightness back the easiest solution would be to create our own lookup table or find a approximation which works. DCP appears to report the brightness in nits by "PropRelay::pr_publish(prop_id=15, value=...)" (scaled by "Brightness_scale"). Link: https://lore.kernel.org/dri-devel/b61d3eeb-6213-afac-2e70-7b9791c86d2e@redhat.com/ Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/Makefile | 2 +- drivers/gpu/drm/apple/apple_drv.c | 12 +- drivers/gpu/drm/apple/dcp-internal.h | 15 ++ drivers/gpu/drm/apple/dcp.c | 31 ++++ drivers/gpu/drm/apple/dcp.h | 1 + drivers/gpu/drm/apple/dcp_backlight.c | 232 ++++++++++++++++++++++++++ drivers/gpu/drm/apple/iomfb.c | 47 +++++- drivers/gpu/drm/apple/iomfb.h | 25 ++- 8 files changed, 349 insertions(+), 16 deletions(-) create mode 100644 drivers/gpu/drm/apple/dcp_backlight.c diff --git a/drivers/gpu/drm/apple/Makefile b/drivers/gpu/drm/apple/Makefile index 2502f781a5dcef..2c02e4dcfd076d 100644 --- a/drivers/gpu/drm/apple/Makefile +++ b/drivers/gpu/drm/apple/Makefile @@ -4,7 +4,7 @@ CFLAGS_trace.o = -I$(src) appledrm-y := apple_drv.o -apple_dcp-y := dcp.o iomfb.o parser.o +apple_dcp-y := dcp.o dcp_backlight.o iomfb.o parser.o apple_dcp-$(CONFIG_TRACING) += trace.o apple_piodma-y := dummy-piodma.o diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index b01f2207faeba6..d6ee078d30b935 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -315,7 +315,6 @@ static int apple_probe_per_dcp(struct device *dev, struct apple_connector *connector; struct drm_encoder *encoder; struct drm_plane *primary; - int con_type; int ret; primary = apple_plane_init(drm, 1U << num, DRM_PLANE_TYPE_PRIMARY); @@ -341,17 +340,8 @@ static int apple_probe_per_dcp(struct device *dev, drm_connector_helper_add(&connector->base, &apple_connector_helper_funcs); - if (of_property_match_string(dcp->dev.of_node, "apple,connector-type", "eDP") >= 0) - con_type = DRM_MODE_CONNECTOR_eDP; - else if (of_property_match_string(dcp->dev.of_node, "apple,connector-type", "HDMI-A") >= 0) - con_type = DRM_MODE_CONNECTOR_HDMIA; - else if (of_property_match_string(dcp->dev.of_node, "apple,connector-type", "USB-C") >= 0) - con_type = DRM_MODE_CONNECTOR_USB; - else - con_type = DRM_MODE_CONNECTOR_Unknown; - ret = drm_connector_init(drm, &connector->base, &apple_connector_funcs, - con_type); + dcp_get_connector_type(dcp)); if (ret) return ret; diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 8c12382e281b42..c2fa002b45d5de 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -64,6 +64,14 @@ struct dcp_fb_reference { #define MAX_NOTCH_HEIGHT 160 +struct dcp_brightness { + u32 dac; + int nits; + int set; + int scale; + bool update; +}; + /* TODO: move IOMFB members to its own struct */ struct apple_dcp { struct device *dev; @@ -128,6 +136,9 @@ struct apple_dcp { struct dcp_display_mode *modes; unsigned int nr_modes; + /* Attributes of the connector */ + int connector_type; + /* Attributes of the connected display */ int width_mm, height_mm; @@ -140,6 +151,10 @@ struct apple_dcp { * on the next successfully completed swap. */ struct list_head swapped_out_fbs; + + struct dcp_brightness brightness; }; +int dcp_backlight_register(struct apple_dcp *dcp); + #endif /* __APPLE_DCP_INTERNAL_H__ */ diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index f4161faad6baf0..d0d6f5be3f3dbf 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -21,6 +21,7 @@ #include "dcp.h" #include "dcp-internal.h" +#include "iomfb.h" #include "parser.h" #include "trace.h" @@ -219,6 +220,14 @@ int dcp_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) } EXPORT_SYMBOL_GPL(dcp_crtc_atomic_check); +int dcp_get_connector_type(struct platform_device *pdev) +{ + struct apple_dcp *dcp = platform_get_drvdata(pdev); + + return (dcp->connector_type); +} +EXPORT_SYMBOL_GPL(dcp_get_connector_type); + void dcp_link(struct platform_device *pdev, struct apple_crtc *crtc, struct apple_connector *connector) { @@ -277,6 +286,7 @@ static int dcp_get_disp_regs(struct apple_dcp *dcp) static int dcp_platform_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct device_node *panel_np; struct apple_dcp *dcp; u32 cpu_ctrl; int ret; @@ -298,6 +308,27 @@ static int dcp_platform_probe(struct platform_device *pdev) of_platform_default_populate(dev->of_node, NULL, dev); + /* intialize brightness scale to a sensible default to avoid divide by 0*/ + dcp->brightness.scale = 65536; + panel_np = of_get_compatible_child(dev->of_node, "apple,panel"); + if (panel_np) { + of_node_put(panel_np); + dcp->connector_type = DRM_MODE_CONNECTOR_eDP; + + /* try to register backlight device, */ + ret = dcp_backlight_register(dcp); + if (ret) + return dev_err_probe(dev, ret, + "Unable to register backlight device\n"); + } else if (of_property_match_string(dev->of_node, "apple,connector-type", "HDMI-A") >= 0) + dcp->connector_type = DRM_MODE_CONNECTOR_HDMIA; + else if (of_property_match_string(dev->of_node, "apple,connector-type", "DP") >= 0) + dcp->connector_type = DRM_MODE_CONNECTOR_DisplayPort; + else if (of_property_match_string(dev->of_node, "apple,connector-type", "USB-C") >= 0) + dcp->connector_type = DRM_MODE_CONNECTOR_USB; + else + dcp->connector_type = DRM_MODE_CONNECTOR_Unknown; + dcp->piodma = dcp_get_dev(dev, "apple,piodma-mapper"); if (!dcp->piodma) { dev_err(dev, "failed to find piodma\n"); diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h index 60e9bcfa4714e0..dfe014f3f5d1da 100644 --- a/drivers/gpu/drm/apple/dcp.h +++ b/drivers/gpu/drm/apple/dcp.h @@ -38,6 +38,7 @@ struct apple_connector { void dcp_poweroff(struct platform_device *pdev); void dcp_poweron(struct platform_device *pdev); int dcp_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state); +int dcp_get_connector_type(struct platform_device *pdev); void dcp_link(struct platform_device *pdev, struct apple_crtc *apple, struct apple_connector *connector); int dcp_start(struct platform_device *pdev); diff --git a/drivers/gpu/drm/apple/dcp_backlight.c b/drivers/gpu/drm/apple/dcp_backlight.c new file mode 100644 index 00000000000000..c695e3bad7db91 --- /dev/null +++ b/drivers/gpu/drm/apple/dcp_backlight.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (C) The Asahi Linux Contributors */ + +#include +#include +#include +#include + +#include +#include +#include +#include "linux/jiffies.h" + +#include "dcp.h" +#include "dcp-internal.h" + +#define MIN_BRIGHTNESS_PART1 2U +#define MAX_BRIGHTNESS_PART1 99U +#define MIN_BRIGHTNESS_PART2 103U +#define MAX_BRIGHTNESS_PART2 510U + +/* + * lookup for display brightness 2 to 99 nits + * */ +static u32 brightness_part1[] = { + 0x0000000, 0x0810038, 0x0f000bd, 0x143011c, + 0x1850165, 0x1bc01a1, 0x1eb01d4, 0x2140200, + 0x2380227, 0x2590249, 0x2770269, 0x2930285, + 0x2ac02a0, 0x2c402b8, 0x2d902cf, 0x2ee02e4, + 0x30102f8, 0x314030b, 0x325031c, 0x335032d, + 0x345033d, 0x354034d, 0x362035b, 0x3700369, + 0x37d0377, 0x38a0384, 0x3960390, 0x3a2039c, + 0x3ad03a7, 0x3b803b3, 0x3c303bd, 0x3cd03c8, + 0x3d703d2, 0x3e103dc, 0x3ea03e5, 0x3f303ef, + 0x3fc03f8, 0x4050400, 0x40d0409, 0x4150411, + 0x41d0419, 0x4250421, 0x42d0429, 0x4340431, + 0x43c0438, 0x443043f, 0x44a0446, 0x451044d, + 0x4570454, 0x45e045b, 0x4640461, 0x46b0468, + 0x471046e, 0x4770474, 0x47d047a, 0x4830480, + 0x4890486, 0x48e048b, 0x4940491, 0x4990497, + 0x49f049c, 0x4a404a1, 0x4a904a7, 0x4ae04ac, + 0x4b304b1, 0x4b804b6, 0x4bd04bb, 0x4c204c0, + 0x4c704c5, 0x4cc04c9, 0x4d004ce, 0x4d504d3, + 0x4d904d7, 0x4de04dc, 0x4e204e0, 0x4e704e4, + 0x4eb04e9, 0x4ef04ed, 0x4f304f1, 0x4f704f5, + 0x4fb04f9, 0x4ff04fd, 0x5030501, 0x5070505, + 0x50b0509, 0x50f050d, 0x5130511, 0x5160515, + 0x51a0518, 0x51e051c, 0x5210520, 0x5250523, + 0x5290527, 0x52c052a, 0x52f052e, 0x5330531, + 0x5360535, 0x53a0538, 0x53d053b, 0x540053f, + 0x5440542, 0x5470545, 0x54a0548, 0x54d054c, + 0x550054f, 0x5530552, 0x5560555, 0x5590558, + 0x55c055b, 0x55f055e, 0x5620561, 0x5650564, + 0x5680567, 0x56b056a, 0x56e056d, 0x571056f, + 0x5740572, 0x5760575, 0x5790578, 0x57c057b, + 0x57f057d, 0x5810580, 0x5840583, 0x5870585, + 0x5890588, 0x58c058b, 0x58f058d +}; + +static u32 brightness_part12[] = { 0x58f058d, 0x59d058f }; + +/* + * lookup table for display brightness 103.3 to 510 nits + * */ +static u32 brightness_part2[] = { + 0x59d058f, 0x5b805ab, 0x5d105c5, 0x5e805dd, + 0x5fe05f3, 0x6120608, 0x625061c, 0x637062e, + 0x6480640, 0x6580650, 0x6680660, 0x677066f, + 0x685067e, 0x693068c, 0x6a00699, 0x6ac06a6, + 0x6b806b2, 0x6c406be, 0x6cf06ca, 0x6da06d5, + 0x6e506df, 0x6ef06ea, 0x6f906f4, 0x70206fe, + 0x70c0707, 0x7150710, 0x71e0719, 0x7260722, + 0x72f072a, 0x7370733, 0x73f073b, 0x7470743, + 0x74e074a, 0x7560752, 0x75d0759, 0x7640760, + 0x76b0768, 0x772076e, 0x7780775, 0x77f077c, + 0x7850782, 0x78c0789, 0x792078f, 0x7980795, + 0x79e079b, 0x7a407a1, 0x7aa07a7, 0x7af07ac, + 0x7b507b2, 0x7ba07b8, 0x7c007bd, 0x7c507c2, + 0x7ca07c8, 0x7cf07cd, 0x7d407d2, 0x7d907d7, + 0x7de07dc, 0x7e307e1, 0x7e807e5, 0x7ec07ea, + 0x7f107ef, 0x7f607f3, 0x7fa07f8, 0x7fe07fc +}; + + +static int dcp_get_brightness(struct backlight_device *bd) +{ + struct apple_dcp *dcp = bl_get_data(bd); + + return dcp->brightness.nits; +} + +#define SCALE_FACTOR (1 << 10) + +static u32 interpolate(int val, int min, int max, u32 *tbl, size_t tbl_size) +{ + u32 frac; + u64 low, high; + u32 interpolated = (tbl_size - 1) * ((val - min) * SCALE_FACTOR) / (max - min); + + size_t index = interpolated / SCALE_FACTOR; + + if (WARN(index + 1 >= tbl_size, "invalid index %zu for brightness %u", index, val)) + return tbl[tbl_size / 2]; + + frac = interpolated & (SCALE_FACTOR - 1); + low = tbl[index]; + high = tbl[index + 1]; + + return ((frac * high) + ((SCALE_FACTOR - frac) * low)) / SCALE_FACTOR; +} + +static u32 calculate_dac(struct apple_dcp *dcp, int val) +{ + u32 dac; + + if (val <= MIN_BRIGHTNESS_PART1) + return 16 * brightness_part1[0]; + else if (val == MAX_BRIGHTNESS_PART1) + return 16 * brightness_part1[ARRAY_SIZE(brightness_part1) - 1]; + else if (val == MIN_BRIGHTNESS_PART2) + return 16 * brightness_part2[0]; + else if (val >= MAX_BRIGHTNESS_PART2) + return brightness_part2[ARRAY_SIZE(brightness_part2) - 1]; + + if (val < MAX_BRIGHTNESS_PART1) { + dac = interpolate(val, MIN_BRIGHTNESS_PART1, MAX_BRIGHTNESS_PART1, + brightness_part1, ARRAY_SIZE(brightness_part1)); + } else if (val > MIN_BRIGHTNESS_PART2) { + dac = interpolate(val, MIN_BRIGHTNESS_PART2, MAX_BRIGHTNESS_PART2, + brightness_part2, ARRAY_SIZE(brightness_part2)); + } else { + dac = interpolate(val, MAX_BRIGHTNESS_PART1, MIN_BRIGHTNESS_PART2, + brightness_part12, ARRAY_SIZE(brightness_part12)); + } + + return 16 * dac; +} + +static int drm_crtc_set_brightness(struct drm_crtc *crtc, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_atomic_state *state; + struct drm_crtc_state *crtc_state; + int ret = 0; + + state = drm_atomic_state_alloc(crtc->dev); + if (!state) + return -ENOMEM; + + state->acquire_ctx = ctx; + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + goto fail; + } + + crtc_state->color_mgmt_changed |= true; + + ret = drm_atomic_commit(state); + +fail: + drm_atomic_state_put(state); + return ret; +} + +static int dcp_set_brightness(struct backlight_device *bd) +{ + int ret = 0; + struct apple_dcp *dcp = bl_get_data(bd); + + bd->props.power = FB_BLANK_UNBLANK; + if (dcp->brightness.set != bd->props.brightness) { + dcp->brightness.dac = calculate_dac(dcp, bd->props.brightness); + dcp->brightness.set = bd->props.brightness; + dcp->brightness.update = true; + } + + if (dcp->brightness.update && dcp->crtc) { + struct drm_modeset_acquire_ctx ctx; + struct drm_device *drm_dev = dcp->crtc->base.dev; + + DRM_MODESET_LOCK_ALL_BEGIN(drm_dev, ctx, 0, ret); + ret = drm_crtc_set_brightness(&dcp->crtc->base, &ctx); + DRM_MODESET_LOCK_ALL_END(drm_dev, ctx, ret); + } + + return ret; +} + +static const struct backlight_ops dcp_backlight_ops = { + .get_brightness = dcp_get_brightness, + .update_status = dcp_set_brightness, +}; + +int dcp_backlight_register(struct apple_dcp *dcp) +{ + struct device *dev = dcp->dev; + struct backlight_device *bd; + struct device_node *panel_np; + struct backlight_properties props = { + .type = BACKLIGHT_PLATFORM, + .brightness = dcp->brightness.nits, + .max_brightness = 0, + .scale = BACKLIGHT_SCALE_LINEAR, + }; + u32 max_brightness; + int ret = 0; + + panel_np = of_get_compatible_child(dev->of_node, "apple,panel"); + if (!panel_np) + return 0; + + if (!of_device_is_available(panel_np)) + goto out_put; + + ret = of_property_read_u32(panel_np, "apple,max-brightness", &max_brightness); + if (ret) { + dev_err(dev, "Missing property 'apple,max-brightness'\n"); + goto out_put; + } + props.max_brightness = min(max_brightness, MAX_BRIGHTNESS_PART2 - 1); + + bd = devm_backlight_device_register(dev, "apple-panel-bl", dev, dcp, + &dcp_backlight_ops, &props); + if (IS_ERR(bd)) + ret = PTR_ERR(bd); + +out_put: + of_node_put(panel_np); + + return ret; +} diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 58d1a91d054c62..eff9178e060228 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -422,6 +422,21 @@ static bool iomfbep_cb_match_backlight_service(struct apple_dcp *dcp, int tag, v return false; } +static void iomfb_cb_pr_publish(struct apple_dcp *dcp, struct iomfb_property *prop) +{ + switch (prop->id) { + case IOMFB_PROPERTY_NITS: + dcp->brightness.nits = prop->value / dcp->brightness.scale; + /* temporary for user debugging during tesing */ + dev_info(dcp->dev, "Backlight updated to %u nits\n", + dcp->brightness.nits); + dcp->brightness.update = false; + break; + default: + dev_dbg(dcp->dev, "pr_publish: id: %d = %u\n", prop->id, prop->value); + } +} + static struct dcp_get_uint_prop_resp dcpep_cb_get_uint_prop(struct apple_dcp *dcp, struct dcp_get_uint_prop_req *req) { @@ -443,6 +458,19 @@ dcpep_cb_get_uint_prop(struct apple_dcp *dcp, struct dcp_get_uint_prop_req *req) return resp; } +static u8 iomfbep_cb_sr_set_property_int(struct apple_dcp *dcp, + struct iomfb_sr_set_property_int_req *req) +{ + if (memcmp(req->obj, "FMOI", sizeof(req->obj)) == 0) { /* "IOMF */ + if (strncmp(req->key, "Brightness_Scale", sizeof(req->key)) == 0) { + if (!req->value_null) + dcp->brightness.scale = req->value; + } + } + + return 1; +} + static void iomfbep_cb_set_fx_prop(struct apple_dcp *dcp, struct iomfb_set_fx_prop_req *req) { // TODO: trace this, see if there properties which needs to used later @@ -1171,6 +1199,8 @@ TRAMPOLINE_INOUT(trampoline_map_piodma, dcpep_cb_map_piodma, struct dcp_map_buf_req, struct dcp_map_buf_resp); TRAMPOLINE_IN(trampoline_unmap_piodma, dcpep_cb_unmap_piodma, struct dcp_unmap_buf_resp); +TRAMPOLINE_INOUT(trampoline_sr_set_property_int, iomfbep_cb_sr_set_property_int, + struct iomfb_sr_set_property_int_req, u8); TRAMPOLINE_INOUT(trampoline_allocate_buffer, dcpep_cb_allocate_buffer, struct dcp_allocate_buffer_req, struct dcp_allocate_buffer_resp); @@ -1195,6 +1225,8 @@ TRAMPOLINE_IN(trampoline_hotplug, dcpep_cb_hotplug, u64); TRAMPOLINE_IN(trampoline_swap_complete_intent_gated, dcpep_cb_swap_complete_intent_gated, struct dcp_swap_complete_intent_gated); +TRAMPOLINE_IN(trampoline_pr_publish, iomfb_cb_pr_publish, + struct iomfb_property); bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, void *) = { @@ -1204,6 +1236,7 @@ bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, [3] = trampoline_rt_bandwidth, [100] = iomfbep_cb_match_pmu_service, [101] = trampoline_zero, /* get_display_default_stride */ + [102] = trampoline_nop, /* set_number_property */ [103] = trampoline_nop, /* set_boolean_property */ [106] = trampoline_nop, /* remove_property */ [107] = trampoline_true, /* create_provider_service */ @@ -1224,14 +1257,14 @@ bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, [207] = iomfbep_cb_match_backlight_service, [208] = trampoline_get_time, [211] = trampoline_nop, /* update_backlight_factor_prop */ - [300] = trampoline_nop, /* pr_publish */ + [300] = trampoline_pr_publish, [401] = trampoline_get_uint_prop, [404] = trampoline_nop, /* sr_set_uint_prop */ [406] = trampoline_set_fx_prop, [408] = trampoline_get_frequency, [411] = trampoline_map_reg, [413] = trampoline_true, /* sr_set_property_dict */ - [414] = trampoline_true, /* sr_set_property_int */ + [414] = trampoline_sr_set_property_int, [415] = trampoline_true, /* sr_set_property_bool */ [451] = trampoline_allocate_buffer, [452] = trampoline_map_physical, @@ -1613,6 +1646,14 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) /* These fields should be set together */ req->swap.swap_completed = req->swap.swap_enabled; + /* update brightness if changed */ + if (dcp->brightness.update) { + req->swap.bl_unk = 1; + req->swap.bl_value = dcp->brightness.dac; + req->swap.bl_power = 0x40; + dcp->brightness.update = false; + } + if (modeset) { struct dcp_display_mode *mode; struct dcp_wait_cookie *cookie; @@ -1666,7 +1707,7 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) dcp->valid_mode = true; } - if (!has_surface) { + if (!has_surface && !crtc_state->color_mgmt_changed) { if (crtc_state->enable && crtc_state->active && !crtc_state->planes_changed) { schedule_work(&dcp->vblank_wq); diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index 68fdc654d597f5..386a84cfcc5cd1 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -63,6 +63,12 @@ struct dcp_packet_header { #define DCP_IS_NULL(ptr) ((ptr) ? 1 : 0) #define DCP_PACKET_ALIGNMENT (0x40) +enum iomfb_property_id { + IOMFB_PROPERTY_NITS = 15, // divide by Brightness_Scale +}; + +#define IOMFB_BRIGHTNESS_MIN 0x10000000 + /* Structures used in v12.0 firmware */ #define SWAP_SURFACES 4 @@ -114,7 +120,11 @@ struct dcp_swap { u32 unk_2c8; u8 unk_2cc[0x14]; u32 unk_2e0; - u8 unk_2e4[0x3c]; + u16 unk_2e2; + u64 bl_unk; + u32 bl_value; // min value is 0x10000000 + u8 bl_power; // constant 0x40 for on + u8 unk_2f3[0x2d]; } __packed; /* Information describing a plane of a planar compressed surface */ @@ -340,6 +350,14 @@ struct dcp_get_uint_prop_resp { u8 padding[3]; } __packed; +struct iomfb_sr_set_property_int_req { + char obj[4]; + char key[0x40]; + u64 value; + u8 value_null; + u8 padding[3]; +} __packed; + struct iomfb_set_fx_prop_req { char obj[4]; char key[0x40]; @@ -410,4 +428,9 @@ struct dcp_read_edt_data_resp { u8 ret; } __packed; +struct iomfb_property { + u32 id; + u32 value; +} __packed; + #endif From 7136111854ee85b30ccafce93ac676c48144f4ac Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 16 Nov 2022 00:10:31 +0100 Subject: [PATCH 0477/1020] HACK: gpu: drm: apple: j314/j316: Ignore 120 Hz mode for integrated display It's currently not useful as DCP limits the swap rate to 60 Hz anyway and marcan reported pointer choppiness with 120 Hz. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/parser.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index fc52c26490ec80..31aecd4b2fc195 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -353,6 +353,19 @@ static int parse_mode(struct dcp_parse_ctx *handle, if (is_virtual) return -EINVAL; + /* + * HACK: + * Ignore the 120 Hz mode on j314/j316 (identified by resolution). + * DCP limits normal swaps to 60 Hz anyway and the 120 Hz mode might + * cause choppiness with X11. + * Just downscoring it and thus making the 60 Hz mode the preferred mode + * seems not enough for some user space. + */ + if (vert.precise_sync_rate >> 16 == 120 && + ((horiz.active == 3024 && vert.active == 1964) || + (horiz.active == 3456 && vert.active == 2234))) + return -EINVAL; + vert.active -= notch_height; vert.sync_width += notch_height; From 28fe69edcf26b938916ee35d1fda77c3ccbc9ff1 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 17 Nov 2022 21:51:09 +0900 Subject: [PATCH 0478/1020] drm/apple: Fix suspend/resume handling Use the drm_modeset helpers for suspend/resume at the subsystem level, which actually do the proper save/restore dance and work, instead of open-coding calls to dcp_poweroff/dcp_poweron which is clearly wrong and doesn't restore properly (nor would it be correct if the display was already off when suspended). Also fix apple_platform_remove while I'm here, since drvdata wasn't getting set so that would never work. Signed-off-by: Hector Martin --- drivers/gpu/drm/apple/apple_drv.c | 31 +++++++++++++++++++++++++++++-- drivers/gpu/drm/apple/dcp.c | 27 --------------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index d6ee078d30b935..a210b60484dd8f 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -403,6 +403,8 @@ static int apple_platform_probe(struct platform_device *pdev) if (IS_ERR(apple)) return PTR_ERR(apple); + dev_set_drvdata(dev, apple); + ret = drm_vblank_init(&apple->drm, nr_dcp); if (ret) return ret; @@ -465,9 +467,9 @@ static int apple_platform_probe(struct platform_device *pdev) static int apple_platform_remove(struct platform_device *pdev) { - struct drm_device *drm = platform_get_drvdata(pdev); + struct apple_drm_private *apple = platform_get_drvdata(pdev); - drm_dev_unregister(drm); + drm_dev_unregister(&apple->drm); return 0; } @@ -478,10 +480,35 @@ static const struct of_device_id of_match[] = { }; MODULE_DEVICE_TABLE(of, of_match); +#ifdef CONFIG_PM_SLEEP +static int apple_platform_suspend(struct device *dev) +{ + struct apple_drm_private *apple = dev_get_drvdata(dev); + + return drm_mode_config_helper_suspend(&apple->drm); +} + +static int apple_platform_resume(struct device *dev) +{ + struct apple_drm_private *apple = dev_get_drvdata(dev); + + drm_mode_config_helper_resume(&apple->drm); + return 0; +} + +static const struct dev_pm_ops apple_platform_pm_ops = { + .suspend = apple_platform_suspend, + .resume = apple_platform_resume, +}; +#endif + static struct platform_driver apple_platform_driver = { .driver = { .name = "apple-drm", .of_match_table = of_match, +#ifdef CONFIG_PM_SLEEP + .pm = &apple_platform_pm_ops, +#endif }, .probe = apple_platform_probe, .remove = apple_platform_remove, diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index d0d6f5be3f3dbf..f4523ccbbce453 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -414,39 +414,12 @@ static const struct of_device_id of_match[] = { }; MODULE_DEVICE_TABLE(of, of_match); -#ifdef CONFIG_PM_SLEEP -/* - * We don't hold any useful persistent state, so for suspend/resume it suffices - * to power off/on the entire DCP. The firmware will sort out the details for - * us. - */ -static int dcp_suspend(struct device *dev) -{ - dcp_poweroff(to_platform_device(dev)); - return 0; -} - -static int dcp_resume(struct device *dev) -{ - dcp_poweron(to_platform_device(dev)); - return 0; -} - -static const struct dev_pm_ops dcp_pm_ops = { - .suspend = dcp_suspend, - .resume = dcp_resume, -}; -#endif - static struct platform_driver apple_platform_driver = { .probe = dcp_platform_probe, .shutdown = dcp_platform_shutdown, .driver = { .name = "apple-dcp", .of_match_table = of_match, -#ifdef CONFIG_PM_SLEEP - .pm = &dcp_pm_ops, -#endif }, }; From 9892ce77853df64fdc7654cba2143e41b8278e77 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 20 Nov 2022 09:20:14 +0100 Subject: [PATCH 0479/1020] gpu: drm: apple: Avoid drm_fb_dma_get_gem_addr It adjust the address by the source position duplicating setting the source postion in IOMFB's swap_submit struct. Prefer the later since it is more explicit. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index eff9178e060228..cd0a6e2c8d9211 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -1567,6 +1568,7 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) l = 0; for_each_oldnew_plane_in_state(state, plane, old_state, new_state, plane_idx) { struct drm_framebuffer *fb = new_state->fb; + struct drm_gem_dma_object *obj; struct drm_rect src_rect; bool opaque = false; @@ -1619,7 +1621,13 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) if (dcp->notch_height > 0) req->swap.dst_rect[l].y += dcp->notch_height; - req->surf_iova[l] = drm_fb_dma_get_gem_addr(fb, new_state, 0); + /* the obvious helper call drm_fb_dma_get_gem_addr() adjusts + * the address for source x/y offsets. Since IOMFB has a direct + * support source position prefer that. + */ + obj = drm_fb_dma_get_gem_obj(fb, 0); + if (obj) + req->surf_iova[l] = obj->dma_addr + fb->offsets[0]; req->surf[l] = (struct dcp_surface){ .opaque = opaque, From 228987b2b16b451a2eb56b788ae6a61df6d99c2c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 23 Nov 2022 00:19:11 +0100 Subject: [PATCH 0480/1020] drm/apple: register backlight device after IOMFB start This allows us to specify the boot display brightness as initial brightness of the baclight device. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp-internal.h | 8 +++- drivers/gpu/drm/apple/dcp.c | 37 +++++++++++++--- drivers/gpu/drm/apple/dcp_backlight.c | 61 +++++++++------------------ drivers/gpu/drm/apple/iomfb.c | 9 ++-- 4 files changed, 64 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index c2fa002b45d5de..9f32fd9d0182ed 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -4,7 +4,9 @@ #ifndef __APPLE_DCP_INTERNAL_H__ #define __APPLE_DCP_INTERNAL_H__ +#include #include +#include #include #include @@ -65,9 +67,10 @@ struct dcp_fb_reference { #define MAX_NOTCH_HEIGHT 160 struct dcp_brightness { + struct backlight_device *bl_dev; + u32 maximum; u32 dac; int nits; - int set; int scale; bool update; }; @@ -153,6 +156,9 @@ struct apple_dcp { struct list_head swapped_out_fbs; struct dcp_brightness brightness; + /* Workqueue for updating the initial initial brightness */ + struct work_struct bl_register_wq; + struct mutex bl_register_mutex; }; int dcp_backlight_register(struct apple_dcp *dcp); diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index f4523ccbbce453..29aa6b018ba23e 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -12,6 +12,7 @@ #include #include #include +#include "linux/workqueue.h" #include #include @@ -252,6 +253,28 @@ int dcp_start(struct platform_device *pdev) } EXPORT_SYMBOL(dcp_start); +static void dcp_work_register_backlight(struct work_struct *work) +{ + int ret; + struct apple_dcp *dcp; + + dcp = container_of(work, struct apple_dcp, bl_register_wq); + + mutex_lock(&dcp->bl_register_mutex); + if (dcp->brightness.bl_dev) + goto out_unlock; + + /* try to register backlight device, */ + ret = dcp_backlight_register(dcp); + if (ret) { + dev_err(dcp->dev, "Unable to register backlight device\n"); + dcp->brightness.maximum = 0; + } + +out_unlock: + mutex_unlock(&dcp->bl_register_mutex); +} + static struct platform_device *dcp_get_dev(struct device *dev, const char *name) { struct platform_device *pdev; @@ -312,14 +335,16 @@ static int dcp_platform_probe(struct platform_device *pdev) dcp->brightness.scale = 65536; panel_np = of_get_compatible_child(dev->of_node, "apple,panel"); if (panel_np) { + if (of_device_is_available(panel_np)) { + ret = of_property_read_u32(panel_np, "apple,max-brightness", + &dcp->brightness.maximum); + if (ret) + dev_err(dev, "Missing property 'apple,max-brightness'\n"); + } of_node_put(panel_np); dcp->connector_type = DRM_MODE_CONNECTOR_eDP; - - /* try to register backlight device, */ - ret = dcp_backlight_register(dcp); - if (ret) - return dev_err_probe(dev, ret, - "Unable to register backlight device\n"); + INIT_WORK(&dcp->bl_register_wq, dcp_work_register_backlight); + mutex_init(&dcp->bl_register_mutex); } else if (of_property_match_string(dev->of_node, "apple,connector-type", "HDMI-A") >= 0) dcp->connector_type = DRM_MODE_CONNECTOR_HDMIA; else if (of_property_match_string(dev->of_node, "apple,connector-type", "DP") >= 0) diff --git a/drivers/gpu/drm/apple/dcp_backlight.c b/drivers/gpu/drm/apple/dcp_backlight.c index c695e3bad7db91..5b4a41c53ca21b 100644 --- a/drivers/gpu/drm/apple/dcp_backlight.c +++ b/drivers/gpu/drm/apple/dcp_backlight.c @@ -165,29 +165,28 @@ static int drm_crtc_set_brightness(struct drm_crtc *crtc, static int dcp_set_brightness(struct backlight_device *bd) { - int ret = 0; + int ret; struct apple_dcp *dcp = bl_get_data(bd); + struct drm_modeset_acquire_ctx ctx; - bd->props.power = FB_BLANK_UNBLANK; - if (dcp->brightness.set != bd->props.brightness) { - dcp->brightness.dac = calculate_dac(dcp, bd->props.brightness); - dcp->brightness.set = bd->props.brightness; - dcp->brightness.update = true; - } + if (bd->props.state & BL_CORE_SUSPENDED) + return 0; - if (dcp->brightness.update && dcp->crtc) { - struct drm_modeset_acquire_ctx ctx; - struct drm_device *drm_dev = dcp->crtc->base.dev; + if (!dcp->crtc) + return -EAGAIN; - DRM_MODESET_LOCK_ALL_BEGIN(drm_dev, ctx, 0, ret); - ret = drm_crtc_set_brightness(&dcp->crtc->base, &ctx); - DRM_MODESET_LOCK_ALL_END(drm_dev, ctx, ret); - } + dcp->brightness.dac = calculate_dac(dcp, bd->props.brightness); + dcp->brightness.update = true; + + DRM_MODESET_LOCK_ALL_BEGIN(dcp->crtc->base.dev, ctx, 0, ret); + ret = drm_crtc_set_brightness(&dcp->crtc->base, &ctx); + DRM_MODESET_LOCK_ALL_END(dcp->crtc->base.dev, ctx, ret); return ret; } static const struct backlight_ops dcp_backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, .get_brightness = dcp_get_brightness, .update_status = dcp_set_brightness, }; @@ -195,38 +194,20 @@ static const struct backlight_ops dcp_backlight_ops = { int dcp_backlight_register(struct apple_dcp *dcp) { struct device *dev = dcp->dev; - struct backlight_device *bd; - struct device_node *panel_np; + struct backlight_device *bl_dev; struct backlight_properties props = { .type = BACKLIGHT_PLATFORM, .brightness = dcp->brightness.nits, - .max_brightness = 0, .scale = BACKLIGHT_SCALE_LINEAR, }; - u32 max_brightness; - int ret = 0; + props.max_brightness = min(dcp->brightness.maximum, MAX_BRIGHTNESS_PART2 - 1); - panel_np = of_get_compatible_child(dev->of_node, "apple,panel"); - if (!panel_np) - return 0; - - if (!of_device_is_available(panel_np)) - goto out_put; - - ret = of_property_read_u32(panel_np, "apple,max-brightness", &max_brightness); - if (ret) { - dev_err(dev, "Missing property 'apple,max-brightness'\n"); - goto out_put; - } - props.max_brightness = min(max_brightness, MAX_BRIGHTNESS_PART2 - 1); - - bd = devm_backlight_device_register(dev, "apple-panel-bl", dev, dcp, - &dcp_backlight_ops, &props); - if (IS_ERR(bd)) - ret = PTR_ERR(bd); + bl_dev = devm_backlight_device_register(dev, "apple-panel-bl", dev, dcp, + &dcp_backlight_ops, &props); + if (IS_ERR(bl_dev)) + return PTR_ERR(bl_dev); -out_put: - of_node_put(panel_np); + dcp->brightness.bl_dev = bl_dev; - return ret; + return 0; } diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index cd0a6e2c8d9211..59fe986f71ba67 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -427,12 +427,13 @@ static void iomfb_cb_pr_publish(struct apple_dcp *dcp, struct iomfb_property *pr { switch (prop->id) { case IOMFB_PROPERTY_NITS: + { dcp->brightness.nits = prop->value / dcp->brightness.scale; - /* temporary for user debugging during tesing */ - dev_info(dcp->dev, "Backlight updated to %u nits\n", - dcp->brightness.nits); - dcp->brightness.update = false; + /* notify backlight device of the initial brightness */ + if (!dcp->brightness.bl_dev && dcp->brightness.maximum > 0) + schedule_work(&dcp->bl_register_wq); break; + } default: dev_dbg(dcp->dev, "pr_publish: id: %d = %u\n", prop->id, prop->value); } From 9098dd53a48ade4074b103760a50db62b2896a90 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 23 Nov 2022 00:27:38 +0100 Subject: [PATCH 0481/1020] drm/apple: Add trace point for display brightness Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 1 + drivers/gpu/drm/apple/trace.h | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 59fe986f71ba67..ca11c0ca8df23f 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -432,6 +432,7 @@ static void iomfb_cb_pr_publish(struct apple_dcp *dcp, struct iomfb_property *pr /* notify backlight device of the initial brightness */ if (!dcp->brightness.bl_dev && dcp->brightness.maximum > 0) schedule_work(&dcp->bl_register_wq); + trace_iomfb_brightness(dcp, prop->value); break; } default: diff --git a/drivers/gpu/drm/apple/trace.h b/drivers/gpu/drm/apple/trace.h index d6a4742fcf470d..ddf6ff9839e4f7 100644 --- a/drivers/gpu/drm/apple/trace.h +++ b/drivers/gpu/drm/apple/trace.h @@ -153,6 +153,24 @@ TRACE_EVENT(iomfb_swap_complete_intent_gated, ) ); +TRACE_EVENT(iomfb_brightness, + TP_PROTO(struct apple_dcp *dcp, u32 nits), + TP_ARGS(dcp, nits), + TP_STRUCT__entry( + __field(u64, dcp) + __field(u32, nits) + ), + TP_fast_assign( + __entry->dcp = (u64)dcp; + __entry->nits = nits; + ), + TP_printk("dcp=%llx, nits=%u (raw=0x%05x)", + __entry->dcp, + __entry->nits >> 16, + __entry->nits + ) +); + #endif /* _TRACE_DCP_H */ /* This part must be outside protection */ From 847fc6c1cf50051b33954ea7755f2d9742c8d25c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 23 Nov 2022 23:21:54 +0100 Subject: [PATCH 0482/1020] drm/apple: Implement drm_crtc_helper_funcs.mode_fixup Prevents KDE for now to set modes not reported by IOMFB. Seen on j493 with the common display resolution of 2560x1600. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 1 + drivers/gpu/drm/apple/dcp.h | 3 +++ drivers/gpu/drm/apple/iomfb.c | 15 ++++++++++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index a210b60484dd8f..bce91de706eb0f 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -304,6 +304,7 @@ static const struct drm_crtc_helper_funcs apple_crtc_helper_funcs = { .atomic_flush = dcp_flush, .atomic_enable = apple_crtc_atomic_enable, .atomic_disable = apple_crtc_atomic_disable, + .mode_fixup = dcp_crtc_mode_fixup, }; static int apple_probe_per_dcp(struct device *dev, diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h index dfe014f3f5d1da..fb4397e7b390fe 100644 --- a/drivers/gpu/drm/apple/dcp.h +++ b/drivers/gpu/drm/apple/dcp.h @@ -49,6 +49,9 @@ void dcp_drm_crtc_vblank(struct apple_crtc *crtc); int dcp_get_modes(struct drm_connector *connector); int dcp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode); +bool dcp_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); void dcp_set_dimensions(struct apple_dcp *dcp); void dcp_send_message(struct apple_dcp *dcp, u8 endpoint, u64 message); diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index ca11c0ca8df23f..d13e4a02deeb5d 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -1484,7 +1484,7 @@ EXPORT_SYMBOL_GPL(dcp_get_modes); /* The user may own drm_display_mode, so we need to search for our copy */ static struct dcp_display_mode *lookup_mode(struct apple_dcp *dcp, - struct drm_display_mode *mode) + const struct drm_display_mode *mode) { int i; @@ -1509,6 +1509,19 @@ int dcp_mode_valid(struct drm_connector *connector, } EXPORT_SYMBOL_GPL(dcp_mode_valid); +bool dcp_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct apple_crtc *apple_crtc = to_apple_crtc(crtc); + struct platform_device *pdev = apple_crtc->dcp; + struct apple_dcp *dcp = platform_get_drvdata(pdev); + + /* TODO: support synthesized modes through scaling */ + return lookup_mode(dcp, mode) != NULL; +} +EXPORT_SYMBOL(dcp_crtc_mode_fixup); + /* Helpers to modeset and swap, used to flush */ static void do_swap(struct apple_dcp *dcp, void *data, void *cookie) { From d352948edd7fd812baff47ab5969936a2c739440 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 24 Nov 2022 21:44:36 +0100 Subject: [PATCH 0483/1020] drm/apple: Read display dimensions from devicetree Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 22 +++++++++++++++------- drivers/gpu/drm/apple/iomfb.c | 15 ++++++++++----- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 29aa6b018ba23e..66d53328f09acb 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -331,16 +331,31 @@ static int dcp_platform_probe(struct platform_device *pdev) of_platform_default_populate(dev->of_node, NULL, dev); + ret = of_property_read_u32(dev->of_node, "apple,notch-height", + &dcp->notch_height); + if (dcp->notch_height > MAX_NOTCH_HEIGHT) + dcp->notch_height = MAX_NOTCH_HEIGHT; + if (dcp->notch_height > 0) + dev_info(dev, "Detected display with notch of %u pixel\n", dcp->notch_height); + /* intialize brightness scale to a sensible default to avoid divide by 0*/ dcp->brightness.scale = 65536; panel_np = of_get_compatible_child(dev->of_node, "apple,panel"); if (panel_np) { + const char height_prop[2][16] = { "adj-height-mm", "height-mm" }; + if (of_device_is_available(panel_np)) { ret = of_property_read_u32(panel_np, "apple,max-brightness", &dcp->brightness.maximum); if (ret) dev_err(dev, "Missing property 'apple,max-brightness'\n"); } + + of_property_read_u32(panel_np, "width-mm", &dcp->width_mm); + /* use adjusted height as long as the notch is hidden */ + of_property_read_u32(panel_np, height_prop[!dcp->notch_height], + &dcp->height_mm); + of_node_put(panel_np); dcp->connector_type = DRM_MODE_CONNECTOR_eDP; INIT_WORK(&dcp->bl_register_wq, dcp_work_register_backlight); @@ -387,13 +402,6 @@ static int dcp_platform_probe(struct platform_device *pdev) dev_warn(dev, "failed read 'apple,asc-dram-mask': %d\n", ret); dev_dbg(dev, "'apple,asc-dram-mask': 0x%011llx\n", dcp->asc_dram_mask); - ret = of_property_read_u32(dev->of_node, "apple,notch-height", - &dcp->notch_height); - if (dcp->notch_height > MAX_NOTCH_HEIGHT) - dcp->notch_height = MAX_NOTCH_HEIGHT; - if (dcp->notch_height > 0) - dev_info(dev, "Detected display with notch of %u pixel\n", dcp->notch_height); - bitmap_zero(dcp->memdesc_map, DCP_MAX_MAPPINGS); // TDOD: mem_desc IDs start at 1, for simplicity just skip '0' entry set_bit(0, dcp->memdesc_map); diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index d13e4a02deeb5d..921e593129daeb 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -763,12 +763,17 @@ static bool dcpep_process_chunks(struct apple_dcp *dcp, return false; } } else if (!strcmp(req->key, "DisplayAttributes")) { - ret = parse_display_attributes(&ctx, &dcp->width_mm, - &dcp->height_mm); + /* DisplayAttributes are empty for integrated displays, use + * display dimensions read from the devicetree + */ + if (dcp->main_display) { + ret = parse_display_attributes(&ctx, &dcp->width_mm, + &dcp->height_mm); - if (ret) { - dev_warn(dcp->dev, "failed to parse display attribs\n"); - return false; + if (ret) { + dev_warn(dcp->dev, "failed to parse display attribs\n"); + return false; + } } dcp_set_dimensions(dcp); From d480dac04fb222900bd0f15d010696bc21f611bf Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Mon, 28 Nov 2022 00:42:25 +0900 Subject: [PATCH 0484/1020] drm/apple: Wait for power on request to complete synchronously Signed-off-by: Asahi Lina --- drivers/gpu/drm/apple/iomfb.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 921e593129daeb..435999f3a354c7 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -941,6 +941,16 @@ static void dcp_on_final(struct apple_dcp *dcp, void *out, void *cookie) } } +static void dcp_on_set_power_state(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct dcp_set_power_state_req req = { + .unklong = 1, + }; + dev_dbg(dcp->dev, "%s", __func__); + + dcp_set_power_state(dcp, false, &req, dcp_on_final, cookie); +} + static void dcp_on_set_parameter(struct apple_dcp *dcp, void *out, void *cookie) { struct dcp_set_parameter_dcp param = { @@ -950,16 +960,13 @@ static void dcp_on_set_parameter(struct apple_dcp *dcp, void *out, void *cookie) }; dev_dbg(dcp->dev, "%s", __func__); - dcp_set_parameter_dcp(dcp, false, ¶m, dcp_on_final, cookie); + dcp_set_parameter_dcp(dcp, false, ¶m, dcp_on_set_power_state, cookie); } void dcp_poweron(struct platform_device *pdev) { struct apple_dcp *dcp = platform_get_drvdata(pdev); struct dcp_wait_cookie *cookie; - struct dcp_set_power_state_req req = { - .unklong = 1, - }; int ret; u32 handle; dev_dbg(dcp->dev, "%s", __func__); @@ -975,15 +982,13 @@ void dcp_poweron(struct platform_device *pdev) if (dcp->main_display) { handle = 0; - dcp_set_display_device(dcp, false, &handle, dcp_on_final, + dcp_set_display_device(dcp, false, &handle, dcp_on_set_power_state, cookie); } else { handle = 2; dcp_set_display_device(dcp, false, &handle, dcp_on_set_parameter, cookie); } - dcp_set_power_state(dcp, true, &req, NULL, NULL); - ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(500)); if (ret == 0) From c93149582b5b02c901fdeeb97d054f61f0191d6c Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Mon, 28 Nov 2022 00:44:41 +0900 Subject: [PATCH 0485/1020] drm/apple: Remove obsolete ignore_swap_complete Signed-off-by: Asahi Lina --- drivers/gpu/drm/apple/dcp-internal.h | 2 -- drivers/gpu/drm/apple/iomfb.c | 5 +---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 9f32fd9d0182ed..24db9f39d78e2a 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -133,8 +133,6 @@ struct apple_dcp { /* eDP display without DP-HDMI conversion */ bool main_display; - bool ignore_swap_complete; - /* Modes valid for the connected display */ struct dcp_display_mode *modes; unsigned int nr_modes; diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 435999f3a354c7..a07bd468189193 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -358,8 +358,7 @@ static void dcpep_cb_swap_complete(struct apple_dcp *dcp, { trace_iomfb_swap_complete(dcp, resp->swap_id); - if (!dcp->ignore_swap_complete) - dcp_drm_crtc_vblank(dcp->crtc); + dcp_drm_crtc_vblank(dcp->crtc); } /* special */ @@ -1550,8 +1549,6 @@ static void complete_set_digital_out_mode(struct apple_dcp *dcp, void *data, struct dcp_wait_cookie *wait = cookie; dev_dbg(dcp->dev, "%s", __func__); - dcp->ignore_swap_complete = false; - if (wait) { complete(&wait->done); kref_put(&wait->refcount, release_wait_cookie); From 1445eb02170a3fdde1d335f7997be1c721fdbdb2 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Mon, 28 Nov 2022 01:02:24 +0900 Subject: [PATCH 0486/1020] drm/asahi: Fix backlight restores on non-microLED devices Apparently what happens here is that the DCP's idea of backlight brightness is desynced with the real brightness across power cycles. This means that even if we just force an update after a power cycle, it doesn't work since it considers it unchanged. To fix this, we need to both force an update on poweron and also explicitly turn the backlight off on poweroff, which makes DCP listen to us and actually update the backlight state properly. Signed-off-by: Asahi Lina --- drivers/gpu/drm/apple/dcp_backlight.c | 1 + drivers/gpu/drm/apple/iomfb.c | 31 ++++++++++++++++++++------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp_backlight.c b/drivers/gpu/drm/apple/dcp_backlight.c index 5b4a41c53ca21b..42b1097eaa0180 100644 --- a/drivers/gpu/drm/apple/dcp_backlight.c +++ b/drivers/gpu/drm/apple/dcp_backlight.c @@ -208,6 +208,7 @@ int dcp_backlight_register(struct apple_dcp *dcp) return PTR_ERR(bl_dev); dcp->brightness.bl_dev = bl_dev; + dcp->brightness.dac = calculate_dac(dcp, dcp->brightness.nits); return 0; } diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index a07bd468189193..17d4602b1cb006 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -994,6 +994,9 @@ void dcp_poweron(struct platform_device *pdev) dev_warn(dcp->dev, "wait for power timed out"); kref_put(&cookie->refcount, release_wait_cookie);; + + /* Force a brightness update after poweron, to restore the brightness */ + dcp->brightness.update = true; } EXPORT_SYMBOL(dcp_poweron); @@ -1036,6 +1039,17 @@ void dcp_poweroff(struct platform_device *pdev) dcp->swap.swap.swap_completed = DCP_REMOVE_LAYERS | 0x7; dcp->swap.swap.unk_10c = 0xFF000000; + /* + * Turn off the backlight. This matters because the DCP's idea of + * backlight brightness gets desynced after a power change, and it + * needs to be told it's going to turn off so it will consider the + * subsequent update on poweron an actual change and restore the + * brightness. + */ + dcp->swap.swap.bl_unk = 1; + dcp->swap.swap.bl_value = 0; + dcp->swap.swap.bl_power = 0; + for (int l = 0; l < SWAP_SURFACES; l++) dcp->swap.surf_null[l] = true; @@ -1676,14 +1690,6 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) /* These fields should be set together */ req->swap.swap_completed = req->swap.swap_enabled; - /* update brightness if changed */ - if (dcp->brightness.update) { - req->swap.bl_unk = 1; - req->swap.bl_value = dcp->brightness.dac; - req->swap.bl_power = 0x40; - dcp->brightness.update = false; - } - if (modeset) { struct dcp_display_mode *mode; struct dcp_wait_cookie *cookie; @@ -1746,6 +1752,15 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) req->clear = 1; } + + /* update brightness if changed */ + if (dcp->brightness.update) { + req->swap.bl_unk = 1; + req->swap.bl_value = dcp->brightness.dac; + req->swap.bl_power = 0x40; + dcp->brightness.update = false; + } + do_swap(dcp, NULL, NULL); } EXPORT_SYMBOL_GPL(dcp_flush); From c8462e735c0f1be26f0a13e5833a4edf71764955 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 27 Nov 2022 21:48:44 +0100 Subject: [PATCH 0487/1020] drm/apple: Schedule backlight update on enable_backlight_message_ap_gated On non mini-LED displays the backlight comes out of power-off (DPMS) with minimal backlight brightness. This seems to be a DCP firmware issue. It logs "[BrightnessLCD.cpp:743][AFK]nitsToDBV: iDAC out of range" to syslog although the brightness in the swap_submit call is valid. This fixes the issue only for clients using swap. For other clients an atomic backlight update has to be scheduled via a work queue. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 17d4602b1cb006..571e950ec787d4 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -696,6 +696,17 @@ dcpep_cb_read_edt_data(struct apple_dcp *dcp, struct dcp_read_edt_data_req *req) }; } +static void iomfbep_cb_enable_backlight_message_ap_gated(struct apple_dcp *dcp, + u8 *enabled) +{ + /* + * update backlight brightness on next swap, on non mini-LED displays + * DCP seems to set an invalid iDAC value after coming out of DPMS. + * syslog: "[BrightnessLCD.cpp:743][AFK]nitsToDBV: iDAC out of range" + */ + dcp->brightness.update = true; +} + /* Chunked data transfer for property dictionaries */ static u8 dcpep_cb_prop_start(struct apple_dcp *dcp, u32 *length) { @@ -1251,6 +1262,8 @@ TRAMPOLINE_IN(trampoline_hotplug, dcpep_cb_hotplug, u64); TRAMPOLINE_IN(trampoline_swap_complete_intent_gated, dcpep_cb_swap_complete_intent_gated, struct dcp_swap_complete_intent_gated); +TRAMPOLINE_IN(trampoline_enable_backlight_message_ap_gated, + iomfbep_cb_enable_backlight_message_ap_gated, u8); TRAMPOLINE_IN(trampoline_pr_publish, iomfb_cb_pr_publish, struct iomfb_property); @@ -1306,7 +1319,7 @@ bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, [582] = trampoline_true, /* create_default_fb_surface */ [589] = trampoline_swap_complete, [591] = trampoline_swap_complete_intent_gated, - [593] = trampoline_nop, /* enable_backlight_message_ap_gated */ + [593] = trampoline_enable_backlight_message_ap_gated, [598] = trampoline_nop, /* find_swap_function_gated */ }; From 9e56498d67c041221060999d9702117893664346 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 27 Nov 2022 22:45:22 +0100 Subject: [PATCH 0488/1020] drm/apple: Report "PMUS.Temperature" only for mini-LED backlights Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp-internal.h | 3 +++ drivers/gpu/drm/apple/dcp.c | 7 ++++++- drivers/gpu/drm/apple/iomfb.c | 3 ++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 24db9f39d78e2a..7fe6490509f754 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -133,6 +133,9 @@ struct apple_dcp { /* eDP display without DP-HDMI conversion */ bool main_display; + /* panel has a mini-LED backllight */ + bool has_mini_led; + /* Modes valid for the connected display */ struct dcp_display_mode *modes; unsigned int nr_modes; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 66d53328f09acb..1c5f5d6c55322d 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -340,7 +340,12 @@ static int dcp_platform_probe(struct platform_device *pdev) /* intialize brightness scale to a sensible default to avoid divide by 0*/ dcp->brightness.scale = 65536; - panel_np = of_get_compatible_child(dev->of_node, "apple,panel"); + panel_np = of_get_compatible_child(dev->of_node, "apple,panel-mini-led"); + if (panel_np) + dcp->has_mini_led = true; + else + panel_np = of_get_compatible_child(dev->of_node, "apple,panel"); + if (panel_np) { const char height_prop[2][16] = { "adj-height-mm", "height-mm" }; diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 571e950ec787d4..c9e12d86c4930e 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -446,7 +446,8 @@ dcpep_cb_get_uint_prop(struct apple_dcp *dcp, struct dcp_get_uint_prop_req *req) .value = 0 }; - if (memcmp(req->obj, "SUMP", sizeof(req->obj)) == 0) { /* "PMUS */ + if (dcp->has_mini_led && + memcmp(req->obj, "SUMP", sizeof(req->obj)) == 0) { /* "PMUS */ if (strncmp(req->key, "Temperature", sizeof(req->key)) == 0) { /* * TODO: value from j314c, find out if it is temperature in From 29435256e0836eccc92b6d6f11bc84d3b49a5193 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 4 Dec 2022 11:36:05 +0100 Subject: [PATCH 0489/1020] drm/apple: Check if DCP firmware is supported Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp-internal.h | 8 +++ drivers/gpu/drm/apple/dcp.c | 77 ++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 7fe6490509f754..18969ab18e8319 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -16,6 +16,11 @@ struct apple_dcp; +enum dcp_firmware_version { + DCP_FIRMWARE_UNKNOWN, + DCP_FIRMWARE_V_12_3, +}; + enum { SYSTEM_ENDPOINT = 0x20, TEST_ENDPOINT = 0x21, @@ -84,6 +89,9 @@ struct apple_dcp { struct apple_crtc *crtc; struct apple_connector *connector; + /* firmware version and compatible firmware version */ + enum dcp_firmware_version fw_compat; + /* Coprocessor control register */ void __iomem *coproc_reg; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 1c5f5d6c55322d..a19c124396ed87 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -3,8 +3,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -306,18 +308,93 @@ static int dcp_get_disp_regs(struct apple_dcp *dcp) return 0; } +#define DCP_FW_VERSION_MIN_LEN 3 +#define DCP_FW_VERSION_MAX_LEN 5 +#define DCP_FW_VERSION_STR_LEN (DCP_FW_VERSION_MAX_LEN * 4) + +static int dcp_read_fw_version(struct device *dev, const char *name, + char *version_str) +{ + u32 ver[DCP_FW_VERSION_MAX_LEN]; + int len_str; + int len; + + len = of_property_read_variable_u32_array(dev->of_node, name, ver, + DCP_FW_VERSION_MIN_LEN, + DCP_FW_VERSION_MAX_LEN); + + switch (len) { + case 3: + len_str = scnprintf(version_str, DCP_FW_VERSION_STR_LEN, + "%d.%d.%d", ver[0], ver[1], ver[2]); + break; + case 4: + len_str = scnprintf(version_str, DCP_FW_VERSION_STR_LEN, + "%d.%d.%d.%d", ver[0], ver[1], ver[2], + ver[3]); + break; + case 5: + len_str = scnprintf(version_str, DCP_FW_VERSION_STR_LEN, + "%d.%d.%d.%d.%d", ver[0], ver[1], ver[2], + ver[3], ver[4]); + break; + default: + len_str = strscpy(version_str, "UNKNOWN", + DCP_FW_VERSION_STR_LEN); + if (len >= 0) + len = -EOVERFLOW; + break; + } + + if (len_str >= DCP_FW_VERSION_STR_LEN) + dev_warn(dev, "'%s' truncated: '%s'\n", name, version_str); + + return len; +} + +static enum dcp_firmware_version dcp_check_firmware_version(struct device *dev) +{ + char compat_str[DCP_FW_VERSION_STR_LEN]; + char fw_str[DCP_FW_VERSION_STR_LEN]; + int ret; + + /* firmware version is just informative */ + dcp_read_fw_version(dev, "apple,firmware-version", fw_str); + + ret = dcp_read_fw_version(dev, "apple,firmware-compat", compat_str); + if (ret < 0) { + dev_err(dev, "Could not read 'apple,firmware-compat': %d\n", ret); + return DCP_FIRMWARE_UNKNOWN; + } + + if (strncmp(compat_str, "12.3.0", sizeof(compat_str)) == 0) + return DCP_FIRMWARE_V_12_3; + + dev_err(dev, "DCP firmware-compat %s (FW: %s) is not supported\n", + compat_str, fw_str); + + return DCP_FIRMWARE_UNKNOWN; +} + static int dcp_platform_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *panel_np; struct apple_dcp *dcp; + enum dcp_firmware_version fw_compat; u32 cpu_ctrl; int ret; + fw_compat = dcp_check_firmware_version(dev); + if (fw_compat == DCP_FIRMWARE_UNKNOWN) + return -ENODEV; + dcp = devm_kzalloc(dev, sizeof(*dcp), GFP_KERNEL); if (!dcp) return -ENOMEM; + dcp->fw_compat = fw_compat; + platform_set_drvdata(pdev, dcp); dcp->dev = dev; From 6d6c6b2d7bdd4db7258c07d2ed51c82cfc5fe335 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Sun, 27 Nov 2022 19:32:55 +0900 Subject: [PATCH 0490/1020] drm/apple: Disable fake vblank IRQ machinery The hardware does not have a vblank IRQ and drm already knows how to deal with that appropriately, so don't pretend it does. Fixes Xorg. Signed-off-by: Asahi Lina --- drivers/gpu/drm/apple/apple_drv.c | 23 ----------------------- drivers/gpu/drm/apple/dcp.c | 6 ------ 2 files changed, 29 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index bce91de706eb0f..0816c492519d7e 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -166,18 +166,6 @@ static struct drm_plane *apple_plane_init(struct drm_device *dev, return plane; } -static int apple_enable_vblank(struct drm_crtc *crtc) -{ - to_apple_crtc(crtc)->vsync_disabled = false; - - return 0; -} - -static void apple_disable_vblank(struct drm_crtc *crtc) -{ - to_apple_crtc(crtc)->vsync_disabled = true; -} - static enum drm_connector_status apple_connector_detect(struct drm_connector *connector, bool force) { @@ -199,7 +187,6 @@ static void apple_crtc_atomic_enable(struct drm_crtc *crtc, dcp_poweron(apple_crtc->dcp); dev_dbg(&apple_crtc->dcp->dev, "%s finished", __func__); } - drm_crtc_vblank_on(crtc); } static void apple_crtc_atomic_disable(struct drm_crtc *crtc, @@ -208,8 +195,6 @@ static void apple_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state; crtc_state = drm_atomic_get_new_crtc_state(state, crtc); - drm_crtc_vblank_off(crtc); - if (crtc_state->active_changed && !crtc_state->active) { struct apple_crtc *apple_crtc = to_apple_crtc(crtc); dev_dbg(&apple_crtc->dcp->dev, "%s", __func__); @@ -233,8 +218,6 @@ static void apple_crtc_atomic_begin(struct drm_crtc *crtc, unsigned long flags; if (crtc->state->event) { - WARN_ON(drm_crtc_vblank_get(crtc) != 0); - spin_lock_irqsave(&crtc->dev->event_lock, flags); apple_crtc->event = crtc->state->event; spin_unlock_irqrestore(&crtc->dev->event_lock, flags); @@ -270,8 +253,6 @@ static const struct drm_crtc_funcs apple_crtc_funcs = { .page_flip = drm_atomic_helper_page_flip, .reset = drm_atomic_helper_crtc_reset, .set_config = drm_atomic_helper_set_config, - .enable_vblank = apple_enable_vblank, - .disable_vblank = apple_disable_vblank, }; static const struct drm_mode_config_funcs apple_mode_config_funcs = { @@ -406,10 +387,6 @@ static int apple_platform_probe(struct platform_device *pdev) dev_set_drvdata(dev, apple); - ret = drm_vblank_init(&apple->drm, nr_dcp); - if (ret) - return ret; - ret = drmm_mode_config_init(&apple->drm); if (ret) goto err_unload; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index a19c124396ed87..c62b814045f5a4 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -38,15 +38,9 @@ void dcp_drm_crtc_vblank(struct apple_crtc *crtc) { unsigned long flags; - if (crtc->vsync_disabled) - return; - - drm_crtc_handle_vblank(&crtc->base); - spin_lock_irqsave(&crtc->base.dev->event_lock, flags); if (crtc->event) { drm_crtc_send_vblank_event(&crtc->base, crtc->event); - drm_crtc_vblank_put(&crtc->base); crtc->event = NULL; } spin_unlock_irqrestore(&crtc->base.dev->event_lock, flags); From c9783739667b363bf95ab1e33a9193b694367f0c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 11 Dec 2022 13:42:49 +0100 Subject: [PATCH 0491/1020] gpu: drm: apple: Parse color modes completely Selecting the mode with the highest score may result in HDR mode for some displays. Since HDR is not support on driver side this produces an unexpected color representation. Full parsing allows us to reject virtual color modes (should already be invalid due to missing "Score"). Preparation for color and timing mode tracing. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/parser.c | 41 ++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index 31aecd4b2fc195..17060bd5e87ba6 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -248,6 +248,16 @@ static int parse_dimension(struct dcp_parse_ctx *handle, struct dimension *dim) return 0; } +struct color_mode { + s64 colorimetry; + s64 depth; + s64 dynamic_range; + s64 eotf; + s64 id; + s64 pixel_encoding; + s64 score; +}; + static int parse_color_modes(struct dcp_parse_ctx *handle, s64 *best_id) { struct iterator outer_it; @@ -258,17 +268,30 @@ static int parse_color_modes(struct dcp_parse_ctx *handle, s64 *best_id) dcp_parse_foreach_in_array(handle, outer_it) { struct iterator it; - s64 score = -1, id = -1; + bool is_virtual = true; + struct color_mode cmode; dcp_parse_foreach_in_dict(handle, it) { char *key = parse_string(it.handle); if (IS_ERR(key)) ret = PTR_ERR(key); - else if (!strcmp(key, "Score")) - ret = parse_int(it.handle, &score); + else if (!strcmp(key, "Colorimetry")) + ret = parse_int(it.handle, &cmode.colorimetry); + else if (!strcmp(key, "Depth")) + ret = parse_int(it.handle, &cmode.depth); + else if (!strcmp(key, "DynamicRange")) + ret = parse_int(it.handle, &cmode.dynamic_range); + else if (!strcmp(key, "EOTF")) + ret = parse_int(it.handle, &cmode.eotf); else if (!strcmp(key, "ID")) - ret = parse_int(it.handle, &id); + ret = parse_int(it.handle, &cmode.id); + else if (!strcmp(key, "IsVirtual")) + ret = parse_bool(it.handle, &is_virtual); + else if (!strcmp(key, "PixelEncoding")) + ret = parse_int(it.handle, &cmode.pixel_encoding); + else if (!strcmp(key, "Score")) + ret = parse_int(it.handle, &cmode.score); else skip(it.handle); @@ -276,13 +299,13 @@ static int parse_color_modes(struct dcp_parse_ctx *handle, s64 *best_id) return ret; } - /* Skip partial entries */ - if (score < 0 || id < 0) + /* Skip virtual or partial entries */ + if (is_virtual || cmode.score < 0 || cmode.id < 0) continue; - if (score > best_score) { - best_score = score; - *best_id = id; + if (cmode.score > best_score) { + best_score = cmode.score; + *best_id = cmode.id; } } From 0eba0597a96c3f7a112219b7708013264c983f17 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 11 Dec 2022 14:11:30 +0100 Subject: [PATCH 0492/1020] gpu: drm: apple: Skip parsing elements of virtual timing modes Prevents trace points of unused color modes. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/parser.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index 17060bd5e87ba6..d84c77be5fd78b 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -343,6 +343,8 @@ static int parse_mode(struct dcp_parse_ctx *handle, if (IS_ERR(key)) ret = PTR_ERR(key); + else if (is_virtual) + skip(it.handle); else if (!strcmp(key, "HorizontalAttributes")) ret = parse_dimension(it.handle, &horiz); else if (!strcmp(key, "VerticalAttributes")) From 14a5a3a494da2d4b4b1f019118d81770b905b454 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 11 Dec 2022 13:58:53 +0100 Subject: [PATCH 0493/1020] gpu: drm: apple: Add tracing for color and timing modes Restrict symbol mapping for EOTF, pixel encoding and colorimetry to tracing due to low confidence that it is correct. Main concern is the colorimetry mapping. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 3 + drivers/gpu/drm/apple/parser.c | 11 +++ drivers/gpu/drm/apple/parser.h | 3 + drivers/gpu/drm/apple/trace.h | 120 +++++++++++++++++++++++++++++++++ 4 files changed, 137 insertions(+) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index c9e12d86c4930e..8be49c918e6d64 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -755,6 +755,9 @@ static bool dcpep_process_chunks(struct apple_dcp *dcp, return false; } + /* used just as opaque pointer for tracing */ + ctx.dcp = dcp; + ret = parse(dcp->chunks.data, dcp->chunks.length, &ctx); if (ret) { diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index d84c77be5fd78b..a253ec62640ba5 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -6,7 +6,9 @@ #include #include #include + #include "parser.h" +#include "trace.h" #define DCP_PARSE_HEADER 0xd3 @@ -303,6 +305,11 @@ static int parse_color_modes(struct dcp_parse_ctx *handle, s64 *best_id) if (is_virtual || cmode.score < 0 || cmode.id < 0) continue; + trace_iomfb_color_mode(handle->dcp, cmode.id, cmode.score, + cmode.depth, cmode.colorimetry, + cmode.eotf, cmode.dynamic_range, + cmode.pixel_encoding); + if (cmode.score > best_score) { best_score = cmode.score; *best_id = cmode.id; @@ -419,6 +426,10 @@ static int parse_mode(struct dcp_parse_ctx *handle, out->timing_mode_id = id; out->color_mode_id = best_color_mode; + trace_iomfb_timing_mode(handle->dcp, id, *score, horiz.active, + vert.active, vert.precise_sync_rate, + best_color_mode); + return 0; } diff --git a/drivers/gpu/drm/apple/parser.h b/drivers/gpu/drm/apple/parser.h index a2d479258ed0eb..92fe9473d56718 100644 --- a/drivers/gpu/drm/apple/parser.h +++ b/drivers/gpu/drm/apple/parser.h @@ -7,7 +7,10 @@ /* For mode parsing */ #include +struct apple_dcp; + struct dcp_parse_ctx { + struct apple_dcp *dcp; void *blob; u32 pos, len; }; diff --git a/drivers/gpu/drm/apple/trace.h b/drivers/gpu/drm/apple/trace.h index ddf6ff9839e4f7..b691fc5a472587 100644 --- a/drivers/gpu/drm/apple/trace.h +++ b/drivers/gpu/drm/apple/trace.h @@ -171,6 +171,126 @@ TRACE_EVENT(iomfb_brightness, ) ); +#define show_eotf(eotf) \ + __print_symbolic(eotf, { 0, "SDR gamma"}, \ + { 1, "HDR gamma"}, \ + { 2, "ST 2084 (PQ)"}, \ + { 3, "BT.2100 (HLG)"}, \ + { 4, "unexpected"}) + +#define show_encoding(enc) \ + __print_symbolic(enc, { 0, "RGB"}, \ + { 1, "YUV 4:2:0"}, \ + { 3, "YUV 4:2:2"}, \ + { 2, "YUV 4:4:4"}, \ + { 4, "DolbyVision (native)"}, \ + { 5, "DolbyVision (HDMI)"}, \ + { 6, "YCbCr 4:2:2 (DP tunnel)"}, \ + { 7, "YCbCr 4:2:2 (HDMI tunnel)"}, \ + { 8, "DolbyVision LL YCbCr 4:2:2"}, \ + { 9, "DolbyVision LL YCbCr 4:2:2 (DP)"}, \ + {10, "DolbyVision LL YCbCr 4:2:2 (HDMI)"}, \ + {11, "DolbyVision LL YCbCr 4:4:4"}, \ + {12, "DolbyVision LL RGB 4:2:2"}, \ + {13, "GRGB as YCbCr422 (Even line blue)"}, \ + {14, "GRGB as YCbCr422 (Even line red)"}, \ + {15, "unexpected"}) + +#define show_colorimetry(col) \ + __print_symbolic(col, { 0, "SMPTE 170M/BT.601"}, \ + { 1, "BT.701"}, \ + { 2, "xvYCC601"}, \ + { 3, "xvYCC709"}, \ + { 4, "sYCC601"}, \ + { 5, "AdobeYCC601"}, \ + { 6, "BT.2020 (c)"}, \ + { 7, "BT.2020 (nc)"}, \ + { 8, "DolbyVision VSVDB"}, \ + { 9, "BT.2020 (RGB)"}, \ + {10, "sRGB"}, \ + {11, "scRGB"}, \ + {12, "scRGBfixed"}, \ + {13, "AdobeRGB"}, \ + {14, "DCI-P3 (D65)"}, \ + {15, "DCI-P3 (Theater)"}, \ + {16, "Default RGB"}, \ + {17, "unexpected"}) + +#define show_range(range) \ + __print_symbolic(range, { 0, "Full"}, \ + { 1, "Limited"}, \ + { 2, "unexpected"}) + +TRACE_EVENT(iomfb_color_mode, + TP_PROTO(struct apple_dcp *dcp, u32 id, u32 score, u32 depth, + u32 colorimetry, u32 eotf, u32 range, u32 pixel_enc), + TP_ARGS(dcp, id, score, depth, colorimetry, eotf, range, pixel_enc), + TP_STRUCT__entry( + __field(u64, dcp) + __field(u32, id) + __field(u32, score) + __field(u32, depth) + __field(u32, colorimetry) + __field(u32, eotf) + __field(u32, range) + __field(u32, pixel_enc) + ), + TP_fast_assign( + __entry->dcp = (u64)dcp; + __entry->id = id; + __entry->score = score; + __entry->depth = depth; + __entry->colorimetry = min_t(u32, colorimetry, 17U); + __entry->eotf = min_t(u32, eotf, 4U); + __entry->range = min_t(u32, range, 2U); + __entry->pixel_enc = min_t(u32, pixel_enc, 15U); + ), + TP_printk("dcp=%llx, id=%u, score=%u, depth=%u, colorimetry=%s, eotf=%s, range=%s, pixel_enc=%s", + __entry->dcp, + __entry->id, + __entry->score, + __entry->depth, + show_colorimetry(__entry->colorimetry), + show_eotf(__entry->eotf), + show_range(__entry->range), + show_encoding(__entry->pixel_enc) + ) +); + +TRACE_EVENT(iomfb_timing_mode, + TP_PROTO(struct apple_dcp *dcp, u32 id, u32 score, u32 width, + u32 height, u32 clock, u32 color_mode), + TP_ARGS(dcp, id, score, width, height, clock, color_mode), + TP_STRUCT__entry( + __field(u64, dcp) + __field(u32, id) + __field(u32, score) + __field(u32, width) + __field(u32, height) + __field(u32, clock) + __field(u32, color_mode) + ), + TP_fast_assign( + __entry->dcp = (u64)dcp; + __entry->id = id; + __entry->score = score; + __entry->width = width; + __entry->height = height; + __entry->clock = clock; + __entry->color_mode = color_mode; + ), + TP_printk("dcp=%llx, id=%u, score=%u, %ux%u@%u.%u, color_mode=%u", + __entry->dcp, + __entry->id, + __entry->score, + __entry->width, + __entry->height, + __entry->clock >> 16, + ((__entry->clock & 0xffff) * 1000) >> 16, + __entry->color_mode + ) +); + #endif /* _TRACE_DCP_H */ /* This part must be outside protection */ From 2ff5024830500b0c7c5b210aafcdcfb0d4c6f3af Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 11 Dec 2022 14:27:13 +0100 Subject: [PATCH 0494/1020] gpu: drm: apple: Prefer SDR color modes DCP best scored color mode might result in an HDR mode. As long as the driver (and DRM) is not ready for HDR try to avoid such modes. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/parser.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index a253ec62640ba5..678c0e42e10682 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -260,13 +260,14 @@ struct color_mode { s64 score; }; -static int parse_color_modes(struct dcp_parse_ctx *handle, s64 *best_id) +static int parse_color_modes(struct dcp_parse_ctx *handle, s64 *preferred_id) { struct iterator outer_it; int ret = 0; - s64 best_score = -1; + s64 best_score = -1, best_score_sdr = -1; + s64 best_id = -1, best_id_sdr = -1; - *best_id = -1; + *preferred_id = -1; dcp_parse_foreach_in_array(handle, outer_it) { struct iterator it; @@ -310,12 +311,25 @@ static int parse_color_modes(struct dcp_parse_ctx *handle, s64 *best_id) cmode.eotf, cmode.dynamic_range, cmode.pixel_encoding); - if (cmode.score > best_score) { - best_score = cmode.score; - *best_id = cmode.id; + if (cmode.eotf == 0) { + if (cmode.score > best_score_sdr) { + best_score_sdr = cmode.score; + best_id_sdr = cmode.id; + } + } else { + if (cmode.score > best_score) { + best_score = cmode.score; + best_id = cmode.id; + } } } + /* prefer SDR color modes as long as HDR is not supported */ + if (best_score_sdr >= 0) + *preferred_id = best_id_sdr; + else if (best_score >= 0) + *preferred_id = best_id; + return 0; } From d72fee3e5d1bbb1ba5f1a3f98c77b1023d72d850 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 11 Dec 2022 17:54:40 +0100 Subject: [PATCH 0495/1020] gpu: drm: apple: Add IOMobileFramebufferAP::get_color_remap_mode Probably not important but avoids an unnecessary difference compred to macOS. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 19 ++++++++++++++++++- drivers/gpu/drm/apple/iomfb.h | 12 ++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 8be49c918e6d64..07a1179f75bc38 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -181,6 +181,7 @@ const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { DCP_METHOD("A410", dcpep_set_display_device), DCP_METHOD("A411", dcpep_is_main_display), DCP_METHOD("A412", dcpep_set_digital_out_mode), + DCP_METHOD("A426", iomfbep_get_color_remap_mode), DCP_METHOD("A439", dcpep_set_parameter_dcp), DCP_METHOD("A443", dcpep_create_default_fb), DCP_METHOD("A447", dcpep_enable_disable_video_power_savings), @@ -261,10 +262,21 @@ static void dcp_push(struct apple_dcp *dcp, bool oob, enum dcpep_method method, cb, cookie); \ } +#define IOMFB_THUNK_INOUT(name, T_in, T_out) \ + static void iomfb_ ## name(struct apple_dcp *dcp, bool oob, T_in *data, \ + dcp_callback_t cb, void *cookie) \ + { \ + dcp_push(dcp, oob, iomfbep_ ## name, sizeof(T_in), sizeof(T_out), \ + data, cb, cookie); \ + } + DCP_THUNK_OUT(iomfb_a131_pmu_service_matched, iomfbep_a131_pmu_service_matched, u32); DCP_THUNK_OUT(iomfb_a132_backlight_service_matched, iomfbep_a132_backlight_service_matched, u32); DCP_THUNK_OUT(iomfb_a358_vi_set_temperature_hint, iomfbep_a358_vi_set_temperature_hint, u32); +IOMFB_THUNK_INOUT(get_color_remap_mode, struct iomfb_get_color_remap_mode_req, + struct iomfb_get_color_remap_mode_resp); + DCP_THUNK_INOUT(dcp_swap_submit, dcpep_swap_submit, struct dcp_swap_submit_req, struct dcp_swap_submit_resp); @@ -1825,9 +1837,14 @@ static void init_1(struct apple_dcp *dcp, void *out, void *cookie) static void dcp_started(struct apple_dcp *dcp, void *data, void *cookie) { + struct iomfb_get_color_remap_mode_req color_remap = + (struct iomfb_get_color_remap_mode_req){ + .mode = 6, + }; + dev_info(dcp->dev, "DCP booted\n"); - init_1(dcp, data, cookie); + iomfb_get_color_remap_mode(dcp, false, &color_remap, init_1, cookie); } void iomfb_recv_msg(struct apple_dcp *dcp, u64 message) diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index 386a84cfcc5cd1..083ebd4e0be33f 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -210,6 +210,7 @@ enum dcpep_method { iomfbep_a131_pmu_service_matched, iomfbep_a132_backlight_service_matched, iomfbep_a358_vi_set_temperature_hint, + iomfbep_get_color_remap_mode, dcpep_num_methods }; @@ -433,4 +434,15 @@ struct iomfb_property { u32 value; } __packed; +struct iomfb_get_color_remap_mode_req { + u32 mode; + u8 mode_null; + u8 padding[3]; +} __packed; + +struct iomfb_get_color_remap_mode_resp { + u32 mode; + u32 ret; +} __packed; + #endif From efbc668bd18bc5911e57ea0f42a6233c954a7241 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 11 Dec 2022 14:39:02 +0100 Subject: [PATCH 0496/1020] gpu: drm: apple: reenable support for {A,X}RGB2101010 Seems to work now with 'surface.colorspace = 12'. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 4 ++-- drivers/gpu/drm/apple/iomfb.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 0816c492519d7e..8c3d54b2f117e1 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -132,8 +132,8 @@ static const struct drm_plane_funcs apple_plane_funcs = { * advertise formats without alpha. */ static const u32 dcp_formats[] = { - // DRM_FORMAT_XRGB2101010, - // DRM_FORMAT_ARGB2101010, + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_ARGB2101010, DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_XBGR8888, diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 07a1179f75bc38..f8f202acd51a77 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -1503,7 +1503,7 @@ static u8 drm_format_to_colorspace(u32 drm) case DRM_FORMAT_ARGB2101010: case DRM_FORMAT_XRGB2101010: - return 2; + return 12; } return 1; From ce9a9ce26f2b2c72d1532f09db21703ad066977c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 11 Dec 2022 23:48:37 +0100 Subject: [PATCH 0497/1020] gpu: drm: apple: Add show_notch module parameter Can be used on devices with camera notch to use the full display height and thus show the notch. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index c62b814045f5a4..9fca0a79ac147e 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,10 @@ #define DCP_BOOT_TIMEOUT msecs_to_jiffies(1000) +static bool show_notch; +module_param(show_notch, bool, 0644); +MODULE_PARM_DESC(show_notch, "Use the full display height and shows the notch"); + /* HACK: moved here to avoid circular dependency between apple_drv and dcp */ void dcp_drm_crtc_vblank(struct apple_crtc *crtc) { @@ -402,8 +407,10 @@ static int dcp_platform_probe(struct platform_device *pdev) of_platform_default_populate(dev->of_node, NULL, dev); - ret = of_property_read_u32(dev->of_node, "apple,notch-height", - &dcp->notch_height); + if (!show_notch) + ret = of_property_read_u32(dev->of_node, "apple,notch-height", + &dcp->notch_height); + if (dcp->notch_height > MAX_NOTCH_HEIGHT) dcp->notch_height = MAX_NOTCH_HEIGHT; if (dcp->notch_height > 0) From 54630ef4938c5a94811b415df4a70be034485c73 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 13 Dec 2022 00:38:56 +0100 Subject: [PATCH 0498/1020] Revert "gpu: drm: apple: reenable support for {A,X}RGB2101010" This reverts commit d39542179a8f5a5d2e80eebf77bc739857a1051c. 'w30r' is a wide gammut mode. As long as the display is SDR DCP will end up displaying picture correctly but on HDR displays like the display in the Macbook Pro 14"/16" (2021) or external displays with HDR EOTF the picture has oversaturated / black colors. The non-wide gammut 10-bit per component pixelformats "l10r" and "R10k" are not supported. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 4 ++-- drivers/gpu/drm/apple/iomfb.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 8c3d54b2f117e1..0816c492519d7e 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -132,8 +132,8 @@ static const struct drm_plane_funcs apple_plane_funcs = { * advertise formats without alpha. */ static const u32 dcp_formats[] = { - DRM_FORMAT_XRGB2101010, - DRM_FORMAT_ARGB2101010, + // DRM_FORMAT_XRGB2101010, + // DRM_FORMAT_ARGB2101010, DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_XBGR8888, diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index f8f202acd51a77..07a1179f75bc38 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -1503,7 +1503,7 @@ static u8 drm_format_to_colorspace(u32 drm) case DRM_FORMAT_ARGB2101010: case DRM_FORMAT_XRGB2101010: - return 12; + return 2; } return 1; From c275feeca20c312ca714eab6e2c6c1c07c43a051 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 26 Dec 2022 00:26:05 +0900 Subject: [PATCH 0499/1020] drm/apple: Enable 10-bit mode & set colorspace to native This works on both 8-bit and 10-bit modes without any weirdness, and gives us the native colorspace without any conversion. Color correction should probably be handled in software anyway. However, we need to use surface 1 (at least on t600x), since 0 seems stuck in bg-sRGB mode for some reason... Signed-off-by: Hector Martin --- drivers/gpu/drm/apple/apple_drv.c | 4 ++-- drivers/gpu/drm/apple/iomfb.c | 24 ++++-------------------- drivers/gpu/drm/apple/iomfb.h | 11 +++++++++++ 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 0816c492519d7e..8c3d54b2f117e1 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -132,8 +132,8 @@ static const struct drm_plane_funcs apple_plane_funcs = { * advertise formats without alpha. */ static const u32 dcp_formats[] = { - // DRM_FORMAT_XRGB2101010, - // DRM_FORMAT_ARGB2101010, + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_ARGB2101010, DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_XBGR8888, diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 07a1179f75bc38..6188a37035a662 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -1492,23 +1492,6 @@ static u32 drm_format_to_dcp(u32 drm) return 0; } -static u8 drm_format_to_colorspace(u32 drm) -{ - switch (drm) { - case DRM_FORMAT_XRGB8888: - case DRM_FORMAT_ARGB8888: - case DRM_FORMAT_XBGR8888: - case DRM_FORMAT_ABGR8888: - return 1; - - case DRM_FORMAT_ARGB2101010: - case DRM_FORMAT_XRGB2101010: - return 2; - } - - return 1; -} - int dcp_get_modes(struct drm_connector *connector) { struct apple_connector *apple_connector = to_apple_connector(connector); @@ -1630,7 +1613,8 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) for (l = 0; l < SWAP_SURFACES; l++) req->surf_null[l] = true; - l = 0; + // Surface 0 has limitations at least on t600x. + l = 1; for_each_oldnew_plane_in_state(state, plane, old_state, new_state, plane_idx) { struct drm_framebuffer *fb = new_state->fb; struct drm_gem_dma_object *obj; @@ -1697,8 +1681,8 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) req->surf[l] = (struct dcp_surface){ .opaque = opaque, .format = drm_format_to_dcp(fb->format->format), - .xfer_func = 13, - .colorspace = drm_format_to_colorspace(fb->format->format), + .xfer_func = DCP_XFER_FUNC_SDR, + .colorspace = DCP_COLORSPACE_NATIVE, .stride = fb->pitches[0], .width = fb->width, .height = fb->height, diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index 083ebd4e0be33f..90b6668b075000 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -74,6 +74,17 @@ enum iomfb_property_id { #define SWAP_SURFACES 4 #define MAX_PLANES 3 +enum dcp_colorspace { + DCP_COLORSPACE_BG_SRGB = 0, + DCP_COLORSPACE_BG_BT2020 = 9, + DCP_COLORSPACE_NATIVE = 12, +}; + +enum dcp_xfer_func { + DCP_XFER_FUNC_SDR = 13, + DCP_XFER_FUNC_HDR = 16, +}; + struct dcp_iouserclient { /* Handle for the IOUserClient. macOS sets this to a kernel VA. */ u64 handle; From a9a9c6bf67e12784e7a29e823b23ac0d8a6874e8 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 31 Dec 2022 14:53:59 +0100 Subject: [PATCH 0500/1020] gpu: drm: apple: Clear all surfaces on startup With "drm/apple: Enable 10-bit mode & set colorspace to native" kernel log messages are shown in an Apple logo shaped region in the middle of the display when using BGRA. The field currently identified as "opaque" is mislabeled and has to be investigated further. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp-internal.h | 3 +++ drivers/gpu/drm/apple/iomfb.c | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 18969ab18e8319..8ca2324cb038c5 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -141,6 +141,9 @@ struct apple_dcp { /* eDP display without DP-HDMI conversion */ bool main_display; + /* clear all surfaces on init */ + bool surfaces_cleared; + /* panel has a mini-LED backllight */ bool has_mini_led; diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 6188a37035a662..b58bd0097f4155 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -1613,6 +1613,15 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) for (l = 0; l < SWAP_SURFACES; l++) req->surf_null[l] = true; + /* + * Clear all surfaces on startup. The boot framebuffer in surface 0 + * sticks around. + */ + if (!dcp->surfaces_cleared) { + req->swap.swap_enabled = DCP_REMOVE_LAYERS | 0xF; + dcp->surfaces_cleared = true; + } + // Surface 0 has limitations at least on t600x. l = 1; for_each_oldnew_plane_in_state(state, plane, old_state, new_state, plane_idx) { From 48bf6546809fb24db6e05933e663ab0c2c70dc9c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 2 Jan 2023 23:37:51 +0100 Subject: [PATCH 0501/1020] drm/apple: Update swap handling - opaque -> is_premultiplied - swap_enabled BIT(31) seems to be update background with dcp_swap.bg_color - add unused fields is_tearing_allowed, ycbcr_matrix, protection_opts, unk_num, unk_denom Changes: use is_premultiplied only for XRGB8/XBGR8, Update background only when necessary. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 37 +++++++++++++++++++++-------------- drivers/gpu/drm/apple/iomfb.h | 23 ++++++++++------------ 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index b58bd0097f4155..92d366e6be558b 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -1062,9 +1062,9 @@ void dcp_poweroff(struct platform_device *pdev) // clear surfaces memset(&dcp->swap, 0, sizeof(dcp->swap)); - dcp->swap.swap.swap_enabled = DCP_REMOVE_LAYERS | 0x7; - dcp->swap.swap.swap_completed = DCP_REMOVE_LAYERS | 0x7; - dcp->swap.swap.unk_10c = 0xFF000000; + dcp->swap.swap.swap_enabled = + dcp->swap.swap.swap_completed = IOMFB_SET_BACKGROUND | 0xF; + dcp->swap.swap.bg_color = 0xFF000000; /* * Turn off the backlight. This matters because the DCP's idea of @@ -1618,7 +1618,8 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) * sticks around. */ if (!dcp->surfaces_cleared) { - req->swap.swap_enabled = DCP_REMOVE_LAYERS | 0xF; + req->swap.swap_enabled = IOMFB_SET_BACKGROUND | 0xF; + req->swap.bg_color = 0xFF000000; dcp->surfaces_cleared = true; } @@ -1628,7 +1629,7 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) struct drm_framebuffer *fb = new_state->fb; struct drm_gem_dma_object *obj; struct drm_rect src_rect; - bool opaque = false; + bool is_premultiplied = false; /* skip planes not for this crtc */ if (old_state->crtc != crtc && new_state->crtc != crtc) @@ -1659,18 +1660,21 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) } if (!new_state->fb) { - if (old_state->fb) - req->swap.swap_enabled |= DCP_REMOVE_LAYERS; - l += 1; continue; } req->surf_null[l] = false; has_surface = 1; - if (!fb->format->has_alpha || - new_state->plane->type == DRM_PLANE_TYPE_PRIMARY) - opaque = true; + /* + * DCP doesn't support XBGR8 / XRGB8 natively. Blending as + * pre-multiplied alpha with a black background can be used as + * workaround for the bottommost plane. + */ + if (fb->format->format == DRM_FORMAT_XRGB8888 || + fb->format->format == DRM_FORMAT_XBGR8888) + is_premultiplied = true; + drm_rect_fp_to_int(&src_rect, &new_state->src); req->swap.src_rect[l] = drm_to_dcp_rect(&src_rect); @@ -1688,7 +1692,7 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) req->surf_iova[l] = obj->dma_addr + fb->offsets[0]; req->surf[l] = (struct dcp_surface){ - .opaque = opaque, + .is_premultiplied = is_premultiplied, .format = drm_format_to_dcp(fb->format->format), .xfer_func = DCP_XFER_FUNC_SDR, .colorspace = DCP_COLORSPACE_NATIVE, @@ -1709,9 +1713,6 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) l += 1; } - /* These fields should be set together */ - req->swap.swap_completed = req->swap.swap_enabled; - if (modeset) { struct dcp_display_mode *mode; struct dcp_wait_cookie *cookie; @@ -1772,9 +1773,15 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) return; } + /* Set black background */ + req->swap.swap_enabled |= IOMFB_SET_BACKGROUND; + req->swap.bg_color = 0xFF000000; req->clear = 1; } + /* These fields should be set together */ + req->swap.swap_completed = req->swap.swap_enabled; + /* update brightness if changed */ if (dcp->brightness.update) { req->swap.bl_unk = 1; diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index 90b6668b075000..a7e9b62425b2c4 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -102,12 +102,9 @@ struct dcp_rect { } __packed; /* - * Set in the swap_{enabled,completed} field to remove missing - * layers. Without this flag, the DCP will assume missing layers have - * not changed since the previous frame and will preserve their - * content. - */ -#define DCP_REMOVE_LAYERS BIT(31) + * Update background color to struct dcp_swap.bg_color + */ +#define IOMFB_SET_BACKGROUND BIT(31) struct dcp_swap { u64 ts1; @@ -126,7 +123,7 @@ struct dcp_swap { u32 swap_enabled; u32 swap_completed; - u32 unk_10c; + u32 bg_color; u8 unk_110[0x1b8]; u32 unk_2c8; u8 unk_2cc[0x14]; @@ -160,12 +157,12 @@ struct dcp_component_types { /* Information describing a surface */ struct dcp_surface { u8 is_tiled; - u8 unk_1; - u8 opaque; /** ignore alpha, also required YUV overlays */ + u8 is_tearing_allowed; + u8 is_premultiplied; u32 plane_cnt; u32 plane_cnt2; u32 format; /* DCP fourcc */ - u32 unk_f; + u32 ycbcr_matrix; u8 xfer_func; u8 colorspace; u32 stride; @@ -176,8 +173,7 @@ struct dcp_surface { u32 width; u32 height; u32 buf_size; - u32 unk_2d; - u32 unk_31; + u64 protection_opts; u32 surface_id; struct dcp_component_types comp_types[MAX_PLANES]; u64 has_comp; @@ -185,7 +181,8 @@ struct dcp_surface { u64 has_planes; u32 compression_info[MAX_PLANES][13]; u64 has_compr_info; - u64 unk_1f5; + u32 unk_num; + u32 unk_denom; u8 padding[7]; } __packed; From a6036117a9e3ce79ad07d6357ae776d149f6058b Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 22 Dec 2022 23:58:44 +0100 Subject: [PATCH 0502/1020] gpu: drm: apple: Use drm_aperture_remove_conflicting_framebuffers This does not seem to be as racy as drm_aperture_remove_framebuffers() and seems to reliably takes over simpledrm's device node. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 53 ++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 8c3d54b2f117e1..847aa7e63c290a 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -339,10 +340,45 @@ static int apple_probe_per_dcp(struct device *dev, return drm_connector_attach_encoder(&connector->base, encoder); } +static int apple_get_fb_resource(struct device *dev, const char *name, + struct resource *fb_r) +{ + int idx, ret = -ENODEV; + struct device_node *node; + + idx = of_property_match_string(dev->of_node, "memory-region-names", name); + + node = of_parse_phandle(dev->of_node, "memory-region", idx); + if (!node) { + dev_err(dev, "reserved-memory node '%s' not found\n", name); + return -ENODEV; + } + + if (!of_device_is_available(node)) { + dev_err(dev, "reserved-memory node '%s' is unavailable\n", name); + goto err; + } + + if (!of_device_is_compatible(node, "framebuffer")) { + dev_err(dev, "reserved-memory node '%s' is incompatible\n", + node->full_name); + goto err; + } + + ret = of_address_to_resource(node, 0, fb_r); + +err: + of_node_put(node); + return ret; +} + + static int apple_platform_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct apple_drm_private *apple; + struct resource fb_r; + resource_size_t fb_size; struct platform_device *dcp[MAX_COPROCESSORS]; int ret, nr_dcp, i; @@ -380,6 +416,18 @@ static int apple_platform_probe(struct platform_device *pdev) if (ret) return ret; + ret = apple_get_fb_resource(dev, "framebuffer", &fb_r); + if (ret) + return ret; + + fb_size = fb_r.end - fb_r.start + 1; + ret = drm_aperture_remove_conflicting_framebuffers(fb_r.start, fb_size, + false, &apple_drm_driver); + if (ret) { + dev_err(dev, "Failed remove fb: %d\n", ret); + return ret; + } + apple = devm_drm_dev_alloc(dev, &apple_drm_driver, struct apple_drm_private, drm); if (IS_ERR(apple)) @@ -425,11 +473,6 @@ static int apple_platform_probe(struct platform_device *pdev) drm_mode_config_reset(&apple->drm); - // remove before registering our DRM device - ret = drm_aperture_remove_framebuffers(false, &apple_drm_driver); - if (ret) - return ret; - ret = drm_dev_register(&apple->drm, 0); if (ret) goto err_unload; From c8edccb8e13aea35d1f43facbd5aab593c41ebfd Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 31 Dec 2022 15:27:07 +0100 Subject: [PATCH 0503/1020] drm/apple: Use drm_module_platform_driver This check for the "nomodeset" kernel command line parameter in its register method. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 3 ++- drivers/gpu/drm/apple/dcp.c | 3 ++- drivers/gpu/drm/apple/dummy-piodma.c | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 847aa7e63c290a..318f6896d4f427 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -535,7 +536,7 @@ static struct platform_driver apple_platform_driver = { .remove = apple_platform_remove, }; -module_platform_driver(apple_platform_driver); +drm_module_platform_driver(apple_platform_driver); MODULE_AUTHOR("Alyssa Rosenzweig "); MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 9fca0a79ac147e..fbbc79ab1e8c14 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -539,7 +540,7 @@ static struct platform_driver apple_platform_driver = { }, }; -module_platform_driver(apple_platform_driver); +drm_module_platform_driver(apple_platform_driver); MODULE_AUTHOR("Alyssa Rosenzweig "); MODULE_DESCRIPTION("Apple Display Controller DRM driver"); diff --git a/drivers/gpu/drm/apple/dummy-piodma.c b/drivers/gpu/drm/apple/dummy-piodma.c index 3d4454df4a25da..daf4327592a041 100644 --- a/drivers/gpu/drm/apple/dummy-piodma.c +++ b/drivers/gpu/drm/apple/dummy-piodma.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2021 Alyssa Rosenzweig */ +#include + #include #include #include @@ -24,7 +26,7 @@ static struct platform_driver dcp_piodma_platform_driver = { }, }; -module_platform_driver(dcp_piodma_platform_driver); +drm_module_platform_driver(dcp_piodma_platform_driver); MODULE_AUTHOR("Alyssa Rosenzweig "); MODULE_DESCRIPTION("[HACK] Apple DCP PIODMA shim"); From 7571917b20e715d3f86b245638d36d7e2dc7e576 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 2 Jan 2023 19:32:07 +0100 Subject: [PATCH 0504/1020] drm/apple: Allocate drm objects according to drm's expectations drm's documentaion explicitly tells us not to use devm_kzalloc(). drm device structures might out live the device when they are in use by userspace while the device vanishes. --- drivers/gpu/drm/apple/apple_drv.c | 43 +++++++++++++++++++++---------- drivers/gpu/drm/apple/dcp.h | 7 +++++ 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 318f6896d4f427..15400b5bf6e50e 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -113,10 +113,16 @@ static const struct drm_plane_helper_funcs apple_plane_helper_funcs = { .atomic_update = apple_plane_atomic_update, }; +static void apple_plane_cleanup(struct drm_plane *plane) +{ + drm_plane_cleanup(plane); + kfree(plane); +} + static const struct drm_plane_funcs apple_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .destroy = drm_plane_cleanup, + .destroy = apple_plane_cleanup, .reset = drm_atomic_helper_plane_reset, .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, @@ -154,7 +160,7 @@ static struct drm_plane *apple_plane_init(struct drm_device *dev, int ret; struct drm_plane *plane; - plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL); + plane = kzalloc(sizeof(*plane), GFP_KERNEL); ret = drm_universal_plane_init(dev, plane, possible_crtcs, &apple_plane_funcs, @@ -247,11 +253,16 @@ static void dcp_atomic_commit_tail(struct drm_atomic_state *old_state) drm_atomic_helper_cleanup_planes(dev, old_state); } +static void apple_crtc_cleanup(struct drm_crtc *crtc) +{ + drm_crtc_cleanup(crtc); + kfree(to_apple_crtc(crtc)); +} static const struct drm_crtc_funcs apple_crtc_funcs = { .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, - .destroy = drm_crtc_cleanup, + .destroy = apple_crtc_cleanup, .page_flip = drm_atomic_helper_page_flip, .reset = drm_atomic_helper_crtc_reset, .set_config = drm_atomic_helper_set_config, @@ -267,9 +278,15 @@ static const struct drm_mode_config_helper_funcs apple_mode_config_helpers = { .atomic_commit_tail = dcp_atomic_commit_tail, }; +static void appledrm_connector_cleanup(struct drm_connector *connector) +{ + drm_connector_cleanup(connector); + kfree(to_apple_connector(connector)); +} + static const struct drm_connector_funcs apple_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = drm_connector_cleanup, + .destroy = appledrm_connector_cleanup, .reset = drm_atomic_helper_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, @@ -297,7 +314,7 @@ static int apple_probe_per_dcp(struct device *dev, { struct apple_crtc *crtc; struct apple_connector *connector; - struct drm_encoder *encoder; + struct apple_encoder *enc; struct drm_plane *primary; int ret; @@ -306,7 +323,7 @@ static int apple_probe_per_dcp(struct device *dev, if (IS_ERR(primary)) return PTR_ERR(primary); - crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL); + crtc = kzalloc(sizeof(*crtc), GFP_KERNEL); ret = drm_crtc_init_with_planes(drm, &crtc->base, primary, NULL, &apple_crtc_funcs, NULL); if (ret) @@ -314,13 +331,13 @@ static int apple_probe_per_dcp(struct device *dev, drm_crtc_helper_add(&crtc->base, &apple_crtc_helper_funcs); - encoder = devm_kzalloc(dev, sizeof(*encoder), GFP_KERNEL); - encoder->possible_crtcs = drm_crtc_mask(&crtc->base); - ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); - if (ret) - return ret; + enc = drmm_simple_encoder_alloc(drm, struct apple_encoder, base, + DRM_MODE_ENCODER_TMDS); + if (IS_ERR(enc)) + return PTR_ERR(enc); + enc->base.possible_crtcs = drm_crtc_mask(&crtc->base); - connector = devm_kzalloc(dev, sizeof(*connector), GFP_KERNEL); + connector = kzalloc(sizeof(*connector), GFP_KERNEL); drm_connector_helper_add(&connector->base, &apple_connector_helper_funcs); @@ -338,7 +355,7 @@ static int apple_probe_per_dcp(struct device *dev, crtc->dcp = dcp; dcp_link(dcp, crtc, connector); - return drm_connector_attach_encoder(&connector->base, encoder); + return drm_connector_attach_encoder(&connector->base, &enc->base); } static int apple_get_fb_resource(struct device *dev, const char *name, diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h index fb4397e7b390fe..f4476f4acaf265 100644 --- a/drivers/gpu/drm/apple/dcp.h +++ b/drivers/gpu/drm/apple/dcp.h @@ -5,6 +5,7 @@ #define __APPLE_DCP_H__ #include +#include #include #include "dcp-internal.h" @@ -35,6 +36,12 @@ struct apple_connector { #define to_apple_connector(x) container_of(x, struct apple_connector, base) +struct apple_encoder { + struct drm_encoder base; +}; + +#define to_apple_encoder(x) container_of(x, struct apple_encoder, base) + void dcp_poweroff(struct platform_device *pdev); void dcp_poweron(struct platform_device *pdev); int dcp_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state); From ed6059b01875c52e958c78cff8b17fea1d97310c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 29 Dec 2022 21:05:40 +0100 Subject: [PATCH 0505/1020] gpu: drm: apple: Use components to avoid deferred probing There was a report of a race between DRM device registration (and removal of the simpledrm device) and GDM startup. The component based device binding ensures that all necessary devices are bind in the probe method of the last missing component. Technically the piodma-mapper should be a component of dcp but since it is only used for its iommu it can be a component of the display subsystem. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 203 +++++++++++++++++++-------- drivers/gpu/drm/apple/dcp-internal.h | 1 - drivers/gpu/drm/apple/dcp.c | 105 +++++++++----- drivers/gpu/drm/apple/dummy-piodma.c | 39 ++++- 4 files changed, 250 insertions(+), 98 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 15400b5bf6e50e..eab93310d8bd0a 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -7,8 +7,9 @@ * Copyright (C) 2014 Endless Mobile */ -#include +#include #include +#include #include #include @@ -390,46 +391,55 @@ static int apple_get_fb_resource(struct device *dev, const char *name, return ret; } +static const struct of_device_id apple_dcp_id_tbl[] = { + { .compatible = "apple,dcp" }, + {}, +}; -static int apple_platform_probe(struct platform_device *pdev) +static int apple_drm_init_dcp(struct device *dev) { - struct device *dev = &pdev->dev; - struct apple_drm_private *apple; - struct resource fb_r; - resource_size_t fb_size; + struct apple_drm_private *apple = dev_get_drvdata(dev); struct platform_device *dcp[MAX_COPROCESSORS]; - int ret, nr_dcp, i; - - for (nr_dcp = 0; nr_dcp < MAX_COPROCESSORS; ++nr_dcp) { - struct device_node *np; - struct device_link *dcp_link; + struct device_node *np; + int ret, num_dcp = 0; - np = of_parse_phandle(dev->of_node, "apple,coprocessors", - nr_dcp); - - if (!np) - break; + for_each_matching_node(np, apple_dcp_id_tbl) { + if (!of_device_is_available(np)) { + of_node_put(np); + continue; + } - dcp[nr_dcp] = of_find_device_by_node(np); + dcp[num_dcp] = of_find_device_by_node(np); + of_node_put(np); + if (!dcp[num_dcp]) + continue; - if (!dcp[nr_dcp]) - return -ENODEV; + ret = apple_probe_per_dcp(dev, &apple->drm, dcp[num_dcp], + num_dcp); + if (ret) + continue; - dcp_link = device_link_add(dev, &dcp[nr_dcp]->dev, - DL_FLAG_AUTOREMOVE_CONSUMER); - if (!dcp_link) { - dev_err(dev, "Failed to link to DCP %d device", nr_dcp); - return -EINVAL; - } + ret = dcp_start(dcp[num_dcp]); + if (ret) + continue; - if (dcp_link->supplier->links.status != DL_DEV_DRIVER_BOUND) - return -EPROBE_DEFER; + num_dcp++; } - /* Need at least 1 DCP for a display subsystem */ - if (nr_dcp < 1) + if (num_dcp < 1) return -ENODEV; + + return 0; +} + +static int apple_drm_init(struct device *dev) +{ + struct apple_drm_private *apple; + struct resource fb_r; + resource_size_t fb_size; + int ret; + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36)); if (ret) return ret; @@ -438,14 +448,6 @@ static int apple_platform_probe(struct platform_device *pdev) if (ret) return ret; - fb_size = fb_r.end - fb_r.start + 1; - ret = drm_aperture_remove_conflicting_framebuffers(fb_r.start, fb_size, - false, &apple_drm_driver); - if (ret) { - dev_err(dev, "Failed remove fb: %d\n", ret); - return ret; - } - apple = devm_drm_dev_alloc(dev, &apple_drm_driver, struct apple_drm_private, drm); if (IS_ERR(apple)) @@ -453,9 +455,21 @@ static int apple_platform_probe(struct platform_device *pdev) dev_set_drvdata(dev, apple); + ret = component_bind_all(dev, apple); + if (ret) + return ret; + + fb_size = fb_r.end - fb_r.start + 1; + ret = drm_aperture_remove_conflicting_framebuffers(fb_r.start, fb_size, + false, &apple_drm_driver); + if (ret) { + dev_err(dev, "Failed remove fb: %d\n", ret); + goto err_unbind; + } + ret = drmm_mode_config_init(&apple->drm); if (ret) - goto err_unload; + goto err_unbind; /* * IOMFB::UPPipeDCP_H13P::verify_surfaces produces the error "plane @@ -477,38 +491,110 @@ static int apple_platform_probe(struct platform_device *pdev) apple->drm.mode_config.funcs = &apple_mode_config_funcs; apple->drm.mode_config.helper_private = &apple_mode_config_helpers; - for (i = 0; i < nr_dcp; ++i) { - ret = apple_probe_per_dcp(dev, &apple->drm, dcp[i], i); - - if (ret) - goto err_unload; - - ret = dcp_start(dcp[i]); - - if (ret) - goto err_unload; - } + ret = apple_drm_init_dcp(dev); + if (ret) + goto err_unbind; drm_mode_config_reset(&apple->drm); ret = drm_dev_register(&apple->drm, 0); if (ret) - goto err_unload; + goto err_unbind; drm_fbdev_generic_setup(&apple->drm, 32); return 0; -err_unload: - drm_dev_put(&apple->drm); +err_unbind: + component_unbind_all(dev, NULL); return ret; } -static int apple_platform_remove(struct platform_device *pdev) +static void apple_drm_uninit(struct device *dev) { - struct apple_drm_private *apple = platform_get_drvdata(pdev); + struct apple_drm_private *apple = dev_get_drvdata(dev); drm_dev_unregister(&apple->drm); + drm_atomic_helper_shutdown(&apple->drm); + + component_unbind_all(dev, NULL); + + dev_set_drvdata(dev, NULL); +} + +static int apple_drm_bind(struct device *dev) +{ + return apple_drm_init(dev); +} + +static void apple_drm_unbind(struct device *dev) +{ + apple_drm_uninit(dev); +} + +const struct component_master_ops apple_drm_ops = { + .bind = apple_drm_bind, + .unbind = apple_drm_unbind, +}; + +static const struct of_device_id apple_component_id_tbl[] = { + { .compatible = "apple,dcp-piodma" }, + {}, +}; + +static int add_display_components(struct device *dev, + struct component_match **matchptr) +{ + struct device_node *np; + + for_each_matching_node(np, apple_component_id_tbl) { + if (of_device_is_available(np)) + drm_of_component_match_add(dev, matchptr, + component_compare_of, np); + of_node_put(np); + } + + return 0; +} + +static int add_dcp_components(struct device *dev, + struct component_match **matchptr) +{ + struct device_node *np; + int num = 0; + + for_each_matching_node(np, apple_dcp_id_tbl) { + if (of_device_is_available(np)) { + drm_of_component_match_add(dev, matchptr, + component_compare_of, np); + num++; + } + of_node_put(np); + } + + return num; +} + +static int apple_platform_probe(struct platform_device *pdev) +{ + struct device *mdev = &pdev->dev; + struct component_match *match = NULL; + int num_dcp; + + /* add PIODMA mapper components */ + add_display_components(mdev, &match); + + /* add DCP components, handle less than 1 as probe error */ + num_dcp = add_dcp_components(mdev, &match); + if (num_dcp < 1) + return -ENODEV; + + return component_master_add_with_match(mdev, &apple_drm_ops, match); +} + +static int apple_platform_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &apple_drm_ops); return 0; } @@ -524,14 +610,19 @@ static int apple_platform_suspend(struct device *dev) { struct apple_drm_private *apple = dev_get_drvdata(dev); - return drm_mode_config_helper_suspend(&apple->drm); + if (apple) + return drm_mode_config_helper_suspend(&apple->drm); + + return 0; } static int apple_platform_resume(struct device *dev) { struct apple_drm_private *apple = dev_get_drvdata(dev); - drm_mode_config_helper_resume(&apple->drm); + if (apple) + drm_mode_config_helper_resume(&apple->drm); + return 0; } diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 8ca2324cb038c5..ed11d38f80bb59 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -84,7 +84,6 @@ struct dcp_brightness { struct apple_dcp { struct device *dev; struct platform_device *piodma; - struct device_link *piodma_link; struct apple_rtkit *rtk; struct apple_crtc *crtc; struct apple_connector *connector; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index fbbc79ab1e8c14..c33376ac417f9e 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -1,21 +1,22 @@ // SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2021 Alyssa Rosenzweig */ +#include #include #include +#include +#include +#include +#include +#include #include #include #include -#include -#include #include -#include -#include -#include -#include +#include #include -#include -#include "linux/workqueue.h" +#include +#include #include #include @@ -376,33 +377,18 @@ static enum dcp_firmware_version dcp_check_firmware_version(struct device *dev) return DCP_FIRMWARE_UNKNOWN; } -static int dcp_platform_probe(struct platform_device *pdev) +static int dcp_comp_bind(struct device *dev, struct device *main, void *data) { - struct device *dev = &pdev->dev; struct device_node *panel_np; - struct apple_dcp *dcp; - enum dcp_firmware_version fw_compat; + struct apple_dcp *dcp = dev_get_drvdata(dev); u32 cpu_ctrl; int ret; - fw_compat = dcp_check_firmware_version(dev); - if (fw_compat == DCP_FIRMWARE_UNKNOWN) - return -ENODEV; - - dcp = devm_kzalloc(dev, sizeof(*dcp), GFP_KERNEL); - if (!dcp) - return -ENOMEM; - - dcp->fw_compat = fw_compat; - - platform_set_drvdata(pdev, dcp); - dcp->dev = dev; - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36)); if (ret) return ret; - dcp->coproc_reg = devm_platform_ioremap_resource_byname(pdev, "coproc"); + dcp->coproc_reg = devm_platform_ioremap_resource_byname(to_platform_device(dev), "coproc"); if (IS_ERR(dcp->coproc_reg)) return PTR_ERR(dcp->coproc_reg); @@ -453,22 +439,17 @@ static int dcp_platform_probe(struct platform_device *pdev) else dcp->connector_type = DRM_MODE_CONNECTOR_Unknown; + /* + * Components do not ensure the bind order of sub components but + * the piodma device is only used for its iommu. The iommu is fully + * initialized by the time dcp_piodma_probe() calls component_add(). + */ dcp->piodma = dcp_get_dev(dev, "apple,piodma-mapper"); if (!dcp->piodma) { dev_err(dev, "failed to find piodma\n"); return -ENODEV; } - dcp->piodma_link = device_link_add(dev, &dcp->piodma->dev, - DL_FLAG_AUTOREMOVE_CONSUMER); - if (!dcp->piodma_link) { - dev_err(dev, "Failed to link to piodma device"); - return -EINVAL; - } - - if (dcp->piodma_link->supplier->links.status != DL_DEV_DRIVER_BOUND) - return -EPROBE_DEFER; - ret = dcp_get_disp_regs(dcp); if (ret) { dev_err(dev, "failed to find display registers\n"); @@ -517,12 +498,57 @@ static int dcp_platform_probe(struct platform_device *pdev) * We need to shutdown DCP before tearing down the display subsystem. Otherwise * the DCP will crash and briefly flash a green screen of death. */ -static void dcp_platform_shutdown(struct platform_device *pdev) +static void dcp_comp_unbind(struct device *dev, struct device *main, void *data) { - struct apple_dcp *dcp = platform_get_drvdata(pdev); + struct apple_dcp *dcp = dev_get_drvdata(dev); - if (dcp->shmem) + if (dcp && dcp->shmem) iomfb_shutdown(dcp); + + platform_device_put(dcp->piodma); + dcp->piodma = NULL; + + devm_clk_put(dev, dcp->clk); + dcp->clk = NULL; +} + +static const struct component_ops dcp_comp_ops = { + .bind = dcp_comp_bind, + .unbind = dcp_comp_unbind, +}; + +static int dcp_platform_probe(struct platform_device *pdev) +{ + enum dcp_firmware_version fw_compat; + struct device *dev = &pdev->dev; + struct apple_dcp *dcp; + + fw_compat = dcp_check_firmware_version(dev); + if (fw_compat == DCP_FIRMWARE_UNKNOWN) + return -ENODEV; + + dcp = devm_kzalloc(dev, sizeof(*dcp), GFP_KERNEL); + if (!dcp) + return -ENOMEM; + + dcp->fw_compat = fw_compat; + dcp->dev = dev; + + platform_set_drvdata(pdev, dcp); + + return component_add(&pdev->dev, &dcp_comp_ops); +} + +static int dcp_platform_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dcp_comp_ops); + + return 0; +} + +static void dcp_platform_shutdown(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dcp_comp_ops); } static const struct of_device_id of_match[] = { @@ -533,6 +559,7 @@ MODULE_DEVICE_TABLE(of, of_match); static struct platform_driver apple_platform_driver = { .probe = dcp_platform_probe, + .remove = dcp_platform_remove, .shutdown = dcp_platform_shutdown, .driver = { .name = "apple-dcp", diff --git a/drivers/gpu/drm/apple/dummy-piodma.c b/drivers/gpu/drm/apple/dummy-piodma.c index daf4327592a041..ec5d4fecf356c0 100644 --- a/drivers/gpu/drm/apple/dummy-piodma.c +++ b/drivers/gpu/drm/apple/dummy-piodma.c @@ -3,13 +3,46 @@ #include -#include +#include #include +#include #include +static int dcp_piodma_comp_bind(struct device *dev, struct device *main, + void *data) +{ + return 0; +} + +static void dcp_piodma_comp_unbind(struct device *dev, struct device *main, + void *data) +{ + /* nothing to do */ +} + +static const struct component_ops dcp_piodma_comp_ops = { + .bind = dcp_piodma_comp_bind, + .unbind = dcp_piodma_comp_unbind, +}; static int dcp_piodma_probe(struct platform_device *pdev) { - return dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36)); + int ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36)); + if (ret) + return ret; + + return component_add(&pdev->dev, &dcp_piodma_comp_ops); +} + +static int dcp_piodma_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dcp_piodma_comp_ops); + + return 0; +} + +static void dcp_piodma_shutdown(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dcp_piodma_comp_ops); } static const struct of_device_id of_match[] = { @@ -20,6 +53,8 @@ MODULE_DEVICE_TABLE(of, of_match); static struct platform_driver dcp_piodma_platform_driver = { .probe = dcp_piodma_probe, + .remove = dcp_piodma_remove, + .shutdown = dcp_piodma_shutdown, .driver = { .name = "apple,dcp-piodma", .of_match_table = of_match, From e1eee44e2817313753f5117696f91e7aeaa34c78 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 29 Dec 2022 21:38:44 +0100 Subject: [PATCH 0506/1020] gpu: drm: apple: Wait for iomfb initialization Avoids "[drm] Cannot find any crtc or sizes" during fbdev initialization if a display is connected. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 19 ++++++++++++++++++- drivers/gpu/drm/apple/dcp-internal.h | 3 +++ drivers/gpu/drm/apple/dcp.c | 26 ++++++++++++++++++++++++++ drivers/gpu/drm/apple/dcp.h | 1 + drivers/gpu/drm/apple/iomfb.c | 5 +++-- 5 files changed, 51 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index eab93310d8bd0a..c1e34d7f9adea1 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -401,7 +402,8 @@ static int apple_drm_init_dcp(struct device *dev) struct apple_drm_private *apple = dev_get_drvdata(dev); struct platform_device *dcp[MAX_COPROCESSORS]; struct device_node *np; - int ret, num_dcp = 0; + u64 timeout; + int i, ret, num_dcp = 0; for_each_matching_node(np, apple_dcp_id_tbl) { if (!of_device_is_available(np)) { @@ -429,6 +431,21 @@ static int apple_drm_init_dcp(struct device *dev) if (num_dcp < 1) return -ENODEV; + timeout = get_jiffies_64() + msecs_to_jiffies(500); + + for (i = 0; i < num_dcp; ++i) { + u64 jiffies = get_jiffies_64(); + u64 wait = time_after_eq64(jiffies, timeout) ? + 0 : + timeout - jiffies; + ret = dcp_wait_ready(dcp[i], wait); + /* There is nothing we can do if a dcp/dcpext does not boot + * (successfully). Ignoring it should not do any harm now. + * Needs to reevaluated whenn adding dcpext support. + */ + if (ret) + dev_warn(dev, "DCP[%d] not ready: %d\n", i, ret); + } return 0; } diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index ed11d38f80bb59..85e9137bbdf10e 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -134,6 +134,9 @@ struct apple_dcp { bool valid_mode; struct dcp_set_digital_out_mode_req mode; + /* completion for active turning true */ + struct completion start_done; + /* Is the DCP booted? */ bool active; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index c33376ac417f9e..4776122b7b0e67 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -116,6 +116,7 @@ static void dcp_rtk_crashed(void *cookie) dcp->connector->connected = 0; schedule_work(&dcp->connector->hotplug_wq); } + complete(&dcp->start_done); } static int dcp_rtk_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr) @@ -247,6 +248,8 @@ int dcp_start(struct platform_device *pdev) struct apple_dcp *dcp = platform_get_drvdata(pdev); int ret; + init_completion(&dcp->start_done); + /* start RTKit endpoints */ ret = iomfb_start_rtkit(dcp); if (ret) @@ -256,6 +259,29 @@ int dcp_start(struct platform_device *pdev) } EXPORT_SYMBOL(dcp_start); +int dcp_wait_ready(struct platform_device *pdev, u64 timeout) +{ + struct apple_dcp *dcp = platform_get_drvdata(pdev); + int ret; + + if (dcp->crashed) + return -ENODEV; + if (dcp->active) + return 0; + if (timeout <= 0) + return -ETIMEDOUT; + + ret = wait_for_completion_timeout(&dcp->start_done, timeout); + if (ret < 0) + return ret; + + if (dcp->crashed) + return -ENODEV; + + return dcp->active ? 0 : -ETIMEDOUT; +} +EXPORT_SYMBOL(dcp_wait_ready); + static void dcp_work_register_backlight(struct work_struct *work) { int ret; diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h index f4476f4acaf265..2011d27e809d53 100644 --- a/drivers/gpu/drm/apple/dcp.h +++ b/drivers/gpu/drm/apple/dcp.h @@ -49,6 +49,7 @@ int dcp_get_connector_type(struct platform_device *pdev); void dcp_link(struct platform_device *pdev, struct apple_crtc *apple, struct apple_connector *connector); int dcp_start(struct platform_device *pdev); +int dcp_wait_ready(struct platform_device *pdev, u64 timeout); void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state); bool dcp_is_initialized(struct platform_device *pdev); void apple_crtc_vblank(struct apple_crtc *apple); diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 92d366e6be558b..4f1161b7da5d15 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -1810,13 +1810,14 @@ static void res_is_main_display(struct apple_dcp *dcp, void *out, void *cookie) dcp->main_display = result != 0; - dcp->active = true; - connector = dcp->connector; if (connector) { connector->connected = dcp->nr_modes > 0; schedule_work(&connector->hotplug_wq); } + + dcp->active = true; + complete(&dcp->start_done); } static void init_3(struct apple_dcp *dcp, void *out, void *cookie) From db69572aee67b6ba3c8077792bf37e05ec56da36 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 8 Jan 2023 21:24:51 +0100 Subject: [PATCH 0507/1020] drm/apple: simplify IOMFB_THUNK_INOUT Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 4f1161b7da5d15..ed126b10b209c2 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -262,20 +262,22 @@ static void dcp_push(struct apple_dcp *dcp, bool oob, enum dcpep_method method, cb, cookie); \ } -#define IOMFB_THUNK_INOUT(name, T_in, T_out) \ - static void iomfb_ ## name(struct apple_dcp *dcp, bool oob, T_in *data, \ - dcp_callback_t cb, void *cookie) \ - { \ - dcp_push(dcp, oob, iomfbep_ ## name, sizeof(T_in), sizeof(T_out), \ - data, cb, cookie); \ +#define IOMFB_THUNK_INOUT(name) \ + static void iomfb_ ## name(struct apple_dcp *dcp, bool oob, \ + struct iomfb_ ## name ## _req *data, \ + dcp_callback_t cb, void *cookie) \ + { \ + dcp_push(dcp, oob, iomfbep_ ## name, \ + sizeof(struct iomfb_ ## name ## _req), \ + sizeof(struct iomfb_ ## name ## _resp), \ + data, cb, cookie); \ } DCP_THUNK_OUT(iomfb_a131_pmu_service_matched, iomfbep_a131_pmu_service_matched, u32); DCP_THUNK_OUT(iomfb_a132_backlight_service_matched, iomfbep_a132_backlight_service_matched, u32); DCP_THUNK_OUT(iomfb_a358_vi_set_temperature_hint, iomfbep_a358_vi_set_temperature_hint, u32); -IOMFB_THUNK_INOUT(get_color_remap_mode, struct iomfb_get_color_remap_mode_req, - struct iomfb_get_color_remap_mode_resp); +IOMFB_THUNK_INOUT(get_color_remap_mode); DCP_THUNK_INOUT(dcp_swap_submit, dcpep_swap_submit, struct dcp_swap_submit_req, struct dcp_swap_submit_resp); From 9ad14f99bab04c69bd8d0f5a66e86eada1baca88 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Wed, 15 Feb 2023 20:57:56 +0900 Subject: [PATCH 0508/1020] drm/apple: Fix parse_string() memory leaks Signed-off-by: Asahi Lina --- drivers/gpu/drm/apple/parser.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index 678c0e42e10682..1d85d76ae0e16f 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -243,6 +243,9 @@ static int parse_dimension(struct dcp_parse_ctx *handle, struct dimension *dim) else skip(it.handle); + if (!IS_ERR_OR_NULL(key)) + kfree(key); + if (ret) return ret; } @@ -298,6 +301,9 @@ static int parse_color_modes(struct dcp_parse_ctx *handle, s64 *preferred_id) else skip(it.handle); + if (!IS_ERR_OR_NULL(key)) + kfree(key); + if (ret) return ret; } @@ -381,6 +387,9 @@ static int parse_mode(struct dcp_parse_ctx *handle, else skip(it.handle); + if (!IS_ERR_OR_NULL(key)) + kfree(key); + if (ret) return ret; } @@ -511,6 +520,9 @@ int parse_display_attributes(struct dcp_parse_ctx *handle, int *width_mm, else skip(it.handle); + if (!IS_ERR_OR_NULL(key)) + kfree(key); + if (ret) return ret; } From 29746576777daa32171b54611591a6dc6c4ef81d Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Wed, 15 Feb 2023 20:57:47 +0900 Subject: [PATCH 0509/1020] drm/apple: Fix bad error return Signed-off-by: Asahi Lina --- drivers/gpu/drm/apple/parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index 1d85d76ae0e16f..5665c2ba19682f 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -229,7 +229,7 @@ static int parse_dimension(struct dcp_parse_ctx *handle, struct dimension *dim) char *key = parse_string(it.handle); if (IS_ERR(key)) - ret = PTR_ERR(handle); + ret = PTR_ERR(key); else if (!strcmp(key, "Active")) ret = parse_int(it.handle, &dim->active); else if (!strcmp(key, "Total")) From f67a069da06a07c9334471b5c82037a9e6a19341 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 22 Jan 2023 20:16:28 +0100 Subject: [PATCH 0510/1020] drm/apple: Set backlight level indirectly if no mode is set Fixes following warning when systemd-backlight restores the backlight level on boot before a mode is set: Call trace: drm_atomic_helper_crtc_duplicate_state+0x58/0x74 drm_atomic_get_crtc_state+0x84/0x120 dcp_set_brightness+0xd8/0x21c [apple_dcp] backlight_device_set_brightness+0x78/0x130 ... Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp_backlight.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp_backlight.c b/drivers/gpu/drm/apple/dcp_backlight.c index 42b1097eaa0180..45827fe6041a27 100644 --- a/drivers/gpu/drm/apple/dcp_backlight.c +++ b/drivers/gpu/drm/apple/dcp_backlight.c @@ -165,21 +165,30 @@ static int drm_crtc_set_brightness(struct drm_crtc *crtc, static int dcp_set_brightness(struct backlight_device *bd) { - int ret; + int ret = 0; struct apple_dcp *dcp = bl_get_data(bd); struct drm_modeset_acquire_ctx ctx; if (bd->props.state & BL_CORE_SUSPENDED) return 0; - if (!dcp->crtc) - return -EAGAIN; + DRM_MODESET_LOCK_ALL_BEGIN(dcp->crtc->base.dev, ctx, 0, ret); dcp->brightness.dac = calculate_dac(dcp, bd->props.brightness); dcp->brightness.update = true; - DRM_MODESET_LOCK_ALL_BEGIN(dcp->crtc->base.dev, ctx, 0, ret); + /* + * Do not actively try to change brightness if no mode is set. + * TODO: should this be reflected the in backlight's power property? + * defer this hopefully until it becomes irrelevant due to proper + * drm integrated backlight handling + */ + if (!dcp->valid_mode) + goto out; + ret = drm_crtc_set_brightness(&dcp->crtc->base, &ctx); + +out: DRM_MODESET_LOCK_ALL_END(dcp->crtc->base.dev, ctx, ret); return ret; From 241541681b71312abbbdc2df94ce9f0b79fe1dff Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 22 Jan 2023 19:43:35 +0100 Subject: [PATCH 0511/1020] drm/apple: Use backlight_get_brightness() Backlight drivers are expected to use this instead of accessing backlight properties. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp_backlight.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp_backlight.c b/drivers/gpu/drm/apple/dcp_backlight.c index 45827fe6041a27..d063ecd7ad2068 100644 --- a/drivers/gpu/drm/apple/dcp_backlight.c +++ b/drivers/gpu/drm/apple/dcp_backlight.c @@ -168,13 +168,11 @@ static int dcp_set_brightness(struct backlight_device *bd) int ret = 0; struct apple_dcp *dcp = bl_get_data(bd); struct drm_modeset_acquire_ctx ctx; - - if (bd->props.state & BL_CORE_SUSPENDED) - return 0; + int brightness = backlight_get_brightness(bd); DRM_MODESET_LOCK_ALL_BEGIN(dcp->crtc->base.dev, ctx, 0, ret); - dcp->brightness.dac = calculate_dac(dcp, bd->props.brightness); + dcp->brightness.dac = calculate_dac(dcp, brightness); dcp->brightness.update = true; /* From f6dfdbd63fd77207ae75e5b68e20a21408248850 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 26 Mar 2023 15:56:03 +0200 Subject: [PATCH 0512/1020] drm/apple: Move panel options to its own sub-struct Fixes overwriting the panel's physical dimensions on poweroff/sleep. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp-internal.h | 16 +++++++++++++--- drivers/gpu/drm/apple/dcp.c | 21 ++++++++++++++------- drivers/gpu/drm/apple/iomfb.c | 2 +- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 85e9137bbdf10e..b34294816ec1ff 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -80,6 +80,16 @@ struct dcp_brightness { bool update; }; +/** laptop/AiO integrated panel parameters from DT */ +struct dcp_panel { + /// panel width in millimeter + int width_mm; + /// panel height in millimeter + int height_mm; + /// panel has a mini-LED backllight + bool has_mini_led; +}; + /* TODO: move IOMFB members to its own struct */ struct apple_dcp { struct device *dev; @@ -146,9 +156,6 @@ struct apple_dcp { /* clear all surfaces on init */ bool surfaces_cleared; - /* panel has a mini-LED backllight */ - bool has_mini_led; - /* Modes valid for the connected display */ struct dcp_display_mode *modes; unsigned int nr_modes; @@ -173,6 +180,9 @@ struct apple_dcp { /* Workqueue for updating the initial initial brightness */ struct work_struct bl_register_wq; struct mutex bl_register_mutex; + + /* integrated panel if present */ + struct dcp_panel panel; }; int dcp_backlight_register(struct apple_dcp *dcp); diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 4776122b7b0e67..c3d9a8dc491da4 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -56,14 +56,21 @@ void dcp_drm_crtc_vblank(struct apple_crtc *crtc) void dcp_set_dimensions(struct apple_dcp *dcp) { int i; + int width_mm = dcp->width_mm; + int height_mm = dcp->height_mm; + + if (width_mm == 0 || height_mm == 0) { + width_mm = dcp->panel.width_mm; + height_mm = dcp->panel.height_mm; + } /* Set the connector info */ if (dcp->connector) { struct drm_connector *connector = &dcp->connector->base; mutex_lock(&connector->dev->mode_config.mutex); - connector->display_info.width_mm = dcp->width_mm; - connector->display_info.height_mm = dcp->height_mm; + connector->display_info.width_mm = width_mm; + connector->display_info.height_mm = height_mm; mutex_unlock(&connector->dev->mode_config.mutex); } @@ -73,8 +80,8 @@ void dcp_set_dimensions(struct apple_dcp *dcp) * DisplayAttributes, and TimingElements may be sent first */ for (i = 0; i < dcp->nr_modes; ++i) { - dcp->modes[i].mode.width_mm = dcp->width_mm; - dcp->modes[i].mode.height_mm = dcp->height_mm; + dcp->modes[i].mode.width_mm = width_mm; + dcp->modes[i].mode.height_mm = height_mm; } } @@ -433,7 +440,7 @@ static int dcp_comp_bind(struct device *dev, struct device *main, void *data) dcp->brightness.scale = 65536; panel_np = of_get_compatible_child(dev->of_node, "apple,panel-mini-led"); if (panel_np) - dcp->has_mini_led = true; + dcp->panel.has_mini_led = true; else panel_np = of_get_compatible_child(dev->of_node, "apple,panel"); @@ -447,10 +454,10 @@ static int dcp_comp_bind(struct device *dev, struct device *main, void *data) dev_err(dev, "Missing property 'apple,max-brightness'\n"); } - of_property_read_u32(panel_np, "width-mm", &dcp->width_mm); + of_property_read_u32(panel_np, "width-mm", &dcp->panel.width_mm); /* use adjusted height as long as the notch is hidden */ of_property_read_u32(panel_np, height_prop[!dcp->notch_height], - &dcp->height_mm); + &dcp->panel.height_mm); of_node_put(panel_np); dcp->connector_type = DRM_MODE_CONNECTOR_eDP; diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index ed126b10b209c2..89651cea1454d6 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -460,7 +460,7 @@ dcpep_cb_get_uint_prop(struct apple_dcp *dcp, struct dcp_get_uint_prop_req *req) .value = 0 }; - if (dcp->has_mini_led && + if (dcp->panel.has_mini_led && memcmp(req->obj, "SUMP", sizeof(req->obj)) == 0) { /* "PMUS */ if (strncmp(req->key, "Temperature", sizeof(req->key)) == 0) { /* From a60c7e6af8ef8b5d9ea4737fcff18f46b744cefb Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Wed, 22 Mar 2023 16:09:09 +0900 Subject: [PATCH 0513/1020] drm/apple: Align buffers to 16K page size Signed-off-by: Asahi Lina --- drivers/gpu/drm/apple/apple_drv.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index c1e34d7f9adea1..7390f30bf60ecd 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -49,12 +49,14 @@ struct apple_drm_private { DEFINE_DRM_GEM_DMA_FOPS(apple_fops); +#define DART_PAGE_SIZE 16384 + static int apple_drm_gem_dumb_create(struct drm_file *file_priv, struct drm_device *drm, struct drm_mode_create_dumb *args) { args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), 64); - args->size = args->pitch * args->height; + args->size = round_up(args->pitch * args->height, DART_PAGE_SIZE); return drm_gem_dma_dumb_create_internal(file_priv, drm, args); } From 388853ffc88324a07fcdbd7caef2a24dc5c29876 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 28 Feb 2023 20:34:03 +0100 Subject: [PATCH 0514/1020] drm/apple: purge unused dcp_update_notify_clients_dcp Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 5 ----- drivers/gpu/drm/apple/iomfb.h | 18 ------------------ 2 files changed, 23 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 89651cea1454d6..2591c7b4a3cdb0 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -170,7 +170,6 @@ static u8 dcp_pop_depth(u8 *depth) const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { DCP_METHOD("A000", dcpep_late_init_signal), DCP_METHOD("A029", dcpep_setup_video_limits), - DCP_METHOD("A034", dcpep_update_notify_clients_dcp), DCP_METHOD("A131", iomfbep_a131_pmu_service_matched), DCP_METHOD("A132", iomfbep_a132_backlight_service_matched), DCP_METHOD("A357", dcpep_set_create_dfb), @@ -305,10 +304,6 @@ DCP_THUNK_VOID(dcp_setup_video_limits, dcpep_setup_video_limits); DCP_THUNK_VOID(dcp_set_create_dfb, dcpep_set_create_dfb); DCP_THUNK_VOID(dcp_first_client_open, dcpep_first_client_open); -__attribute__((unused)) -DCP_THUNK_IN(dcp_update_notify_clients_dcp, dcpep_update_notify_clients_dcp, - struct dcp_update_notify_clients_dcp); - DCP_THUNK_INOUT(dcp_set_parameter_dcp, dcpep_set_parameter_dcp, struct dcp_set_parameter_dcp, u32); diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index a7e9b62425b2c4..54b34fbba94894 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -211,7 +211,6 @@ enum dcpep_method { dcpep_flush_supports_power, dcpep_set_power_state, dcpep_first_client_open, - dcpep_update_notify_clients_dcp, dcpep_set_parameter_dcp, dcpep_enable_disable_video_power_savings, dcpep_is_main_display, @@ -395,23 +394,6 @@ struct dcp_set_dcpav_prop_end_req { char key[0x40]; } __packed; -struct dcp_update_notify_clients_dcp { - u32 client_0; - u32 client_1; - u32 client_2; - u32 client_3; - u32 client_4; - u32 client_5; - u32 client_6; - u32 client_7; - u32 client_8; - u32 client_9; - u32 client_a; - u32 client_b; - u32 client_c; - u32 client_d; -} __packed; - struct dcp_set_parameter_dcp { u32 param; u32 value[8]; From 72bd0114f1bae7ffd88181de1338c97ad7027c8f Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 8 Jan 2023 21:30:22 +0100 Subject: [PATCH 0515/1020] drm/apple: Add callbacks triggered by last_client_close_dcp() Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 2591c7b4a3cdb0..962b3c619254d4 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -1330,9 +1330,14 @@ bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, [576] = trampoline_hotplug, [577] = trampoline_nop, /* powerstate_notify */ [582] = trampoline_true, /* create_default_fb_surface */ + [584] = trampoline_nop, /* IOMobileFramebufferAP::clear_default_surface */ + [588] = trampoline_nop, /* resize_default_fb_surface_gated */ [589] = trampoline_swap_complete, [591] = trampoline_swap_complete_intent_gated, [593] = trampoline_enable_backlight_message_ap_gated, + [594] = trampoline_nop, /* IOMobileFramebufferAP::setSystemConsoleMode */ + [596] = trampoline_false, /* IOMobileFramebufferAP::isDFBAllocated */ + [597] = trampoline_false, /* IOMobileFramebufferAP::preserveContents */ [598] = trampoline_nop, /* find_swap_function_gated */ }; From 86722f25d70ba3b987e1471eee1db20b85ba96c9 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Fri, 17 Feb 2023 23:17:10 +0100 Subject: [PATCH 0516/1020] drm/apple: Add support for the macOS 13.2 DCP firmware This adds support for multiple incompatible DCP firmware versions. The approach taken here duplicates more than necessary. Unmodified calls do not need to be templated. For simplicity and in the expectation that more calls and callbacks are modified in the future everything is templated. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/Makefile | 2 + drivers/gpu/drm/apple/dcp-internal.h | 11 +- drivers/gpu/drm/apple/dcp.c | 2 + drivers/gpu/drm/apple/iomfb.c | 1476 ++---------------------- drivers/gpu/drm/apple/iomfb.h | 123 +- drivers/gpu/drm/apple/iomfb_internal.h | 123 ++ drivers/gpu/drm/apple/iomfb_template.c | 1344 +++++++++++++++++++++ drivers/gpu/drm/apple/iomfb_template.h | 181 +++ drivers/gpu/drm/apple/iomfb_v12_3.c | 105 ++ drivers/gpu/drm/apple/iomfb_v12_3.h | 17 + drivers/gpu/drm/apple/iomfb_v13_2.c | 105 ++ drivers/gpu/drm/apple/iomfb_v13_2.h | 17 + drivers/gpu/drm/apple/version_utils.h | 14 + 13 files changed, 2024 insertions(+), 1496 deletions(-) create mode 100644 drivers/gpu/drm/apple/iomfb_internal.h create mode 100644 drivers/gpu/drm/apple/iomfb_template.c create mode 100644 drivers/gpu/drm/apple/iomfb_template.h create mode 100644 drivers/gpu/drm/apple/iomfb_v12_3.c create mode 100644 drivers/gpu/drm/apple/iomfb_v12_3.h create mode 100644 drivers/gpu/drm/apple/iomfb_v13_2.c create mode 100644 drivers/gpu/drm/apple/iomfb_v13_2.h create mode 100644 drivers/gpu/drm/apple/version_utils.h diff --git a/drivers/gpu/drm/apple/Makefile b/drivers/gpu/drm/apple/Makefile index 2c02e4dcfd076d..45ef064b3e6fa5 100644 --- a/drivers/gpu/drm/apple/Makefile +++ b/drivers/gpu/drm/apple/Makefile @@ -5,6 +5,8 @@ CFLAGS_trace.o = -I$(src) appledrm-y := apple_drv.o apple_dcp-y := dcp.o dcp_backlight.o iomfb.o parser.o +apple_dcp-y += iomfb_v12_3.o +apple_dcp-y += iomfb_v13_2.o apple_dcp-$(CONFIG_TRACING) += trace.o apple_piodma-y := dummy-piodma.o diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index b34294816ec1ff..59777809a7f370 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -11,6 +11,8 @@ #include #include "iomfb.h" +#include "iomfb_v12_3.h" +#include "iomfb_v13_2.h" #define DCP_MAX_PLANES 2 @@ -19,6 +21,7 @@ struct apple_dcp; enum dcp_firmware_version { DCP_FIRMWARE_UNKNOWN, DCP_FIRMWARE_V_12_3, + DCP_FIRMWARE_V_13_2, }; enum { @@ -134,11 +137,17 @@ struct apple_dcp { struct dcp_channel ch_cmd, ch_oobcmd; struct dcp_channel ch_cb, ch_oobcb, ch_async; + /* iomfb EP callback handlers */ + const iomfb_cb_handler *cb_handlers; + /* Active chunked transfer. There can only be one at a time. */ struct dcp_chunks chunks; /* Queued swap. Owned by the DCP to avoid per-swap memory allocation */ - struct dcp_swap_submit_req swap; + union { + struct dcp_swap_submit_req_v12_3 v12_3; + struct dcp_swap_submit_req_v13_2 v13_2; + } swap; /* Current display mode */ bool valid_mode; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index c3d9a8dc491da4..05fa58033180a8 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -403,6 +403,8 @@ static enum dcp_firmware_version dcp_check_firmware_version(struct device *dev) if (strncmp(compat_str, "12.3.0", sizeof(compat_str)) == 0) return DCP_FIRMWARE_V_12_3; + if (strncmp(compat_str, "13.2.0", sizeof(compat_str)) == 0) + return DCP_FIRMWARE_V_13_2; dev_err(dev, "DCP firmware-compat %s (FW: %s) is not supported\n", compat_str, fw_str); diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 962b3c619254d4..4964bb063bff1f 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -1,19 +1,19 @@ // SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2021 Alyssa Rosenzweig */ +#include #include #include -#include -#include -#include +#include #include #include #include #include -#include -#include +#include +#include +#include +#include #include -#include #include #include @@ -25,28 +25,10 @@ #include "dcp.h" #include "dcp-internal.h" #include "iomfb.h" +#include "iomfb_internal.h" #include "parser.h" #include "trace.h" -/* Register defines used in bandwidth setup structure */ -#define REG_SCRATCH (0x14) -#define REG_SCRATCH_T600X (0x988) -#define REG_DOORBELL (0x0) -#define REG_DOORBELL_BIT (2) - -struct dcp_wait_cookie { - struct kref refcount; - struct completion done; -}; - -static void release_wait_cookie(struct kref *ref) -{ - struct dcp_wait_cookie *cookie; - cookie = container_of(ref, struct dcp_wait_cookie, refcount); - - kfree(cookie); -} - static int dcp_tx_offset(enum dcp_context_id id) { switch (id) { @@ -165,33 +147,8 @@ static u8 dcp_pop_depth(u8 *depth) return --(*depth); } -#define DCP_METHOD(tag, name) [name] = { #name, tag } - -const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { - DCP_METHOD("A000", dcpep_late_init_signal), - DCP_METHOD("A029", dcpep_setup_video_limits), - DCP_METHOD("A131", iomfbep_a131_pmu_service_matched), - DCP_METHOD("A132", iomfbep_a132_backlight_service_matched), - DCP_METHOD("A357", dcpep_set_create_dfb), - DCP_METHOD("A358", iomfbep_a358_vi_set_temperature_hint), - DCP_METHOD("A401", dcpep_start_signal), - DCP_METHOD("A407", dcpep_swap_start), - DCP_METHOD("A408", dcpep_swap_submit), - DCP_METHOD("A410", dcpep_set_display_device), - DCP_METHOD("A411", dcpep_is_main_display), - DCP_METHOD("A412", dcpep_set_digital_out_mode), - DCP_METHOD("A426", iomfbep_get_color_remap_mode), - DCP_METHOD("A439", dcpep_set_parameter_dcp), - DCP_METHOD("A443", dcpep_create_default_fb), - DCP_METHOD("A447", dcpep_enable_disable_video_power_savings), - DCP_METHOD("A454", dcpep_first_client_open), - DCP_METHOD("A460", dcpep_set_display_refresh_properties), - DCP_METHOD("A463", dcpep_flush_supports_power), - DCP_METHOD("A468", dcpep_set_power_state), -}; - /* Call a DCP function given by a tag */ -static void dcp_push(struct apple_dcp *dcp, bool oob, enum dcpep_method method, +void dcp_push(struct apple_dcp *dcp, bool oob, const struct dcp_method_entry *call, u32 in_len, u32 out_len, void *data, dcp_callback_t cb, void *cookie) { @@ -203,10 +160,10 @@ static void dcp_push(struct apple_dcp *dcp, bool oob, enum dcpep_method method, .out_len = out_len, /* Tag is reversed due to endianness of the fourcc */ - .tag[0] = dcp_methods[method].tag[3], - .tag[1] = dcp_methods[method].tag[2], - .tag[2] = dcp_methods[method].tag[1], - .tag[3] = dcp_methods[method].tag[0], + .tag[0] = call->tag[3], + .tag[1] = call->tag[2], + .tag[2] = call->tag[1], + .tag[3] = call->tag[0], }; u8 depth = dcp_push_depth(&ch->depth); @@ -221,7 +178,7 @@ static void dcp_push(struct apple_dcp *dcp, bool oob, enum dcpep_method method, if (in_len > 0) memcpy(out_data, data, in_len); - trace_iomfb_push(dcp, &dcp_methods[method], context, offset, depth); + trace_iomfb_push(dcp, call, context, offset, depth); ch->callbacks[depth] = cb; ch->cookies[depth] = cookie; @@ -232,88 +189,8 @@ static void dcp_push(struct apple_dcp *dcp, bool oob, enum dcpep_method method, dcpep_msg(context, data_len, offset)); } -#define DCP_THUNK_VOID(func, handle) \ - static void func(struct apple_dcp *dcp, bool oob, dcp_callback_t cb, \ - void *cookie) \ - { \ - dcp_push(dcp, oob, handle, 0, 0, NULL, cb, cookie); \ - } - -#define DCP_THUNK_OUT(func, handle, T) \ - static void func(struct apple_dcp *dcp, bool oob, dcp_callback_t cb, \ - void *cookie) \ - { \ - dcp_push(dcp, oob, handle, 0, sizeof(T), NULL, cb, cookie); \ - } - -#define DCP_THUNK_IN(func, handle, T) \ - static void func(struct apple_dcp *dcp, bool oob, T *data, \ - dcp_callback_t cb, void *cookie) \ - { \ - dcp_push(dcp, oob, handle, sizeof(T), 0, data, cb, cookie); \ - } - -#define DCP_THUNK_INOUT(func, handle, T_in, T_out) \ - static void func(struct apple_dcp *dcp, bool oob, T_in *data, \ - dcp_callback_t cb, void *cookie) \ - { \ - dcp_push(dcp, oob, handle, sizeof(T_in), sizeof(T_out), data, \ - cb, cookie); \ - } - -#define IOMFB_THUNK_INOUT(name) \ - static void iomfb_ ## name(struct apple_dcp *dcp, bool oob, \ - struct iomfb_ ## name ## _req *data, \ - dcp_callback_t cb, void *cookie) \ - { \ - dcp_push(dcp, oob, iomfbep_ ## name, \ - sizeof(struct iomfb_ ## name ## _req), \ - sizeof(struct iomfb_ ## name ## _resp), \ - data, cb, cookie); \ - } - -DCP_THUNK_OUT(iomfb_a131_pmu_service_matched, iomfbep_a131_pmu_service_matched, u32); -DCP_THUNK_OUT(iomfb_a132_backlight_service_matched, iomfbep_a132_backlight_service_matched, u32); -DCP_THUNK_OUT(iomfb_a358_vi_set_temperature_hint, iomfbep_a358_vi_set_temperature_hint, u32); - -IOMFB_THUNK_INOUT(get_color_remap_mode); - -DCP_THUNK_INOUT(dcp_swap_submit, dcpep_swap_submit, struct dcp_swap_submit_req, - struct dcp_swap_submit_resp); - -DCP_THUNK_INOUT(dcp_swap_start, dcpep_swap_start, struct dcp_swap_start_req, - struct dcp_swap_start_resp); - -DCP_THUNK_INOUT(dcp_set_power_state, dcpep_set_power_state, - struct dcp_set_power_state_req, - struct dcp_set_power_state_resp); - -DCP_THUNK_INOUT(dcp_set_digital_out_mode, dcpep_set_digital_out_mode, - struct dcp_set_digital_out_mode_req, u32); - -DCP_THUNK_INOUT(dcp_set_display_device, dcpep_set_display_device, u32, u32); - -DCP_THUNK_OUT(dcp_set_display_refresh_properties, - dcpep_set_display_refresh_properties, u32); - -DCP_THUNK_OUT(dcp_late_init_signal, dcpep_late_init_signal, u32); -DCP_THUNK_IN(dcp_flush_supports_power, dcpep_flush_supports_power, u32); -DCP_THUNK_OUT(dcp_create_default_fb, dcpep_create_default_fb, u32); -DCP_THUNK_OUT(dcp_start_signal, dcpep_start_signal, u32); -DCP_THUNK_VOID(dcp_setup_video_limits, dcpep_setup_video_limits); -DCP_THUNK_VOID(dcp_set_create_dfb, dcpep_set_create_dfb); -DCP_THUNK_VOID(dcp_first_client_open, dcpep_first_client_open); - -DCP_THUNK_INOUT(dcp_set_parameter_dcp, dcpep_set_parameter_dcp, - struct dcp_set_parameter_dcp, u32); - -DCP_THUNK_INOUT(dcp_enable_disable_video_power_savings, - dcpep_enable_disable_video_power_savings, u32, int); - -DCP_THUNK_OUT(dcp_is_main_display, dcpep_is_main_display, u32); - /* Parse a callback tag "D123" into the ID 123. Returns -EINVAL on failure. */ -static int dcp_parse_tag(char tag[4]) +int dcp_parse_tag(char tag[4]) { u32 d[3]; int i; @@ -332,7 +209,7 @@ static int dcp_parse_tag(char tag[4]) } /* Ack a callback from the DCP */ -static void dcp_ack(struct apple_dcp *dcp, enum dcp_context_id context) +void dcp_ack(struct apple_dcp *dcp, enum dcp_context_id context) { struct dcp_channel *ch = dcp_get_channel(dcp, context); @@ -341,776 +218,54 @@ static void dcp_ack(struct apple_dcp *dcp, enum dcp_context_id context) dcpep_ack(context)); } -/* DCP callback handlers */ -static void dcpep_cb_nop(struct apple_dcp *dcp) -{ - /* No operation */ -} - -static u8 dcpep_cb_true(struct apple_dcp *dcp) -{ - return true; -} - -static u8 dcpep_cb_false(struct apple_dcp *dcp) -{ - return false; -} - -static u32 dcpep_cb_zero(struct apple_dcp *dcp) -{ - return 0; -} - -static void dcpep_cb_swap_complete(struct apple_dcp *dcp, - struct dc_swap_complete_resp *resp) -{ - trace_iomfb_swap_complete(dcp, resp->swap_id); - - dcp_drm_crtc_vblank(dcp->crtc); -} - -/* special */ -static void complete_vi_set_temperature_hint(struct apple_dcp *dcp, void *out, void *cookie) -{ - // ack D100 cb_match_pmu_service - dcp_ack(dcp, DCP_CONTEXT_CB); -} - -static bool iomfbep_cb_match_pmu_service(struct apple_dcp *dcp, int tag, void *out, void *in) -{ - trace_iomfb_callback(dcp, tag, __func__); - iomfb_a358_vi_set_temperature_hint(dcp, false, - complete_vi_set_temperature_hint, - NULL); - - // return false for deferred ACK - return false; -} - -static void complete_pmu_service_matched(struct apple_dcp *dcp, void *out, void *cookie) +void dcp_sleep(struct apple_dcp *dcp) { - struct dcp_channel *ch = &dcp->ch_cb; - u8 *succ = ch->output[ch->depth - 1]; - - *succ = true; - - // ack D206 cb_match_pmu_service_2 - dcp_ack(dcp, DCP_CONTEXT_CB); -} - -static bool iomfbep_cb_match_pmu_service_2(struct apple_dcp *dcp, int tag, void *out, void *in) -{ - trace_iomfb_callback(dcp, tag, __func__); - - iomfb_a131_pmu_service_matched(dcp, false, complete_pmu_service_matched, - out); - - // return false for deferred ACK - return false; -} - -static void complete_backlight_service_matched(struct apple_dcp *dcp, void *out, void *cookie) -{ - struct dcp_channel *ch = &dcp->ch_cb; - u8 *succ = ch->output[ch->depth - 1]; - - *succ = true; - - // ack D206 cb_match_backlight_service - dcp_ack(dcp, DCP_CONTEXT_CB); -} - -static bool iomfbep_cb_match_backlight_service(struct apple_dcp *dcp, int tag, void *out, void *in) -{ - trace_iomfb_callback(dcp, tag, __func__); - - iomfb_a132_backlight_service_matched(dcp, false, complete_backlight_service_matched, out); - - // return false for deferred ACK - return false; -} - -static void iomfb_cb_pr_publish(struct apple_dcp *dcp, struct iomfb_property *prop) -{ - switch (prop->id) { - case IOMFB_PROPERTY_NITS: - { - dcp->brightness.nits = prop->value / dcp->brightness.scale; - /* notify backlight device of the initial brightness */ - if (!dcp->brightness.bl_dev && dcp->brightness.maximum > 0) - schedule_work(&dcp->bl_register_wq); - trace_iomfb_brightness(dcp, prop->value); + switch (dcp->fw_compat) { + case DCP_FIRMWARE_V_12_3: + iomfb_sleep_v12_3(dcp); + break; + case DCP_FIRMWARE_V_13_2: + iomfb_sleep_v13_2(dcp); break; - } default: - dev_dbg(dcp->dev, "pr_publish: id: %d = %u\n", prop->id, prop->value); - } -} - -static struct dcp_get_uint_prop_resp -dcpep_cb_get_uint_prop(struct apple_dcp *dcp, struct dcp_get_uint_prop_req *req) -{ - struct dcp_get_uint_prop_resp resp = (struct dcp_get_uint_prop_resp){ - .value = 0 - }; - - if (dcp->panel.has_mini_led && - memcmp(req->obj, "SUMP", sizeof(req->obj)) == 0) { /* "PMUS */ - if (strncmp(req->key, "Temperature", sizeof(req->key)) == 0) { - /* - * TODO: value from j314c, find out if it is temperature in - * centigrade C and which temperature sensor reports it - */ - resp.value = 3029; - resp.ret = true; - } - } - - return resp; -} - -static u8 iomfbep_cb_sr_set_property_int(struct apple_dcp *dcp, - struct iomfb_sr_set_property_int_req *req) -{ - if (memcmp(req->obj, "FMOI", sizeof(req->obj)) == 0) { /* "IOMF */ - if (strncmp(req->key, "Brightness_Scale", sizeof(req->key)) == 0) { - if (!req->value_null) - dcp->brightness.scale = req->value; - } - } - - return 1; -} - -static void iomfbep_cb_set_fx_prop(struct apple_dcp *dcp, struct iomfb_set_fx_prop_req *req) -{ - // TODO: trace this, see if there properties which needs to used later -} - -/* - * Callback to map a buffer allocated with allocate_buf for PIODMA usage. - * PIODMA is separate from the main DCP and uses own IOVA space on a dedicated - * stream of the display DART, rather than the expected DCP DART. - * - * XXX: This relies on dma_get_sgtable in concert with dma_map_sgtable, which - * is a "fundamentally unsafe" operation according to the docs. And yet - * everyone does it... - */ -static struct dcp_map_buf_resp dcpep_cb_map_piodma(struct apple_dcp *dcp, - struct dcp_map_buf_req *req) -{ - struct sg_table *map; - int ret; - - if (req->buffer >= ARRAY_SIZE(dcp->memdesc)) - goto reject; - - map = &dcp->memdesc[req->buffer].map; - - if (!map->sgl) - goto reject; - - /* Use PIODMA device instead of DCP to map against the right IOMMU. */ - ret = dma_map_sgtable(&dcp->piodma->dev, map, DMA_BIDIRECTIONAL, 0); - - if (ret) - goto reject; - - return (struct dcp_map_buf_resp){ .dva = sg_dma_address(map->sgl) }; - -reject: - dev_err(dcp->dev, "denying map of invalid buffer %llx for pidoma\n", - req->buffer); - return (struct dcp_map_buf_resp){ .ret = EINVAL }; -} - -static void dcpep_cb_unmap_piodma(struct apple_dcp *dcp, - struct dcp_unmap_buf_resp *resp) -{ - struct sg_table *map; - dma_addr_t dma_addr; - - if (resp->buffer >= ARRAY_SIZE(dcp->memdesc)) { - dev_warn(dcp->dev, "unmap request for out of range buffer %llu", - resp->buffer); - return; - } - - map = &dcp->memdesc[resp->buffer].map; - - if (!map->sgl) { - dev_warn(dcp->dev, - "unmap for non-mapped buffer %llu iova:0x%08llx", - resp->buffer, resp->dva); - return; - } - - dma_addr = sg_dma_address(map->sgl); - if (dma_addr != resp->dva) { - dev_warn(dcp->dev, "unmap buffer %llu address mismatch dma_addr:%llx dva:%llx", - resp->buffer, dma_addr, resp->dva); - return; - } - - /* Use PIODMA device instead of DCP to unmap from the right IOMMU. */ - dma_unmap_sgtable(&dcp->piodma->dev, map, DMA_BIDIRECTIONAL, 0); -} - -/* - * Allocate an IOVA contiguous buffer mapped to the DCP. The buffer need not be - * physically contigiuous, however we should save the sgtable in case the - * buffer needs to be later mapped for PIODMA. - */ -static struct dcp_allocate_buffer_resp -dcpep_cb_allocate_buffer(struct apple_dcp *dcp, - struct dcp_allocate_buffer_req *req) -{ - struct dcp_allocate_buffer_resp resp = { 0 }; - struct dcp_mem_descriptor *memdesc; - u32 id; - - resp.dva_size = ALIGN(req->size, 4096); - resp.mem_desc_id = - find_first_zero_bit(dcp->memdesc_map, DCP_MAX_MAPPINGS); - - if (resp.mem_desc_id >= DCP_MAX_MAPPINGS) { - dev_warn(dcp->dev, "DCP overflowed mapping table, ignoring"); - resp.dva_size = 0; - resp.mem_desc_id = 0; - return resp; - } - id = resp.mem_desc_id; - set_bit(id, dcp->memdesc_map); - - memdesc = &dcp->memdesc[id]; - - memdesc->size = resp.dva_size; - memdesc->buf = dma_alloc_coherent(dcp->dev, memdesc->size, - &memdesc->dva, GFP_KERNEL); - - dma_get_sgtable(dcp->dev, &memdesc->map, memdesc->buf, memdesc->dva, - memdesc->size); - resp.dva = memdesc->dva; - - return resp; -} - -static u8 dcpep_cb_release_mem_desc(struct apple_dcp *dcp, u32 *mem_desc_id) -{ - struct dcp_mem_descriptor *memdesc; - u32 id = *mem_desc_id; - - if (id >= DCP_MAX_MAPPINGS) { - dev_warn(dcp->dev, - "unmap request for out of range mem_desc_id %u", id); - return 0; - } - - if (!test_and_clear_bit(id, dcp->memdesc_map)) { - dev_warn(dcp->dev, "unmap request for unused mem_desc_id %u", - id); - return 0; - } - - memdesc = &dcp->memdesc[id]; - if (memdesc->buf) { - dma_free_coherent(dcp->dev, memdesc->size, memdesc->buf, - memdesc->dva); - - memdesc->buf = NULL; - memset(&memdesc->map, 0, sizeof(memdesc->map)); - } else { - memdesc->reg = 0; - } - - memdesc->size = 0; - - return 1; -} - -/* Validate that the specified region is a display register */ -static bool is_disp_register(struct apple_dcp *dcp, u64 start, u64 end) -{ - int i; - - for (i = 0; i < dcp->nr_disp_registers; ++i) { - struct resource *r = dcp->disp_registers[i]; - - if ((start >= r->start) && (end <= r->end)) - return true; - } - - return false; -} - -/* - * Map contiguous physical memory into the DCP's address space. The firmware - * uses this to map the display registers we advertise in - * sr_map_device_memory_with_index, so we bounds check against that to guard - * safe against malicious coprocessors. - */ -static struct dcp_map_physical_resp -dcpep_cb_map_physical(struct apple_dcp *dcp, struct dcp_map_physical_req *req) -{ - int size = ALIGN(req->size, 4096); - u32 id; - - if (!is_disp_register(dcp, req->paddr, req->paddr + size - 1)) { - dev_err(dcp->dev, "refusing to map phys address %llx size %llx", - req->paddr, req->size); - return (struct dcp_map_physical_resp){}; - } - - id = find_first_zero_bit(dcp->memdesc_map, DCP_MAX_MAPPINGS); - set_bit(id, dcp->memdesc_map); - dcp->memdesc[id].size = size; - dcp->memdesc[id].reg = req->paddr; - - return (struct dcp_map_physical_resp){ - .dva_size = size, - .mem_desc_id = id, - .dva = dma_map_resource(dcp->dev, req->paddr, size, - DMA_BIDIRECTIONAL, 0), - }; -} - -static u64 dcpep_cb_get_frequency(struct apple_dcp *dcp) -{ - return clk_get_rate(dcp->clk); -} - -static struct dcp_map_reg_resp dcpep_cb_map_reg(struct apple_dcp *dcp, - struct dcp_map_reg_req *req) -{ - if (req->index >= dcp->nr_disp_registers) { - dev_warn(dcp->dev, "attempted to read invalid reg index %u", - req->index); - - return (struct dcp_map_reg_resp){ .ret = 1 }; - } else { - struct resource *rsrc = dcp->disp_registers[req->index]; - - return (struct dcp_map_reg_resp){ - .addr = rsrc->start, .length = resource_size(rsrc) - }; - } -} - -static struct dcp_read_edt_data_resp -dcpep_cb_read_edt_data(struct apple_dcp *dcp, struct dcp_read_edt_data_req *req) -{ - return (struct dcp_read_edt_data_resp){ - .value[0] = req->value[0], - .ret = 0, - }; -} - -static void iomfbep_cb_enable_backlight_message_ap_gated(struct apple_dcp *dcp, - u8 *enabled) -{ - /* - * update backlight brightness on next swap, on non mini-LED displays - * DCP seems to set an invalid iDAC value after coming out of DPMS. - * syslog: "[BrightnessLCD.cpp:743][AFK]nitsToDBV: iDAC out of range" - */ - dcp->brightness.update = true; -} - -/* Chunked data transfer for property dictionaries */ -static u8 dcpep_cb_prop_start(struct apple_dcp *dcp, u32 *length) -{ - if (dcp->chunks.data != NULL) { - dev_warn(dcp->dev, "ignoring spurious transfer start\n"); - return false; - } - - dcp->chunks.length = *length; - dcp->chunks.data = devm_kzalloc(dcp->dev, *length, GFP_KERNEL); - - if (!dcp->chunks.data) { - dev_warn(dcp->dev, "failed to allocate chunks\n"); - return false; - } - - return true; -} - -static u8 dcpep_cb_prop_chunk(struct apple_dcp *dcp, - struct dcp_set_dcpav_prop_chunk_req *req) -{ - if (!dcp->chunks.data) { - dev_warn(dcp->dev, "ignoring spurious chunk\n"); - return false; - } - - if (req->offset + req->length > dcp->chunks.length) { - dev_warn(dcp->dev, "ignoring overflowing chunk\n"); - return false; - } - - memcpy(dcp->chunks.data + req->offset, req->data, req->length); - return true; -} - -static bool dcpep_process_chunks(struct apple_dcp *dcp, - struct dcp_set_dcpav_prop_end_req *req) -{ - struct dcp_parse_ctx ctx; - int ret; - - if (!dcp->chunks.data) { - dev_warn(dcp->dev, "ignoring spurious end\n"); - return false; - } - - /* used just as opaque pointer for tracing */ - ctx.dcp = dcp; - - ret = parse(dcp->chunks.data, dcp->chunks.length, &ctx); - - if (ret) { - dev_warn(dcp->dev, "bad header on dcpav props\n"); - return false; - } - - if (!strcmp(req->key, "TimingElements")) { - dcp->modes = enumerate_modes(&ctx, &dcp->nr_modes, - dcp->width_mm, dcp->height_mm, - dcp->notch_height); - - if (IS_ERR(dcp->modes)) { - dev_warn(dcp->dev, "failed to parse modes\n"); - dcp->modes = NULL; - dcp->nr_modes = 0; - return false; - } - } else if (!strcmp(req->key, "DisplayAttributes")) { - /* DisplayAttributes are empty for integrated displays, use - * display dimensions read from the devicetree - */ - if (dcp->main_display) { - ret = parse_display_attributes(&ctx, &dcp->width_mm, - &dcp->height_mm); - - if (ret) { - dev_warn(dcp->dev, "failed to parse display attribs\n"); - return false; - } - } - - dcp_set_dimensions(dcp); - } - - return true; -} - -static u8 dcpep_cb_prop_end(struct apple_dcp *dcp, - struct dcp_set_dcpav_prop_end_req *req) -{ - u8 resp = dcpep_process_chunks(dcp, req); - - /* Reset for the next transfer */ - devm_kfree(dcp->dev, dcp->chunks.data); - dcp->chunks.data = NULL; - - return resp; -} - -/* Boot sequence */ -static void boot_done(struct apple_dcp *dcp, void *out, void *cookie) -{ - struct dcp_channel *ch = &dcp->ch_cb; - u8 *succ = ch->output[ch->depth - 1]; - dev_dbg(dcp->dev, "boot done"); - - *succ = true; - dcp_ack(dcp, DCP_CONTEXT_CB); -} - -static void boot_5(struct apple_dcp *dcp, void *out, void *cookie) -{ - dcp_set_display_refresh_properties(dcp, false, boot_done, NULL); -} - -static void boot_4(struct apple_dcp *dcp, void *out, void *cookie) -{ - dcp_late_init_signal(dcp, false, boot_5, NULL); -} - -static void boot_3(struct apple_dcp *dcp, void *out, void *cookie) -{ - u32 v_true = true; - - dcp_flush_supports_power(dcp, false, &v_true, boot_4, NULL); -} - -static void boot_2(struct apple_dcp *dcp, void *out, void *cookie) -{ - dcp_setup_video_limits(dcp, false, boot_3, NULL); -} - -static void boot_1_5(struct apple_dcp *dcp, void *out, void *cookie) -{ - dcp_create_default_fb(dcp, false, boot_2, NULL); -} - -/* Use special function signature to defer the ACK */ -static bool dcpep_cb_boot_1(struct apple_dcp *dcp, int tag, void *out, void *in) -{ - trace_iomfb_callback(dcp, tag, __func__); - dcp_set_create_dfb(dcp, false, boot_1_5, NULL); - return false; -} - -static struct dcp_rt_bandwidth dcpep_cb_rt_bandwidth(struct apple_dcp *dcp) -{ - if (dcp->disp_registers[5] && dcp->disp_registers[6]) - return (struct dcp_rt_bandwidth){ - .reg_scratch = - dcp->disp_registers[5]->start + REG_SCRATCH, - .reg_doorbell = - dcp->disp_registers[6]->start + REG_DOORBELL, - .doorbell_bit = REG_DOORBELL_BIT, - - .padding[3] = 0x4, // XXX: required by 11.x firmware - }; - else if (dcp->disp_registers[4]) - return (struct dcp_rt_bandwidth){ - .reg_scratch = dcp->disp_registers[4]->start + - REG_SCRATCH_T600X, - .reg_doorbell = 0, - .doorbell_bit = 0, - }; - else - return (struct dcp_rt_bandwidth){ - .reg_scratch = 0, - .reg_doorbell = 0, - .doorbell_bit = 0, - }; -} - -/* Callback to get the current time as milliseconds since the UNIX epoch */ -static u64 dcpep_cb_get_time(struct apple_dcp *dcp) -{ - return ktime_to_ms(ktime_get_real()); -} - -struct dcp_swap_cookie { - struct kref refcount; - struct completion done; - u32 swap_id; -}; - -static void release_swap_cookie(struct kref *ref) -{ - struct dcp_swap_cookie *cookie; - cookie = container_of(ref, struct dcp_swap_cookie, refcount); - - kfree(cookie); -} - -static void dcp_swap_cleared(struct apple_dcp *dcp, void *data, void *cookie) -{ - struct dcp_swap_submit_resp *resp = data; - dev_dbg(dcp->dev, "%s", __func__); - - if (cookie) { - struct dcp_swap_cookie *info = cookie; - complete(&info->done); - kref_put(&info->refcount, release_swap_cookie); - } - - if (resp->ret) { - dev_err(dcp->dev, "swap_clear failed! status %u\n", resp->ret); - dcp_drm_crtc_vblank(dcp->crtc); - return; - } - - while (!list_empty(&dcp->swapped_out_fbs)) { - struct dcp_fb_reference *entry; - entry = list_first_entry(&dcp->swapped_out_fbs, - struct dcp_fb_reference, head); - if (entry->fb) - drm_framebuffer_put(entry->fb); - list_del(&entry->head); - kfree(entry); - } -} - -static void dcp_swap_clear_started(struct apple_dcp *dcp, void *data, - void *cookie) -{ - struct dcp_swap_start_resp *resp = data; - dev_dbg(dcp->dev, "%s swap_id: %u", __func__, resp->swap_id); - dcp->swap.swap.swap_id = resp->swap_id; - - if (cookie) { - struct dcp_swap_cookie *info = cookie; - info->swap_id = resp->swap_id; - } - - dcp_swap_submit(dcp, false, &dcp->swap, dcp_swap_cleared, cookie); -} - -static void dcp_on_final(struct apple_dcp *dcp, void *out, void *cookie) -{ - struct dcp_wait_cookie *wait = cookie; - dev_dbg(dcp->dev, "%s", __func__); - - if (wait) { - complete(&wait->done); - kref_put(&wait->refcount, release_wait_cookie); + WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); + break; } } -static void dcp_on_set_power_state(struct apple_dcp *dcp, void *out, void *cookie) -{ - struct dcp_set_power_state_req req = { - .unklong = 1, - }; - dev_dbg(dcp->dev, "%s", __func__); - - dcp_set_power_state(dcp, false, &req, dcp_on_final, cookie); -} - -static void dcp_on_set_parameter(struct apple_dcp *dcp, void *out, void *cookie) -{ - struct dcp_set_parameter_dcp param = { - .param = 14, - .value = { 0 }, - .count = 1, - }; - dev_dbg(dcp->dev, "%s", __func__); - - dcp_set_parameter_dcp(dcp, false, ¶m, dcp_on_set_power_state, cookie); -} - void dcp_poweron(struct platform_device *pdev) { struct apple_dcp *dcp = platform_get_drvdata(pdev); - struct dcp_wait_cookie *cookie; - int ret; - u32 handle; - dev_dbg(dcp->dev, "%s", __func__); - cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); - if (!cookie) - return; - - init_completion(&cookie->done); - kref_init(&cookie->refcount); - /* increase refcount to ensure the receiver has a reference */ - kref_get(&cookie->refcount); - - if (dcp->main_display) { - handle = 0; - dcp_set_display_device(dcp, false, &handle, dcp_on_set_power_state, - cookie); - } else { - handle = 2; - dcp_set_display_device(dcp, false, &handle, - dcp_on_set_parameter, cookie); + switch (dcp->fw_compat) { + case DCP_FIRMWARE_V_12_3: + iomfb_poweron_v12_3(dcp); + break; + case DCP_FIRMWARE_V_13_2: + iomfb_poweron_v13_2(dcp); + break; + default: + WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); + break; } - ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(500)); - - if (ret == 0) - dev_warn(dcp->dev, "wait for power timed out"); - - kref_put(&cookie->refcount, release_wait_cookie);; - - /* Force a brightness update after poweron, to restore the brightness */ - dcp->brightness.update = true; } EXPORT_SYMBOL(dcp_poweron); -static void complete_set_powerstate(struct apple_dcp *dcp, void *out, - void *cookie) -{ - struct dcp_wait_cookie *wait = cookie; - - if (wait) { - complete(&wait->done); - kref_put(&wait->refcount, release_wait_cookie); - } -} - void dcp_poweroff(struct platform_device *pdev) { struct apple_dcp *dcp = platform_get_drvdata(pdev); - int ret, swap_id; - struct dcp_set_power_state_req power_req = { - .unklong = 0, - }; - struct dcp_swap_cookie *cookie; - struct dcp_wait_cookie *poff_cookie; - struct dcp_swap_start_req swap_req = { 0 }; - - dev_dbg(dcp->dev, "%s", __func__); - - cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); - if (!cookie) - return; - init_completion(&cookie->done); - kref_init(&cookie->refcount); - /* increase refcount to ensure the receiver has a reference */ - kref_get(&cookie->refcount); - // clear surfaces - memset(&dcp->swap, 0, sizeof(dcp->swap)); - - dcp->swap.swap.swap_enabled = - dcp->swap.swap.swap_completed = IOMFB_SET_BACKGROUND | 0xF; - dcp->swap.swap.bg_color = 0xFF000000; - - /* - * Turn off the backlight. This matters because the DCP's idea of - * backlight brightness gets desynced after a power change, and it - * needs to be told it's going to turn off so it will consider the - * subsequent update on poweron an actual change and restore the - * brightness. - */ - dcp->swap.swap.bl_unk = 1; - dcp->swap.swap.bl_value = 0; - dcp->swap.swap.bl_power = 0; - - for (int l = 0; l < SWAP_SURFACES; l++) - dcp->swap.surf_null[l] = true; - - dcp_swap_start(dcp, false, &swap_req, dcp_swap_clear_started, cookie); - - ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(50)); - swap_id = cookie->swap_id; - kref_put(&cookie->refcount, release_swap_cookie); - if (ret <= 0) { - dcp->crashed = true; - return; + switch (dcp->fw_compat) { + case DCP_FIRMWARE_V_12_3: + iomfb_poweroff_v12_3(dcp); + break; + case DCP_FIRMWARE_V_13_2: + iomfb_poweroff_v13_2(dcp); + break; + default: + WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); + break; } - - dev_dbg(dcp->dev, "%s: clear swap submitted: %u", __func__, swap_id); - - poff_cookie = kzalloc(sizeof(*poff_cookie), GFP_KERNEL); - if (!poff_cookie) - return; - init_completion(&poff_cookie->done); - kref_init(&poff_cookie->refcount); - /* increase refcount to ensure the receiver has a reference */ - kref_get(&poff_cookie->refcount); - - dcp_set_power_state(dcp, false, &power_req, complete_set_powerstate, - poff_cookie); - ret = wait_for_completion_timeout(&poff_cookie->done, - msecs_to_jiffies(1000)); - - if (ret == 0) - dev_warn(dcp->dev, "setPowerState(0) timeout %u ms", 1000); - else if (ret > 0) - dev_dbg(dcp->dev, - "setPowerState(0) finished with %d ms to spare", - jiffies_to_msecs(ret)); - - kref_put(&poff_cookie->refcount, release_wait_cookie); - dev_dbg(dcp->dev, "%s: setPowerState(0) done", __func__); } EXPORT_SYMBOL(dcp_poweroff); @@ -1148,199 +303,6 @@ void dcp_hotplug(struct work_struct *work) } EXPORT_SYMBOL_GPL(dcp_hotplug); -static void dcpep_cb_hotplug(struct apple_dcp *dcp, u64 *connected) -{ - struct apple_connector *connector = dcp->connector; - - /* DCP issues hotplug_gated callbacks after SetPowerState() calls on - * devices with display (macbooks, imacs). This must not result in - * connector state changes on DRM side. Some applications won't enable - * a CRTC with a connector in disconnected state. Weston after DPMS off - * is one example. dcp_is_main_display() returns true on devices with - * integrated display. Ignore the hotplug_gated() callbacks there. - */ - if (dcp->main_display) - return; - - /* Hotplug invalidates mode. DRM doesn't always handle this. */ - if (!(*connected)) { - dcp->valid_mode = false; - /* after unplug swap will not complete until the next - * set_digital_out_mode */ - schedule_work(&dcp->vblank_wq); - } - - if (connector && connector->connected != !!(*connected)) { - connector->connected = !!(*connected); - dcp->valid_mode = false; - schedule_work(&connector->hotplug_wq); - } -} - -static void -dcpep_cb_swap_complete_intent_gated(struct apple_dcp *dcp, - struct dcp_swap_complete_intent_gated *info) -{ - trace_iomfb_swap_complete_intent_gated(dcp, info->swap_id, - info->width, info->height); -} - -#define DCPEP_MAX_CB (1000) - -/* - * Define type-safe trampolines. Define typedefs to enforce type-safety on the - * input data (so if the types don't match, gcc errors out). - */ - -#define TRAMPOLINE_VOID(func, handler) \ - static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ - { \ - trace_iomfb_callback(dcp, tag, #handler); \ - handler(dcp); \ - return true; \ - } - -#define TRAMPOLINE_IN(func, handler, T_in) \ - typedef void (*callback_##handler)(struct apple_dcp *, T_in *); \ - \ - static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ - { \ - callback_##handler cb = handler; \ - \ - trace_iomfb_callback(dcp, tag, #handler); \ - cb(dcp, in); \ - return true; \ - } - -#define TRAMPOLINE_INOUT(func, handler, T_in, T_out) \ - typedef T_out (*callback_##handler)(struct apple_dcp *, T_in *); \ - \ - static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ - { \ - T_out *typed_out = out; \ - callback_##handler cb = handler; \ - \ - trace_iomfb_callback(dcp, tag, #handler); \ - *typed_out = cb(dcp, in); \ - return true; \ - } - -#define TRAMPOLINE_OUT(func, handler, T_out) \ - static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ - { \ - T_out *typed_out = out; \ - \ - trace_iomfb_callback(dcp, tag, #handler); \ - *typed_out = handler(dcp); \ - return true; \ - } - -TRAMPOLINE_VOID(trampoline_nop, dcpep_cb_nop); -TRAMPOLINE_OUT(trampoline_true, dcpep_cb_true, u8); -TRAMPOLINE_OUT(trampoline_false, dcpep_cb_false, u8); -TRAMPOLINE_OUT(trampoline_zero, dcpep_cb_zero, u32); -TRAMPOLINE_IN(trampoline_swap_complete, dcpep_cb_swap_complete, - struct dc_swap_complete_resp); -TRAMPOLINE_INOUT(trampoline_get_uint_prop, dcpep_cb_get_uint_prop, - struct dcp_get_uint_prop_req, struct dcp_get_uint_prop_resp); -TRAMPOLINE_IN(trampoline_set_fx_prop, iomfbep_cb_set_fx_prop, - struct iomfb_set_fx_prop_req) -TRAMPOLINE_INOUT(trampoline_map_piodma, dcpep_cb_map_piodma, - struct dcp_map_buf_req, struct dcp_map_buf_resp); -TRAMPOLINE_IN(trampoline_unmap_piodma, dcpep_cb_unmap_piodma, - struct dcp_unmap_buf_resp); -TRAMPOLINE_INOUT(trampoline_sr_set_property_int, iomfbep_cb_sr_set_property_int, - struct iomfb_sr_set_property_int_req, u8); -TRAMPOLINE_INOUT(trampoline_allocate_buffer, dcpep_cb_allocate_buffer, - struct dcp_allocate_buffer_req, - struct dcp_allocate_buffer_resp); -TRAMPOLINE_INOUT(trampoline_map_physical, dcpep_cb_map_physical, - struct dcp_map_physical_req, struct dcp_map_physical_resp); -TRAMPOLINE_INOUT(trampoline_release_mem_desc, dcpep_cb_release_mem_desc, u32, - u8); -TRAMPOLINE_INOUT(trampoline_map_reg, dcpep_cb_map_reg, struct dcp_map_reg_req, - struct dcp_map_reg_resp); -TRAMPOLINE_INOUT(trampoline_read_edt_data, dcpep_cb_read_edt_data, - struct dcp_read_edt_data_req, struct dcp_read_edt_data_resp); -TRAMPOLINE_INOUT(trampoline_prop_start, dcpep_cb_prop_start, u32, u8); -TRAMPOLINE_INOUT(trampoline_prop_chunk, dcpep_cb_prop_chunk, - struct dcp_set_dcpav_prop_chunk_req, u8); -TRAMPOLINE_INOUT(trampoline_prop_end, dcpep_cb_prop_end, - struct dcp_set_dcpav_prop_end_req, u8); -TRAMPOLINE_OUT(trampoline_rt_bandwidth, dcpep_cb_rt_bandwidth, - struct dcp_rt_bandwidth); -TRAMPOLINE_OUT(trampoline_get_frequency, dcpep_cb_get_frequency, u64); -TRAMPOLINE_OUT(trampoline_get_time, dcpep_cb_get_time, u64); -TRAMPOLINE_IN(trampoline_hotplug, dcpep_cb_hotplug, u64); -TRAMPOLINE_IN(trampoline_swap_complete_intent_gated, - dcpep_cb_swap_complete_intent_gated, - struct dcp_swap_complete_intent_gated); -TRAMPOLINE_IN(trampoline_enable_backlight_message_ap_gated, - iomfbep_cb_enable_backlight_message_ap_gated, u8); -TRAMPOLINE_IN(trampoline_pr_publish, iomfb_cb_pr_publish, - struct iomfb_property); - -bool (*const dcpep_cb_handlers[DCPEP_MAX_CB])(struct apple_dcp *, int, void *, - void *) = { - [0] = trampoline_true, /* did_boot_signal */ - [1] = trampoline_true, /* did_power_on_signal */ - [2] = trampoline_nop, /* will_power_off_signal */ - [3] = trampoline_rt_bandwidth, - [100] = iomfbep_cb_match_pmu_service, - [101] = trampoline_zero, /* get_display_default_stride */ - [102] = trampoline_nop, /* set_number_property */ - [103] = trampoline_nop, /* set_boolean_property */ - [106] = trampoline_nop, /* remove_property */ - [107] = trampoline_true, /* create_provider_service */ - [108] = trampoline_true, /* create_product_service */ - [109] = trampoline_true, /* create_pmu_service */ - [110] = trampoline_true, /* create_iomfb_service */ - [111] = trampoline_true, /* create_backlight_service */ - [116] = dcpep_cb_boot_1, - [117] = trampoline_false, /* is_dark_boot */ - [118] = trampoline_false, /* is_dark_boot / is_waking_from_hibernate*/ - [120] = trampoline_read_edt_data, - [122] = trampoline_prop_start, - [123] = trampoline_prop_chunk, - [124] = trampoline_prop_end, - [201] = trampoline_map_piodma, - [202] = trampoline_unmap_piodma, - [206] = iomfbep_cb_match_pmu_service_2, - [207] = iomfbep_cb_match_backlight_service, - [208] = trampoline_get_time, - [211] = trampoline_nop, /* update_backlight_factor_prop */ - [300] = trampoline_pr_publish, - [401] = trampoline_get_uint_prop, - [404] = trampoline_nop, /* sr_set_uint_prop */ - [406] = trampoline_set_fx_prop, - [408] = trampoline_get_frequency, - [411] = trampoline_map_reg, - [413] = trampoline_true, /* sr_set_property_dict */ - [414] = trampoline_sr_set_property_int, - [415] = trampoline_true, /* sr_set_property_bool */ - [451] = trampoline_allocate_buffer, - [452] = trampoline_map_physical, - [456] = trampoline_release_mem_desc, - [552] = trampoline_true, /* set_property_dict_0 */ - [561] = trampoline_true, /* set_property_dict */ - [563] = trampoline_true, /* set_property_int */ - [565] = trampoline_true, /* set_property_bool */ - [567] = trampoline_true, /* set_property_str */ - [574] = trampoline_zero, /* power_up_dart */ - [576] = trampoline_hotplug, - [577] = trampoline_nop, /* powerstate_notify */ - [582] = trampoline_true, /* create_default_fb_surface */ - [584] = trampoline_nop, /* IOMobileFramebufferAP::clear_default_surface */ - [588] = trampoline_nop, /* resize_default_fb_surface_gated */ - [589] = trampoline_swap_complete, - [591] = trampoline_swap_complete_intent_gated, - [593] = trampoline_enable_backlight_message_ap_gated, - [594] = trampoline_nop, /* IOMobileFramebufferAP::setSystemConsoleMode */ - [596] = trampoline_false, /* IOMobileFramebufferAP::isDFBAllocated */ - [597] = trampoline_false, /* IOMobileFramebufferAP::preserveContents */ - [598] = trampoline_nop, /* find_swap_function_gated */ -}; - static void dcpep_handle_cb(struct apple_dcp *dcp, enum dcp_context_id context, void *data, u32 length, u16 offset) { @@ -1351,7 +313,7 @@ static void dcpep_handle_cb(struct apple_dcp *dcp, enum dcp_context_id context, struct dcp_channel *ch = dcp_get_channel(dcp, context); u8 depth; - if (tag < 0 || tag >= DCPEP_MAX_CB || !dcpep_cb_handlers[tag]) { + if (tag < 0 || tag >= IOMFB_MAX_CB || !dcp->cb_handlers || !dcp->cb_handlers[tag]) { dev_warn(dev, "received unknown callback %c%c%c%c\n", hdr->tag[3], hdr->tag[2], hdr->tag[1], hdr->tag[0]); return; @@ -1369,7 +331,7 @@ static void dcpep_handle_cb(struct apple_dcp *dcp, enum dcp_context_id context, ch->output[depth] = out; ch->end[depth] = offset + ALIGN(length, DCP_PACKET_ALIGNMENT); - if (dcpep_cb_handlers[tag](dcp, tag, out, in)) + if (dcp->cb_handlers[tag](dcp, tag, out, in)) dcp_ack(dcp, context); } @@ -1425,48 +387,12 @@ static void dcpep_got_msg(struct apple_dcp *dcp, u64 message) dcpep_handle_cb(dcp, ctx_id, data, length, offset); } -/* - * Callback for swap requests. If a swap failed, we'll never get a swap - * complete event so we need to fake a vblank event early to avoid a hang. - */ - -static void dcp_swapped(struct apple_dcp *dcp, void *data, void *cookie) -{ - struct dcp_swap_submit_resp *resp = data; - - if (resp->ret) { - dev_err(dcp->dev, "swap failed! status %u\n", resp->ret); - dcp_drm_crtc_vblank(dcp->crtc); - return; - } - - while (!list_empty(&dcp->swapped_out_fbs)) { - struct dcp_fb_reference *entry; - entry = list_first_entry(&dcp->swapped_out_fbs, - struct dcp_fb_reference, head); - if (entry->fb) - drm_framebuffer_put(entry->fb); - list_del(&entry->head); - kfree(entry); - } -} - -static void dcp_swap_started(struct apple_dcp *dcp, void *data, void *cookie) -{ - struct dcp_swap_start_resp *resp = data; - - dcp->swap.swap.swap_id = resp->swap_id; - - trace_iomfb_swap_submit(dcp, resp->swap_id); - dcp_swap_submit(dcp, false, &dcp->swap, dcp_swapped, NULL); -} - /* * DRM specifies rectangles as start and end coordinates. DCP specifies * rectangles as a start coordinate and a width/height. Convert a DRM rectangle * to a DCP rectangle. */ -static struct dcp_rect drm_to_dcp_rect(struct drm_rect *rect) +struct dcp_rect drm_to_dcp_rect(struct drm_rect *rect) { return (struct dcp_rect){ .x = rect->x1, .y = rect->y1, @@ -1474,7 +400,7 @@ static struct dcp_rect drm_to_dcp_rect(struct drm_rect *rect) .h = drm_rect_height(rect) }; } -static u32 drm_format_to_dcp(u32 drm) +u32 drm_format_to_dcp(u32 drm) { switch (drm) { case DRM_FORMAT_XRGB8888: @@ -1520,7 +446,7 @@ int dcp_get_modes(struct drm_connector *connector) EXPORT_SYMBOL_GPL(dcp_get_modes); /* The user may own drm_display_mode, so we need to search for our copy */ -static struct dcp_display_mode *lookup_mode(struct apple_dcp *dcp, +struct dcp_display_mode *lookup_mode(struct apple_dcp *dcp, const struct drm_display_mode *mode) { int i; @@ -1559,46 +485,11 @@ bool dcp_crtc_mode_fixup(struct drm_crtc *crtc, } EXPORT_SYMBOL(dcp_crtc_mode_fixup); -/* Helpers to modeset and swap, used to flush */ -static void do_swap(struct apple_dcp *dcp, void *data, void *cookie) -{ - struct dcp_swap_start_req start_req = { 0 }; - dev_dbg(dcp->dev, "%s", __func__); - - if (dcp->connector && dcp->connector->connected) - dcp_swap_start(dcp, false, &start_req, dcp_swap_started, NULL); - else - dcp_drm_crtc_vblank(dcp->crtc); -} - -static void complete_set_digital_out_mode(struct apple_dcp *dcp, void *data, - void *cookie) -{ - struct dcp_wait_cookie *wait = cookie; - dev_dbg(dcp->dev, "%s", __func__); - - if (wait) { - complete(&wait->done); - kref_put(&wait->refcount, release_wait_cookie); - } -} void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) { struct platform_device *pdev = to_apple_crtc(crtc)->dcp; struct apple_dcp *dcp = platform_get_drvdata(pdev); - struct drm_plane *plane; - struct drm_plane_state *new_state, *old_state; - struct drm_crtc_state *crtc_state; - struct dcp_swap_submit_req *req = &dcp->swap; - int plane_idx, l; - int has_surface = 0; - bool modeset; - dev_dbg(dcp->dev, "%s", __func__); - - crtc_state = drm_atomic_get_new_crtc_state(state, crtc); - - modeset = drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode; if (dcp_channel_busy(&dcp->ch_cmd)) { @@ -1610,191 +501,34 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) return; } - /* Reset to defaults */ - memset(req, 0, sizeof(*req)); - for (l = 0; l < SWAP_SURFACES; l++) - req->surf_null[l] = true; - - /* - * Clear all surfaces on startup. The boot framebuffer in surface 0 - * sticks around. - */ - if (!dcp->surfaces_cleared) { - req->swap.swap_enabled = IOMFB_SET_BACKGROUND | 0xF; - req->swap.bg_color = 0xFF000000; - dcp->surfaces_cleared = true; - } - - // Surface 0 has limitations at least on t600x. - l = 1; - for_each_oldnew_plane_in_state(state, plane, old_state, new_state, plane_idx) { - struct drm_framebuffer *fb = new_state->fb; - struct drm_gem_dma_object *obj; - struct drm_rect src_rect; - bool is_premultiplied = false; - - /* skip planes not for this crtc */ - if (old_state->crtc != crtc && new_state->crtc != crtc) - continue; - - WARN_ON(l >= SWAP_SURFACES); - - req->swap.swap_enabled |= BIT(l); - - if (old_state->fb && fb != old_state->fb) { - /* - * Race condition between a framebuffer unbind getting - * swapped out and GEM unreferencing a framebuffer. If - * we lose the race, the display gets IOVA faults and - * the DCP crashes. We need to extend the lifetime of - * the drm_framebuffer (and hence the GEM object) until - * after we get a swap complete for the swap unbinding - * it. - */ - struct dcp_fb_reference *entry = - kzalloc(sizeof(*entry), GFP_KERNEL); - if (entry) { - entry->fb = old_state->fb; - list_add_tail(&entry->head, - &dcp->swapped_out_fbs); - } - drm_framebuffer_get(old_state->fb); - } - - if (!new_state->fb) { - l += 1; - continue; - } - req->surf_null[l] = false; - has_surface = 1; - - /* - * DCP doesn't support XBGR8 / XRGB8 natively. Blending as - * pre-multiplied alpha with a black background can be used as - * workaround for the bottommost plane. - */ - if (fb->format->format == DRM_FORMAT_XRGB8888 || - fb->format->format == DRM_FORMAT_XBGR8888) - is_premultiplied = true; - - drm_rect_fp_to_int(&src_rect, &new_state->src); - - req->swap.src_rect[l] = drm_to_dcp_rect(&src_rect); - req->swap.dst_rect[l] = drm_to_dcp_rect(&new_state->dst); - - if (dcp->notch_height > 0) - req->swap.dst_rect[l].y += dcp->notch_height; - - /* the obvious helper call drm_fb_dma_get_gem_addr() adjusts - * the address for source x/y offsets. Since IOMFB has a direct - * support source position prefer that. - */ - obj = drm_fb_dma_get_gem_obj(fb, 0); - if (obj) - req->surf_iova[l] = obj->dma_addr + fb->offsets[0]; - - req->surf[l] = (struct dcp_surface){ - .is_premultiplied = is_premultiplied, - .format = drm_format_to_dcp(fb->format->format), - .xfer_func = DCP_XFER_FUNC_SDR, - .colorspace = DCP_COLORSPACE_NATIVE, - .stride = fb->pitches[0], - .width = fb->width, - .height = fb->height, - .buf_size = fb->height * fb->pitches[0], - .surface_id = req->swap.surf_ids[l], - - /* Only used for compressed or multiplanar surfaces */ - .pix_size = 1, - .pel_w = 1, - .pel_h = 1, - .has_comp = 1, - .has_planes = 1, - }; - - l += 1; - } - - if (modeset) { - struct dcp_display_mode *mode; - struct dcp_wait_cookie *cookie; - int ret; - - mode = lookup_mode(dcp, &crtc_state->mode); - if (!mode) { - dev_warn(dcp->dev, "no match for " DRM_MODE_FMT, - DRM_MODE_ARG(&crtc_state->mode)); - schedule_work(&dcp->vblank_wq); - return; - } - - dev_info(dcp->dev, "set_digital_out_mode(color:%d timing:%d)", - mode->color_mode_id, mode->timing_mode_id); - dcp->mode = (struct dcp_set_digital_out_mode_req){ - .color_mode_id = mode->color_mode_id, - .timing_mode_id = mode->timing_mode_id - }; - - cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); - if (!cookie) { - schedule_work(&dcp->vblank_wq); - return; - } - - init_completion(&cookie->done); - kref_init(&cookie->refcount); - /* increase refcount to ensure the receiver has a reference */ - kref_get(&cookie->refcount); - - dcp_set_digital_out_mode(dcp, false, &dcp->mode, - complete_set_digital_out_mode, cookie); - - dev_dbg(dcp->dev, "%s - wait for modeset", __func__); - ret = wait_for_completion_timeout(&cookie->done, - msecs_to_jiffies(500)); - - kref_put(&cookie->refcount, release_wait_cookie); - - if (ret == 0) { - dev_dbg(dcp->dev, "set_digital_out_mode 200 ms"); - schedule_work(&dcp->vblank_wq); - return; - } else if (ret > 0) { - dev_dbg(dcp->dev, - "set_digital_out_mode finished with %d to spare", - jiffies_to_msecs(ret)); - } - - dcp->valid_mode = true; - } - - if (!has_surface && !crtc_state->color_mgmt_changed) { - if (crtc_state->enable && crtc_state->active && - !crtc_state->planes_changed) { - schedule_work(&dcp->vblank_wq); - return; - } - - /* Set black background */ - req->swap.swap_enabled |= IOMFB_SET_BACKGROUND; - req->swap.bg_color = 0xFF000000; - req->clear = 1; + switch (dcp->fw_compat) { + case DCP_FIRMWARE_V_12_3: + iomfb_flush_v12_3(dcp, crtc, state); + break; + case DCP_FIRMWARE_V_13_2: + iomfb_flush_v13_2(dcp, crtc, state); + break; + default: + WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); + break; } +} +EXPORT_SYMBOL_GPL(dcp_flush); - /* These fields should be set together */ - req->swap.swap_completed = req->swap.swap_enabled; - - /* update brightness if changed */ - if (dcp->brightness.update) { - req->swap.bl_unk = 1; - req->swap.bl_value = dcp->brightness.dac; - req->swap.bl_power = 0x40; - dcp->brightness.update = false; +void iomfb_start(struct apple_dcp *dcp) +{ + switch (dcp->fw_compat) { + case DCP_FIRMWARE_V_12_3: + iomfb_start_v12_3(dcp); + break; + case DCP_FIRMWARE_V_13_2: + iomfb_start_v13_2(dcp); + break; + default: + WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); + break; } - - do_swap(dcp, NULL, NULL); } -EXPORT_SYMBOL_GPL(dcp_flush); bool dcp_is_initialized(struct platform_device *pdev) { @@ -1804,58 +538,12 @@ bool dcp_is_initialized(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(dcp_is_initialized); -static void res_is_main_display(struct apple_dcp *dcp, void *out, void *cookie) -{ - struct apple_connector *connector; - int result = *(int *)out; - dev_info(dcp->dev, "DCP is_main_display: %d\n", result); - - dcp->main_display = result != 0; - - connector = dcp->connector; - if (connector) { - connector->connected = dcp->nr_modes > 0; - schedule_work(&connector->hotplug_wq); - } - - dcp->active = true; - complete(&dcp->start_done); -} - -static void init_3(struct apple_dcp *dcp, void *out, void *cookie) -{ - dcp_is_main_display(dcp, false, res_is_main_display, NULL); -} - -static void init_2(struct apple_dcp *dcp, void *out, void *cookie) -{ - dcp_first_client_open(dcp, false, init_3, NULL); -} - -static void init_1(struct apple_dcp *dcp, void *out, void *cookie) -{ - u32 val = 0; - dcp_enable_disable_video_power_savings(dcp, false, &val, init_2, NULL); -} - -static void dcp_started(struct apple_dcp *dcp, void *data, void *cookie) -{ - struct iomfb_get_color_remap_mode_req color_remap = - (struct iomfb_get_color_remap_mode_req){ - .mode = 6, - }; - - dev_info(dcp->dev, "DCP booted\n"); - - iomfb_get_color_remap_mode(dcp, false, &color_remap, init_1, cookie); -} - void iomfb_recv_msg(struct apple_dcp *dcp, u64 message) { enum dcpep_type type = FIELD_GET(IOMFB_MESSAGE_TYPE, message); if (type == IOMFB_MESSAGE_TYPE_INITIALIZED) - dcp_start_signal(dcp, false, dcp_started, NULL); + iomfb_start(dcp); else if (type == IOMFB_MESSAGE_TYPE_MSG) dcpep_got_msg(dcp, message); else @@ -1878,13 +566,19 @@ int iomfb_start_rtkit(struct apple_dcp *dcp) void iomfb_shutdown(struct apple_dcp *dcp) { - struct dcp_set_power_state_req req = { - /* defaults are ok */ - }; - /* We're going down */ dcp->active = false; dcp->valid_mode = false; - dcp_set_power_state(dcp, false, &req, NULL, NULL); + switch (dcp->fw_compat) { + case DCP_FIRMWARE_V_12_3: + iomfb_shutdown_v12_3(dcp); + break; + case DCP_FIRMWARE_V_13_2: + iomfb_shutdown_v13_2(dcp); + break; + default: + WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); + break; + } } diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index 54b34fbba94894..0c8061bb96e3e0 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -6,6 +6,8 @@ #include +#include "version_utils.h" + /* Fixed size of shared memory between DCP and AP */ #define DCP_SHMEM_SIZE 0x100000 @@ -106,35 +108,6 @@ struct dcp_rect { */ #define IOMFB_SET_BACKGROUND BIT(31) -struct dcp_swap { - u64 ts1; - u64 ts2; - u64 unk_10[6]; - u64 flags1; - u64 flags2; - - u32 swap_id; - - u32 surf_ids[SWAP_SURFACES]; - struct dcp_rect src_rect[SWAP_SURFACES]; - u32 surf_flags[SWAP_SURFACES]; - u32 surf_unk[SWAP_SURFACES]; - struct dcp_rect dst_rect[SWAP_SURFACES]; - u32 swap_enabled; - u32 swap_completed; - - u32 bg_color; - u8 unk_110[0x1b8]; - u32 unk_2c8; - u8 unk_2cc[0x14]; - u32 unk_2e0; - u16 unk_2e2; - u64 bl_unk; - u32 bl_value; // min value is 0x10000000 - u8 bl_power; // constant 0x40 for on - u8 unk_2f3[0x2d]; -} __packed; - /* Information describing a plane of a planar compressed surface */ struct dcp_plane_info { u32 width; @@ -154,38 +127,6 @@ struct dcp_component_types { u8 types[7]; } __packed; -/* Information describing a surface */ -struct dcp_surface { - u8 is_tiled; - u8 is_tearing_allowed; - u8 is_premultiplied; - u32 plane_cnt; - u32 plane_cnt2; - u32 format; /* DCP fourcc */ - u32 ycbcr_matrix; - u8 xfer_func; - u8 colorspace; - u32 stride; - u16 pix_size; - u8 pel_w; - u8 pel_h; - u32 offset; - u32 width; - u32 height; - u32 buf_size; - u64 protection_opts; - u32 surface_id; - struct dcp_component_types comp_types[MAX_PLANES]; - u64 has_comp; - struct dcp_plane_info planes[MAX_PLANES]; - u64 has_planes; - u32 compression_info[MAX_PLANES][13]; - u64 has_compr_info; - u32 unk_num; - u32 unk_denom; - u8 padding[7]; -} __packed; - struct dcp_rt_bandwidth { u64 unk1; u64 reg_scratch; @@ -218,14 +159,22 @@ enum dcpep_method { iomfbep_a132_backlight_service_matched, iomfbep_a358_vi_set_temperature_hint, iomfbep_get_color_remap_mode, + iomfbep_last_client_close, dcpep_num_methods }; +#define IOMFB_METHOD(tag, name) [name] = { #name, tag } + struct dcp_method_entry { const char *name; char tag[4]; }; +#define IOMFB_MAX_CB (1000) +struct apple_dcp; + +typedef bool (*iomfb_cb_handler)(struct apple_dcp *, int, void *, void *); + /* Prototypes */ struct dcp_set_digital_out_mode_req { @@ -287,21 +236,6 @@ struct dcp_map_physical_resp { u32 mem_desc_id; } __packed; -struct dcp_map_reg_req { - char obj[4]; - u32 index; - u32 flags; - u8 addr_null; - u8 length_null; - u8 padding[2]; -} __packed; - -struct dcp_map_reg_resp { - u64 addr; - u64 length; - u32 ret; -} __packed; - struct dcp_swap_start_req { u32 swap_id; struct dcp_iouserclient client; @@ -316,34 +250,6 @@ struct dcp_swap_start_resp { u32 ret; } __packed; -struct dcp_swap_submit_req { - struct dcp_swap swap; - struct dcp_surface surf[SWAP_SURFACES]; - u64 surf_iova[SWAP_SURFACES]; - u8 unkbool; - u64 unkdouble; - u32 clear; // or maybe switch to default fb? - u8 swap_null; - u8 surf_null[SWAP_SURFACES]; - u8 unkoutbool_null; - u8 padding[1]; -} __packed; - -struct dcp_swap_submit_resp { - u8 unkoutbool; - u32 ret; - u8 padding[3]; -} __packed; - -struct dc_swap_complete_resp { - u32 swap_id; - u8 unkbool; - u64 swap_data; - u8 swap_info[0x6c4]; - u32 unkint; - u8 swap_info_null; -} __packed; - struct dcp_get_uint_prop_req { char obj[4]; char key[0x40]; @@ -435,4 +341,13 @@ struct iomfb_get_color_remap_mode_resp { u32 ret; } __packed; +struct iomfb_last_client_close_req { + u8 unkint_null; + u8 padding[3]; +} __packed; + +struct iomfb_last_client_close_resp { + u32 unkint; +} __packed; + #endif diff --git a/drivers/gpu/drm/apple/iomfb_internal.h b/drivers/gpu/drm/apple/iomfb_internal.h new file mode 100644 index 00000000000000..401b6ec32848d3 --- /dev/null +++ b/drivers/gpu/drm/apple/iomfb_internal.h @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright The Asahi Linux Contributors */ + +#include +#include + +#include "dcp-internal.h" + +struct apple_dcp; + +typedef void (*dcp_callback_t)(struct apple_dcp *, void *, void *); + + +#define DCP_THUNK_VOID(func, handle) \ + static void func(struct apple_dcp *dcp, bool oob, dcp_callback_t cb, \ + void *cookie) \ + { \ + dcp_push(dcp, oob, &dcp_methods[handle], 0, 0, NULL, cb, cookie); \ + } + +#define DCP_THUNK_OUT(func, handle, T) \ + static void func(struct apple_dcp *dcp, bool oob, dcp_callback_t cb, \ + void *cookie) \ + { \ + dcp_push(dcp, oob, &dcp_methods[handle], 0, sizeof(T), NULL, cb, cookie); \ + } + +#define DCP_THUNK_IN(func, handle, T) \ + static void func(struct apple_dcp *dcp, bool oob, T *data, \ + dcp_callback_t cb, void *cookie) \ + { \ + dcp_push(dcp, oob, &dcp_methods[handle], sizeof(T), 0, data, cb, cookie); \ + } + +#define DCP_THUNK_INOUT(func, handle, T_in, T_out) \ + static void func(struct apple_dcp *dcp, bool oob, T_in *data, \ + dcp_callback_t cb, void *cookie) \ + { \ + dcp_push(dcp, oob, &dcp_methods[handle], sizeof(T_in), sizeof(T_out), data, \ + cb, cookie); \ + } + +#define IOMFB_THUNK_INOUT(name) \ + static void iomfb_ ## name(struct apple_dcp *dcp, bool oob, \ + struct iomfb_ ## name ## _req *data, \ + dcp_callback_t cb, void *cookie) \ + { \ + dcp_push(dcp, oob, &dcp_methods[iomfbep_ ## name], \ + sizeof(struct iomfb_ ## name ## _req), \ + sizeof(struct iomfb_ ## name ## _resp), \ + data, cb, cookie); \ + } + +/* + * Define type-safe trampolines. Define typedefs to enforce type-safety on the + * input data (so if the types don't match, gcc errors out). + */ + +#define TRAMPOLINE_VOID(func, handler) \ + static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ + { \ + trace_iomfb_callback(dcp, tag, #handler); \ + handler(dcp); \ + return true; \ + } + +#define TRAMPOLINE_IN(func, handler, T_in) \ + typedef void (*callback_##handler)(struct apple_dcp *, T_in *); \ + \ + static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ + { \ + callback_##handler cb = handler; \ + \ + trace_iomfb_callback(dcp, tag, #handler); \ + cb(dcp, in); \ + return true; \ + } + +#define TRAMPOLINE_INOUT(func, handler, T_in, T_out) \ + typedef T_out (*callback_##handler)(struct apple_dcp *, T_in *); \ + \ + static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ + { \ + T_out *typed_out = out; \ + callback_##handler cb = handler; \ + \ + trace_iomfb_callback(dcp, tag, #handler); \ + *typed_out = cb(dcp, in); \ + return true; \ + } + +#define TRAMPOLINE_OUT(func, handler, T_out) \ + static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ + { \ + T_out *typed_out = out; \ + \ + trace_iomfb_callback(dcp, tag, #handler); \ + *typed_out = handler(dcp); \ + return true; \ + } + +/* Call a DCP function given by a tag */ +void dcp_push(struct apple_dcp *dcp, bool oob, const struct dcp_method_entry *call, + u32 in_len, u32 out_len, void *data, dcp_callback_t cb, + void *cookie); + +/* Parse a callback tag "D123" into the ID 123. Returns -EINVAL on failure. */ +int dcp_parse_tag(char tag[4]); + +void dcp_ack(struct apple_dcp *dcp, enum dcp_context_id context); + +/* + * DRM specifies rectangles as start and end coordinates. DCP specifies + * rectangles as a start coordinate and a width/height. Convert a DRM rectangle + * to a DCP rectangle. + */ +struct dcp_rect drm_to_dcp_rect(struct drm_rect *rect); + +u32 drm_format_to_dcp(u32 drm); + +/* The user may own drm_display_mode, so we need to search for our copy */ +struct dcp_display_mode *lookup_mode(struct apple_dcp *dcp, + const struct drm_display_mode *mode); diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c new file mode 100644 index 00000000000000..c6c62e4bc4c125 --- /dev/null +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -0,0 +1,1344 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Copyright 2021 Alyssa Rosenzweig + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "dcp.h" +#include "dcp-internal.h" +#include "iomfb.h" +#include "iomfb_internal.h" +#include "parser.h" +#include "trace.h" +#include "version_utils.h" + +/* Register defines used in bandwidth setup structure */ +#define REG_SCRATCH (0x14) +#define REG_SCRATCH_T600X (0x988) +#define REG_DOORBELL (0x0) +#define REG_DOORBELL_BIT (2) + +struct dcp_wait_cookie { + struct kref refcount; + struct completion done; +}; + +static void release_wait_cookie(struct kref *ref) +{ + struct dcp_wait_cookie *cookie; + cookie = container_of(ref, struct dcp_wait_cookie, refcount); + + kfree(cookie); +} + +DCP_THUNK_OUT(iomfb_a131_pmu_service_matched, iomfbep_a131_pmu_service_matched, u32); +DCP_THUNK_OUT(iomfb_a132_backlight_service_matched, iomfbep_a132_backlight_service_matched, u32); +DCP_THUNK_OUT(iomfb_a358_vi_set_temperature_hint, iomfbep_a358_vi_set_temperature_hint, u32); + +IOMFB_THUNK_INOUT(get_color_remap_mode); +IOMFB_THUNK_INOUT(last_client_close); + +DCP_THUNK_INOUT(dcp_swap_submit, dcpep_swap_submit, + struct DCP_FW_NAME(dcp_swap_submit_req), + struct DCP_FW_NAME(dcp_swap_submit_resp)); + +DCP_THUNK_INOUT(dcp_swap_start, dcpep_swap_start, struct dcp_swap_start_req, + struct dcp_swap_start_resp); + +DCP_THUNK_INOUT(dcp_set_power_state, dcpep_set_power_state, + struct dcp_set_power_state_req, + struct dcp_set_power_state_resp); + +DCP_THUNK_INOUT(dcp_set_digital_out_mode, dcpep_set_digital_out_mode, + struct dcp_set_digital_out_mode_req, u32); + +DCP_THUNK_INOUT(dcp_set_display_device, dcpep_set_display_device, u32, u32); + +DCP_THUNK_OUT(dcp_set_display_refresh_properties, + dcpep_set_display_refresh_properties, u32); + +#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) +DCP_THUNK_INOUT(dcp_late_init_signal, dcpep_late_init_signal, u32, u32); +#else +DCP_THUNK_OUT(dcp_late_init_signal, dcpep_late_init_signal, u32); +#endif +DCP_THUNK_IN(dcp_flush_supports_power, dcpep_flush_supports_power, u32); +DCP_THUNK_OUT(dcp_create_default_fb, dcpep_create_default_fb, u32); +DCP_THUNK_OUT(dcp_start_signal, dcpep_start_signal, u32); +DCP_THUNK_VOID(dcp_setup_video_limits, dcpep_setup_video_limits); +DCP_THUNK_VOID(dcp_set_create_dfb, dcpep_set_create_dfb); +DCP_THUNK_VOID(dcp_first_client_open, dcpep_first_client_open); + +DCP_THUNK_INOUT(dcp_set_parameter_dcp, dcpep_set_parameter_dcp, + struct dcp_set_parameter_dcp, u32); + +DCP_THUNK_INOUT(dcp_enable_disable_video_power_savings, + dcpep_enable_disable_video_power_savings, u32, int); + +DCP_THUNK_OUT(dcp_is_main_display, dcpep_is_main_display, u32); + +/* DCP callback handlers */ +static void dcpep_cb_nop(struct apple_dcp *dcp) +{ + /* No operation */ +} + +static u8 dcpep_cb_true(struct apple_dcp *dcp) +{ + return true; +} + +static u8 dcpep_cb_false(struct apple_dcp *dcp) +{ + return false; +} + +static u32 dcpep_cb_zero(struct apple_dcp *dcp) +{ + return 0; +} + +static void dcpep_cb_swap_complete(struct apple_dcp *dcp, + struct DCP_FW_NAME(dc_swap_complete_resp) *resp) +{ + trace_iomfb_swap_complete(dcp, resp->swap_id); + + dcp_drm_crtc_vblank(dcp->crtc); +} + +/* special */ +static void complete_vi_set_temperature_hint(struct apple_dcp *dcp, void *out, void *cookie) +{ + // ack D100 cb_match_pmu_service + dcp_ack(dcp, DCP_CONTEXT_CB); +} + +static bool iomfbep_cb_match_pmu_service(struct apple_dcp *dcp, int tag, void *out, void *in) +{ + trace_iomfb_callback(dcp, tag, __func__); + iomfb_a358_vi_set_temperature_hint(dcp, false, + complete_vi_set_temperature_hint, + NULL); + + // return false for deferred ACK + return false; +} + +static void complete_pmu_service_matched(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct dcp_channel *ch = &dcp->ch_cb; + u8 *succ = ch->output[ch->depth - 1]; + + *succ = true; + + // ack D206 cb_match_pmu_service_2 + dcp_ack(dcp, DCP_CONTEXT_CB); +} + +static bool iomfbep_cb_match_pmu_service_2(struct apple_dcp *dcp, int tag, void *out, void *in) +{ + trace_iomfb_callback(dcp, tag, __func__); + + iomfb_a131_pmu_service_matched(dcp, false, complete_pmu_service_matched, + out); + + // return false for deferred ACK + return false; +} + +static void complete_backlight_service_matched(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct dcp_channel *ch = &dcp->ch_cb; + u8 *succ = ch->output[ch->depth - 1]; + + *succ = true; + + // ack D206 cb_match_backlight_service + dcp_ack(dcp, DCP_CONTEXT_CB); +} + +static bool iomfbep_cb_match_backlight_service(struct apple_dcp *dcp, int tag, void *out, void *in) +{ + trace_iomfb_callback(dcp, tag, __func__); + + iomfb_a132_backlight_service_matched(dcp, false, complete_backlight_service_matched, out); + + // return false for deferred ACK + return false; +} + +static void iomfb_cb_pr_publish(struct apple_dcp *dcp, struct iomfb_property *prop) +{ + switch (prop->id) { + case IOMFB_PROPERTY_NITS: + { + dcp->brightness.nits = prop->value / dcp->brightness.scale; + /* notify backlight device of the initial brightness */ + if (!dcp->brightness.bl_dev && dcp->brightness.maximum > 0) + schedule_work(&dcp->bl_register_wq); + trace_iomfb_brightness(dcp, prop->value); + break; + } + default: + dev_dbg(dcp->dev, "pr_publish: id: %d = %u\n", prop->id, prop->value); + } +} + +static struct dcp_get_uint_prop_resp +dcpep_cb_get_uint_prop(struct apple_dcp *dcp, struct dcp_get_uint_prop_req *req) +{ + struct dcp_get_uint_prop_resp resp = (struct dcp_get_uint_prop_resp){ + .value = 0 + }; + + if (dcp->panel.has_mini_led && + memcmp(req->obj, "SUMP", sizeof(req->obj)) == 0) { /* "PMUS */ + if (strncmp(req->key, "Temperature", sizeof(req->key)) == 0) { + /* + * TODO: value from j314c, find out if it is temperature in + * centigrade C and which temperature sensor reports it + */ + resp.value = 3029; + resp.ret = true; + } + } + + return resp; +} + +static u8 iomfbep_cb_sr_set_property_int(struct apple_dcp *dcp, + struct iomfb_sr_set_property_int_req *req) +{ + if (memcmp(req->obj, "FMOI", sizeof(req->obj)) == 0) { /* "IOMF */ + if (strncmp(req->key, "Brightness_Scale", sizeof(req->key)) == 0) { + if (!req->value_null) + dcp->brightness.scale = req->value; + } + } + + return 1; +} + +static void iomfbep_cb_set_fx_prop(struct apple_dcp *dcp, struct iomfb_set_fx_prop_req *req) +{ + // TODO: trace this, see if there properties which needs to used later +} + +/* + * Callback to map a buffer allocated with allocate_buf for PIODMA usage. + * PIODMA is separate from the main DCP and uses own IOVA space on a dedicated + * stream of the display DART, rather than the expected DCP DART. + * + * XXX: This relies on dma_get_sgtable in concert with dma_map_sgtable, which + * is a "fundamentally unsafe" operation according to the docs. And yet + * everyone does it... + */ +static struct dcp_map_buf_resp dcpep_cb_map_piodma(struct apple_dcp *dcp, + struct dcp_map_buf_req *req) +{ + struct sg_table *map; + int ret; + + if (req->buffer >= ARRAY_SIZE(dcp->memdesc)) + goto reject; + + map = &dcp->memdesc[req->buffer].map; + + if (!map->sgl) + goto reject; + + /* Use PIODMA device instead of DCP to map against the right IOMMU. */ + ret = dma_map_sgtable(&dcp->piodma->dev, map, DMA_BIDIRECTIONAL, 0); + + if (ret) + goto reject; + + return (struct dcp_map_buf_resp){ .dva = sg_dma_address(map->sgl) }; + +reject: + dev_err(dcp->dev, "denying map of invalid buffer %llx for pidoma\n", + req->buffer); + return (struct dcp_map_buf_resp){ .ret = EINVAL }; +} + +static void dcpep_cb_unmap_piodma(struct apple_dcp *dcp, + struct dcp_unmap_buf_resp *resp) +{ + struct sg_table *map; + dma_addr_t dma_addr; + + if (resp->buffer >= ARRAY_SIZE(dcp->memdesc)) { + dev_warn(dcp->dev, "unmap request for out of range buffer %llu", + resp->buffer); + return; + } + + map = &dcp->memdesc[resp->buffer].map; + + if (!map->sgl) { + dev_warn(dcp->dev, + "unmap for non-mapped buffer %llu iova:0x%08llx", + resp->buffer, resp->dva); + return; + } + + dma_addr = sg_dma_address(map->sgl); + if (dma_addr != resp->dva) { + dev_warn(dcp->dev, "unmap buffer %llu address mismatch dma_addr:%llx dva:%llx", + resp->buffer, dma_addr, resp->dva); + return; + } + + /* Use PIODMA device instead of DCP to unmap from the right IOMMU. */ + dma_unmap_sgtable(&dcp->piodma->dev, map, DMA_BIDIRECTIONAL, 0); +} + +/* + * Allocate an IOVA contiguous buffer mapped to the DCP. The buffer need not be + * physically contigiuous, however we should save the sgtable in case the + * buffer needs to be later mapped for PIODMA. + */ +static struct dcp_allocate_buffer_resp +dcpep_cb_allocate_buffer(struct apple_dcp *dcp, + struct dcp_allocate_buffer_req *req) +{ + struct dcp_allocate_buffer_resp resp = { 0 }; + struct dcp_mem_descriptor *memdesc; + u32 id; + + resp.dva_size = ALIGN(req->size, 4096); + resp.mem_desc_id = + find_first_zero_bit(dcp->memdesc_map, DCP_MAX_MAPPINGS); + + if (resp.mem_desc_id >= DCP_MAX_MAPPINGS) { + dev_warn(dcp->dev, "DCP overflowed mapping table, ignoring"); + resp.dva_size = 0; + resp.mem_desc_id = 0; + return resp; + } + id = resp.mem_desc_id; + set_bit(id, dcp->memdesc_map); + + memdesc = &dcp->memdesc[id]; + + memdesc->size = resp.dva_size; + memdesc->buf = dma_alloc_coherent(dcp->dev, memdesc->size, + &memdesc->dva, GFP_KERNEL); + + dma_get_sgtable(dcp->dev, &memdesc->map, memdesc->buf, memdesc->dva, + memdesc->size); + resp.dva = memdesc->dva; + + return resp; +} + +static u8 dcpep_cb_release_mem_desc(struct apple_dcp *dcp, u32 *mem_desc_id) +{ + struct dcp_mem_descriptor *memdesc; + u32 id = *mem_desc_id; + + if (id >= DCP_MAX_MAPPINGS) { + dev_warn(dcp->dev, + "unmap request for out of range mem_desc_id %u", id); + return 0; + } + + if (!test_and_clear_bit(id, dcp->memdesc_map)) { + dev_warn(dcp->dev, "unmap request for unused mem_desc_id %u", + id); + return 0; + } + + memdesc = &dcp->memdesc[id]; + if (memdesc->buf) { + dma_free_coherent(dcp->dev, memdesc->size, memdesc->buf, + memdesc->dva); + + memdesc->buf = NULL; + memset(&memdesc->map, 0, sizeof(memdesc->map)); + } else { + memdesc->reg = 0; + } + + memdesc->size = 0; + + return 1; +} + +/* Validate that the specified region is a display register */ +static bool is_disp_register(struct apple_dcp *dcp, u64 start, u64 end) +{ + int i; + + for (i = 0; i < dcp->nr_disp_registers; ++i) { + struct resource *r = dcp->disp_registers[i]; + + if ((start >= r->start) && (end <= r->end)) + return true; + } + + return false; +} + +/* + * Map contiguous physical memory into the DCP's address space. The firmware + * uses this to map the display registers we advertise in + * sr_map_device_memory_with_index, so we bounds check against that to guard + * safe against malicious coprocessors. + */ +static struct dcp_map_physical_resp +dcpep_cb_map_physical(struct apple_dcp *dcp, struct dcp_map_physical_req *req) +{ + int size = ALIGN(req->size, 4096); + u32 id; + + if (!is_disp_register(dcp, req->paddr, req->paddr + size - 1)) { + dev_err(dcp->dev, "refusing to map phys address %llx size %llx", + req->paddr, req->size); + return (struct dcp_map_physical_resp){}; + } + + id = find_first_zero_bit(dcp->memdesc_map, DCP_MAX_MAPPINGS); + set_bit(id, dcp->memdesc_map); + dcp->memdesc[id].size = size; + dcp->memdesc[id].reg = req->paddr; + + return (struct dcp_map_physical_resp){ + .dva_size = size, + .mem_desc_id = id, + .dva = dma_map_resource(dcp->dev, req->paddr, size, + DMA_BIDIRECTIONAL, 0), + }; +} + +static u64 dcpep_cb_get_frequency(struct apple_dcp *dcp) +{ + return clk_get_rate(dcp->clk); +} + +static struct DCP_FW_NAME(dcp_map_reg_resp) dcpep_cb_map_reg(struct apple_dcp *dcp, + struct DCP_FW_NAME(dcp_map_reg_req) *req) +{ + if (req->index >= dcp->nr_disp_registers) { + dev_warn(dcp->dev, "attempted to read invalid reg index %u", + req->index); + + return (struct DCP_FW_NAME(dcp_map_reg_resp)){ .ret = 1 }; + } else { + struct resource *rsrc = dcp->disp_registers[req->index]; +#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) + dma_addr_t dva = dma_map_resource(dcp->dev, rsrc->start, resource_size(rsrc), + DMA_BIDIRECTIONAL, 0); + WARN_ON(dva == DMA_MAPPING_ERROR); +#endif + + return (struct DCP_FW_NAME(dcp_map_reg_resp)){ + .addr = rsrc->start, + .length = resource_size(rsrc), +#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) + .dva = dva, +#endif + }; + } +} + +static struct dcp_read_edt_data_resp +dcpep_cb_read_edt_data(struct apple_dcp *dcp, struct dcp_read_edt_data_req *req) +{ + return (struct dcp_read_edt_data_resp){ + .value[0] = req->value[0], + .ret = 0, + }; +} + +static void iomfbep_cb_enable_backlight_message_ap_gated(struct apple_dcp *dcp, + u8 *enabled) +{ + /* + * update backlight brightness on next swap, on non mini-LED displays + * DCP seems to set an invalid iDAC value after coming out of DPMS. + * syslog: "[BrightnessLCD.cpp:743][AFK]nitsToDBV: iDAC out of range" + */ + dcp->brightness.update = true; +} + +/* Chunked data transfer for property dictionaries */ +static u8 dcpep_cb_prop_start(struct apple_dcp *dcp, u32 *length) +{ + if (dcp->chunks.data != NULL) { + dev_warn(dcp->dev, "ignoring spurious transfer start\n"); + return false; + } + + dcp->chunks.length = *length; + dcp->chunks.data = devm_kzalloc(dcp->dev, *length, GFP_KERNEL); + + if (!dcp->chunks.data) { + dev_warn(dcp->dev, "failed to allocate chunks\n"); + return false; + } + + return true; +} + +static u8 dcpep_cb_prop_chunk(struct apple_dcp *dcp, + struct dcp_set_dcpav_prop_chunk_req *req) +{ + if (!dcp->chunks.data) { + dev_warn(dcp->dev, "ignoring spurious chunk\n"); + return false; + } + + if (req->offset + req->length > dcp->chunks.length) { + dev_warn(dcp->dev, "ignoring overflowing chunk\n"); + return false; + } + + memcpy(dcp->chunks.data + req->offset, req->data, req->length); + return true; +} + +static bool dcpep_process_chunks(struct apple_dcp *dcp, + struct dcp_set_dcpav_prop_end_req *req) +{ + struct dcp_parse_ctx ctx; + int ret; + + if (!dcp->chunks.data) { + dev_warn(dcp->dev, "ignoring spurious end\n"); + return false; + } + + /* used just as opaque pointer for tracing */ + ctx.dcp = dcp; + + ret = parse(dcp->chunks.data, dcp->chunks.length, &ctx); + + if (ret) { + dev_warn(dcp->dev, "bad header on dcpav props\n"); + return false; + } + + if (!strcmp(req->key, "TimingElements")) { + dcp->modes = enumerate_modes(&ctx, &dcp->nr_modes, + dcp->width_mm, dcp->height_mm, + dcp->notch_height); + + if (IS_ERR(dcp->modes)) { + dev_warn(dcp->dev, "failed to parse modes\n"); + dcp->modes = NULL; + dcp->nr_modes = 0; + return false; + } + } else if (!strcmp(req->key, "DisplayAttributes")) { + /* DisplayAttributes are empty for integrated displays, use + * display dimensions read from the devicetree + */ + if (dcp->main_display) { + ret = parse_display_attributes(&ctx, &dcp->width_mm, + &dcp->height_mm); + + if (ret) { + dev_warn(dcp->dev, "failed to parse display attribs\n"); + return false; + } + } + + dcp_set_dimensions(dcp); + } + + return true; +} + +static u8 dcpep_cb_prop_end(struct apple_dcp *dcp, + struct dcp_set_dcpav_prop_end_req *req) +{ + u8 resp = dcpep_process_chunks(dcp, req); + + /* Reset for the next transfer */ + devm_kfree(dcp->dev, dcp->chunks.data); + dcp->chunks.data = NULL; + + return resp; +} + +/* Boot sequence */ +static void boot_done(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct dcp_channel *ch = &dcp->ch_cb; + u8 *succ = ch->output[ch->depth - 1]; + dev_dbg(dcp->dev, "boot done"); + + *succ = true; + dcp_ack(dcp, DCP_CONTEXT_CB); +} + +static void boot_5(struct apple_dcp *dcp, void *out, void *cookie) +{ + dcp_set_display_refresh_properties(dcp, false, boot_done, NULL); +} + +static void boot_4(struct apple_dcp *dcp, void *out, void *cookie) +{ +#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) + u32 v_true = 1; + dcp_late_init_signal(dcp, false, &v_true, boot_5, NULL); +#else + dcp_late_init_signal(dcp, false, boot_5, NULL); +#endif +} + +static void boot_3(struct apple_dcp *dcp, void *out, void *cookie) +{ + u32 v_true = true; + + dcp_flush_supports_power(dcp, false, &v_true, boot_4, NULL); +} + +static void boot_2(struct apple_dcp *dcp, void *out, void *cookie) +{ + dcp_setup_video_limits(dcp, false, boot_3, NULL); +} + +static void boot_1_5(struct apple_dcp *dcp, void *out, void *cookie) +{ + dcp_create_default_fb(dcp, false, boot_2, NULL); +} + +/* Use special function signature to defer the ACK */ +static bool dcpep_cb_boot_1(struct apple_dcp *dcp, int tag, void *out, void *in) +{ + trace_iomfb_callback(dcp, tag, __func__); + dcp_set_create_dfb(dcp, false, boot_1_5, NULL); + return false; +} + +static struct dcp_rt_bandwidth dcpep_cb_rt_bandwidth(struct apple_dcp *dcp) +{ + if (dcp->disp_registers[5] && dcp->disp_registers[6]) + return (struct dcp_rt_bandwidth){ + .reg_scratch = + dcp->disp_registers[5]->start + REG_SCRATCH, + .reg_doorbell = + dcp->disp_registers[6]->start + REG_DOORBELL, + .doorbell_bit = REG_DOORBELL_BIT, + + .padding[3] = 0x4, // XXX: required by 11.x firmware + }; + else if (dcp->disp_registers[4]) + return (struct dcp_rt_bandwidth){ + .reg_scratch = dcp->disp_registers[4]->start + + REG_SCRATCH_T600X, + .reg_doorbell = 0, + .doorbell_bit = 0, + }; + else + return (struct dcp_rt_bandwidth){ + .reg_scratch = 0, + .reg_doorbell = 0, + .doorbell_bit = 0, + }; +} + +/* Callback to get the current time as milliseconds since the UNIX epoch */ +static u64 dcpep_cb_get_time(struct apple_dcp *dcp) +{ + return ktime_to_ms(ktime_get_real()); +} + +struct dcp_swap_cookie { + struct kref refcount; + struct completion done; + u32 swap_id; +}; + +static void release_swap_cookie(struct kref *ref) +{ + struct dcp_swap_cookie *cookie; + cookie = container_of(ref, struct dcp_swap_cookie, refcount); + + kfree(cookie); +} + +static void dcp_swap_cleared(struct apple_dcp *dcp, void *data, void *cookie) +{ + struct DCP_FW_NAME(dcp_swap_submit_resp) *resp = data; + dev_dbg(dcp->dev, "%s", __func__); + + if (cookie) { + struct dcp_swap_cookie *info = cookie; + complete(&info->done); + kref_put(&info->refcount, release_swap_cookie); + } + + if (resp->ret) { + dev_err(dcp->dev, "swap_clear failed! status %u\n", resp->ret); + dcp_drm_crtc_vblank(dcp->crtc); + return; + } + + while (!list_empty(&dcp->swapped_out_fbs)) { + struct dcp_fb_reference *entry; + entry = list_first_entry(&dcp->swapped_out_fbs, + struct dcp_fb_reference, head); + if (entry->fb) + drm_framebuffer_put(entry->fb); + list_del(&entry->head); + kfree(entry); + } +} + +static void dcp_swap_clear_started(struct apple_dcp *dcp, void *data, + void *cookie) +{ + struct dcp_swap_start_resp *resp = data; + dev_dbg(dcp->dev, "%s swap_id: %u", __func__, resp->swap_id); + DCP_FW_UNION(dcp->swap).swap.swap_id = resp->swap_id; + + if (cookie) { + struct dcp_swap_cookie *info = cookie; + info->swap_id = resp->swap_id; + } + + dcp_swap_submit(dcp, false, &DCP_FW_UNION(dcp->swap), dcp_swap_cleared, cookie); +} + +static void dcp_on_final(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct dcp_wait_cookie *wait = cookie; + dev_dbg(dcp->dev, "%s", __func__); + + if (wait) { + complete(&wait->done); + kref_put(&wait->refcount, release_wait_cookie); + } +} + +static void dcp_on_set_power_state(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct dcp_set_power_state_req req = { + .unklong = 1, + }; + dev_dbg(dcp->dev, "%s", __func__); + + dcp_set_power_state(dcp, false, &req, dcp_on_final, cookie); +} + +static void dcp_on_set_parameter(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct dcp_set_parameter_dcp param = { + .param = 14, + .value = { 0 }, +#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) + .count = 3, +#else + .count = 1, +#endif + }; + dev_dbg(dcp->dev, "%s", __func__); + + dcp_set_parameter_dcp(dcp, false, ¶m, dcp_on_set_power_state, cookie); +} + +void DCP_FW_NAME(iomfb_poweron)(struct apple_dcp *dcp) +{ + struct dcp_wait_cookie *cookie; + int ret; + u32 handle; + dev_err(dcp->dev, "dcp_poweron() starting\n"); + + dev_dbg(dcp->dev, "%s", __func__); + + cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); + if (!cookie) + return; + + init_completion(&cookie->done); + kref_init(&cookie->refcount); + /* increase refcount to ensure the receiver has a reference */ + kref_get(&cookie->refcount); + + if (dcp->main_display) { + handle = 0; + dcp_set_display_device(dcp, false, &handle, dcp_on_set_power_state, + cookie); + } else { + handle = 2; + dcp_set_display_device(dcp, false, &handle, + dcp_on_set_parameter, cookie); + } + ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(500)); + + if (ret == 0) + dev_warn(dcp->dev, "wait for power timed out"); + + kref_put(&cookie->refcount, release_wait_cookie);; + + /* Force a brightness update after poweron, to restore the brightness */ + dcp->brightness.update = true; +} + +static void complete_set_powerstate(struct apple_dcp *dcp, void *out, + void *cookie) +{ + struct dcp_wait_cookie *wait = cookie; + + if (wait) { + complete(&wait->done); + kref_put(&wait->refcount, release_wait_cookie); + } +} + +static void last_client_closed_poff(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct dcp_set_power_state_req power_req = { + .unklong = 0, + }; + dcp_set_power_state(dcp, false, &power_req, complete_set_powerstate, + cookie); +} + +void DCP_FW_NAME(iomfb_poweroff)(struct apple_dcp *dcp) +{ + int ret, swap_id; + struct iomfb_last_client_close_req last_client_req = {}; + struct dcp_swap_cookie *cookie; + struct dcp_wait_cookie *poff_cookie; + struct dcp_swap_start_req swap_req = { 0 }; + struct DCP_FW_NAME(dcp_swap_submit_req) *swap = &DCP_FW_UNION(dcp->swap); + + dev_dbg(dcp->dev, "%s", __func__); + + cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); + if (!cookie) + return; + init_completion(&cookie->done); + kref_init(&cookie->refcount); + /* increase refcount to ensure the receiver has a reference */ + kref_get(&cookie->refcount); + + // clear surfaces + memset(swap, 0, sizeof(*swap)); + + swap->swap.swap_enabled = + swap->swap.swap_completed = IOMFB_SET_BACKGROUND | 0xF; + swap->swap.bg_color = 0xFF000000; + + /* + * Turn off the backlight. This matters because the DCP's idea of + * backlight brightness gets desynced after a power change, and it + * needs to be told it's going to turn off so it will consider the + * subsequent update on poweron an actual change and restore the + * brightness. + */ + swap->swap.bl_unk = 1; + swap->swap.bl_value = 0; + swap->swap.bl_power = 0; + + for (int l = 0; l < SWAP_SURFACES; l++) + swap->surf_null[l] = true; +#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) + for (int l = 0; l < 5; l++) + swap->surf2_null[l] = true; + swap->unkU32Ptr_null = true; + swap->unkU32out_null = true; +#endif + + dcp_swap_start(dcp, false, &swap_req, dcp_swap_clear_started, cookie); + + ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(50)); + swap_id = cookie->swap_id; + kref_put(&cookie->refcount, release_swap_cookie); + if (ret <= 0) { + dcp->crashed = true; + return; + } + + dev_dbg(dcp->dev, "%s: clear swap submitted: %u", __func__, swap_id); + + poff_cookie = kzalloc(sizeof(*poff_cookie), GFP_KERNEL); + if (!poff_cookie) + return; + init_completion(&poff_cookie->done); + kref_init(&poff_cookie->refcount); + /* increase refcount to ensure the receiver has a reference */ + kref_get(&poff_cookie->refcount); + + iomfb_last_client_close(dcp, false, &last_client_req, + last_client_closed_poff, poff_cookie); + ret = wait_for_completion_timeout(&poff_cookie->done, + msecs_to_jiffies(1000)); + + if (ret == 0) + dev_warn(dcp->dev, "setPowerState(0) timeout %u ms", 1000); + else if (ret > 0) + dev_dbg(dcp->dev, + "setPowerState(0) finished with %d ms to spare", + jiffies_to_msecs(ret)); + + kref_put(&poff_cookie->refcount, release_wait_cookie); + dev_dbg(dcp->dev, "%s: setPowerState(0) done", __func__); + + dev_err(dcp->dev, "dcp_poweroff() done\n"); +} + +static void last_client_closed_sleep(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct dcp_set_power_state_req power_req = { + .unklong = 0, + }; + dcp_set_power_state(dcp, false, &power_req, complete_set_powerstate, cookie); +} + +void DCP_FW_NAME(iomfb_sleep)(struct apple_dcp *dcp) +{ + int ret; + struct iomfb_last_client_close_req req = {}; + + struct dcp_wait_cookie *cookie; + + cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); + if (!cookie) + return; + init_completion(&cookie->done); + kref_init(&cookie->refcount); + /* increase refcount to ensure the receiver has a reference */ + kref_get(&cookie->refcount); + + iomfb_last_client_close(dcp, false, &req, last_client_closed_sleep, + cookie); + ret = wait_for_completion_timeout(&cookie->done, + msecs_to_jiffies(1000)); + + if (ret == 0) + dev_warn(dcp->dev, "setDCPPower(0) timeout %u ms", 1000); + + kref_put(&cookie->refcount, release_wait_cookie); + dev_dbg(dcp->dev, "%s: setDCPPower(0) done", __func__); + + dev_err(dcp->dev, "dcp_sleep() done\n"); +} + +static void dcpep_cb_hotplug(struct apple_dcp *dcp, u64 *connected) +{ + struct apple_connector *connector = dcp->connector; + + /* DCP issues hotplug_gated callbacks after SetPowerState() calls on + * devices with display (macbooks, imacs). This must not result in + * connector state changes on DRM side. Some applications won't enable + * a CRTC with a connector in disconnected state. Weston after DPMS off + * is one example. dcp_is_main_display() returns true on devices with + * integrated display. Ignore the hotplug_gated() callbacks there. + */ + if (dcp->main_display) + return; + + /* Hotplug invalidates mode. DRM doesn't always handle this. */ + if (!(*connected)) { + dcp->valid_mode = false; + /* after unplug swap will not complete until the next + * set_digital_out_mode */ + schedule_work(&dcp->vblank_wq); + } + + if (connector && connector->connected != !!(*connected)) { + connector->connected = !!(*connected); + dcp->valid_mode = false; + schedule_work(&connector->hotplug_wq); + } +} + +static void +dcpep_cb_swap_complete_intent_gated(struct apple_dcp *dcp, + struct dcp_swap_complete_intent_gated *info) +{ + trace_iomfb_swap_complete_intent_gated(dcp, info->swap_id, + info->width, info->height); +} + +TRAMPOLINE_VOID(trampoline_nop, dcpep_cb_nop); +TRAMPOLINE_OUT(trampoline_true, dcpep_cb_true, u8); +TRAMPOLINE_OUT(trampoline_false, dcpep_cb_false, u8); +TRAMPOLINE_OUT(trampoline_zero, dcpep_cb_zero, u32); +TRAMPOLINE_IN(trampoline_swap_complete, dcpep_cb_swap_complete, + struct DCP_FW_NAME(dc_swap_complete_resp)); +TRAMPOLINE_INOUT(trampoline_get_uint_prop, dcpep_cb_get_uint_prop, + struct dcp_get_uint_prop_req, struct dcp_get_uint_prop_resp); +TRAMPOLINE_IN(trampoline_set_fx_prop, iomfbep_cb_set_fx_prop, + struct iomfb_set_fx_prop_req) +TRAMPOLINE_INOUT(trampoline_map_piodma, dcpep_cb_map_piodma, + struct dcp_map_buf_req, struct dcp_map_buf_resp); +TRAMPOLINE_IN(trampoline_unmap_piodma, dcpep_cb_unmap_piodma, + struct dcp_unmap_buf_resp); +TRAMPOLINE_INOUT(trampoline_sr_set_property_int, iomfbep_cb_sr_set_property_int, + struct iomfb_sr_set_property_int_req, u8); +TRAMPOLINE_INOUT(trampoline_allocate_buffer, dcpep_cb_allocate_buffer, + struct dcp_allocate_buffer_req, + struct dcp_allocate_buffer_resp); +TRAMPOLINE_INOUT(trampoline_map_physical, dcpep_cb_map_physical, + struct dcp_map_physical_req, struct dcp_map_physical_resp); +TRAMPOLINE_INOUT(trampoline_release_mem_desc, dcpep_cb_release_mem_desc, u32, + u8); +TRAMPOLINE_INOUT(trampoline_map_reg, dcpep_cb_map_reg, + struct DCP_FW_NAME(dcp_map_reg_req), + struct DCP_FW_NAME(dcp_map_reg_resp)); +TRAMPOLINE_INOUT(trampoline_read_edt_data, dcpep_cb_read_edt_data, + struct dcp_read_edt_data_req, struct dcp_read_edt_data_resp); +TRAMPOLINE_INOUT(trampoline_prop_start, dcpep_cb_prop_start, u32, u8); +TRAMPOLINE_INOUT(trampoline_prop_chunk, dcpep_cb_prop_chunk, + struct dcp_set_dcpav_prop_chunk_req, u8); +TRAMPOLINE_INOUT(trampoline_prop_end, dcpep_cb_prop_end, + struct dcp_set_dcpav_prop_end_req, u8); +TRAMPOLINE_OUT(trampoline_rt_bandwidth, dcpep_cb_rt_bandwidth, + struct dcp_rt_bandwidth); +TRAMPOLINE_OUT(trampoline_get_frequency, dcpep_cb_get_frequency, u64); +TRAMPOLINE_OUT(trampoline_get_time, dcpep_cb_get_time, u64); +TRAMPOLINE_IN(trampoline_hotplug, dcpep_cb_hotplug, u64); +TRAMPOLINE_IN(trampoline_swap_complete_intent_gated, + dcpep_cb_swap_complete_intent_gated, + struct dcp_swap_complete_intent_gated); +TRAMPOLINE_IN(trampoline_enable_backlight_message_ap_gated, + iomfbep_cb_enable_backlight_message_ap_gated, u8); +TRAMPOLINE_IN(trampoline_pr_publish, iomfb_cb_pr_publish, + struct iomfb_property); + +/* + * Callback for swap requests. If a swap failed, we'll never get a swap + * complete event so we need to fake a vblank event early to avoid a hang. + */ + +static void dcp_swapped(struct apple_dcp *dcp, void *data, void *cookie) +{ + struct DCP_FW_NAME(dcp_swap_submit_resp) *resp = data; + + if (resp->ret) { + dev_err(dcp->dev, "swap failed! status %u\n", resp->ret); + dcp_drm_crtc_vblank(dcp->crtc); + return; + } + + while (!list_empty(&dcp->swapped_out_fbs)) { + struct dcp_fb_reference *entry; + entry = list_first_entry(&dcp->swapped_out_fbs, + struct dcp_fb_reference, head); + if (entry->fb) + drm_framebuffer_put(entry->fb); + list_del(&entry->head); + kfree(entry); + } +} + +static void dcp_swap_started(struct apple_dcp *dcp, void *data, void *cookie) +{ + struct dcp_swap_start_resp *resp = data; + + DCP_FW_UNION(dcp->swap).swap.swap_id = resp->swap_id; + + trace_iomfb_swap_submit(dcp, resp->swap_id); + dcp_swap_submit(dcp, false, &DCP_FW_UNION(dcp->swap), dcp_swapped, NULL); +} + +/* Helpers to modeset and swap, used to flush */ +static void do_swap(struct apple_dcp *dcp, void *data, void *cookie) +{ + struct dcp_swap_start_req start_req = { 0 }; + dev_dbg(dcp->dev, "%s", __func__); + + if (dcp->connector && dcp->connector->connected) + dcp_swap_start(dcp, false, &start_req, dcp_swap_started, NULL); + else + dcp_drm_crtc_vblank(dcp->crtc); +} + +static void complete_set_digital_out_mode(struct apple_dcp *dcp, void *data, + void *cookie) +{ + struct dcp_wait_cookie *wait = cookie; + dev_dbg(dcp->dev, "%s", __func__); + + if (wait) { + complete(&wait->done); + kref_put(&wait->refcount, release_wait_cookie); + } +} + +void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, struct drm_atomic_state *state) +{ + struct drm_plane *plane; + struct drm_plane_state *new_state, *old_state; + struct drm_crtc_state *crtc_state; + struct DCP_FW_NAME(dcp_swap_submit_req) *req = &DCP_FW_UNION(dcp->swap); + int plane_idx, l; + int has_surface = 0; + bool modeset; + dev_dbg(dcp->dev, "%s", __func__); + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + modeset = drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode; + + /* Reset to defaults */ + memset(req, 0, sizeof(*req)); + for (l = 0; l < SWAP_SURFACES; l++) + req->surf_null[l] = true; +#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) + for (l = 0; l < 5; l++) + req->surf2_null[l] = true; + req->unkU32Ptr_null = true; + req->unkU32out_null = true; +#endif + + /* + * Clear all surfaces on startup. The boot framebuffer in surface 0 + * sticks around. + */ + if (!dcp->surfaces_cleared) { + req->swap.swap_enabled = IOMFB_SET_BACKGROUND | 0xF; + req->swap.bg_color = 0xFF000000; + dcp->surfaces_cleared = true; + } + + // Surface 0 has limitations at least on t600x. + l = 1; + for_each_oldnew_plane_in_state(state, plane, old_state, new_state, plane_idx) { + struct drm_framebuffer *fb = new_state->fb; + struct drm_gem_dma_object *obj; + struct drm_rect src_rect; + bool is_premultiplied = false; + + /* skip planes not for this crtc */ + if (old_state->crtc != crtc && new_state->crtc != crtc) + continue; + + WARN_ON(l >= SWAP_SURFACES); + + req->swap.swap_enabled |= BIT(l); + + if (old_state->fb && fb != old_state->fb) { + /* + * Race condition between a framebuffer unbind getting + * swapped out and GEM unreferencing a framebuffer. If + * we lose the race, the display gets IOVA faults and + * the DCP crashes. We need to extend the lifetime of + * the drm_framebuffer (and hence the GEM object) until + * after we get a swap complete for the swap unbinding + * it. + */ + struct dcp_fb_reference *entry = + kzalloc(sizeof(*entry), GFP_KERNEL); + if (entry) { + entry->fb = old_state->fb; + list_add_tail(&entry->head, + &dcp->swapped_out_fbs); + } + drm_framebuffer_get(old_state->fb); + } + + if (!new_state->fb) { + l += 1; + continue; + } + req->surf_null[l] = false; + has_surface = 1; + + /* + * DCP doesn't support XBGR8 / XRGB8 natively. Blending as + * pre-multiplied alpha with a black background can be used as + * workaround for the bottommost plane. + */ + if (fb->format->format == DRM_FORMAT_XRGB8888 || + fb->format->format == DRM_FORMAT_XBGR8888) + is_premultiplied = true; + + drm_rect_fp_to_int(&src_rect, &new_state->src); + + req->swap.src_rect[l] = drm_to_dcp_rect(&src_rect); + req->swap.dst_rect[l] = drm_to_dcp_rect(&new_state->dst); + + if (dcp->notch_height > 0) + req->swap.dst_rect[l].y += dcp->notch_height; + + /* the obvious helper call drm_fb_dma_get_gem_addr() adjusts + * the address for source x/y offsets. Since IOMFB has a direct + * support source position prefer that. + */ + obj = drm_fb_dma_get_gem_obj(fb, 0); + if (obj) + req->surf_iova[l] = obj->dma_addr + fb->offsets[0]; + + req->surf[l] = (struct DCP_FW_NAME(dcp_surface)){ + .is_premultiplied = is_premultiplied, + .format = drm_format_to_dcp(fb->format->format), + .xfer_func = DCP_XFER_FUNC_SDR, + .colorspace = DCP_COLORSPACE_NATIVE, + .stride = fb->pitches[0], + .width = fb->width, + .height = fb->height, + .buf_size = fb->height * fb->pitches[0], + .surface_id = req->swap.surf_ids[l], + + /* Only used for compressed or multiplanar surfaces */ + .pix_size = 1, + .pel_w = 1, + .pel_h = 1, + .has_comp = 1, + .has_planes = 1, + }; + + l += 1; + } + + if (modeset) { + struct dcp_display_mode *mode; + struct dcp_wait_cookie *cookie; + int ret; + + mode = lookup_mode(dcp, &crtc_state->mode); + if (!mode) { + dev_warn(dcp->dev, "no match for " DRM_MODE_FMT, + DRM_MODE_ARG(&crtc_state->mode)); + schedule_work(&dcp->vblank_wq); + return; + } + + dev_info(dcp->dev, "set_digital_out_mode(color:%d timing:%d)", + mode->color_mode_id, mode->timing_mode_id); + dcp->mode = (struct dcp_set_digital_out_mode_req){ + .color_mode_id = mode->color_mode_id, + .timing_mode_id = mode->timing_mode_id + }; + + cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); + if (!cookie) { + schedule_work(&dcp->vblank_wq); + return; + } + + init_completion(&cookie->done); + kref_init(&cookie->refcount); + /* increase refcount to ensure the receiver has a reference */ + kref_get(&cookie->refcount); + + dcp_set_digital_out_mode(dcp, false, &dcp->mode, + complete_set_digital_out_mode, cookie); + + dev_dbg(dcp->dev, "%s - wait for modeset", __func__); + ret = wait_for_completion_timeout(&cookie->done, + msecs_to_jiffies(500)); + + kref_put(&cookie->refcount, release_wait_cookie); + + if (ret == 0) { + dev_dbg(dcp->dev, "set_digital_out_mode 200 ms"); + schedule_work(&dcp->vblank_wq); + return; + } else if (ret > 0) { + dev_dbg(dcp->dev, + "set_digital_out_mode finished with %d to spare", + jiffies_to_msecs(ret)); + } + + dcp->valid_mode = true; + } + + if (!has_surface && !crtc_state->color_mgmt_changed) { + if (crtc_state->enable && crtc_state->active && + !crtc_state->planes_changed) { + schedule_work(&dcp->vblank_wq); + return; + } + + /* Set black background */ + req->swap.swap_enabled |= IOMFB_SET_BACKGROUND; + req->swap.bg_color = 0xFF000000; + req->clear = 1; + } + + /* These fields should be set together */ + req->swap.swap_completed = req->swap.swap_enabled; + + /* update brightness if changed */ + if (dcp->brightness.update) { + req->swap.bl_unk = 1; + req->swap.bl_value = dcp->brightness.dac; + req->swap.bl_power = 0x40; + dcp->brightness.update = false; + } + + do_swap(dcp, NULL, NULL); +} + +static void res_is_main_display(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct apple_connector *connector; + int result = *(int *)out; + dev_info(dcp->dev, "DCP is_main_display: %d\n", result); + + dcp->main_display = result != 0; + + connector = dcp->connector; + if (connector) { + connector->connected = dcp->nr_modes > 0; + schedule_work(&connector->hotplug_wq); + } + + dcp->active = true; + complete(&dcp->start_done); +} + +static void init_3(struct apple_dcp *dcp, void *out, void *cookie) +{ + dcp_is_main_display(dcp, false, res_is_main_display, NULL); +} + +static void init_2(struct apple_dcp *dcp, void *out, void *cookie) +{ + dcp_first_client_open(dcp, false, init_3, NULL); +} + +static void init_1(struct apple_dcp *dcp, void *out, void *cookie) +{ + u32 val = 0; + dcp_enable_disable_video_power_savings(dcp, false, &val, init_2, NULL); +} + +static void dcp_started(struct apple_dcp *dcp, void *data, void *cookie) +{ + struct iomfb_get_color_remap_mode_req color_remap = + (struct iomfb_get_color_remap_mode_req){ + .mode = 6, + }; + + dev_info(dcp->dev, "DCP booted\n"); + + iomfb_get_color_remap_mode(dcp, false, &color_remap, init_1, cookie); +} + +void DCP_FW_NAME(iomfb_shutdown)(struct apple_dcp *dcp) +{ + struct dcp_set_power_state_req req = { + /* defaults are ok */ + }; + + dcp_set_power_state(dcp, false, &req, NULL, NULL); +} diff --git a/drivers/gpu/drm/apple/iomfb_template.h b/drivers/gpu/drm/apple/iomfb_template.h new file mode 100644 index 00000000000000..539ec65e5825f4 --- /dev/null +++ b/drivers/gpu/drm/apple/iomfb_template.h @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright 2021 Alyssa Rosenzweig */ + +/* + * This file is intended to be included multiple times with IOMFB_VER + * defined to declare DCP firmware version dependent structs. + */ + +#ifdef DCP_FW_VER + +#include + +#include + +#include "iomfb.h" +#include "version_utils.h" + +struct DCP_FW_NAME(dcp_swap) { + u64 ts1; + u64 ts2; + u64 unk_10[6]; + u64 flags1; + u64 flags2; + + u32 swap_id; + + u32 surf_ids[SWAP_SURFACES]; + struct dcp_rect src_rect[SWAP_SURFACES]; + u32 surf_flags[SWAP_SURFACES]; + u32 surf_unk[SWAP_SURFACES]; + struct dcp_rect dst_rect[SWAP_SURFACES]; + u32 swap_enabled; + u32 swap_completed; + + u32 bg_color; + u8 unk_110[0x1b8]; + u32 unk_2c8; + u8 unk_2cc[0x14]; + u32 unk_2e0; +#if DCP_FW_VER < DCP_FW_VERSION(13, 2, 0) + u16 unk_2e2; +#else + u8 unk_2e2[3]; +#endif + u64 bl_unk; + u32 bl_value; // min value is 0x10000000 + u8 bl_power; // constant 0x40 for on + u8 unk_2f3[0x2d]; +#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) + u8 unk_320[0x13f]; +#endif +} __packed; + +/* Information describing a surface */ +struct DCP_FW_NAME(dcp_surface) { + u8 is_tiled; + u8 is_tearing_allowed; + u8 is_premultiplied; + u32 plane_cnt; + u32 plane_cnt2; + u32 format; /* DCP fourcc */ + u32 ycbcr_matrix; + u8 xfer_func; + u8 colorspace; + u32 stride; + u16 pix_size; + u8 pel_w; + u8 pel_h; + u32 offset; + u32 width; + u32 height; + u32 buf_size; + u64 protection_opts; + u32 surface_id; + struct dcp_component_types comp_types[MAX_PLANES]; + u64 has_comp; + struct dcp_plane_info planes[MAX_PLANES]; + u64 has_planes; + u32 compression_info[MAX_PLANES][13]; + u64 has_compr_info; + u32 unk_num; + u32 unk_denom; +#if DCP_FW_VER < DCP_FW_VERSION(13, 2, 0) + u8 padding[7]; +#else + u8 padding[47]; +#endif +} __packed; + +/* Prototypes */ + +struct DCP_FW_NAME(dcp_swap_submit_req) { + struct DCP_FW_NAME(dcp_swap) swap; + struct DCP_FW_NAME(dcp_surface) surf[SWAP_SURFACES]; + u64 surf_iova[SWAP_SURFACES]; +#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) + u64 unk_u64_a[SWAP_SURFACES]; + struct DCP_FW_NAME(dcp_surface) surf2[5]; + u64 surf2_iova[5]; +#endif + u8 unkbool; + u64 unkdouble; +#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) + u64 unkU64; + u8 unkbool2; +#endif + u32 clear; // or maybe switch to default fb? +#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) + u32 unkU32Ptr; +#endif + u8 swap_null; + u8 surf_null[SWAP_SURFACES]; +#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) + u8 surf2_null[5]; +#endif + u8 unkoutbool_null; +#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) + u8 unkU32Ptr_null; + u8 unkU32out_null; +#endif + u8 padding[1]; +} __packed; + +struct DCP_FW_NAME(dcp_swap_submit_resp) { + u8 unkoutbool; +#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) + u32 unkU32out; +#endif + u32 ret; + u8 padding[3]; +} __packed; + +struct DCP_FW_NAME(dc_swap_complete_resp) { + u32 swap_id; + u8 unkbool; + u64 swap_data; +#if DCP_FW_VER < DCP_FW_VERSION(13, 2, 0) + u8 swap_info[0x6c4]; +#else + u8 swap_info[0x6c5]; +#endif + u32 unkint; + u8 swap_info_null; +} __packed; + +struct DCP_FW_NAME(dcp_map_reg_req) { + char obj[4]; + u32 index; + u32 flags; +#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) + u8 unk_u64_null; +#endif + u8 addr_null; + u8 length_null; +#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) + u8 padding[1]; +#else + u8 padding[2]; +#endif +} __packed; + +struct DCP_FW_NAME(dcp_map_reg_resp) { +#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) + u64 dva; +#endif + u64 addr; + u64 length; + u32 ret; +} __packed; + + +struct apple_dcp; + +void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, struct drm_atomic_state *state); +void DCP_FW_NAME(iomfb_poweron)(struct apple_dcp *dcp); +void DCP_FW_NAME(iomfb_poweroff)(struct apple_dcp *dcp); +void DCP_FW_NAME(iomfb_sleep)(struct apple_dcp *dcp); +void DCP_FW_NAME(iomfb_start)(struct apple_dcp *dcp); +void DCP_FW_NAME(iomfb_shutdown)(struct apple_dcp *dcp); + +#endif diff --git a/drivers/gpu/drm/apple/iomfb_v12_3.c b/drivers/gpu/drm/apple/iomfb_v12_3.c new file mode 100644 index 00000000000000..354abbfdb24c36 --- /dev/null +++ b/drivers/gpu/drm/apple/iomfb_v12_3.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright The Asahi Linux Contributors */ + +#include "iomfb_v12_3.h" +#include "iomfb_v13_2.h" +#include "version_utils.h" + +static const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { + IOMFB_METHOD("A000", dcpep_late_init_signal), + IOMFB_METHOD("A029", dcpep_setup_video_limits), + IOMFB_METHOD("A131", iomfbep_a131_pmu_service_matched), + IOMFB_METHOD("A132", iomfbep_a132_backlight_service_matched), + IOMFB_METHOD("A357", dcpep_set_create_dfb), + IOMFB_METHOD("A358", iomfbep_a358_vi_set_temperature_hint), + IOMFB_METHOD("A401", dcpep_start_signal), + IOMFB_METHOD("A407", dcpep_swap_start), + IOMFB_METHOD("A408", dcpep_swap_submit), + IOMFB_METHOD("A410", dcpep_set_display_device), + IOMFB_METHOD("A411", dcpep_is_main_display), + IOMFB_METHOD("A412", dcpep_set_digital_out_mode), + IOMFB_METHOD("A426", iomfbep_get_color_remap_mode), + IOMFB_METHOD("A439", dcpep_set_parameter_dcp), + IOMFB_METHOD("A443", dcpep_create_default_fb), + IOMFB_METHOD("A447", dcpep_enable_disable_video_power_savings), + IOMFB_METHOD("A454", dcpep_first_client_open), + IOMFB_METHOD("A455", iomfbep_last_client_close), + IOMFB_METHOD("A460", dcpep_set_display_refresh_properties), + IOMFB_METHOD("A463", dcpep_flush_supports_power), + IOMFB_METHOD("A468", dcpep_set_power_state), +}; + +#define DCP_FW v12_3 +#define DCP_FW_VER DCP_FW_VERSION(12, 3, 0) + +#include "iomfb_template.c" + +static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = { + [0] = trampoline_true, /* did_boot_signal */ + [1] = trampoline_true, /* did_power_on_signal */ + [2] = trampoline_nop, /* will_power_off_signal */ + [3] = trampoline_rt_bandwidth, + [100] = iomfbep_cb_match_pmu_service, + [101] = trampoline_zero, /* get_display_default_stride */ + [102] = trampoline_nop, /* set_number_property */ + [103] = trampoline_nop, /* set_boolean_property */ + [106] = trampoline_nop, /* remove_property */ + [107] = trampoline_true, /* create_provider_service */ + [108] = trampoline_true, /* create_product_service */ + [109] = trampoline_true, /* create_pmu_service */ + [110] = trampoline_true, /* create_iomfb_service */ + [111] = trampoline_true, /* create_backlight_service */ + [116] = dcpep_cb_boot_1, + [117] = trampoline_false, /* is_dark_boot */ + [118] = trampoline_false, /* is_dark_boot / is_waking_from_hibernate*/ + [120] = trampoline_read_edt_data, + [122] = trampoline_prop_start, + [123] = trampoline_prop_chunk, + [124] = trampoline_prop_end, + [201] = trampoline_map_piodma, + [202] = trampoline_unmap_piodma, + [206] = iomfbep_cb_match_pmu_service_2, + [207] = iomfbep_cb_match_backlight_service, + [208] = trampoline_get_time, + [211] = trampoline_nop, /* update_backlight_factor_prop */ + [300] = trampoline_pr_publish, + [401] = trampoline_get_uint_prop, + [404] = trampoline_nop, /* sr_set_uint_prop */ + [406] = trampoline_set_fx_prop, + [408] = trampoline_get_frequency, + [411] = trampoline_map_reg, + [413] = trampoline_true, /* sr_set_property_dict */ + [414] = trampoline_sr_set_property_int, + [415] = trampoline_true, /* sr_set_property_bool */ + [451] = trampoline_allocate_buffer, + [452] = trampoline_map_physical, + [456] = trampoline_release_mem_desc, + [552] = trampoline_true, /* set_property_dict_0 */ + [561] = trampoline_true, /* set_property_dict */ + [563] = trampoline_true, /* set_property_int */ + [565] = trampoline_true, /* set_property_bool */ + [567] = trampoline_true, /* set_property_str */ + [574] = trampoline_zero, /* power_up_dart */ + [576] = trampoline_hotplug, + [577] = trampoline_nop, /* powerstate_notify */ + [582] = trampoline_true, /* create_default_fb_surface */ + [584] = trampoline_nop, /* IOMobileFramebufferAP::clear_default_surface */ + [588] = trampoline_nop, /* resize_default_fb_surface_gated */ + [589] = trampoline_swap_complete, + [591] = trampoline_swap_complete_intent_gated, + [593] = trampoline_enable_backlight_message_ap_gated, + [594] = trampoline_nop, /* IOMobileFramebufferAP::setSystemConsoleMode */ + [596] = trampoline_false, /* IOMobileFramebufferAP::isDFBAllocated */ + [597] = trampoline_false, /* IOMobileFramebufferAP::preserveContents */ + [598] = trampoline_nop, /* find_swap_function_gated */ +}; + +void DCP_FW_NAME(iomfb_start)(struct apple_dcp *dcp) +{ + dcp->cb_handlers = cb_handlers; + + dcp_start_signal(dcp, false, dcp_started, NULL); +} + +#undef DCP_FW_VER +#undef DCP_FW diff --git a/drivers/gpu/drm/apple/iomfb_v12_3.h b/drivers/gpu/drm/apple/iomfb_v12_3.h new file mode 100644 index 00000000000000..7359685d981fe5 --- /dev/null +++ b/drivers/gpu/drm/apple/iomfb_v12_3.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright The Asahi Linux Contributors */ + +#ifndef __APPLE_IOMFB_V12_3_H__ +#define __APPLE_IOMFB_V12_3_H__ + +#include "version_utils.h" + +#define DCP_FW v12_3 +#define DCP_FW_VER DCP_FW_VERSION(12, 3, 0) + +#include "iomfb_template.h" + +#undef DCP_FW_VER +#undef DCP_FW + +#endif /* __APPLE_IOMFB_V12_3_H__ */ diff --git a/drivers/gpu/drm/apple/iomfb_v13_2.c b/drivers/gpu/drm/apple/iomfb_v13_2.c new file mode 100644 index 00000000000000..27f1d84e928a69 --- /dev/null +++ b/drivers/gpu/drm/apple/iomfb_v13_2.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright The Asahi Linux Contributors */ + +#include "iomfb_v12_3.h" +#include "iomfb_v13_2.h" +#include "version_utils.h" + +static const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { + IOMFB_METHOD("A000", dcpep_late_init_signal), + IOMFB_METHOD("A029", dcpep_setup_video_limits), + IOMFB_METHOD("A131", iomfbep_a131_pmu_service_matched), + IOMFB_METHOD("A132", iomfbep_a132_backlight_service_matched), + IOMFB_METHOD("A373", dcpep_set_create_dfb), + IOMFB_METHOD("A374", iomfbep_a358_vi_set_temperature_hint), + IOMFB_METHOD("A401", dcpep_start_signal), + IOMFB_METHOD("A407", dcpep_swap_start), + IOMFB_METHOD("A408", dcpep_swap_submit), + IOMFB_METHOD("A410", dcpep_set_display_device), + IOMFB_METHOD("A411", dcpep_is_main_display), + IOMFB_METHOD("A412", dcpep_set_digital_out_mode), + IOMFB_METHOD("A426", iomfbep_get_color_remap_mode), + IOMFB_METHOD("A441", dcpep_set_parameter_dcp), + IOMFB_METHOD("A445", dcpep_create_default_fb), + IOMFB_METHOD("A449", dcpep_enable_disable_video_power_savings), + IOMFB_METHOD("A456", dcpep_first_client_open), + IOMFB_METHOD("A457", iomfbep_last_client_close), + IOMFB_METHOD("A462", dcpep_set_display_refresh_properties), + IOMFB_METHOD("A465", dcpep_flush_supports_power), + IOMFB_METHOD("A471", dcpep_set_power_state), +}; + +#define DCP_FW v13_2 +#define DCP_FW_VER DCP_FW_VERSION(13, 2, 0) + +#include "iomfb_template.c" + +static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = { + [0] = trampoline_true, /* did_boot_signal */ + [1] = trampoline_true, /* did_power_on_signal */ + [2] = trampoline_nop, /* will_power_off_signal */ + [3] = trampoline_rt_bandwidth, + [100] = iomfbep_cb_match_pmu_service, + [101] = trampoline_zero, /* get_display_default_stride */ + [102] = trampoline_nop, /* set_number_property */ + [103] = trampoline_nop, /* set_boolean_property */ + [106] = trampoline_nop, /* remove_property */ + [107] = trampoline_true, /* create_provider_service */ + [108] = trampoline_true, /* create_product_service */ + [109] = trampoline_true, /* create_pmu_service */ + [110] = trampoline_true, /* create_iomfb_service */ + [111] = trampoline_true, /* create_backlight_service */ + [112] = trampoline_true, /* create_nvram_servce? */ + [119] = dcpep_cb_boot_1, + [120] = trampoline_false, /* is_dark_boot */ + [121] = trampoline_false, /* is_dark_boot / is_waking_from_hibernate*/ + [123] = trampoline_read_edt_data, + [125] = trampoline_prop_start, + [126] = trampoline_prop_chunk, + [127] = trampoline_prop_end, + [201] = trampoline_map_piodma, + [202] = trampoline_unmap_piodma, + [206] = iomfbep_cb_match_pmu_service_2, + [207] = iomfbep_cb_match_backlight_service, + [208] = trampoline_get_time, + [211] = trampoline_nop, /* update_backlight_factor_prop */ + [300] = trampoline_pr_publish, + [401] = trampoline_get_uint_prop, + [404] = trampoline_nop, /* sr_set_uint_prop */ + [406] = trampoline_set_fx_prop, + [408] = trampoline_get_frequency, + [411] = trampoline_map_reg, + [413] = trampoline_true, /* sr_set_property_dict */ + [414] = trampoline_sr_set_property_int, + [415] = trampoline_true, /* sr_set_property_bool */ + [451] = trampoline_allocate_buffer, + [452] = trampoline_map_physical, + [456] = trampoline_release_mem_desc, + [552] = trampoline_true, /* set_property_dict_0 */ + [561] = trampoline_true, /* set_property_dict */ + [563] = trampoline_true, /* set_property_int */ + [565] = trampoline_true, /* set_property_bool */ + [567] = trampoline_true, /* set_property_str */ + [574] = trampoline_zero, /* power_up_dart */ + [576] = trampoline_hotplug, + [577] = trampoline_nop, /* powerstate_notify */ + [582] = trampoline_true, /* create_default_fb_surface */ + [584] = trampoline_nop, /* IOMobileFramebufferAP::clear_default_surface */ + [588] = trampoline_nop, /* resize_default_fb_surface_gated */ + [589] = trampoline_swap_complete, + [591] = trampoline_swap_complete_intent_gated, + [593] = trampoline_enable_backlight_message_ap_gated, + [594] = trampoline_nop, /* IOMobileFramebufferAP::setSystemConsoleMode */ + [596] = trampoline_false, /* IOMobileFramebufferAP::isDFBAllocated */ + [597] = trampoline_false, /* IOMobileFramebufferAP::preserveContents */ + [598] = trampoline_nop, /* find_swap_function_gated */ +}; +void DCP_FW_NAME(iomfb_start)(struct apple_dcp *dcp) +{ + dcp->cb_handlers = cb_handlers; + + dcp_start_signal(dcp, false, dcp_started, NULL); +} + +#undef DCP_FW_VER +#undef DCP_FW diff --git a/drivers/gpu/drm/apple/iomfb_v13_2.h b/drivers/gpu/drm/apple/iomfb_v13_2.h new file mode 100644 index 00000000000000..f3810b727235bc --- /dev/null +++ b/drivers/gpu/drm/apple/iomfb_v13_2.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright The Asahi Linux Contributors */ + +#ifndef __APPLE_IOMFB_V13_2_H__ +#define __APPLE_IOMFB_V13_2_H__ + +#include "version_utils.h" + +#define DCP_FW v13_2 +#define DCP_FW_VER DCP_FW_VERSION(13, 2, 0) + +#include "iomfb_template.h" + +#undef DCP_FW_VER +#undef DCP_FW + +#endif /* __APPLE_IOMFB_V13_2_H__ */ diff --git a/drivers/gpu/drm/apple/version_utils.h b/drivers/gpu/drm/apple/version_utils.h new file mode 100644 index 00000000000000..c85baea729414d --- /dev/null +++ b/drivers/gpu/drm/apple/version_utils.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright The Asahi Linux Contributors */ + +#ifndef __APPLE_VERSION_UTILS_H__ +#define __APPLE_VERSION_UTILS_H__ + +#include + +#define DCP_FW_UNION(u) (u).DCP_FW +#define DCP_FW_SUFFIX CONCATENATE(_, DCP_FW) +#define DCP_FW_NAME(name) CONCATENATE(name, DCP_FW_SUFFIX) +#define DCP_FW_VERSION(x, y, z) ( ((x) << 16) | ((y) << 8) | (z) ) + +#endif /*__APPLE_VERSION_UTILS_H__*/ From 5d4b901320a4d91699f2d63114643ba04cb59b7a Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 9 Mar 2023 12:44:51 +0100 Subject: [PATCH 0517/1020] drm/apple: ignore surf[3] in clear swap calls MacOS 13.2 does the same and it is unclear if surf[3] can be used at all. PRobably not necessary but found during debugging to firmware 13.2. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb_template.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index c6c62e4bc4c125..fb2be2669a7e5d 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -841,7 +841,7 @@ void DCP_FW_NAME(iomfb_poweroff)(struct apple_dcp *dcp) memset(swap, 0, sizeof(*swap)); swap->swap.swap_enabled = - swap->swap.swap_completed = IOMFB_SET_BACKGROUND | 0xF; + swap->swap.swap_completed = IOMFB_SET_BACKGROUND | 0x7; swap->swap.bg_color = 0xFF000000; /* @@ -1113,7 +1113,7 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru * sticks around. */ if (!dcp->surfaces_cleared) { - req->swap.swap_enabled = IOMFB_SET_BACKGROUND | 0xF; + req->swap.swap_enabled = IOMFB_SET_BACKGROUND | 0x7; req->swap.bg_color = 0xFF000000; dcp->surfaces_cleared = true; } From de99d3b32f7d3fc760b3f545c43cf6be4357d511 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 12 Mar 2023 21:38:50 +0100 Subject: [PATCH 0518/1020] drm/apple: Support color transformation matrices kwin 5.27.3 adds support for "Night Color" via drm "CTM" properties. Wire CTM support up via the "set_matrix" iomfb call. Link: https://bugs.kde.org/show_bug.cgi?id=455720 Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 1 + drivers/gpu/drm/apple/iomfb.h | 14 ++++++++++++++ drivers/gpu/drm/apple/iomfb_template.c | 20 +++++++++++++++++++- drivers/gpu/drm/apple/iomfb_v12_3.c | 1 + drivers/gpu/drm/apple/iomfb_v13_2.c | 1 + 5 files changed, 36 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 7390f30bf60ecd..72dc760daa8967 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -334,6 +334,7 @@ static int apple_probe_per_dcp(struct device *dev, return ret; drm_crtc_helper_add(&crtc->base, &apple_crtc_helper_funcs); + drm_crtc_enable_color_mgmt(&crtc->base, 0, true, 0); enc = drmm_simple_encoder_alloc(drm, struct apple_encoder, base, DRM_MODE_ENCODER_TMDS); diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index 0c8061bb96e3e0..7cda9124bcf96b 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -160,6 +160,7 @@ enum dcpep_method { iomfbep_a358_vi_set_temperature_hint, iomfbep_get_color_remap_mode, iomfbep_last_client_close, + iomfbep_set_matrix, dcpep_num_methods }; @@ -350,4 +351,17 @@ struct iomfb_last_client_close_resp { u32 unkint; } __packed; +struct iomfb_set_matrix_req { + u32 unk_u32; // maybe length? + u64 r[3]; + u64 g[3]; + u64 b[3]; + u8 matrix_null; + u8 padding[3]; +} __packed; + +struct iomfb_set_matrix_resp { + u32 ret; +} __packed; + #endif diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index fb2be2669a7e5d..ea95fa37eee079 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -55,6 +55,7 @@ DCP_THUNK_OUT(iomfb_a131_pmu_service_matched, iomfbep_a131_pmu_service_matched, DCP_THUNK_OUT(iomfb_a132_backlight_service_matched, iomfbep_a132_backlight_service_matched, u32); DCP_THUNK_OUT(iomfb_a358_vi_set_temperature_hint, iomfbep_a358_vi_set_temperature_hint, u32); +IOMFB_THUNK_INOUT(set_matrix); IOMFB_THUNK_INOUT(get_color_remap_mode); IOMFB_THUNK_INOUT(last_client_close); @@ -1285,7 +1286,24 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru dcp->brightness.update = false; } - do_swap(dcp, NULL, NULL); + if (crtc_state->color_mgmt_changed && crtc_state->ctm) { + struct iomfb_set_matrix_req mat; + struct drm_color_ctm *ctm = (struct drm_color_ctm *)crtc_state->ctm->data; + + mat.unk_u32 = 9; + mat.r[0] = ctm->matrix[0]; + mat.r[1] = ctm->matrix[1]; + mat.r[2] = ctm->matrix[2]; + mat.g[0] = ctm->matrix[3]; + mat.g[1] = ctm->matrix[4]; + mat.g[2] = ctm->matrix[5]; + mat.b[0] = ctm->matrix[6]; + mat.b[1] = ctm->matrix[7]; + mat.b[2] = ctm->matrix[8]; + + iomfb_set_matrix(dcp, false, &mat, do_swap, NULL); + } else + do_swap(dcp, NULL, NULL); } static void res_is_main_display(struct apple_dcp *dcp, void *out, void *cookie) diff --git a/drivers/gpu/drm/apple/iomfb_v12_3.c b/drivers/gpu/drm/apple/iomfb_v12_3.c index 354abbfdb24c36..c226a1139a84c8 100644 --- a/drivers/gpu/drm/apple/iomfb_v12_3.c +++ b/drivers/gpu/drm/apple/iomfb_v12_3.c @@ -18,6 +18,7 @@ static const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { IOMFB_METHOD("A410", dcpep_set_display_device), IOMFB_METHOD("A411", dcpep_is_main_display), IOMFB_METHOD("A412", dcpep_set_digital_out_mode), + IOMFB_METHOD("A422", iomfbep_set_matrix), IOMFB_METHOD("A426", iomfbep_get_color_remap_mode), IOMFB_METHOD("A439", dcpep_set_parameter_dcp), IOMFB_METHOD("A443", dcpep_create_default_fb), diff --git a/drivers/gpu/drm/apple/iomfb_v13_2.c b/drivers/gpu/drm/apple/iomfb_v13_2.c index 27f1d84e928a69..63ae1e79adda10 100644 --- a/drivers/gpu/drm/apple/iomfb_v13_2.c +++ b/drivers/gpu/drm/apple/iomfb_v13_2.c @@ -18,6 +18,7 @@ static const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { IOMFB_METHOD("A410", dcpep_set_display_device), IOMFB_METHOD("A411", dcpep_is_main_display), IOMFB_METHOD("A412", dcpep_set_digital_out_mode), + IOMFB_METHOD("A422", iomfbep_set_matrix), IOMFB_METHOD("A426", iomfbep_get_color_remap_mode), IOMFB_METHOD("A441", dcpep_set_parameter_dcp), IOMFB_METHOD("A445", dcpep_create_default_fb), From 3efd9bed795f7151d0602d005cd606bdfdc74b17 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 23 Mar 2023 08:40:42 +0100 Subject: [PATCH 0519/1020] drm/apple: Drop unsupported DRM_FORMAT_ARGB2101010 Depends on https://gitlab.freedesktop.org/asahi/mesa/-/merge_requests/5 Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 1 - drivers/gpu/drm/apple/iomfb.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 72dc760daa8967..075dfe719b8cf6 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -145,7 +145,6 @@ static const struct drm_plane_funcs apple_plane_funcs = { */ static const u32 dcp_formats[] = { DRM_FORMAT_XRGB2101010, - DRM_FORMAT_ARGB2101010, DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_XBGR8888, diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 4964bb063bff1f..7a0699c9e9ee6a 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -411,7 +411,6 @@ u32 drm_format_to_dcp(u32 drm) case DRM_FORMAT_ABGR8888: return fourcc_code('A', 'B', 'G', 'R'); - case DRM_FORMAT_ARGB2101010: case DRM_FORMAT_XRGB2101010: return fourcc_code('r', '0', '3', 'w'); } From 83357c5b349b869a9287f03769478a67f18b3af6 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 11 Apr 2023 21:57:22 +0900 Subject: [PATCH 0520/1020] dcp: Allow unused trampolines Signed-off-by: Hector Martin --- drivers/gpu/drm/apple/iomfb_internal.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb_internal.h b/drivers/gpu/drm/apple/iomfb_internal.h index 401b6ec32848d3..09f8857d30c341 100644 --- a/drivers/gpu/drm/apple/iomfb_internal.h +++ b/drivers/gpu/drm/apple/iomfb_internal.h @@ -57,7 +57,7 @@ typedef void (*dcp_callback_t)(struct apple_dcp *, void *, void *); */ #define TRAMPOLINE_VOID(func, handler) \ - static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ + static bool __maybe_unused func(struct apple_dcp *dcp, int tag, void *out, void *in) \ { \ trace_iomfb_callback(dcp, tag, #handler); \ handler(dcp); \ @@ -67,7 +67,7 @@ typedef void (*dcp_callback_t)(struct apple_dcp *, void *, void *); #define TRAMPOLINE_IN(func, handler, T_in) \ typedef void (*callback_##handler)(struct apple_dcp *, T_in *); \ \ - static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ + static bool __maybe_unused func(struct apple_dcp *dcp, int tag, void *out, void *in) \ { \ callback_##handler cb = handler; \ \ @@ -79,7 +79,7 @@ typedef void (*dcp_callback_t)(struct apple_dcp *, void *, void *); #define TRAMPOLINE_INOUT(func, handler, T_in, T_out) \ typedef T_out (*callback_##handler)(struct apple_dcp *, T_in *); \ \ - static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ + static bool __maybe_unused func(struct apple_dcp *dcp, int tag, void *out, void *in) \ { \ T_out *typed_out = out; \ callback_##handler cb = handler; \ @@ -90,7 +90,7 @@ typedef void (*dcp_callback_t)(struct apple_dcp *, void *, void *); } #define TRAMPOLINE_OUT(func, handler, T_out) \ - static bool func(struct apple_dcp *dcp, int tag, void *out, void *in) \ + static bool __maybe_unused func(struct apple_dcp *dcp, int tag, void *out, void *in) \ { \ T_out *typed_out = out; \ \ From 68b9142c0d03a6c65a68ab0fdd1646cfa718ed75 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 11 Apr 2023 21:57:38 +0900 Subject: [PATCH 0521/1020] dcp: Add get_tiling_state Signed-off-by: Hector Martin --- drivers/gpu/drm/apple/iomfb.h | 13 +++++++++++++ drivers/gpu/drm/apple/iomfb_template.c | 12 ++++++++++++ drivers/gpu/drm/apple/iomfb_v13_2.c | 2 ++ 3 files changed, 27 insertions(+) diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index 7cda9124bcf96b..6602bf19107d43 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -364,4 +364,17 @@ struct iomfb_set_matrix_resp { u32 ret; } __packed; +struct dcpep_get_tiling_state_req { + u32 event; + u32 param; + u32 value; + u8 value_null; + u8 padding[3]; +} __packed; + +struct dcpep_get_tiling_state_resp { + u32 value; + u32 ret; +} __packed; + #endif diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index ea95fa37eee079..8403a65d056868 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -977,6 +977,16 @@ dcpep_cb_swap_complete_intent_gated(struct apple_dcp *dcp, info->width, info->height); } +static struct dcpep_get_tiling_state_resp +dcpep_cb_get_tiling_state(struct apple_dcp *dcp, + struct dcpep_get_tiling_state_req *req) +{ + return (struct dcpep_get_tiling_state_resp){ + .value = 0, + .ret = 1, + }; +} + TRAMPOLINE_VOID(trampoline_nop, dcpep_cb_nop); TRAMPOLINE_OUT(trampoline_true, dcpep_cb_true, u8); TRAMPOLINE_OUT(trampoline_false, dcpep_cb_false, u8); @@ -1022,6 +1032,8 @@ TRAMPOLINE_IN(trampoline_enable_backlight_message_ap_gated, iomfbep_cb_enable_backlight_message_ap_gated, u8); TRAMPOLINE_IN(trampoline_pr_publish, iomfb_cb_pr_publish, struct iomfb_property); +TRAMPOLINE_INOUT(trampoline_get_tiling_state, dcpep_cb_get_tiling_state, + struct dcpep_get_tiling_state_req, struct dcpep_get_tiling_state_resp); /* * Callback for swap requests. If a swap failed, we'll never get a swap diff --git a/drivers/gpu/drm/apple/iomfb_v13_2.c b/drivers/gpu/drm/apple/iomfb_v13_2.c index 63ae1e79adda10..356a2aa2433be0 100644 --- a/drivers/gpu/drm/apple/iomfb_v13_2.c +++ b/drivers/gpu/drm/apple/iomfb_v13_2.c @@ -51,6 +51,8 @@ static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = { [110] = trampoline_true, /* create_iomfb_service */ [111] = trampoline_true, /* create_backlight_service */ [112] = trampoline_true, /* create_nvram_servce? */ + [113] = trampoline_get_tiling_state, + [114] = trampoline_false, /* set_tiling_state */ [119] = dcpep_cb_boot_1, [120] = trampoline_false, /* is_dark_boot */ [121] = trampoline_false, /* is_dark_boot / is_waking_from_hibernate*/ From 833cf2eaf4214b440174a4f20464f071d2ef70dd Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 11 Apr 2023 22:54:28 +0900 Subject: [PATCH 0522/1020] dcp: 42-bit DMA masks Signed-off-by: Hector Martin --- drivers/gpu/drm/apple/apple_drv.c | 2 +- drivers/gpu/drm/apple/dcp.c | 2 +- drivers/gpu/drm/apple/dummy-piodma.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 075dfe719b8cf6..b487a71d617a77 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -459,7 +459,7 @@ static int apple_drm_init(struct device *dev) resource_size_t fb_size; int ret; - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36)); + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(42)); if (ret) return ret; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 05fa58033180a8..a8456c170259bf 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -419,7 +419,7 @@ static int dcp_comp_bind(struct device *dev, struct device *main, void *data) u32 cpu_ctrl; int ret; - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36)); + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(42)); if (ret) return ret; diff --git a/drivers/gpu/drm/apple/dummy-piodma.c b/drivers/gpu/drm/apple/dummy-piodma.c index ec5d4fecf356c0..eaa0476854a603 100644 --- a/drivers/gpu/drm/apple/dummy-piodma.c +++ b/drivers/gpu/drm/apple/dummy-piodma.c @@ -26,7 +26,7 @@ static const struct component_ops dcp_piodma_comp_ops = { }; static int dcp_piodma_probe(struct platform_device *pdev) { - int ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36)); + int ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(42)); if (ret) return ret; From bb52147276dd49506744e9080368d05eff4d20a3 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 11 Apr 2023 22:55:25 +0900 Subject: [PATCH 0523/1020] dcp: T602X bwreq support Signed-off-by: Hector Martin --- drivers/gpu/drm/apple/iomfb_template.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 8403a65d056868..f0fdc01fae793c 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -35,6 +35,7 @@ /* Register defines used in bandwidth setup structure */ #define REG_SCRATCH (0x14) #define REG_SCRATCH_T600X (0x988) +#define REG_SCRATCH_T602X (0x1208) #define REG_DOORBELL (0x0) #define REG_DOORBELL_BIT (2) @@ -636,7 +637,7 @@ static bool dcpep_cb_boot_1(struct apple_dcp *dcp, int tag, void *out, void *in) static struct dcp_rt_bandwidth dcpep_cb_rt_bandwidth(struct apple_dcp *dcp) { - if (dcp->disp_registers[5] && dcp->disp_registers[6]) + if (dcp->disp_registers[5] && dcp->disp_registers[6]) { return (struct dcp_rt_bandwidth){ .reg_scratch = dcp->disp_registers[5]->start + REG_SCRATCH, @@ -646,19 +647,24 @@ static struct dcp_rt_bandwidth dcpep_cb_rt_bandwidth(struct apple_dcp *dcp) .padding[3] = 0x4, // XXX: required by 11.x firmware }; - else if (dcp->disp_registers[4]) + } else if (dcp->disp_registers[4]) { + u32 offset = REG_SCRATCH_T600X; + if (of_device_is_compatible(dcp->dev->of_node, "apple,t6020-dcp")) + offset = REG_SCRATCH_T602X; + return (struct dcp_rt_bandwidth){ .reg_scratch = dcp->disp_registers[4]->start + - REG_SCRATCH_T600X, + offset, .reg_doorbell = 0, .doorbell_bit = 0, }; - else + } else { return (struct dcp_rt_bandwidth){ .reg_scratch = 0, .reg_doorbell = 0, .doorbell_bit = 0, }; + } } /* Callback to get the current time as milliseconds since the UNIX epoch */ From 55539068c5cfd09618fc95a88e86cc381bacafe8 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 11 Apr 2023 22:55:46 +0900 Subject: [PATCH 0524/1020] dcp: Warn if DMA mapping fails Signed-off-by: Hector Martin --- drivers/gpu/drm/apple/iomfb_template.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index f0fdc01fae793c..0e718742a92e02 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -412,6 +412,7 @@ static struct dcp_map_physical_resp dcpep_cb_map_physical(struct apple_dcp *dcp, struct dcp_map_physical_req *req) { int size = ALIGN(req->size, 4096); + dma_addr_t dva; u32 id; if (!is_disp_register(dcp, req->paddr, req->paddr + size - 1)) { @@ -425,11 +426,13 @@ dcpep_cb_map_physical(struct apple_dcp *dcp, struct dcp_map_physical_req *req) dcp->memdesc[id].size = size; dcp->memdesc[id].reg = req->paddr; + dva = dma_map_resource(dcp->dev, req->paddr, size, DMA_BIDIRECTIONAL, 0); + WARN_ON(dva == DMA_MAPPING_ERROR); + return (struct dcp_map_physical_resp){ .dva_size = size, .mem_desc_id = id, - .dva = dma_map_resource(dcp->dev, req->paddr, size, - DMA_BIDIRECTIONAL, 0), + .dva = dva, }; } From 3b8506fcb81433dc06c9dbb7d3e1cab9c5b7928e Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Fri, 14 Apr 2023 08:07:52 +0200 Subject: [PATCH 0525/1020] WIP: drm/apple: Port to incompatible V13.3 firmware interface Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/Makefile | 2 +- drivers/gpu/drm/apple/dcp-internal.h | 6 +-- drivers/gpu/drm/apple/dcp.c | 4 +- drivers/gpu/drm/apple/iomfb.c | 24 ++++----- drivers/gpu/drm/apple/iomfb.h | 14 +++++ drivers/gpu/drm/apple/iomfb_template.c | 10 ++++ drivers/gpu/drm/apple/iomfb_template.h | 1 + drivers/gpu/drm/apple/iomfb_v12_3.c | 2 +- .../apple/{iomfb_v13_2.c => iomfb_v13_3.c} | 52 ++++++++++--------- .../apple/{iomfb_v13_2.h => iomfb_v13_3.h} | 10 ++-- 10 files changed, 76 insertions(+), 49 deletions(-) rename drivers/gpu/drm/apple/{iomfb_v13_2.c => iomfb_v13_3.c} (73%) rename drivers/gpu/drm/apple/{iomfb_v13_2.h => iomfb_v13_3.h} (52%) diff --git a/drivers/gpu/drm/apple/Makefile b/drivers/gpu/drm/apple/Makefile index 45ef064b3e6fa5..71e1aa3766f37b 100644 --- a/drivers/gpu/drm/apple/Makefile +++ b/drivers/gpu/drm/apple/Makefile @@ -6,7 +6,7 @@ appledrm-y := apple_drv.o apple_dcp-y := dcp.o dcp_backlight.o iomfb.o parser.o apple_dcp-y += iomfb_v12_3.o -apple_dcp-y += iomfb_v13_2.o +apple_dcp-y += iomfb_v13_3.o apple_dcp-$(CONFIG_TRACING) += trace.o apple_piodma-y := dummy-piodma.o diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 59777809a7f370..fee15867b5c0cf 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -12,7 +12,7 @@ #include "iomfb.h" #include "iomfb_v12_3.h" -#include "iomfb_v13_2.h" +#include "iomfb_v13_3.h" #define DCP_MAX_PLANES 2 @@ -21,7 +21,7 @@ struct apple_dcp; enum dcp_firmware_version { DCP_FIRMWARE_UNKNOWN, DCP_FIRMWARE_V_12_3, - DCP_FIRMWARE_V_13_2, + DCP_FIRMWARE_V_13_3, }; enum { @@ -146,7 +146,7 @@ struct apple_dcp { /* Queued swap. Owned by the DCP to avoid per-swap memory allocation */ union { struct dcp_swap_submit_req_v12_3 v12_3; - struct dcp_swap_submit_req_v13_2 v13_2; + struct dcp_swap_submit_req_v13_3 v13_3; } swap; /* Current display mode */ diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index a8456c170259bf..4925b433ede3e3 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -403,8 +403,8 @@ static enum dcp_firmware_version dcp_check_firmware_version(struct device *dev) if (strncmp(compat_str, "12.3.0", sizeof(compat_str)) == 0) return DCP_FIRMWARE_V_12_3; - if (strncmp(compat_str, "13.2.0", sizeof(compat_str)) == 0) - return DCP_FIRMWARE_V_13_2; + if (strncmp(compat_str, "13.3.0", sizeof(compat_str)) == 0) + return DCP_FIRMWARE_V_13_3; dev_err(dev, "DCP firmware-compat %s (FW: %s) is not supported\n", compat_str, fw_str); diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 7a0699c9e9ee6a..09fd542323a82c 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -224,8 +224,8 @@ void dcp_sleep(struct apple_dcp *dcp) case DCP_FIRMWARE_V_12_3: iomfb_sleep_v12_3(dcp); break; - case DCP_FIRMWARE_V_13_2: - iomfb_sleep_v13_2(dcp); + case DCP_FIRMWARE_V_13_3: + iomfb_sleep_v13_3(dcp); break; default: WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); @@ -241,8 +241,8 @@ void dcp_poweron(struct platform_device *pdev) case DCP_FIRMWARE_V_12_3: iomfb_poweron_v12_3(dcp); break; - case DCP_FIRMWARE_V_13_2: - iomfb_poweron_v13_2(dcp); + case DCP_FIRMWARE_V_13_3: + iomfb_poweron_v13_3(dcp); break; default: WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); @@ -259,8 +259,8 @@ void dcp_poweroff(struct platform_device *pdev) case DCP_FIRMWARE_V_12_3: iomfb_poweroff_v12_3(dcp); break; - case DCP_FIRMWARE_V_13_2: - iomfb_poweroff_v13_2(dcp); + case DCP_FIRMWARE_V_13_3: + iomfb_poweroff_v13_3(dcp); break; default: WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); @@ -504,8 +504,8 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) case DCP_FIRMWARE_V_12_3: iomfb_flush_v12_3(dcp, crtc, state); break; - case DCP_FIRMWARE_V_13_2: - iomfb_flush_v13_2(dcp, crtc, state); + case DCP_FIRMWARE_V_13_3: + iomfb_flush_v13_3(dcp, crtc, state); break; default: WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); @@ -520,8 +520,8 @@ void iomfb_start(struct apple_dcp *dcp) case DCP_FIRMWARE_V_12_3: iomfb_start_v12_3(dcp); break; - case DCP_FIRMWARE_V_13_2: - iomfb_start_v13_2(dcp); + case DCP_FIRMWARE_V_13_3: + iomfb_start_v13_3(dcp); break; default: WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); @@ -573,8 +573,8 @@ void iomfb_shutdown(struct apple_dcp *dcp) case DCP_FIRMWARE_V_12_3: iomfb_shutdown_v12_3(dcp); break; - case DCP_FIRMWARE_V_13_2: - iomfb_shutdown_v13_2(dcp); + case DCP_FIRMWARE_V_13_3: + iomfb_shutdown_v13_3(dcp); break; default: WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index 6602bf19107d43..e8168338d374ef 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -136,6 +136,20 @@ struct dcp_rt_bandwidth { u32 padding[7]; } __packed; +struct frame_sync_props { + u8 unk[28]; +}; + +struct dcp_set_frame_sync_props_req { + struct frame_sync_props props; + u8 frame_sync_props_null; + u8 padding[3]; +} __packed; + +struct dcp_set_frame_sync_props_resp { + struct frame_sync_props props; +} __packed; + /* Method calls */ enum dcpep_method { diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 0e718742a92e02..b2bd5d384b5c39 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -670,6 +670,13 @@ static struct dcp_rt_bandwidth dcpep_cb_rt_bandwidth(struct apple_dcp *dcp) } } +static struct dcp_set_frame_sync_props_resp +dcpep_cb_set_frame_sync_props(struct apple_dcp *dcp, + struct dcp_set_frame_sync_props_req *req) +{ + return (struct dcp_set_frame_sync_props_resp){}; +} + /* Callback to get the current time as milliseconds since the UNIX epoch */ static u64 dcpep_cb_get_time(struct apple_dcp *dcp) { @@ -1031,6 +1038,9 @@ TRAMPOLINE_INOUT(trampoline_prop_end, dcpep_cb_prop_end, struct dcp_set_dcpav_prop_end_req, u8); TRAMPOLINE_OUT(trampoline_rt_bandwidth, dcpep_cb_rt_bandwidth, struct dcp_rt_bandwidth); +TRAMPOLINE_INOUT(trampoline_set_frame_sync_props, dcpep_cb_set_frame_sync_props, + struct dcp_set_frame_sync_props_req, + struct dcp_set_frame_sync_props_resp); TRAMPOLINE_OUT(trampoline_get_frequency, dcpep_cb_get_frequency, u64); TRAMPOLINE_OUT(trampoline_get_time, dcpep_cb_get_time, u64); TRAMPOLINE_IN(trampoline_hotplug, dcpep_cb_hotplug, u64); diff --git a/drivers/gpu/drm/apple/iomfb_template.h b/drivers/gpu/drm/apple/iomfb_template.h index 539ec65e5825f4..e9c249609f46cb 100644 --- a/drivers/gpu/drm/apple/iomfb_template.h +++ b/drivers/gpu/drm/apple/iomfb_template.h @@ -48,6 +48,7 @@ struct DCP_FW_NAME(dcp_swap) { u8 unk_2f3[0x2d]; #if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) u8 unk_320[0x13f]; + u64 unk_1; #endif } __packed; diff --git a/drivers/gpu/drm/apple/iomfb_v12_3.c b/drivers/gpu/drm/apple/iomfb_v12_3.c index c226a1139a84c8..8188321004a63f 100644 --- a/drivers/gpu/drm/apple/iomfb_v12_3.c +++ b/drivers/gpu/drm/apple/iomfb_v12_3.c @@ -2,7 +2,7 @@ /* Copyright The Asahi Linux Contributors */ #include "iomfb_v12_3.h" -#include "iomfb_v13_2.h" +#include "iomfb_v13_3.h" #include "version_utils.h" static const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { diff --git a/drivers/gpu/drm/apple/iomfb_v13_2.c b/drivers/gpu/drm/apple/iomfb_v13_3.c similarity index 73% rename from drivers/gpu/drm/apple/iomfb_v13_2.c rename to drivers/gpu/drm/apple/iomfb_v13_3.c index 356a2aa2433be0..18020c6cd39493 100644 --- a/drivers/gpu/drm/apple/iomfb_v13_2.c +++ b/drivers/gpu/drm/apple/iomfb_v13_3.c @@ -2,7 +2,7 @@ /* Copyright The Asahi Linux Contributors */ #include "iomfb_v12_3.h" -#include "iomfb_v13_2.h" +#include "iomfb_v13_3.h" #include "version_utils.h" static const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { @@ -25,13 +25,13 @@ static const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { IOMFB_METHOD("A449", dcpep_enable_disable_video_power_savings), IOMFB_METHOD("A456", dcpep_first_client_open), IOMFB_METHOD("A457", iomfbep_last_client_close), - IOMFB_METHOD("A462", dcpep_set_display_refresh_properties), - IOMFB_METHOD("A465", dcpep_flush_supports_power), - IOMFB_METHOD("A471", dcpep_set_power_state), + IOMFB_METHOD("A463", dcpep_set_display_refresh_properties), + IOMFB_METHOD("A466", dcpep_flush_supports_power), + IOMFB_METHOD("A472", dcpep_set_power_state), }; -#define DCP_FW v13_2 -#define DCP_FW_VER DCP_FW_VERSION(13, 2, 0) +#define DCP_FW v13_3 +#define DCP_FW_VER DCP_FW_VERSION(13, 3, 0) #include "iomfb_template.c" @@ -40,32 +40,34 @@ static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = { [1] = trampoline_true, /* did_power_on_signal */ [2] = trampoline_nop, /* will_power_off_signal */ [3] = trampoline_rt_bandwidth, + [6] = trampoline_set_frame_sync_props, [100] = iomfbep_cb_match_pmu_service, [101] = trampoline_zero, /* get_display_default_stride */ [102] = trampoline_nop, /* set_number_property */ - [103] = trampoline_nop, /* set_boolean_property */ - [106] = trampoline_nop, /* remove_property */ - [107] = trampoline_true, /* create_provider_service */ - [108] = trampoline_true, /* create_product_service */ - [109] = trampoline_true, /* create_pmu_service */ - [110] = trampoline_true, /* create_iomfb_service */ - [111] = trampoline_true, /* create_backlight_service */ - [112] = trampoline_true, /* create_nvram_servce? */ - [113] = trampoline_get_tiling_state, - [114] = trampoline_false, /* set_tiling_state */ - [119] = dcpep_cb_boot_1, - [120] = trampoline_false, /* is_dark_boot */ - [121] = trampoline_false, /* is_dark_boot / is_waking_from_hibernate*/ - [123] = trampoline_read_edt_data, - [125] = trampoline_prop_start, - [126] = trampoline_prop_chunk, - [127] = trampoline_prop_end, + [103] = trampoline_nop, /* trigger_user_cal_loader */ + [104] = trampoline_nop, /* set_boolean_property */ + [107] = trampoline_nop, /* remove_property */ + [108] = trampoline_true, /* create_provider_service */ + [109] = trampoline_true, /* create_product_service */ + [110] = trampoline_true, /* create_pmu_service */ + [111] = trampoline_true, /* create_iomfb_service */ + [112] = trampoline_true, /* create_backlight_service */ + [113] = trampoline_true, /* create_nvram_servce? */ + [114] = trampoline_get_tiling_state, + [115] = trampoline_false, /* set_tiling_state */ + [120] = dcpep_cb_boot_1, + [121] = trampoline_false, /* is_dark_boot */ + [122] = trampoline_false, /* is_dark_boot / is_waking_from_hibernate*/ + [124] = trampoline_read_edt_data, + [126] = trampoline_prop_start, + [127] = trampoline_prop_chunk, + [128] = trampoline_prop_end, [201] = trampoline_map_piodma, [202] = trampoline_unmap_piodma, [206] = iomfbep_cb_match_pmu_service_2, [207] = iomfbep_cb_match_backlight_service, - [208] = trampoline_get_time, - [211] = trampoline_nop, /* update_backlight_factor_prop */ + [208] = trampoline_nop, /* update_backlight_factor_prop */ + [209] = trampoline_get_time, [300] = trampoline_pr_publish, [401] = trampoline_get_uint_prop, [404] = trampoline_nop, /* sr_set_uint_prop */ diff --git a/drivers/gpu/drm/apple/iomfb_v13_2.h b/drivers/gpu/drm/apple/iomfb_v13_3.h similarity index 52% rename from drivers/gpu/drm/apple/iomfb_v13_2.h rename to drivers/gpu/drm/apple/iomfb_v13_3.h index f3810b727235bc..bbb3156b40f893 100644 --- a/drivers/gpu/drm/apple/iomfb_v13_2.h +++ b/drivers/gpu/drm/apple/iomfb_v13_3.h @@ -1,17 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright The Asahi Linux Contributors */ -#ifndef __APPLE_IOMFB_V13_2_H__ -#define __APPLE_IOMFB_V13_2_H__ +#ifndef __APPLE_IOMFB_V13_3_H__ +#define __APPLE_IOMFB_V13_3_H__ #include "version_utils.h" -#define DCP_FW v13_2 -#define DCP_FW_VER DCP_FW_VERSION(13, 2, 0) +#define DCP_FW v13_3 +#define DCP_FW_VER DCP_FW_VERSION(13, 3, 0) #include "iomfb_template.h" #undef DCP_FW_VER #undef DCP_FW -#endif /* __APPLE_IOMFB_V13_2_H__ */ +#endif /* __APPLE_IOMFB_V13_3_H__ */ From a045e279b74fc116e935cc7743ac405487c6bf30 Mon Sep 17 00:00:00 2001 From: Zongmin Zhou Date: Tue, 28 Mar 2023 10:31:29 +0800 Subject: [PATCH 0526/1020] drm/probe_helper: fix the warning reported when calling drm_kms_helper_poll_disable during suspend When drivers call drm_kms_helper_poll_disable from their device suspend implementation without enabled output polling before, following warning will be reported,due to work->func not be initialized: [ 55.141361] WARNING: CPU: 3 PID: 372 at kernel/workqueue.c:3066 __flush_work+0x22f/0x240 [ 55.141382] Modules linked in: nls_iso8859_1 snd_hda_codec_generic ledtrig_audio snd_hda_intel snd_intel_dspcfg snd_intel_sdw_acpi snd_hda_codec snd_hda_core snd_hwdep snd_pcm snd_seq_midi snd_seq_midi_event snd_rawmidi snd_seq intel_rapl_msr intel_rapl_common bochs drm_vram_helper drm_ttm_helper snd_seq_device nfit ttm crct10dif_pclmul snd_timer ghash_clmulni_intel binfmt_misc sha512_ssse3 aesni_intel drm_kms_helper joydev input_leds syscopyarea crypto_simd snd cryptd sysfillrect sysimgblt mac_hid serio_raw soundcore qemu_fw_cfg sch_fq_codel msr parport_pc ppdev lp parport drm ramoops reed_solomon pstore_blk pstore_zone efi_pstore virtio_rng ip_tables x_tables autofs4 hid_generic usbhid hid ahci virtio_net i2c_i801 crc32_pclmul psmouse virtio_scsi libahci i2c_smbus lpc_ich xhci_pci net_failover virtio_blk xhci_pci_renesas failover [ 55.141430] CPU: 3 PID: 372 Comm: kworker/u16:9 Not tainted 6.2.0-rc6+ #16 [ 55.141433] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.12.1-0-ga5cab58e9a3f-prebuilt.qemu.org 04/01/2014 [ 55.141435] Workqueue: events_unbound async_run_entry_fn [ 55.141441] RIP: 0010:__flush_work+0x22f/0x240 [ 55.141444] Code: 8b 43 28 48 8b 53 30 89 c1 e9 f9 fe ff ff 4c 89 f7 e8 b5 95 d9 00 e8 00 53 08 00 45 31 ff e9 11 ff ff ff 0f 0b e9 0a ff ff ff <0f> 0b 45 31 ff e9 00 ff ff ff e8 e2 54 d8 00 66 90 90 90 90 90 90 [ 55.141446] RSP: 0018:ff59221940833c18 EFLAGS: 00010246 [ 55.141449] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffffffff9b72bcbe [ 55.141450] RDX: 0000000000000001 RSI: 0000000000000001 RDI: ff3ea01e4265e330 [ 55.141451] RBP: ff59221940833c90 R08: 0000000000000000 R09: 8080808080808080 [ 55.141453] R10: ff3ea01e42b3caf4 R11: 000000000000000f R12: ff3ea01e4265e330 [ 55.141454] R13: 0000000000000001 R14: ff3ea01e505e5e80 R15: 0000000000000001 [ 55.141455] FS: 0000000000000000(0000) GS:ff3ea01fb7cc0000(0000) knlGS:0000000000000000 [ 55.141456] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 55.141458] CR2: 0000563543ad1546 CR3: 000000010ee82005 CR4: 0000000000771ee0 [ 55.141464] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 55.141465] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [ 55.141466] PKRU: 55555554 [ 55.141467] Call Trace: [ 55.141469] [ 55.141472] ? pcie_wait_cmd+0xdf/0x220 [ 55.141478] ? mptcp_seq_show+0xe0/0x180 [ 55.141484] __cancel_work_timer+0x124/0x1b0 [ 55.141487] cancel_delayed_work_sync+0x17/0x20 [ 55.141490] drm_kms_helper_poll_disable+0x26/0x40 [drm_kms_helper] [ 55.141516] drm_mode_config_helper_suspend+0x25/0x90 [drm_kms_helper] [ 55.141531] ? __pm_runtime_resume+0x64/0x90 [ 55.141536] bochs_pm_suspend+0x16/0x20 [bochs] [ 55.141540] pci_pm_suspend+0x8b/0x1b0 [ 55.141545] ? __pfx_pci_pm_suspend+0x10/0x10 [ 55.141547] dpm_run_callback+0x4c/0x160 [ 55.141550] __device_suspend+0x14c/0x4c0 [ 55.141553] async_suspend+0x24/0xa0 [ 55.141555] async_run_entry_fn+0x34/0x120 [ 55.141557] process_one_work+0x21a/0x3f0 [ 55.141560] worker_thread+0x4e/0x3c0 [ 55.141563] ? __pfx_worker_thread+0x10/0x10 [ 55.141565] kthread+0xf2/0x120 [ 55.141568] ? __pfx_kthread+0x10/0x10 [ 55.141570] ret_from_fork+0x29/0x50 [ 55.141575] [ 55.141575] ---[ end trace 0000000000000000 ]--- Fixes: a4e771729a51 ("drm/probe_helper: sort out poll_running vs poll_enabled") Signed-off-by: Zongmin Zhou --- drivers/gpu/drm/drm_probe_helper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index bf2dd1f46b6c4f..d67abd96e7a057 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -902,7 +902,8 @@ void drm_kms_helper_poll_disable(struct drm_device *dev) if (dev->mode_config.poll_running) drm_kms_helper_disable_hpd(dev); - cancel_delayed_work_sync(&dev->mode_config.output_poll_work); + if (dev->mode_config.poll_enabled) + cancel_delayed_work_sync(&dev->mode_config.output_poll_work); dev->mode_config.poll_running = false; } From d5b328856c788c83a18be2d19a37189ab960a45c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 15 Apr 2023 16:43:39 +0200 Subject: [PATCH 0527/1020] drm/apple: Remove simpledrm framebuffer before DRM device alloc Should result in drm apple to be registered as first DRM device replacing simpledrm. Should resolve problems with userspace assuming that card0 is the main displays device. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index b487a71d617a77..e34e73d690f5c6 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -467,6 +467,14 @@ static int apple_drm_init(struct device *dev) if (ret) return ret; + fb_size = fb_r.end - fb_r.start + 1; + ret = drm_aperture_remove_conflicting_framebuffers(fb_r.start, fb_size, + &apple_drm_driver); + if (ret) { + dev_err(dev, "Failed remove fb: %d\n", ret); + goto err_unbind; + } + apple = devm_drm_dev_alloc(dev, &apple_drm_driver, struct apple_drm_private, drm); if (IS_ERR(apple)) @@ -478,14 +486,6 @@ static int apple_drm_init(struct device *dev) if (ret) return ret; - fb_size = fb_r.end - fb_r.start + 1; - ret = drm_aperture_remove_conflicting_framebuffers(fb_r.start, fb_size, - false, &apple_drm_driver); - if (ret) { - dev_err(dev, "Failed remove fb: %d\n", ret); - goto err_unbind; - } - ret = drmm_mode_config_init(&apple->drm); if (ret) goto err_unbind; From cb6d13b41a2dcdf58af8d25ea9cd5945bb3b24dc Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 25 Apr 2023 01:49:14 +0900 Subject: [PATCH 0528/1020] drm/apple: Mark DCP as being in the wakeup path This prevents the PD from being shut down on suspend, which we need until we support runtime PM properly again. Signed-off-by: Hector Martin --- drivers/gpu/drm/apple/dcp.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 4925b433ede3e3..cc1b74275444eb 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -586,6 +586,26 @@ static void dcp_platform_shutdown(struct platform_device *pdev) component_del(&pdev->dev, &dcp_comp_ops); } +static __maybe_unused int dcp_platform_suspend(struct device *dev) +{ + /* + * Set the device as a wakeup device, which forces its power + * domains to stay on. We need this as we do not support full + * shutdown properly yet. + */ + device_set_wakeup_path(dev); + + return 0; +} + +static __maybe_unused int dcp_platform_resume(struct device *dev) +{ + return 0; +} + +static SIMPLE_DEV_PM_OPS(dcp_platform_pm_ops, + dcp_platform_suspend, dcp_platform_resume); + static const struct of_device_id of_match[] = { { .compatible = "apple,dcp" }, {} @@ -599,6 +619,7 @@ static struct platform_driver apple_platform_driver = { .driver = { .name = "apple-dcp", .of_match_table = of_match, + .pm = pm_sleep_ptr(&dcp_platform_pm_ops), }, }; From 42563d63d0752cbf8adefb62a55e3e351120c1bb Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 30 Apr 2023 16:36:33 +0200 Subject: [PATCH 0529/1020] drm: apple: iomfb: Increase modeset timeout to 2.5 seconds Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb_template.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index b2bd5d384b5c39..0871888c987bed 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -1276,12 +1276,12 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru dev_dbg(dcp->dev, "%s - wait for modeset", __func__); ret = wait_for_completion_timeout(&cookie->done, - msecs_to_jiffies(500)); + msecs_to_jiffies(2500)); kref_put(&cookie->refcount, release_wait_cookie); if (ret == 0) { - dev_dbg(dcp->dev, "set_digital_out_mode 200 ms"); + dev_info(dcp->dev, "set_digital_out_mode timed out"); schedule_work(&dcp->vblank_wq); return; } else if (ret > 0) { From d570a52d6df203ae0f246c01a9e396edafb8a939 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 30 Apr 2023 16:01:01 +0200 Subject: [PATCH 0530/1020] drm: apple: Only match backlight service on DCP with panel Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp-internal.h | 1 + drivers/gpu/drm/apple/dcp.c | 5 +++++ drivers/gpu/drm/apple/iomfb_template.c | 24 +++++++++++++++++++----- drivers/gpu/drm/apple/iomfb_v12_3.c | 2 +- drivers/gpu/drm/apple/iomfb_v13_3.c | 2 +- 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index fee15867b5c0cf..e4857e16616b8c 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -195,5 +195,6 @@ struct apple_dcp { }; int dcp_backlight_register(struct apple_dcp *dcp); +bool dcp_has_panel(struct apple_dcp *dcp); #endif /* __APPLE_DCP_INTERNAL_H__ */ diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index cc1b74275444eb..31a3095a74f48a 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -85,6 +85,11 @@ void dcp_set_dimensions(struct apple_dcp *dcp) } } +bool dcp_has_panel(struct apple_dcp *dcp) +{ + return dcp->panel.width_mm > 0; +} + /* * Helper to send a DRM vblank event. We do not know how call swap_submit_dcp * without surfaces. To avoid timeouts in drm_atomic_helper_wait_for_vblanks diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 0871888c987bed..a4a787cea87f12 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -183,6 +183,12 @@ static bool iomfbep_cb_match_backlight_service(struct apple_dcp *dcp, int tag, v { trace_iomfb_callback(dcp, tag, __func__); + if (!dcp_has_panel(dcp)) { + u8 *succ = out; + *succ = true; + return true; + } + iomfb_a132_backlight_service_matched(dcp, false, complete_backlight_service_matched, out); // return false for deferred ACK @@ -194,11 +200,13 @@ static void iomfb_cb_pr_publish(struct apple_dcp *dcp, struct iomfb_property *pr switch (prop->id) { case IOMFB_PROPERTY_NITS: { - dcp->brightness.nits = prop->value / dcp->brightness.scale; - /* notify backlight device of the initial brightness */ - if (!dcp->brightness.bl_dev && dcp->brightness.maximum > 0) - schedule_work(&dcp->bl_register_wq); - trace_iomfb_brightness(dcp, prop->value); + if (dcp_has_panel(dcp)) { + dcp->brightness.nits = prop->value / dcp->brightness.scale; + /* notify backlight device of the initial brightness */ + if (!dcp->brightness.bl_dev && dcp->brightness.maximum > 0) + schedule_work(&dcp->bl_register_wq); + trace_iomfb_brightness(dcp, prop->value); + } break; } default: @@ -1003,6 +1011,11 @@ dcpep_cb_get_tiling_state(struct apple_dcp *dcp, }; } +static u8 dcpep_cb_create_backlight_service(struct apple_dcp *dcp) +{ + return dcp_has_panel(dcp); +} + TRAMPOLINE_VOID(trampoline_nop, dcpep_cb_nop); TRAMPOLINE_OUT(trampoline_true, dcpep_cb_true, u8); TRAMPOLINE_OUT(trampoline_false, dcpep_cb_false, u8); @@ -1053,6 +1066,7 @@ TRAMPOLINE_IN(trampoline_pr_publish, iomfb_cb_pr_publish, struct iomfb_property); TRAMPOLINE_INOUT(trampoline_get_tiling_state, dcpep_cb_get_tiling_state, struct dcpep_get_tiling_state_req, struct dcpep_get_tiling_state_resp); +TRAMPOLINE_OUT(trampoline_create_backlight_service, dcpep_cb_create_backlight_service, u8); /* * Callback for swap requests. If a swap failed, we'll never get a swap diff --git a/drivers/gpu/drm/apple/iomfb_v12_3.c b/drivers/gpu/drm/apple/iomfb_v12_3.c index 8188321004a63f..5bc8bc2f8bd290 100644 --- a/drivers/gpu/drm/apple/iomfb_v12_3.c +++ b/drivers/gpu/drm/apple/iomfb_v12_3.c @@ -49,7 +49,7 @@ static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = { [108] = trampoline_true, /* create_product_service */ [109] = trampoline_true, /* create_pmu_service */ [110] = trampoline_true, /* create_iomfb_service */ - [111] = trampoline_true, /* create_backlight_service */ + [111] = trampoline_create_backlight_service, [116] = dcpep_cb_boot_1, [117] = trampoline_false, /* is_dark_boot */ [118] = trampoline_false, /* is_dark_boot / is_waking_from_hibernate*/ diff --git a/drivers/gpu/drm/apple/iomfb_v13_3.c b/drivers/gpu/drm/apple/iomfb_v13_3.c index 18020c6cd39493..b82ed1f32e0e8e 100644 --- a/drivers/gpu/drm/apple/iomfb_v13_3.c +++ b/drivers/gpu/drm/apple/iomfb_v13_3.c @@ -51,7 +51,7 @@ static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = { [109] = trampoline_true, /* create_product_service */ [110] = trampoline_true, /* create_pmu_service */ [111] = trampoline_true, /* create_iomfb_service */ - [112] = trampoline_true, /* create_backlight_service */ + [112] = trampoline_create_backlight_service, [113] = trampoline_true, /* create_nvram_servce? */ [114] = trampoline_get_tiling_state, [115] = trampoline_false, /* set_tiling_state */ From 8b3ec6a9e5f91610174fcd6222b12f18ab251308 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 30 Apr 2023 16:34:14 +0200 Subject: [PATCH 0531/1020] drm: apple: iomfb: limit backlight updates to integrated panels Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb_template.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index a4a787cea87f12..90f3a2030beb9c 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -876,9 +876,11 @@ void DCP_FW_NAME(iomfb_poweroff)(struct apple_dcp *dcp) * subsequent update on poweron an actual change and restore the * brightness. */ - swap->swap.bl_unk = 1; - swap->swap.bl_value = 0; - swap->swap.bl_power = 0; + if (dcp_has_panel(dcp)) { + swap->swap.bl_unk = 1; + swap->swap.bl_value = 0; + swap->swap.bl_power = 0; + } for (int l = 0; l < SWAP_SURFACES; l++) swap->surf_null[l] = true; @@ -1324,7 +1326,7 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru req->swap.swap_completed = req->swap.swap_enabled; /* update brightness if changed */ - if (dcp->brightness.update) { + if (dcp_has_panel(dcp) && dcp->brightness.update) { req->swap.bl_unk = 1; req->swap.bl_value = dcp->brightness.dac; req->swap.bl_power = 0x40; From e3bbecf65faee987dafc039889d954682346aa31 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 16 Jul 2023 17:51:10 +0200 Subject: [PATCH 0532/1020] drm: apple: backlight: avoid updating the brightness with a commit An atomic_commit for brightness changes will consume a DCP swap without frame buffer updates and will result in a lost frame. After updating the next brightness values wait for 1 frame duration (at 23.976 fps). Check if the brightness update still needs to be send to DVCP or if a swap did that in the meintime. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp_backlight.c | 28 ++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp_backlight.c b/drivers/gpu/drm/apple/dcp_backlight.c index d063ecd7ad2068..0eeb3d6d92c5a2 100644 --- a/drivers/gpu/drm/apple/dcp_backlight.c +++ b/drivers/gpu/drm/apple/dcp_backlight.c @@ -136,18 +136,24 @@ static u32 calculate_dac(struct apple_dcp *dcp, int val) return 16 * dac; } -static int drm_crtc_set_brightness(struct drm_crtc *crtc, - struct drm_modeset_acquire_ctx *ctx) +static int drm_crtc_set_brightness(struct apple_dcp *dcp) { struct drm_atomic_state *state; struct drm_crtc_state *crtc_state; + struct drm_modeset_acquire_ctx ctx; + struct drm_crtc *crtc = &dcp->crtc->base; int ret = 0; + DRM_MODESET_LOCK_ALL_BEGIN(crtc->dev, ctx, 0, ret); + + if (!dcp->brightness.update) + goto done; + state = drm_atomic_state_alloc(crtc->dev); if (!state) return -ENOMEM; - state->acquire_ctx = ctx; + state->acquire_ctx = &ctx; crtc_state = drm_atomic_get_crtc_state(state, crtc); if (IS_ERR(crtc_state)) { ret = PTR_ERR(crtc_state); @@ -160,6 +166,9 @@ static int drm_crtc_set_brightness(struct drm_crtc *crtc, fail: drm_atomic_state_put(state); +done: + DRM_MODESET_LOCK_ALL_END(crtc->dev, ctx, ret); + return ret; } @@ -175,6 +184,8 @@ static int dcp_set_brightness(struct backlight_device *bd) dcp->brightness.dac = calculate_dac(dcp, brightness); dcp->brightness.update = true; + DRM_MODESET_LOCK_ALL_END(dcp->crtc->base.dev, ctx, ret); + /* * Do not actively try to change brightness if no mode is set. * TODO: should this be reflected the in backlight's power property? @@ -182,14 +193,13 @@ static int dcp_set_brightness(struct backlight_device *bd) * drm integrated backlight handling */ if (!dcp->valid_mode) - goto out; - - ret = drm_crtc_set_brightness(&dcp->crtc->base, &ctx); + return 0; -out: - DRM_MODESET_LOCK_ALL_END(dcp->crtc->base.dev, ctx, ret); + /* Wait 1 vblank cycle in the hope an atomic swap has already updated + * the brightness */ + msleep((1001 + 23) / 24); // 42ms for 23.976 fps - return ret; + return drm_crtc_set_brightness(dcp); } static const struct backlight_ops dcp_backlight_ops = { From 4c9ba7a249ff2dadb3e1715a3b49b37e0b355468 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 15 Jul 2023 18:59:10 +0200 Subject: [PATCH 0533/1020] drm/apple: Get rid of the piodma dummy driver It's only needed to configure the display contoller's iommu to share buffers between the DCP co-processor and the display controller. Possible concern is runtime PM for it and its iommu. If we don't set it up the power domain might never go to lower power states even if it could. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/Makefile | 2 - drivers/gpu/drm/apple/apple_drv.c | 23 ---------- drivers/gpu/drm/apple/dcp.c | 64 ++++++++++++++++++-------- drivers/gpu/drm/apple/dummy-piodma.c | 68 ---------------------------- 4 files changed, 44 insertions(+), 113 deletions(-) delete mode 100644 drivers/gpu/drm/apple/dummy-piodma.c diff --git a/drivers/gpu/drm/apple/Makefile b/drivers/gpu/drm/apple/Makefile index 71e1aa3766f37b..f6490a8e09e2ba 100644 --- a/drivers/gpu/drm/apple/Makefile +++ b/drivers/gpu/drm/apple/Makefile @@ -9,11 +9,9 @@ apple_dcp-y += iomfb_v12_3.o apple_dcp-y += iomfb_v13_3.o apple_dcp-$(CONFIG_TRACING) += trace.o -apple_piodma-y := dummy-piodma.o obj-$(CONFIG_DRM_APPLE) += appledrm.o obj-$(CONFIG_DRM_APPLE) += apple_dcp.o -obj-$(CONFIG_DRM_APPLE) += apple_piodma.o # header test diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index e34e73d690f5c6..bb947102c10e90 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -556,26 +556,6 @@ const struct component_master_ops apple_drm_ops = { .unbind = apple_drm_unbind, }; -static const struct of_device_id apple_component_id_tbl[] = { - { .compatible = "apple,dcp-piodma" }, - {}, -}; - -static int add_display_components(struct device *dev, - struct component_match **matchptr) -{ - struct device_node *np; - - for_each_matching_node(np, apple_component_id_tbl) { - if (of_device_is_available(np)) - drm_of_component_match_add(dev, matchptr, - component_compare_of, np); - of_node_put(np); - } - - return 0; -} - static int add_dcp_components(struct device *dev, struct component_match **matchptr) { @@ -600,9 +580,6 @@ static int apple_platform_probe(struct platform_device *pdev) struct component_match *match = NULL; int num_dcp; - /* add PIODMA mapper components */ - add_display_components(mdev, &match); - /* add DCP components, handle less than 1 as probe error */ num_dcp = add_dcp_components(mdev, &match); if (num_dcp < 1) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 31a3095a74f48a..fdc2b3a43ecee1 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -316,17 +317,39 @@ static void dcp_work_register_backlight(struct work_struct *work) mutex_unlock(&dcp->bl_register_mutex); } -static struct platform_device *dcp_get_dev(struct device *dev, const char *name) +static int dcp_create_piodma_iommu_dev(struct apple_dcp *dcp) { - struct platform_device *pdev; - struct device_node *node = of_parse_phandle(dev->of_node, name, 0); + int ret; + struct device_node *node = of_get_child_by_name(dcp->dev->of_node, "piodma"); if (!node) - return NULL; + return dev_err_probe(dcp->dev, -ENODEV, + "Failed to get piodma child DT node\n"); + + dcp->piodma = of_platform_device_create(node, NULL, dcp->dev); + if (!dcp->piodma) { + of_node_put(node); + return dev_err_probe(dcp->dev, -ENODEV, "Failed to gcreate piodma pdev for %pOF\n", node); + } + + ret = dma_set_mask_and_coherent(&dcp->piodma->dev, DMA_BIT_MASK(42)); + if (ret) + goto err_destroy_pdev; + + ret = of_dma_configure(&dcp->piodma->dev, node, true); + if (ret) { + ret = dev_err_probe(dcp->dev, ret, + "Failed to configure IOMMU child DMA\n"); + goto err_destroy_pdev; + } + of_node_put(node); - pdev = of_find_device_by_node(node); + return 0; + +err_destroy_pdev: of_node_put(node); - return pdev; + of_platform_device_destroy(&dcp->piodma->dev, NULL); + return ret; } static int dcp_get_disp_regs(struct apple_dcp *dcp) @@ -432,8 +455,6 @@ static int dcp_comp_bind(struct device *dev, struct device *main, void *data) if (IS_ERR(dcp->coproc_reg)) return PTR_ERR(dcp->coproc_reg); - of_platform_default_populate(dev->of_node, NULL, dev); - if (!show_notch) ret = of_property_read_u32(dev->of_node, "apple,notch-height", &dcp->notch_height); @@ -479,16 +500,10 @@ static int dcp_comp_bind(struct device *dev, struct device *main, void *data) else dcp->connector_type = DRM_MODE_CONNECTOR_Unknown; - /* - * Components do not ensure the bind order of sub components but - * the piodma device is only used for its iommu. The iommu is fully - * initialized by the time dcp_piodma_probe() calls component_add(). - */ - dcp->piodma = dcp_get_dev(dev, "apple,piodma-mapper"); - if (!dcp->piodma) { - dev_err(dev, "failed to find piodma\n"); - return -ENODEV; - } + ret = dcp_create_piodma_iommu_dev(dcp); + if (ret) + return dev_err_probe(dev, ret, + "Failed to created PIODMA iommu child device"); ret = dcp_get_disp_regs(dcp); if (ret) { @@ -545,8 +560,10 @@ static void dcp_comp_unbind(struct device *dev, struct device *main, void *data) if (dcp && dcp->shmem) iomfb_shutdown(dcp); - platform_device_put(dcp->piodma); - dcp->piodma = NULL; + if (dcp->piodma) { + of_platform_device_destroy(&dcp->piodma->dev, NULL); + dcp->piodma = NULL; + } devm_clk_put(dev, dcp->clk); dcp->clk = NULL; @@ -562,6 +579,7 @@ static int dcp_platform_probe(struct platform_device *pdev) enum dcp_firmware_version fw_compat; struct device *dev = &pdev->dev; struct apple_dcp *dcp; + int ret; fw_compat = dcp_check_firmware_version(dev); if (fw_compat == DCP_FIRMWARE_UNKNOWN) @@ -576,6 +594,12 @@ static int dcp_platform_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dcp); + ret = devm_of_platform_populate(dev); + if (ret) { + dev_err(dev, "failed to populate child devices: %d\n", ret); + return ret; + } + return component_add(&pdev->dev, &dcp_comp_ops); } diff --git a/drivers/gpu/drm/apple/dummy-piodma.c b/drivers/gpu/drm/apple/dummy-piodma.c deleted file mode 100644 index eaa0476854a603..00000000000000 --- a/drivers/gpu/drm/apple/dummy-piodma.c +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only OR MIT -/* Copyright 2021 Alyssa Rosenzweig */ - -#include - -#include -#include -#include -#include - -static int dcp_piodma_comp_bind(struct device *dev, struct device *main, - void *data) -{ - return 0; -} - -static void dcp_piodma_comp_unbind(struct device *dev, struct device *main, - void *data) -{ - /* nothing to do */ -} - -static const struct component_ops dcp_piodma_comp_ops = { - .bind = dcp_piodma_comp_bind, - .unbind = dcp_piodma_comp_unbind, -}; -static int dcp_piodma_probe(struct platform_device *pdev) -{ - int ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(42)); - if (ret) - return ret; - - return component_add(&pdev->dev, &dcp_piodma_comp_ops); -} - -static int dcp_piodma_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &dcp_piodma_comp_ops); - - return 0; -} - -static void dcp_piodma_shutdown(struct platform_device *pdev) -{ - component_del(&pdev->dev, &dcp_piodma_comp_ops); -} - -static const struct of_device_id of_match[] = { - { .compatible = "apple,dcp-piodma" }, - {} -}; -MODULE_DEVICE_TABLE(of, of_match); - -static struct platform_driver dcp_piodma_platform_driver = { - .probe = dcp_piodma_probe, - .remove = dcp_piodma_remove, - .shutdown = dcp_piodma_shutdown, - .driver = { - .name = "apple,dcp-piodma", - .of_match_table = of_match, - }, -}; - -drm_module_platform_driver(dcp_piodma_platform_driver); - -MODULE_AUTHOR("Alyssa Rosenzweig "); -MODULE_DESCRIPTION("[HACK] Apple DCP PIODMA shim"); -MODULE_LICENSE("Dual MIT/GPL"); From 71a637797ffdd42285c7a1b323a8fe52227993f2 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 19 Jul 2023 09:22:24 +0200 Subject: [PATCH 0534/1020] drm/apple: Use iommu domain for piodma maps The current use of of dma_get_sgtable/dma_map_sgtable is deemed unsafe. Replace it with an unmanaged iommu domain for the piodma iommu to map the buffers. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp-internal.h | 1 + drivers/gpu/drm/apple/dcp.c | 18 +++++++++++- drivers/gpu/drm/apple/iomfb_template.c | 40 +++++++++++++------------- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index e4857e16616b8c..8baf529f1463c7 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -97,6 +97,7 @@ struct dcp_panel { struct apple_dcp { struct device *dev; struct platform_device *piodma; + struct iommu_domain *iommu_dom; struct apple_rtkit *rtk; struct apple_crtc *crtc; struct apple_connector *connector; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index fdc2b3a43ecee1..4e5812b5c2d729 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -344,8 +344,22 @@ static int dcp_create_piodma_iommu_dev(struct apple_dcp *dcp) } of_node_put(node); - return 0; + dcp->iommu_dom = iommu_domain_alloc(&platform_bus_type); + if (!dcp->iommu_dom) { + ret = -ENOMEM; + goto err_destroy_pdev; + } + + ret = iommu_attach_device(dcp->iommu_dom, &dcp->piodma->dev); + if (ret) { + ret = dev_err_probe(dcp->dev, ret, + "Failed to attach IOMMU child domain\n"); + goto err_free_domain; + } + return 0; +err_free_domain: + iommu_domain_free(dcp->iommu_dom); err_destroy_pdev: of_node_put(node); of_platform_device_destroy(&dcp->piodma->dev, NULL); @@ -561,6 +575,8 @@ static void dcp_comp_unbind(struct device *dev, struct device *main, void *data) iomfb_shutdown(dcp); if (dcp->piodma) { + iommu_detach_device(dcp->iommu_dom, &dcp->piodma->dev); + iommu_domain_free(dcp->iommu_dom); of_platform_device_destroy(&dcp->piodma->dev, NULL); dcp->piodma = NULL; } diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 90f3a2030beb9c..78da8dc79796ca 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -258,32 +258,33 @@ static void iomfbep_cb_set_fx_prop(struct apple_dcp *dcp, struct iomfb_set_fx_pr * Callback to map a buffer allocated with allocate_buf for PIODMA usage. * PIODMA is separate from the main DCP and uses own IOVA space on a dedicated * stream of the display DART, rather than the expected DCP DART. - * - * XXX: This relies on dma_get_sgtable in concert with dma_map_sgtable, which - * is a "fundamentally unsafe" operation according to the docs. And yet - * everyone does it... */ static struct dcp_map_buf_resp dcpep_cb_map_piodma(struct apple_dcp *dcp, struct dcp_map_buf_req *req) { + struct dcp_mem_descriptor *memdesc; struct sg_table *map; - int ret; + ssize_t ret; if (req->buffer >= ARRAY_SIZE(dcp->memdesc)) goto reject; - map = &dcp->memdesc[req->buffer].map; + memdesc = &dcp->memdesc[req->buffer]; + map = &memdesc->map; if (!map->sgl) goto reject; - /* Use PIODMA device instead of DCP to map against the right IOMMU. */ - ret = dma_map_sgtable(&dcp->piodma->dev, map, DMA_BIDIRECTIONAL, 0); + /* use the piodma iommu domain to map against the right IOMMU */ + ret = iommu_map_sgtable(dcp->iommu_dom, memdesc->dva, map, + IOMMU_READ | IOMMU_WRITE); - if (ret) + if (ret != memdesc->size) { + dev_err(dcp->dev, "iommu_map_sgtable() returned %zd instead of expected buffer size of %zu\n", ret, memdesc->size); goto reject; + } - return (struct dcp_map_buf_resp){ .dva = sg_dma_address(map->sgl) }; + return (struct dcp_map_buf_resp){ .dva = memdesc->dva }; reject: dev_err(dcp->dev, "denying map of invalid buffer %llx for pidoma\n", @@ -294,8 +295,7 @@ static struct dcp_map_buf_resp dcpep_cb_map_piodma(struct apple_dcp *dcp, static void dcpep_cb_unmap_piodma(struct apple_dcp *dcp, struct dcp_unmap_buf_resp *resp) { - struct sg_table *map; - dma_addr_t dma_addr; + struct dcp_mem_descriptor *memdesc; if (resp->buffer >= ARRAY_SIZE(dcp->memdesc)) { dev_warn(dcp->dev, "unmap request for out of range buffer %llu", @@ -303,24 +303,24 @@ static void dcpep_cb_unmap_piodma(struct apple_dcp *dcp, return; } - map = &dcp->memdesc[resp->buffer].map; + memdesc = &dcp->memdesc[resp->buffer]; - if (!map->sgl) { + if (!memdesc->buf) { dev_warn(dcp->dev, "unmap for non-mapped buffer %llu iova:0x%08llx", resp->buffer, resp->dva); return; } - dma_addr = sg_dma_address(map->sgl); - if (dma_addr != resp->dva) { - dev_warn(dcp->dev, "unmap buffer %llu address mismatch dma_addr:%llx dva:%llx", - resp->buffer, dma_addr, resp->dva); + if (memdesc->dva != resp->dva) { + dev_warn(dcp->dev, "unmap buffer %llu address mismatch " + "memdesc.dva:%llx dva:%llx", resp->buffer, + memdesc->dva, resp->dva); return; } - /* Use PIODMA device instead of DCP to unmap from the right IOMMU. */ - dma_unmap_sgtable(&dcp->piodma->dev, map, DMA_BIDIRECTIONAL, 0); + /* use the piodma iommu domain to unmap from the right IOMMU */ + iommu_unmap(dcp->iommu_dom, memdesc->dva, memdesc->size); } /* From 0509c135b9c8f3fa78a5e351cf57760c703d4124 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 20 Jul 2023 00:36:51 +0200 Subject: [PATCH 0535/1020] drm: apple: Align PIODMA buffers to SZ_16K The iommu scatter table/list mapping can only map full iommu page size extents. Just align the actual the allocation to the iommu page size. This could be handled differently using DARTs subpage protection but there's no easy way to integrate that. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb_template.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 78da8dc79796ca..81ffbc5d1cf660 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -279,7 +279,10 @@ static struct dcp_map_buf_resp dcpep_cb_map_piodma(struct apple_dcp *dcp, ret = iommu_map_sgtable(dcp->iommu_dom, memdesc->dva, map, IOMMU_READ | IOMMU_WRITE); - if (ret != memdesc->size) { + /* HACK: expect size to be 16K aligned since the iommu API only maps + * full pages + */ + if (ret < 0 || ret != ALIGN(memdesc->size, SZ_16K)) { dev_err(dcp->dev, "iommu_map_sgtable() returned %zd instead of expected buffer size of %zu\n", ret, memdesc->size); goto reject; } @@ -334,6 +337,7 @@ dcpep_cb_allocate_buffer(struct apple_dcp *dcp, { struct dcp_allocate_buffer_resp resp = { 0 }; struct dcp_mem_descriptor *memdesc; + size_t size; u32 id; resp.dva_size = ALIGN(req->size, 4096); @@ -352,11 +356,13 @@ dcpep_cb_allocate_buffer(struct apple_dcp *dcp, memdesc = &dcp->memdesc[id]; memdesc->size = resp.dva_size; - memdesc->buf = dma_alloc_coherent(dcp->dev, memdesc->size, + /* HACK: align size to 16K since the iommu API only maps full pages */ + size = ALIGN(resp.dva_size, SZ_16K); + memdesc->buf = dma_alloc_coherent(dcp->dev, size, &memdesc->dva, GFP_KERNEL); dma_get_sgtable(dcp->dev, &memdesc->map, memdesc->buf, memdesc->dva, - memdesc->size); + size); resp.dva = memdesc->dva; return resp; From bf93b3af1eecffbfa03640cd3f77769a6ddb3d89 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 23 Aug 2023 20:50:35 +0200 Subject: [PATCH 0536/1020] drm: apple: Add D129 allocate_bandwidth iomfb callback Used on M2 Ultra During startup. Units are unclear. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.h | 15 +++++++++++++++ drivers/gpu/drm/apple/iomfb_template.c | 12 ++++++++++++ drivers/gpu/drm/apple/iomfb_v13_3.c | 1 + 3 files changed, 28 insertions(+) diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index e8168338d374ef..e8d7fef09f9f86 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -127,6 +127,21 @@ struct dcp_component_types { u8 types[7]; } __packed; +struct dcp_allocate_bandwidth_req { + u64 unk1; + u64 unk2; + u64 unk3; + u8 unk1_null; + u8 unk2_null; + u8 padding[8]; +} __packed; + +struct dcp_allocate_bandwidth_resp { + u64 unk1; + u64 unk2; + u32 ret; +} __packed; + struct dcp_rt_bandwidth { u64 unk1; u64 reg_scratch; diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 81ffbc5d1cf660..63eec02c636704 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -652,6 +652,16 @@ static bool dcpep_cb_boot_1(struct apple_dcp *dcp, int tag, void *out, void *in) return false; } +static struct dcp_allocate_bandwidth_resp dcpep_cb_allocate_bandwidth(struct apple_dcp *dcp, + struct dcp_allocate_bandwidth_req *req) +{ + return (struct dcp_allocate_bandwidth_resp){ + .unk1 = req->unk1, + .unk2 = req->unk2, + .ret = 1, + }; +} + static struct dcp_rt_bandwidth dcpep_cb_rt_bandwidth(struct apple_dcp *dcp) { if (dcp->disp_registers[5] && dcp->disp_registers[6]) { @@ -1057,6 +1067,8 @@ TRAMPOLINE_INOUT(trampoline_prop_chunk, dcpep_cb_prop_chunk, struct dcp_set_dcpav_prop_chunk_req, u8); TRAMPOLINE_INOUT(trampoline_prop_end, dcpep_cb_prop_end, struct dcp_set_dcpav_prop_end_req, u8); +TRAMPOLINE_INOUT(trampoline_allocate_bandwidth, dcpep_cb_allocate_bandwidth, + struct dcp_allocate_bandwidth_req, struct dcp_allocate_bandwidth_resp); TRAMPOLINE_OUT(trampoline_rt_bandwidth, dcpep_cb_rt_bandwidth, struct dcp_rt_bandwidth); TRAMPOLINE_INOUT(trampoline_set_frame_sync_props, dcpep_cb_set_frame_sync_props, diff --git a/drivers/gpu/drm/apple/iomfb_v13_3.c b/drivers/gpu/drm/apple/iomfb_v13_3.c index b82ed1f32e0e8e..8e45fca918c320 100644 --- a/drivers/gpu/drm/apple/iomfb_v13_3.c +++ b/drivers/gpu/drm/apple/iomfb_v13_3.c @@ -62,6 +62,7 @@ static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = { [126] = trampoline_prop_start, [127] = trampoline_prop_chunk, [128] = trampoline_prop_end, + [129] = trampoline_allocate_bandwidth, [201] = trampoline_map_piodma, [202] = trampoline_unmap_piodma, [206] = iomfbep_cb_match_pmu_service_2, From aa2b2006aaa81c0619f19bd7fa30a72edef2bf25 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 4 Sep 2023 23:07:45 +0200 Subject: [PATCH 0537/1020] drm: apple: Update supported firmware versions to 12.3 and 13.5 Removes support for all firmware versions which report as compatible to 13.3 except 13.5. This will be removed after m1n1 reports firmware 13.5 as "apple,firmware-compat" for a while. The files with "v13_3" will be renamed at a later point to avoid conflicts with development trees. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp-internal.h | 2 +- drivers/gpu/drm/apple/dcp.c | 14 ++++++++++++-- drivers/gpu/drm/apple/iomfb.c | 12 ++++++------ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 8baf529f1463c7..7d54d28913f331 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -21,7 +21,7 @@ struct apple_dcp; enum dcp_firmware_version { DCP_FIRMWARE_UNKNOWN, DCP_FIRMWARE_V_12_3, - DCP_FIRMWARE_V_13_3, + DCP_FIRMWARE_V_13_5, }; enum { diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 4e5812b5c2d729..8a67437dcb428b 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -445,8 +445,18 @@ static enum dcp_firmware_version dcp_check_firmware_version(struct device *dev) if (strncmp(compat_str, "12.3.0", sizeof(compat_str)) == 0) return DCP_FIRMWARE_V_12_3; - if (strncmp(compat_str, "13.3.0", sizeof(compat_str)) == 0) - return DCP_FIRMWARE_V_13_3; + /* + * m1n1 reports firmware version 13.5 as compatible with 13.3. This is + * only true for the iomfb endpoint. The interface for the dptx-port + * endpoint changed between 13.3 and 13.5. The driver will only support + * firmware 13.5. Check the actual firmware version for compat version + * 13.3 until m1n1 reports 13.5 as "firmware-compat". + */ + else if ((strncmp(compat_str, "13.3.0", sizeof(compat_str)) == 0) && + (strncmp(fw_str, "13.5.0", sizeof(compat_str)) == 0)) + return DCP_FIRMWARE_V_13_5; + else if (strncmp(compat_str, "13.5.0", sizeof(compat_str)) == 0) + return DCP_FIRMWARE_V_13_5; dev_err(dev, "DCP firmware-compat %s (FW: %s) is not supported\n", compat_str, fw_str); diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 09fd542323a82c..2dd3987c6e36b4 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -224,7 +224,7 @@ void dcp_sleep(struct apple_dcp *dcp) case DCP_FIRMWARE_V_12_3: iomfb_sleep_v12_3(dcp); break; - case DCP_FIRMWARE_V_13_3: + case DCP_FIRMWARE_V_13_5: iomfb_sleep_v13_3(dcp); break; default: @@ -241,7 +241,7 @@ void dcp_poweron(struct platform_device *pdev) case DCP_FIRMWARE_V_12_3: iomfb_poweron_v12_3(dcp); break; - case DCP_FIRMWARE_V_13_3: + case DCP_FIRMWARE_V_13_5: iomfb_poweron_v13_3(dcp); break; default: @@ -259,7 +259,7 @@ void dcp_poweroff(struct platform_device *pdev) case DCP_FIRMWARE_V_12_3: iomfb_poweroff_v12_3(dcp); break; - case DCP_FIRMWARE_V_13_3: + case DCP_FIRMWARE_V_13_5: iomfb_poweroff_v13_3(dcp); break; default: @@ -504,7 +504,7 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) case DCP_FIRMWARE_V_12_3: iomfb_flush_v12_3(dcp, crtc, state); break; - case DCP_FIRMWARE_V_13_3: + case DCP_FIRMWARE_V_13_5: iomfb_flush_v13_3(dcp, crtc, state); break; default: @@ -520,7 +520,7 @@ void iomfb_start(struct apple_dcp *dcp) case DCP_FIRMWARE_V_12_3: iomfb_start_v12_3(dcp); break; - case DCP_FIRMWARE_V_13_3: + case DCP_FIRMWARE_V_13_5: iomfb_start_v13_3(dcp); break; default: @@ -573,7 +573,7 @@ void iomfb_shutdown(struct apple_dcp *dcp) case DCP_FIRMWARE_V_12_3: iomfb_shutdown_v12_3(dcp); break; - case DCP_FIRMWARE_V_13_3: + case DCP_FIRMWARE_V_13_5: iomfb_shutdown_v13_3(dcp); break; default: From 7b3fd529457cef4d706f11ff02a7346069012d27 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 7 Nov 2023 00:14:55 +0100 Subject: [PATCH 0538/1020] drm: apple: dcp: Port over to DEFINE_SIMPLE_DEV_PM_OPS Avoids ugly "__maybe_unused". Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 8a67437dcb428b..b6e19e8a1c5b3d 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -641,7 +641,7 @@ static void dcp_platform_shutdown(struct platform_device *pdev) component_del(&pdev->dev, &dcp_comp_ops); } -static __maybe_unused int dcp_platform_suspend(struct device *dev) +static int dcp_platform_suspend(struct device *dev) { /* * Set the device as a wakeup device, which forces its power @@ -653,13 +653,13 @@ static __maybe_unused int dcp_platform_suspend(struct device *dev) return 0; } -static __maybe_unused int dcp_platform_resume(struct device *dev) +static int dcp_platform_resume(struct device *dev) { return 0; } -static SIMPLE_DEV_PM_OPS(dcp_platform_pm_ops, - dcp_platform_suspend, dcp_platform_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(dcp_platform_pm_ops, + dcp_platform_suspend, dcp_platform_resume); static const struct of_device_id of_match[] = { { .compatible = "apple,dcp" }, From 86d38e18bc87a6e9ea5443299e075b7853ce7ea4 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 7 Nov 2023 00:30:54 +0100 Subject: [PATCH 0539/1020] drm: apple: dcp: Remove cargo-culted devm_of_platform_populate It does not do anything for dcp and its iommu only child node. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index b6e19e8a1c5b3d..a37dd475886ee7 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -605,7 +605,6 @@ static int dcp_platform_probe(struct platform_device *pdev) enum dcp_firmware_version fw_compat; struct device *dev = &pdev->dev; struct apple_dcp *dcp; - int ret; fw_compat = dcp_check_firmware_version(dev); if (fw_compat == DCP_FIRMWARE_UNKNOWN) @@ -620,12 +619,6 @@ static int dcp_platform_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dcp); - ret = devm_of_platform_populate(dev); - if (ret) { - dev_err(dev, "failed to populate child devices: %d\n", ret); - return ret; - } - return component_add(&pdev->dev, &dcp_comp_ops); } From e04a3d2e4ffbc308dd1af11df7442d3cd74f6e3f Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 30 Apr 2023 16:26:20 +0200 Subject: [PATCH 0540/1020] drm: apple: iomfb: implement abort_swaps_dcp To match macOS behavior and in the hope to fix dcpext crashes on t8112. Crashes still occur but let's keep this. Shouldn;t make a difference since we're on the swaps to finish. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.h | 20 ++++++++++++++++ drivers/gpu/drm/apple/iomfb_template.c | 32 ++++++++++++++++++++++---- drivers/gpu/drm/apple/iomfb_v12_3.c | 1 + drivers/gpu/drm/apple/iomfb_v13_3.c | 1 + 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index e8d7fef09f9f86..5d54a59c7ced45 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -189,6 +189,7 @@ enum dcpep_method { iomfbep_a358_vi_set_temperature_hint, iomfbep_get_color_remap_mode, iomfbep_last_client_close, + iomfbep_abort_swaps_dcp, iomfbep_set_matrix, dcpep_num_methods }; @@ -380,6 +381,25 @@ struct iomfb_last_client_close_resp { u32 unkint; } __packed; +struct io_user_client { + u64 addr; + u32 unk; + u8 flag1; + u8 flag2; + u8 pad[2]; +} __packed; + +struct iomfb_abort_swaps_dcp_req { + struct io_user_client client; + u8 client_null; + u8 pad[3]; +} __packed; + +struct iomfb_abort_swaps_dcp_resp { + struct io_user_client client; + u32 ret; +} __packed; + struct iomfb_set_matrix_req { u32 unk_u32; // maybe length? u64 r[3]; diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 63eec02c636704..1a21fe41ff84c8 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -59,6 +59,7 @@ DCP_THUNK_OUT(iomfb_a358_vi_set_temperature_hint, iomfbep_a358_vi_set_temperatur IOMFB_THUNK_INOUT(set_matrix); IOMFB_THUNK_INOUT(get_color_remap_mode); IOMFB_THUNK_INOUT(last_client_close); +IOMFB_THUNK_INOUT(abort_swaps_dcp); DCP_THUNK_INOUT(dcp_swap_submit, dcpep_swap_submit, struct DCP_FW_NAME(dcp_swap_submit_req), @@ -859,10 +860,21 @@ static void last_client_closed_poff(struct apple_dcp *dcp, void *out, void *cook cookie); } +static void aborted_swaps_dcp_poff(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct iomfb_last_client_close_req last_client_req = {}; + iomfb_last_client_close(dcp, false, &last_client_req, + last_client_closed_poff, cookie); +} + void DCP_FW_NAME(iomfb_poweroff)(struct apple_dcp *dcp) { int ret, swap_id; - struct iomfb_last_client_close_req last_client_req = {}; + struct iomfb_abort_swaps_dcp_req abort_req = { + .client = { + .flag2 = 1, + }, + }; struct dcp_swap_cookie *cookie; struct dcp_wait_cookie *poff_cookie; struct dcp_swap_start_req swap_req = { 0 }; @@ -927,8 +939,8 @@ void DCP_FW_NAME(iomfb_poweroff)(struct apple_dcp *dcp) /* increase refcount to ensure the receiver has a reference */ kref_get(&poff_cookie->refcount); - iomfb_last_client_close(dcp, false, &last_client_req, - last_client_closed_poff, poff_cookie); + iomfb_abort_swaps_dcp(dcp, false, &abort_req, + aborted_swaps_dcp_poff, poff_cookie); ret = wait_for_completion_timeout(&poff_cookie->done, msecs_to_jiffies(1000)); @@ -953,10 +965,20 @@ static void last_client_closed_sleep(struct apple_dcp *dcp, void *out, void *coo dcp_set_power_state(dcp, false, &power_req, complete_set_powerstate, cookie); } +static void aborted_swaps_dcp_sleep(struct apple_dcp *dcp, void *out, void *cookie) +{ + struct iomfb_last_client_close_req req = { 0 }; + iomfb_last_client_close(dcp, false, &req, last_client_closed_sleep, cookie); +} + void DCP_FW_NAME(iomfb_sleep)(struct apple_dcp *dcp) { int ret; - struct iomfb_last_client_close_req req = {}; + struct iomfb_abort_swaps_dcp_req req = { + .client = { + .flag2 = 1, + }, + }; struct dcp_wait_cookie *cookie; @@ -968,7 +990,7 @@ void DCP_FW_NAME(iomfb_sleep)(struct apple_dcp *dcp) /* increase refcount to ensure the receiver has a reference */ kref_get(&cookie->refcount); - iomfb_last_client_close(dcp, false, &req, last_client_closed_sleep, + iomfb_abort_swaps_dcp(dcp, false, &req, aborted_swaps_dcp_sleep, cookie); ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(1000)); diff --git a/drivers/gpu/drm/apple/iomfb_v12_3.c b/drivers/gpu/drm/apple/iomfb_v12_3.c index 5bc8bc2f8bd290..abcd1e4aab3ff8 100644 --- a/drivers/gpu/drm/apple/iomfb_v12_3.c +++ b/drivers/gpu/drm/apple/iomfb_v12_3.c @@ -27,6 +27,7 @@ static const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { IOMFB_METHOD("A455", iomfbep_last_client_close), IOMFB_METHOD("A460", dcpep_set_display_refresh_properties), IOMFB_METHOD("A463", dcpep_flush_supports_power), + IOMFB_METHOD("A464", iomfbep_abort_swaps_dcp), IOMFB_METHOD("A468", dcpep_set_power_state), }; diff --git a/drivers/gpu/drm/apple/iomfb_v13_3.c b/drivers/gpu/drm/apple/iomfb_v13_3.c index 8e45fca918c320..9c692ba3c81b92 100644 --- a/drivers/gpu/drm/apple/iomfb_v13_3.c +++ b/drivers/gpu/drm/apple/iomfb_v13_3.c @@ -27,6 +27,7 @@ static const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { IOMFB_METHOD("A457", iomfbep_last_client_close), IOMFB_METHOD("A463", dcpep_set_display_refresh_properties), IOMFB_METHOD("A466", dcpep_flush_supports_power), + IOMFB_METHOD("A467", iomfbep_abort_swaps_dcp), IOMFB_METHOD("A472", dcpep_set_power_state), }; From d3ec8198e3ec6b18de6fb489f7b984cd8d14c145 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 6 Nov 2023 23:05:41 +0100 Subject: [PATCH 0541/1020] drm: apple: iomfb: Increase modeset tiemout to 8.5 seconds DCP itself uses with the 13.5 firmware a timeout of 8 seconds for modesets. Using a longer timeout prevents overlapping calls to dcp and might improve reliabilty with slower displays. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb_template.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 1a21fe41ff84c8..ea9ed8f967ebad 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -1330,9 +1330,14 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru dcp_set_digital_out_mode(dcp, false, &dcp->mode, complete_set_digital_out_mode, cookie); + /* + * The DCP firmware has an internal timeout of ~8 seconds for + * modesets. Add an extra 500ms to safe side that the modeset + * call has returned. + */ dev_dbg(dcp->dev, "%s - wait for modeset", __func__); ret = wait_for_completion_timeout(&cookie->done, - msecs_to_jiffies(2500)); + msecs_to_jiffies(8500)); kref_put(&cookie->refcount, release_wait_cookie); From d7bf4913cd63beeebd0191c60d45c4c6affa8a24 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 6 Nov 2023 21:13:29 +0100 Subject: [PATCH 0542/1020] drm: apple: Remove explicit asc-dram-mask handling This is no longer necessary after introducing "apple,dma-range" for the dart driver. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp-internal.h | 3 --- drivers/gpu/drm/apple/dcp.c | 14 ++------------ drivers/gpu/drm/apple/iomfb.c | 1 - 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 7d54d28913f331..76c0cf5fae31f4 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -108,9 +108,6 @@ struct apple_dcp { /* Coprocessor control register */ void __iomem *coproc_reg; - /* mask for DCP IO virtual addresses shared over rtkit */ - u64 asc_dram_mask; - /* DCP has crashed */ bool crashed; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index a37dd475886ee7..90a738aa9c046b 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -145,8 +145,7 @@ static int dcp_rtk_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr) return -ENOMEM; // TODO: get map from device-tree - phy_addr = iommu_iova_to_phys(domain, - bfr->iova & ~dcp->asc_dram_mask); + phy_addr = iommu_iova_to_phys(domain, bfr->iova); if (!phy_addr) return -ENOMEM; @@ -166,8 +165,6 @@ static int dcp_rtk_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr) if (!bfr->buffer) return -ENOMEM; - bfr->iova |= dcp->asc_dram_mask; - dev_info(dcp->dev, "shmem_setup: iova: %lx, buffer: %lx", (uintptr_t)bfr->iova, (uintptr_t)bfr->buffer); } @@ -182,8 +179,7 @@ static void dcp_rtk_shmem_destroy(void *cookie, struct apple_rtkit_shmem *bfr) if (bfr->is_mapped) memunmap(bfr->buffer); else - dma_free_coherent(dcp->dev, bfr->size, bfr->buffer, - bfr->iova & ~dcp->asc_dram_mask); + dma_free_coherent(dcp->dev, bfr->size, bfr->buffer, bfr->iova); } static struct apple_rtkit_ops rtkit_ops = { @@ -540,12 +536,6 @@ static int dcp_comp_bind(struct device *dev, struct device *main, void *data) return dev_err_probe(dev, PTR_ERR(dcp->clk), "Unable to find clock\n"); - ret = of_property_read_u64(dev->of_node, "apple,asc-dram-mask", - &dcp->asc_dram_mask); - if (ret) - dev_warn(dev, "failed read 'apple,asc-dram-mask': %d\n", ret); - dev_dbg(dev, "'apple,asc-dram-mask': 0x%011llx\n", dcp->asc_dram_mask); - bitmap_zero(dcp->memdesc_map, DCP_MAX_MAPPINGS); // TDOD: mem_desc IDs start at 1, for simplicity just skip '0' entry set_bit(0, dcp->memdesc_map); diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 2dd3987c6e36b4..642f178637241a 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -557,7 +557,6 @@ int iomfb_start_rtkit(struct apple_dcp *dcp) dcp->shmem = dma_alloc_coherent(dcp->dev, DCP_SHMEM_SIZE, &shmem_iova, GFP_KERNEL); - shmem_iova |= dcp->asc_dram_mask; dcp_send_message(dcp, IOMFB_ENDPOINT, dcpep_set_shmem(shmem_iova)); return 0; From 98a72095b3a96d41a952c77f4b900faf1f60b13c Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Sat, 5 Nov 2022 13:15:33 +0100 Subject: [PATCH 0543/1020] mux: apple DP xbar: Add Apple silicon DisplayPort crossbar This drivers adds support for the display crossbar used to route display controller streams to the three different modes (DP AltMode, USB4 Tunnel #0/#1) of the Type-C ports. Signed-off-by: Sven Peter --- drivers/mux/Kconfig | 13 ++ drivers/mux/Makefile | 2 + drivers/mux/apple-display-crossbar.c | 305 +++++++++++++++++++++++++++ 3 files changed, 320 insertions(+) create mode 100644 drivers/mux/apple-display-crossbar.c diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig index 80f015cf6e54f6..c0f62ae4c8047f 100644 --- a/drivers/mux/Kconfig +++ b/drivers/mux/Kconfig @@ -31,6 +31,19 @@ config MUX_ADGS1408 To compile the driver as a module, choose M here: the module will be called mux-adgs1408. +config MUX_APPLE_DPXBAR + tristate "Apple Silicon Display Crossbar" + depends on ARCH_APPLE + help + Apple Silicon Display Crossbar multiplexer. + + This drivers adds support for the display crossbar used to route + display controller streams to the three different modes + (DP AltMode, USB4 Tunnel #0/#1) of the Type-C ports. + + To compile this driver as a module, chose M here: the module will be + called mux-apple-display-crossbar. + config MUX_GPIO tristate "GPIO-controlled Multiplexer" depends on GPIOLIB || COMPILE_TEST diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile index 6e9fa47daf5663..7b5b3325068010 100644 --- a/drivers/mux/Makefile +++ b/drivers/mux/Makefile @@ -8,9 +8,11 @@ mux-adg792a-objs := adg792a.o mux-adgs1408-objs := adgs1408.o mux-gpio-objs := gpio.o mux-mmio-objs := mmio.o +mux-apple-display-crossbar-objs := apple-display-crossbar.o obj-$(CONFIG_MULTIPLEXER) += mux-core.o obj-$(CONFIG_MUX_ADG792A) += mux-adg792a.o obj-$(CONFIG_MUX_ADGS1408) += mux-adgs1408.o +obj-$(CONFIG_MUX_APPLE_DPXBAR) += mux-apple-display-crossbar.o obj-$(CONFIG_MUX_GPIO) += mux-gpio.o obj-$(CONFIG_MUX_MMIO) += mux-mmio.o diff --git a/drivers/mux/apple-display-crossbar.c b/drivers/mux/apple-display-crossbar.c new file mode 100644 index 00000000000000..1c8470b518c7f1 --- /dev/null +++ b/drivers/mux/apple-display-crossbar.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple Silicon Display Crossbar multiplexer driver + * + * Copyright (C) Asahi Linux Contributors + * + * Author: Sven Peter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FIFO_WR_DPTX_CLK_EN 0x000 +#define FIFO_WR_N_CLK_EN 0x004 +#define FIFO_WR_UNK_EN 0x008 +#define FIFO_RD_PCLK1_EN 0x020 +#define FIFO_RD_PCLK2_EN 0x024 +#define FIFO_RD_N_CLK_EN 0x028 +#define FIFO_RD_UNK_EN 0x02c + +#define OUT_PCLK1_EN 0x040 +#define OUT_PCLK2_EN 0x044 +#define OUT_N_CLK_EN 0x048 +#define OUT_UNK_EN 0x04c + +#define CROSSBAR_DISPEXT_EN 0x050 +#define CROSSBAR_MUX_CTRL 0x060 +#define CROSSBAR_MUX_CTRL_DPPHY_SELECT0 GENMASK(23, 20) +#define CROSSBAR_MUX_CTRL_DPIN1_SELECT0 GENMASK(19, 16) +#define CROSSBAR_MUX_CTRL_DPIN0_SELECT0 GENMASK(15, 12) +#define CROSSBAR_MUX_CTRL_DPPHY_SELECT1 GENMASK(11, 8) +#define CROSSBAR_MUX_CTRL_DPIN1_SELECT1 GENMASK(7, 4) +#define CROSSBAR_MUX_CTRL_DPIN0_SELECT1 GENMASK(3, 0) +#define CROSSBAR_ATC_EN 0x070 + +#define FIFO_WR_DPTX_CLK_EN_STAT 0x800 +#define FIFO_WR_N_CLK_EN_STAT 0x804 +#define FIFO_RD_PCLK1_EN_STAT 0x820 +#define FIFO_RD_PCLK2_EN_STAT 0x824 +#define FIFO_RD_N_CLK_EN_STAT 0x828 + +#define OUT_PCLK1_EN_STAT 0x840 +#define OUT_PCLK2_EN_STAT 0x844 +#define OUT_N_CLK_EN_STAT 0x848 + +#define UNK_TUNABLE 0xc00 + +#define ATC_DPIN0 BIT(0) +#define ATC_DPIN1 BIT(4) +#define ATC_DPPHY BIT(8) + +enum { MUX_DPPHY = 0, MUX_DPIN0 = 1, MUX_DPIN1 = 2, MUX_MAX = 3 }; +static const char *apple_dpxbar_names[MUX_MAX] = { "dpphy", "dpin0", "dpin1" }; + +struct apple_dpxbar_hw { + unsigned int n_ufp; + u32 tunable; +}; + +struct apple_dpxbar { + struct device *dev; + void __iomem *regs; + int selected_dispext[MUX_MAX]; + spinlock_t lock; +}; + +static inline void dpxbar_mask32(struct apple_dpxbar *xbar, u32 reg, u32 mask, + u32 set) +{ + u32 value = readl(xbar->regs + reg); + value &= ~mask; + value |= set; + writel(value, xbar->regs + reg); +} + +static inline void dpxbar_set32(struct apple_dpxbar *xbar, u32 reg, u32 set) +{ + dpxbar_mask32(xbar, reg, 0, set); +} + +static inline void dpxbar_clear32(struct apple_dpxbar *xbar, u32 reg, u32 clear) +{ + dpxbar_mask32(xbar, reg, clear, 0); +} + +static int apple_dpxbar_set(struct mux_control *mux, int state) +{ + struct apple_dpxbar *dpxbar = mux_chip_priv(mux->chip); + unsigned int index = mux_control_get_index(mux); + unsigned long flags; + unsigned int mux_state; + unsigned int dispext_bit; + unsigned int atc_bit; + bool enable; + int ret = 0; + u32 mux_mask, mux_set; + + if (state == MUX_IDLE_DISCONNECT) { + /* + * Technically this will select dispext0,0 in the mux control + * register. Practically that doesn't matter since everything + * else is disabled. + */ + mux_state = 0; + enable = false; + } else if (state >= 0 && state < 9) { + dispext_bit = 1 << state; + mux_state = state; + enable = true; + } else { + return -EINVAL; + } + + switch (index) { + case MUX_DPPHY: + mux_mask = CROSSBAR_MUX_CTRL_DPPHY_SELECT0 | + CROSSBAR_MUX_CTRL_DPPHY_SELECT1; + mux_set = + FIELD_PREP(CROSSBAR_MUX_CTRL_DPPHY_SELECT0, mux_state) | + FIELD_PREP(CROSSBAR_MUX_CTRL_DPPHY_SELECT1, mux_state); + atc_bit = ATC_DPPHY; + break; + case MUX_DPIN0: + mux_mask = CROSSBAR_MUX_CTRL_DPIN0_SELECT0 | + CROSSBAR_MUX_CTRL_DPIN0_SELECT1; + mux_set = + FIELD_PREP(CROSSBAR_MUX_CTRL_DPIN0_SELECT0, mux_state) | + FIELD_PREP(CROSSBAR_MUX_CTRL_DPIN0_SELECT1, mux_state); + atc_bit = ATC_DPIN0; + break; + case MUX_DPIN1: + mux_mask = CROSSBAR_MUX_CTRL_DPIN1_SELECT0 | + CROSSBAR_MUX_CTRL_DPIN1_SELECT1; + mux_set = + FIELD_PREP(CROSSBAR_MUX_CTRL_DPIN1_SELECT0, mux_state) | + FIELD_PREP(CROSSBAR_MUX_CTRL_DPIN1_SELECT1, mux_state); + atc_bit = ATC_DPIN1; + break; + default: + return -EINVAL; + } + + spin_lock_irqsave(&dpxbar->lock, flags); + + /* ensure the selected dispext isn't already used in this crossbar */ + if (enable) { + for (int i = 0; i < MUX_MAX; ++i) { + if (i == index) + continue; + if (dpxbar->selected_dispext[i] == state) { + spin_unlock_irqrestore(&dpxbar->lock, flags); + return -EBUSY; + } + } + } + + dpxbar_set32(dpxbar, OUT_N_CLK_EN, atc_bit); + dpxbar_clear32(dpxbar, OUT_UNK_EN, atc_bit); + dpxbar_clear32(dpxbar, OUT_PCLK1_EN, atc_bit); + dpxbar_clear32(dpxbar, CROSSBAR_ATC_EN, atc_bit); + + if (dpxbar->selected_dispext[index] >= 0) { + u32 prev_dispext_bit = 1 << dpxbar->selected_dispext[index]; + + dpxbar_set32(dpxbar, FIFO_WR_N_CLK_EN, prev_dispext_bit); + dpxbar_set32(dpxbar, FIFO_RD_N_CLK_EN, prev_dispext_bit); + dpxbar_clear32(dpxbar, FIFO_WR_UNK_EN, prev_dispext_bit); + dpxbar_clear32(dpxbar, FIFO_RD_UNK_EN, prev_dispext_bit); + dpxbar_clear32(dpxbar, FIFO_WR_DPTX_CLK_EN, prev_dispext_bit); + dpxbar_clear32(dpxbar, FIFO_RD_PCLK1_EN, prev_dispext_bit); + dpxbar_clear32(dpxbar, CROSSBAR_DISPEXT_EN, prev_dispext_bit); + + dpxbar->selected_dispext[index] = -1; + } + + dpxbar_mask32(dpxbar, CROSSBAR_MUX_CTRL, mux_mask, mux_set); + + if (enable) { + dpxbar_clear32(dpxbar, FIFO_WR_N_CLK_EN, dispext_bit); + dpxbar_clear32(dpxbar, FIFO_RD_N_CLK_EN, dispext_bit); + dpxbar_clear32(dpxbar, OUT_N_CLK_EN, atc_bit); + dpxbar_set32(dpxbar, FIFO_WR_UNK_EN, dispext_bit); + dpxbar_set32(dpxbar, FIFO_RD_UNK_EN, dispext_bit); + dpxbar_set32(dpxbar, OUT_UNK_EN, atc_bit); + dpxbar_set32(dpxbar, FIFO_WR_DPTX_CLK_EN, dispext_bit); + dpxbar_set32(dpxbar, FIFO_RD_PCLK1_EN, dispext_bit); + dpxbar_set32(dpxbar, OUT_PCLK1_EN, atc_bit); + dpxbar_set32(dpxbar, CROSSBAR_ATC_EN, atc_bit); + dpxbar_set32(dpxbar, CROSSBAR_DISPEXT_EN, dispext_bit); + + /* + * Work around some HW quirk: + * Without toggling the RD_PCLK enable here the connection + * doesn't come up. Testing has shown that a delay of about + * 5 usec is required which is doubled here to be on the + * safe side. + */ + dpxbar_clear32(dpxbar, FIFO_RD_PCLK1_EN, dispext_bit); + udelay(10); + dpxbar_set32(dpxbar, FIFO_RD_PCLK1_EN, dispext_bit); + + dpxbar->selected_dispext[index] = state; + } + + spin_unlock_irqrestore(&dpxbar->lock, flags); + + if (enable) + dev_err(dpxbar->dev, "Switched %s to dispext%u,%u\n", + apple_dpxbar_names[index], mux_state >> 1, + mux_state & 1); + else + dev_err(dpxbar->dev, "Switched %s to disconnected state\n", + apple_dpxbar_names[index]); + + return ret; +} + +static const struct mux_control_ops apple_dpxbar_ops = { + .set = apple_dpxbar_set, +}; + +static int apple_dpxbar_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mux_chip *mux_chip; + struct apple_dpxbar *dpxbar; + const struct apple_dpxbar_hw *hw; + int ret; + + hw = of_device_get_match_data(dev); + mux_chip = devm_mux_chip_alloc(dev, MUX_MAX, sizeof(*dpxbar)); + if (IS_ERR(mux_chip)) + return PTR_ERR(mux_chip); + + dpxbar = mux_chip_priv(mux_chip); + mux_chip->ops = &apple_dpxbar_ops; + spin_lock_init(&dpxbar->lock); + + dpxbar->dev = dev; + dpxbar->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dpxbar->regs)) + return PTR_ERR(dpxbar->regs); + + writel(hw->tunable, dpxbar->regs + UNK_TUNABLE); + + for (unsigned int i = 0; i < MUX_MAX; ++i) { + mux_chip->mux[i].states = hw->n_ufp; + mux_chip->mux[i].idle_state = MUX_IDLE_DISCONNECT; + dpxbar->selected_dispext[i] = -1; + } + + ret = devm_mux_chip_register(dev, mux_chip); + if (ret < 0) + return ret; + + return 0; +} + +const static struct apple_dpxbar_hw apple_dpxbar_hw_t8103 = { + .n_ufp = 2, + .tunable = 0, +}; + +const static struct apple_dpxbar_hw apple_dpxbar_hw_t6000 = { + .n_ufp = 9, + .tunable = 5, +}; + +static const struct of_device_id apple_dpxbar_ids[] = { + { + .compatible = "apple,t8103-display-crossbar", + .data = &apple_dpxbar_hw_t8103, + }, + { + .compatible = "apple,t8112-display-crossbar", + .data = &apple_dpxbar_hw_t8103, + }, + { + .compatible = "apple,t6000-display-crossbar", + .data = &apple_dpxbar_hw_t6000, + }, + {} +}; +MODULE_DEVICE_TABLE(of, apple_dpxbar_ids); + +static struct platform_driver apple_dpxbar_driver = { + .driver = { + .name = "apple-display-crossbar", + .of_match_table = apple_dpxbar_ids, + }, + .probe = apple_dpxbar_probe, +}; +module_platform_driver(apple_dpxbar_driver); + +MODULE_DESCRIPTION("Apple Silicon display crossbar multiplexer driver"); +MODULE_AUTHOR("Sven Peter "); +MODULE_LICENSE("GPL v2"); From 8a2fe095d07b14e7b1f9340f20cca0bd2a9a455c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 30 Apr 2023 15:04:35 +0200 Subject: [PATCH 0544/1020] mux: apple dp crossbar: Support t8112 varient Signed-off-by: Janne Grunau --- drivers/mux/apple-display-crossbar.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/mux/apple-display-crossbar.c b/drivers/mux/apple-display-crossbar.c index 1c8470b518c7f1..065e1a4669450e 100644 --- a/drivers/mux/apple-display-crossbar.c +++ b/drivers/mux/apple-display-crossbar.c @@ -269,6 +269,11 @@ const static struct apple_dpxbar_hw apple_dpxbar_hw_t8103 = { .tunable = 0, }; +const static struct apple_dpxbar_hw apple_dpxbar_hw_t8112 = { + .n_ufp = 4, + .tunable = 4278196325, +}; + const static struct apple_dpxbar_hw apple_dpxbar_hw_t6000 = { .n_ufp = 9, .tunable = 5, @@ -281,7 +286,7 @@ static const struct of_device_id apple_dpxbar_ids[] = { }, { .compatible = "apple,t8112-display-crossbar", - .data = &apple_dpxbar_hw_t8103, + .data = &apple_dpxbar_hw_t8112, }, { .compatible = "apple,t6000-display-crossbar", From f9ce597dd05c31409d53b3bb35d6932766917092 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 30 Apr 2023 15:05:36 +0200 Subject: [PATCH 0545/1020] mux: apple dp crossbar: FIFO_RD_UNK_EN seems to use 2 bits per dispext* Signed-off-by: Janne Grunau --- drivers/mux/apple-display-crossbar.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/mux/apple-display-crossbar.c b/drivers/mux/apple-display-crossbar.c index 065e1a4669450e..75cc7f0193f51e 100644 --- a/drivers/mux/apple-display-crossbar.c +++ b/drivers/mux/apple-display-crossbar.c @@ -98,6 +98,7 @@ static int apple_dpxbar_set(struct mux_control *mux, int state) unsigned long flags; unsigned int mux_state; unsigned int dispext_bit; + unsigned int dispext_bit_en; unsigned int atc_bit; bool enable; int ret = 0; @@ -113,6 +114,7 @@ static int apple_dpxbar_set(struct mux_control *mux, int state) enable = false; } else if (state >= 0 && state < 9) { dispext_bit = 1 << state; + dispext_bit_en = 1 << (2 * state); mux_state = state; enable = true; } else { @@ -169,11 +171,12 @@ static int apple_dpxbar_set(struct mux_control *mux, int state) if (dpxbar->selected_dispext[index] >= 0) { u32 prev_dispext_bit = 1 << dpxbar->selected_dispext[index]; + u32 prev_dispext_bit_en = 1 << (2 * dpxbar->selected_dispext[index]); dpxbar_set32(dpxbar, FIFO_WR_N_CLK_EN, prev_dispext_bit); dpxbar_set32(dpxbar, FIFO_RD_N_CLK_EN, prev_dispext_bit); dpxbar_clear32(dpxbar, FIFO_WR_UNK_EN, prev_dispext_bit); - dpxbar_clear32(dpxbar, FIFO_RD_UNK_EN, prev_dispext_bit); + dpxbar_clear32(dpxbar, FIFO_RD_UNK_EN, prev_dispext_bit_en); dpxbar_clear32(dpxbar, FIFO_WR_DPTX_CLK_EN, prev_dispext_bit); dpxbar_clear32(dpxbar, FIFO_RD_PCLK1_EN, prev_dispext_bit); dpxbar_clear32(dpxbar, CROSSBAR_DISPEXT_EN, prev_dispext_bit); @@ -188,7 +191,7 @@ static int apple_dpxbar_set(struct mux_control *mux, int state) dpxbar_clear32(dpxbar, FIFO_RD_N_CLK_EN, dispext_bit); dpxbar_clear32(dpxbar, OUT_N_CLK_EN, atc_bit); dpxbar_set32(dpxbar, FIFO_WR_UNK_EN, dispext_bit); - dpxbar_set32(dpxbar, FIFO_RD_UNK_EN, dispext_bit); + dpxbar_set32(dpxbar, FIFO_RD_UNK_EN, dispext_bit_en); dpxbar_set32(dpxbar, OUT_UNK_EN, atc_bit); dpxbar_set32(dpxbar, FIFO_WR_DPTX_CLK_EN, dispext_bit); dpxbar_set32(dpxbar, FIFO_RD_PCLK1_EN, dispext_bit); From 765508d74ba4c2b30aa7be327bc6c95bb4a2a522 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 30 Apr 2023 15:26:44 +0200 Subject: [PATCH 0546/1020] mux: apple dp crossbar: Read UNK_TUNABLE before and after writing it Makes traces easier to compare with macOS. Signed-off-by: Janne Grunau --- drivers/mux/apple-display-crossbar.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mux/apple-display-crossbar.c b/drivers/mux/apple-display-crossbar.c index 75cc7f0193f51e..1c40a4dfff83d4 100644 --- a/drivers/mux/apple-display-crossbar.c +++ b/drivers/mux/apple-display-crossbar.c @@ -252,7 +252,9 @@ static int apple_dpxbar_probe(struct platform_device *pdev) if (IS_ERR(dpxbar->regs)) return PTR_ERR(dpxbar->regs); + readl(dpxbar->regs + UNK_TUNABLE); writel(hw->tunable, dpxbar->regs + UNK_TUNABLE); + readl(dpxbar->regs + UNK_TUNABLE); for (unsigned int i = 0; i < MUX_MAX; ++i) { mux_chip->mux[i].states = hw->n_ufp; From 5f8d1ace65dd2ba8bc8b87c376a97711a7d05363 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 17 Aug 2023 23:00:08 +0200 Subject: [PATCH 0547/1020] mux: apple dp crossbar: Support t602x DP cross bar variant This is a simplified version and probably should live in a separate file. Even the shared registers are quite different. Signed-off-by: Janne Grunau --- drivers/mux/apple-display-crossbar.c | 159 ++++++++++++++++++++++++++- 1 file changed, 155 insertions(+), 4 deletions(-) diff --git a/drivers/mux/apple-display-crossbar.c b/drivers/mux/apple-display-crossbar.c index 1c40a4dfff83d4..819c1333c85c6c 100644 --- a/drivers/mux/apple-display-crossbar.c +++ b/drivers/mux/apple-display-crossbar.c @@ -18,6 +18,32 @@ #include #include +/** + * T602x register interface is cleary different so most of the nemes below are + * probly wrong. + */ + +#define T602X_FIFO_WR_DPTX_CLK_EN 0x000 +#define T602X_FIFO_WR_N_CLK_EN 0x004 +#define T602X_FIFO_WR_UNK_EN 0x008 +#define T602X_REG_00C 0x00c +#define T602X_REG_014 0x014 +#define T602X_REG_018 0x018 +#define T602X_REG_01C 0x01c +#define T602X_FIFO_RD_PCLK2_EN 0x024 +#define T602X_FIFO_RD_N_CLK_EN 0x028 +#define T602X_FIFO_RD_UNK_EN 0x02c +#define T602X_REG_030 0x030 +#define T602X_REG_034 0x034 + +#define T602X_REG_804_STAT 0x804 // status of 0x004 +#define T602X_REG_810_STAT 0x810 // status of 0x014 +#define T602X_REG_81C_STAT 0x81c // status of 0x024 + +/** + * T8013, T600x, T8112 dp crossbar registers. + */ + #define FIFO_WR_DPTX_CLK_EN 0x000 #define FIFO_WR_N_CLK_EN 0x004 #define FIFO_WR_UNK_EN 0x008 @@ -63,6 +89,7 @@ static const char *apple_dpxbar_names[MUX_MAX] = { "dpphy", "dpin0", "dpin1" }; struct apple_dpxbar_hw { unsigned int n_ufp; u32 tunable; + const struct mux_control_ops *ops; }; struct apple_dpxbar { @@ -91,6 +118,112 @@ static inline void dpxbar_clear32(struct apple_dpxbar *xbar, u32 reg, u32 clear) dpxbar_mask32(xbar, reg, clear, 0); } +static int apple_dpxbar_set_t602x(struct mux_control *mux, int state) +{ + struct apple_dpxbar *dpxbar = mux_chip_priv(mux->chip); + unsigned int index = mux_control_get_index(mux); + unsigned long flags; + unsigned int mux_state; + unsigned int dispext_bit; + unsigned int dispext_bit_en; + unsigned int dispext_bit_4; + bool enable; + int ret = 0; + + if (state == MUX_IDLE_DISCONNECT) { + /* + * Technically this will select dispext0,0 in the mux control + * register. Practically that doesn't matter since everything + * else is disabled. + */ + mux_state = 0; + enable = false; + } else if (state >= 0 && state < 9) { + dispext_bit = 1 << state; + dispext_bit_en = 1 << (2 * state); + dispext_bit_4 = 1 << (4 * state); + mux_state = state; + enable = true; + } else { + return -EINVAL; + } + + spin_lock_irqsave(&dpxbar->lock, flags); + + /* ensure the selected dispext isn't already used in this crossbar */ + if (enable) { + for (int i = 0; i < MUX_MAX; ++i) { + if (i == index) + continue; + if (dpxbar->selected_dispext[i] == state) { + spin_unlock_irqrestore(&dpxbar->lock, flags); + return -EBUSY; + } + } + } + + if (dpxbar->selected_dispext[index] >= 0) { + u32 prev_dispext_bit = 1 << dpxbar->selected_dispext[index]; + u32 prev_dispext_bit_en = 1 << (2 * dpxbar->selected_dispext[index]); + u32 prev_dispext_bit_4 = 1 << (4 * dpxbar->selected_dispext[index]); + + dpxbar_clear32(dpxbar, T602X_FIFO_RD_UNK_EN, prev_dispext_bit_en); + dpxbar_clear32(dpxbar, T602X_FIFO_WR_DPTX_CLK_EN, prev_dispext_bit); + dpxbar_clear32(dpxbar, T602X_REG_00C, prev_dispext_bit_en); + + dpxbar_clear32(dpxbar, T602X_REG_01C, 0x100); + + dpxbar_clear32(dpxbar, T602X_FIFO_WR_UNK_EN, prev_dispext_bit_en); + dpxbar_clear32(dpxbar, T602X_REG_018, prev_dispext_bit_4); + + dpxbar_clear32(dpxbar, T602X_FIFO_RD_N_CLK_EN, 0x100); + + dpxbar_set32(dpxbar, T602X_FIFO_WR_N_CLK_EN, prev_dispext_bit_en); + dpxbar_set32(dpxbar, T602X_REG_014, prev_dispext_bit_en); + + dpxbar_set32(dpxbar, FIFO_RD_PCLK1_EN, 0x100); + + dpxbar->selected_dispext[index] = -1; + } + + if (enable) { + dpxbar_set32(dpxbar, T602X_REG_030, state << 20); + dpxbar_set32(dpxbar, T602X_REG_030, state << 8); + udelay(10); + + dpxbar_clear32(dpxbar, T602X_FIFO_WR_N_CLK_EN, dispext_bit); + dpxbar_clear32(dpxbar, T602X_REG_014, dispext_bit_en); + + dpxbar_clear32(dpxbar, T602X_FIFO_RD_PCLK2_EN, 0x100); + + dpxbar_set32(dpxbar, T602X_FIFO_WR_UNK_EN, dispext_bit); + dpxbar_set32(dpxbar, T602X_REG_018, dispext_bit_4); + + dpxbar_set32(dpxbar, T602X_FIFO_RD_N_CLK_EN, 0x100); + dpxbar_set32(dpxbar, T602X_FIFO_WR_DPTX_CLK_EN, dispext_bit); + dpxbar_set32(dpxbar, T602X_REG_00C, dispext_bit_en); + + dpxbar_set32(dpxbar, T602X_REG_01C, 0x100); + dpxbar_set32(dpxbar, T602X_REG_034, 0x100); + + dpxbar_set32(dpxbar, T602X_FIFO_RD_UNK_EN, dispext_bit_en); + + dpxbar->selected_dispext[index] = state; + } + + spin_unlock_irqrestore(&dpxbar->lock, flags); + + if (enable) + dev_err(dpxbar->dev, "Switched %s to dispext%u,%u\n", + apple_dpxbar_names[index], mux_state >> 1, + mux_state & 1); + else + dev_err(dpxbar->dev, "Switched %s to disconnected state\n", + apple_dpxbar_names[index]); + + return ret; +} + static int apple_dpxbar_set(struct mux_control *mux, int state) { struct apple_dpxbar *dpxbar = mux_chip_priv(mux->chip); @@ -230,6 +363,10 @@ static const struct mux_control_ops apple_dpxbar_ops = { .set = apple_dpxbar_set, }; +static const struct mux_control_ops apple_dpxbar_t602x_ops = { + .set = apple_dpxbar_set_t602x, +}; + static int apple_dpxbar_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -244,7 +381,7 @@ static int apple_dpxbar_probe(struct platform_device *pdev) return PTR_ERR(mux_chip); dpxbar = mux_chip_priv(mux_chip); - mux_chip->ops = &apple_dpxbar_ops; + mux_chip->ops = hw->ops; spin_lock_init(&dpxbar->lock); dpxbar->dev = dev; @@ -252,9 +389,11 @@ static int apple_dpxbar_probe(struct platform_device *pdev) if (IS_ERR(dpxbar->regs)) return PTR_ERR(dpxbar->regs); - readl(dpxbar->regs + UNK_TUNABLE); - writel(hw->tunable, dpxbar->regs + UNK_TUNABLE); - readl(dpxbar->regs + UNK_TUNABLE); + if (!of_device_is_compatible(dev->of_node, "apple,t6020-display-crossbar")) { + readl(dpxbar->regs + UNK_TUNABLE); + writel(hw->tunable, dpxbar->regs + UNK_TUNABLE); + readl(dpxbar->regs + UNK_TUNABLE); + } for (unsigned int i = 0; i < MUX_MAX; ++i) { mux_chip->mux[i].states = hw->n_ufp; @@ -272,16 +411,24 @@ static int apple_dpxbar_probe(struct platform_device *pdev) const static struct apple_dpxbar_hw apple_dpxbar_hw_t8103 = { .n_ufp = 2, .tunable = 0, + .ops = &apple_dpxbar_ops, }; const static struct apple_dpxbar_hw apple_dpxbar_hw_t8112 = { .n_ufp = 4, .tunable = 4278196325, + .ops = &apple_dpxbar_ops, }; const static struct apple_dpxbar_hw apple_dpxbar_hw_t6000 = { .n_ufp = 9, .tunable = 5, + .ops = &apple_dpxbar_ops, +}; + +const static struct apple_dpxbar_hw apple_dpxbar_hw_t6020 = { + .n_ufp = 9, + .ops = &apple_dpxbar_t602x_ops, }; static const struct of_device_id apple_dpxbar_ids[] = { @@ -297,6 +444,10 @@ static const struct of_device_id apple_dpxbar_ids[] = { .compatible = "apple,t6000-display-crossbar", .data = &apple_dpxbar_hw_t6000, }, + { + .compatible = "apple,t6020-display-crossbar", + .data = &apple_dpxbar_hw_t6020, + }, {} }; MODULE_DEVICE_TABLE(of, apple_dpxbar_ids); From 3c15af57642f0eb53299e9a94ed1605b16194391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 15 Feb 2023 16:20:22 +0100 Subject: [PATCH 0548/1020] gpu: drm: apple: Add utility functions for matching on dict keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- drivers/gpu/drm/apple/parser.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index 5665c2ba19682f..84a76440c6e7e2 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -117,6 +117,39 @@ static int skip(struct dcp_parse_ctx *handle) } } +static int skip_pair(struct dcp_parse_ctx *handle) +{ + int ret; + + ret = skip(handle); + if (ret) + return ret; + + return skip(handle); +} + +static bool consume_string(struct dcp_parse_ctx *ctx, const char *specimen) +{ + struct dcp_parse_tag *tag; + const char *key; + ctx->pos = round_up(ctx->pos, 4); + + if (ctx->pos + sizeof(*tag) + strlen(specimen) - 1 > ctx->len) + return false; + tag = ctx->blob + ctx->pos; + key = ctx->blob + ctx->pos + sizeof(*tag); + if (tag->padding) + return false; + + if (tag->type != DCP_TYPE_STRING || + tag->size != strlen(specimen) || + strncmp(key, specimen, tag->size)) + return false; + + skip(ctx); + return true; +} + /* Caller must free the result */ static char *parse_string(struct dcp_parse_ctx *handle) { From f16cc6ce78aadf799d57eb50255572cb11da8147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 23 Feb 2023 13:07:49 +0100 Subject: [PATCH 0549/1020] gpu: drm: apple: Add 'parse_blob' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- drivers/gpu/drm/apple/parser.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index 84a76440c6e7e2..4f16e5505c3367 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -199,6 +199,26 @@ static int parse_bool(struct dcp_parse_ctx *handle, bool *b) return 0; } +static int parse_blob(struct dcp_parse_ctx *handle, size_t size, u8 **blob) +{ + struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_BLOB); + u8 *out; + + if (IS_ERR(tag)) + return PTR_ERR(tag); + + if (tag->size < size) + return -EINVAL; + + out = parse_bytes(handle, tag->size); + + if (IS_ERR(out)) + return PTR_ERR(out); + + *blob = out; + return 0; +} + struct iterator { struct dcp_parse_ctx *handle; u32 idx, len; From 44daf5f8521b8329033c5bc9b106fc97ac0aac82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 15 Feb 2023 16:22:17 +0100 Subject: [PATCH 0550/1020] gpu: drm: apple: Add sound mode parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- drivers/gpu/drm/apple/dcp-internal.h | 2 + drivers/gpu/drm/apple/parser.c | 306 +++++++++++++++++++++++++++ drivers/gpu/drm/apple/parser.h | 20 ++ drivers/gpu/drm/apple/trace.h | 23 ++ 4 files changed, 351 insertions(+) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 76c0cf5fae31f4..c04585c3374991 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -195,4 +195,6 @@ struct apple_dcp { int dcp_backlight_register(struct apple_dcp *dcp); bool dcp_has_panel(struct apple_dcp *dcp); +#define DCP_AUDIO_MAX_CHANS 15 + #endif /* __APPLE_DCP_INTERNAL_H__ */ diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index 4f16e5505c3367..50a4fc31cd06ce 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -7,6 +7,8 @@ #include #include +#include // for sound format masks + #include "parser.h" #include "trace.h" @@ -586,3 +588,307 @@ int parse_display_attributes(struct dcp_parse_ctx *handle, int *width_mm, return 0; } + +int parse_sample_rate_bit(struct dcp_parse_ctx *handle, unsigned int *ratebit) +{ + s64 rate; + int ret = parse_int(handle, &rate); + + if (ret) + return ret; + + *ratebit = snd_pcm_rate_to_rate_bit(rate); + if (*ratebit == SNDRV_PCM_RATE_KNOT) { + /* + * The rate wasn't recognized, and unless we supply + * a supplementary constraint, the SNDRV_PCM_RATE_KNOT bit + * will allow any rate. So clear it. + */ + *ratebit = 0; + } + + return 0; +} + +int parse_sample_fmtbit(struct dcp_parse_ctx *handle, u64 *fmtbit) +{ + s64 sample_size; + int ret = parse_int(handle, &sample_size); + + if (ret) + return ret; + + switch (sample_size) { + case 16: + *fmtbit = SNDRV_PCM_FMTBIT_S16; + break; + case 20: + *fmtbit = SNDRV_PCM_FMTBIT_S20; + break; + case 24: + *fmtbit = SNDRV_PCM_FMTBIT_S24; + break; + case 32: + *fmtbit = SNDRV_PCM_FMTBIT_S32; + break; + default: + *fmtbit = 0; + break; + } + + return 0; +} + +static struct { + const char *label; + u8 type; +} chan_position_names[] = { + { "Front Left", SNDRV_CHMAP_FL }, + { "Front Right", SNDRV_CHMAP_FR }, + { "Rear Left", SNDRV_CHMAP_RL }, + { "Rear Right", SNDRV_CHMAP_RR }, + { "Front Center", SNDRV_CHMAP_FC }, + { "Low Frequency Effects", SNDRV_CHMAP_LFE }, + { "Rear Center", SNDRV_CHMAP_RC }, + { "Front Left Center", SNDRV_CHMAP_FLC }, + { "Front Right Center", SNDRV_CHMAP_FRC }, + { "Rear Left Center", SNDRV_CHMAP_RLC }, + { "Rear Right Center", SNDRV_CHMAP_RRC }, + { "Front Left Wide", SNDRV_CHMAP_FLW }, + { "Front Right Wide", SNDRV_CHMAP_FRW }, + { "Front Left High", SNDRV_CHMAP_FLH }, + { "Front Center High", SNDRV_CHMAP_FCH }, + { "Front Right High", SNDRV_CHMAP_FRH }, + { "Top Center", SNDRV_CHMAP_TC }, +}; + +static void append_chmap(struct snd_pcm_chmap_elem *chmap, u8 type) +{ + if (!chmap || chmap->channels >= ARRAY_SIZE(chmap->map)) + return; + + chmap->map[chmap->channels] = type; + chmap->channels++; +} + +static int parse_chmap(struct dcp_parse_ctx *handle, struct snd_pcm_chmap_elem *chmap) +{ + struct iterator it; + int i, ret; + + if (!chmap) { + skip(handle); + return 0; + } + + chmap->channels = 0; + + dcp_parse_foreach_in_array(handle, it) { + for (i = 0; i < ARRAY_SIZE(chan_position_names); i++) + if (consume_string(it.handle, chan_position_names[i].label)) + break; + + if (i == ARRAY_SIZE(chan_position_names)) { + ret = skip(it.handle); + if (ret) + return ret; + + append_chmap(chmap, SNDRV_CHMAP_UNKNOWN); + continue; + } + + append_chmap(chmap, chan_position_names[i].type); + } + + return 0; +} + +static int parse_chan_layout_element(struct dcp_parse_ctx *handle, + unsigned int *nchans_out, + struct snd_pcm_chmap_elem *chmap) +{ + struct iterator it; + int ret; + s64 nchans = 0; + + dcp_parse_foreach_in_dict(handle, it) { + if (consume_string(it.handle, "ActiveChannelCount")) + ret = parse_int(it.handle, &nchans); + else if (consume_string(it.handle, "ChannelLayout")) + ret = parse_chmap(it.handle, chmap); + else + ret = skip_pair(it.handle); + + if (ret) + return ret; + } + + if (nchans_out) + *nchans_out = nchans; + + return 0; +} + +static int parse_nchans_mask(struct dcp_parse_ctx *handle, unsigned int *mask) +{ + struct iterator it; + int ret; + + *mask = 0; + + dcp_parse_foreach_in_array(handle, it) { + int nchans; + + ret = parse_chan_layout_element(it.handle, &nchans, NULL); + if (ret) + return ret; + *mask |= 1 << nchans; + } + + return 0; +} + +static int parse_avep_element(struct dcp_parse_ctx *handle, + struct dcp_sound_format_mask *sieve, + struct dcp_sound_format_mask *hits) +{ + struct dcp_sound_format_mask mask = {0, 0, 0}; + struct iterator it; + int ret; + + dcp_parse_foreach_in_dict(handle, it) { + if (consume_string(handle, "StreamSampleRate")) + ret = parse_sample_rate_bit(it.handle, &mask.rates); + else if (consume_string(handle, "SampleSize")) + ret = parse_sample_fmtbit(it.handle, &mask.formats); + else if (consume_string(handle, "AudioChannelLayoutElements")) + ret = parse_nchans_mask(it.handle, &mask.nchans); + else + ret = skip_pair(it.handle); + + if (ret) + return ret; + } + + trace_avep_sound_mode(handle->dcp, mask.rates, mask.formats, mask.nchans); + + if (!(mask.rates & sieve->rates) || !(mask.formats & sieve->formats) || + !(mask.nchans & sieve->nchans)) + return 0; + + if (hits) { + hits->rates |= mask.rates; + hits->formats |= mask.formats; + hits->nchans |= mask.nchans; + } + + return 1; +} + +static int parse_mode_in_avep_element(struct dcp_parse_ctx *handle, + unsigned int selected_nchans, + struct snd_pcm_chmap_elem *chmap, + struct dcp_sound_cookie *cookie) +{ + struct iterator it; + struct dcp_parse_ctx save_handle; + int ret; + + dcp_parse_foreach_in_dict(handle, it) { + if (consume_string(it.handle, "AudioChannelLayoutElements")) { + struct iterator inner_it; + int nchans; + + dcp_parse_foreach_in_array(it.handle, inner_it) { + save_handle = *it.handle; + ret = parse_chan_layout_element(inner_it.handle, + &nchans, NULL); + if (ret) + return ret; + + if (nchans != selected_nchans) + continue; + + /* + * Now that we know this layout matches the + * selected channel number, reread the element + * and fill in the channel map. + */ + *inner_it.handle = save_handle; + ret = parse_chan_layout_element(inner_it.handle, + NULL, chmap); + if (ret) + return ret; + } + } else if (consume_string(it.handle, "ElementData")) { + u8 *blob; + + ret = parse_blob(it.handle, sizeof(*cookie), &blob); + if (ret) + return ret; + + if (cookie) + memcpy(cookie, blob, sizeof(*cookie)); + } else { + ret = skip_pair(it.handle); + if (ret) + return ret; + } + } + + return 0; +} + +int parse_sound_constraints(struct dcp_parse_ctx *handle, + struct dcp_sound_format_mask *sieve, + struct dcp_sound_format_mask *hits) +{ + int ret; + struct iterator it; + + if (hits) { + hits->rates = 0; + hits->formats = 0; + hits->nchans = 0; + } + + dcp_parse_foreach_in_array(handle, it) { + ret = parse_avep_element(it.handle, sieve, hits); + + if (ret < 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(parse_sound_constraints); + +int parse_sound_mode(struct dcp_parse_ctx *handle, + struct dcp_sound_format_mask *sieve, + struct snd_pcm_chmap_elem *chmap, + struct dcp_sound_cookie *cookie) +{ + struct dcp_parse_ctx save_handle; + struct iterator it; + int ret; + + dcp_parse_foreach_in_array(handle, it) { + save_handle = *it.handle; + ret = parse_avep_element(it.handle, sieve, NULL); + + if (!ret) + continue; + + if (ret < 0) + return ret; + + ret = parse_mode_in_avep_element(&save_handle, __ffs(sieve->nchans), + chmap, cookie); + if (ret < 0) + return ret; + return 1; + } + + return 0; +} +EXPORT_SYMBOL_GPL(parse_sound_mode); diff --git a/drivers/gpu/drm/apple/parser.h b/drivers/gpu/drm/apple/parser.h index 92fe9473d56718..030e3a33b4877d 100644 --- a/drivers/gpu/drm/apple/parser.h +++ b/drivers/gpu/drm/apple/parser.h @@ -32,4 +32,24 @@ struct dcp_display_mode *enumerate_modes(struct dcp_parse_ctx *handle, int parse_display_attributes(struct dcp_parse_ctx *handle, int *width_mm, int *height_mm); + +struct dcp_sound_format_mask { + u64 formats; /* SNDRV_PCM_FMTBIT_* */ + unsigned int rates; /* SNDRV_PCM_RATE_* */ + unsigned int nchans; +}; + +struct dcp_sound_cookie { + u8 data[24]; +}; + +struct snd_pcm_chmap_elem; +int parse_sound_constraints(struct dcp_parse_ctx *handle, + struct dcp_sound_format_mask *sieve, + struct dcp_sound_format_mask *hits); +int parse_sound_mode(struct dcp_parse_ctx *handle, + struct dcp_sound_format_mask *sieve, + struct snd_pcm_chmap_elem *chmap, + struct dcp_sound_cookie *cookie); + #endif diff --git a/drivers/gpu/drm/apple/trace.h b/drivers/gpu/drm/apple/trace.h index b691fc5a472587..576d5fc630e5d9 100644 --- a/drivers/gpu/drm/apple/trace.h +++ b/drivers/gpu/drm/apple/trace.h @@ -291,6 +291,29 @@ TRACE_EVENT(iomfb_timing_mode, ) ); +TRACE_EVENT(avep_sound_mode, + TP_PROTO(struct apple_dcp *dcp, u32 rates, u64 formats, unsigned int nchans), + TP_ARGS(dcp, rates, formats, nchans), + TP_STRUCT__entry( + __field(u64, dcp) + __field(u32, rates) + __field(u64, formats) + __field(unsigned int, nchans) + ), + TP_fast_assign( + __entry->dcp = (u64)dcp; + __entry->rates = rates; + __entry->formats = formats; + __entry->nchans = nchans; + ), + TP_printk("dcp=%llx, rates=%#x, formats=%#llx, nchans=%#x", + __entry->dcp, + __entry->rates, + __entry->formats, + __entry->nchans + ) +); + #endif /* _TRACE_DCP_H */ /* This part must be outside protection */ From a7985f5299857af498ec22f2ba0b3edb92be472a Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Sun, 12 Feb 2023 15:51:58 +0100 Subject: [PATCH 0551/1020] drm: apple: DCP AFK/EPIC support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sven Peter Co-developed-by: Martin Povišer Signed-off-by: Martin Povišer Co-developed-by: Janne Grunau Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/Makefile | 2 +- drivers/gpu/drm/apple/afk.c | 948 +++++++++++++++++++++++++++ drivers/gpu/drm/apple/afk.h | 187 ++++++ drivers/gpu/drm/apple/dcp-internal.h | 1 + drivers/gpu/drm/apple/dcp.c | 1 + drivers/gpu/drm/apple/parser.c | 64 +- drivers/gpu/drm/apple/parser.h | 3 +- drivers/gpu/drm/apple/trace.h | 110 ++++ 8 files changed, 1313 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/apple/afk.c create mode 100644 drivers/gpu/drm/apple/afk.h diff --git a/drivers/gpu/drm/apple/Makefile b/drivers/gpu/drm/apple/Makefile index f6490a8e09e2ba..2176ec9474a852 100644 --- a/drivers/gpu/drm/apple/Makefile +++ b/drivers/gpu/drm/apple/Makefile @@ -4,7 +4,7 @@ CFLAGS_trace.o = -I$(src) appledrm-y := apple_drv.o -apple_dcp-y := dcp.o dcp_backlight.o iomfb.o parser.o +apple_dcp-y := afk.o dcp.o dcp_backlight.o iomfb.o parser.o apple_dcp-y += iomfb_v12_3.o apple_dcp-y += iomfb_v13_3.o apple_dcp-$(CONFIG_TRACING) += trace.o diff --git a/drivers/gpu/drm/apple/afk.c b/drivers/gpu/drm/apple/afk.c new file mode 100644 index 00000000000000..5c3a78890f4ec0 --- /dev/null +++ b/drivers/gpu/drm/apple/afk.c @@ -0,0 +1,948 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright 2022 Sven Peter */ + +#include +#include +#include +#include + +#include "afk.h" +#include "trace.h" + +struct afk_receive_message_work { + struct apple_dcp_afkep *ep; + u64 message; + struct work_struct work; +}; + +#define RBEP_TYPE GENMASK(63, 48) + +enum rbep_msg_type { + RBEP_INIT = 0x80, + RBEP_INIT_ACK = 0xa0, + RBEP_GETBUF = 0x89, + RBEP_GETBUF_ACK = 0xa1, + RBEP_INIT_TX = 0x8a, + RBEP_INIT_RX = 0x8b, + RBEP_START = 0xa3, + RBEP_START_ACK = 0x86, + RBEP_SEND = 0xa2, + RBEP_RECV = 0x85, + RBEP_SHUTDOWN = 0xc0, + RBEP_SHUTDOWN_ACK = 0xc1, +}; + +#define BLOCK_SHIFT 6 + +#define GETBUF_SIZE GENMASK(31, 16) +#define GETBUF_TAG GENMASK(15, 0) +#define GETBUF_ACK_DVA GENMASK(47, 0) + +#define INITRB_OFFSET GENMASK(47, 32) +#define INITRB_SIZE GENMASK(31, 16) +#define INITRB_TAG GENMASK(15, 0) + +#define SEND_WPTR GENMASK(31, 0) + +static void afk_send(struct apple_dcp_afkep *ep, u64 message) +{ + dcp_send_message(ep->dcp, ep->endpoint, message); +} + +struct apple_dcp_afkep *afk_init(struct apple_dcp *dcp, u32 endpoint, + const struct apple_epic_service_ops *ops) +{ + struct apple_dcp_afkep *afkep; + int ret; + + afkep = devm_kzalloc(dcp->dev, sizeof(*afkep), GFP_KERNEL); + if (!afkep) + return ERR_PTR(-ENOMEM); + + afkep->ops = ops; + afkep->dcp = dcp; + afkep->endpoint = endpoint; + afkep->wq = alloc_ordered_workqueue("apple-dcp-afkep%02x", + WQ_MEM_RECLAIM, endpoint); + if (!afkep->wq) { + ret = -ENOMEM; + goto out_free_afkep; + } + + // TODO: devm_ for wq + + init_completion(&afkep->started); + init_completion(&afkep->stopped); + spin_lock_init(&afkep->lock); + + return afkep; + +out_free_afkep: + devm_kfree(dcp->dev, afkep); + return ERR_PTR(ret); +} + +int afk_start(struct apple_dcp_afkep *ep) +{ + int ret; + + reinit_completion(&ep->started); + apple_rtkit_start_ep(ep->dcp->rtk, ep->endpoint); + afk_send(ep, FIELD_PREP(RBEP_TYPE, RBEP_INIT)); + + ret = wait_for_completion_timeout(&ep->started, msecs_to_jiffies(1000)); + if (ret <= 0) + return -ETIMEDOUT; + else + return 0; +} + +static void afk_getbuf(struct apple_dcp_afkep *ep, u64 message) +{ + u16 size = FIELD_GET(GETBUF_SIZE, message) << BLOCK_SHIFT; + u16 tag = FIELD_GET(GETBUF_TAG, message); + u64 reply; + + trace_afk_getbuf(ep, size, tag); + + if (ep->bfr) { + dev_err(ep->dcp->dev, + "Got GETBUF message but buffer already exists\n"); + return; + } + + ep->bfr = dmam_alloc_coherent(ep->dcp->dev, size, &ep->bfr_dma, + GFP_KERNEL); + if (!ep->bfr) { + dev_err(ep->dcp->dev, "Failed to allocate %d bytes buffer\n", + size); + return; + } + + ep->bfr_size = size; + ep->bfr_tag = tag; + + reply = FIELD_PREP(RBEP_TYPE, RBEP_GETBUF_ACK); + reply |= FIELD_PREP(GETBUF_ACK_DVA, ep->bfr_dma); + afk_send(ep, reply); +} + +static void afk_init_rxtx(struct apple_dcp_afkep *ep, u64 message, + struct afk_ringbuffer *bfr) +{ + u16 base = FIELD_GET(INITRB_OFFSET, message) << BLOCK_SHIFT; + u16 size = FIELD_GET(INITRB_SIZE, message) << BLOCK_SHIFT; + u16 tag = FIELD_GET(INITRB_TAG, message); + u32 bufsz, end; + + if (tag != ep->bfr_tag) { + dev_err(ep->dcp->dev, "AFK[ep:%02x]: expected tag 0x%x but got 0x%x", + ep->endpoint, ep->bfr_tag, tag); + return; + } + + if (bfr->ready) { + dev_err(ep->dcp->dev, "AFK[ep:%02x]: buffer is already initialized\n", + ep->endpoint); + return; + } + + if (base >= ep->bfr_size) { + dev_err(ep->dcp->dev, + "AFK[ep:%02x]: requested base 0x%x >= max size 0x%lx", + ep->endpoint, base, ep->bfr_size); + return; + } + + end = base + size; + if (end > ep->bfr_size) { + dev_err(ep->dcp->dev, + "AFK[ep:%02x]: requested end 0x%x > max size 0x%lx", + ep->endpoint, end, ep->bfr_size); + return; + } + + bfr->hdr = ep->bfr + base; + bufsz = le32_to_cpu(bfr->hdr->bufsz); + if (bufsz + sizeof(*bfr->hdr) != size) { + dev_err(ep->dcp->dev, + "AFK[ep:%02x]: ring buffer size 0x%x != expected 0x%lx", + ep->endpoint, bufsz, sizeof(*bfr->hdr)); + return; + } + + bfr->buf = bfr->hdr + 1; + bfr->bufsz = bufsz; + bfr->ready = true; + + if (ep->rxbfr.ready && ep->txbfr.ready) + afk_send(ep, FIELD_PREP(RBEP_TYPE, RBEP_START)); +} + +static const struct apple_epic_service_ops * +afk_match_service(struct apple_dcp_afkep *ep, const char *name) +{ + const struct apple_epic_service_ops *ops; + + if (!name[0]) + return NULL; + if (!ep->ops) + return NULL; + + for (ops = ep->ops; ops->name[0]; ops++) { + if (strcmp(ops->name, name)) + continue; + + return ops; + } + + return NULL; +} + +static void afk_recv_handle_init(struct apple_dcp_afkep *ep, u32 channel, + u8 *payload, size_t payload_size) +{ + char name[32]; + s64 epic_unit = -1; + const char *service_name = name; + const char *epic_name = NULL, *epic_class = NULL; + const struct apple_epic_service_ops *ops; + struct dcp_parse_ctx ctx; + u8 *props = payload + sizeof(name); + size_t props_size = payload_size - sizeof(name); + + WARN_ON(ep->services[channel].enabled); + + if (payload_size < sizeof(name)) { + dev_err(ep->dcp->dev, "AFK[ep:%02x]: payload too small: %lx\n", + ep->endpoint, payload_size); + return; + } + + strlcpy(name, payload, sizeof(name)); + + /* + * in DCP firmware 13.2 DCP reports interface-name as name which starts + * with "dispext%d" using -1 s ID for "dcp". In the 12.3 firmware + * EPICProviderClass was used. If the init call has props parse them and + * use EPICProviderClass to match the service. + */ + if (props_size > 36) { + int ret = parse(props, props_size, &ctx); + if (ret) { + dev_err(ep->dcp->dev, + "AFK[ep:%02x]: Failed to parse service init props for %s\n", + ep->endpoint, name); + return; + } + ret = parse_epic_service_init(&ctx, &epic_name, &epic_class, &epic_unit); + if (ret) { + dev_err(ep->dcp->dev, + "AFK[ep:%02x]: failed to extract init props: %d\n", + ep->endpoint, ret); + return; + } + service_name = epic_class; + } else { + service_name = name; + } + + ops = afk_match_service(ep, service_name); + if (!ops) { + dev_err(ep->dcp->dev, + "AFK[ep:%02x]: unable to match service %s on channel %d\n", + ep->endpoint, service_name, channel); + goto free; + } + + spin_lock_init(&ep->services[channel].lock); + ep->services[channel].enabled = true; + ep->services[channel].ops = ops; + ep->services[channel].ep = ep; + ep->services[channel].channel = channel; + ep->services[channel].cmd_tag = 0; + ops->init(&ep->services[channel], epic_name, epic_class, epic_unit); + dev_info(ep->dcp->dev, "AFK[ep:%02x]: new service %s on channel %d\n", + ep->endpoint, service_name, channel); +free: + kfree(epic_name); + kfree(epic_class); +} + +static void afk_recv_handle_teardown(struct apple_dcp_afkep *ep, u32 channel) +{ + struct apple_epic_service *service = &ep->services[channel]; + const struct apple_epic_service_ops *ops; + unsigned long flags; + + WARN_ON(!service->enabled); + + // TODO: think through what locking is necessary + spin_lock_irqsave(&service->lock, flags); + service->enabled = false; + ops = service->ops; + spin_unlock_irqrestore(&service->lock, flags); + + if (ops->teardown) + ops->teardown(service); +} + +static void afk_recv_handle_reply(struct apple_dcp_afkep *ep, u32 channel, + u16 tag, void *payload, size_t payload_size) +{ + struct epic_cmd *cmd = payload; + struct apple_epic_service *service = &ep->services[channel]; + unsigned long flags; + u8 idx = tag & 0xff; + void *rxbuf, *txbuf; + dma_addr_t rxbuf_dma, txbuf_dma; + size_t rxlen, txlen; + + if (payload_size < sizeof(*cmd)) { + dev_err(ep->dcp->dev, + "AFK[ep:%02x]: command reply on channel %d too small: %ld\n", + ep->endpoint, channel, payload_size); + return; + } + + if (idx >= MAX_PENDING_CMDS) { + dev_err(ep->dcp->dev, + "AFK[ep:%02x]: command reply on channel %d out of range: %d\n", + ep->endpoint, channel, idx); + return; + } + + spin_lock_irqsave(&service->lock, flags); + if (service->cmds[idx].done) { + dev_err(ep->dcp->dev, + "AFK[ep:%02x]: command reply on channel %d already handled\n", + ep->endpoint, channel); + spin_unlock_irqrestore(&service->lock, flags); + return; + } + + if (tag != service->cmds[idx].tag) { + dev_err(ep->dcp->dev, + "AFK[ep:%02x]: command reply on channel %d has invalid tag: expected 0x%04x != 0x%04x\n", + ep->endpoint, channel, tag, service->cmds[idx].tag); + spin_unlock_irqrestore(&service->lock, flags); + return; + } + + service->cmds[idx].done = true; + service->cmds[idx].retcode = le32_to_cpu(cmd->retcode); + if (service->cmds[idx].free_on_ack) { + /* defer freeing until we're no longer in atomic context */ + rxbuf = service->cmds[idx].rxbuf; + txbuf = service->cmds[idx].txbuf; + rxlen = service->cmds[idx].rxlen; + txlen = service->cmds[idx].txlen; + rxbuf_dma = service->cmds[idx].rxbuf_dma; + txbuf_dma = service->cmds[idx].txbuf_dma; + bitmap_release_region(service->cmd_map, idx, 0); + } else { + rxbuf = txbuf = NULL; + rxlen = txlen = 0; + } + if (service->cmds[idx].completion) + complete(service->cmds[idx].completion); + + spin_unlock_irqrestore(&service->lock, flags); + + if (rxbuf && rxlen) + dma_free_coherent(ep->dcp->dev, rxlen, rxbuf, rxbuf_dma); + if (txbuf && txlen) + dma_free_coherent(ep->dcp->dev, txlen, txbuf, txbuf_dma); +} + +struct epic_std_service_ap_call { + __le32 unk0; + __le32 unk1; + __le32 type; + __le32 len; + __le32 magic; + u8 _unk[48]; +} __attribute__((packed)); + +static void afk_recv_handle_std_service(struct apple_dcp_afkep *ep, u32 channel, + u32 type, struct epic_hdr *ehdr, + struct epic_sub_hdr *eshdr, + void *payload, size_t payload_size) +{ + struct apple_epic_service *service = &ep->services[channel]; + + if (type == EPIC_TYPE_NOTIFY && eshdr->category == EPIC_CAT_NOTIFY) { + struct epic_std_service_ap_call *call = payload; + size_t call_size; + void *reply; + int ret; + + if (payload_size < sizeof(*call)) + return; + + call_size = le32_to_cpu(call->len); + if (payload_size < sizeof(*call) + call_size) + return; + + if (!service->ops->call) + return; + reply = kzalloc(payload_size, GFP_KERNEL); + if (!reply) + return; + + ret = service->ops->call(service, le32_to_cpu(call->type), + payload + sizeof(*call), call_size, + reply + sizeof(*call), call_size); + if (ret) { + kfree(reply); + return; + } + + memcpy(reply, call, sizeof(*call)); + afk_send_epic(ep, channel, le16_to_cpu(eshdr->tag), + EPIC_TYPE_NOTIFY_ACK, EPIC_CAT_REPLY, + EPIC_SUBTYPE_STD_SERVICE, reply, payload_size); + kfree(reply); + + return; + } + + if (type == EPIC_TYPE_NOTIFY && eshdr->category == EPIC_CAT_REPORT) { + struct epic_std_service_ap_call *call = payload; + size_t call_size; + + if (payload_size < sizeof(*call)) + return; + + call_size = le32_to_cpu(call->len); + if (payload_size < sizeof(*call) + call_size) + return; + + if (!service->ops->report) + return; + + service->ops->report(service, le32_to_cpu(call->type), + payload + sizeof(*call), call_size); + return; + } + + dev_err(ep->dcp->dev, + "AFK[ep:%02x]: channel %d received unhandled standard service message: %x / %x\n", + ep->endpoint, channel, type, eshdr->category); + print_hex_dump(KERN_INFO, "AFK: ", DUMP_PREFIX_NONE, 16, 1, payload, + payload_size, true); +} + +static void afk_recv_handle(struct apple_dcp_afkep *ep, u32 channel, u32 type, + u8 *data, size_t data_size) +{ + struct epic_hdr *ehdr = (struct epic_hdr *)data; + struct epic_sub_hdr *eshdr = + (struct epic_sub_hdr *)(data + sizeof(*ehdr)); + u16 subtype = le16_to_cpu(eshdr->type); + u8 *payload = data + sizeof(*ehdr) + sizeof(*eshdr); + size_t payload_size; + + if (data_size < sizeof(*ehdr) + sizeof(*eshdr)) { + dev_err(ep->dcp->dev, "AFK[ep:%02x]: payload too small: %lx\n", + ep->endpoint, data_size); + return; + } + payload_size = data_size - sizeof(*ehdr) - sizeof(*eshdr); + + trace_afk_recv_handle(ep, channel, type, data_size, ehdr, eshdr); + + if (channel >= AFK_MAX_CHANNEL) { + dev_err(ep->dcp->dev, "AFK[ep:%02x]: channel %d out of bounds\n", + ep->endpoint, channel); + return; + } + + if (!ep->services[channel].enabled) { + if (type != EPIC_TYPE_NOTIFY) { + dev_err(ep->dcp->dev, + "AFK[ep:%02x]: expected notify but got 0x%x on channel %d\n", + ep->endpoint, type, channel); + return; + } + if (eshdr->category != EPIC_CAT_REPORT) { + dev_err(ep->dcp->dev, + "AFK[ep:%02x]: expected report but got 0x%x on channel %d\n", + ep->endpoint, eshdr->category, channel); + return; + } + if (subtype != EPIC_SUBTYPE_ANNOUNCE) { + dev_err(ep->dcp->dev, + "AFK[ep:%02x]: expected announce but got 0x%x on channel %d\n", + ep->endpoint, subtype, channel); + return; + } + + return afk_recv_handle_init(ep, channel, payload, payload_size); + } + + if (!ep->services[channel].enabled) { + dev_err(ep->dcp->dev, "AFK[ep:%02x]: channel %d has no service\n", + ep->endpoint, channel); + return; + } + + if (type == EPIC_TYPE_NOTIFY && eshdr->category == EPIC_CAT_REPORT && + subtype == EPIC_SUBTYPE_TEARDOWN) + return afk_recv_handle_teardown(ep, channel); + + if (type == EPIC_TYPE_REPLY && eshdr->category == EPIC_CAT_REPLY) + return afk_recv_handle_reply(ep, channel, + le16_to_cpu(eshdr->tag), payload, + payload_size); + + if (subtype == EPIC_SUBTYPE_STD_SERVICE) + return afk_recv_handle_std_service( + ep, channel, type, ehdr, eshdr, payload, payload_size); + + dev_err(ep->dcp->dev, "AFK[ep:%02x]: channel %d received unhandled message " + "(type %x subtype %x)\n", ep->endpoint, channel, type, subtype); + print_hex_dump(KERN_INFO, "AFK: ", DUMP_PREFIX_NONE, 16, 1, payload, + payload_size, true); +} + +static bool afk_recv(struct apple_dcp_afkep *ep) +{ + struct afk_qe *hdr; + u32 rptr, wptr; + u32 magic, size, channel, type; + + if (!ep->rxbfr.ready) { + dev_err(ep->dcp->dev, "AFK[ep:%02x]: got RECV but not ready\n", + ep->endpoint); + return false; + } + + rptr = le32_to_cpu(ep->rxbfr.hdr->rptr); + wptr = le32_to_cpu(ep->rxbfr.hdr->wptr); + trace_afk_recv_rwptr_pre(ep, rptr, wptr); + + if (rptr == wptr) + return false; + + if (rptr > (ep->rxbfr.bufsz - sizeof(*hdr))) { + dev_warn(ep->dcp->dev, + "AFK[ep:%02x]: rptr out of bounds: 0x%x > 0x%lx\n", + ep->endpoint, rptr, ep->rxbfr.bufsz - sizeof(*hdr)); + return false; + } + + dma_rmb(); + + hdr = ep->rxbfr.buf + rptr; + magic = le32_to_cpu(hdr->magic); + size = le32_to_cpu(hdr->size); + trace_afk_recv_qe(ep, rptr, magic, size); + + if (magic != QE_MAGIC) { + dev_warn(ep->dcp->dev, "AFK[ep:%02x]: invalid queue entry magic: 0x%x\n", + ep->endpoint, magic); + return false; + } + + /* + * If there's not enough space for the payload the co-processor inserted + * the current dummy queue entry and we have to advance to the next one + * which will contain the real data. + */ + if (rptr + size + sizeof(*hdr) > ep->rxbfr.bufsz) { + rptr = 0; + hdr = ep->rxbfr.buf + rptr; + magic = le32_to_cpu(hdr->magic); + size = le32_to_cpu(hdr->size); + trace_afk_recv_qe(ep, rptr, magic, size); + + if (magic != QE_MAGIC) { + dev_warn(ep->dcp->dev, + "AFK[ep:%02x]: invalid next queue entry magic: 0x%x\n", + ep->endpoint, magic); + return false; + } + + ep->rxbfr.hdr->rptr = cpu_to_le32(rptr); + } + + if (rptr + size + sizeof(*hdr) > ep->rxbfr.bufsz) { + dev_warn(ep->dcp->dev, + "AFK[ep:%02x]: queue entry out of bounds: 0x%lx > 0x%lx\n", + ep->endpoint, rptr + size + sizeof(*hdr), ep->rxbfr.bufsz); + return false; + } + + channel = le32_to_cpu(hdr->channel); + type = le32_to_cpu(hdr->type); + + afk_recv_handle(ep, channel, type, hdr->data, size); + + rptr = ALIGN(rptr + sizeof(*hdr) + size, 1 << BLOCK_SHIFT); + if (WARN_ON(rptr > ep->rxbfr.bufsz)) + rptr = 0; + if (rptr == ep->rxbfr.bufsz) + rptr = 0; + + dma_mb(); + + ep->rxbfr.hdr->rptr = cpu_to_le32(rptr); + trace_afk_recv_rwptr_post(ep, rptr, wptr); + + return true; +} + +static void afk_receive_message_worker(struct work_struct *work_) +{ + struct afk_receive_message_work *work; + u16 type; + + work = container_of(work_, struct afk_receive_message_work, work); + + type = FIELD_GET(RBEP_TYPE, work->message); + switch (type) { + case RBEP_INIT_ACK: + break; + + case RBEP_START_ACK: + complete_all(&work->ep->started); + break; + + case RBEP_SHUTDOWN_ACK: + complete_all(&work->ep->stopped); + break; + + case RBEP_GETBUF: + afk_getbuf(work->ep, work->message); + break; + + case RBEP_INIT_TX: + afk_init_rxtx(work->ep, work->message, &work->ep->txbfr); + break; + + case RBEP_INIT_RX: + afk_init_rxtx(work->ep, work->message, &work->ep->rxbfr); + break; + + case RBEP_RECV: + while (afk_recv(work->ep)) + ; + break; + + default: + dev_err(work->ep->dcp->dev, + "Received unknown AFK message type: 0x%x\n", type); + } + + kfree(work); +} + +int afk_receive_message(struct apple_dcp_afkep *ep, u64 message) +{ + struct afk_receive_message_work *work; + + // TODO: comment why decoupling from rtkit thread is required here + work = kzalloc(sizeof(*work), GFP_KERNEL); + if (!work) + return -ENOMEM; + + work->ep = ep; + work->message = message; + INIT_WORK(&work->work, afk_receive_message_worker); + queue_work(ep->wq, &work->work); + + return 0; +} + +int afk_send_epic(struct apple_dcp_afkep *ep, u32 channel, u16 tag, + enum epic_type etype, enum epic_category ecat, u8 stype, + const void *payload, size_t payload_len) +{ + u32 rptr, wptr; + struct afk_qe *hdr, *hdr2; + struct epic_hdr *ehdr; + struct epic_sub_hdr *eshdr; + unsigned long flags; + size_t total_epic_size, total_size; + int ret; + + spin_lock_irqsave(&ep->lock, flags); + + dma_rmb(); + rptr = le32_to_cpu(ep->txbfr.hdr->rptr); + wptr = le32_to_cpu(ep->txbfr.hdr->wptr); + trace_afk_send_rwptr_pre(ep, rptr, wptr); + total_epic_size = sizeof(*ehdr) + sizeof(*eshdr) + payload_len; + total_size = sizeof(*hdr) + total_epic_size; + + hdr = hdr2 = NULL; + + /* + * We need to figure out how to place the entire headers and payload + * into the ring buffer: + * - If the write pointer is in front of the read pointer we just need + * enough space inbetween to store everything. + * - If the read pointer has already wrapper around the end of the + * buffer we can + * a) either store the entire payload at the writer pointer if + * there's enough space until the end, + * b) or just store the queue entry at the write pointer to indicate + * that we need to wrap to the start and then store the headers + * and the payload at the beginning of the buffer. The queue + * header has to be store twice in this case. + * In either case we have to ensure that there's always enough space + * so that we don't accidentally overwrite other buffers. + */ + if (wptr < rptr) { + /* + * If wptr < rptr we can't wrap around and only have to make + * sure that there's enough space for the entire payload. + */ + if (wptr + total_size > rptr) { + ret = -ENOMEM; + goto out; + } + + hdr = ep->txbfr.buf + wptr; + wptr += sizeof(*hdr); + } else { + /* We need enough space to place at least a queue entry */ + if (wptr + sizeof(*hdr) > ep->txbfr.bufsz) { + ret = -ENOMEM; + goto out; + } + + /* + * If we can place a single queue entry but not the full payload + * we need to place one queue entry at the end of the ring + * buffer and then another one together with the entire + * payload at the beginning. + */ + if (wptr + total_size > ep->txbfr.bufsz) { + /* + * Ensure there's space for the queue entry at the + * beginning + */ + if (sizeof(*hdr) > rptr) { + ret = -ENOMEM; + goto out; + } + + /* + * Place two queue entries to indicate we want to wrap + * around to the firmware. + */ + hdr = ep->txbfr.buf + wptr; + hdr2 = ep->txbfr.buf; + wptr = sizeof(*hdr); + + /* Ensure there's enough space for the entire payload */ + if (wptr + total_epic_size > rptr) { + ret = -ENOMEM; + goto out; + } + } else { + /* We have enough space to place the entire payload */ + hdr = ep->txbfr.buf + wptr; + wptr += sizeof(*hdr); + } + } + /* + * At this point we're guaranteed that hdr (and possibly hdr2) point + * to a buffer large enough to fit the queue entry and that we have + * enough space at wptr to store the payload. + */ + + hdr->magic = cpu_to_le32(QE_MAGIC); + hdr->size = cpu_to_le32(total_epic_size); + hdr->channel = cpu_to_le32(channel); + hdr->type = cpu_to_le32(etype); + if (hdr2) + memcpy(hdr2, hdr, sizeof(*hdr)); + + ehdr = ep->txbfr.buf + wptr; + memset(ehdr, 0, sizeof(*ehdr)); + ehdr->version = 2; + ehdr->seq = cpu_to_le16(ep->qe_seq++); + ehdr->timestamp = cpu_to_le64(0); + wptr += sizeof(*ehdr); + + eshdr = ep->txbfr.buf + wptr; + memset(eshdr, 0, sizeof(*eshdr)); + eshdr->length = cpu_to_le32(payload_len); + eshdr->version = 3; + eshdr->category = ecat; + eshdr->type = cpu_to_le16(stype); + eshdr->timestamp = cpu_to_le64(0); + eshdr->tag = cpu_to_le16(tag); + eshdr->inline_len = cpu_to_le16(0); + wptr += sizeof(*eshdr); + + memcpy(ep->txbfr.buf + wptr, payload, payload_len); + wptr += payload_len; + wptr = ALIGN(wptr, 1 << BLOCK_SHIFT); + if (wptr == ep->txbfr.bufsz) + wptr = 0; + trace_afk_send_rwptr_post(ep, rptr, wptr); + + ep->txbfr.hdr->wptr = cpu_to_le32(wptr); + afk_send(ep, FIELD_PREP(RBEP_TYPE, RBEP_SEND) | + FIELD_PREP(SEND_WPTR, wptr)); + ret = 0; + +out: + spin_unlock_irqrestore(&ep->lock, flags); + return ret; +} + +int afk_send_command(struct apple_epic_service *service, u8 type, + const void *payload, size_t payload_len, void *output, + size_t output_len, u32 *retcode) +{ + struct epic_cmd cmd; + void *rxbuf, *txbuf; + dma_addr_t rxbuf_dma, txbuf_dma; + unsigned long flags; + int ret, idx; + u16 tag; + struct apple_dcp_afkep *ep = service->ep; + DECLARE_COMPLETION_ONSTACK(completion); + + rxbuf = dma_alloc_coherent(ep->dcp->dev, output_len, &rxbuf_dma, + GFP_KERNEL); + if (!rxbuf) + return -ENOMEM; + txbuf = dma_alloc_coherent(ep->dcp->dev, payload_len, &txbuf_dma, + GFP_KERNEL); + if (!txbuf) { + ret = -ENOMEM; + goto err_free_rxbuf; + } + + memcpy(txbuf, payload, payload_len); + + cmd.retcode = cpu_to_le32(0); + cmd.rxbuf = cpu_to_le64(rxbuf_dma); + cmd.rxlen = cpu_to_le32(output_len); + cmd.txbuf = cpu_to_le64(txbuf_dma); + cmd.txlen = cpu_to_le32(payload_len); + + spin_lock_irqsave(&service->lock, flags); + idx = bitmap_find_free_region(service->cmd_map, MAX_PENDING_CMDS, 0); + if (idx < 0) { + ret = -ENOSPC; + goto err_unlock; + } + + tag = (service->cmd_tag & 0xff) << 8; + tag |= idx & 0xff; + service->cmd_tag++; + + service->cmds[idx].tag = tag; + service->cmds[idx].rxbuf = rxbuf; + service->cmds[idx].txbuf = txbuf; + service->cmds[idx].rxbuf_dma = rxbuf_dma; + service->cmds[idx].txbuf_dma = txbuf_dma; + service->cmds[idx].rxlen = output_len; + service->cmds[idx].txlen = payload_len; + service->cmds[idx].free_on_ack = false; + service->cmds[idx].done = false; + service->cmds[idx].completion = &completion; + init_completion(&completion); + + spin_unlock_irqrestore(&service->lock, flags); + + ret = afk_send_epic(service->ep, service->channel, tag, + EPIC_TYPE_COMMAND, EPIC_CAT_COMMAND, type, &cmd, + sizeof(cmd)); + if (ret) + goto err_free_cmd; + + ret = wait_for_completion_timeout(&completion, + msecs_to_jiffies(MSEC_PER_SEC)); + + if (ret <= 0) { + spin_lock_irqsave(&service->lock, flags); + /* + * Check again while we're inside the lock to make sure + * the command wasn't completed just after + * wait_for_completion_timeout returned. + */ + if (!service->cmds[idx].done) { + service->cmds[idx].completion = NULL; + service->cmds[idx].free_on_ack = true; + spin_unlock_irqrestore(&service->lock, flags); + return -ETIMEDOUT; + } + spin_unlock_irqrestore(&service->lock, flags); + } + + ret = 0; + if (retcode) + *retcode = service->cmds[idx].retcode; + if (output && output_len) + memcpy(output, rxbuf, output_len); + +err_free_cmd: + spin_lock_irqsave(&service->lock, flags); + bitmap_release_region(service->cmd_map, idx, 0); +err_unlock: + spin_unlock_irqrestore(&service->lock, flags); + dma_free_coherent(ep->dcp->dev, payload_len, txbuf, txbuf_dma); +err_free_rxbuf: + dma_free_coherent(ep->dcp->dev, output_len, rxbuf, rxbuf_dma); + return ret; +} + +int afk_service_call(struct apple_epic_service *service, u16 group, u32 command, + const void *data, size_t data_len, size_t data_pad, + void *output, size_t output_len, size_t output_pad) +{ + struct epic_service_call *call; + void *bfr; + size_t bfr_len = max(data_len + data_pad, output_len + output_pad) + + sizeof(*call); + int ret; + u32 retcode; + u32 retlen; + + bfr = kzalloc(bfr_len, GFP_KERNEL); + if (!bfr) + return -ENOMEM; + + call = bfr; + call->group = cpu_to_le16(group); + call->command = cpu_to_le32(command); + call->data_len = cpu_to_le32(data_len + data_pad); + call->magic = cpu_to_le32(EPIC_SERVICE_CALL_MAGIC); + + memcpy(bfr + sizeof(*call), data, data_len); + + ret = afk_send_command(service, EPIC_SUBTYPE_STD_SERVICE, bfr, bfr_len, + bfr, bfr_len, &retcode); + if (ret) + goto out; + if (retcode) { + ret = -EINVAL; + goto out; + } + if (le32_to_cpu(call->magic) != EPIC_SERVICE_CALL_MAGIC || + le16_to_cpu(call->group) != group || + le32_to_cpu(call->command) != command) { + ret = -EINVAL; + goto out; + } + + retlen = le32_to_cpu(call->data_len); + if (output_len < retlen) + retlen = output_len; + if (output && output_len) { + memset(output, 0, output_len); + memcpy(output, bfr + sizeof(*call), retlen); + } + +out: + kfree(bfr); + return ret; +} diff --git a/drivers/gpu/drm/apple/afk.h b/drivers/gpu/drm/apple/afk.h new file mode 100644 index 00000000000000..b800840b4f4a3a --- /dev/null +++ b/drivers/gpu/drm/apple/afk.h @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * AFK (Apple Firmware Kit) EPIC (EndPoint Interface Client) support + */ +/* Copyright 2022 Sven Peter */ + +#ifndef _DRM_APPLE_DCP_AFK_H +#define _DRM_APPLE_DCP_AFK_H + +#include +#include + +#include "dcp.h" + +#define AFK_MAX_CHANNEL 16 +#define MAX_PENDING_CMDS 16 + +struct apple_epic_service_ops; +struct apple_dcp_afkep; + +struct epic_cmd_info { + u16 tag; + + void *rxbuf; + void *txbuf; + dma_addr_t rxbuf_dma; + dma_addr_t txbuf_dma; + size_t rxlen; + size_t txlen; + + u32 retcode; + bool done; + bool free_on_ack; + struct completion *completion; +}; + +struct apple_epic_service { + const struct apple_epic_service_ops *ops; + struct apple_dcp_afkep *ep; + + struct epic_cmd_info cmds[MAX_PENDING_CMDS]; + DECLARE_BITMAP(cmd_map, MAX_PENDING_CMDS); + u8 cmd_tag; + spinlock_t lock; + + u32 channel; + bool enabled; + + void *cookie; +}; + +struct apple_epic_service_ops { + const char name[32]; + + void (*init)(struct apple_epic_service *service, const char *name, + const char *class, s64 unit); + int (*call)(struct apple_epic_service *service, u32 idx, + const void *data, size_t data_size, void *reply, + size_t reply_size); + int (*report)(struct apple_epic_service *service, u32 idx, + const void *data, size_t data_size); + void (*teardown)(struct apple_epic_service *service); +}; + +struct afk_ringbuffer_header { + __le32 bufsz; + u32 unk; + u32 _pad1[14]; + __le32 rptr; + u32 _pad2[15]; + __le32 wptr; + u32 _pad3[15]; +}; + +struct afk_qe { +#define QE_MAGIC 0x20504f49 // ' POI' + __le32 magic; + __le32 size; + __le32 channel; + __le32 type; + u8 data[]; +}; + +struct epic_hdr { + u8 version; + __le16 seq; + u8 _pad; + __le32 unk; + __le64 timestamp; +} __attribute__((packed)); + +struct epic_sub_hdr { + __le32 length; + u8 version; + u8 category; + __le16 type; + __le64 timestamp; + __le16 tag; + __le16 unk; + __le32 inline_len; +} __attribute__((packed)); + +struct epic_cmd { + __le32 retcode; + __le64 rxbuf; + __le64 txbuf; + __le32 rxlen; + __le32 txlen; +} __attribute__((packed)); + +struct epic_service_call { + u8 _pad0[2]; + __le16 group; + __le32 command; + __le32 data_len; +#define EPIC_SERVICE_CALL_MAGIC 0x69706378 + __le32 magic; + u8 _pad1[48]; +} __attribute__((packed)); +static_assert(sizeof(struct epic_service_call) == 64); + +enum epic_type { + EPIC_TYPE_NOTIFY = 0, + EPIC_TYPE_COMMAND = 3, + EPIC_TYPE_REPLY = 4, + EPIC_TYPE_NOTIFY_ACK = 8, +}; + +enum epic_category { + EPIC_CAT_REPORT = 0x00, + EPIC_CAT_NOTIFY = 0x10, + EPIC_CAT_REPLY = 0x20, + EPIC_CAT_COMMAND = 0x30, +}; + +enum epic_subtype { + EPIC_SUBTYPE_ANNOUNCE = 0x30, + EPIC_SUBTYPE_TEARDOWN = 0x32, + EPIC_SUBTYPE_STD_SERVICE = 0xc0, +}; + +struct afk_ringbuffer { + bool ready; + struct afk_ringbuffer_header *hdr; + u32 rptr; + void *buf; + size_t bufsz; +}; + +struct apple_dcp_afkep { + struct apple_dcp *dcp; + + u32 endpoint; + struct workqueue_struct *wq; + + struct completion started; + struct completion stopped; + + void *bfr; + u16 bfr_tag; + size_t bfr_size; + dma_addr_t bfr_dma; + + struct afk_ringbuffer txbfr; + struct afk_ringbuffer rxbfr; + + spinlock_t lock; + u16 qe_seq; + + const struct apple_epic_service_ops *ops; + struct apple_epic_service services[AFK_MAX_CHANNEL]; +}; + +struct apple_dcp_afkep *afk_init(struct apple_dcp *dcp, u32 endpoint, + const struct apple_epic_service_ops *ops); +int afk_start(struct apple_dcp_afkep *ep); +int afk_receive_message(struct apple_dcp_afkep *ep, u64 message); +int afk_send_epic(struct apple_dcp_afkep *ep, u32 channel, u16 tag, + enum epic_type etype, enum epic_category ecat, u8 stype, + const void *payload, size_t payload_len); +int afk_send_command(struct apple_epic_service *service, u8 type, + const void *payload, size_t payload_len, void *output, + size_t output_len, u32 *retcode); +int afk_service_call(struct apple_epic_service *service, u16 group, u32 command, + const void *data, size_t data_len, size_t data_pad, + void *output, size_t output_len, size_t output_pad); +#endif diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index c04585c3374991..aa28ac54651a8a 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -17,6 +17,7 @@ #define DCP_MAX_PLANES 2 struct apple_dcp; +struct apple_dcp_afkep; enum dcp_firmware_version { DCP_FIRMWARE_UNKNOWN, diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 90a738aa9c046b..029621dd7f9480 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -26,6 +26,7 @@ #include #include +#include "afk.h" #include "dcp.h" #include "dcp-internal.h" #include "iomfb.h" diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index 50a4fc31cd06ce..f8b8576ebaf464 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -589,6 +589,68 @@ int parse_display_attributes(struct dcp_parse_ctx *handle, int *width_mm, return 0; } +int parse_epic_service_init(struct dcp_parse_ctx *handle, const char **name, + const char **class, s64 *unit) +{ + int ret = 0; + struct iterator it; + bool parsed_unit = false; + bool parsed_name = false; + bool parsed_class = false; + + *name = ERR_PTR(-ENOENT); + *class = ERR_PTR(-ENOENT); + + dcp_parse_foreach_in_dict(handle, it) { + char *key = parse_string(it.handle); + + if (IS_ERR(key)) { + ret = PTR_ERR(key); + break; + } + + if (!strcmp(key, "EPICName")) { + *name = parse_string(it.handle); + if (IS_ERR(*name)) + ret = PTR_ERR(*name); + else + parsed_name = true; + } else if (!strcmp(key, "EPICProviderClass")) { + *class = parse_string(it.handle); + if (IS_ERR(*class)) + ret = PTR_ERR(*class); + else + parsed_class = true; + } else if (!strcmp(key, "EPICUnit")) { + ret = parse_int(it.handle, unit); + if (!ret) + parsed_unit = true; + } else { + skip(it.handle); + } + + kfree(key); + if (ret) + break; + } + + if (!parsed_unit || !parsed_name || !parsed_class) + ret = -ENOENT; + + if (ret) { + if (!IS_ERR(*name)) { + kfree(*name); + *name = ERR_PTR(ret); + } + if (!IS_ERR(*class)) { + kfree(*class); + *class = ERR_PTR(ret); + } + } + + return ret; +} + int parse_sample_rate_bit(struct dcp_parse_ctx *handle, unsigned int *ratebit) { s64 rate; @@ -891,4 +953,4 @@ int parse_sound_mode(struct dcp_parse_ctx *handle, return 0; } -EXPORT_SYMBOL_GPL(parse_sound_mode); +EXPORT_SYMBOL_GPL(parse_sound_mode); \ No newline at end of file diff --git a/drivers/gpu/drm/apple/parser.h b/drivers/gpu/drm/apple/parser.h index 030e3a33b4877d..6d8717873cdd6c 100644 --- a/drivers/gpu/drm/apple/parser.h +++ b/drivers/gpu/drm/apple/parser.h @@ -31,7 +31,8 @@ struct dcp_display_mode *enumerate_modes(struct dcp_parse_ctx *handle, int height_mm, unsigned notch_height); int parse_display_attributes(struct dcp_parse_ctx *handle, int *width_mm, int *height_mm); - +int parse_epic_service_init(struct dcp_parse_ctx *handle, const char **name, + const char **class, s64 *unit); struct dcp_sound_format_mask { u64 formats; /* SNDRV_PCM_FMTBIT_* */ diff --git a/drivers/gpu/drm/apple/trace.h b/drivers/gpu/drm/apple/trace.h index 576d5fc630e5d9..dd081223267ae8 100644 --- a/drivers/gpu/drm/apple/trace.h +++ b/drivers/gpu/drm/apple/trace.h @@ -7,7 +7,9 @@ #if !defined(_TRACE_DCP_H) || defined(TRACE_HEADER_MULTI_READ) #define _TRACE_DCP_H +#include "afk.h" #include "dcp-internal.h" +#include "parser.h" #include #include @@ -22,6 +24,17 @@ { HDCP_ENDPOINT, "hdcp" }, \ { REMOTE_ALLOC_ENDPOINT, "remotealloc" }, \ { IOMFB_ENDPOINT, "iomfb" }) +#define print_epic_type(etype) \ + __print_symbolic(etype, { EPIC_TYPE_NOTIFY, "notify" }, \ + { EPIC_TYPE_COMMAND, "command" }, \ + { EPIC_TYPE_REPLY, "reply" }, \ + { EPIC_TYPE_NOTIFY_ACK, "notify-ack" }) + +#define print_epic_category(ecat) \ + __print_symbolic(ecat, { EPIC_CAT_REPORT, "report" }, \ + { EPIC_CAT_NOTIFY, "notify" }, \ + { EPIC_CAT_REPLY, "reply" }, \ + { EPIC_CAT_COMMAND, "command" }) TRACE_EVENT(dcp_recv_msg, TP_PROTO(struct apple_dcp *dcp, u8 endpoint, u64 message), @@ -55,6 +68,103 @@ TRACE_EVENT(dcp_send_msg, __get_str(devname), __entry->endpoint, show_dcp_endpoint(__entry->endpoint), __entry->message)); +TRACE_EVENT( + afk_getbuf, TP_PROTO(struct apple_dcp_afkep *ep, u16 size, u16 tag), + TP_ARGS(ep, size, tag), + + TP_STRUCT__entry(__string(devname, dev_name(ep->dcp->dev)) + __field(u8, endpoint) __field(u16, size) + __field(u16, tag)), + + TP_fast_assign(__assign_str(devname, dev_name(ep->dcp->dev)); + __entry->endpoint = ep->endpoint; __entry->size = size; + __entry->tag = tag;), + + TP_printk( + "%s: endpoint 0x%x (%s): get buffer with size 0x%x and tag 0x%x", + __get_str(devname), __entry->endpoint, + show_dcp_endpoint(__entry->endpoint), __entry->size, + __entry->tag)); + +DECLARE_EVENT_CLASS(afk_rwptr_template, + TP_PROTO(struct apple_dcp_afkep *ep, u32 rptr, u32 wptr), + TP_ARGS(ep, rptr, wptr), + + TP_STRUCT__entry(__string(devname, dev_name(ep->dcp->dev)) + __field(u8, endpoint) __field(u32, rptr) + __field(u32, wptr)), + + TP_fast_assign(__assign_str(devname, dev_name(ep->dcp->dev)); + __entry->endpoint = ep->endpoint; + __entry->rptr = rptr; __entry->wptr = wptr;), + + TP_printk("%s: endpoint 0x%x (%s): rptr 0x%x, wptr 0x%x", + __get_str(devname), __entry->endpoint, + show_dcp_endpoint(__entry->endpoint), __entry->rptr, + __entry->wptr)); + +DEFINE_EVENT(afk_rwptr_template, afk_recv_rwptr_pre, + TP_PROTO(struct apple_dcp_afkep *ep, u32 rptr, u32 wptr), + TP_ARGS(ep, rptr, wptr)); +DEFINE_EVENT(afk_rwptr_template, afk_recv_rwptr_post, + TP_PROTO(struct apple_dcp_afkep *ep, u32 rptr, u32 wptr), + TP_ARGS(ep, rptr, wptr)); +DEFINE_EVENT(afk_rwptr_template, afk_send_rwptr_pre, + TP_PROTO(struct apple_dcp_afkep *ep, u32 rptr, u32 wptr), + TP_ARGS(ep, rptr, wptr)); +DEFINE_EVENT(afk_rwptr_template, afk_send_rwptr_post, + TP_PROTO(struct apple_dcp_afkep *ep, u32 rptr, u32 wptr), + TP_ARGS(ep, rptr, wptr)); + +TRACE_EVENT( + afk_recv_qe, + TP_PROTO(struct apple_dcp_afkep *ep, u32 rptr, u32 magic, u32 size), + TP_ARGS(ep, rptr, magic, size), + + TP_STRUCT__entry(__string(devname, dev_name(ep->dcp->dev)) + __field(u8, endpoint) __field(u32, rptr) + __field(u32, magic) + __field(u32, size)), + + TP_fast_assign(__assign_str(devname, dev_name(ep->dcp->dev)); + __entry->endpoint = ep->endpoint; __entry->rptr = rptr; + __entry->magic = magic; __entry->size = size;), + + TP_printk("%s: endpoint 0x%x (%s): QE rptr 0x%x, magic 0x%x, size 0x%x", + __get_str(devname), __entry->endpoint, + show_dcp_endpoint(__entry->endpoint), __entry->rptr, + __entry->magic, __entry->size)); + +TRACE_EVENT( + afk_recv_handle, + TP_PROTO(struct apple_dcp_afkep *ep, u32 channel, u32 type, + u32 data_size, struct epic_hdr *ehdr, + struct epic_sub_hdr *eshdr), + TP_ARGS(ep, channel, type, data_size, ehdr, eshdr), + + TP_STRUCT__entry(__string(devname, dev_name(ep->dcp->dev)) __field( + u8, endpoint) __field(u32, channel) __field(u32, type) + __field(u32, data_size) __field(u8, category) + __field(u16, subtype) + __field(u16, tag)), + + TP_fast_assign(__assign_str(devname, dev_name(ep->dcp->dev)); + __entry->endpoint = ep->endpoint; + __entry->channel = channel; __entry->type = type; + __entry->data_size = data_size; + __entry->category = eshdr->category, + __entry->subtype = le16_to_cpu(eshdr->type), + __entry->tag = le16_to_cpu(eshdr->tag)), + + TP_printk( + "%s: endpoint 0x%x (%s): channel 0x%x, type 0x%x (%s), data_size 0x%x, category: 0x%x (%s), subtype: 0x%x, seq: 0x%x", + __get_str(devname), __entry->endpoint, + show_dcp_endpoint(__entry->endpoint), __entry->channel, + __entry->type, print_epic_type(__entry->type), + __entry->data_size, __entry->category, + print_epic_category(__entry->category), __entry->subtype, + __entry->tag)); + TRACE_EVENT(iomfb_callback, TP_PROTO(struct apple_dcp *dcp, int tag, const char *name), TP_ARGS(dcp, tag, name), From e5bb2085661f4745ba52659669ce82cd456fa947 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 12 Nov 2023 12:35:51 +0100 Subject: [PATCH 0552/1020] drm: apple: afk: Use linear array of services "Channel numbers" as received by AFK/EPIC are constantly increasing over restarts of the endpoint. Use a linear array of services and match based on the channel number. The number of services per endpoint is too small to make a difference. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/afk.c | 72 +++++++++++++++++++++++++++---------- drivers/gpu/drm/apple/afk.h | 1 + 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/apple/afk.c b/drivers/gpu/drm/apple/afk.c index 5c3a78890f4ec0..14b544055713ba 100644 --- a/drivers/gpu/drm/apple/afk.c +++ b/drivers/gpu/drm/apple/afk.c @@ -199,11 +199,22 @@ afk_match_service(struct apple_dcp_afkep *ep, const char *name) return NULL; } +static struct apple_epic_service *afk_epic_find_service(struct apple_dcp_afkep *ep, + u32 channel) +{ + for (u32 i = 0; i < ep->num_channels; i++) + if (ep->services[i].enabled && ep->services[i].channel == channel) + return &ep->services[i]; + + return NULL; +} + static void afk_recv_handle_init(struct apple_dcp_afkep *ep, u32 channel, u8 *payload, size_t payload_size) { char name[32]; s64 epic_unit = -1; + u32 ch_idx; const char *service_name = name; const char *epic_name = NULL, *epic_class = NULL; const struct apple_epic_service_ops *ops; @@ -211,7 +222,7 @@ static void afk_recv_handle_init(struct apple_dcp_afkep *ep, u32 channel, u8 *props = payload + sizeof(name); size_t props_size = payload_size - sizeof(name); - WARN_ON(ep->services[channel].enabled); + WARN_ON(afk_epic_find_service(ep, channel)); if (payload_size < sizeof(name)) { dev_err(ep->dcp->dev, "AFK[ep:%02x]: payload too small: %lx\n", @@ -219,6 +230,12 @@ static void afk_recv_handle_init(struct apple_dcp_afkep *ep, u32 channel, return; } + if (ep->num_channels >= AFK_MAX_CHANNEL) { + dev_err(ep->dcp->dev, "AFK[ep:%02x]: too many enabled services!\n", + ep->endpoint); + return; + } + strlcpy(name, payload, sizeof(name)); /* @@ -255,13 +272,14 @@ static void afk_recv_handle_init(struct apple_dcp_afkep *ep, u32 channel, goto free; } - spin_lock_init(&ep->services[channel].lock); - ep->services[channel].enabled = true; - ep->services[channel].ops = ops; - ep->services[channel].ep = ep; - ep->services[channel].channel = channel; - ep->services[channel].cmd_tag = 0; - ops->init(&ep->services[channel], epic_name, epic_class, epic_unit); + ch_idx = ep->num_channels++; + spin_lock_init(&ep->services[ch_idx].lock); + ep->services[ch_idx].enabled = true; + ep->services[ch_idx].ops = ops; + ep->services[ch_idx].ep = ep; + ep->services[ch_idx].channel = channel; + ep->services[ch_idx].cmd_tag = 0; + ops->init(&ep->services[ch_idx], epic_name, epic_class, epic_unit); dev_info(ep->dcp->dev, "AFK[ep:%02x]: new service %s on channel %d\n", ep->endpoint, service_name, channel); free: @@ -271,11 +289,16 @@ static void afk_recv_handle_init(struct apple_dcp_afkep *ep, u32 channel, static void afk_recv_handle_teardown(struct apple_dcp_afkep *ep, u32 channel) { - struct apple_epic_service *service = &ep->services[channel]; + struct apple_epic_service *service; const struct apple_epic_service_ops *ops; unsigned long flags; - WARN_ON(!service->enabled); + service = afk_epic_find_service(ep, channel); + if (!service) { + dev_warn(ep->dcp->dev, "AFK[ep:%02x]: teardown for disabled channel %u\n", + ep->endpoint, channel); + return; + } // TODO: think through what locking is necessary spin_lock_irqsave(&service->lock, flags); @@ -291,13 +314,20 @@ static void afk_recv_handle_reply(struct apple_dcp_afkep *ep, u32 channel, u16 tag, void *payload, size_t payload_size) { struct epic_cmd *cmd = payload; - struct apple_epic_service *service = &ep->services[channel]; + struct apple_epic_service *service; unsigned long flags; u8 idx = tag & 0xff; void *rxbuf, *txbuf; dma_addr_t rxbuf_dma, txbuf_dma; size_t rxlen, txlen; + service = afk_epic_find_service(ep, channel); + if (!service) { + dev_warn(ep->dcp->dev, "AFK[ep:%02x]: command reply on disabled channel %u\n", + ep->endpoint, channel); + return; + } + if (payload_size < sizeof(*cmd)) { dev_err(ep->dcp->dev, "AFK[ep:%02x]: command reply on channel %d too small: %ld\n", @@ -369,7 +399,14 @@ static void afk_recv_handle_std_service(struct apple_dcp_afkep *ep, u32 channel, struct epic_sub_hdr *eshdr, void *payload, size_t payload_size) { - struct apple_epic_service *service = &ep->services[channel]; + struct apple_epic_service *service = afk_epic_find_service(ep, channel); + + if (!service) { + dev_warn(ep->dcp->dev, + "AFK[ep:%02x]: std service notify on disabled channel %u\n", + ep->endpoint, channel); + return; + } if (type == EPIC_TYPE_NOTIFY && eshdr->category == EPIC_CAT_NOTIFY) { struct epic_std_service_ap_call *call = payload; @@ -436,6 +473,7 @@ static void afk_recv_handle_std_service(struct apple_dcp_afkep *ep, u32 channel, static void afk_recv_handle(struct apple_dcp_afkep *ep, u32 channel, u32 type, u8 *data, size_t data_size) { + struct apple_epic_service *service; struct epic_hdr *ehdr = (struct epic_hdr *)data; struct epic_sub_hdr *eshdr = (struct epic_sub_hdr *)(data + sizeof(*ehdr)); @@ -452,13 +490,9 @@ static void afk_recv_handle(struct apple_dcp_afkep *ep, u32 channel, u32 type, trace_afk_recv_handle(ep, channel, type, data_size, ehdr, eshdr); - if (channel >= AFK_MAX_CHANNEL) { - dev_err(ep->dcp->dev, "AFK[ep:%02x]: channel %d out of bounds\n", - ep->endpoint, channel); - return; - } + service = afk_epic_find_service(ep, channel); - if (!ep->services[channel].enabled) { + if (!service) { if (type != EPIC_TYPE_NOTIFY) { dev_err(ep->dcp->dev, "AFK[ep:%02x]: expected notify but got 0x%x on channel %d\n", @@ -481,7 +515,7 @@ static void afk_recv_handle(struct apple_dcp_afkep *ep, u32 channel, u32 type, return afk_recv_handle_init(ep, channel, payload, payload_size); } - if (!ep->services[channel].enabled) { + if (!service) { dev_err(ep->dcp->dev, "AFK[ep:%02x]: channel %d has no service\n", ep->endpoint, channel); return; diff --git a/drivers/gpu/drm/apple/afk.h b/drivers/gpu/drm/apple/afk.h index b800840b4f4a3a..fe4ed35159ace0 100644 --- a/drivers/gpu/drm/apple/afk.h +++ b/drivers/gpu/drm/apple/afk.h @@ -169,6 +169,7 @@ struct apple_dcp_afkep { const struct apple_epic_service_ops *ops; struct apple_epic_service services[AFK_MAX_CHANNEL]; + u32 num_channels; }; struct apple_dcp_afkep *afk_init(struct apple_dcp *dcp, u32 endpoint, From a93586ec028e328ba4a04cf85ba58e1bbc4c0803 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Sat, 5 Nov 2022 13:15:34 +0100 Subject: [PATCH 0553/1020] drm: apple: Add DPTX support This is required for DP Altmode, DP Thunderbolt tunneling and HDMI output on 14/16-inch Macbook Pros and M2* desktop devices. M2* desktops and 14 and 16 inch Macbook Pros expose a DisplayPort to HDMI converter which is driven by the DP output of one of the DCP/DCPext display coprocessor/controller blocks. Two gpio pins are used for power control. Another gpio pin acts as HDMI hpd. Do not use the hpd as direct drm_connector interrupt since that is already wired to DCPs hotplug notification. Instead use it to trigger link setup via the dptx endpoint. Signed-off-by: Sven Peter Co-developed-by: Janne Grunau Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/Kconfig | 1 + drivers/gpu/drm/apple/Makefile | 3 +- drivers/gpu/drm/apple/apple_drv.c | 11 +- drivers/gpu/drm/apple/dcp-internal.h | 34 +++ drivers/gpu/drm/apple/dcp.c | 225 ++++++++++++++- drivers/gpu/drm/apple/dcp.h | 3 + drivers/gpu/drm/apple/dcp_trace.c | 3 + drivers/gpu/drm/apple/dptxep.c | 407 +++++++++++++++++++++++++++ drivers/gpu/drm/apple/dptxep.h | 66 +++++ drivers/gpu/drm/apple/ibootep.c | 29 ++ drivers/gpu/drm/apple/parser.c | 11 +- drivers/gpu/drm/apple/parser.h | 5 + drivers/gpu/drm/apple/systemep.c | 100 +++++++ drivers/gpu/drm/apple/trace.h | 140 +++++++++ 14 files changed, 1025 insertions(+), 13 deletions(-) create mode 100644 drivers/gpu/drm/apple/dcp_trace.c create mode 100644 drivers/gpu/drm/apple/dptxep.c create mode 100644 drivers/gpu/drm/apple/dptxep.h create mode 100644 drivers/gpu/drm/apple/ibootep.c create mode 100644 drivers/gpu/drm/apple/systemep.c diff --git a/drivers/gpu/drm/apple/Kconfig b/drivers/gpu/drm/apple/Kconfig index 9b9bcb7b5433e0..f0504641b5f641 100644 --- a/drivers/gpu/drm/apple/Kconfig +++ b/drivers/gpu/drm/apple/Kconfig @@ -7,5 +7,6 @@ config DRM_APPLE select DRM_KMS_DMA_HELPER select DRM_GEM_DMA_HELPER select VIDEOMODE_HELPERS + select MULTIPLEXER help Say Y if you have an Apple Silicon chipset. diff --git a/drivers/gpu/drm/apple/Makefile b/drivers/gpu/drm/apple/Makefile index 2176ec9474a852..afe5c7ea2785cf 100644 --- a/drivers/gpu/drm/apple/Makefile +++ b/drivers/gpu/drm/apple/Makefile @@ -4,7 +4,8 @@ CFLAGS_trace.o = -I$(src) appledrm-y := apple_drv.o -apple_dcp-y := afk.o dcp.o dcp_backlight.o iomfb.o parser.o +apple_dcp-y := afk.o dcp.o dcp_backlight.o dptxep.o iomfb.o parser.o systemep.o +apple_dcp-y += ibootep.o apple_dcp-y += iomfb_v12_3.o apple_dcp-y += iomfb_v13_3.o apple_dcp-$(CONFIG_TRACING) += trace.o diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index bb947102c10e90..ce68c19a9a3a03 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -313,7 +313,7 @@ static const struct drm_crtc_helper_funcs apple_crtc_helper_funcs = { static int apple_probe_per_dcp(struct device *dev, struct drm_device *drm, struct platform_device *dcp, - int num) + int num, bool dcp_ext) { struct apple_crtc *crtc; struct apple_connector *connector; @@ -345,6 +345,10 @@ static int apple_probe_per_dcp(struct device *dev, drm_connector_helper_add(&connector->base, &apple_connector_helper_funcs); + // HACK: + if (dcp_ext) + connector->base.fwnode = fwnode_handle_get(dev->fwnode); + ret = drm_connector_init(drm, &connector->base, &apple_connector_funcs, dcp_get_connector_type(dcp)); if (ret) @@ -396,6 +400,7 @@ static int apple_get_fb_resource(struct device *dev, const char *name, static const struct of_device_id apple_dcp_id_tbl[] = { { .compatible = "apple,dcp" }, + { .compatible = "apple,dcpext" }, {}, }; @@ -408,10 +413,12 @@ static int apple_drm_init_dcp(struct device *dev) int i, ret, num_dcp = 0; for_each_matching_node(np, apple_dcp_id_tbl) { + bool dcp_ext; if (!of_device_is_available(np)) { of_node_put(np); continue; } + dcp_ext = of_device_is_compatible(np, "apple,dcpext"); dcp[num_dcp] = of_find_device_by_node(np); of_node_put(np); @@ -419,7 +426,7 @@ static int apple_drm_init_dcp(struct device *dev) continue; ret = apple_probe_per_dcp(dev, &apple->drm, dcp[num_dcp], - num_dcp); + num_dcp, dcp_ext); if (ret) continue; diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index aa28ac54651a8a..849bd2937ebb9d 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -7,9 +7,12 @@ #include #include #include +#include +#include #include #include +#include "dptxep.h" #include "iomfb.h" #include "iomfb_v12_3.h" #include "iomfb_v13_3.h" @@ -94,6 +97,10 @@ struct dcp_panel { bool has_mini_led; }; +struct apple_dcp_hw_data { + u32 num_dptx_ports; +}; + /* TODO: move IOMFB members to its own struct */ struct apple_dcp { struct device *dev; @@ -103,6 +110,8 @@ struct apple_dcp { struct apple_crtc *crtc; struct apple_connector *connector; + struct apple_dcp_hw_data hw; + /* firmware version and compatible firmware version */ enum dcp_firmware_version fw_compat; @@ -127,6 +136,8 @@ struct apple_dcp { struct resource *disp_registers[MAX_DISP_REGISTERS]; unsigned int nr_disp_registers; + u32 index; + /* Bitmap of memory descriptors used for mappings made by the DCP */ DECLARE_BITMAP(memdesc_map, DCP_MAX_MAPPINGS); @@ -191,6 +202,29 @@ struct apple_dcp { /* integrated panel if present */ struct dcp_panel panel; + + struct apple_dcp_afkep *systemep; + struct completion systemep_done; + + struct apple_dcp_afkep *ibootep; + + struct apple_dcp_afkep *dptxep; + + struct dptx_port dptxport[2]; + + /* these fields are output port specific */ + struct phy *phy; + struct mux_control *xbar; + + struct gpio_desc *hdmi_hpd; + struct gpio_desc *hdmi_pwren; + struct gpio_desc *dp2hdmi_pwren; + + struct mutex hpd_mutex; + + u32 dptx_phy; + u32 dptx_die; + int hdmi_hpd_irq; }; int dcp_backlight_register(struct apple_dcp *dcp); diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 029621dd7f9480..69f118ae8b6e59 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -115,6 +116,15 @@ static void dcp_recv_msg(void *cookie, u8 endpoint, u64 message) switch (endpoint) { case IOMFB_ENDPOINT: return iomfb_recv_msg(dcp, message); + case SYSTEM_ENDPOINT: + afk_receive_message(dcp->systemep, message); + return; + case DISP0_ENDPOINT: + afk_receive_message(dcp->ibootep, message); + return; + case DPTX_ENDPOINT: + afk_receive_message(dcp->dptxep, message); + return; default: WARN(endpoint, "unknown DCP endpoint %hhu", endpoint); } @@ -194,7 +204,7 @@ void dcp_send_message(struct apple_dcp *dcp, u8 endpoint, u64 message) { trace_dcp_send_msg(dcp, endpoint, message); apple_rtkit_send_message(dcp->rtk, endpoint, message, NULL, - false); + true); } int dcp_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) @@ -243,6 +253,66 @@ int dcp_get_connector_type(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(dcp_get_connector_type); +static int dcp_dptx_connect(struct apple_dcp *dcp, u32 port) +{ + if (!dcp->phy) { + dev_warn(dcp->dev, "dcp_dptx_connect: missing phy\n"); + return -ENODEV; + } + + mutex_lock(&dcp->hpd_mutex); + if (!dcp->dptxport[port].enabled) { + dev_warn(dcp->dev, "dcp_dptx_connect: dptx service for port %d not enabled\n", port); + mutex_unlock(&dcp->hpd_mutex); + return -ENODEV; + } + + if (dcp->dptxport[port].connected) + return 0; + + dcp->dptxport[port].atcphy = dcp->phy; + dptxport_connect(dcp->dptxport[port].service, 0, dcp->dptx_phy, dcp->dptx_die); + dptxport_request_display(dcp->dptxport[port].service); + dcp->dptxport[port].connected = true; + mutex_unlock(&dcp->hpd_mutex); + + return 0; +} + +static int dcp_dptx_disconnect(struct apple_dcp *dcp, u32 port) +{ + struct apple_connector *connector = dcp->connector; + + mutex_lock(&dcp->hpd_mutex); + if (connector && connector->connected) { + dcp->valid_mode = false; + schedule_work(&connector->hotplug_wq); + } + + if (dcp->dptxport[port].enabled && dcp->dptxport[port].connected) { + dptxport_release_display(dcp->dptxport[port].service); + dcp->dptxport[port].connected = false; + } + mutex_unlock(&dcp->hpd_mutex); + + return 0; +} + +static irqreturn_t dcp_dp2hdmi_hpd(int irq, void *data) +{ + struct apple_dcp *dcp = data; + bool connected = gpiod_get_value_cansleep(dcp->hdmi_hpd); + + dev_info(dcp->dev, "DP2HDMI HPD connected:%d\n", connected); + + if (connected) + dcp_dptx_connect(dcp, 0); + else + dcp_dptx_disconnect(dcp, 0); + + return IRQ_HANDLED; +} + void dcp_link(struct platform_device *pdev, struct apple_crtc *crtc, struct apple_connector *connector) { @@ -261,6 +331,28 @@ int dcp_start(struct platform_device *pdev) init_completion(&dcp->start_done); /* start RTKit endpoints */ + ret = systemep_init(dcp); + if (ret) + dev_warn(dcp->dev, "Failed to start system endpoint: %d", ret); + + if (dcp->phy) { + if (dcp->fw_compat >= DCP_FIRMWARE_V_13_5) { + ret = ibootep_init(dcp); + if (ret) + dev_warn(dcp->dev, + "Failed to start IBOOT endpoint: %d", + ret); + + ret = dptxep_init(dcp); + if (ret) + dev_warn(dcp->dev, + "Failed to start DPTX endpoint: %d", + ret); + } else + dev_warn(dcp->dev, + "OS firmware incompatible with dptxport EP\n"); + } + ret = iomfb_start_rtkit(dcp); if (ret) dev_err(dcp->dev, "Failed to start IOMFB endpoint: %d", ret); @@ -269,6 +361,23 @@ int dcp_start(struct platform_device *pdev) } EXPORT_SYMBOL(dcp_start); +static int dcp_enable_dp2hdmi_hpd(struct apple_dcp *dcp) +{ + if (dcp->hdmi_hpd) { + bool connected = gpiod_get_value_cansleep(dcp->hdmi_hpd); + dev_info(dcp->dev, "%s: DP2HDMI HPD connected:%d\n", __func__, connected); + + // necessary on j473/j474 but not on j314c + if (connected) + dcp_dptx_connect(dcp, 0); + + if (dcp->hdmi_hpd_irq) + enable_irq(dcp->hdmi_hpd_irq); + } + + return 0; +} + int dcp_wait_ready(struct platform_device *pdev, u64 timeout) { struct apple_dcp *dcp = platform_get_drvdata(pdev); @@ -277,7 +386,7 @@ int dcp_wait_ready(struct platform_device *pdev, u64 timeout) if (dcp->crashed) return -ENODEV; if (dcp->active) - return 0; + return dcp_enable_dp2hdmi_hpd(dcp);; if (timeout <= 0) return -ETIMEDOUT; @@ -288,6 +397,9 @@ int dcp_wait_ready(struct platform_device *pdev, u64 timeout) if (dcp->crashed) return -ENODEV; + if (dcp->active) + dcp_enable_dp2hdmi_hpd(dcp); + return dcp->active ? 0 : -ETIMEDOUT; } EXPORT_SYMBOL(dcp_wait_ready); @@ -476,6 +588,17 @@ static int dcp_comp_bind(struct device *dev, struct device *main, void *data) if (IS_ERR(dcp->coproc_reg)) return PTR_ERR(dcp->coproc_reg); + of_property_read_u32(dev->of_node, "apple,dcp-index", + &dcp->index); + of_property_read_u32(dev->of_node, "apple,dptx-phy", + &dcp->dptx_phy); + of_property_read_u32(dev->of_node, "apple,dptx-die", + &dcp->dptx_die); + if (dcp->index || dcp->dptx_phy || dcp->dptx_die) + dev_info(dev, "DCP index:%u dptx target phy: %u dptx die: %u\n", + dcp->index, dcp->dptx_phy, dcp->dptx_die); + mutex_init(&dcp->hpd_mutex); + if (!show_notch) ret = of_property_read_u32(dev->of_node, "apple,notch-height", &dcp->notch_height); @@ -560,7 +683,6 @@ static int dcp_comp_bind(struct device *dev, struct device *main, void *data) if (ret) return dev_err_probe(dev, ret, "Failed to boot RTKit: %d", ret); - return ret; } @@ -572,6 +694,9 @@ static void dcp_comp_unbind(struct device *dev, struct device *main, void *data) { struct apple_dcp *dcp = dev_get_drvdata(dev); + if (dcp->hdmi_hpd_irq) + disable_irq(dcp->hdmi_hpd_irq); + if (dcp && dcp->shmem) iomfb_shutdown(dcp); @@ -596,6 +721,7 @@ static int dcp_platform_probe(struct platform_device *pdev) enum dcp_firmware_version fw_compat; struct device *dev = &pdev->dev; struct apple_dcp *dcp; + u32 mux_index; fw_compat = dcp_check_firmware_version(dev); if (fw_compat == DCP_FIRMWARE_UNKNOWN) @@ -607,9 +733,71 @@ static int dcp_platform_probe(struct platform_device *pdev) dcp->fw_compat = fw_compat; dcp->dev = dev; + dcp->hw = *(struct apple_dcp_hw_data *)of_device_get_match_data(dev); platform_set_drvdata(pdev, dcp); + dcp->phy = devm_phy_optional_get(dev, "dp-phy"); + if (IS_ERR(dcp->phy)) { + dev_err(dev, "Failed to get dp-phy: %ld", PTR_ERR(dcp->phy)); + return PTR_ERR(dcp->phy); + } + if (dcp->phy) { + int ret; + /* + * Request DP2HDMI related GPIOs as optional for DP-altmode + * compatibility. J180D misses a dp2hdmi-pwren GPIO in the + * template ADT. TODO: check device ADT + */ + dcp->hdmi_hpd = devm_gpiod_get_optional(dev, "hdmi-hpd", GPIOD_IN); + if (IS_ERR(dcp->hdmi_hpd)) + return PTR_ERR(dcp->hdmi_hpd); + if (dcp->hdmi_hpd) { + int irq = gpiod_to_irq(dcp->hdmi_hpd); + if (irq < 0) { + dev_err(dev, "failed to translate HDMI hpd GPIO to IRQ\n"); + return irq; + } + dcp->hdmi_hpd_irq = irq; + + ret = devm_request_threaded_irq(dev, dcp->hdmi_hpd_irq, + NULL, dcp_dp2hdmi_hpd, + IRQF_ONESHOT | IRQF_NO_AUTOEN | + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "dp2hdmi-hpd-irq", dcp); + if (ret < 0) { + dev_err(dev, "failed to request HDMI hpd irq %d: %d", + irq, ret); + return ret; + } + } + + /* + * Power DP2HDMI on as it is required for the HPD irq. + * TODO: check if one is sufficient for the hpd to save power + * on battery powered Macbooks. + */ + dcp->hdmi_pwren = devm_gpiod_get_optional(dev, "hdmi-pwren", GPIOD_OUT_HIGH); + if (IS_ERR(dcp->hdmi_pwren)) + return PTR_ERR(dcp->hdmi_pwren); + + dcp->dp2hdmi_pwren = devm_gpiod_get_optional(dev, "dp2hdmi-pwren", GPIOD_OUT_HIGH); + if (IS_ERR(dcp->dp2hdmi_pwren)) + return PTR_ERR(dcp->dp2hdmi_pwren); + + ret = of_property_read_u32(dev->of_node, "mux-index", &mux_index); + if (!ret) { + dcp->xbar = devm_mux_control_get(dev, "dp-xbar"); + if (IS_ERR(dcp->xbar)) { + dev_err(dev, "Failed to get dp-xbar: %ld", PTR_ERR(dcp->xbar)); + return PTR_ERR(dcp->xbar); + } + ret = mux_control_select(dcp->xbar, mux_index); + if (ret) + dev_warn(dev, "mux_control_select failed: %d\n", ret); + } + } + return component_add(&pdev->dev, &dcp_comp_ops); } @@ -627,6 +815,10 @@ static void dcp_platform_shutdown(struct platform_device *pdev) static int dcp_platform_suspend(struct device *dev) { + struct apple_dcp *dcp = dev_get_drvdata(dev); + + if (dcp->hdmi_hpd_irq) + disable_irq(dcp->hdmi_hpd_irq); /* * Set the device as a wakeup device, which forces its power * domains to stay on. We need this as we do not support full @@ -639,14 +831,39 @@ static int dcp_platform_suspend(struct device *dev) static int dcp_platform_resume(struct device *dev) { + struct apple_dcp *dcp = dev_get_drvdata(dev); + + if (dcp->hdmi_hpd_irq) + enable_irq(dcp->hdmi_hpd_irq); + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(dcp_platform_pm_ops, dcp_platform_suspend, dcp_platform_resume); + +static const struct apple_dcp_hw_data apple_dcp_hw_t6020 = { + .num_dptx_ports = 1, +}; + +static const struct apple_dcp_hw_data apple_dcp_hw_t8112 = { + .num_dptx_ports = 2, +}; + +static const struct apple_dcp_hw_data apple_dcp_hw_dcp = { + .num_dptx_ports = 0, +}; + +static const struct apple_dcp_hw_data apple_dcp_hw_dcpext = { + .num_dptx_ports = 2, +}; + static const struct of_device_id of_match[] = { - { .compatible = "apple,dcp" }, + { .compatible = "apple,t6020-dcp", .data = &apple_dcp_hw_t6020, }, + { .compatible = "apple,t8112-dcp", .data = &apple_dcp_hw_t8112, }, + { .compatible = "apple,dcp", .data = &apple_dcp_hw_dcp, }, + { .compatible = "apple,dcpext", .data = &apple_dcp_hw_dcpext, }, {} }; MODULE_DEVICE_TABLE(of, of_match); diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h index 2011d27e809d53..e0dc96109b9d2b 100644 --- a/drivers/gpu/drm/apple/dcp.h +++ b/drivers/gpu/drm/apple/dcp.h @@ -68,4 +68,7 @@ void iomfb_shutdown(struct apple_dcp *dcp); /* rtkit message handler for IOMFB messages */ void iomfb_recv_msg(struct apple_dcp *dcp, u64 message); +int systemep_init(struct apple_dcp *dcp); +int dptxep_init(struct apple_dcp *dcp); +int ibootep_init(struct apple_dcp *dcp); #endif diff --git a/drivers/gpu/drm/apple/dcp_trace.c b/drivers/gpu/drm/apple/dcp_trace.c new file mode 100644 index 00000000000000..d18e71af73a74d --- /dev/null +++ b/drivers/gpu/drm/apple/dcp_trace.c @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: GPL-2.0 +#define CREATE_TRACE_POINTS +#include "dcp_trace.h" \ No newline at end of file diff --git a/drivers/gpu/drm/apple/dptxep.c b/drivers/gpu/drm/apple/dptxep.c new file mode 100644 index 00000000000000..e929e93cdbaef3 --- /dev/null +++ b/drivers/gpu/drm/apple/dptxep.c @@ -0,0 +1,407 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright 2022 Sven Peter */ + +#include +#include +#include + +#include "afk.h" +#include "dcp.h" +#include "dptxep.h" +#include "parser.h" +#include "trace.h" + +struct dcpdptx_connection_cmd { + __le32 unk; + __le32 target; +} __attribute__((packed)); + +struct dcpdptx_hotplug_cmd { + u8 _pad0[16]; + __le32 unk; +} __attribute__((packed)); + +struct dptxport_apcall_link_rate { + __le32 retcode; + u8 _unk0[12]; + __le32 link_rate; + u8 _unk1[12]; +} __attribute__((packed)); + +struct dptxport_apcall_get_support { + __le32 retcode; + u8 _unk0[12]; + __le32 supported; + u8 _unk1[12]; +} __attribute__((packed)); + +struct dptxport_apcall_max_drive_settings { + __le32 retcode; + u8 _unk0[12]; + __le32 max_drive_settings[2]; + u8 _unk1[8]; +}; + +int dptxport_validate_connection(struct apple_epic_service *service, u8 core, + u8 atc, u8 die) +{ + struct dptx_port *dptx = service->cookie; + struct dcpdptx_connection_cmd cmd, resp; + int ret; + u32 target = FIELD_PREP(DCPDPTX_REMOTE_PORT_CORE, core) | + FIELD_PREP(DCPDPTX_REMOTE_PORT_ATC, atc) | + FIELD_PREP(DCPDPTX_REMOTE_PORT_DIE, die) | + DCPDPTX_REMOTE_PORT_CONNECTED; + + trace_dptxport_validate_connection(dptx, core, atc, die); + + cmd.target = cpu_to_le32(target); + cmd.unk = cpu_to_le32(0x100); + ret = afk_service_call(service, 0, 14, &cmd, sizeof(cmd), 40, &resp, + sizeof(resp), 40); + if (ret) + return ret; + + if (le32_to_cpu(resp.target) != target) + return -EINVAL; + if (le32_to_cpu(resp.unk) != 0x100) + return -EINVAL; + + return 0; +} + +int dptxport_connect(struct apple_epic_service *service, u8 core, u8 atc, + u8 die) +{ + struct dptx_port *dptx = service->cookie; + struct dcpdptx_connection_cmd cmd, resp; + int ret; + u32 target = FIELD_PREP(DCPDPTX_REMOTE_PORT_CORE, core) | + FIELD_PREP(DCPDPTX_REMOTE_PORT_ATC, atc) | + FIELD_PREP(DCPDPTX_REMOTE_PORT_DIE, die) | + DCPDPTX_REMOTE_PORT_CONNECTED; + + trace_dptxport_connect(dptx, core, atc, die); + + cmd.target = cpu_to_le32(target); + cmd.unk = cpu_to_le32(0x100); + ret = afk_service_call(service, 0, 13, &cmd, sizeof(cmd), 24, &resp, + sizeof(resp), 24); + if (ret) + return ret; + + if (le32_to_cpu(resp.target) != target) + return -EINVAL; + if (le32_to_cpu(resp.unk) != 0x100) + return -EINVAL; + + return 0; +} + +int dptxport_request_display(struct apple_epic_service *service) +{ + return afk_service_call(service, 0, 8, NULL, 0, 16, NULL, 0, 16); +} + +int dptxport_release_display(struct apple_epic_service *service) +{ + return afk_service_call(service, 0, 9, NULL, 0, 16, NULL, 0, 16); +} + +int dptxport_set_hpd(struct apple_epic_service *service, bool hpd) +{ + struct dcpdptx_hotplug_cmd cmd, resp; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + + if (hpd) + cmd.unk = cpu_to_le32(1); + + ret = afk_service_call(service, 8, 10, &cmd, sizeof(cmd), 12, &resp, + sizeof(resp), 12); + if (ret) + return ret; + if (le32_to_cpu(resp.unk) != 1) + return -EINVAL; + return 0; +} + +static int +dptxport_call_get_max_drive_settings(struct apple_epic_service *service, + void *reply_, size_t reply_size) +{ + struct dptxport_apcall_max_drive_settings *reply = reply_; + + if (reply_size < sizeof(*reply)) + return -EINVAL; + + reply->retcode = cpu_to_le32(0); + reply->max_drive_settings[0] = cpu_to_le32(0x3); + reply->max_drive_settings[1] = cpu_to_le32(0x3); + + return 0; +} + +static int dptxport_call_get_max_link_rate(struct apple_epic_service *service, + void *reply_, size_t reply_size) +{ + struct dptxport_apcall_link_rate *reply = reply_; + + if (reply_size < sizeof(*reply)) + return -EINVAL; + + reply->retcode = cpu_to_le32(0); + reply->link_rate = cpu_to_le32(LINK_RATE_HBR3); + + return 0; +} + +static int dptxport_call_get_link_rate(struct apple_epic_service *service, + void *reply_, size_t reply_size) +{ + struct dptx_port *dptx = service->cookie; + struct dptxport_apcall_link_rate *reply = reply_; + + if (reply_size < sizeof(*reply)) + return -EINVAL; + + reply->retcode = cpu_to_le32(0); + reply->link_rate = cpu_to_le32(dptx->link_rate); + + return 0; +} + +static int +dptxport_call_will_change_link_config(struct apple_epic_service *service) +{ + struct dptx_port *dptx = service->cookie; + + dptx->phy_ops.dp.set_lanes = 0; + dptx->phy_ops.dp.set_rate = 0; + dptx->phy_ops.dp.set_voltages = 0; + + return 0; +} + +static int +dptxport_call_did_change_link_config(struct apple_epic_service *service) +{ + /* assume the link config did change and wait a little bit */ + mdelay(10); + return 0; +} + +static int dptxport_call_set_link_rate(struct apple_epic_service *service, + const void *data, size_t data_size, + void *reply_, size_t reply_size) +{ + struct dptx_port *dptx = service->cookie; + const struct dptxport_apcall_link_rate *request = data; + struct dptxport_apcall_link_rate *reply = reply_; + u32 link_rate, phy_link_rate; + bool phy_set_rate = false; + int ret; + + if (reply_size < sizeof(*reply)) + return -EINVAL; + if (data_size < sizeof(*request)) + return -EINVAL; + + link_rate = le32_to_cpu(request->link_rate); + trace_dptxport_call_set_link_rate(dptx, link_rate); + + switch (link_rate) { + case LINK_RATE_RBR: + phy_link_rate = 1620; + phy_set_rate = true; + break; + case LINK_RATE_HBR: + phy_link_rate = 2700; + phy_set_rate = true; + break; + case LINK_RATE_HBR2: + phy_link_rate = 5400; + phy_set_rate = true; + break; + case LINK_RATE_HBR3: + phy_link_rate = 8100; + phy_set_rate = true; + break; + case 0: + phy_link_rate = 0; + phy_set_rate = true; + break; + default: + dev_err(service->ep->dcp->dev, + "DPTXPort: Unsupported link rate 0x%x requested\n", + link_rate); + link_rate = 0; + phy_set_rate = false; + break; + } + + if (phy_set_rate) { + dptx->phy_ops.dp.link_rate = phy_link_rate; + dptx->phy_ops.dp.set_rate = 1; + + if (dptx->atcphy) { + ret = phy_configure(dptx->atcphy, &dptx->phy_ops); + if (ret) + return ret; + } + + //if (dptx->phy_ops.dp.set_rate) + dptx->link_rate = dptx->pending_link_rate = link_rate; + + } + + //dptx->pending_link_rate = link_rate; + reply->retcode = cpu_to_le32(0); + reply->link_rate = cpu_to_le32(link_rate); + + return 0; +} + +static int dptxport_call_get_supports_hpd(struct apple_epic_service *service, + void *reply_, size_t reply_size) +{ + struct dptxport_apcall_get_support *reply = reply_; + + if (reply_size < sizeof(*reply)) + return -EINVAL; + + reply->retcode = cpu_to_le32(0); + reply->supported = cpu_to_le32(0); + return 0; +} + +static int +dptxport_call_get_supports_downspread(struct apple_epic_service *service, + void *reply_, size_t reply_size) +{ + struct dptxport_apcall_get_support *reply = reply_; + + if (reply_size < sizeof(*reply)) + return -EINVAL; + + reply->retcode = cpu_to_le32(0); + reply->supported = cpu_to_le32(0); + return 0; +} + +static int dptxport_call(struct apple_epic_service *service, u32 idx, + const void *data, size_t data_size, void *reply, + size_t reply_size) +{ + struct dptx_port *dptx = service->cookie; + trace_dptxport_apcall(dptx, idx, data_size); + + switch (idx) { + case DPTX_APCALL_WILL_CHANGE_LINKG_CONFIG: + return dptxport_call_will_change_link_config(service); + case DPTX_APCALL_DID_CHANGE_LINK_CONFIG: + return dptxport_call_did_change_link_config(service); + case DPTX_APCALL_GET_MAX_LINK_RATE: + return dptxport_call_get_max_link_rate(service, reply, + reply_size); + case DPTX_APCALL_GET_LINK_RATE: + return dptxport_call_get_link_rate(service, reply, reply_size); + case DPTX_APCALL_SET_LINK_RATE: + return dptxport_call_set_link_rate(service, data, data_size, + reply, reply_size); + case DPTX_APCALL_GET_SUPPORTS_HPD: + return dptxport_call_get_supports_hpd(service, reply, + reply_size); + case DPTX_APCALL_GET_SUPPORTS_DOWN_SPREAD: + return dptxport_call_get_supports_downspread(service, reply, + reply_size); + case DPTX_APCALL_GET_MAX_DRIVE_SETTINGS: + return dptxport_call_get_max_drive_settings(service, reply, + reply_size); + default: + /* just try to ACK and hope for the best... */ + dev_info(service->ep->dcp->dev, "DPTXPort: acking unhandled call %u\n", + idx); + fallthrough; + /* we can silently ignore and just ACK these calls */ + case DPTX_APCALL_ACTIVATE: + case DPTX_APCALL_DEACTIVATE: + case DPTX_APCALL_SET_DRIVE_SETTINGS: + case DPTX_APCALL_GET_DRIVE_SETTINGS: + memcpy(reply, data, min(reply_size, data_size)); + if (reply_size > 4) + memset(reply, 0, 4); + return 0; + } +} + +static void dptxport_init(struct apple_epic_service *service, const char *name, + const char *class, s64 unit) +{ + + if (strcmp(name, "dcpdptx-port-epic")) + return; + if (strcmp(class, "AppleDCPDPTXRemotePort")) + return; + + trace_dptxport_init(service->ep->dcp, unit); + + switch (unit) { + case 0: + case 1: + if (service->ep->dcp->dptxport[unit].enabled) { + dev_err(service->ep->dcp->dev, + "DPTXPort: unit %lld already exists\n", unit); + return; + } + service->ep->dcp->dptxport[unit].unit = unit; + service->ep->dcp->dptxport[unit].service = service; + service->ep->dcp->dptxport[unit].enabled = true; + service->cookie = (void *)&service->ep->dcp->dptxport[unit]; + complete(&service->ep->dcp->dptxport[unit].enable_completion); + break; + default: + dev_err(service->ep->dcp->dev, "DPTXPort: invalid unit %lld\n", + unit); + } +} + +static const struct apple_epic_service_ops dptxep_ops[] = { + { + .name = "AppleDCPDPTXRemotePort", + .init = dptxport_init, + .call = dptxport_call, + }, + {} +}; + +int dptxep_init(struct apple_dcp *dcp) +{ + int ret; + u32 port; + unsigned long timeout = msecs_to_jiffies(1000); + + init_completion(&dcp->dptxport[0].enable_completion); + init_completion(&dcp->dptxport[1].enable_completion); + + dcp->dptxep = afk_init(dcp, DPTX_ENDPOINT, dptxep_ops); + if (IS_ERR(dcp->dptxep)) + return PTR_ERR(dcp->dptxep); + + ret = afk_start(dcp->dptxep); + if (ret) + return ret; + + for (port = 0; port < dcp->hw.num_dptx_ports; port++) { + ret = wait_for_completion_timeout(&dcp->dptxport[port].enable_completion, + timeout); + if (!ret) + return -ETIMEDOUT; + else if (ret < 0) + return ret; + timeout = ret; + } + + return 0; +} diff --git a/drivers/gpu/drm/apple/dptxep.h b/drivers/gpu/drm/apple/dptxep.h new file mode 100644 index 00000000000000..efd1d5005f56da --- /dev/null +++ b/drivers/gpu/drm/apple/dptxep.h @@ -0,0 +1,66 @@ +#ifndef __APPLE_DCP_DPTXEP_H__ +#define __APPLE_DCP_DPTXEP_H__ + +#include +#include + +enum dptx_apcall { + DPTX_APCALL_ACTIVATE = 0, + DPTX_APCALL_DEACTIVATE = 1, + DPTX_APCALL_GET_MAX_DRIVE_SETTINGS = 2, + DPTX_APCALL_SET_DRIVE_SETTINGS = 3, + DPTX_APCALL_GET_DRIVE_SETTINGS = 4, + DPTX_APCALL_WILL_CHANGE_LINKG_CONFIG = 5, + DPTX_APCALL_DID_CHANGE_LINK_CONFIG = 6, + DPTX_APCALL_GET_MAX_LINK_RATE = 7, + DPTX_APCALL_GET_LINK_RATE = 8, + DPTX_APCALL_SET_LINK_RATE = 9, + DPTX_APCALL_GET_ACTIVE_LANE_COUNT = 10, + DPTX_APCALL_SET_ACTIVE_LANE_COUNT = 11, + DPTX_APCALL_GET_SUPPORTS_DOWN_SPREAD = 12, + DPTX_APCALL_GET_DOWN_SPREAD = 13, + DPTX_APCALL_SET_DOWN_SPREAD = 14, + DPTX_APCALL_GET_SUPPORTS_LANE_MAPPING = 15, + DPTX_APCALL_SET_LANE_MAP = 16, + DPTX_APCALL_GET_SUPPORTS_HPD = 17, + DPTX_APCALL_FORCE_HOTPLUG_DETECT = 18, + DPTX_APCALL_INACTIVE_SINK_DETECTED = 19, + DPTX_APCALL_SET_TILED_DISPLAY_HINTS = 20, + DPTX_APCALL_DEVICE_NOT_RESPONDING = 21, + DPTX_APCALL_DEVICE_BUSY_TIMEOUT = 22, + DPTX_APCALL_DEVICE_NOT_STARTED = 23, +}; + +#define DCPDPTX_REMOTE_PORT_CORE GENMASK(3, 0) +#define DCPDPTX_REMOTE_PORT_ATC GENMASK(7, 4) +#define DCPDPTX_REMOTE_PORT_DIE GENMASK(11, 8) +#define DCPDPTX_REMOTE_PORT_CONNECTED BIT(15) + +enum dptx_link_rate { + LINK_RATE_RBR = 0x06, + LINK_RATE_HBR = 0x0a, + LINK_RATE_HBR2 = 0x14, + LINK_RATE_HBR3 = 0x1e, +}; + +struct apple_epic_service; + +struct dptx_port { + bool enabled, connected; + struct completion enable_completion; + u32 unit; + struct apple_epic_service *service; + union phy_configure_opts phy_ops; + struct phy *atcphy; + struct mux_control *mux; + u32 link_rate, pending_link_rate; +}; + +int dptxport_validate_connection(struct apple_epic_service *service, u8 core, + u8 atc, u8 die); +int dptxport_connect(struct apple_epic_service *service, u8 core, u8 atc, + u8 die); +int dptxport_request_display(struct apple_epic_service *service); +int dptxport_release_display(struct apple_epic_service *service); +int dptxport_set_hpd(struct apple_epic_service *service, bool hpd); +#endif diff --git a/drivers/gpu/drm/apple/ibootep.c b/drivers/gpu/drm/apple/ibootep.c new file mode 100644 index 00000000000000..ae4bc8a69f2a8d --- /dev/null +++ b/drivers/gpu/drm/apple/ibootep.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright 2023 */ + +#include + +#include "afk.h" +#include "dcp.h" + +static void disp_service_init(struct apple_epic_service *service, const char *name, + const char *class, s64 unit) +{ +} + + +static const struct apple_epic_service_ops ibootep_ops[] = { + { + .name = "disp0-service", + .init = disp_service_init, + }, + {} +}; + +int ibootep_init(struct apple_dcp *dcp) +{ + dcp->ibootep = afk_init(dcp, DISP0_ENDPOINT, ibootep_ops); + afk_start(dcp->ibootep); + + return 0; +} diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index f8b8576ebaf464..474f704859bd84 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -270,11 +270,6 @@ int parse(void *blob, size_t size, struct dcp_parse_ctx *ctx) return 0; } -struct dimension { - s64 total, front_porch, sync_width, active; - s64 precise_sync_rate; -}; - static int parse_dimension(struct dcp_parse_ctx *handle, struct dimension *dim) { struct iterator it; @@ -445,10 +440,14 @@ static int parse_mode(struct dcp_parse_ctx *handle, if (!IS_ERR_OR_NULL(key)) kfree(key); - if (ret) + if (ret) { + trace_iomfb_parse_mode_fail(id, &horiz, &vert, best_color_mode, is_virtual, *score); return ret; + } } + trace_iomfb_parse_mode_success(id, &horiz, &vert, best_color_mode, is_virtual, *score); + /* * Reject modes without valid color mode. */ diff --git a/drivers/gpu/drm/apple/parser.h b/drivers/gpu/drm/apple/parser.h index 6d8717873cdd6c..f9cc3718fa84f7 100644 --- a/drivers/gpu/drm/apple/parser.h +++ b/drivers/gpu/drm/apple/parser.h @@ -25,6 +25,11 @@ struct dcp_display_mode { u32 timing_mode_id; }; +struct dimension { + s64 total, front_porch, sync_width, active; + s64 precise_sync_rate; +}; + int parse(void *blob, size_t size, struct dcp_parse_ctx *ctx); struct dcp_display_mode *enumerate_modes(struct dcp_parse_ctx *handle, unsigned int *count, int width_mm, diff --git a/drivers/gpu/drm/apple/systemep.c b/drivers/gpu/drm/apple/systemep.c new file mode 100644 index 00000000000000..5383a83f1e6c28 --- /dev/null +++ b/drivers/gpu/drm/apple/systemep.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright 2022 Sven Peter */ + +#include + +#include "afk.h" +#include "dcp.h" + +static bool enable_verbose_logging; +module_param(enable_verbose_logging, bool, 0644); +MODULE_PARM_DESC(enable_verbose_logging, "Enable DCP firmware verbose logging"); + +/* + * Serialized setProperty("gAFKConfigLogMask", 0xffff) IPC call which + * will set the DCP firmware log level to the most verbose setting + */ +#define SYSTEM_SET_PROPERTY 0x43 +static const u8 setprop_gAFKConfigLogMask_ffff[] = { + 0x14, 0x00, 0x00, 0x00, 0x67, 0x41, 0x46, 0x4b, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x4c, 0x6f, 0x67, 0x4d, 0x61, 0x73, + 0x6b, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x84, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +struct systemep_work { + struct apple_epic_service *service; + struct work_struct work; +}; + +static void system_log_work(struct work_struct *work_) +{ + struct systemep_work *work = + container_of(work_, struct systemep_work, work); + + afk_send_command(work->service, SYSTEM_SET_PROPERTY, + setprop_gAFKConfigLogMask_ffff, + sizeof(setprop_gAFKConfigLogMask_ffff), NULL, + sizeof(setprop_gAFKConfigLogMask_ffff), NULL); + complete(&work->service->ep->dcp->systemep_done); + kfree(work); +} + +static void system_init(struct apple_epic_service *service, const char *name, + const char *class, s64 unit) +{ + struct systemep_work *work; + + if (!enable_verbose_logging) + return; + + /* + * We're called from the service message handler thread and can't + * dispatch blocking message from there. + */ + work = kzalloc(sizeof(*work), GFP_KERNEL); + if (!work) + return; + + work->service = service; + INIT_WORK(&work->work, system_log_work); + schedule_work(&work->work); +} + +static void powerlog_init(struct apple_epic_service *service, const char *name, + const char *class, s64 unit) +{ +} + +static const struct apple_epic_service_ops systemep_ops[] = { + { + .name = "system", + .init = system_init, + }, + { + .name = "powerlog-service", + .init = powerlog_init, + }, + {} +}; + +int systemep_init(struct apple_dcp *dcp) +{ + init_completion(&dcp->systemep_done); + + dcp->systemep = afk_init(dcp, SYSTEM_ENDPOINT, systemep_ops); + afk_start(dcp->systemep); + + if (!enable_verbose_logging) + return 0; + + /* + * Timeouts aren't really fatal here: in the worst case we just weren't + * able to enable additional debug prints inside DCP + */ + if (!wait_for_completion_timeout(&dcp->systemep_done, + msecs_to_jiffies(MSEC_PER_SEC))) + dev_err(dcp->dev, "systemep: couldn't enable verbose logs\n"); + + return 0; +} diff --git a/drivers/gpu/drm/apple/trace.h b/drivers/gpu/drm/apple/trace.h index dd081223267ae8..9858305c4068c7 100644 --- a/drivers/gpu/drm/apple/trace.h +++ b/drivers/gpu/drm/apple/trace.h @@ -8,6 +8,7 @@ #define _TRACE_DCP_H #include "afk.h" +#include "dptxep.h" #include "dcp-internal.h" #include "parser.h" @@ -36,6 +37,43 @@ { EPIC_CAT_REPLY, "reply" }, \ { EPIC_CAT_COMMAND, "command" }) +#define show_dptxport_apcall(idx) \ + __print_symbolic( \ + idx, { DPTX_APCALL_ACTIVATE, "activate" }, \ + { DPTX_APCALL_DEACTIVATE, "deactivate" }, \ + { DPTX_APCALL_GET_MAX_DRIVE_SETTINGS, \ + "get_max_drive_settings" }, \ + { DPTX_APCALL_SET_DRIVE_SETTINGS, "set_drive_settings" }, \ + { DPTX_APCALL_GET_DRIVE_SETTINGS, "get_drive_settings" }, \ + { DPTX_APCALL_WILL_CHANGE_LINKG_CONFIG, \ + "will_change_link_config" }, \ + { DPTX_APCALL_DID_CHANGE_LINK_CONFIG, \ + "did_change_link_config" }, \ + { DPTX_APCALL_GET_MAX_LINK_RATE, "get_max_link_rate" }, \ + { DPTX_APCALL_GET_LINK_RATE, "get_link_rate" }, \ + { DPTX_APCALL_SET_LINK_RATE, "set_link_rate" }, \ + { DPTX_APCALL_GET_ACTIVE_LANE_COUNT, \ + "get_active_lane_count" }, \ + { DPTX_APCALL_SET_ACTIVE_LANE_COUNT, \ + "set_active_lane_count" }, \ + { DPTX_APCALL_GET_SUPPORTS_DOWN_SPREAD, \ + "get_supports_downspread" }, \ + { DPTX_APCALL_GET_DOWN_SPREAD, "get_downspread" }, \ + { DPTX_APCALL_SET_DOWN_SPREAD, "set_downspread" }, \ + { DPTX_APCALL_GET_SUPPORTS_LANE_MAPPING, \ + "get_supports_lane_mapping" }, \ + { DPTX_APCALL_SET_LANE_MAP, "set_lane_map" }, \ + { DPTX_APCALL_GET_SUPPORTS_HPD, "get_supports_hpd" }, \ + { DPTX_APCALL_FORCE_HOTPLUG_DETECT, "force_hotplug_detect" }, \ + { DPTX_APCALL_INACTIVE_SINK_DETECTED, \ + "inactive_sink_detected" }, \ + { DPTX_APCALL_SET_TILED_DISPLAY_HINTS, \ + "set_tiled_display_hints" }, \ + { DPTX_APCALL_DEVICE_NOT_RESPONDING, \ + "device_not_responding" }, \ + { DPTX_APCALL_DEVICE_BUSY_TIMEOUT, "device_busy_timeout" }, \ + { DPTX_APCALL_DEVICE_NOT_STARTED, "device_not_started" }) + TRACE_EVENT(dcp_recv_msg, TP_PROTO(struct apple_dcp *dcp, u8 endpoint, u64 message), TP_ARGS(dcp, endpoint, message), @@ -263,6 +301,108 @@ TRACE_EVENT(iomfb_swap_complete_intent_gated, ) ); +DECLARE_EVENT_CLASS(iomfb_parse_mode_template, + TP_PROTO(s64 id, struct dimension *horiz, struct dimension *vert, s64 best_color_mode, bool is_virtual, s64 score), + TP_ARGS(id, horiz, vert, best_color_mode, is_virtual, score), + + TP_STRUCT__entry(__field(s64, id) + __field_struct(struct dimension, horiz) + __field_struct(struct dimension, vert) + __field(s64, best_color_mode) + __field(bool, is_virtual) + __field(s64, score)), + + TP_fast_assign(__entry->id = id; + __entry->horiz = *horiz; + __entry->vert = *vert; + __entry->best_color_mode = best_color_mode; + __entry->is_virtual = is_virtual; + __entry->score = score;), + + TP_printk("id: %lld, best_color_mode: %lld, resolution:%lldx%lld virtual: %d, score: %lld", + __entry->id, __entry->best_color_mode, + __entry->horiz.active, __entry->vert.active, + __entry->is_virtual, __entry->score)); + +DEFINE_EVENT(iomfb_parse_mode_template, iomfb_parse_mode_success, + TP_PROTO(s64 id, struct dimension *horiz, struct dimension *vert, s64 best_color_mode, bool is_virtual, s64 score), + TP_ARGS(id, horiz, vert, best_color_mode, is_virtual, score)); + +DEFINE_EVENT(iomfb_parse_mode_template, iomfb_parse_mode_fail, + TP_PROTO(s64 id, struct dimension *horiz, struct dimension *vert, s64 best_color_mode, bool is_virtual, s64 score), + TP_ARGS(id, horiz, vert, best_color_mode, is_virtual, score)); + +TRACE_EVENT(dptxport_init, TP_PROTO(struct apple_dcp *dcp, u64 unit), + TP_ARGS(dcp, unit), + + TP_STRUCT__entry(__string(devname, dev_name(dcp->dev)) + __field(u64, unit)), + + TP_fast_assign(__assign_str(devname, dev_name(dcp->dev)); + __entry->unit = unit;), + + TP_printk("%s: dptxport unit %lld initialized", __get_str(devname), + __entry->unit)); + +TRACE_EVENT( + dptxport_apcall, + TP_PROTO(struct dptx_port *dptx, int idx, size_t len), + TP_ARGS(dptx, idx, len), + + TP_STRUCT__entry(__string(devname, dev_name(dptx->service->ep->dcp->dev)) + __field(u32, unit) __field(int, idx) __field(size_t, len)), + + TP_fast_assign(__assign_str(devname, dev_name(dptx->service->ep->dcp->dev)); + __entry->unit = dptx->unit; __entry->idx = idx; __entry->len = len;), + + TP_printk("%s: dptx%d: AP Call %d (%s) with len %lu", __get_str(devname), + __entry->unit, + __entry->idx, show_dptxport_apcall(__entry->idx), __entry->len)); + +TRACE_EVENT( + dptxport_validate_connection, + TP_PROTO(struct dptx_port *dptx, u8 core, u8 atc, u8 die), + TP_ARGS(dptx, core, atc, die), + + TP_STRUCT__entry(__string(devname, dev_name(dptx->service->ep->dcp->dev)) + __field(u32, unit) __field(u8, core) __field(u8, atc) __field(u8, die)), + + TP_fast_assign(__assign_str(devname, dev_name(dptx->service->ep->dcp->dev)); + __entry->unit = dptx->unit; __entry->core = core; __entry->atc = atc; __entry->die = die;), + + TP_printk("%s: dptx%d: core %d, atc %d, die %d", __get_str(devname), + __entry->unit, __entry->core, __entry->atc, __entry->die)); + +TRACE_EVENT( + dptxport_connect, + TP_PROTO(struct dptx_port *dptx, u8 core, u8 atc, u8 die), + TP_ARGS(dptx, core, atc, die), + + TP_STRUCT__entry(__string(devname, dev_name(dptx->service->ep->dcp->dev)) + __field(u32, unit) __field(u8, core) __field(u8, atc) __field(u8, die)), + + TP_fast_assign(__assign_str(devname, dev_name(dptx->service->ep->dcp->dev)); + __entry->unit = dptx->unit; __entry->core = core; __entry->atc = atc; __entry->die = die;), + + TP_printk("%s: dptx%d: core %d, atc %d, die %d", __get_str(devname), + __entry->unit, __entry->core, __entry->atc, __entry->die)); + +TRACE_EVENT( + dptxport_call_set_link_rate, + TP_PROTO(struct dptx_port *dptx, u32 link_rate), + TP_ARGS(dptx, link_rate), + + TP_STRUCT__entry(__string(devname, dev_name(dptx->service->ep->dcp->dev)) + __field(u32, unit) + __field(u32, link_rate)), + + TP_fast_assign(__assign_str(devname, dev_name(dptx->service->ep->dcp->dev)); + __entry->unit = dptx->unit; + __entry->link_rate = link_rate;), + + TP_printk("%s: dptx%d: link rate 0x%x", __get_str(devname), __entry->unit, + __entry->link_rate)); + TRACE_EVENT(iomfb_brightness, TP_PROTO(struct apple_dcp *dcp, u32 nits), TP_ARGS(dcp, nits), From 516579eb6b87a2220b95e746b288b9260b966b54 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 12 Nov 2023 10:06:45 +0100 Subject: [PATCH 0554/1020] drm: apple: Move offsets for rt_bandwidth callback to DT The offsets differ for every DCP instance. Instead of hardcoding offsets for each SoC family offsets and calculate the instance offset move everything to the device tree. This helps multi die SoCs since there is and unexpected offset between both dies. On multi die SoCs device tree changes were necessary to avoid translating the PMGR reg via the seconds die "ranges" property. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp-internal.h | 8 ++ drivers/gpu/drm/apple/dcp.c | 122 ++++++++++++++++++++++++- drivers/gpu/drm/apple/iomfb_template.c | 51 +++++------ 3 files changed, 151 insertions(+), 30 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 849bd2937ebb9d..7fe2c3f98aa377 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -136,6 +137,13 @@ struct apple_dcp { struct resource *disp_registers[MAX_DISP_REGISTERS]; unsigned int nr_disp_registers; + struct resource disp_bw_scratch_res; + struct resource disp_bw_doorbell_res; + u32 disp_bw_scratch_index; + u32 disp_bw_scratch_offset; + u32 disp_bw_doorbell_index; + u32 disp_bw_doorbell_offset; + u32 index; /* Bitmap of memory descriptors used for mappings made by the DCP */ diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 69f118ae8b6e59..14931460a93f07 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -475,11 +476,108 @@ static int dcp_create_piodma_iommu_dev(struct apple_dcp *dcp) return ret; } +static int dcp_get_bw_scratch_reg(struct apple_dcp *dcp, u32 expected) +{ + struct of_phandle_args ph_args; + u32 addr_idx, disp_idx, offset; + int ret; + + ret = of_parse_phandle_with_args(dcp->dev->of_node, "apple,bw-scratch", + "#apple,bw-scratch-cells", 0, &ph_args); + if (ret < 0) { + dev_err(dcp->dev, "Failed to read 'apple,bw-scratch': %d\n", ret); + return ret; + } + + if (ph_args.args_count != 3) { + dev_err(dcp->dev, "Unexpected 'apple,bw-scratch' arg count %d\n", + ph_args.args_count); + ret = -EINVAL; + goto err_of_node_put; + } + + addr_idx = ph_args.args[0]; + disp_idx = ph_args.args[1]; + offset = ph_args.args[2]; + + if (disp_idx != expected || disp_idx >= MAX_DISP_REGISTERS) { + dev_err(dcp->dev, "Unexpected disp_reg value in 'apple,bw-scratch': %d\n", + disp_idx); + ret = -EINVAL; + goto err_of_node_put; + } + + ret = of_address_to_resource(ph_args.np, addr_idx, &dcp->disp_bw_scratch_res); + if (ret < 0) { + dev_err(dcp->dev, "Failed to get 'apple,bw-scratch' resource %d from %pOF\n", + addr_idx, ph_args.np); + goto err_of_node_put; + } + if (offset > resource_size(&dcp->disp_bw_scratch_res) - 4) { + ret = -EINVAL; + goto err_of_node_put; + } + + dcp->disp_registers[disp_idx] = &dcp->disp_bw_scratch_res; + dcp->disp_bw_scratch_index = disp_idx; + dcp->disp_bw_scratch_offset = offset; + ret = 0; + +err_of_node_put: + of_node_put(ph_args.np); + return ret; +} + +static int dcp_get_bw_doorbell_reg(struct apple_dcp *dcp, u32 expected) +{ + struct of_phandle_args ph_args; + u32 addr_idx, disp_idx; + int ret; + + ret = of_parse_phandle_with_args(dcp->dev->of_node, "apple,bw-doorbell", + "#apple,bw-doorbell-cells", 0, &ph_args); + if (ret < 0) { + dev_err(dcp->dev, "Failed to read 'apple,bw-doorbell': %d\n", ret); + return ret; + } + + if (ph_args.args_count != 2) { + dev_err(dcp->dev, "Unexpected 'apple,bw-doorbell' arg count %d\n", + ph_args.args_count); + ret = -EINVAL; + goto err_of_node_put; + } + + addr_idx = ph_args.args[0]; + disp_idx = ph_args.args[1]; + + if (disp_idx != expected || disp_idx >= MAX_DISP_REGISTERS) { + dev_err(dcp->dev, "Unexpected disp_reg value in 'apple,bw-doorbell': %d\n", + disp_idx); + ret = -EINVAL; + goto err_of_node_put; + } + + ret = of_address_to_resource(ph_args.np, addr_idx, &dcp->disp_bw_doorbell_res); + if (ret < 0) { + dev_err(dcp->dev, "Failed to get 'apple,bw-doorbell' resource %d from %pOF\n", + addr_idx, ph_args.np); + goto err_of_node_put; + } + dcp->disp_bw_doorbell_index = disp_idx; + dcp->disp_registers[disp_idx] = &dcp->disp_bw_doorbell_res; + ret = 0; + +err_of_node_put: + of_node_put(ph_args.np); + return ret; +} + static int dcp_get_disp_regs(struct apple_dcp *dcp) { struct platform_device *pdev = to_platform_device(dcp->dev); int count = pdev->num_resources - 1; - int i; + int i, ret; if (count <= 0 || count > MAX_DISP_REGISTERS) return -EINVAL; @@ -489,6 +587,20 @@ static int dcp_get_disp_regs(struct apple_dcp *dcp) platform_get_resource(pdev, IORESOURCE_MEM, 1 + i); } + /* load pmgr bandwidth scratch resource and offset */ + ret = dcp_get_bw_scratch_reg(dcp, count); + if (ret < 0) + return ret; + count += 1; + + /* load pmgr bandwidth doorbell resource if present (only on t8103) */ + if (of_property_present(dcp->dev->of_node, "apple,bw-doorbell")) { + ret = dcp_get_bw_doorbell_reg(dcp, count); + if (ret < 0) + return ret; + count += 1; + } + dcp->nr_disp_registers = count; return 0; } @@ -727,6 +839,14 @@ static int dcp_platform_probe(struct platform_device *pdev) if (fw_compat == DCP_FIRMWARE_UNKNOWN) return -ENODEV; + /* Check for "apple,bw-scratch" to avoid probing appledrm with outdated + * device trees. This prevents replacing simpledrm and ending up without + * display. + */ + if (!of_property_present(dev->of_node, "apple,bw-scratch")) + return dev_err_probe(dev, -ENODEV, "Incompatible devicetree! " + "Use devicetree matching this kernel.\n"); + dcp = devm_kzalloc(dev, sizeof(*dcp), GFP_KERNEL); if (!dcp) return -ENOMEM; diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index ea9ed8f967ebad..0f50c1d873f303 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -33,11 +33,7 @@ #include "version_utils.h" /* Register defines used in bandwidth setup structure */ -#define REG_SCRATCH (0x14) -#define REG_SCRATCH_T600X (0x988) -#define REG_SCRATCH_T602X (0x1208) -#define REG_DOORBELL (0x0) -#define REG_DOORBELL_BIT (2) +#define REG_DOORBELL_BIT(idx) (2 + (idx)) struct dcp_wait_cookie { struct kref refcount; @@ -665,34 +661,31 @@ static struct dcp_allocate_bandwidth_resp dcpep_cb_allocate_bandwidth(struct app static struct dcp_rt_bandwidth dcpep_cb_rt_bandwidth(struct apple_dcp *dcp) { - if (dcp->disp_registers[5] && dcp->disp_registers[6]) { - return (struct dcp_rt_bandwidth){ - .reg_scratch = - dcp->disp_registers[5]->start + REG_SCRATCH, - .reg_doorbell = - dcp->disp_registers[6]->start + REG_DOORBELL, - .doorbell_bit = REG_DOORBELL_BIT, - - .padding[3] = 0x4, // XXX: required by 11.x firmware - }; - } else if (dcp->disp_registers[4]) { - u32 offset = REG_SCRATCH_T600X; - if (of_device_is_compatible(dcp->dev->of_node, "apple,t6020-dcp")) - offset = REG_SCRATCH_T602X; - - return (struct dcp_rt_bandwidth){ - .reg_scratch = dcp->disp_registers[4]->start + - offset, - .reg_doorbell = 0, - .doorbell_bit = 0, - }; - } else { - return (struct dcp_rt_bandwidth){ + struct dcp_rt_bandwidth rt_bw = (struct dcp_rt_bandwidth){ .reg_scratch = 0, .reg_doorbell = 0, .doorbell_bit = 0, - }; + }; + + if (dcp->disp_bw_scratch_index) { + u32 offset = dcp->disp_bw_scratch_offset; + u32 index = dcp->disp_bw_scratch_index; + rt_bw.reg_scratch = dcp->disp_registers[index]->start + offset; } + + if (dcp->disp_bw_doorbell_index) { + u32 index = dcp->disp_bw_doorbell_index; + rt_bw.reg_doorbell = dcp->disp_registers[index]->start; + rt_bw.doorbell_bit = REG_DOORBELL_BIT(dcp->index); + /* + * This is most certainly not padding. t8103-dcp crashes without + * setting this immediately during modeset on 12.3 and 13.5 + * firmware. + */ + rt_bw.padding[3] = 0x4; + } + + return rt_bw; } static struct dcp_set_frame_sync_props_resp From b7fa03fd3e07f38adabef8fda1f61de9df0a7b1e Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 17 Aug 2023 23:52:39 +0200 Subject: [PATCH 0555/1020] drm: apple: iomfb: Do not match/create PMU service for dcpext Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp-internal.h | 3 +++ drivers/gpu/drm/apple/dcp.c | 2 ++ drivers/gpu/drm/apple/iomfb_template.c | 16 ++++++++++++++++ drivers/gpu/drm/apple/iomfb_v12_3.c | 2 +- drivers/gpu/drm/apple/iomfb_v13_3.c | 2 +- 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 7fe2c3f98aa377..64025d23282d1c 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -183,6 +183,9 @@ struct apple_dcp { /* clear all surfaces on init */ bool surfaces_cleared; + /* is dcpext / requires dptx */ + bool is_dptx; + /* Modes valid for the connected display */ struct dcp_display_mode *modes; unsigned int nr_modes; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 14931460a93f07..ac69d65297a148 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -700,6 +700,8 @@ static int dcp_comp_bind(struct device *dev, struct device *main, void *data) if (IS_ERR(dcp->coproc_reg)) return PTR_ERR(dcp->coproc_reg); + dcp->is_dptx = dcp->phy != NULL; + of_property_read_u32(dev->of_node, "apple,dcp-index", &dcp->index); of_property_read_u32(dev->of_node, "apple,dptx-phy", diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 0f50c1d873f303..4fe22e2f7d496d 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -135,6 +135,10 @@ static void complete_vi_set_temperature_hint(struct apple_dcp *dcp, void *out, v static bool iomfbep_cb_match_pmu_service(struct apple_dcp *dcp, int tag, void *out, void *in) { trace_iomfb_callback(dcp, tag, __func__); + + if (dcp->is_dptx) + return true; + iomfb_a358_vi_set_temperature_hint(dcp, false, complete_vi_set_temperature_hint, NULL); @@ -158,6 +162,12 @@ static bool iomfbep_cb_match_pmu_service_2(struct apple_dcp *dcp, int tag, void { trace_iomfb_callback(dcp, tag, __func__); + if (dcp->is_dptx) { + u8 *ret = out; + ret[0] = 1; + return true; + } + iomfb_a131_pmu_service_matched(dcp, false, complete_pmu_service_matched, out); @@ -1044,6 +1054,11 @@ dcpep_cb_get_tiling_state(struct apple_dcp *dcp, }; } +static u8 dcpep_cb_create_pmu_service(struct apple_dcp *dcp) +{ + return !dcp->is_dptx; +} + static u8 dcpep_cb_create_backlight_service(struct apple_dcp *dcp) { return dcp_has_panel(dcp); @@ -1101,6 +1116,7 @@ TRAMPOLINE_IN(trampoline_pr_publish, iomfb_cb_pr_publish, struct iomfb_property); TRAMPOLINE_INOUT(trampoline_get_tiling_state, dcpep_cb_get_tiling_state, struct dcpep_get_tiling_state_req, struct dcpep_get_tiling_state_resp); +TRAMPOLINE_OUT(trampoline_create_pmu_service, dcpep_cb_create_pmu_service, u8); TRAMPOLINE_OUT(trampoline_create_backlight_service, dcpep_cb_create_backlight_service, u8); /* diff --git a/drivers/gpu/drm/apple/iomfb_v12_3.c b/drivers/gpu/drm/apple/iomfb_v12_3.c index abcd1e4aab3ff8..8b4d87ad9012bd 100644 --- a/drivers/gpu/drm/apple/iomfb_v12_3.c +++ b/drivers/gpu/drm/apple/iomfb_v12_3.c @@ -48,7 +48,7 @@ static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = { [106] = trampoline_nop, /* remove_property */ [107] = trampoline_true, /* create_provider_service */ [108] = trampoline_true, /* create_product_service */ - [109] = trampoline_true, /* create_pmu_service */ + [109] = trampoline_create_pmu_service, [110] = trampoline_true, /* create_iomfb_service */ [111] = trampoline_create_backlight_service, [116] = dcpep_cb_boot_1, diff --git a/drivers/gpu/drm/apple/iomfb_v13_3.c b/drivers/gpu/drm/apple/iomfb_v13_3.c index 9c692ba3c81b92..0689c0a593f784 100644 --- a/drivers/gpu/drm/apple/iomfb_v13_3.c +++ b/drivers/gpu/drm/apple/iomfb_v13_3.c @@ -50,7 +50,7 @@ static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = { [107] = trampoline_nop, /* remove_property */ [108] = trampoline_true, /* create_provider_service */ [109] = trampoline_true, /* create_product_service */ - [110] = trampoline_true, /* create_pmu_service */ + [110] = trampoline_create_pmu_service, [111] = trampoline_true, /* create_iomfb_service */ [112] = trampoline_create_backlight_service, [113] = trampoline_true, /* create_nvram_servce? */ From 9dc71c0134cb630b434ded1536f5c1e75d8e3547 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 9 Apr 2023 22:44:35 +0200 Subject: [PATCH 0556/1020] drm: apple: afk: Adapt to macOS 13.3 firmware Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/afk.c | 9 ++++++--- drivers/gpu/drm/apple/afk.h | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/apple/afk.c b/drivers/gpu/drm/apple/afk.c index 14b544055713ba..69e732f9f3532a 100644 --- a/drivers/gpu/drm/apple/afk.c +++ b/drivers/gpu/drm/apple/afk.c @@ -493,7 +493,7 @@ static void afk_recv_handle(struct apple_dcp_afkep *ep, u32 channel, u32 type, service = afk_epic_find_service(ep, channel); if (!service) { - if (type != EPIC_TYPE_NOTIFY) { + if (type != EPIC_TYPE_NOTIFY && type != EPIC_TYPE_REPLY) { dev_err(ep->dcp->dev, "AFK[ep:%02x]: expected notify but got 0x%x on channel %d\n", ep->endpoint, type, channel); @@ -805,12 +805,15 @@ int afk_send_epic(struct apple_dcp_afkep *ep, u32 channel, u16 tag, eshdr = ep->txbfr.buf + wptr; memset(eshdr, 0, sizeof(*eshdr)); eshdr->length = cpu_to_le32(payload_len); - eshdr->version = 3; + eshdr->version = 4; eshdr->category = ecat; eshdr->type = cpu_to_le16(stype); eshdr->timestamp = cpu_to_le64(0); eshdr->tag = cpu_to_le16(tag); - eshdr->inline_len = cpu_to_le16(0); + if (ecat == EPIC_CAT_REPLY) + eshdr->inline_len = cpu_to_le16(payload_len - 4); + else + eshdr->inline_len = cpu_to_le16(0); wptr += sizeof(*eshdr); memcpy(ep->txbfr.buf + wptr, payload, payload_len); diff --git a/drivers/gpu/drm/apple/afk.h b/drivers/gpu/drm/apple/afk.h index fe4ed35159ace0..1fdb4100352b25 100644 --- a/drivers/gpu/drm/apple/afk.h +++ b/drivers/gpu/drm/apple/afk.h @@ -106,6 +106,8 @@ struct epic_cmd { __le64 txbuf; __le32 rxlen; __le32 txlen; + u8 rxcookie; + u8 txcookie; } __attribute__((packed)); struct epic_service_call { From d6bf1655bb5d7464bb25cb9aa7cff260de0c26e6 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Fri, 28 Apr 2023 22:24:59 +0200 Subject: [PATCH 0557/1020] drm: apple: dptx: Port APCALL to macOS 13.3 firmware The 13.3 firmware has an additional get_max_lane_count call inserted with ID 10. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dptxep.c | 23 +++++++++++++++++++++++ drivers/gpu/drm/apple/dptxep.h | 29 +++++++++++++++-------------- drivers/gpu/drm/apple/trace.h | 2 ++ 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/apple/dptxep.c b/drivers/gpu/drm/apple/dptxep.c index e929e93cdbaef3..aa75aad225c19b 100644 --- a/drivers/gpu/drm/apple/dptxep.c +++ b/drivers/gpu/drm/apple/dptxep.c @@ -28,6 +28,13 @@ struct dptxport_apcall_link_rate { u8 _unk1[12]; } __attribute__((packed)); +struct dptxport_apcall_lane_count { + __le32 retcode; + u8 _unk0[12]; + __le64 lane_count; + u8 _unk1[8]; +} __attribute__((packed)); + struct dptxport_apcall_get_support { __le32 retcode; u8 _unk0[12]; @@ -157,6 +164,20 @@ static int dptxport_call_get_max_link_rate(struct apple_epic_service *service, return 0; } +static int dptxport_call_get_max_lane_count(struct apple_epic_service *service, + void *reply_, size_t reply_size) +{ + struct dptxport_apcall_lane_count *reply = reply_; + + if (reply_size < sizeof(*reply)) + return -EINVAL; + + reply->retcode = cpu_to_le32(0); + reply->lane_count = cpu_to_le64(4); + + return 0; +} + static int dptxport_call_get_link_rate(struct apple_epic_service *service, void *reply_, size_t reply_size) { @@ -310,6 +331,8 @@ static int dptxport_call(struct apple_epic_service *service, u32 idx, case DPTX_APCALL_SET_LINK_RATE: return dptxport_call_set_link_rate(service, data, data_size, reply, reply_size); + case DPTX_APCALL_GET_MAX_LANE_COUNT: + return dptxport_call_get_max_lane_count(service, reply, reply_size); case DPTX_APCALL_GET_SUPPORTS_HPD: return dptxport_call_get_supports_hpd(service, reply, reply_size); diff --git a/drivers/gpu/drm/apple/dptxep.h b/drivers/gpu/drm/apple/dptxep.h index efd1d5005f56da..8f0483e7030b7a 100644 --- a/drivers/gpu/drm/apple/dptxep.h +++ b/drivers/gpu/drm/apple/dptxep.h @@ -15,20 +15,21 @@ enum dptx_apcall { DPTX_APCALL_GET_MAX_LINK_RATE = 7, DPTX_APCALL_GET_LINK_RATE = 8, DPTX_APCALL_SET_LINK_RATE = 9, - DPTX_APCALL_GET_ACTIVE_LANE_COUNT = 10, - DPTX_APCALL_SET_ACTIVE_LANE_COUNT = 11, - DPTX_APCALL_GET_SUPPORTS_DOWN_SPREAD = 12, - DPTX_APCALL_GET_DOWN_SPREAD = 13, - DPTX_APCALL_SET_DOWN_SPREAD = 14, - DPTX_APCALL_GET_SUPPORTS_LANE_MAPPING = 15, - DPTX_APCALL_SET_LANE_MAP = 16, - DPTX_APCALL_GET_SUPPORTS_HPD = 17, - DPTX_APCALL_FORCE_HOTPLUG_DETECT = 18, - DPTX_APCALL_INACTIVE_SINK_DETECTED = 19, - DPTX_APCALL_SET_TILED_DISPLAY_HINTS = 20, - DPTX_APCALL_DEVICE_NOT_RESPONDING = 21, - DPTX_APCALL_DEVICE_BUSY_TIMEOUT = 22, - DPTX_APCALL_DEVICE_NOT_STARTED = 23, + DPTX_APCALL_GET_MAX_LANE_COUNT = 10, + DPTX_APCALL_GET_ACTIVE_LANE_COUNT = 11, + DPTX_APCALL_SET_ACTIVE_LANE_COUNT = 12, + DPTX_APCALL_GET_SUPPORTS_DOWN_SPREAD = 13, + DPTX_APCALL_GET_DOWN_SPREAD = 14, + DPTX_APCALL_SET_DOWN_SPREAD = 15, + DPTX_APCALL_GET_SUPPORTS_LANE_MAPPING = 16, + DPTX_APCALL_SET_LANE_MAP = 17, + DPTX_APCALL_GET_SUPPORTS_HPD = 18, + DPTX_APCALL_FORCE_HOTPLUG_DETECT = 19, + DPTX_APCALL_INACTIVE_SINK_DETECTED = 20, + DPTX_APCALL_SET_TILED_DISPLAY_HINTS = 21, + DPTX_APCALL_DEVICE_NOT_RESPONDING = 22, + DPTX_APCALL_DEVICE_BUSY_TIMEOUT = 23, + DPTX_APCALL_DEVICE_NOT_STARTED = 24, }; #define DCPDPTX_REMOTE_PORT_CORE GENMASK(3, 0) diff --git a/drivers/gpu/drm/apple/trace.h b/drivers/gpu/drm/apple/trace.h index 9858305c4068c7..a9912c7502082b 100644 --- a/drivers/gpu/drm/apple/trace.h +++ b/drivers/gpu/drm/apple/trace.h @@ -52,6 +52,8 @@ { DPTX_APCALL_GET_MAX_LINK_RATE, "get_max_link_rate" }, \ { DPTX_APCALL_GET_LINK_RATE, "get_link_rate" }, \ { DPTX_APCALL_SET_LINK_RATE, "set_link_rate" }, \ + { DPTX_APCALL_GET_MAX_LANE_COUNT, \ + "get_max_lane_count" }, \ { DPTX_APCALL_GET_ACTIVE_LANE_COUNT, \ "get_active_lane_count" }, \ { DPTX_APCALL_SET_ACTIVE_LANE_COUNT, \ From b339ed18fc734034430d7b5c81f25e9601b2ce12 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Fri, 18 Aug 2023 00:05:15 +0200 Subject: [PATCH 0558/1020] drm: apple: dptx: port interface to macOS 13.5 firmware Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dptxep.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/apple/dptxep.c b/drivers/gpu/drm/apple/dptxep.c index aa75aad225c19b..98e922cfcda634 100644 --- a/drivers/gpu/drm/apple/dptxep.c +++ b/drivers/gpu/drm/apple/dptxep.c @@ -64,7 +64,7 @@ int dptxport_validate_connection(struct apple_epic_service *service, u8 core, cmd.target = cpu_to_le32(target); cmd.unk = cpu_to_le32(0x100); - ret = afk_service_call(service, 0, 14, &cmd, sizeof(cmd), 40, &resp, + ret = afk_service_call(service, 0, 12, &cmd, sizeof(cmd), 40, &resp, sizeof(resp), 40); if (ret) return ret; @@ -92,7 +92,7 @@ int dptxport_connect(struct apple_epic_service *service, u8 core, u8 atc, cmd.target = cpu_to_le32(target); cmd.unk = cpu_to_le32(0x100); - ret = afk_service_call(service, 0, 13, &cmd, sizeof(cmd), 24, &resp, + ret = afk_service_call(service, 0, 11, &cmd, sizeof(cmd), 24, &resp, sizeof(resp), 24); if (ret) return ret; @@ -107,12 +107,12 @@ int dptxport_connect(struct apple_epic_service *service, u8 core, u8 atc, int dptxport_request_display(struct apple_epic_service *service) { - return afk_service_call(service, 0, 8, NULL, 0, 16, NULL, 0, 16); + return afk_service_call(service, 0, 6, NULL, 0, 16, NULL, 0, 16); } int dptxport_release_display(struct apple_epic_service *service) { - return afk_service_call(service, 0, 9, NULL, 0, 16, NULL, 0, 16); + return afk_service_call(service, 0, 7, NULL, 0, 16, NULL, 0, 16); } int dptxport_set_hpd(struct apple_epic_service *service, bool hpd) @@ -125,7 +125,7 @@ int dptxport_set_hpd(struct apple_epic_service *service, bool hpd) if (hpd) cmd.unk = cpu_to_le32(1); - ret = afk_service_call(service, 8, 10, &cmd, sizeof(cmd), 12, &resp, + ret = afk_service_call(service, 8, 8, &cmd, sizeof(cmd), 12, &resp, sizeof(resp), 12); if (ret) return ret; From 7f60a77f4ac44fbd3d0e54947b19281504856f26 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 6 Nov 2023 23:36:20 +0100 Subject: [PATCH 0559/1020] drm: apple: dptx: Add set_active_lanes APCALL Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dptxep.c | 55 ++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/drivers/gpu/drm/apple/dptxep.c b/drivers/gpu/drm/apple/dptxep.c index 98e922cfcda634..1461f15b7e1cdd 100644 --- a/drivers/gpu/drm/apple/dptxep.c +++ b/drivers/gpu/drm/apple/dptxep.c @@ -35,6 +35,13 @@ struct dptxport_apcall_lane_count { u8 _unk1[8]; } __attribute__((packed)); +struct dptxport_apcall_set_active_lane_count { + __le32 retcode; + u8 _unk0[12]; + __le64 lane_count; + u8 _unk1[8]; +} __packed; + struct dptxport_apcall_get_support { __le32 retcode; u8 _unk0[12]; @@ -178,6 +185,51 @@ static int dptxport_call_get_max_lane_count(struct apple_epic_service *service, return 0; } +static int dptxport_call_set_active_lane_count(struct apple_epic_service *service, + const void *data, size_t data_size, + void *reply_, size_t reply_size) +{ + struct dptx_port *dptx = service->cookie; + const struct dptxport_apcall_set_active_lane_count *request = data; + struct dptxport_apcall_set_active_lane_count *reply = reply_; + int ret = 0; + int retcode = 0; + + if (reply_size < sizeof(*reply)) + return -1; + if (data_size < sizeof(*request)) + return -1; + + u64 lane_count = cpu_to_le64(request->lane_count); + + switch (lane_count) { + case 0 ... 2: + case 4: + dptx->phy_ops.dp.lanes = lane_count; + dptx->phy_ops.dp.set_lanes = 1; + break; + default: + dev_err(service->ep->dcp->dev, "set_active_lane_count: invalid lane count:%llu\n", lane_count); + retcode = 1; + lane_count = 0; + break; + } + + if (dptx->phy_ops.dp.set_lanes) { + if (dptx->atcphy) { + ret = phy_configure(dptx->atcphy, &dptx->phy_ops); + if (ret) + return ret; + } + dptx->phy_ops.dp.set_lanes = 0; + } + + reply->retcode = cpu_to_le32(retcode); + reply->lane_count = cpu_to_le64(lane_count); + + return ret; +} + static int dptxport_call_get_link_rate(struct apple_epic_service *service, void *reply_, size_t reply_size) { @@ -333,6 +385,9 @@ static int dptxport_call(struct apple_epic_service *service, u32 idx, reply, reply_size); case DPTX_APCALL_GET_MAX_LANE_COUNT: return dptxport_call_get_max_lane_count(service, reply, reply_size); + case DPTX_APCALL_SET_ACTIVE_LANE_COUNT: + return dptxport_call_set_active_lane_count(service, data, data_size, + reply, reply_size); case DPTX_APCALL_GET_SUPPORTS_HPD: return dptxport_call_get_supports_hpd(service, reply, reply_size); From 3b31c82b289abc4b7e858d76eb16356e0731b7a1 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 6 Nov 2023 23:37:27 +0100 Subject: [PATCH 0560/1020] drm: apple: dptx: Add DPTX_APCALL_ACTIVATE Configures the phy to the correct dcp(ext) source by abusing submode in the phy_set_mode_ext() call. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dptxep.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/dptxep.c b/drivers/gpu/drm/apple/dptxep.c index 1461f15b7e1cdd..92883be3a16cbf 100644 --- a/drivers/gpu/drm/apple/dptxep.c +++ b/drivers/gpu/drm/apple/dptxep.c @@ -363,6 +363,24 @@ dptxport_call_get_supports_downspread(struct apple_epic_service *service, return 0; } +static int +dptxport_call_activate(struct apple_epic_service *service, + const void *data, size_t data_size, + void *reply, size_t reply_size) +{ + struct dptx_port *dptx = service->cookie; + const struct apple_dcp *dcp = service->ep->dcp; + + // TODO: hack, use phy_set_mode to select the correct DCP(EXT) input + phy_set_mode_ext(dptx->atcphy, PHY_MODE_DP, dcp->index); + + memcpy(reply, data, min(reply_size, data_size)); + if (reply_size > 4) + memset(reply, 0, 4); + + return 0; +} + static int dptxport_call(struct apple_epic_service *service, u32 idx, const void *data, size_t data_size, void *reply, size_t reply_size) @@ -397,13 +415,15 @@ static int dptxport_call(struct apple_epic_service *service, u32 idx, case DPTX_APCALL_GET_MAX_DRIVE_SETTINGS: return dptxport_call_get_max_drive_settings(service, reply, reply_size); + case DPTX_APCALL_ACTIVATE: + return dptxport_call_activate(service, data, data_size, + reply, reply_size); default: /* just try to ACK and hope for the best... */ dev_info(service->ep->dcp->dev, "DPTXPort: acking unhandled call %u\n", idx); fallthrough; /* we can silently ignore and just ACK these calls */ - case DPTX_APCALL_ACTIVATE: case DPTX_APCALL_DEACTIVATE: case DPTX_APCALL_SET_DRIVE_SETTINGS: case DPTX_APCALL_GET_DRIVE_SETTINGS: From b88ab6b5447ff893957163835013df02f3194b1a Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 6 Nov 2023 23:44:08 +0100 Subject: [PATCH 0561/1020] drm: apple: dptx: Adapt dptxport_connect() to observed behavior Adapt to behavior seen on j474s with dcp0 driving lpdptx-phy and dp2hdmi using the macOS 13.5 firmware. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dptxep.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/apple/dptxep.c b/drivers/gpu/drm/apple/dptxep.c index 92883be3a16cbf..08f683cc220b94 100644 --- a/drivers/gpu/drm/apple/dptxep.c +++ b/drivers/gpu/drm/apple/dptxep.c @@ -89,6 +89,7 @@ int dptxport_connect(struct apple_epic_service *service, u8 core, u8 atc, { struct dptx_port *dptx = service->cookie; struct dcpdptx_connection_cmd cmd, resp; + u32 unk_field = 0x0; // seen as 0x100 under some conditions int ret; u32 target = FIELD_PREP(DCPDPTX_REMOTE_PORT_CORE, core) | FIELD_PREP(DCPDPTX_REMOTE_PORT_ATC, atc) | @@ -98,7 +99,7 @@ int dptxport_connect(struct apple_epic_service *service, u8 core, u8 atc, trace_dptxport_connect(dptx, core, atc, die); cmd.target = cpu_to_le32(target); - cmd.unk = cpu_to_le32(0x100); + cmd.unk = cpu_to_le32(unk_field); ret = afk_service_call(service, 0, 11, &cmd, sizeof(cmd), 24, &resp, sizeof(resp), 24); if (ret) @@ -106,8 +107,9 @@ int dptxport_connect(struct apple_epic_service *service, u8 core, u8 atc, if (le32_to_cpu(resp.target) != target) return -EINVAL; - if (le32_to_cpu(resp.unk) != 0x100) - return -EINVAL; + if (le32_to_cpu(resp.unk) != unk_field) + dev_notice(service->ep->dcp->dev, "unexpected unk field in reply: 0x%x (0x%x)\n", + le32_to_cpu(resp.unk), unk_field); return 0; } From 4324b8d2621d940721ecf67d664169c3928566a4 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 16 Nov 2023 19:38:49 +0900 Subject: [PATCH 0562/1020] drm: apple: afk: Clear commands before sending them Signed-off-by: Hector Martin --- drivers/gpu/drm/apple/afk.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/apple/afk.c b/drivers/gpu/drm/apple/afk.c index 69e732f9f3532a..d0c36f476a6c03 100644 --- a/drivers/gpu/drm/apple/afk.c +++ b/drivers/gpu/drm/apple/afk.c @@ -859,6 +859,7 @@ int afk_send_command(struct apple_epic_service *service, u8 type, memcpy(txbuf, payload, payload_len); + memset(&cmd, 0, sizeof(cmd)); cmd.retcode = cpu_to_le32(0); cmd.rxbuf = cpu_to_le64(rxbuf_dma); cmd.rxlen = cpu_to_le32(output_len); @@ -949,6 +950,8 @@ int afk_service_call(struct apple_epic_service *service, u16 group, u32 command, return -ENOMEM; call = bfr; + + memset(call, 0, sizeof(*call)); call->group = cpu_to_le16(group); call->command = cpu_to_le32(command); call->data_len = cpu_to_le32(data_len + data_pad); From 0ec8208d05327c1bbc893ced1f5a275aa2adb346 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 17 Nov 2023 00:02:27 +0900 Subject: [PATCH 0563/1020] drm: apple: Fix missing unlock path in dcp_dptx_connect Signed-off-by: Hector Martin --- drivers/gpu/drm/apple/dcp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index ac69d65297a148..ffe5221ee3ab25 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -269,12 +269,14 @@ static int dcp_dptx_connect(struct apple_dcp *dcp, u32 port) } if (dcp->dptxport[port].connected) - return 0; + goto ret; dcp->dptxport[port].atcphy = dcp->phy; dptxport_connect(dcp->dptxport[port].service, 0, dcp->dptx_phy, dcp->dptx_die); dptxport_request_display(dcp->dptxport[port].service); dcp->dptxport[port].connected = true; + +ret: mutex_unlock(&dcp->hpd_mutex); return 0; From e0d1d0046cc1c0be88ec010b0c0077c745c2fe4f Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 17 Nov 2023 00:03:36 +0900 Subject: [PATCH 0564/1020] drm: apple: dptxep: Fix reply size check Signed-off-by: Hector Martin --- drivers/gpu/drm/apple/dptxep.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/dptxep.c b/drivers/gpu/drm/apple/dptxep.c index 08f683cc220b94..9b838468750e89 100644 --- a/drivers/gpu/drm/apple/dptxep.c +++ b/drivers/gpu/drm/apple/dptxep.c @@ -377,7 +377,7 @@ dptxport_call_activate(struct apple_epic_service *service, phy_set_mode_ext(dptx->atcphy, PHY_MODE_DP, dcp->index); memcpy(reply, data, min(reply_size, data_size)); - if (reply_size > 4) + if (reply_size >= 4) memset(reply, 0, 4); return 0; @@ -430,7 +430,7 @@ static int dptxport_call(struct apple_epic_service *service, u32 idx, case DPTX_APCALL_SET_DRIVE_SETTINGS: case DPTX_APCALL_GET_DRIVE_SETTINGS: memcpy(reply, data, min(reply_size, data_size)); - if (reply_size > 4) + if (reply_size >= 4) memset(reply, 0, 4); return 0; } From fff1753dea82b8ee0dad82dab2ec774eae098001 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Fri, 17 Nov 2023 00:03:51 +0900 Subject: [PATCH 0565/1020] drm: apple: dptxep: Implement drive settings stuff Just in case, for consistency with macOS. Signed-off-by: Hector Martin --- drivers/gpu/drm/apple/dptxep.c | 75 +++++++++++++++++++++++++++++++++- drivers/gpu/drm/apple/dptxep.h | 1 + 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/dptxep.c b/drivers/gpu/drm/apple/dptxep.c index 9b838468750e89..bb52acf3685515 100644 --- a/drivers/gpu/drm/apple/dptxep.c +++ b/drivers/gpu/drm/apple/dptxep.c @@ -56,6 +56,18 @@ struct dptxport_apcall_max_drive_settings { u8 _unk1[8]; }; +struct dptxport_apcall_drive_settings { + __le32 retcode; + u8 _unk0[12]; + __le32 unk1; + __le32 unk2; + __le32 unk3; + __le32 unk4; + __le32 unk5; + __le32 unk6; + __le32 unk7; +}; + int dptxport_validate_connection(struct apple_epic_service *service, u8 core, u8 atc, u8 die) { @@ -159,6 +171,61 @@ dptxport_call_get_max_drive_settings(struct apple_epic_service *service, return 0; } +static int +dptxport_call_get_drive_settings(struct apple_epic_service *service, + const void *request_, size_t request_size, + void *reply_, size_t reply_size) +{ + struct dptx_port *dptx = service->cookie; + const struct dptxport_apcall_drive_settings *request = request_; + struct dptxport_apcall_drive_settings *reply = reply_; + + if (reply_size < sizeof(*reply) || request_size < sizeof(*request)) + return -EINVAL; + + *reply = *request; + + /* Clear the rest of the buffer */ + memset(reply_ + sizeof(*reply), 0, reply_size - sizeof(*reply)); + + if (reply->retcode != 4) + dev_err(service->ep->dcp->dev, + "get_drive_settings: unexpected retcode %d\n", + reply->retcode); + + reply->retcode = 4; /* Should already be 4? */ + reply->unk5 = dptx->drive_settings[0]; + reply->unk6 = 0; + reply->unk7 = dptx->drive_settings[1]; + + return 0; +} + +static int +dptxport_call_set_drive_settings(struct apple_epic_service *service, + const void *request_, size_t request_size, + void *reply_, size_t reply_size) +{ + struct dptx_port *dptx = service->cookie; + const struct dptxport_apcall_drive_settings *request = request_; + struct dptxport_apcall_drive_settings *reply = reply_; + + if (reply_size < sizeof(*reply) || request_size < sizeof(*request)) + return -EINVAL; + + *reply = *request; + reply->retcode = cpu_to_le32(0); + + dev_info(service->ep->dcp->dev, "set_drive_settings: %d:%d:%d:%d:%d:%d:%d\n", + request->unk1, request->unk2, request->unk3, request->unk4, + request->unk5, request->unk6, request->unk7); + + dptx->drive_settings[0] = reply->unk5; + dptx->drive_settings[1] = reply->unk7; + + return 0; +} + static int dptxport_call_get_max_link_rate(struct apple_epic_service *service, void *reply_, size_t reply_size) { @@ -417,6 +484,12 @@ static int dptxport_call(struct apple_epic_service *service, u32 idx, case DPTX_APCALL_GET_MAX_DRIVE_SETTINGS: return dptxport_call_get_max_drive_settings(service, reply, reply_size); + case DPTX_APCALL_GET_DRIVE_SETTINGS: + return dptxport_call_get_drive_settings(service, data, data_size, + reply, reply_size); + case DPTX_APCALL_SET_DRIVE_SETTINGS: + return dptxport_call_set_drive_settings(service, data, data_size, + reply, reply_size); case DPTX_APCALL_ACTIVATE: return dptxport_call_activate(service, data, data_size, reply, reply_size); @@ -427,8 +500,6 @@ static int dptxport_call(struct apple_epic_service *service, u32 idx, fallthrough; /* we can silently ignore and just ACK these calls */ case DPTX_APCALL_DEACTIVATE: - case DPTX_APCALL_SET_DRIVE_SETTINGS: - case DPTX_APCALL_GET_DRIVE_SETTINGS: memcpy(reply, data, min(reply_size, data_size)); if (reply_size >= 4) memset(reply, 0, 4); diff --git a/drivers/gpu/drm/apple/dptxep.h b/drivers/gpu/drm/apple/dptxep.h index 8f0483e7030b7a..481ebbc97bf38d 100644 --- a/drivers/gpu/drm/apple/dptxep.h +++ b/drivers/gpu/drm/apple/dptxep.h @@ -55,6 +55,7 @@ struct dptx_port { struct phy *atcphy; struct mux_control *mux; u32 link_rate, pending_link_rate; + u32 drive_settings[2]; }; int dptxport_validate_connection(struct apple_epic_service *service, u8 core, From 6adb4fa0c4212dcf5c3bf5562ae7cca4cf86a8cb Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 19 Nov 2023 16:28:06 +0900 Subject: [PATCH 0566/1020] fixup! drm/apple: Add support for the macOS 13.2 DCP firmware --- drivers/gpu/drm/apple/version_utils.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/apple/version_utils.h b/drivers/gpu/drm/apple/version_utils.h index c85baea729414d..5a33ce1db61c47 100644 --- a/drivers/gpu/drm/apple/version_utils.h +++ b/drivers/gpu/drm/apple/version_utils.h @@ -5,6 +5,7 @@ #define __APPLE_VERSION_UTILS_H__ #include +#include #define DCP_FW_UNION(u) (u).DCP_FW #define DCP_FW_SUFFIX CONCATENATE(_, DCP_FW) From dd21e1bbb466f36dacbd09b0916e8a7d8532523f Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Wed, 22 Nov 2023 13:59:35 +0900 Subject: [PATCH 0567/1020] drm/apple: Add missing sound Kconfig dependencies Signed-off-by: Asahi Lina --- drivers/gpu/drm/apple/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/apple/Kconfig b/drivers/gpu/drm/apple/Kconfig index f0504641b5f641..fe69b04a912f93 100644 --- a/drivers/gpu/drm/apple/Kconfig +++ b/drivers/gpu/drm/apple/Kconfig @@ -3,10 +3,12 @@ config DRM_APPLE tristate "DRM Support for Apple display controllers" depends on DRM && OF && ARM64 depends on ARCH_APPLE || COMPILE_TEST + depends on SND select DRM_KMS_HELPER select DRM_KMS_DMA_HELPER select DRM_GEM_DMA_HELPER select VIDEOMODE_HELPERS select MULTIPLEXER + select SND_PCM help Say Y if you have an Apple Silicon chipset. From 7b83dcfdaccbfe0180e05684b0105f8882e907e1 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 20 Nov 2023 22:43:48 +0100 Subject: [PATCH 0568/1020] drm: apple: HACK: Do not delete piodma platform device of_platform_device_destroy() can trigger several NULL pointer dereference which have been elusive so far. Comment this for now since the oopses causes the shutdown to hang. Since dcp can not be reloaded this leaks the platform device on shutdown and reboot. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index ffe5221ee3ab25..fee362ae582ee2 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -819,7 +819,10 @@ static void dcp_comp_unbind(struct device *dev, struct device *main, void *data) if (dcp->piodma) { iommu_detach_device(dcp->iommu_dom, &dcp->piodma->dev); iommu_domain_free(dcp->iommu_dom); - of_platform_device_destroy(&dcp->piodma->dev, NULL); + /* TODO: the piodma platform device has to be destroyed but + * doing so leads to all kind of breakage. + */ + // of_platform_device_destroy(&dcp->piodma->dev, NULL); dcp->piodma = NULL; } From b9a15d6965568aef3d8037e7ad227dadedd6bdc3 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 6 Nov 2023 22:27:54 +0100 Subject: [PATCH 0569/1020] drm: apple: afk: Update read pointer before processing message Avoids out of order messages and already unmapped buffers while tracing with hv/trace_dcp.py. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/afk.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/afk.c b/drivers/gpu/drm/apple/afk.c index d0c36f476a6c03..2112e5eeef000d 100644 --- a/drivers/gpu/drm/apple/afk.c +++ b/drivers/gpu/drm/apple/afk.c @@ -611,8 +611,6 @@ static bool afk_recv(struct apple_dcp_afkep *ep) channel = le32_to_cpu(hdr->channel); type = le32_to_cpu(hdr->type); - afk_recv_handle(ep, channel, type, hdr->data, size); - rptr = ALIGN(rptr + sizeof(*hdr) + size, 1 << BLOCK_SHIFT); if (WARN_ON(rptr > ep->rxbfr.bufsz)) rptr = 0; @@ -624,6 +622,15 @@ static bool afk_recv(struct apple_dcp_afkep *ep) ep->rxbfr.hdr->rptr = cpu_to_le32(rptr); trace_afk_recv_rwptr_post(ep, rptr, wptr); + /* + * TODO: this is theoretically unsafe since DCP could overwrite data + * after the read pointer was updated above. Do it anyway since + * it avoids 2 problems in the DCP tracer: + * 1. the tracer sees replies before the the notifies from dcp + * 2. the tracer tries to read buffers after they are unmapped. + */ + afk_recv_handle(ep, channel, type, hdr->data, size); + return true; } From 4dd50558ae9386ca08d723438d61a78d3c565b37 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 19 Nov 2023 18:07:41 +0100 Subject: [PATCH 0570/1020] drm: apple: Implement D592 callback This callback is occasionally seen around (failed) modesets. There seems to be no need to handle it so just trace it. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb_template.c | 7 +++++++ drivers/gpu/drm/apple/iomfb_v12_3.c | 1 + drivers/gpu/drm/apple/iomfb_v13_3.c | 1 + drivers/gpu/drm/apple/trace.h | 17 +++++++++++++++++ 4 files changed, 26 insertions(+) diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 4fe22e2f7d496d..a80c48f84d2741 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -1044,6 +1044,12 @@ dcpep_cb_swap_complete_intent_gated(struct apple_dcp *dcp, info->width, info->height); } +static void +dcpep_cb_abort_swap_ap_gated(struct apple_dcp *dcp, u32 *swap_id) +{ + trace_iomfb_abort_swap_ap_gated(dcp, *swap_id); +} + static struct dcpep_get_tiling_state_resp dcpep_cb_get_tiling_state(struct apple_dcp *dcp, struct dcpep_get_tiling_state_req *req) @@ -1110,6 +1116,7 @@ TRAMPOLINE_IN(trampoline_hotplug, dcpep_cb_hotplug, u64); TRAMPOLINE_IN(trampoline_swap_complete_intent_gated, dcpep_cb_swap_complete_intent_gated, struct dcp_swap_complete_intent_gated); +TRAMPOLINE_IN(trampoline_abort_swap_ap_gated, dcpep_cb_abort_swap_ap_gated, u32); TRAMPOLINE_IN(trampoline_enable_backlight_message_ap_gated, iomfbep_cb_enable_backlight_message_ap_gated, u8); TRAMPOLINE_IN(trampoline_pr_publish, iomfb_cb_pr_publish, diff --git a/drivers/gpu/drm/apple/iomfb_v12_3.c b/drivers/gpu/drm/apple/iomfb_v12_3.c index 8b4d87ad9012bd..ad3cbf576cfdcf 100644 --- a/drivers/gpu/drm/apple/iomfb_v12_3.c +++ b/drivers/gpu/drm/apple/iomfb_v12_3.c @@ -89,6 +89,7 @@ static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = { [588] = trampoline_nop, /* resize_default_fb_surface_gated */ [589] = trampoline_swap_complete, [591] = trampoline_swap_complete_intent_gated, + [592] = trampoline_abort_swap_ap_gated, [593] = trampoline_enable_backlight_message_ap_gated, [594] = trampoline_nop, /* IOMobileFramebufferAP::setSystemConsoleMode */ [596] = trampoline_false, /* IOMobileFramebufferAP::isDFBAllocated */ diff --git a/drivers/gpu/drm/apple/iomfb_v13_3.c b/drivers/gpu/drm/apple/iomfb_v13_3.c index 0689c0a593f784..0311e1c8c39874 100644 --- a/drivers/gpu/drm/apple/iomfb_v13_3.c +++ b/drivers/gpu/drm/apple/iomfb_v13_3.c @@ -95,6 +95,7 @@ static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = { [588] = trampoline_nop, /* resize_default_fb_surface_gated */ [589] = trampoline_swap_complete, [591] = trampoline_swap_complete_intent_gated, + [592] = trampoline_abort_swap_ap_gated, [593] = trampoline_enable_backlight_message_ap_gated, [594] = trampoline_nop, /* IOMobileFramebufferAP::setSystemConsoleMode */ [596] = trampoline_false, /* IOMobileFramebufferAP::isDFBAllocated */ diff --git a/drivers/gpu/drm/apple/trace.h b/drivers/gpu/drm/apple/trace.h index a9912c7502082b..7e26944f70e554 100644 --- a/drivers/gpu/drm/apple/trace.h +++ b/drivers/gpu/drm/apple/trace.h @@ -303,6 +303,23 @@ TRACE_EVENT(iomfb_swap_complete_intent_gated, ) ); +TRACE_EVENT(iomfb_abort_swap_ap_gated, + TP_PROTO(struct apple_dcp *dcp, u32 swap_id), + TP_ARGS(dcp, swap_id), + TP_STRUCT__entry( + __field(u64, dcp) + __field(u32, swap_id) + ), + TP_fast_assign( + __entry->dcp = (u64)dcp; + __entry->swap_id = swap_id; + ), + TP_printk("dcp=%llx, swap_id=%u", + __entry->dcp, + __entry->swap_id + ) +); + DECLARE_EVENT_CLASS(iomfb_parse_mode_template, TP_PROTO(s64 id, struct dimension *horiz, struct dimension *vert, s64 best_color_mode, bool is_virtual, s64 score), TP_ARGS(id, horiz, vert, best_color_mode, is_virtual, score), From 6b1ce4f8831a73263babaaf7ebf61be1aacf3f31 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 19 Nov 2023 18:25:22 +0100 Subject: [PATCH 0571/1020] drm: apple: Keep information at which swap_id fb are still referenced Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp-internal.h | 4 ++++ drivers/gpu/drm/apple/iomfb_template.c | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 64025d23282d1c..5ac2fcfa455cec 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -75,6 +75,7 @@ struct dcp_channel { struct dcp_fb_reference { struct list_head head; struct drm_framebuffer *fb; + u32 swap_id; }; #define MAX_NOTCH_HEIGHT 160 @@ -167,6 +168,9 @@ struct apple_dcp { struct dcp_swap_submit_req_v13_3 v13_3; } swap; + /* swap id of the last completed swap */ + u32 last_swap_id; + /* Current display mode */ bool valid_mode; struct dcp_set_digital_out_mode_req mode; diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index a80c48f84d2741..739abaf30d1b97 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -121,6 +121,7 @@ static void dcpep_cb_swap_complete(struct apple_dcp *dcp, struct DCP_FW_NAME(dc_swap_complete_resp) *resp) { trace_iomfb_swap_complete(dcp, resp->swap_id); + dcp->last_swap_id = resp->swap_id; dcp_drm_crtc_vblank(dcp->crtc); } @@ -746,6 +747,8 @@ static void dcp_swap_cleared(struct apple_dcp *dcp, void *data, void *cookie) struct dcp_fb_reference *entry; entry = list_first_entry(&dcp->swapped_out_fbs, struct dcp_fb_reference, head); + if (entry->swap_id == dcp->last_swap_id) + break; if (entry->fb) drm_framebuffer_put(entry->fb); list_del(&entry->head); @@ -1145,6 +1148,8 @@ static void dcp_swapped(struct apple_dcp *dcp, void *data, void *cookie) struct dcp_fb_reference *entry; entry = list_first_entry(&dcp->swapped_out_fbs, struct dcp_fb_reference, head); + if (entry->swap_id == dcp->last_swap_id) + break; if (entry->fb) drm_framebuffer_put(entry->fb); list_del(&entry->head); @@ -1252,6 +1257,7 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru kzalloc(sizeof(*entry), GFP_KERNEL); if (entry) { entry->fb = old_state->fb; + entry->swap_id = dcp->last_swap_id; list_add_tail(&entry->head, &dcp->swapped_out_fbs); } From 8b8479457f39dd2ecd543c2bb73ea14704787971 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 20 Nov 2023 20:09:25 +0100 Subject: [PATCH 0572/1020] Revert "drm: apple: iomfb: Do not match/create PMU service for dcpext" This reverts commit ab69434d230f9951644e10c9142dbc43ea0516c4. --- drivers/gpu/drm/apple/dcp-internal.h | 3 --- drivers/gpu/drm/apple/dcp.c | 2 -- drivers/gpu/drm/apple/iomfb_template.c | 16 ---------------- drivers/gpu/drm/apple/iomfb_v12_3.c | 2 +- drivers/gpu/drm/apple/iomfb_v13_3.c | 2 +- 5 files changed, 2 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 5ac2fcfa455cec..1041aecb211822 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -187,9 +187,6 @@ struct apple_dcp { /* clear all surfaces on init */ bool surfaces_cleared; - /* is dcpext / requires dptx */ - bool is_dptx; - /* Modes valid for the connected display */ struct dcp_display_mode *modes; unsigned int nr_modes; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index fee362ae582ee2..ddccf5412eefd2 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -702,8 +702,6 @@ static int dcp_comp_bind(struct device *dev, struct device *main, void *data) if (IS_ERR(dcp->coproc_reg)) return PTR_ERR(dcp->coproc_reg); - dcp->is_dptx = dcp->phy != NULL; - of_property_read_u32(dev->of_node, "apple,dcp-index", &dcp->index); of_property_read_u32(dev->of_node, "apple,dptx-phy", diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 739abaf30d1b97..b921ff62f019aa 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -136,10 +136,6 @@ static void complete_vi_set_temperature_hint(struct apple_dcp *dcp, void *out, v static bool iomfbep_cb_match_pmu_service(struct apple_dcp *dcp, int tag, void *out, void *in) { trace_iomfb_callback(dcp, tag, __func__); - - if (dcp->is_dptx) - return true; - iomfb_a358_vi_set_temperature_hint(dcp, false, complete_vi_set_temperature_hint, NULL); @@ -163,12 +159,6 @@ static bool iomfbep_cb_match_pmu_service_2(struct apple_dcp *dcp, int tag, void { trace_iomfb_callback(dcp, tag, __func__); - if (dcp->is_dptx) { - u8 *ret = out; - ret[0] = 1; - return true; - } - iomfb_a131_pmu_service_matched(dcp, false, complete_pmu_service_matched, out); @@ -1063,11 +1053,6 @@ dcpep_cb_get_tiling_state(struct apple_dcp *dcp, }; } -static u8 dcpep_cb_create_pmu_service(struct apple_dcp *dcp) -{ - return !dcp->is_dptx; -} - static u8 dcpep_cb_create_backlight_service(struct apple_dcp *dcp) { return dcp_has_panel(dcp); @@ -1126,7 +1111,6 @@ TRAMPOLINE_IN(trampoline_pr_publish, iomfb_cb_pr_publish, struct iomfb_property); TRAMPOLINE_INOUT(trampoline_get_tiling_state, dcpep_cb_get_tiling_state, struct dcpep_get_tiling_state_req, struct dcpep_get_tiling_state_resp); -TRAMPOLINE_OUT(trampoline_create_pmu_service, dcpep_cb_create_pmu_service, u8); TRAMPOLINE_OUT(trampoline_create_backlight_service, dcpep_cb_create_backlight_service, u8); /* diff --git a/drivers/gpu/drm/apple/iomfb_v12_3.c b/drivers/gpu/drm/apple/iomfb_v12_3.c index ad3cbf576cfdcf..0fe08c42d64659 100644 --- a/drivers/gpu/drm/apple/iomfb_v12_3.c +++ b/drivers/gpu/drm/apple/iomfb_v12_3.c @@ -48,7 +48,7 @@ static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = { [106] = trampoline_nop, /* remove_property */ [107] = trampoline_true, /* create_provider_service */ [108] = trampoline_true, /* create_product_service */ - [109] = trampoline_create_pmu_service, + [109] = trampoline_true, /* create_pmu_service */ [110] = trampoline_true, /* create_iomfb_service */ [111] = trampoline_create_backlight_service, [116] = dcpep_cb_boot_1, diff --git a/drivers/gpu/drm/apple/iomfb_v13_3.c b/drivers/gpu/drm/apple/iomfb_v13_3.c index 0311e1c8c39874..1ee29112be4543 100644 --- a/drivers/gpu/drm/apple/iomfb_v13_3.c +++ b/drivers/gpu/drm/apple/iomfb_v13_3.c @@ -50,7 +50,7 @@ static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = { [107] = trampoline_nop, /* remove_property */ [108] = trampoline_true, /* create_provider_service */ [109] = trampoline_true, /* create_product_service */ - [110] = trampoline_create_pmu_service, + [110] = trampoline_true, /* create_pmu_service */ [111] = trampoline_true, /* create_iomfb_service */ [112] = trampoline_create_backlight_service, [113] = trampoline_true, /* create_nvram_servce? */ From 6d65158dc1275be95653414951867471edd686a8 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 20 Nov 2023 22:48:02 +0100 Subject: [PATCH 0573/1020] drm: apple: dptx: Implement APCALL_DEACTIVATE and reset the phy This mirrors what macOS does and should make reconnections more reliable. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dptxep.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/apple/dptxep.c b/drivers/gpu/drm/apple/dptxep.c index bb52acf3685515..bb34c5b71a2b03 100644 --- a/drivers/gpu/drm/apple/dptxep.c +++ b/drivers/gpu/drm/apple/dptxep.c @@ -450,6 +450,23 @@ dptxport_call_activate(struct apple_epic_service *service, return 0; } +static int +dptxport_call_deactivate(struct apple_epic_service *service, + const void *data, size_t data_size, + void *reply, size_t reply_size) +{ + struct dptx_port *dptx = service->cookie; + + /* deactivate phy */ + phy_set_mode_ext(dptx->atcphy, PHY_MODE_INVALID, 0); + + memcpy(reply, data, min(reply_size, data_size)); + if (reply_size >= 4) + memset(reply, 0, 4); + + return 0; +} + static int dptxport_call(struct apple_epic_service *service, u32 idx, const void *data, size_t data_size, void *reply, size_t reply_size) @@ -493,13 +510,13 @@ static int dptxport_call(struct apple_epic_service *service, u32 idx, case DPTX_APCALL_ACTIVATE: return dptxport_call_activate(service, data, data_size, reply, reply_size); + case DPTX_APCALL_DEACTIVATE: + return dptxport_call_deactivate(service, data, data_size, + reply, reply_size); default: /* just try to ACK and hope for the best... */ dev_info(service->ep->dcp->dev, "DPTXPort: acking unhandled call %u\n", idx); - fallthrough; - /* we can silently ignore and just ACK these calls */ - case DPTX_APCALL_DEACTIVATE: memcpy(reply, data, min(reply_size, data_size)); if (reply_size >= 4) memset(reply, 0, 4); From bbe5a0b79131c67c56b03640c6aa39b1214a1a6f Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 20 Nov 2023 22:56:43 +0100 Subject: [PATCH 0574/1020] drm: apple: Disconnect dptx When the CRTC is powered down Seems to make disconnect / reconnect more reliable and almost fixes suspend/resume. The drm device tries to modeset too early on resume which leaves the screen blank. This should reduce power consumption after disconnecting the HDMI port. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 65 +++++++++++++++++++++++++++++++++++ drivers/gpu/drm/apple/iomfb.c | 51 --------------------------- 2 files changed, 65 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index ddccf5412eefd2..995ad779aeef44 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -287,6 +287,7 @@ static int dcp_dptx_disconnect(struct apple_dcp *dcp, u32 port) struct apple_connector *connector = dcp->connector; mutex_lock(&dcp->hpd_mutex); + if (connector && connector->connected) { dcp->valid_mode = false; schedule_work(&connector->hotplug_wq); @@ -407,6 +408,70 @@ int dcp_wait_ready(struct platform_device *pdev, u64 timeout) } EXPORT_SYMBOL(dcp_wait_ready); + +void dcp_sleep(struct apple_dcp *dcp) +{ + switch (dcp->fw_compat) { + case DCP_FIRMWARE_V_12_3: + iomfb_sleep_v12_3(dcp); + break; + case DCP_FIRMWARE_V_13_5: + iomfb_sleep_v13_3(dcp); + break; + default: + WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); + break; + } +} + +void dcp_poweron(struct platform_device *pdev) +{ + struct apple_dcp *dcp = platform_get_drvdata(pdev); + + if (dcp->hdmi_hpd) { + bool connected = gpiod_get_value_cansleep(dcp->hdmi_hpd); + dev_info(dcp->dev, "%s: DP2HDMI HPD connected:%d\n", __func__, connected); + + if (connected) + dcp_dptx_connect(dcp, 0); + } + + switch (dcp->fw_compat) { + case DCP_FIRMWARE_V_12_3: + iomfb_poweron_v12_3(dcp); + break; + case DCP_FIRMWARE_V_13_5: + iomfb_poweron_v13_3(dcp); + break; + default: + WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); + break; + } +} +EXPORT_SYMBOL(dcp_poweron); + +void dcp_poweroff(struct platform_device *pdev) +{ + struct apple_dcp *dcp = platform_get_drvdata(pdev); + + switch (dcp->fw_compat) { + case DCP_FIRMWARE_V_12_3: + iomfb_poweroff_v12_3(dcp); + break; + case DCP_FIRMWARE_V_13_5: + iomfb_poweroff_v13_3(dcp); + break; + default: + WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); + break; + } + + if (dcp->phy) + dcp_dptx_disconnect(dcp, 0); + +} +EXPORT_SYMBOL(dcp_poweroff); + static void dcp_work_register_backlight(struct work_struct *work) { int ret; diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 642f178637241a..ba3799fba8da04 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -218,57 +218,6 @@ void dcp_ack(struct apple_dcp *dcp, enum dcp_context_id context) dcpep_ack(context)); } -void dcp_sleep(struct apple_dcp *dcp) -{ - switch (dcp->fw_compat) { - case DCP_FIRMWARE_V_12_3: - iomfb_sleep_v12_3(dcp); - break; - case DCP_FIRMWARE_V_13_5: - iomfb_sleep_v13_3(dcp); - break; - default: - WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); - break; - } -} - -void dcp_poweron(struct platform_device *pdev) -{ - struct apple_dcp *dcp = platform_get_drvdata(pdev); - - switch (dcp->fw_compat) { - case DCP_FIRMWARE_V_12_3: - iomfb_poweron_v12_3(dcp); - break; - case DCP_FIRMWARE_V_13_5: - iomfb_poweron_v13_3(dcp); - break; - default: - WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); - break; - } -} -EXPORT_SYMBOL(dcp_poweron); - -void dcp_poweroff(struct platform_device *pdev) -{ - struct apple_dcp *dcp = platform_get_drvdata(pdev); - - switch (dcp->fw_compat) { - case DCP_FIRMWARE_V_12_3: - iomfb_poweroff_v12_3(dcp); - break; - case DCP_FIRMWARE_V_13_5: - iomfb_poweroff_v13_3(dcp); - break; - default: - WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); - break; - } -} -EXPORT_SYMBOL(dcp_poweroff); - /* * Helper to send a DRM hotplug event. The DCP is accessed from a single * (RTKit) thread. To handle hotplug callbacks, we need to call From 0f70e52e9c567e3d1bbf7355ca5504fe37edec48 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 21 Nov 2023 23:32:07 +0100 Subject: [PATCH 0575/1020] drm: apple: dptx: Wait for completion of dptx_connect. Makes connects more reliable. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 17 ++++++++++++----- drivers/gpu/drm/apple/dptxep.c | 4 ++++ drivers/gpu/drm/apple/dptxep.h | 1 + 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 995ad779aeef44..e597dc42b3f047 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -256,6 +256,8 @@ EXPORT_SYMBOL_GPL(dcp_get_connector_type); static int dcp_dptx_connect(struct apple_dcp *dcp, u32 port) { + int ret = 0; + if (!dcp->phy) { dev_warn(dcp->dev, "dcp_dptx_connect: missing phy\n"); return -ENODEV; @@ -264,22 +266,27 @@ static int dcp_dptx_connect(struct apple_dcp *dcp, u32 port) mutex_lock(&dcp->hpd_mutex); if (!dcp->dptxport[port].enabled) { dev_warn(dcp->dev, "dcp_dptx_connect: dptx service for port %d not enabled\n", port); - mutex_unlock(&dcp->hpd_mutex); - return -ENODEV; + ret = -ENODEV; + goto out_unlock; } if (dcp->dptxport[port].connected) - goto ret; + goto out_unlock; + reinit_completion(&dcp->dptxport[port].linkcfg_completion); dcp->dptxport[port].atcphy = dcp->phy; dptxport_connect(dcp->dptxport[port].service, 0, dcp->dptx_phy, dcp->dptx_die); dptxport_request_display(dcp->dptxport[port].service); dcp->dptxport[port].connected = true; -ret: mutex_unlock(&dcp->hpd_mutex); - + wait_for_completion_timeout(&dcp->dptxport[port].linkcfg_completion, + msecs_to_jiffies(1000)); return 0; + +out_unlock: + mutex_unlock(&dcp->hpd_mutex); + return ret; } static int dcp_dptx_disconnect(struct apple_dcp *dcp, u32 port) diff --git a/drivers/gpu/drm/apple/dptxep.c b/drivers/gpu/drm/apple/dptxep.c index bb34c5b71a2b03..7e9d3768a0c85f 100644 --- a/drivers/gpu/drm/apple/dptxep.c +++ b/drivers/gpu/drm/apple/dptxep.c @@ -329,8 +329,10 @@ dptxport_call_will_change_link_config(struct apple_epic_service *service) static int dptxport_call_did_change_link_config(struct apple_epic_service *service) { + struct dptx_port *dptx = service->cookie; /* assume the link config did change and wait a little bit */ mdelay(10); + complete(&dptx->linkcfg_completion); return 0; } @@ -572,6 +574,8 @@ int dptxep_init(struct apple_dcp *dcp) init_completion(&dcp->dptxport[0].enable_completion); init_completion(&dcp->dptxport[1].enable_completion); + init_completion(&dcp->dptxport[0].linkcfg_completion); + init_completion(&dcp->dptxport[1].linkcfg_completion); dcp->dptxep = afk_init(dcp, DPTX_ENDPOINT, dptxep_ops); if (IS_ERR(dcp->dptxep)) diff --git a/drivers/gpu/drm/apple/dptxep.h b/drivers/gpu/drm/apple/dptxep.h index 481ebbc97bf38d..4a0770d43c954c 100644 --- a/drivers/gpu/drm/apple/dptxep.h +++ b/drivers/gpu/drm/apple/dptxep.h @@ -49,6 +49,7 @@ struct apple_epic_service; struct dptx_port { bool enabled, connected; struct completion enable_completion; + struct completion linkcfg_completion; u32 unit; struct apple_epic_service *service; union phy_configure_opts phy_ops; From c4ffd5f161ecce0ea823f2983aa22573821ce722 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 21 Nov 2023 23:50:49 +0100 Subject: [PATCH 0576/1020] drm: apple: HPD: Only act on connect IRQs DCP notices the disconnects on its own and the parallel handling just results in confusion (both on DRM and developer side). Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index e597dc42b3f047..dbee49c9db2619 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -314,12 +314,16 @@ static irqreturn_t dcp_dp2hdmi_hpd(int irq, void *data) struct apple_dcp *dcp = data; bool connected = gpiod_get_value_cansleep(dcp->hdmi_hpd); - dev_info(dcp->dev, "DP2HDMI HPD connected:%d\n", connected); + /* do nothing on disconnect and trust that dcp detects it itself. + * Parallel disconnect HPDs result drm disabling the CRTC even when it + * should not. + * The interrupt should be changed to rising but for now the disconnect + * IRQs might be helpful for debugging. + */ + dev_info(dcp->dev, "DP2HDMI HPD irq, connected:%d\n", connected); if (connected) dcp_dptx_connect(dcp, 0); - else - dcp_dptx_disconnect(dcp, 0); return IRQ_HANDLED; } From ad5bf0268784d27a350cb0bdf08124ce2831268a Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 21 Nov 2023 23:57:07 +0100 Subject: [PATCH 0577/1020] drm: apple: iomfb: Improve hotplug related logging Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 3 ++- drivers/gpu/drm/apple/iomfb_template.c | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index ba3799fba8da04..f941120523c9c1 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -235,7 +235,8 @@ void dcp_hotplug(struct work_struct *work) dev = connector->base.dev; dcp = platform_get_drvdata(connector->dcp); - dev_info(dcp->dev, "%s: connected: %d", __func__, connector->connected); + dev_info(dcp->dev, "%s() connected:%d valid_mode:%d\n", __func__, + connector->connected, dcp->valid_mode); /* * DCP defers link training until we set a display mode. But we set diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index b921ff62f019aa..9b45179f23d38e 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -1014,6 +1014,9 @@ static void dcpep_cb_hotplug(struct apple_dcp *dcp, u64 *connected) if (dcp->main_display) return; + dev_info(dcp->dev, "cb_hotplug() connected:%llu, valid_mode:%d\n", + *connected, dcp->valid_mode); + /* Hotplug invalidates mode. DRM doesn't always handle this. */ if (!(*connected)) { dcp->valid_mode = false; From e6bf5e4392d475eed59a3dafea969a12d2745320 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 22 Nov 2023 09:41:29 +0100 Subject: [PATCH 0578/1020] drm: apple: Extract modeset crtc's atomic_flush() Triggering modesets from drm_connector_helper_funcs.atomic_check is more in line with DRM/KMS' design and allows returning errors from failed modesets. Ignore hotplug callbacks from DCP during modeset. DCP always does disconnected -> connected on (at least the initial) modeset. Shield drm helpers from this. This improves reliability with externel (dptx based) displays. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 2 + drivers/gpu/drm/apple/dcp-internal.h | 1 + drivers/gpu/drm/apple/dcp.h | 2 + drivers/gpu/drm/apple/iomfb.c | 41 ++++++++ drivers/gpu/drm/apple/iomfb_template.c | 137 ++++++++++++++----------- drivers/gpu/drm/apple/iomfb_template.h | 2 + 6 files changed, 124 insertions(+), 61 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index ce68c19a9a3a03..88ac82c3540372 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -299,6 +299,8 @@ static const struct drm_connector_funcs apple_connector_funcs = { static const struct drm_connector_helper_funcs apple_connector_helper_funcs = { .get_modes = dcp_get_modes, .mode_valid = dcp_mode_valid, + .atomic_check = dcp_connector_atomic_check, + }; static const struct drm_crtc_helper_funcs apple_crtc_helper_funcs = { diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 1041aecb211822..e975ae5be371fd 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -172,6 +172,7 @@ struct apple_dcp { u32 last_swap_id; /* Current display mode */ + bool during_modeset; bool valid_mode; struct dcp_set_digital_out_mode_req mode; diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h index e0dc96109b9d2b..039dcf3ab319d8 100644 --- a/drivers/gpu/drm/apple/dcp.h +++ b/drivers/gpu/drm/apple/dcp.h @@ -57,6 +57,8 @@ void dcp_drm_crtc_vblank(struct apple_crtc *crtc); int dcp_get_modes(struct drm_connector *connector); int dcp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode); +int dcp_connector_atomic_check(struct drm_connector *connector, + struct drm_atomic_state *state); bool dcp_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index f941120523c9c1..2b2a15a13f6223 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -421,6 +421,47 @@ int dcp_mode_valid(struct drm_connector *connector, } EXPORT_SYMBOL_GPL(dcp_mode_valid); +int dcp_connector_atomic_check(struct drm_connector *connector, + struct drm_atomic_state *state) +{ + struct apple_connector *apple_connector = to_apple_connector(connector); + struct platform_device *pdev = apple_connector->dcp; + struct apple_dcp *dcp = platform_get_drvdata(pdev); + struct drm_crtc *crtc = &dcp->crtc->base; + struct drm_crtc_state *crtc_state; + int ret = -EIO; + bool modeset; + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + if (!crtc_state) + return 0; + + modeset = drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode; + + if (!modeset) + return 0; + + /* ignore no mode, poweroff is handled elsewhere */ + if (crtc_state->mode.hdisplay == 0 && crtc_state->mode.vdisplay == 0) + return 0; + + switch (dcp->fw_compat) { + case DCP_FIRMWARE_V_12_3: + ret = iomfb_modeset_v12_3(dcp, crtc_state); + break; + case DCP_FIRMWARE_V_13_5: + ret = iomfb_modeset_v13_3(dcp, crtc_state); + break; + default: + WARN_ONCE(true, "Unexpected firmware version: %u\n", + dcp->fw_compat); + break; + } + + return ret; +} +EXPORT_SYMBOL_GPL(dcp_connector_atomic_check); + bool dcp_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 9b45179f23d38e..6afd4c74856753 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -1014,6 +1014,13 @@ static void dcpep_cb_hotplug(struct apple_dcp *dcp, u64 *connected) if (dcp->main_display) return; + if (dcp->during_modeset) { + dev_info(dcp->dev, + "cb_hotplug() ignored during modeset connected:%llu\n", + *connected); + return; + } + dev_info(dcp->dev, "cb_hotplug() connected:%llu, valid_mode:%d\n", *connected, dcp->valid_mode); @@ -1178,6 +1185,75 @@ static void complete_set_digital_out_mode(struct apple_dcp *dcp, void *data, } } +int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, + struct drm_crtc_state *crtc_state) +{ + struct dcp_display_mode *mode; + struct dcp_wait_cookie *cookie; + int ret; + + mode = lookup_mode(dcp, &crtc_state->mode); + if (!mode) { + dev_err(dcp->dev, "no match for " DRM_MODE_FMT "\n", + DRM_MODE_ARG(&crtc_state->mode)); + return -EIO; + } + + dev_info(dcp->dev, + "set_digital_out_mode(color:%d timing:%d) " DRM_MODE_FMT "\n", + mode->color_mode_id, mode->timing_mode_id, + DRM_MODE_ARG(&crtc_state->mode)); + dcp->mode = (struct dcp_set_digital_out_mode_req){ + .color_mode_id = mode->color_mode_id, + .timing_mode_id = mode->timing_mode_id + }; + + cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); + if (!cookie) { + return -ENOMEM; + } + + init_completion(&cookie->done); + kref_init(&cookie->refcount); + /* increase refcount to ensure the receiver has a reference */ + kref_get(&cookie->refcount); + + dcp->during_modeset = true; + + dcp_set_digital_out_mode(dcp, false, &dcp->mode, + complete_set_digital_out_mode, cookie); + + /* + * The DCP firmware has an internal timeout of ~8 seconds for + * modesets. Add an extra 500ms to safe side that the modeset + * call has returned. + */ + dev_dbg(dcp->dev, "%s - wait for modeset", __func__); + ret = wait_for_completion_timeout(&cookie->done, + msecs_to_jiffies(8500)); + + kref_put(&cookie->refcount, release_wait_cookie); + dcp->during_modeset = false; + dev_info(dcp->dev, "set_digital_out_mode finished:%d\n", ret); + + if (ret == 0) { + dev_info(dcp->dev, "set_digital_out_mode timed out\n"); + return -EIO; + } else if (ret < 0) { + dev_info(dcp->dev, + "waiting on set_digital_out_mode failed:%d\n", ret); + return -EIO; + + } else if (ret > 0) { + dev_dbg(dcp->dev, + "set_digital_out_mode finished with %d to spare\n", + jiffies_to_msecs(ret)); + } + dcp->valid_mode = true; + + return 0; +} + void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, struct drm_atomic_state *state) { struct drm_plane *plane; @@ -1186,13 +1262,10 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru struct DCP_FW_NAME(dcp_swap_submit_req) *req = &DCP_FW_UNION(dcp->swap); int plane_idx, l; int has_surface = 0; - bool modeset; dev_dbg(dcp->dev, "%s", __func__); crtc_state = drm_atomic_get_new_crtc_state(state, crtc); - modeset = drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode; - /* Reset to defaults */ memset(req, 0, sizeof(*req)); for (l = 0; l < SWAP_SURFACES; l++) @@ -1305,64 +1378,6 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru l += 1; } - if (modeset) { - struct dcp_display_mode *mode; - struct dcp_wait_cookie *cookie; - int ret; - - mode = lookup_mode(dcp, &crtc_state->mode); - if (!mode) { - dev_warn(dcp->dev, "no match for " DRM_MODE_FMT, - DRM_MODE_ARG(&crtc_state->mode)); - schedule_work(&dcp->vblank_wq); - return; - } - - dev_info(dcp->dev, "set_digital_out_mode(color:%d timing:%d)", - mode->color_mode_id, mode->timing_mode_id); - dcp->mode = (struct dcp_set_digital_out_mode_req){ - .color_mode_id = mode->color_mode_id, - .timing_mode_id = mode->timing_mode_id - }; - - cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); - if (!cookie) { - schedule_work(&dcp->vblank_wq); - return; - } - - init_completion(&cookie->done); - kref_init(&cookie->refcount); - /* increase refcount to ensure the receiver has a reference */ - kref_get(&cookie->refcount); - - dcp_set_digital_out_mode(dcp, false, &dcp->mode, - complete_set_digital_out_mode, cookie); - - /* - * The DCP firmware has an internal timeout of ~8 seconds for - * modesets. Add an extra 500ms to safe side that the modeset - * call has returned. - */ - dev_dbg(dcp->dev, "%s - wait for modeset", __func__); - ret = wait_for_completion_timeout(&cookie->done, - msecs_to_jiffies(8500)); - - kref_put(&cookie->refcount, release_wait_cookie); - - if (ret == 0) { - dev_info(dcp->dev, "set_digital_out_mode timed out"); - schedule_work(&dcp->vblank_wq); - return; - } else if (ret > 0) { - dev_dbg(dcp->dev, - "set_digital_out_mode finished with %d to spare", - jiffies_to_msecs(ret)); - } - - dcp->valid_mode = true; - } - if (!has_surface && !crtc_state->color_mgmt_changed) { if (crtc_state->enable && crtc_state->active && !crtc_state->planes_changed) { diff --git a/drivers/gpu/drm/apple/iomfb_template.h b/drivers/gpu/drm/apple/iomfb_template.h index e9c249609f46cb..f446a4d8f38b90 100644 --- a/drivers/gpu/drm/apple/iomfb_template.h +++ b/drivers/gpu/drm/apple/iomfb_template.h @@ -172,6 +172,8 @@ struct DCP_FW_NAME(dcp_map_reg_resp) { struct apple_dcp; +int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, + struct drm_crtc_state *crtc_state); void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, struct drm_atomic_state *state); void DCP_FW_NAME(iomfb_poweron)(struct apple_dcp *dcp); void DCP_FW_NAME(iomfb_poweroff)(struct apple_dcp *dcp); From f2b0742f431575d682f29f7829da09bd75633237 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 22 Nov 2023 09:53:09 +0100 Subject: [PATCH 0579/1020] drm: apple: dptx: Log connect/disconnect calls Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index dbee49c9db2619..17d37b491b6978 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -262,6 +262,7 @@ static int dcp_dptx_connect(struct apple_dcp *dcp, u32 port) dev_warn(dcp->dev, "dcp_dptx_connect: missing phy\n"); return -ENODEV; } + dev_info(dcp->dev, "%s(port=%d)\n", __func__, port); mutex_lock(&dcp->hpd_mutex); if (!dcp->dptxport[port].enabled) { @@ -292,6 +293,7 @@ static int dcp_dptx_connect(struct apple_dcp *dcp, u32 port) static int dcp_dptx_disconnect(struct apple_dcp *dcp, u32 port) { struct apple_connector *connector = dcp->connector; + dev_info(dcp->dev, "%s(port=%d)\n", __func__, port); mutex_lock(&dcp->hpd_mutex); From 1b1213a3f98819e605869224b5158b4f38f0d5eb Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 23 Nov 2023 22:58:16 +0100 Subject: [PATCH 0580/1020] drm: apple: Move modeset into drm_crtc's atomic_enable squash! drm: apple: Extract modeset crtc's atomic_flush() Fixes: 99d7bb861908 ("drm: apple: Extract modeset crtc's atomic_flush()") Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 5 +++-- drivers/gpu/drm/apple/dcp.h | 4 ++-- drivers/gpu/drm/apple/iomfb.c | 12 +++++------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 88ac82c3540372..98677fbec6ad2e 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -198,6 +198,9 @@ static void apple_crtc_atomic_enable(struct drm_crtc *crtc, dcp_poweron(apple_crtc->dcp); dev_dbg(&apple_crtc->dcp->dev, "%s finished", __func__); } + + if (crtc_state->active) + dcp_crtc_atomic_modeset(crtc, state); } static void apple_crtc_atomic_disable(struct drm_crtc *crtc, @@ -299,8 +302,6 @@ static const struct drm_connector_funcs apple_connector_funcs = { static const struct drm_connector_helper_funcs apple_connector_helper_funcs = { .get_modes = dcp_get_modes, .mode_valid = dcp_mode_valid, - .atomic_check = dcp_connector_atomic_check, - }; static const struct drm_crtc_helper_funcs apple_crtc_helper_funcs = { diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h index 039dcf3ab319d8..298520c0832e93 100644 --- a/drivers/gpu/drm/apple/dcp.h +++ b/drivers/gpu/drm/apple/dcp.h @@ -57,8 +57,8 @@ void dcp_drm_crtc_vblank(struct apple_crtc *crtc); int dcp_get_modes(struct drm_connector *connector); int dcp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode); -int dcp_connector_atomic_check(struct drm_connector *connector, - struct drm_atomic_state *state); +int dcp_crtc_atomic_modeset(struct drm_crtc *crtc, + struct drm_atomic_state *state); bool dcp_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 2b2a15a13f6223..74bb67e1f7e0fe 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -421,13 +421,11 @@ int dcp_mode_valid(struct drm_connector *connector, } EXPORT_SYMBOL_GPL(dcp_mode_valid); -int dcp_connector_atomic_check(struct drm_connector *connector, - struct drm_atomic_state *state) +int dcp_crtc_atomic_modeset(struct drm_crtc *crtc, + struct drm_atomic_state *state) { - struct apple_connector *apple_connector = to_apple_connector(connector); - struct platform_device *pdev = apple_connector->dcp; - struct apple_dcp *dcp = platform_get_drvdata(pdev); - struct drm_crtc *crtc = &dcp->crtc->base; + struct apple_crtc *apple_crtc = to_apple_crtc(crtc); + struct apple_dcp *dcp = platform_get_drvdata(apple_crtc->dcp); struct drm_crtc_state *crtc_state; int ret = -EIO; bool modeset; @@ -460,7 +458,7 @@ int dcp_connector_atomic_check(struct drm_connector *connector, return ret; } -EXPORT_SYMBOL_GPL(dcp_connector_atomic_check); +EXPORT_SYMBOL_GPL(dcp_crtc_atomic_modeset); bool dcp_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, From 2354fcdcb5155f424a4bacb563755fc4a23e0e9c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 23 Nov 2023 22:58:51 +0100 Subject: [PATCH 0581/1020] drm: apple: Fix DPTX hotplug handling - Do not trigger an hotplug event from disconnect. DCP/iomfb notices that itself. - Check HPD status before disconnecting DPTX in the crtc disable path. - disconnect on suspend to allow an orderly re-connect on resume Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 17d37b491b6978..c4ccc9a15c11f2 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -292,16 +292,9 @@ static int dcp_dptx_connect(struct apple_dcp *dcp, u32 port) static int dcp_dptx_disconnect(struct apple_dcp *dcp, u32 port) { - struct apple_connector *connector = dcp->connector; dev_info(dcp->dev, "%s(port=%d)\n", __func__, port); mutex_lock(&dcp->hpd_mutex); - - if (connector && connector->connected) { - dcp->valid_mode = false; - schedule_work(&connector->hotplug_wq); - } - if (dcp->dptxport[port].enabled && dcp->dptxport[port].connected) { dptxport_release_display(dcp->dptxport[port].service); dcp->dptxport[port].connected = false; @@ -479,9 +472,11 @@ void dcp_poweroff(struct platform_device *pdev) break; } - if (dcp->phy) - dcp_dptx_disconnect(dcp, 0); - + if (dcp->hdmi_hpd) { + bool connected = gpiod_get_value_cansleep(dcp->hdmi_hpd); + if (!connected) + dcp_dptx_disconnect(dcp, 0); + } } EXPORT_SYMBOL(dcp_poweroff); @@ -1020,8 +1015,10 @@ static int dcp_platform_suspend(struct device *dev) { struct apple_dcp *dcp = dev_get_drvdata(dev); - if (dcp->hdmi_hpd_irq) + if (dcp->hdmi_hpd_irq) { disable_irq(dcp->hdmi_hpd_irq); + dcp_dptx_disconnect(dcp, 0); + } /* * Set the device as a wakeup device, which forces its power * domains to stay on. We need this as we do not support full From f309adf1cb74eeba58592d0c5bafa3ffa0f9baa1 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 23 Nov 2023 23:04:47 +0100 Subject: [PATCH 0582/1020] drm: apple: iomfb: Use drm_kms_helper_connector_hotplug_event Avoid device wide hotplugs as DCP knowns the affected connector. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 74bb67e1f7e0fe..29975eef545c28 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -243,13 +243,11 @@ void dcp_hotplug(struct work_struct *work) * display modes from atomic_flush, so userspace needs to trigger a * flush, or the CRTC gets no signal. */ - if (connector->base.state && !dcp->valid_mode && connector->connected) { - drm_connector_set_link_status_property( - &connector->base, DRM_MODE_LINK_STATUS_BAD); - } + if (connector->base.state && !dcp->valid_mode && connector->connected) + drm_connector_set_link_status_property(&connector->base, + DRM_MODE_LINK_STATUS_BAD); - if (dev && dev->registered) - drm_kms_helper_hotplug_event(dev); + drm_kms_helper_connector_hotplug_event(&connector->base); } EXPORT_SYMBOL_GPL(dcp_hotplug); From 7f15e8414f9457e9828fb776932de91b4f565c1f Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 25 Nov 2023 10:46:41 +0100 Subject: [PATCH 0583/1020] fixup! drm: apple: Disconnect dptx When the CRTC is powered down Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index c4ccc9a15c11f2..c87d3eb6462ebc 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -414,8 +414,7 @@ int dcp_wait_ready(struct platform_device *pdev, u64 timeout) } EXPORT_SYMBOL(dcp_wait_ready); - -void dcp_sleep(struct apple_dcp *dcp) +static void dcp_sleep(struct apple_dcp *dcp) { switch (dcp->fw_compat) { case DCP_FIRMWARE_V_12_3: From 2ff8f575dee060fbec7d72091b256274cb9d7876 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 25 Nov 2023 10:48:13 +0100 Subject: [PATCH 0584/1020] fixup! drm/apple: Add support for the macOS 13.2 DCP firmware Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 29975eef545c28..e0f50539f3f49c 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -501,7 +501,7 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) } EXPORT_SYMBOL_GPL(dcp_flush); -void iomfb_start(struct apple_dcp *dcp) +static void iomfb_start(struct apple_dcp *dcp) { switch (dcp->fw_compat) { case DCP_FIRMWARE_V_12_3: From 6b348e738957ba1bc18042a843797622f2f9a99d Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 25 Nov 2023 10:49:08 +0100 Subject: [PATCH 0585/1020] fixup! mux: apple dp crossbar: Support t602x DP cross bar variant Signed-off-by: Janne Grunau --- drivers/mux/apple-display-crossbar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mux/apple-display-crossbar.c b/drivers/mux/apple-display-crossbar.c index 819c1333c85c6c..7acd85c8b4eea4 100644 --- a/drivers/mux/apple-display-crossbar.c +++ b/drivers/mux/apple-display-crossbar.c @@ -18,7 +18,7 @@ #include #include -/** +/* * T602x register interface is cleary different so most of the nemes below are * probly wrong. */ From c31914b0b4e5c0e961796db0edfd3824b731679d Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 25 Nov 2023 11:23:03 +0100 Subject: [PATCH 0586/1020] fixup! drm: apple: iomfb: Use drm_kms_helper_connector_hotplug_event Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index e0f50539f3f49c..8ef1559bc061e9 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -228,11 +228,9 @@ void dcp_ack(struct apple_dcp *dcp, enum dcp_context_id context) void dcp_hotplug(struct work_struct *work) { struct apple_connector *connector; - struct drm_device *dev; struct apple_dcp *dcp; connector = container_of(work, struct apple_connector, hotplug_wq); - dev = connector->base.dev; dcp = platform_get_drvdata(connector->dcp); dev_info(dcp->dev, "%s() connected:%d valid_mode:%d\n", __func__, From 08771d6d56620b4bdaecee1fcb6e4d2d79be8cc8 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 25 Nov 2023 11:36:37 +0100 Subject: [PATCH 0587/1020] fixup! drm: apple: Disconnect dptx When the CRTC is powered down Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index c87d3eb6462ebc..cc8f3ebd532f52 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -414,7 +414,7 @@ int dcp_wait_ready(struct platform_device *pdev, u64 timeout) } EXPORT_SYMBOL(dcp_wait_ready); -static void dcp_sleep(struct apple_dcp *dcp) +static void __maybe_unused dcp_sleep(struct apple_dcp *dcp) { switch (dcp->fw_compat) { case DCP_FIRMWARE_V_12_3: From 6d72a203f49d98589266502341b9df1d3293a8be Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 26 Nov 2023 18:30:59 +0100 Subject: [PATCH 0588/1020] drm : apple: iomfb: Handle OOB ASYNC/CB context Only observed with dcp/dptx in linux after initialisation and reset in m1n1. On the initial startup dcp sends two D576 (hotPlug_notify_gated) presumendly due to state confusion due to the multiple dptx connections. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp-internal.h | 2 +- drivers/gpu/drm/apple/iomfb.c | 4 ++++ drivers/gpu/drm/apple/iomfb.h | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index e975ae5be371fd..24d23364e8f415 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -154,7 +154,7 @@ struct apple_dcp { struct dcp_mem_descriptor memdesc[DCP_MAX_MAPPINGS]; struct dcp_channel ch_cmd, ch_oobcmd; - struct dcp_channel ch_cb, ch_oobcb, ch_async; + struct dcp_channel ch_cb, ch_oobcb, ch_async, ch_oobasync; /* iomfb EP callback handlers */ const iomfb_cb_handler *cb_handlers; diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 8ef1559bc061e9..dcb58febf5269e 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -48,6 +48,8 @@ static int dcp_channel_offset(enum dcp_context_id id) switch (id) { case DCP_CONTEXT_ASYNC: return 0x40000; + case DCP_CONTEXT_OOBASYNC: + return 0x48000; case DCP_CONTEXT_CB: return 0x60000; case DCP_CONTEXT_OOBCB: @@ -117,6 +119,8 @@ static struct dcp_channel *dcp_get_channel(struct apple_dcp *dcp, return &dcp->ch_oobcmd; case DCP_CONTEXT_ASYNC: return &dcp->ch_async; + case DCP_CONTEXT_OOBASYNC: + return &dcp->ch_oobasync; default: return NULL; } diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index 5d54a59c7ced45..8fb225e6169e42 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -28,6 +28,9 @@ enum dcp_context_id { /* Out-of-band command */ DCP_CONTEXT_OOBCMD = 6, + /* Out-of-band Asynchronous */ + DCP_CONTEXT_OOBASYNC = 7, + DCP_NUM_CONTEXTS }; From 7893a12ad5376237a39758076e7fc41e9008faca Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 26 Nov 2023 18:55:03 +0100 Subject: [PATCH 0589/1020] fixup! drm: apple: DCP AFK/EPIC support Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index 474f704859bd84..a837af1bd0a5e8 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -952,4 +952,4 @@ int parse_sound_mode(struct dcp_parse_ctx *handle, return 0; } -EXPORT_SYMBOL_GPL(parse_sound_mode); \ No newline at end of file +EXPORT_SYMBOL_GPL(parse_sound_mode); From c1e23e183cb0ac6d224d468cdaa2717133adcb02 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 26 Nov 2023 18:57:07 +0100 Subject: [PATCH 0590/1020] drm: apple: iomfb: Extend hotplug/mode parsing logging Under unknown but slightly broken conditions dcp sends timing modes without linked color modes. Log a warning when this happens and log the number of valid modes before emitting HPD events. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 4 ++-- drivers/gpu/drm/apple/iomfb_template.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index dcb58febf5269e..1f69eb107663d9 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -237,8 +237,8 @@ void dcp_hotplug(struct work_struct *work) connector = container_of(work, struct apple_connector, hotplug_wq); dcp = platform_get_drvdata(connector->dcp); - dev_info(dcp->dev, "%s() connected:%d valid_mode:%d\n", __func__, - connector->connected, dcp->valid_mode); + dev_info(dcp->dev, "%s() connected:%d valid_mode:%d nr_modes:%u\n", __func__, + connector->connected, dcp->valid_mode, dcp->nr_modes); /* * DCP defers link training until we set a display mode. But we set diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 6afd4c74856753..980bab05dd00a9 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -567,6 +567,8 @@ static bool dcpep_process_chunks(struct apple_dcp *dcp, dcp->nr_modes = 0; return false; } + if (dcp->nr_modes == 0) + dev_warn(dcp->dev, "TimingElements without valid modes!\n"); } else if (!strcmp(req->key, "DisplayAttributes")) { /* DisplayAttributes are empty for integrated displays, use * display dimensions read from the devicetree From 825762d609e1fb7712256e44475162d3f58b4d3d Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 27 Nov 2023 00:11:12 +0100 Subject: [PATCH 0591/1020] drm: apple: Adjust startup sequence and timing for dptx DPTX setup from an initialized connection and display with sleeping and reset dcp is unfortunately quite fragile. The display connection has to be stopped and reestablished. Goodbye flicker free boot. If the IOMFB endpoint is started too early dcp might provide incomplete timing modes which prevent modesets. On display standby a HPD is triggered should result in a fully initialized dcp. If not a display cable unplug and plug should help. MacOS doesn't handle this at all and just gives up. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 8 +++- drivers/gpu/drm/apple/dcp.c | 64 +++++++++++++++++-------------- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 98677fbec6ad2e..3bfa3c9a461ae4 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -443,7 +444,10 @@ static int apple_drm_init_dcp(struct device *dev) if (num_dcp < 1) return -ENODEV; - timeout = get_jiffies_64() + msecs_to_jiffies(500); + /* + * Starting DPTX might take some time. + */ + timeout = get_jiffies_64() + msecs_to_jiffies(3000); for (i = 0; i < num_dcp; ++i) { u64 jiffies = get_jiffies_64(); @@ -458,6 +462,8 @@ static int apple_drm_init_dcp(struct device *dev) if (ret) dev_warn(dev, "DCP[%d] not ready: %d\n", i, ret); } + /* HACK: Wait for dcp* to settle before a modeset */ + msleep(100); return 0; } diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index cc8f3ebd532f52..842caaa4934b1f 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -345,23 +345,40 @@ int dcp_start(struct platform_device *pdev) if (ret) dev_warn(dcp->dev, "Failed to start system endpoint: %d", ret); - if (dcp->phy) { - if (dcp->fw_compat >= DCP_FIRMWARE_V_13_5) { - ret = ibootep_init(dcp); - if (ret) - dev_warn(dcp->dev, - "Failed to start IBOOT endpoint: %d", - ret); - - ret = dptxep_init(dcp); - if (ret) - dev_warn(dcp->dev, - "Failed to start DPTX endpoint: %d", - ret); - } else - dev_warn(dcp->dev, - "OS firmware incompatible with dptxport EP\n"); - } + if (dcp->phy && dcp->fw_compat >= DCP_FIRMWARE_V_13_5) { + ret = ibootep_init(dcp); + if (ret) + dev_warn(dcp->dev, "Failed to start IBOOT endpoint: %d", + ret); + + ret = dptxep_init(dcp); + if (ret) + dev_warn(dcp->dev, "Failed to start DPTX endpoint: %d", + ret); + else if (dcp->dptxport[0].enabled) { + bool connected; + /* force disconnect on start - necessary if the display + * is already up from m1n1 + */ + dptxport_set_hpd(dcp->dptxport[0].service, false); + dptxport_release_display(dcp->dptxport[0].service); + usleep_range(10 * USEC_PER_MSEC, 25 * USEC_PER_MSEC); + + connected = gpiod_get_value_cansleep(dcp->hdmi_hpd); + dev_info(dcp->dev, "%s: DP2HDMI HPD connected:%d\n", __func__, connected); + + // necessary on j473/j474 but not on j314c + if (connected) + dcp_dptx_connect(dcp, 0); + /* + * Long sleep necessary to ensure dcp delivers timing + * modes with matched color modes. + * 400ms was sufficient on j473 + */ + msleep(500); + } + } else if (dcp->phy) + dev_warn(dcp->dev, "OS firmware incompatible with dptxport EP\n"); ret = iomfb_start_rtkit(dcp); if (ret) @@ -373,17 +390,8 @@ EXPORT_SYMBOL(dcp_start); static int dcp_enable_dp2hdmi_hpd(struct apple_dcp *dcp) { - if (dcp->hdmi_hpd) { - bool connected = gpiod_get_value_cansleep(dcp->hdmi_hpd); - dev_info(dcp->dev, "%s: DP2HDMI HPD connected:%d\n", __func__, connected); - - // necessary on j473/j474 but not on j314c - if (connected) - dcp_dptx_connect(dcp, 0); - - if (dcp->hdmi_hpd_irq) - enable_irq(dcp->hdmi_hpd_irq); - } + if (dcp->hdmi_hpd_irq) + enable_irq(dcp->hdmi_hpd_irq); return 0; } From a285de65b394e18b0eca47b89b53f689ecccc8fa Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 28 Nov 2023 14:27:18 +0100 Subject: [PATCH 0592/1020] drm: apple: dcp: Fix resume with DPTX based display outputs Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 842caaa4934b1f..2978aa08dd7c80 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -1043,6 +1043,13 @@ static int dcp_platform_resume(struct device *dev) if (dcp->hdmi_hpd_irq) enable_irq(dcp->hdmi_hpd_irq); + if (dcp->hdmi_hpd) { + bool connected = gpiod_get_value_cansleep(dcp->hdmi_hpd); + dev_info(dcp->dev, "resume: HPD connected:%d\n", connected); + if (connected) + dcp_dptx_connect(dcp, 0); + } + return 0; } From bca5f75020897627adf928cc0faf1e67d3137380 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 2 Dec 2023 10:26:13 +0100 Subject: [PATCH 0593/1020] drm: apple: Be less noisy about teardown notifies without service Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/afk.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/apple/afk.c b/drivers/gpu/drm/apple/afk.c index 2112e5eeef000d..25c2734b3ea599 100644 --- a/drivers/gpu/drm/apple/afk.c +++ b/drivers/gpu/drm/apple/afk.c @@ -505,6 +505,12 @@ static void afk_recv_handle(struct apple_dcp_afkep *ep, u32 channel, u32 type, ep->endpoint, eshdr->category, channel); return; } + if (subtype == EPIC_SUBTYPE_TEARDOWN) { + dev_dbg(ep->dcp->dev, + "AFK[ep:%02x]: teardown without service on channel %d\n", + ep->endpoint, channel); + return; + } if (subtype != EPIC_SUBTYPE_ANNOUNCE) { dev_err(ep->dcp->dev, "AFK[ep:%02x]: expected announce but got 0x%x on channel %d\n", From 8b9e29b0480e6276f6057845d6aee64861fc5e8f Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 3 Dec 2023 23:57:25 +0100 Subject: [PATCH 0594/1020] drm: apple: dptx: Wait for link config on connect Should make connect more reliable by avoiding hardcoded waits which are either to long or too short. In the second case the display can't be brought up since dcp fails to report any modes during start. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 22 ++++++++++++++-------- drivers/gpu/drm/apple/dptxep.c | 8 ++++++-- drivers/gpu/drm/apple/dptxep.h | 1 + 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 2978aa08dd7c80..dde79ffdafd8e8 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -254,6 +255,8 @@ int dcp_get_connector_type(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(dcp_get_connector_type); +#define DPTX_CONNECT_TIMEOUT msecs_to_jiffies(1000) + static int dcp_dptx_connect(struct apple_dcp *dcp, u32 port) { int ret = 0; @@ -281,8 +284,17 @@ static int dcp_dptx_connect(struct apple_dcp *dcp, u32 port) dcp->dptxport[port].connected = true; mutex_unlock(&dcp->hpd_mutex); - wait_for_completion_timeout(&dcp->dptxport[port].linkcfg_completion, - msecs_to_jiffies(1000)); + ret = wait_for_completion_timeout(&dcp->dptxport[port].linkcfg_completion, + DPTX_CONNECT_TIMEOUT); + if (ret < 0) + dev_warn(dcp->dev, "dcp_dptx_connect: port %d link complete failed:%d\n", + port, ret); + else + dev_dbg(dcp->dev, "dcp_dptx_connect: waited %d ms for link\n", + jiffies_to_msecs(DPTX_CONNECT_TIMEOUT - ret)); + + usleep_range(5, 10); + return 0; out_unlock: @@ -370,12 +382,6 @@ int dcp_start(struct platform_device *pdev) // necessary on j473/j474 but not on j314c if (connected) dcp_dptx_connect(dcp, 0); - /* - * Long sleep necessary to ensure dcp delivers timing - * modes with matched color modes. - * 400ms was sufficient on j473 - */ - msleep(500); } } else if (dcp->phy) dev_warn(dcp->dev, "OS firmware incompatible with dptxport EP\n"); diff --git a/drivers/gpu/drm/apple/dptxep.c b/drivers/gpu/drm/apple/dptxep.c index 7e9d3768a0c85f..d442e8a285568a 100644 --- a/drivers/gpu/drm/apple/dptxep.c +++ b/drivers/gpu/drm/apple/dptxep.c @@ -293,9 +293,14 @@ static int dptxport_call_set_active_lane_count(struct apple_epic_service *servic dptx->phy_ops.dp.set_lanes = 0; } + dptx->lane_count = lane_count; + reply->retcode = cpu_to_le32(retcode); reply->lane_count = cpu_to_le64(lane_count); + if (dptx->lane_count > 0) + complete(&dptx->linkcfg_completion); + return ret; } @@ -329,10 +334,9 @@ dptxport_call_will_change_link_config(struct apple_epic_service *service) static int dptxport_call_did_change_link_config(struct apple_epic_service *service) { - struct dptx_port *dptx = service->cookie; /* assume the link config did change and wait a little bit */ mdelay(10); - complete(&dptx->linkcfg_completion); + return 0; } diff --git a/drivers/gpu/drm/apple/dptxep.h b/drivers/gpu/drm/apple/dptxep.h index 4a0770d43c954c..0bf2534054fd7b 100644 --- a/drivers/gpu/drm/apple/dptxep.h +++ b/drivers/gpu/drm/apple/dptxep.h @@ -55,6 +55,7 @@ struct dptx_port { union phy_configure_opts phy_ops; struct phy *atcphy; struct mux_control *mux; + u32 lane_count; u32 link_rate, pending_link_rate; u32 drive_settings[2]; }; From e28adbe12cf2fe30283fcc4a5f22b0eed2ee6f65 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Fri, 1 Dec 2023 23:41:53 +0100 Subject: [PATCH 0595/1020] drm: apple: Prefer RGB SDR modes DCP color mode scoring seems to prefer high bit depth color modes even when it it would require DSC. For example 12-bit 4k 60 Hz YCbCr 4:4:4 over a 600 MHz HDMI 2.0 link. Prefer 8-/10-bit RGB or YCbCr 4:4:4 modes if available. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb_template.c | 16 ++++++ drivers/gpu/drm/apple/parser.c | 78 ++++++++++++++++++-------- drivers/gpu/drm/apple/parser.h | 68 ++++++++++++++++++++++ 3 files changed, 138 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 980bab05dd00a9..a9714ef7f85739 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -1192,6 +1192,7 @@ int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, { struct dcp_display_mode *mode; struct dcp_wait_cookie *cookie; + struct dcp_color_mode *cmode = NULL; int ret; mode = lookup_mode(dcp, &crtc_state->mode); @@ -1205,6 +1206,21 @@ int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, "set_digital_out_mode(color:%d timing:%d) " DRM_MODE_FMT "\n", mode->color_mode_id, mode->timing_mode_id, DRM_MODE_ARG(&crtc_state->mode)); + if (mode->color_mode_id == mode->sdr_rgb.id) + cmode = &mode->sdr_rgb; + else if (mode->color_mode_id == mode->sdr_444.id) + cmode = &mode->sdr_444; + else if (mode->color_mode_id == mode->sdr.id) + cmode = &mode->sdr; + else if (mode->color_mode_id == mode->best.id) + cmode = &mode->best; + if (cmode) + dev_info(dcp->dev, + "set_digital_out_mode() color mode depth:%hhu format:%u " + "colorimetry:%u eotf:%u range:%u\n", cmode->depth, + cmode->format, cmode->colorimetry, cmode->eotf, + cmode->range); + dcp->mode = (struct dcp_set_digital_out_mode_req){ .color_mode_id = mode->color_mode_id, .timing_mode_id = mode->timing_mode_id diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index a837af1bd0a5e8..fcac87f0fadec0 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -313,14 +313,42 @@ struct color_mode { s64 score; }; -static int parse_color_modes(struct dcp_parse_ctx *handle, s64 *preferred_id) +static int fill_color_mode(struct dcp_color_mode *color, + struct color_mode *cmode) +{ + if (color->score >= cmode->score) + return 0; + + if (cmode->colorimetry < 0 || cmode->colorimetry >= DCP_COLORIMETRY_COUNT) + return -EINVAL; + if (cmode->depth < 8 || cmode->depth > 12) + return -EINVAL; + if (cmode->dynamic_range < 0 || cmode->dynamic_range >= DCP_COLOR_YCBCR_RANGE_COUNT) + return -EINVAL; + if (cmode->eotf < 0 || cmode->eotf >= DCP_EOTF_COUNT) + return -EINVAL; + if (cmode->pixel_encoding < 0 || cmode->pixel_encoding >= DCP_COLOR_FORMAT_COUNT) + return -EINVAL; + + color->score = cmode->score; + color->id = cmode->id; + color->eotf = cmode->eotf; + color->format = cmode->pixel_encoding; + color->colorimetry = cmode->colorimetry; + color->range = cmode->dynamic_range; + color->depth = cmode->depth; + + return 0; +} + +static int parse_color_modes(struct dcp_parse_ctx *handle, + struct dcp_display_mode *out) { struct iterator outer_it; int ret = 0; - s64 best_score = -1, best_score_sdr = -1; - s64 best_id = -1, best_id_sdr = -1; - - *preferred_id = -1; + out->sdr_444.score = -1; + out->sdr_rgb.score = -1; + out->best.score = -1; dcp_parse_foreach_in_array(handle, outer_it) { struct iterator it; @@ -367,25 +395,18 @@ static int parse_color_modes(struct dcp_parse_ctx *handle, s64 *preferred_id) cmode.eotf, cmode.dynamic_range, cmode.pixel_encoding); - if (cmode.eotf == 0) { - if (cmode.score > best_score_sdr) { - best_score_sdr = cmode.score; - best_id_sdr = cmode.id; - } - } else { - if (cmode.score > best_score) { - best_score = cmode.score; - best_id = cmode.id; - } + if (cmode.eotf == DCP_EOTF_SDR_GAMMA) { + if (cmode.pixel_encoding == DCP_COLOR_FORMAT_RGB && + cmode.depth <= 10) + fill_color_mode(&out->sdr_rgb, &cmode); + else if (cmode.pixel_encoding == DCP_COLOR_FORMAT_YCBCR444 && + cmode.depth <= 10) + fill_color_mode(&out->sdr_444, &cmode); + fill_color_mode(&out->sdr, &cmode); } + fill_color_mode(&out->best, &cmode); } - /* prefer SDR color modes as long as HDR is not supported */ - if (best_score_sdr >= 0) - *preferred_id = best_id_sdr; - else if (best_score >= 0) - *preferred_id = best_id; - return 0; } @@ -427,7 +448,7 @@ static int parse_mode(struct dcp_parse_ctx *handle, else if (!strcmp(key, "VerticalAttributes")) ret = parse_dimension(it.handle, &vert); else if (!strcmp(key, "ColorModes")) - ret = parse_color_modes(it.handle, &best_color_mode); + ret = parse_color_modes(it.handle, out); else if (!strcmp(key, "ID")) ret = parse_int(it.handle, &id); else if (!strcmp(key, "IsVirtual")) @@ -445,8 +466,17 @@ static int parse_mode(struct dcp_parse_ctx *handle, return ret; } } - - trace_iomfb_parse_mode_success(id, &horiz, &vert, best_color_mode, is_virtual, *score); + if (out->sdr_rgb.score >= 0) + best_color_mode = out->sdr_rgb.id; + else if (out->sdr_444.score >= 0) + best_color_mode = out->sdr_444.id; + else if (out->sdr.score >= 0) + best_color_mode = out->sdr.id; + else if (out->best.score >= 0) + best_color_mode = out->best.id; + + trace_iomfb_parse_mode_success(id, &horiz, &vert, best_color_mode, + is_virtual, *score); /* * Reject modes without valid color mode. diff --git a/drivers/gpu/drm/apple/parser.h b/drivers/gpu/drm/apple/parser.h index f9cc3718fa84f7..f867416070e49d 100644 --- a/drivers/gpu/drm/apple/parser.h +++ b/drivers/gpu/drm/apple/parser.h @@ -15,6 +15,70 @@ struct dcp_parse_ctx { u32 pos, len; }; +enum dcp_color_eotf { + DCP_EOTF_SDR_GAMMA = 0, // "SDR gamma" + DCP_EOTF_HDR_GAMMA = 1, // "HDR gamma" + DCP_EOTF_ST_2084 = 2, // "ST 2084 (PQ)" + DCP_EOTF_BT_2100 = 3, // "BT.2100 (HLG)" + DCP_EOTF_COUNT +}; + +enum dcp_color_format { + DCP_COLOR_FORMAT_RGB = 0, // "RGB" + DCP_COLOR_FORMAT_YCBCR420 = 1, // "YUV 4:2:0" + DCP_COLOR_FORMAT_YCBCR422 = 3, // "YUV 4:2:2" + DCP_COLOR_FORMAT_YCBCR444 = 2, // "YUV 4:4:4" + DCP_COLOR_FORMAT_DV_NATIVE = 4, // "DolbyVision (native)" + DCP_COLOR_FORMAT_DV_HDMI = 5, // "DolbyVision (HDMI)" + DCP_COLOR_FORMAT_YCBCR422_DP = 6, // "YCbCr 4:2:2 (DP tunnel)" + DCP_COLOR_FORMAT_YCBCR422_HDMI = 7, // "YCbCr 4:2:2 (HDMI tunnel)" + DCP_COLOR_FORMAT_DV_LL_YCBCR422 = 8, // "DolbyVision LL YCbCr 4:2:2" + DCP_COLOR_FORMAT_DV_LL_YCBCR422_DP = 9, // "DolbyVision LL YCbCr 4:2:2 (DP)" + DCP_COLOR_FORMAT_DV_LL_YCBCR422_HDMI = 10, // "DolbyVision LL YCbCr 4:2:2 (HDMI)" + DCP_COLOR_FORMAT_DV_LL_YCBCR444 = 11, // "DolbyVision LL YCbCr 4:4:4" + DCP_COLOR_FORMAT_DV_LL_RGB422 = 12, // "DolbyVision LL RGB 4:2:2" + DCP_COLOR_FORMAT_GRGB_BLUE_422 = 13, // "GRGB as YCbCr422 (Even line blue)" + DCP_COLOR_FORMAT_GRGB_RED_422 = 14, // "GRGB as YCbCr422 (Even line red)" + DCP_COLOR_FORMAT_COUNT +}; + +enum dcp_colorimetry { + DCP_COLORIMETRY_BT601 = 0, // "SMPTE 170M/BT.601" + DCP_COLORIMETRY_BT709 = 1, // "BT.701" + DCP_COLORIMETRY_XVYCC_601 = 2, // "xvYCC601" + DCP_COLORIMETRY_XVYCC_709 = 3, // "xvYCC709" + DCP_COLORIMETRY_SYCC_601 = 4, // "sYCC601" + DCP_COLORIMETRY_ADOBE_YCC_601 = 5, // "AdobeYCC601" + DCP_COLORIMETRY_BT2020_CYCC = 6, // "BT.2020 (c)" + DCP_COLORIMETRY_BT2020_YCC = 7, // "BT.2020 (nc)" + DCP_COLORIMETRY_VSVDB = 8, // "DolbyVision VSVDB" + DCP_COLORIMETRY_BT2020_RGB = 9, // "BT.2020 (RGB)" + DCP_COLORIMETRY_SRGB = 10, // "sRGB" + DCP_COLORIMETRY_SCRGB = 11, // "scRGB" + DCP_COLORIMETRY_SCRGB_FIXED = 12, // "scRGBfixed" + DCP_COLORIMETRY_ADOBE_RGB = 13, // "AdobeRGB" + DCP_COLORIMETRY_DCI_P3_RGB_D65 = 14, // "DCI-P3 (D65)" + DCP_COLORIMETRY_DCI_P3_RGB_THEATER = 15, // "DCI-P3 (Theater)" + DCP_COLORIMETRY_RGB = 16, // "Default RGB" + DCP_COLORIMETRY_COUNT +}; + +enum dcp_color_range { + DCP_COLOR_YCBCR_RANGE_FULL = 0, + DCP_COLOR_YCBCR_RANGE_LIMITED = 1, + DCP_COLOR_YCBCR_RANGE_COUNT +}; + +struct dcp_color_mode { + s64 score; + u32 id; + enum dcp_color_eotf eotf; + enum dcp_color_format format; + enum dcp_colorimetry colorimetry; + enum dcp_color_range range; + u8 depth; +}; + /* * Represents a single display mode. These mode objects are populated at * runtime based on the TimingElements dictionary sent by the DCP. @@ -23,6 +87,10 @@ struct dcp_display_mode { struct drm_display_mode mode; u32 color_mode_id; u32 timing_mode_id; + struct dcp_color_mode sdr_rgb; + struct dcp_color_mode sdr_444; + struct dcp_color_mode sdr; + struct dcp_color_mode best; }; struct dimension { From fa0684d1e29da39606e06969b65cb43b1167b5f3 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 4 Dec 2023 23:27:29 +0100 Subject: [PATCH 0596/1020] drm: apple: iomfb: Always parse DisplayAttributes Fixes missing physical display dimensions for HDMI display on Macbook Pros. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb_template.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index a9714ef7f85739..49ddca62075853 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -570,17 +570,12 @@ static bool dcpep_process_chunks(struct apple_dcp *dcp, if (dcp->nr_modes == 0) dev_warn(dcp->dev, "TimingElements without valid modes!\n"); } else if (!strcmp(req->key, "DisplayAttributes")) { - /* DisplayAttributes are empty for integrated displays, use - * display dimensions read from the devicetree - */ - if (dcp->main_display) { - ret = parse_display_attributes(&ctx, &dcp->width_mm, - &dcp->height_mm); + ret = parse_display_attributes(&ctx, &dcp->width_mm, + &dcp->height_mm); - if (ret) { - dev_warn(dcp->dev, "failed to parse display attribs\n"); - return false; - } + if (ret) { + dev_warn(dcp->dev, "failed to parse display attribs\n"); + return false; } dcp_set_dimensions(dcp); From 23fc65d127e050e8e91e1b48b20d2d96ff91cec3 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 10 Dec 2023 13:01:22 +0100 Subject: [PATCH 0597/1020] drm: apple: parser: constify parser data Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/parser.c | 40 +++++++++++++++++----------------- drivers/gpu/drm/apple/parser.h | 4 ++-- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index fcac87f0fadec0..958b94dd1b0f3e 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -30,9 +30,9 @@ struct dcp_parse_tag { bool last : 1; } __packed; -static void *parse_bytes(struct dcp_parse_ctx *ctx, size_t count) +static const void *parse_bytes(struct dcp_parse_ctx *ctx, size_t count) { - void *ptr = ctx->blob + ctx->pos; + const void *ptr = ctx->blob + ctx->pos; if (ctx->pos + count > ctx->len) return ERR_PTR(-EINVAL); @@ -41,14 +41,14 @@ static void *parse_bytes(struct dcp_parse_ctx *ctx, size_t count) return ptr; } -static u32 *parse_u32(struct dcp_parse_ctx *ctx) +static const u32 *parse_u32(struct dcp_parse_ctx *ctx) { return parse_bytes(ctx, sizeof(u32)); } -static struct dcp_parse_tag *parse_tag(struct dcp_parse_ctx *ctx) +static const struct dcp_parse_tag *parse_tag(struct dcp_parse_ctx *ctx) { - struct dcp_parse_tag *tag; + const struct dcp_parse_tag *tag; /* Align to 32-bits */ ctx->pos = round_up(ctx->pos, 4); @@ -64,10 +64,10 @@ static struct dcp_parse_tag *parse_tag(struct dcp_parse_ctx *ctx) return tag; } -static struct dcp_parse_tag *parse_tag_of_type(struct dcp_parse_ctx *ctx, +static const struct dcp_parse_tag *parse_tag_of_type(struct dcp_parse_ctx *ctx, enum dcp_parse_type type) { - struct dcp_parse_tag *tag = parse_tag(ctx); + const struct dcp_parse_tag *tag = parse_tag(ctx); if (IS_ERR(tag)) return tag; @@ -80,7 +80,7 @@ static struct dcp_parse_tag *parse_tag_of_type(struct dcp_parse_ctx *ctx, static int skip(struct dcp_parse_ctx *handle) { - struct dcp_parse_tag *tag = parse_tag(handle); + const struct dcp_parse_tag *tag = parse_tag(handle); int ret = 0; int i; @@ -132,7 +132,7 @@ static int skip_pair(struct dcp_parse_ctx *handle) static bool consume_string(struct dcp_parse_ctx *ctx, const char *specimen) { - struct dcp_parse_tag *tag; + const struct dcp_parse_tag *tag; const char *key; ctx->pos = round_up(ctx->pos, 4); @@ -155,7 +155,7 @@ static bool consume_string(struct dcp_parse_ctx *ctx, const char *specimen) /* Caller must free the result */ static char *parse_string(struct dcp_parse_ctx *handle) { - struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_STRING); + const struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_STRING); const char *in; char *out; @@ -175,8 +175,8 @@ static char *parse_string(struct dcp_parse_ctx *handle) static int parse_int(struct dcp_parse_ctx *handle, s64 *value) { - void *tag = parse_tag_of_type(handle, DCP_TYPE_INT64); - s64 *in; + const void *tag = parse_tag_of_type(handle, DCP_TYPE_INT64); + const s64 *in; if (IS_ERR(tag)) return PTR_ERR(tag); @@ -192,7 +192,7 @@ static int parse_int(struct dcp_parse_ctx *handle, s64 *value) static int parse_bool(struct dcp_parse_ctx *handle, bool *b) { - struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_BOOL); + const struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_BOOL); if (IS_ERR(tag)) return PTR_ERR(tag); @@ -201,10 +201,10 @@ static int parse_bool(struct dcp_parse_ctx *handle, bool *b) return 0; } -static int parse_blob(struct dcp_parse_ctx *handle, size_t size, u8 **blob) +static int parse_blob(struct dcp_parse_ctx *handle, size_t size, u8 const **blob) { - struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_BLOB); - u8 *out; + const struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_BLOB); + const u8 *out; if (IS_ERR(tag)) return PTR_ERR(tag); @@ -229,7 +229,7 @@ struct iterator { static int iterator_begin(struct dcp_parse_ctx *handle, struct iterator *it, bool dict) { - struct dcp_parse_tag *tag; + const struct dcp_parse_tag *tag; enum dcp_parse_type type = dict ? DCP_TYPE_DICTIONARY : DCP_TYPE_ARRAY; *it = (struct iterator) { @@ -250,9 +250,9 @@ static int iterator_begin(struct dcp_parse_ctx *handle, struct iterator *it, #define dcp_parse_foreach_in_dict(handle, it) \ for (iterator_begin(handle, &it, true); it.idx < it.len; ++it.idx) -int parse(void *blob, size_t size, struct dcp_parse_ctx *ctx) +int parse(const void *blob, size_t size, struct dcp_parse_ctx *ctx) { - u32 *header; + const u32 *header; *ctx = (struct dcp_parse_ctx) { .blob = blob, @@ -912,7 +912,7 @@ static int parse_mode_in_avep_element(struct dcp_parse_ctx *handle, return ret; } } else if (consume_string(it.handle, "ElementData")) { - u8 *blob; + const u8 *blob; ret = parse_blob(it.handle, sizeof(*cookie), &blob); if (ret) diff --git a/drivers/gpu/drm/apple/parser.h b/drivers/gpu/drm/apple/parser.h index f867416070e49d..a008e220cec3dd 100644 --- a/drivers/gpu/drm/apple/parser.h +++ b/drivers/gpu/drm/apple/parser.h @@ -11,7 +11,7 @@ struct apple_dcp; struct dcp_parse_ctx { struct apple_dcp *dcp; - void *blob; + const void *blob; u32 pos, len; }; @@ -98,7 +98,7 @@ struct dimension { s64 precise_sync_rate; }; -int parse(void *blob, size_t size, struct dcp_parse_ctx *ctx); +int parse(const void *blob, size_t size, struct dcp_parse_ctx *ctx); struct dcp_display_mode *enumerate_modes(struct dcp_parse_ctx *handle, unsigned int *count, int width_mm, int height_mm, unsigned notch_height); From e6aaf217a64c3897816472397f25abeafd068d84 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 10 Dec 2023 13:27:12 +0100 Subject: [PATCH 0598/1020] drm: apple: epic: Pass full notfiy/report payload to handler The payload is not necessarily epic_std_service_ap_call. The powerlog service on the system endpoint passes serialized dictionaries as payload. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/afk.c | 18 +++--------------- drivers/gpu/drm/apple/afk.h | 4 +++- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/apple/afk.c b/drivers/gpu/drm/apple/afk.c index 25c2734b3ea599..99d579d5ce473a 100644 --- a/drivers/gpu/drm/apple/afk.c +++ b/drivers/gpu/drm/apple/afk.c @@ -445,21 +445,9 @@ static void afk_recv_handle_std_service(struct apple_dcp_afkep *ep, u32 channel, } if (type == EPIC_TYPE_NOTIFY && eshdr->category == EPIC_CAT_REPORT) { - struct epic_std_service_ap_call *call = payload; - size_t call_size; - - if (payload_size < sizeof(*call)) - return; - - call_size = le32_to_cpu(call->len); - if (payload_size < sizeof(*call) + call_size) - return; - - if (!service->ops->report) - return; - - service->ops->report(service, le32_to_cpu(call->type), - payload + sizeof(*call), call_size); + if (service->ops->report) + service->ops->report(service, le16_to_cpu(eshdr->type), + payload, payload_size); return; } diff --git a/drivers/gpu/drm/apple/afk.h b/drivers/gpu/drm/apple/afk.h index 1fdb4100352b25..737288b1346b28 100644 --- a/drivers/gpu/drm/apple/afk.h +++ b/drivers/gpu/drm/apple/afk.h @@ -49,6 +49,8 @@ struct apple_epic_service { void *cookie; }; +enum epic_subtype; + struct apple_epic_service_ops { const char name[32]; @@ -57,7 +59,7 @@ struct apple_epic_service_ops { int (*call)(struct apple_epic_service *service, u32 idx, const void *data, size_t data_size, void *reply, size_t reply_size); - int (*report)(struct apple_epic_service *service, u32 idx, + int (*report)(struct apple_epic_service *service, enum epic_subtype type, const void *data, size_t data_size); void (*teardown)(struct apple_epic_service *service); }; From 448601c50be139a20a018c9d394de4be9377fc41 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 10 Dec 2023 13:40:14 +0100 Subject: [PATCH 0599/1020] drm: apple: epic: systemep: Parse "mNits" log events The 13.5 firmware has stopped updating the NITS property on backlight brightness changes. Parse system log events instead which report backlight's brightness in millinits. Fixes the backlight device's "actual_brightness" property used by the systemd backlight service to save and restore brightness. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/parser.c | 48 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/apple/parser.h | 9 ++++++ drivers/gpu/drm/apple/systemep.c | 37 ++++++++++++++++++++++++ 3 files changed, 94 insertions(+) diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index 958b94dd1b0f3e..6d629bb0dfc45e 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -983,3 +983,51 @@ int parse_sound_mode(struct dcp_parse_ctx *handle, return 0; } EXPORT_SYMBOL_GPL(parse_sound_mode); + +int parse_system_log_mnits(struct dcp_parse_ctx *handle, struct dcp_system_ev_mnits *entry) +{ + struct iterator it; + int ret; + s64 mnits = -1; + s64 idac = -1; + s64 timestamp = -1; + bool type_match = false; + + dcp_parse_foreach_in_dict(handle, it) { + char *key = parse_string(it.handle); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + } else if (!strcmp(key, "mNits")) { + ret = parse_int(it.handle, &mnits); + } else if (!strcmp(key, "iDAC")) { + ret = parse_int(it.handle, &idac); + } else if (!strcmp(key, "logEvent")) { + const char * value = parse_string(it.handle); + if (!IS_ERR_OR_NULL(value)) { + type_match = strcmp(value, "Display (Event Forward)") == 0; + kfree(value); + } + } else if (!strcmp(key, "timestamp")) { + ret = parse_int(it.handle, ×tamp); + } else { + skip(it.handle); + } + + if (!IS_ERR_OR_NULL(key)) + kfree(key); + + if (ret) { + pr_err("dcp parser: failed to parse mNits sys event\n"); + return ret; + } + } + + if (!type_match || mnits < 0 || idac < 0 || timestamp < 0) + return -EINVAL; + + entry->millinits = mnits; + entry->idac = idac; + entry->timestamp = timestamp; + + return 0; +} diff --git a/drivers/gpu/drm/apple/parser.h b/drivers/gpu/drm/apple/parser.h index a008e220cec3dd..2f52e063bbd426 100644 --- a/drivers/gpu/drm/apple/parser.h +++ b/drivers/gpu/drm/apple/parser.h @@ -126,4 +126,13 @@ int parse_sound_mode(struct dcp_parse_ctx *handle, struct snd_pcm_chmap_elem *chmap, struct dcp_sound_cookie *cookie); +struct dcp_system_ev_mnits { + u32 timestamp; + u32 millinits; + u32 idac; +}; + +int parse_system_log_mnits(struct dcp_parse_ctx *handle, + struct dcp_system_ev_mnits *entry); + #endif diff --git a/drivers/gpu/drm/apple/systemep.c b/drivers/gpu/drm/apple/systemep.c index 5383a83f1e6c28..9fe7a0ce495aab 100644 --- a/drivers/gpu/drm/apple/systemep.c +++ b/drivers/gpu/drm/apple/systemep.c @@ -5,6 +5,7 @@ #include "afk.h" #include "dcp.h" +#include "parser.h" static bool enable_verbose_logging; module_param(enable_verbose_logging, bool, 0644); @@ -66,6 +67,41 @@ static void powerlog_init(struct apple_epic_service *service, const char *name, { } +static int powerlog_report(struct apple_epic_service *service, enum epic_subtype type, + const void *data, size_t data_size) +{ + struct dcp_system_ev_mnits mnits; + struct dcp_parse_ctx parse_ctx; + struct apple_dcp *dcp = service->ep->dcp; + int ret; + + dev_dbg(dcp->dev, "systemep[ch:%u]: report type:%02x len:%zu\n", + service->channel, type, data_size); + + if (type != EPIC_SUBTYPE_STD_SERVICE) + return 0; + + ret = parse(data, data_size, &parse_ctx); + if (ret) { + dev_warn(service->ep->dcp->dev, "systemep: failed to parse report: %d\n", ret); + return ret; + } + + ret = parse_system_log_mnits(&parse_ctx, &mnits); + if (ret) { + /* ignore parse errors in the case dcp sends unknown log events */ + dev_dbg(dcp->dev, "systemep: failed to parse mNits event: %d\n", ret); + return 0; + } + + dev_dbg(dcp->dev, "systemep: mNits event: Nits: %u.%03u, iDAC: %u\n", + mnits.millinits / 1000, mnits.millinits % 1000, mnits.idac); + + dcp->brightness.nits = mnits.millinits / 1000; + + return 0; +} + static const struct apple_epic_service_ops systemep_ops[] = { { .name = "system", @@ -74,6 +110,7 @@ static const struct apple_epic_service_ops systemep_ops[] = { { .name = "powerlog-service", .init = powerlog_init, + .report = powerlog_report, }, {} }; From 7cc79fb3f3b452f13e5ab4ae4e35b6d55d76f1ce Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 11 Dec 2023 13:37:48 +0100 Subject: [PATCH 0600/1020] fixup! mux: apple DP xbar: Add Apple silicon DisplayPort crossbar Signed-off-by: Janne Grunau --- drivers/mux/apple-display-crossbar.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/mux/apple-display-crossbar.c b/drivers/mux/apple-display-crossbar.c index 7acd85c8b4eea4..992321fd38631e 100644 --- a/drivers/mux/apple-display-crossbar.c +++ b/drivers/mux/apple-display-crossbar.c @@ -214,12 +214,12 @@ static int apple_dpxbar_set_t602x(struct mux_control *mux, int state) spin_unlock_irqrestore(&dpxbar->lock, flags); if (enable) - dev_err(dpxbar->dev, "Switched %s to dispext%u,%u\n", - apple_dpxbar_names[index], mux_state >> 1, - mux_state & 1); + dev_info(dpxbar->dev, "Switched %s to dispext%u,%u\n", + apple_dpxbar_names[index], mux_state >> 1, + mux_state & 1); else - dev_err(dpxbar->dev, "Switched %s to disconnected state\n", - apple_dpxbar_names[index]); + dev_info(dpxbar->dev, "Switched %s to disconnected state\n", + apple_dpxbar_names[index]); return ret; } @@ -349,12 +349,12 @@ static int apple_dpxbar_set(struct mux_control *mux, int state) spin_unlock_irqrestore(&dpxbar->lock, flags); if (enable) - dev_err(dpxbar->dev, "Switched %s to dispext%u,%u\n", - apple_dpxbar_names[index], mux_state >> 1, - mux_state & 1); + dev_info(dpxbar->dev, "Switched %s to dispext%u,%u\n", + apple_dpxbar_names[index], mux_state >> 1, + mux_state & 1); else - dev_err(dpxbar->dev, "Switched %s to disconnected state\n", - apple_dpxbar_names[index]); + dev_info(dpxbar->dev, "Switched %s to disconnected state\n", + apple_dpxbar_names[index]); return ret; } From 14147d960af209043e202746b55c19a74e20e535 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 27 Feb 2024 19:17:45 +0100 Subject: [PATCH 0601/1020] fixup! drm: apple: afk: Use linear array of services --- drivers/gpu/drm/apple/afk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/afk.c b/drivers/gpu/drm/apple/afk.c index 99d579d5ce473a..9fbcd18878e814 100644 --- a/drivers/gpu/drm/apple/afk.c +++ b/drivers/gpu/drm/apple/afk.c @@ -236,7 +236,7 @@ static void afk_recv_handle_init(struct apple_dcp_afkep *ep, u32 channel, return; } - strlcpy(name, payload, sizeof(name)); + strscpy(name, payload, sizeof(name)); /* * in DCP firmware 13.2 DCP reports interface-name as name which starts From c2c4ac4eaf6144f7f7ad5a10facb61120e46dd28 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 27 Feb 2024 19:17:45 +0100 Subject: [PATCH 0602/1020] fixup! WIP: drm/apple: Add DCP display driver --- drivers/gpu/drm/apple/apple_drv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 3bfa3c9a461ae4..d17fe03f96557c 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include From 6b12e9be4d26c46dd4c64fd877af4aced9365383 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 27 Feb 2024 19:17:45 +0100 Subject: [PATCH 0603/1020] fixup! drm: apple: DCP AFK/EPIC support --- drivers/gpu/drm/apple/afk.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/apple/afk.c b/drivers/gpu/drm/apple/afk.c index 9fbcd18878e814..34d3182a6a4038 100644 --- a/drivers/gpu/drm/apple/afk.c +++ b/drivers/gpu/drm/apple/afk.c @@ -2,6 +2,7 @@ /* Copyright 2022 Sven Peter */ #include +#include #include #include #include From b219e0ec2f35d73f4cddb8ee0c41416c500daf50 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 22 Jan 2024 21:21:25 +0100 Subject: [PATCH 0604/1020] fixup! drm: apple: DCP AFK/EPIC support Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/afk.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/apple/afk.c b/drivers/gpu/drm/apple/afk.c index 34d3182a6a4038..52a5bf5f8a6479 100644 --- a/drivers/gpu/drm/apple/afk.c +++ b/drivers/gpu/drm/apple/afk.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2022 Sven Peter */ +#include #include #include #include From cfe99e5c4db4068d3a6e0d1c4a6ca3cafac677a1 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 22 Jan 2024 21:22:33 +0100 Subject: [PATCH 0605/1020] fixup! drm: apple: Add DPTX support Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dptxep.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/apple/dptxep.c b/drivers/gpu/drm/apple/dptxep.c index d442e8a285568a..56b86966e807a7 100644 --- a/drivers/gpu/drm/apple/dptxep.c +++ b/drivers/gpu/drm/apple/dptxep.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2022 Sven Peter */ +#include #include #include #include From 48f195c5e8f5cc2b488aaac0b6c22126c3cfcaf8 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 22 Jan 2024 21:23:46 +0100 Subject: [PATCH 0606/1020] fixup! gpu: drm: apple: iomfb: Use FIELD_{GET,PREP} Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 1f69eb107663d9..b179c183f46529 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -2,6 +2,7 @@ /* Copyright 2021 Alyssa Rosenzweig */ #include +#include #include #include #include From a5d4b131724b76bf05ad0dc4591d069283149cf2 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 22 Jan 2024 21:27:03 +0100 Subject: [PATCH 0607/1020] fixup! WIP: drm/apple: Add DCP display driver Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index d17fe03f96557c..5dcc3c119c16c3 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -537,7 +537,7 @@ static int apple_drm_init(struct device *dev) if (ret) goto err_unbind; - drm_fbdev_generic_setup(&apple->drm, 32); + drm_fbdev_dma_setup(&apple->drm, 32); return 0; From 9053a6bff97ee1bac2a34d7b6b0bb618438c8f1f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 17 Jan 2024 11:44:10 +0100 Subject: [PATCH 0608/1020] drm: apple: mark local functions static With linux-6.8, the kernel warns about functions that have no extern declaration, so mark both of these static. Fixes: 2d782b0d007d ("gpu: drm: apple: Add sound mode parsing") Signed-off-by: Arnd Bergmann --- drivers/gpu/drm/apple/parser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index 6d629bb0dfc45e..4a4a6458ecebc9 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -680,7 +680,7 @@ int parse_epic_service_init(struct dcp_parse_ctx *handle, const char **name, return ret; } -int parse_sample_rate_bit(struct dcp_parse_ctx *handle, unsigned int *ratebit) +static int parse_sample_rate_bit(struct dcp_parse_ctx *handle, unsigned int *ratebit) { s64 rate; int ret = parse_int(handle, &rate); @@ -701,7 +701,7 @@ int parse_sample_rate_bit(struct dcp_parse_ctx *handle, unsigned int *ratebit) return 0; } -int parse_sample_fmtbit(struct dcp_parse_ctx *handle, u64 *fmtbit) +static int parse_sample_fmtbit(struct dcp_parse_ctx *handle, u64 *fmtbit) { s64 sample_size; int ret = parse_int(handle, &sample_size); From a527049c33ee52f68928d6fa75a4424876b999a0 Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Thu, 11 Jan 2024 11:39:10 +0100 Subject: [PATCH 0609/1020] drm/apple: Add missing RTKit Kconfig dependency Signed-off-by: Alyssa Ross --- drivers/gpu/drm/apple/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/apple/Kconfig b/drivers/gpu/drm/apple/Kconfig index fe69b04a912f93..1bffe0f48ebff5 100644 --- a/drivers/gpu/drm/apple/Kconfig +++ b/drivers/gpu/drm/apple/Kconfig @@ -3,6 +3,7 @@ config DRM_APPLE tristate "DRM Support for Apple display controllers" depends on DRM && OF && ARM64 depends on ARCH_APPLE || COMPILE_TEST + depends on APPLE_RTKIT depends on SND select DRM_KMS_HELPER select DRM_KMS_DMA_HELPER From 2c8d1faa7def3c5df55b80b81812402fd295eb89 Mon Sep 17 00:00:00 2001 From: Jonathan Gray Date: Mon, 22 Jan 2024 18:54:31 +1100 Subject: [PATCH 0610/1020] drm/apple: spelling fixes Signed-off-by: Jonathan Gray --- drivers/gpu/drm/apple/apple_drv.c | 2 +- drivers/gpu/drm/apple/dcp-internal.h | 2 +- drivers/gpu/drm/apple/dcp.c | 4 ++-- drivers/gpu/drm/apple/iomfb.c | 2 +- drivers/gpu/drm/apple/iomfb_template.c | 2 +- drivers/gpu/drm/apple/iomfb_v13_3.c | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 5dcc3c119c16c3..8c2ec2625f1bcf 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -458,7 +458,7 @@ static int apple_drm_init_dcp(struct device *dev) ret = dcp_wait_ready(dcp[i], wait); /* There is nothing we can do if a dcp/dcpext does not boot * (successfully). Ignoring it should not do any harm now. - * Needs to reevaluated whenn adding dcpext support. + * Needs to reevaluated when adding dcpext support. */ if (ret) dev_warn(dev, "DCP[%d] not ready: %d\n", i, ret); diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 24d23364e8f415..e625ea574b88ae 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -95,7 +95,7 @@ struct dcp_panel { int width_mm; /// panel height in millimeter int height_mm; - /// panel has a mini-LED backllight + /// panel has a mini-LED backlight bool has_mini_led; }; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index dde79ffdafd8e8..73aaa2358f9d2f 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -808,7 +808,7 @@ static int dcp_comp_bind(struct device *dev, struct device *main, void *data) if (dcp->notch_height > 0) dev_info(dev, "Detected display with notch of %u pixel\n", dcp->notch_height); - /* intialize brightness scale to a sensible default to avoid divide by 0*/ + /* initialize brightness scale to a sensible default to avoid divide by 0*/ dcp->brightness.scale = 65536; panel_np = of_get_compatible_child(dev->of_node, "apple,panel-mini-led"); if (panel_np) @@ -877,7 +877,7 @@ static int dcp_comp_bind(struct device *dev, struct device *main, void *data) dcp->rtk = devm_apple_rtkit_init(dev, dcp, "mbox", 0, &rtkit_ops); if (IS_ERR(dcp->rtk)) return dev_err_probe(dev, PTR_ERR(dcp->rtk), - "Failed to intialize RTKit"); + "Failed to initialize RTKit"); ret = apple_rtkit_wake(dcp->rtk); if (ret) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index b179c183f46529..2944911ced770a 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -274,7 +274,7 @@ static void dcpep_handle_cb(struct apple_dcp *dcp, enum dcp_context_id context, out = in + hdr->in_len; // TODO: verify that in_len and out_len match our prototypes - // for now just clear the out data to have at least consistant results + // for now just clear the out data to have at least consistent results if (hdr->out_len) memset(out, 0, hdr->out_len); diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 49ddca62075853..7c8dc5a25d143c 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -326,7 +326,7 @@ static void dcpep_cb_unmap_piodma(struct apple_dcp *dcp, /* * Allocate an IOVA contiguous buffer mapped to the DCP. The buffer need not be - * physically contigiuous, however we should save the sgtable in case the + * physically contiguous, however we should save the sgtable in case the * buffer needs to be later mapped for PIODMA. */ static struct dcp_allocate_buffer_resp diff --git a/drivers/gpu/drm/apple/iomfb_v13_3.c b/drivers/gpu/drm/apple/iomfb_v13_3.c index 1ee29112be4543..115490fd9cc6e3 100644 --- a/drivers/gpu/drm/apple/iomfb_v13_3.c +++ b/drivers/gpu/drm/apple/iomfb_v13_3.c @@ -53,7 +53,7 @@ static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = { [110] = trampoline_true, /* create_pmu_service */ [111] = trampoline_true, /* create_iomfb_service */ [112] = trampoline_create_backlight_service, - [113] = trampoline_true, /* create_nvram_servce? */ + [113] = trampoline_true, /* create_nvram_service? */ [114] = trampoline_get_tiling_state, [115] = trampoline_false, /* set_tiling_state */ [120] = dcpep_cb_boot_1, From 6fdac4fc5d0ade5addd61f0cfc4cf8b6334eb230 Mon Sep 17 00:00:00 2001 From: Mark Kettenis Date: Thu, 28 Dec 2023 11:41:55 +0100 Subject: [PATCH 0611/1020] drm: apple: backlight: force backlight update after resume If the DCP firmware indicates that it didn't restore the brightness, schedule an update. Wait for 1 frame duration and check if the brightness update has been taken care of by a swap that happened in the meantime. Fixes restoring the brightness after resume when running on a dumb framebuffer where swaps may not happen for a very long time. Signed-off-by: Mark Kettenis --- drivers/gpu/drm/apple/dcp-internal.h | 3 +++ drivers/gpu/drm/apple/dcp.c | 10 +++++++++ drivers/gpu/drm/apple/dcp_backlight.c | 31 +++++++++++++++----------- drivers/gpu/drm/apple/iomfb_template.c | 1 + 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index e625ea574b88ae..7fbca0b6bb9778 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -212,6 +212,8 @@ struct apple_dcp { /* Workqueue for updating the initial initial brightness */ struct work_struct bl_register_wq; struct mutex bl_register_mutex; + /* Workqueue for updating the brightness */ + struct work_struct bl_update_wq; /* integrated panel if present */ struct dcp_panel panel; @@ -241,6 +243,7 @@ struct apple_dcp { }; int dcp_backlight_register(struct apple_dcp *dcp); +int dcp_backlight_update(struct apple_dcp *dcp); bool dcp_has_panel(struct apple_dcp *dcp); #define DCP_AUDIO_MAX_CHANS 15 diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 73aaa2358f9d2f..a0e3d39e147bbc 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -515,6 +515,15 @@ static void dcp_work_register_backlight(struct work_struct *work) mutex_unlock(&dcp->bl_register_mutex); } +static void dcp_work_update_backlight(struct work_struct *work) +{ + struct apple_dcp *dcp; + + dcp = container_of(work, struct apple_dcp, bl_update_wq); + + dcp_backlight_update(dcp); +} + static int dcp_create_piodma_iommu_dev(struct apple_dcp *dcp) { int ret; @@ -835,6 +844,7 @@ static int dcp_comp_bind(struct device *dev, struct device *main, void *data) dcp->connector_type = DRM_MODE_CONNECTOR_eDP; INIT_WORK(&dcp->bl_register_wq, dcp_work_register_backlight); mutex_init(&dcp->bl_register_mutex); + INIT_WORK(&dcp->bl_update_wq, dcp_work_update_backlight); } else if (of_property_match_string(dev->of_node, "apple,connector-type", "HDMI-A") >= 0) dcp->connector_type = DRM_MODE_CONNECTOR_HDMIA; else if (of_property_match_string(dev->of_node, "apple,connector-type", "DP") >= 0) diff --git a/drivers/gpu/drm/apple/dcp_backlight.c b/drivers/gpu/drm/apple/dcp_backlight.c index 0eeb3d6d92c5a2..dfc78f3ce37b0d 100644 --- a/drivers/gpu/drm/apple/dcp_backlight.c +++ b/drivers/gpu/drm/apple/dcp_backlight.c @@ -172,20 +172,8 @@ static int drm_crtc_set_brightness(struct apple_dcp *dcp) return ret; } -static int dcp_set_brightness(struct backlight_device *bd) +int dcp_backlight_update(struct apple_dcp *dcp) { - int ret = 0; - struct apple_dcp *dcp = bl_get_data(bd); - struct drm_modeset_acquire_ctx ctx; - int brightness = backlight_get_brightness(bd); - - DRM_MODESET_LOCK_ALL_BEGIN(dcp->crtc->base.dev, ctx, 0, ret); - - dcp->brightness.dac = calculate_dac(dcp, brightness); - dcp->brightness.update = true; - - DRM_MODESET_LOCK_ALL_END(dcp->crtc->base.dev, ctx, ret); - /* * Do not actively try to change brightness if no mode is set. * TODO: should this be reflected the in backlight's power property? @@ -202,6 +190,23 @@ static int dcp_set_brightness(struct backlight_device *bd) return drm_crtc_set_brightness(dcp); } +static int dcp_set_brightness(struct backlight_device *bd) +{ + int ret = 0; + struct apple_dcp *dcp = bl_get_data(bd); + struct drm_modeset_acquire_ctx ctx; + int brightness = backlight_get_brightness(bd); + + DRM_MODESET_LOCK_ALL_BEGIN(dcp->crtc->base.dev, ctx, 0, ret); + + dcp->brightness.dac = calculate_dac(dcp, brightness); + dcp->brightness.update = true; + + DRM_MODESET_LOCK_ALL_END(dcp->crtc->base.dev, ctx, ret); + + return dcp_backlight_update(dcp); +} + static const struct backlight_ops dcp_backlight_ops = { .options = BL_CORE_SUSPENDRESUME, .get_brightness = dcp_get_brightness, diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 7c8dc5a25d143c..c2f8dc003c4eeb 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -497,6 +497,7 @@ static void iomfbep_cb_enable_backlight_message_ap_gated(struct apple_dcp *dcp, * syslog: "[BrightnessLCD.cpp:743][AFK]nitsToDBV: iDAC out of range" */ dcp->brightness.update = true; + schedule_work(&dcp->bl_update_wq); } /* Chunked data transfer for property dictionaries */ From 2dd9a4437743ff7751fbfb31ae656b494bea2ae2 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 22 Jan 2024 22:24:11 +0100 Subject: [PATCH 0612/1020] drm: apple: Fix/remove log messages Add missing training '\n' and remove leftover dev_dbg() statements. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/afk.c | 8 +++--- drivers/gpu/drm/apple/apple_drv.c | 4 --- drivers/gpu/drm/apple/dcp.c | 30 +++++++++---------- drivers/gpu/drm/apple/dcp_backlight.c | 2 +- drivers/gpu/drm/apple/iomfb.c | 4 +-- drivers/gpu/drm/apple/iomfb_template.c | 40 ++++++++------------------ 6 files changed, 34 insertions(+), 54 deletions(-) diff --git a/drivers/gpu/drm/apple/afk.c b/drivers/gpu/drm/apple/afk.c index 52a5bf5f8a6479..b3a5cf74e817e9 100644 --- a/drivers/gpu/drm/apple/afk.c +++ b/drivers/gpu/drm/apple/afk.c @@ -138,7 +138,7 @@ static void afk_init_rxtx(struct apple_dcp_afkep *ep, u64 message, u32 bufsz, end; if (tag != ep->bfr_tag) { - dev_err(ep->dcp->dev, "AFK[ep:%02x]: expected tag 0x%x but got 0x%x", + dev_err(ep->dcp->dev, "AFK[ep:%02x]: expected tag 0x%x but got 0x%x\n", ep->endpoint, ep->bfr_tag, tag); return; } @@ -151,7 +151,7 @@ static void afk_init_rxtx(struct apple_dcp_afkep *ep, u64 message, if (base >= ep->bfr_size) { dev_err(ep->dcp->dev, - "AFK[ep:%02x]: requested base 0x%x >= max size 0x%lx", + "AFK[ep:%02x]: requested base 0x%x >= max size 0x%lx\n", ep->endpoint, base, ep->bfr_size); return; } @@ -159,7 +159,7 @@ static void afk_init_rxtx(struct apple_dcp_afkep *ep, u64 message, end = base + size; if (end > ep->bfr_size) { dev_err(ep->dcp->dev, - "AFK[ep:%02x]: requested end 0x%x > max size 0x%lx", + "AFK[ep:%02x]: requested end 0x%x > max size 0x%lx\n", ep->endpoint, end, ep->bfr_size); return; } @@ -168,7 +168,7 @@ static void afk_init_rxtx(struct apple_dcp_afkep *ep, u64 message, bufsz = le32_to_cpu(bfr->hdr->bufsz); if (bufsz + sizeof(*bfr->hdr) != size) { dev_err(ep->dcp->dev, - "AFK[ep:%02x]: ring buffer size 0x%x != expected 0x%lx", + "AFK[ep:%02x]: ring buffer size 0x%x != expected 0x%lx\n", ep->endpoint, bufsz, sizeof(*bfr->hdr)); return; } diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 8c2ec2625f1bcf..c9ce200fa53b50 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -196,9 +196,7 @@ static void apple_crtc_atomic_enable(struct drm_crtc *crtc, if (crtc_state->active_changed && crtc_state->active) { struct apple_crtc *apple_crtc = to_apple_crtc(crtc); - dev_dbg(&apple_crtc->dcp->dev, "%s", __func__); dcp_poweron(apple_crtc->dcp); - dev_dbg(&apple_crtc->dcp->dev, "%s finished", __func__); } if (crtc_state->active) @@ -213,9 +211,7 @@ static void apple_crtc_atomic_disable(struct drm_crtc *crtc, if (crtc_state->active_changed && !crtc_state->active) { struct apple_crtc *apple_crtc = to_apple_crtc(crtc); - dev_dbg(&apple_crtc->dcp->dev, "%s", __func__); dcp_poweroff(apple_crtc->dcp); - dev_dbg(&apple_crtc->dcp->dev, "%s finished", __func__); } if (crtc->state->event && !crtc->state->active) { diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index a0e3d39e147bbc..2083650074bc30 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -128,7 +128,7 @@ static void dcp_recv_msg(void *cookie, u8 endpoint, u64 message) afk_receive_message(dcp->dptxep, message); return; default: - WARN(endpoint, "unknown DCP endpoint %hhu", endpoint); + WARN(endpoint, "unknown DCP endpoint %hhu\n", endpoint); } } @@ -137,7 +137,7 @@ static void dcp_rtk_crashed(void *cookie) struct apple_dcp *dcp = cookie; dcp->crashed = true; - dev_err(dcp->dev, "DCP has crashed"); + dev_err(dcp->dev, "DCP has crashed\n"); if (dcp->connector) { dcp->connector->connected = 0; schedule_work(&dcp->connector->hotplug_wq); @@ -169,7 +169,7 @@ static int dcp_rtk_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr) bfr->is_mapped = true; dev_info(dcp->dev, - "shmem_setup: iova: %lx -> pa: %lx -> iomem: %lx", + "shmem_setup: iova: %lx -> pa: %lx -> iomem: %lx\n", (uintptr_t)bfr->iova, (uintptr_t)phy_addr, (uintptr_t)bfr->buffer); } else { @@ -178,7 +178,7 @@ static int dcp_rtk_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr) if (!bfr->buffer) return -ENOMEM; - dev_info(dcp->dev, "shmem_setup: iova: %lx, buffer: %lx", + dev_info(dcp->dev, "shmem_setup: iova: %lx, buffer: %lx\n", (uintptr_t)bfr->iova, (uintptr_t)bfr->buffer); } @@ -226,7 +226,7 @@ int dcp_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) needs_modeset = drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode; if (!needs_modeset && !dcp->connector->connected) { - dev_err(dcp->dev, "crtc_atomic_check: disconnected but no modeset"); + dev_err(dcp->dev, "crtc_atomic_check: disconnected but no modeset\n"); return -EINVAL; } @@ -239,7 +239,7 @@ int dcp_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) } if (plane_count > DCP_MAX_PLANES) { - dev_err(dcp->dev, "crtc_atomic_check: Blend supports only 2 layers!"); + dev_err(dcp->dev, "crtc_atomic_check: Blend supports only 2 layers!\n"); return -EINVAL; } @@ -355,17 +355,17 @@ int dcp_start(struct platform_device *pdev) /* start RTKit endpoints */ ret = systemep_init(dcp); if (ret) - dev_warn(dcp->dev, "Failed to start system endpoint: %d", ret); + dev_warn(dcp->dev, "Failed to start system endpoint: %d\n", ret); if (dcp->phy && dcp->fw_compat >= DCP_FIRMWARE_V_13_5) { ret = ibootep_init(dcp); if (ret) - dev_warn(dcp->dev, "Failed to start IBOOT endpoint: %d", + dev_warn(dcp->dev, "Failed to start IBOOT endpoint: %d\n", ret); ret = dptxep_init(dcp); if (ret) - dev_warn(dcp->dev, "Failed to start DPTX endpoint: %d", + dev_warn(dcp->dev, "Failed to start DPTX endpoint: %d\n", ret); else if (dcp->dptxport[0].enabled) { bool connected; @@ -388,7 +388,7 @@ int dcp_start(struct platform_device *pdev) ret = iomfb_start_rtkit(dcp); if (ret) - dev_err(dcp->dev, "Failed to start IOMFB endpoint: %d", ret); + dev_err(dcp->dev, "Failed to start IOMFB endpoint: %d\n", ret); return ret; } @@ -887,12 +887,12 @@ static int dcp_comp_bind(struct device *dev, struct device *main, void *data) dcp->rtk = devm_apple_rtkit_init(dev, dcp, "mbox", 0, &rtkit_ops); if (IS_ERR(dcp->rtk)) return dev_err_probe(dev, PTR_ERR(dcp->rtk), - "Failed to initialize RTKit"); + "Failed to initialize RTKit\n"); ret = apple_rtkit_wake(dcp->rtk); if (ret) return dev_err_probe(dev, ret, - "Failed to boot RTKit: %d", ret); + "Failed to boot RTKit: %d\n", ret); return ret; } @@ -960,7 +960,7 @@ static int dcp_platform_probe(struct platform_device *pdev) dcp->phy = devm_phy_optional_get(dev, "dp-phy"); if (IS_ERR(dcp->phy)) { - dev_err(dev, "Failed to get dp-phy: %ld", PTR_ERR(dcp->phy)); + dev_err(dev, "Failed to get dp-phy: %ld\n", PTR_ERR(dcp->phy)); return PTR_ERR(dcp->phy); } if (dcp->phy) { @@ -987,7 +987,7 @@ static int dcp_platform_probe(struct platform_device *pdev) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "dp2hdmi-hpd-irq", dcp); if (ret < 0) { - dev_err(dev, "failed to request HDMI hpd irq %d: %d", + dev_err(dev, "failed to request HDMI hpd irq %d: %d\n", irq, ret); return ret; } @@ -1010,7 +1010,7 @@ static int dcp_platform_probe(struct platform_device *pdev) if (!ret) { dcp->xbar = devm_mux_control_get(dev, "dp-xbar"); if (IS_ERR(dcp->xbar)) { - dev_err(dev, "Failed to get dp-xbar: %ld", PTR_ERR(dcp->xbar)); + dev_err(dev, "Failed to get dp-xbar: %ld\n", PTR_ERR(dcp->xbar)); return PTR_ERR(dcp->xbar); } ret = mux_control_select(dcp->xbar, mux_index); diff --git a/drivers/gpu/drm/apple/dcp_backlight.c b/drivers/gpu/drm/apple/dcp_backlight.c index dfc78f3ce37b0d..ed3b240ead8557 100644 --- a/drivers/gpu/drm/apple/dcp_backlight.c +++ b/drivers/gpu/drm/apple/dcp_backlight.c @@ -99,7 +99,7 @@ static u32 interpolate(int val, int min, int max, u32 *tbl, size_t tbl_size) size_t index = interpolated / SCALE_FACTOR; - if (WARN(index + 1 >= tbl_size, "invalid index %zu for brightness %u", index, val)) + if (WARN(index + 1 >= tbl_size, "invalid index %zu for brightness %u\n", index, val)) return tbl[tbl_size / 2]; frac = interpolated & (SCALE_FACTOR - 1); diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 2944911ced770a..9fe8487053efeb 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -326,7 +326,7 @@ static void dcpep_got_msg(struct apple_dcp *dcp, u64 message) channel_offset = dcp_channel_offset(ctx_id); if (channel_offset < 0) { - dev_warn(dcp->dev, "invalid context received %u", ctx_id); + dev_warn(dcp->dev, "invalid context received %u\n", ctx_id); return; } @@ -482,7 +482,7 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) if (dcp_channel_busy(&dcp->ch_cmd)) { - dev_err(dcp->dev, "unexpected busy command channel"); + dev_err(dcp->dev, "unexpected busy command channel\n"); /* HACK: issue a delayed vblank event to avoid timeouts in * drm_atomic_helper_wait_for_vblanks(). */ diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index c2f8dc003c4eeb..dd9af89b9e3985 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -299,7 +299,7 @@ static void dcpep_cb_unmap_piodma(struct apple_dcp *dcp, struct dcp_mem_descriptor *memdesc; if (resp->buffer >= ARRAY_SIZE(dcp->memdesc)) { - dev_warn(dcp->dev, "unmap request for out of range buffer %llu", + dev_warn(dcp->dev, "unmap request for out of range buffer %llu\n", resp->buffer); return; } @@ -308,14 +308,14 @@ static void dcpep_cb_unmap_piodma(struct apple_dcp *dcp, if (!memdesc->buf) { dev_warn(dcp->dev, - "unmap for non-mapped buffer %llu iova:0x%08llx", + "unmap for non-mapped buffer %llu iova:0x%08llx\n", resp->buffer, resp->dva); return; } if (memdesc->dva != resp->dva) { dev_warn(dcp->dev, "unmap buffer %llu address mismatch " - "memdesc.dva:%llx dva:%llx", resp->buffer, + "memdesc.dva:%llx dva:%llx\n", resp->buffer, memdesc->dva, resp->dva); return; } @@ -343,7 +343,7 @@ dcpep_cb_allocate_buffer(struct apple_dcp *dcp, find_first_zero_bit(dcp->memdesc_map, DCP_MAX_MAPPINGS); if (resp.mem_desc_id >= DCP_MAX_MAPPINGS) { - dev_warn(dcp->dev, "DCP overflowed mapping table, ignoring"); + dev_warn(dcp->dev, "DCP overflowed mapping table, ignoring\n"); resp.dva_size = 0; resp.mem_desc_id = 0; return resp; @@ -378,7 +378,7 @@ static u8 dcpep_cb_release_mem_desc(struct apple_dcp *dcp, u32 *mem_desc_id) } if (!test_and_clear_bit(id, dcp->memdesc_map)) { - dev_warn(dcp->dev, "unmap request for unused mem_desc_id %u", + dev_warn(dcp->dev, "unmap request for unused mem_desc_id %u\n", id); return 0; } @@ -428,7 +428,7 @@ dcpep_cb_map_physical(struct apple_dcp *dcp, struct dcp_map_physical_req *req) u32 id; if (!is_disp_register(dcp, req->paddr, req->paddr + size - 1)) { - dev_err(dcp->dev, "refusing to map phys address %llx size %llx", + dev_err(dcp->dev, "refusing to map phys address %llx size %llx\n", req->paddr, req->size); return (struct dcp_map_physical_resp){}; } @@ -457,7 +457,7 @@ static struct DCP_FW_NAME(dcp_map_reg_resp) dcpep_cb_map_reg(struct apple_dcp *d struct DCP_FW_NAME(dcp_map_reg_req) *req) { if (req->index >= dcp->nr_disp_registers) { - dev_warn(dcp->dev, "attempted to read invalid reg index %u", + dev_warn(dcp->dev, "attempted to read invalid reg index %u\n", req->index); return (struct DCP_FW_NAME(dcp_map_reg_resp)){ .ret = 1 }; @@ -602,7 +602,7 @@ static void boot_done(struct apple_dcp *dcp, void *out, void *cookie) { struct dcp_channel *ch = &dcp->ch_cb; u8 *succ = ch->output[ch->depth - 1]; - dev_dbg(dcp->dev, "boot done"); + dev_dbg(dcp->dev, "boot done\n"); *succ = true; dcp_ack(dcp, DCP_CONTEXT_CB); @@ -717,7 +717,6 @@ static void release_swap_cookie(struct kref *ref) static void dcp_swap_cleared(struct apple_dcp *dcp, void *data, void *cookie) { struct DCP_FW_NAME(dcp_swap_submit_resp) *resp = data; - dev_dbg(dcp->dev, "%s", __func__); if (cookie) { struct dcp_swap_cookie *info = cookie; @@ -748,7 +747,6 @@ static void dcp_swap_clear_started(struct apple_dcp *dcp, void *data, void *cookie) { struct dcp_swap_start_resp *resp = data; - dev_dbg(dcp->dev, "%s swap_id: %u", __func__, resp->swap_id); DCP_FW_UNION(dcp->swap).swap.swap_id = resp->swap_id; if (cookie) { @@ -762,7 +760,6 @@ static void dcp_swap_clear_started(struct apple_dcp *dcp, void *data, static void dcp_on_final(struct apple_dcp *dcp, void *out, void *cookie) { struct dcp_wait_cookie *wait = cookie; - dev_dbg(dcp->dev, "%s", __func__); if (wait) { complete(&wait->done); @@ -775,7 +772,6 @@ static void dcp_on_set_power_state(struct apple_dcp *dcp, void *out, void *cooki struct dcp_set_power_state_req req = { .unklong = 1, }; - dev_dbg(dcp->dev, "%s", __func__); dcp_set_power_state(dcp, false, &req, dcp_on_final, cookie); } @@ -791,7 +787,6 @@ static void dcp_on_set_parameter(struct apple_dcp *dcp, void *out, void *cookie) .count = 1, #endif }; - dev_dbg(dcp->dev, "%s", __func__); dcp_set_parameter_dcp(dcp, false, ¶m, dcp_on_set_power_state, cookie); } @@ -803,8 +798,6 @@ void DCP_FW_NAME(iomfb_poweron)(struct apple_dcp *dcp) u32 handle; dev_err(dcp->dev, "dcp_poweron() starting\n"); - dev_dbg(dcp->dev, "%s", __func__); - cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); if (!cookie) return; @@ -826,7 +819,7 @@ void DCP_FW_NAME(iomfb_poweron)(struct apple_dcp *dcp) ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(500)); if (ret == 0) - dev_warn(dcp->dev, "wait for power timed out"); + dev_warn(dcp->dev, "wait for power timed out\n"); kref_put(&cookie->refcount, release_wait_cookie);; @@ -874,8 +867,6 @@ void DCP_FW_NAME(iomfb_poweroff)(struct apple_dcp *dcp) struct dcp_swap_start_req swap_req = { 0 }; struct DCP_FW_NAME(dcp_swap_submit_req) *swap = &DCP_FW_UNION(dcp->swap); - dev_dbg(dcp->dev, "%s", __func__); - cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); if (!cookie) return; @@ -923,7 +914,7 @@ void DCP_FW_NAME(iomfb_poweroff)(struct apple_dcp *dcp) return; } - dev_dbg(dcp->dev, "%s: clear swap submitted: %u", __func__, swap_id); + dev_dbg(dcp->dev, "%s: clear swap submitted: %u\n", __func__, swap_id); poff_cookie = kzalloc(sizeof(*poff_cookie), GFP_KERNEL); if (!poff_cookie) @@ -939,14 +930,13 @@ void DCP_FW_NAME(iomfb_poweroff)(struct apple_dcp *dcp) msecs_to_jiffies(1000)); if (ret == 0) - dev_warn(dcp->dev, "setPowerState(0) timeout %u ms", 1000); + dev_warn(dcp->dev, "setPowerState(0) timeout %u ms\n", 1000); else if (ret > 0) dev_dbg(dcp->dev, "setPowerState(0) finished with %d ms to spare", jiffies_to_msecs(ret)); kref_put(&poff_cookie->refcount, release_wait_cookie); - dev_dbg(dcp->dev, "%s: setPowerState(0) done", __func__); dev_err(dcp->dev, "dcp_poweroff() done\n"); } @@ -990,11 +980,9 @@ void DCP_FW_NAME(iomfb_sleep)(struct apple_dcp *dcp) msecs_to_jiffies(1000)); if (ret == 0) - dev_warn(dcp->dev, "setDCPPower(0) timeout %u ms", 1000); + dev_warn(dcp->dev, "setDCPPower(0) timeout %u ms\n", 1000); kref_put(&cookie->refcount, release_wait_cookie); - dev_dbg(dcp->dev, "%s: setDCPPower(0) done", __func__); - dev_err(dcp->dev, "dcp_sleep() done\n"); } @@ -1163,7 +1151,6 @@ static void dcp_swap_started(struct apple_dcp *dcp, void *data, void *cookie) static void do_swap(struct apple_dcp *dcp, void *data, void *cookie) { struct dcp_swap_start_req start_req = { 0 }; - dev_dbg(dcp->dev, "%s", __func__); if (dcp->connector && dcp->connector->connected) dcp_swap_start(dcp, false, &start_req, dcp_swap_started, NULL); @@ -1175,7 +1162,6 @@ static void complete_set_digital_out_mode(struct apple_dcp *dcp, void *data, void *cookie) { struct dcp_wait_cookie *wait = cookie; - dev_dbg(dcp->dev, "%s", __func__); if (wait) { complete(&wait->done); @@ -1242,7 +1228,6 @@ int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, * modesets. Add an extra 500ms to safe side that the modeset * call has returned. */ - dev_dbg(dcp->dev, "%s - wait for modeset", __func__); ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(8500)); @@ -1276,7 +1261,6 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru struct DCP_FW_NAME(dcp_swap_submit_req) *req = &DCP_FW_UNION(dcp->swap); int plane_idx, l; int has_surface = 0; - dev_dbg(dcp->dev, "%s", __func__); crtc_state = drm_atomic_get_new_crtc_state(state, crtc); From 4b80cc366774ab47b7680d25f5104f177c3915ba Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 5 Mar 2024 20:34:48 +0100 Subject: [PATCH 0613/1020] fixup! WIP: drm/apple: Add DCP display driver Signed-off-by: Janne Grunau --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index c462767f26d06b..5bec0ec011e700 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1487,7 +1487,7 @@ S: Odd fixes F: drivers/input/mouse/bcm5974.c APPLE DRM DISPLAY DRIVER -M: Alyssa Rosenzweig +M: Janne Grunau L: dri-devel@lists.freedesktop.org S: Maintained T: git git://anongit.freedesktop.org/drm/drm-misc From bd92b5aabb5004420a196dcb57a2d87f3e1ec6e0 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Tue, 26 Mar 2024 22:05:50 +0100 Subject: [PATCH 0614/1020] drm: apple: dptx: Debounce HPD by simple msleep() Not necessarily only a debounce but 500ms sleep in the HPD interrupt handler seems to make the modeset more reliable on M2* desktop devices. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 2083650074bc30..dcfb8a3cc2ad1c 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -329,6 +329,12 @@ static irqreturn_t dcp_dp2hdmi_hpd(int irq, void *data) */ dev_info(dcp->dev, "DP2HDMI HPD irq, connected:%d\n", connected); + if (connected) { + msleep(500); + connected = gpiod_get_value_cansleep(dcp->hdmi_hpd); + dev_info(dcp->dev, "DP2HDMI HPD irq, 500ms debounce: connected:%d\n", connected); + } + if (connected) dcp_dptx_connect(dcp, 0); From 5cd8f80be306276d492030a3c03d49bce9f4ad5a Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 21 Jan 2024 14:47:56 +0100 Subject: [PATCH 0615/1020] drm: apple: Add Kconfig option for audio Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/Kconfig | 9 +++++++-- drivers/gpu/drm/apple/parser.c | 8 ++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/Kconfig b/drivers/gpu/drm/apple/Kconfig index 1bffe0f48ebff5..56b6e855d12176 100644 --- a/drivers/gpu/drm/apple/Kconfig +++ b/drivers/gpu/drm/apple/Kconfig @@ -4,12 +4,17 @@ config DRM_APPLE depends on DRM && OF && ARM64 depends on ARCH_APPLE || COMPILE_TEST depends on APPLE_RTKIT - depends on SND select DRM_KMS_HELPER select DRM_KMS_DMA_HELPER select DRM_GEM_DMA_HELPER select VIDEOMODE_HELPERS select MULTIPLEXER - select SND_PCM help Say Y if you have an Apple Silicon chipset. + +config DRM_APPLE_AUDIO + bool "DisplayPort/HDMI Audio support" + default y + depends on DRM_APPLE + depends on SND + select SND_PCM diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index 4a4a6458ecebc9..965fcf88a0f4b6 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -7,7 +7,9 @@ #include #include +#if IS_ENABLED(CONFIG_DRM_APPLE_AUDIO) #include // for sound format masks +#endif #include "parser.h" #include "trace.h" @@ -119,6 +121,7 @@ static int skip(struct dcp_parse_ctx *handle) } } +#if IS_ENABLED(CONFIG_DRM_APPLE_AUDIO) static int skip_pair(struct dcp_parse_ctx *handle) { int ret; @@ -151,6 +154,7 @@ static bool consume_string(struct dcp_parse_ctx *ctx, const char *specimen) skip(ctx); return true; } +#endif /* Caller must free the result */ static char *parse_string(struct dcp_parse_ctx *handle) @@ -201,6 +205,7 @@ static int parse_bool(struct dcp_parse_ctx *handle, bool *b) return 0; } +#if IS_ENABLED(CONFIG_DRM_APPLE_AUDIO) static int parse_blob(struct dcp_parse_ctx *handle, size_t size, u8 const **blob) { const struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_BLOB); @@ -220,6 +225,7 @@ static int parse_blob(struct dcp_parse_ctx *handle, size_t size, u8 const **blob *blob = out; return 0; } +#endif struct iterator { struct dcp_parse_ctx *handle; @@ -680,6 +686,7 @@ int parse_epic_service_init(struct dcp_parse_ctx *handle, const char **name, return ret; } +#if IS_ENABLED(CONFIG_DRM_APPLE_AUDIO) static int parse_sample_rate_bit(struct dcp_parse_ctx *handle, unsigned int *ratebit) { s64 rate; @@ -983,6 +990,7 @@ int parse_sound_mode(struct dcp_parse_ctx *handle, return 0; } EXPORT_SYMBOL_GPL(parse_sound_mode); +#endif int parse_system_log_mnits(struct dcp_parse_ctx *handle, struct dcp_system_ev_mnits *entry) { From 3aa2bde555a3e5a3434877f1c36937c93d0f0ed9 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 25 Dec 2023 18:03:37 +0100 Subject: [PATCH 0616/1020] drm: apple: iomfb: export property dicts in connector debugfs Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/Makefile | 1 + drivers/gpu/drm/apple/apple_drv.c | 2 + drivers/gpu/drm/apple/connector.c | 122 +++++++++++++++++++++++++ drivers/gpu/drm/apple/connector.h | 39 ++++++++ drivers/gpu/drm/apple/dcp.h | 15 +-- drivers/gpu/drm/apple/iomfb_template.c | 5 +- 6 files changed, 168 insertions(+), 16 deletions(-) create mode 100644 drivers/gpu/drm/apple/connector.c create mode 100644 drivers/gpu/drm/apple/connector.h diff --git a/drivers/gpu/drm/apple/Makefile b/drivers/gpu/drm/apple/Makefile index afe5c7ea2785cf..74b29b970d16b3 100644 --- a/drivers/gpu/drm/apple/Makefile +++ b/drivers/gpu/drm/apple/Makefile @@ -5,6 +5,7 @@ CFLAGS_trace.o = -I$(src) appledrm-y := apple_drv.o apple_dcp-y := afk.o dcp.o dcp_backlight.o dptxep.o iomfb.o parser.o systemep.o +apple_dcp-y += connector.o apple_dcp-y += ibootep.o apple_dcp-y += iomfb_v12_3.o apple_dcp-y += iomfb_v13_3.o diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index c9ce200fa53b50..ec966774b1e80d 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -295,6 +295,7 @@ static const struct drm_connector_funcs apple_connector_funcs = { .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .detect = apple_connector_detect, + .debugfs_init = apple_connector_debugfs_init, }; static const struct drm_connector_helper_funcs apple_connector_helper_funcs = { @@ -343,6 +344,7 @@ static int apple_probe_per_dcp(struct device *dev, enc->base.possible_crtcs = drm_crtc_mask(&crtc->base); connector = kzalloc(sizeof(*connector), GFP_KERNEL); + mutex_init(&connector->chunk_lock); drm_connector_helper_add(&connector->base, &apple_connector_helper_funcs); diff --git a/drivers/gpu/drm/apple/connector.c b/drivers/gpu/drm/apple/connector.c new file mode 100644 index 00000000000000..a39bd249697d90 --- /dev/null +++ b/drivers/gpu/drm/apple/connector.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * Copyright (C) The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include + +#include + +#include "connector.h" +#include "dcp-internal.h" + +enum dcp_chunk_type { + DCP_CHUNK_COLOR_ELEMENTS, + DCP_CHUNK_TIMING_ELELMENTS, + DCP_CHUNK_DISPLAY_ATTRIBUTES, + DCP_CHUNK_TRANSPORT, + DCP_CHUNK_NUM_TYPES, +}; + +static int chunk_show(struct seq_file *m, + enum dcp_chunk_type chunk_type) +{ + struct apple_connector *apple_con = m->private; + struct dcp_chunks *chunk = NULL; + + mutex_lock(&apple_con->chunk_lock); + + switch (chunk_type) { + case DCP_CHUNK_COLOR_ELEMENTS: + chunk = &apple_con->color_elements; + break; + case DCP_CHUNK_TIMING_ELELMENTS: + chunk = &apple_con->timing_elements; + break; + case DCP_CHUNK_DISPLAY_ATTRIBUTES: + chunk = &apple_con->display_attributes; + break; + case DCP_CHUNK_TRANSPORT: + chunk = &apple_con->transport; + break; + default: + break; + } + + if (chunk) + seq_write(m, chunk->data, chunk->length); + + mutex_unlock(&apple_con->chunk_lock); + + return 0; +} + +#define CONNECTOR_DEBUGFS_ENTRY(name, type) \ +static int chunk_ ## name ## _show(struct seq_file *m, void *data) \ +{ \ + return chunk_show(m, type); \ +} \ +static int chunk_ ## name ## _open(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, chunk_ ## name ## _show, inode->i_private); \ +} \ +static const struct file_operations chunk_ ## name ## _fops = { \ + .owner = THIS_MODULE, \ + .open = chunk_ ## name ## _open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ +} + +CONNECTOR_DEBUGFS_ENTRY(color, DCP_CHUNK_COLOR_ELEMENTS); +CONNECTOR_DEBUGFS_ENTRY(timing, DCP_CHUNK_TIMING_ELELMENTS); +CONNECTOR_DEBUGFS_ENTRY(display_attribs, DCP_CHUNK_DISPLAY_ATTRIBUTES); +CONNECTOR_DEBUGFS_ENTRY(transport, DCP_CHUNK_TRANSPORT); + +void apple_connector_debugfs_init(struct drm_connector *connector, struct dentry *root) +{ + struct apple_connector *apple_con = to_apple_connector(connector); + + debugfs_create_file("ColorElements", 0444, root, apple_con, + &chunk_color_fops); + debugfs_create_file("TimingElements", 0444, root, apple_con, + &chunk_timing_fops); + debugfs_create_file("DisplayAttributes", 0444, root, apple_con, + &chunk_display_attribs_fops); + debugfs_create_file("Transport", 0444, root, apple_con, + &chunk_transport_fops); +} +EXPORT_SYMBOL(apple_connector_debugfs_init); + +static void dcp_connector_set_dict(struct apple_connector *connector, + struct dcp_chunks *dict, + struct dcp_chunks *chunks) +{ + if (dict->data) + devm_kfree(&connector->dcp->dev, dict->data); + + *dict = *chunks; +} + +void dcp_connector_update_dict(struct apple_connector *connector, const char *key, + struct dcp_chunks *chunks) +{ + mutex_lock(&connector->chunk_lock); + if (!strcmp(key, "ColorElements")) + dcp_connector_set_dict(connector, &connector->color_elements, chunks); + else if (!strcmp(key, "TimingElements")) + dcp_connector_set_dict(connector, &connector->timing_elements, chunks); + else if (!strcmp(key, "DisplayAttributes")) + dcp_connector_set_dict(connector, &connector->display_attributes, chunks); + else if (!strcmp(key, "Transport")) + dcp_connector_set_dict(connector, &connector->transport, chunks); + + chunks->data = NULL; + chunks->length = 0; + + mutex_unlock(&connector->chunk_lock); +} diff --git a/drivers/gpu/drm/apple/connector.h b/drivers/gpu/drm/apple/connector.h new file mode 100644 index 00000000000000..79a1eef8c32da8 --- /dev/null +++ b/drivers/gpu/drm/apple/connector.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* "Copyright" 2021 Alyssa Rosenzweig */ + +#ifndef __APPLE_CONNECTOR_H__ +#define __APPLE_CONNECTOR_H__ + +#include + +#include +#include "drm/drm_connector.h" + +#include "dcp-internal.h" + +void dcp_hotplug(struct work_struct *work); + +struct apple_connector { + struct drm_connector base; + bool connected; + + struct platform_device *dcp; + + /* Workqueue for sending hotplug events to the associated device */ + struct work_struct hotplug_wq; + + struct mutex chunk_lock; + + struct dcp_chunks color_elements; + struct dcp_chunks timing_elements; + struct dcp_chunks display_attributes; + struct dcp_chunks transport; +}; + +#define to_apple_connector(x) container_of(x, struct apple_connector, base) + +void apple_connector_debugfs_init(struct drm_connector *connector, struct dentry *root); + +void dcp_connector_update_dict(struct apple_connector *connector, const char *key, + struct dcp_chunks *chunks); +#endif diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h index 298520c0832e93..52c5c94a2a8bbe 100644 --- a/drivers/gpu/drm/apple/dcp.h +++ b/drivers/gpu/drm/apple/dcp.h @@ -8,6 +8,7 @@ #include #include +#include "connector.h" #include "dcp-internal.h" #include "parser.h" @@ -22,20 +23,6 @@ struct apple_crtc { #define to_apple_crtc(x) container_of(x, struct apple_crtc, base) -void dcp_hotplug(struct work_struct *work); - -struct apple_connector { - struct drm_connector base; - bool connected; - - struct platform_device *dcp; - - /* Workqueue for sending hotplug events to the associated device */ - struct work_struct hotplug_wq; -}; - -#define to_apple_connector(x) container_of(x, struct apple_connector, base) - struct apple_encoder { struct drm_encoder base; }; diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index dd9af89b9e3985..ace4dc74996987 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -590,9 +590,10 @@ static u8 dcpep_cb_prop_end(struct apple_dcp *dcp, { u8 resp = dcpep_process_chunks(dcp, req); - /* Reset for the next transfer */ - devm_kfree(dcp->dev, dcp->chunks.data); + /* move chunked data to connector to provide it via debugfs */ + dcp_connector_update_dict(dcp->connector, req->key, &dcp->chunks); dcp->chunks.data = NULL; + dcp->chunks.length = 0; return resp; } From 3f5b7915d6f3f04c8f35044b1bf983a1eb9b5488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 13 Feb 2023 14:55:13 +0100 Subject: [PATCH 0617/1020] gpu: drm: apple: Expose injecting of EPIC calls via debugfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer Co-developed-by: Janne Grunau Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/Kconfig | 5 + drivers/gpu/drm/apple/afk.c | 161 +++++++++++++++++++++++++++ drivers/gpu/drm/apple/afk.h | 8 ++ drivers/gpu/drm/apple/connector.c | 29 +++++ drivers/gpu/drm/apple/dcp-internal.h | 3 + 5 files changed, 206 insertions(+) diff --git a/drivers/gpu/drm/apple/Kconfig b/drivers/gpu/drm/apple/Kconfig index 56b6e855d12176..6cf972f8ce1162 100644 --- a/drivers/gpu/drm/apple/Kconfig +++ b/drivers/gpu/drm/apple/Kconfig @@ -18,3 +18,8 @@ config DRM_APPLE_AUDIO depends on DRM_APPLE depends on SND select SND_PCM + +config DRM_APPLE_DEBUG + bool "Enable additional driver debugging" + depends on DRM_APPLE + depends on EXPERT # only for developers diff --git a/drivers/gpu/drm/apple/afk.c b/drivers/gpu/drm/apple/afk.c index b3a5cf74e817e9..218c28dfe84249 100644 --- a/drivers/gpu/drm/apple/afk.c +++ b/drivers/gpu/drm/apple/afk.c @@ -2,7 +2,9 @@ /* Copyright 2022 Sven Peter */ #include +#include #include +#include #include #include #include @@ -181,6 +183,18 @@ static void afk_init_rxtx(struct apple_dcp_afkep *ep, u64 message, afk_send(ep, FIELD_PREP(RBEP_TYPE, RBEP_START)); } +#if IS_ENABLED(CONFIG_DRM_APPLE_DEBUG) +static void afk_populate_service_debugfs(struct apple_epic_service *srv); +static void afk_remove_service_debugfs(struct apple_epic_service *srv); +#else +static void afk_populate_service_debugfs(struct apple_epic_service *srv) +{ +} +static void afk_remove_service_debugfs(struct apple_epic_service *srv) +{ +} +#endif + static const struct apple_epic_service_ops * afk_match_service(struct apple_dcp_afkep *ep, const char *name) { @@ -284,6 +298,9 @@ static void afk_recv_handle_init(struct apple_dcp_afkep *ep, u32 channel, ops->init(&ep->services[ch_idx], epic_name, epic_class, epic_unit); dev_info(ep->dcp->dev, "AFK[ep:%02x]: new service %s on channel %d\n", ep->endpoint, service_name, channel); + + afk_populate_service_debugfs(&ep->services[ch_idx]); + free: kfree(epic_name); kfree(epic_class); @@ -302,6 +319,8 @@ static void afk_recv_handle_teardown(struct apple_dcp_afkep *ep, u32 channel) return; } + afk_remove_service_debugfs(service); + // TODO: think through what locking is necessary spin_lock_irqsave(&service->lock, flags); service->enabled = false; @@ -989,3 +1008,145 @@ int afk_service_call(struct apple_epic_service *service, u16 group, u32 command, kfree(bfr); return ret; } + +#if IS_ENABLED(CONFIG_DRM_APPLE_DEBUG) + +#define AFK_DEBUGFS_MAX_REPLY 8192 + +static ssize_t service_call_write_file(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct apple_epic_service *srv = file->private_data; + void *buf; + int ret; + struct { + u32 group; + u32 command; + } call_info; + + if (count < sizeof(call_info)) + return -EINVAL; + if (!srv->debugfs.scratch) { + srv->debugfs.scratch = \ + devm_kzalloc(srv->ep->dcp->dev, AFK_DEBUGFS_MAX_REPLY, GFP_KERNEL); + if (!srv->debugfs.scratch) + return -ENOMEM; + } + + ret = copy_from_user(&call_info, user_buf, sizeof(call_info)); + if (ret == sizeof(call_info)) + return -EFAULT; + user_buf += sizeof(call_info); + count -= sizeof(call_info); + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + ret = copy_from_user(buf, user_buf, count); + if (ret == count) { + kfree(buf); + return -EFAULT; + } + + memset(srv->debugfs.scratch, 0, AFK_DEBUGFS_MAX_REPLY); + dma_mb(); + + ret = afk_service_call(srv, call_info.group, call_info.command, buf, count, 0, + srv->debugfs.scratch, AFK_DEBUGFS_MAX_REPLY, 0); + kfree(buf); + + if (ret < 0) + return ret; + + return count + sizeof(call_info); +} + +static ssize_t service_call_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct apple_epic_service *srv = file->private_data; + + if (!srv->debugfs.scratch) + return -EINVAL; + + return simple_read_from_buffer(user_buf, count, ppos, + srv->debugfs.scratch, AFK_DEBUGFS_MAX_REPLY); +} + +static const struct file_operations service_call_fops = { + .open = simple_open, + .write = service_call_write_file, + .read = service_call_read_file, +}; + +static ssize_t service_raw_call_write_file(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct apple_epic_service *srv = file->private_data; + u32 retcode; + int ret; + + if (!srv->debugfs.scratch) { + srv->debugfs.scratch = \ + devm_kzalloc(srv->ep->dcp->dev, AFK_DEBUGFS_MAX_REPLY, GFP_KERNEL); + if (!srv->debugfs.scratch) + return -ENOMEM; + } + + memset(srv->debugfs.scratch, 0, AFK_DEBUGFS_MAX_REPLY); + ret = copy_from_user(srv->debugfs.scratch, user_buf, count); + if (ret == count) + return -EFAULT; + + ret = afk_send_command(srv, EPIC_SUBTYPE_STD_SERVICE, srv->debugfs.scratch, count, + srv->debugfs.scratch, AFK_DEBUGFS_MAX_REPLY, &retcode); + if (ret < 0) + return ret; + if (retcode) + return -EINVAL; + + return count; +} + +static ssize_t service_raw_call_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct apple_epic_service *srv = file->private_data; + + if (!srv->debugfs.scratch) + return -EINVAL; + + return simple_read_from_buffer(user_buf, count, ppos, + srv->debugfs.scratch, AFK_DEBUGFS_MAX_REPLY); +} + +static const struct file_operations service_raw_call_fops = { + .open = simple_open, + .write = service_raw_call_write_file, + .read = service_raw_call_read_file, +}; + +static void afk_populate_service_debugfs(struct apple_epic_service *srv) +{ + if (!srv->ep->debugfs_entry || !srv->ops) + return; + + if (strcmp(srv->ops->name, "DCPAVAudioInterface") == 0) { + srv->debugfs.entry = debugfs_create_dir(srv->ops->name, + srv->ep->debugfs_entry); + debugfs_create_file("call", 0600, srv->debugfs.entry, srv, + &service_call_fops); + debugfs_create_file("raw_call", 0600, srv->debugfs.entry, srv, + &service_raw_call_fops); + } +} + +static void afk_remove_service_debugfs(struct apple_epic_service *srv) +{ + if (srv->debugfs.entry) { + debugfs_remove_recursive(srv->debugfs.entry); + srv->debugfs.entry = NULL; + } +} + +#endif diff --git a/drivers/gpu/drm/apple/afk.h b/drivers/gpu/drm/apple/afk.h index 737288b1346b28..0f91f32e08e301 100644 --- a/drivers/gpu/drm/apple/afk.h +++ b/drivers/gpu/drm/apple/afk.h @@ -8,6 +8,7 @@ #define _DRM_APPLE_DCP_AFK_H #include +#include #include #include "dcp.h" @@ -47,6 +48,11 @@ struct apple_epic_service { bool enabled; void *cookie; + + struct { + struct dentry *entry; + u8 *scratch; + } debugfs; }; enum epic_subtype; @@ -174,6 +180,8 @@ struct apple_dcp_afkep { const struct apple_epic_service_ops *ops; struct apple_epic_service services[AFK_MAX_CHANNEL]; u32 num_channels; + + struct dentry *debugfs_entry; }; struct apple_dcp_afkep *afk_init(struct apple_dcp *dcp, u32 endpoint, diff --git a/drivers/gpu/drm/apple/connector.c b/drivers/gpu/drm/apple/connector.c index a39bd249697d90..46de8e8756f1ed 100644 --- a/drivers/gpu/drm/apple/connector.c +++ b/drivers/gpu/drm/apple/connector.c @@ -3,6 +3,7 @@ * Copyright (C) The Asahi Linux Contributors */ +#include "linux/err.h" #include #include #include @@ -77,6 +78,25 @@ CONNECTOR_DEBUGFS_ENTRY(timing, DCP_CHUNK_TIMING_ELELMENTS); CONNECTOR_DEBUGFS_ENTRY(display_attribs, DCP_CHUNK_DISPLAY_ATTRIBUTES); CONNECTOR_DEBUGFS_ENTRY(transport, DCP_CHUNK_TRANSPORT); +static void dcp_afk_debugfs_root(struct platform_device *pdev, int ep, struct dentry *root) +{ +#if IS_ENABLED(CONFIG_DRM_APPLE_DEBUG) + struct dentry *entry = NULL; + struct apple_dcp *dcp = platform_get_drvdata(pdev); + + switch (ep) { + case AV_ENDPOINT: + entry = debugfs_create_dir("avep", root); + break; + default: + break; + } + + if (!IS_ERR_OR_NULL(entry)) + dcp->ep_debugfs[ep - 0x20] = entry; +#endif +} + void apple_connector_debugfs_init(struct drm_connector *connector, struct dentry *root) { struct apple_connector *apple_con = to_apple_connector(connector); @@ -89,6 +109,15 @@ void apple_connector_debugfs_init(struct drm_connector *connector, struct dentry &chunk_display_attribs_fops); debugfs_create_file("Transport", 0444, root, apple_con, &chunk_transport_fops); + + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_DisplayPort: + case DRM_MODE_CONNECTOR_HDMIA: + dcp_afk_debugfs_root(apple_con->dcp, AV_ENDPOINT, root); + break; + default: + break; + } } EXPORT_SYMBOL(apple_connector_debugfs_init); diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 7fbca0b6bb9778..9f9832133e73ac 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -227,6 +227,9 @@ struct apple_dcp { struct dptx_port dptxport[2]; + /* debugfs entries */ + struct dentry *ep_debugfs[0x20]; + /* these fields are output port specific */ struct phy *phy; struct mux_control *xbar; From 2bde6bb8d0624e8d9021cb114021e364ef3a4acf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 13 Feb 2023 14:56:24 +0100 Subject: [PATCH 0618/1020] gpu: drm: apple: Set up client of AV endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- drivers/gpu/drm/apple/Makefile | 1 + drivers/gpu/drm/apple/audio.h | 26 +++ drivers/gpu/drm/apple/av.c | 289 +++++++++++++++++++++++++++ drivers/gpu/drm/apple/av.h | 9 + drivers/gpu/drm/apple/dcp-internal.h | 6 + drivers/gpu/drm/apple/dcp.c | 16 ++ drivers/gpu/drm/apple/dcp.h | 2 + 7 files changed, 349 insertions(+) create mode 100644 drivers/gpu/drm/apple/audio.h create mode 100644 drivers/gpu/drm/apple/av.c create mode 100644 drivers/gpu/drm/apple/av.h diff --git a/drivers/gpu/drm/apple/Makefile b/drivers/gpu/drm/apple/Makefile index 74b29b970d16b3..23618675a7a058 100644 --- a/drivers/gpu/drm/apple/Makefile +++ b/drivers/gpu/drm/apple/Makefile @@ -5,6 +5,7 @@ CFLAGS_trace.o = -I$(src) appledrm-y := apple_drv.o apple_dcp-y := afk.o dcp.o dcp_backlight.o dptxep.o iomfb.o parser.o systemep.o +apple_dcp-$(CONFIG_DRM_APPLE_AUDIO) += av.o apple_dcp-y += connector.o apple_dcp-y += ibootep.o apple_dcp-y += iomfb_v12_3.o diff --git a/drivers/gpu/drm/apple/audio.h b/drivers/gpu/drm/apple/audio.h new file mode 100644 index 00000000000000..3cf4d31417694e --- /dev/null +++ b/drivers/gpu/drm/apple/audio.h @@ -0,0 +1,26 @@ +#ifndef __AUDIO_H__ +#define __AUDIO_H__ + +#include + +struct device; +struct device_node; +struct dcp_sound_cookie; + +typedef void (*dcp_audio_hotplug_callback)(struct device *dev, bool connected); + +struct dcp_audio_pdata { + struct device *dcp_dev; + struct device_node *dpaudio_node; +}; + +void dcp_audiosrv_set_hotplug_cb(struct device *dev, struct device *audio_dev, + dcp_audio_hotplug_callback cb); +int dcp_audiosrv_prepare(struct device *dev, struct dcp_sound_cookie *cookie); +int dcp_audiosrv_startlink(struct device *dev, struct dcp_sound_cookie *cookie); +int dcp_audiosrv_stoplink(struct device *dev); +int dcp_audiosrv_unprepare(struct device *dev); +int dcp_audiosrv_get_elements(struct device *dev, void *elements, size_t maxsize); +int dcp_audiosrv_get_product_attrs(struct device *dev, void *attrs, size_t maxsize); + +#endif /* __AUDIO_H__ */ diff --git a/drivers/gpu/drm/apple/av.c b/drivers/gpu/drm/apple/av.c new file mode 100644 index 00000000000000..8e8e0b25c12bc5 --- /dev/null +++ b/drivers/gpu/drm/apple/av.c @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright 2023 Martin Povišer */ + +// #define DEBUG + +#include +#include +#include +#include + +#include "audio.h" +#include "afk.h" +#include "dcp.h" + +struct audiosrv_data { + struct device *audio_dev; + dcp_audio_hotplug_callback hotplug_cb; + bool plugged; + struct mutex plug_lock; + + struct apple_epic_service *srv; + struct rw_semaphore srv_rwsem; +}; + +static void av_interface_init(struct apple_epic_service *service, const char *name, + const char *class, s64 unit) +{ +} + +static void av_audiosrv_init(struct apple_epic_service *service, const char *name, + const char *class, s64 unit) +{ + struct apple_dcp *dcp = service->ep->dcp; + struct audiosrv_data *asrv = dcp->audiosrv; + int err; + + mutex_lock(&asrv->plug_lock); + + down_write(&asrv->srv_rwsem); + asrv->srv = service; + up_write(&asrv->srv_rwsem); + + /* TODO: this must be done elsewhere */ + err = afk_service_call(asrv->srv, 0, 6, NULL, 0, 32, NULL, 0, 32); + if (err) + dev_err(dcp->dev, "error opening audio service: %d\n", err); + + asrv->plugged = true; + if (asrv->hotplug_cb) + asrv->hotplug_cb(asrv->audio_dev, true); + + mutex_unlock(&asrv->plug_lock); +} + +static void av_audiosrv_teardown(struct apple_epic_service *service) +{ + struct apple_dcp *dcp = service->ep->dcp; + struct audiosrv_data *asrv = dcp->audiosrv; + + mutex_lock(&asrv->plug_lock); + + down_write(&asrv->srv_rwsem); + asrv->srv = NULL; + up_write(&asrv->srv_rwsem); + + asrv->plugged = false; + if (asrv->hotplug_cb) + asrv->hotplug_cb(asrv->audio_dev, false); + + mutex_unlock(&asrv->plug_lock); +} + +void dcp_audiosrv_set_hotplug_cb(struct device *dev, struct device *audio_dev, + dcp_audio_hotplug_callback cb) +{ + struct apple_dcp *dcp = dev_get_drvdata(dev); + struct audiosrv_data *asrv = dcp->audiosrv; + + mutex_lock(&asrv->plug_lock); + asrv->audio_dev = audio_dev; + asrv->hotplug_cb = cb; + + if (cb) + cb(audio_dev, asrv->plugged); + mutex_unlock(&asrv->plug_lock); +} +EXPORT_SYMBOL_GPL(dcp_audiosrv_set_hotplug_cb); + +int dcp_audiosrv_prepare(struct device *dev, struct dcp_sound_cookie *cookie) +{ + struct apple_dcp *dcp = dev_get_drvdata(dev); + struct audiosrv_data *asrv = dcp->audiosrv; + int ret; + + down_write(&asrv->srv_rwsem); + ret = afk_service_call(asrv->srv, 0, 8, cookie, sizeof(*cookie), + 64 - sizeof(*cookie), NULL, 0, 64); + up_write(&asrv->srv_rwsem); + + return ret; +} +EXPORT_SYMBOL_GPL(dcp_audiosrv_prepare); + +int dcp_audiosrv_startlink(struct device *dev, struct dcp_sound_cookie *cookie) +{ + struct apple_dcp *dcp = dev_get_drvdata(dev); + struct audiosrv_data *asrv = dcp->audiosrv; + int ret; + + down_write(&asrv->srv_rwsem); + ret = afk_service_call(asrv->srv, 0, 9, cookie, sizeof(*cookie), + 64 - sizeof(*cookie), NULL, 0, 64); + up_write(&asrv->srv_rwsem); + + return ret; +} +EXPORT_SYMBOL_GPL(dcp_audiosrv_startlink); + +int dcp_audiosrv_stoplink(struct device *dev) +{ + struct apple_dcp *dcp = dev_get_drvdata(dev); + struct audiosrv_data *asrv = dcp->audiosrv; + int ret; + + down_write(&asrv->srv_rwsem); + ret = afk_service_call(asrv->srv, 0, 12, NULL, 0, 64, NULL, 0, 64); + up_write(&asrv->srv_rwsem); + + return ret; +} +EXPORT_SYMBOL_GPL(dcp_audiosrv_stoplink); + +int dcp_audiosrv_unprepare(struct device *dev) +{ + struct apple_dcp *dcp = dev_get_drvdata(dev); + struct audiosrv_data *asrv = dcp->audiosrv; + int ret; + + down_write(&asrv->srv_rwsem); + ret = afk_service_call(asrv->srv, 0, 13, NULL, 0, 64, NULL, 0, 64); + up_write(&asrv->srv_rwsem); + + return ret; +} +EXPORT_SYMBOL_GPL(dcp_audiosrv_unprepare); + +static int +dcp_audiosrv_osobject_call(struct apple_epic_service *service, u16 group, + u32 command, void *output, size_t output_maxsize, + size_t *output_size) +{ + struct { + __le64 max_size; + u8 _pad1[24]; + __le64 used_size; + u8 _pad2[8]; + } __attribute__((packed)) *hdr; + static_assert(sizeof(*hdr) == 48); + size_t bfr_len = output_maxsize + sizeof(*hdr); + void *bfr; + int ret; + + bfr = kzalloc(bfr_len, GFP_KERNEL); + if (!bfr) + return -ENOMEM; + + hdr = bfr; + hdr->max_size = cpu_to_le64(output_maxsize); + ret = afk_service_call(service, group, command, hdr, sizeof(*hdr), output_maxsize, + bfr, sizeof(*hdr) + output_maxsize, 0); + if (ret) + return ret; + + if (output) + memcpy(output, bfr + sizeof(*hdr), output_maxsize); + + if (output_size) + *output_size = le64_to_cpu(hdr->used_size); + + return 0; +} + +int dcp_audiosrv_get_elements(struct device *dev, void *elements, size_t maxsize) +{ + struct apple_dcp *dcp = dev_get_drvdata(dev); + struct audiosrv_data *asrv = dcp->audiosrv; + size_t size; + int ret; + + down_write(&asrv->srv_rwsem); + ret = dcp_audiosrv_osobject_call(asrv->srv, 1, 18, elements, maxsize, &size); + up_write(&asrv->srv_rwsem); + + if (ret) + dev_err(dev, "audiosrv: error getting elements: %d\n", ret); + else + dev_dbg(dev, "audiosrv: got %zd bytes worth of elements\n", size); + + return ret; +} +EXPORT_SYMBOL_GPL(dcp_audiosrv_get_elements); + +int dcp_audiosrv_get_product_attrs(struct device *dev, void *attrs, size_t maxsize) +{ + struct apple_dcp *dcp = dev_get_drvdata(dev); + struct audiosrv_data *asrv = dcp->audiosrv; + size_t size; + int ret; + + down_write(&asrv->srv_rwsem); + ret = dcp_audiosrv_osobject_call(asrv->srv, 1, 20, attrs, maxsize, &size); + up_write(&asrv->srv_rwsem); + + if (ret) + dev_err(dev, "audiosrv: error getting product attributes: %d\n", ret); + else + dev_dbg(dev, "audiosrv: got %zd bytes worth of product attributes\n", size); + + return ret; +} +EXPORT_SYMBOL_GPL(dcp_audiosrv_get_product_attrs); + +static int av_audiosrv_report(struct apple_epic_service *service, u32 idx, + const void *data, size_t data_size) +{ + dev_dbg(service->ep->dcp->dev, "got audio report %d size %zx\n", idx, data_size); +#ifdef DEBUG + print_hex_dump(KERN_DEBUG, "audio report: ", DUMP_PREFIX_NONE, 16, 1, data, data_size, true); +#endif + + return 0; +} + +static const struct apple_epic_service_ops avep_ops[] = { + { + .name = "DCPAVSimpleVideoInterface", + .init = av_interface_init, + }, + { + .name = "DCPAVAudioInterface", + .init = av_audiosrv_init, + .report = av_audiosrv_report, + .teardown = av_audiosrv_teardown, + }, + {} +}; + +int avep_init(struct apple_dcp *dcp) +{ + struct dcp_audio_pdata *audio_pdata; + struct platform_device *audio_pdev; + struct audiosrv_data *audiosrv_data; + struct device *dev = dcp->dev; + int ret; + + audiosrv_data = devm_kzalloc(dcp->dev, sizeof(*audiosrv_data), GFP_KERNEL); + audio_pdata = devm_kzalloc(dcp->dev, sizeof(*audio_pdata), GFP_KERNEL); + if (!audiosrv_data || !audio_pdata) + return -ENOMEM; + init_rwsem(&audiosrv_data->srv_rwsem); + mutex_init(&audiosrv_data->plug_lock); + dcp->audiosrv = audiosrv_data; + + audio_pdata->dcp_dev = dcp->dev; + /* TODO: free OF reference */ + audio_pdata->dpaudio_node = \ + of_parse_phandle(dev->of_node, "apple,audio-xmitter", 0); + if (!audio_pdata->dpaudio_node || + !of_device_is_available(audio_pdata->dpaudio_node)) { + dev_info(dev, "No audio support\n"); + return 0; + } + + audio_pdev = platform_device_register_data(dev, "dcp-hdmi-audio", + PLATFORM_DEVID_AUTO, + audio_pdata, sizeof(*audio_pdata)); + if (IS_ERR(audio_pdev)) + return dev_err_probe(dev, PTR_ERR(audio_pdev), "registering audio device\n"); + + dcp->avep = afk_init(dcp, AV_ENDPOINT, avep_ops); + if (IS_ERR(dcp->avep)) + return PTR_ERR(dcp->avep); + dcp->avep->debugfs_entry = dcp->ep_debugfs[AV_ENDPOINT - 0x20]; + ret = afk_start(dcp->avep); + if (ret) + return ret; + + return 0; +} diff --git a/drivers/gpu/drm/apple/av.h b/drivers/gpu/drm/apple/av.h new file mode 100644 index 00000000000000..b1f92fb5d07f90 --- /dev/null +++ b/drivers/gpu/drm/apple/av.h @@ -0,0 +1,9 @@ +#ifndef __AV_H__ +#define __AV_H__ + +#include "parser.h" + +//int avep_audiosrv_startlink(struct apple_dcp *dcp, struct dcp_sound_cookie *cookie); +//int avep_audiosrv_stoplink(struct apple_dcp *dcp); + +#endif /* __AV_H__ */ diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 9f9832133e73ac..4c352fd7791dec 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -34,6 +34,7 @@ enum { TEST_ENDPOINT = 0x21, DCP_EXPERT_ENDPOINT = 0x22, DISP0_ENDPOINT = 0x23, + AV_ENDPOINT = 0x29, DPTX_ENDPOINT = 0x2a, HDCP_ENDPOINT = 0x2b, REMOTE_ALLOC_ENDPOINT = 0x2d, @@ -89,6 +90,8 @@ struct dcp_brightness { bool update; }; +struct audiosrv_data; + /** laptop/AiO integrated panel parameters from DT */ struct dcp_panel { /// panel width in millimeter @@ -223,6 +226,9 @@ struct apple_dcp { struct apple_dcp_afkep *ibootep; + struct apple_dcp_afkep *avep; + struct audiosrv_data *audiosrv; + struct apple_dcp_afkep *dptxep; struct dptx_port dptxport[2]; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index dcfb8a3cc2ad1c..8558423169a44d 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,10 @@ static bool show_notch; module_param(show_notch, bool, 0644); MODULE_PARM_DESC(show_notch, "Use the full display height and shows the notch"); +static bool noaudio; +module_param(noaudio, bool, 0644); +MODULE_PARM_DESC(noaudio, "Skip audio support"); + /* HACK: moved here to avoid circular dependency between apple_drv and dcp */ void dcp_drm_crtc_vblank(struct apple_crtc *crtc) { @@ -118,6 +123,9 @@ static void dcp_recv_msg(void *cookie, u8 endpoint, u64 message) switch (endpoint) { case IOMFB_ENDPOINT: return iomfb_recv_msg(dcp, message); + case AV_ENDPOINT: + afk_receive_message(dcp->avep, message); + return; case SYSTEM_ENDPOINT: afk_receive_message(dcp->systemep, message); return; @@ -363,6 +371,14 @@ int dcp_start(struct platform_device *pdev) if (ret) dev_warn(dcp->dev, "Failed to start system endpoint: %d\n", ret); +#if IS_ENABLED(CONFIG_DRM_APPLE_AUDIO) + if (!noaudio) { + ret = avep_init(dcp); + if (ret) + dev_warn(dcp->dev, "Failed to start AV endpoint: %d", ret); + } +#endif + if (dcp->phy && dcp->fw_compat >= DCP_FIRMWARE_V_13_5) { ret = ibootep_init(dcp); if (ret) diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h index 52c5c94a2a8bbe..0a1981040996dc 100644 --- a/drivers/gpu/drm/apple/dcp.h +++ b/drivers/gpu/drm/apple/dcp.h @@ -60,4 +60,6 @@ void iomfb_recv_msg(struct apple_dcp *dcp, u64 message); int systemep_init(struct apple_dcp *dcp); int dptxep_init(struct apple_dcp *dcp); int ibootep_init(struct apple_dcp *dcp); +int avep_init(struct apple_dcp *dcp); + #endif From 580df6424b1d2552092219379128c47b6a0bc564 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 13 Nov 2023 21:59:46 +0100 Subject: [PATCH 0619/1020] drm: apple: av: Support macOS 12.3 and 13.5 firmware APIs Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/av.c | 74 +++++++++++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/apple/av.c b/drivers/gpu/drm/apple/av.c index 8e8e0b25c12bc5..e839a560ae6557 100644 --- a/drivers/gpu/drm/apple/av.c +++ b/drivers/gpu/drm/apple/av.c @@ -11,6 +11,39 @@ #include "audio.h" #include "afk.h" #include "dcp.h" +#include "dcp-internal.h" + +struct dcp_av_audio_cmds { + /* commands in group 0*/ + u32 open; + u32 prepare; + u32 start_link; + u32 stop_link; + u32 unprepare; + /* commands in group 1*/ + u32 get_elements; + u32 get_product_attrs; +}; + +static const struct dcp_av_audio_cmds dcp_av_audio_cmds_v12_3 = { + .open = 6, + .prepare = 8, + .start_link = 9, + .stop_link = 12, + .unprepare = 13, + .get_elements = 18, + .get_product_attrs = 20, +}; + +static const struct dcp_av_audio_cmds dcp_av_audio_cmds_v13_5 = { + .open = 4, + .prepare = 6, + .start_link = 7, + .stop_link = 10, + .unprepare = 11, + .get_elements = 16, + .get_product_attrs = 18, +}; struct audiosrv_data { struct device *audio_dev; @@ -20,6 +53,8 @@ struct audiosrv_data { struct apple_epic_service *srv; struct rw_semaphore srv_rwsem; + + struct dcp_av_audio_cmds cmds; }; static void av_interface_init(struct apple_epic_service *service, const char *name, @@ -41,7 +76,8 @@ static void av_audiosrv_init(struct apple_epic_service *service, const char *nam up_write(&asrv->srv_rwsem); /* TODO: this must be done elsewhere */ - err = afk_service_call(asrv->srv, 0, 6, NULL, 0, 32, NULL, 0, 32); + err = afk_service_call(asrv->srv, 0, asrv->cmds.open, NULL, 0, 32, NULL, + 0, 32); if (err) dev_err(dcp->dev, "error opening audio service: %d\n", err); @@ -93,8 +129,9 @@ int dcp_audiosrv_prepare(struct device *dev, struct dcp_sound_cookie *cookie) int ret; down_write(&asrv->srv_rwsem); - ret = afk_service_call(asrv->srv, 0, 8, cookie, sizeof(*cookie), - 64 - sizeof(*cookie), NULL, 0, 64); + ret = afk_service_call(asrv->srv, 0, asrv->cmds.prepare, cookie, + sizeof(*cookie), 64 - sizeof(*cookie), NULL, 0, + 64); up_write(&asrv->srv_rwsem); return ret; @@ -108,8 +145,9 @@ int dcp_audiosrv_startlink(struct device *dev, struct dcp_sound_cookie *cookie) int ret; down_write(&asrv->srv_rwsem); - ret = afk_service_call(asrv->srv, 0, 9, cookie, sizeof(*cookie), - 64 - sizeof(*cookie), NULL, 0, 64); + ret = afk_service_call(asrv->srv, 0, asrv->cmds.start_link, cookie, + sizeof(*cookie), 64 - sizeof(*cookie), NULL, 0, + 64); up_write(&asrv->srv_rwsem); return ret; @@ -123,7 +161,8 @@ int dcp_audiosrv_stoplink(struct device *dev) int ret; down_write(&asrv->srv_rwsem); - ret = afk_service_call(asrv->srv, 0, 12, NULL, 0, 64, NULL, 0, 64); + ret = afk_service_call(asrv->srv, 0, asrv->cmds.stop_link, NULL, 0, 64, + NULL, 0, 64); up_write(&asrv->srv_rwsem); return ret; @@ -137,7 +176,8 @@ int dcp_audiosrv_unprepare(struct device *dev) int ret; down_write(&asrv->srv_rwsem); - ret = afk_service_call(asrv->srv, 0, 13, NULL, 0, 64, NULL, 0, 64); + ret = afk_service_call(asrv->srv, 0, asrv->cmds.unprepare, NULL, 0, 64, + NULL, 0, 64); up_write(&asrv->srv_rwsem); return ret; @@ -188,7 +228,8 @@ int dcp_audiosrv_get_elements(struct device *dev, void *elements, size_t maxsize int ret; down_write(&asrv->srv_rwsem); - ret = dcp_audiosrv_osobject_call(asrv->srv, 1, 18, elements, maxsize, &size); + ret = dcp_audiosrv_osobject_call(asrv->srv, 1, asrv->cmds.get_elements, + elements, maxsize, &size); up_write(&asrv->srv_rwsem); if (ret) @@ -208,7 +249,9 @@ int dcp_audiosrv_get_product_attrs(struct device *dev, void *attrs, size_t maxsi int ret; down_write(&asrv->srv_rwsem); - ret = dcp_audiosrv_osobject_call(asrv->srv, 1, 20, attrs, maxsize, &size); + ret = dcp_audiosrv_osobject_call(asrv->srv, 1, + asrv->cmds.get_product_attrs, attrs, + maxsize, &size); up_write(&asrv->srv_rwsem); if (ret) @@ -259,6 +302,19 @@ int avep_init(struct apple_dcp *dcp) return -ENOMEM; init_rwsem(&audiosrv_data->srv_rwsem); mutex_init(&audiosrv_data->plug_lock); + + switch (dcp->fw_compat) { + case DCP_FIRMWARE_V_12_3: + audiosrv_data->cmds = dcp_av_audio_cmds_v12_3; + break; + case DCP_FIRMWARE_V_13_5: + audiosrv_data->cmds = dcp_av_audio_cmds_v13_5; + break; + default: + dev_err(dcp->dev, "Audio not supported for firmware\n"); + return -ENODEV; + } + dcp->audiosrv = audiosrv_data; audio_pdata->dcp_dev = dcp->dev; From 596ff70859e4e0cb22e25a2a3aedd34c061a6612 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 13 Nov 2023 23:00:13 +0100 Subject: [PATCH 0620/1020] drm: apple: av: Do not open AV service from afk receive handler Use a completion to do it from avep_init() instead. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/av.c | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/apple/av.c b/drivers/gpu/drm/apple/av.c index e839a560ae6557..3f3a806fc523bd 100644 --- a/drivers/gpu/drm/apple/av.c +++ b/drivers/gpu/drm/apple/av.c @@ -51,6 +51,7 @@ struct audiosrv_data { bool plugged; struct mutex plug_lock; + struct completion init_completion; struct apple_epic_service *srv; struct rw_semaphore srv_rwsem; @@ -67,7 +68,6 @@ static void av_audiosrv_init(struct apple_epic_service *service, const char *nam { struct apple_dcp *dcp = service->ep->dcp; struct audiosrv_data *asrv = dcp->audiosrv; - int err; mutex_lock(&asrv->plug_lock); @@ -75,16 +75,8 @@ static void av_audiosrv_init(struct apple_epic_service *service, const char *nam asrv->srv = service; up_write(&asrv->srv_rwsem); - /* TODO: this must be done elsewhere */ - err = afk_service_call(asrv->srv, 0, asrv->cmds.open, NULL, 0, 32, NULL, - 0, 32); - if (err) - dev_err(dcp->dev, "error opening audio service: %d\n", err); - + complete(&asrv->init_completion); asrv->plugged = true; - if (asrv->hotplug_cb) - asrv->hotplug_cb(asrv->audio_dev, true); - mutex_unlock(&asrv->plug_lock); } @@ -314,6 +306,7 @@ int avep_init(struct apple_dcp *dcp) dev_err(dcp->dev, "Audio not supported for firmware\n"); return -ENODEV; } + init_completion(&audiosrv_data->init_completion); dcp->audiosrv = audiosrv_data; @@ -341,5 +334,29 @@ int avep_init(struct apple_dcp *dcp) if (ret) return ret; + ret = wait_for_completion_timeout(&dcp->audiosrv->init_completion, + msecs_to_jiffies(500)); + if (ret < 0) { + dev_err(dcp->dev, "error waiting on audio service init: %d\n", ret); + return ret; + } else if (!ret) { + dev_err(dcp->dev, "timeout while waiting for audio service init\n"); + return -ETIMEDOUT; + } + + /* open AV audio service */ + ret = afk_service_call(dcp->audiosrv->srv, 0, dcp->audiosrv->cmds.open, + NULL, 0, 32, NULL, 0, 32); + if (ret) { + dev_err(dcp->dev, "error opening audio service: %d\n", ret); + return ret; + } + + mutex_lock(&dcp->audiosrv->plug_lock); + if (dcp->audiosrv->hotplug_cb) + dcp->audiosrv->hotplug_cb(dcp->audiosrv->audio_dev, + dcp->audiosrv->plugged); + mutex_unlock(&dcp->audiosrv->plug_lock); + return 0; } From 933ebbe879c9436382bcb4f70572d754fb02a247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 23 Feb 2023 12:49:43 +0100 Subject: [PATCH 0621/1020] gpu: drm: apple: Add DCP audio driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Povišer --- drivers/gpu/drm/apple/Kconfig | 1 + drivers/gpu/drm/apple/Makefile | 4 + drivers/gpu/drm/apple/audio.c | 606 +++++++++++++++++++++++ drivers/gpu/drm/apple/hdmi-codec-chmap.h | 123 +++++ 4 files changed, 734 insertions(+) create mode 100644 drivers/gpu/drm/apple/audio.c create mode 100644 drivers/gpu/drm/apple/hdmi-codec-chmap.h diff --git a/drivers/gpu/drm/apple/Kconfig b/drivers/gpu/drm/apple/Kconfig index 6cf972f8ce1162..6b544a479d3264 100644 --- a/drivers/gpu/drm/apple/Kconfig +++ b/drivers/gpu/drm/apple/Kconfig @@ -18,6 +18,7 @@ config DRM_APPLE_AUDIO depends on DRM_APPLE depends on SND select SND_PCM + select SND_DMAENGINE_PCM config DRM_APPLE_DEBUG bool "Enable additional driver debugging" diff --git a/drivers/gpu/drm/apple/Makefile b/drivers/gpu/drm/apple/Makefile index 23618675a7a058..95fb8005745f48 100644 --- a/drivers/gpu/drm/apple/Makefile +++ b/drivers/gpu/drm/apple/Makefile @@ -12,9 +12,13 @@ apple_dcp-y += iomfb_v12_3.o apple_dcp-y += iomfb_v13_3.o apple_dcp-$(CONFIG_TRACING) += trace.o +apple_dcp_audio-y := audio.o obj-$(CONFIG_DRM_APPLE) += appledrm.o obj-$(CONFIG_DRM_APPLE) += apple_dcp.o +ifeq ($(CONFIG_DRM_APPLE_AUDIO),y) +obj-$(CONFIG_DRM_APPLE) += apple_dcp_audio.o +endif # header test diff --git a/drivers/gpu/drm/apple/audio.c b/drivers/gpu/drm/apple/audio.c new file mode 100644 index 00000000000000..6b931214463e6e --- /dev/null +++ b/drivers/gpu/drm/apple/audio.c @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * DCP Audio Bits + * + * Copyright (C) The Asahi Linux Contributors + * + * TODO: + * - figure some nice identification of the sound card (in case + * there's many DCP instances) + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "av.h" +#include "audio.h" +#include "parser.h" + +#define DCPAUD_ELEMENTS_MAXSIZE 16384 +#define DCPAUD_PRODUCTATTRS_MAXSIZE 1024 + +#define DRV_NAME "dcp-hdmi-audio" + +struct dcp_audio { + struct device *dev; + struct dcp_audio_pdata *pdata; + struct dma_chan *chan; + struct snd_card *card; + struct snd_jack *jack; + struct snd_pcm_substream *substream; + unsigned int open_cookie; + + struct mutex data_lock; + bool connected; + unsigned int connection_cookie; + + struct snd_pcm_chmap_elem selected_chmap; + struct dcp_sound_cookie selected_cookie; + void *elements; + void *productattrs; + + struct snd_pcm_chmap *chmap_info; +}; + +static const struct snd_pcm_hardware dcp_pcm_hw = { + .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_LE | + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 0, + .rate_max = UINT_MAX, + .channels_min = 1, + .channels_max = 16, + .buffer_bytes_max = SIZE_MAX, + .period_bytes_min = 4096, /* TODO */ + .period_bytes_max = SIZE_MAX, + .periods_min = 2, + .periods_max = UINT_MAX, +}; + +static int dcpaud_read_remote_info(struct dcp_audio *dcpaud) +{ + int ret; + + ret = dcp_audiosrv_get_elements(dcpaud->pdata->dcp_dev, dcpaud->elements, + DCPAUD_ELEMENTS_MAXSIZE); + if (ret < 0) + return ret; + + ret = dcp_audiosrv_get_product_attrs(dcpaud->pdata->dcp_dev, dcpaud->productattrs, + DCPAUD_PRODUCTATTRS_MAXSIZE); + if (ret < 0) + return ret; + + return 0; +} + +static int dcpaud_interval_bitmask(struct snd_interval *i, + unsigned int mask) +{ + struct snd_interval range; + if (!mask) + return -EINVAL; + + snd_interval_any(&range); + range.min = __ffs(mask); + range.max = __fls(mask); + return snd_interval_refine(i, &range); +} + +extern const struct snd_pcm_hw_constraint_list snd_pcm_known_rates; + +static void dcpaud_fill_fmt_sieve(struct snd_pcm_hw_params *params, + struct dcp_sound_format_mask *sieve) +{ + struct snd_interval *c = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *r = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_mask *f = hw_param_mask(params, + SNDRV_PCM_HW_PARAM_FORMAT); + int i; + + sieve->nchans = GENMASK(c->max, c->min); + sieve->formats = f->bits[0] | ((u64) f->bits[1]) << 32; /* TODO: don't open-code */ + + for (i = 0; i < snd_pcm_known_rates.count; i++) { + unsigned int rate = snd_pcm_known_rates.list[i]; + + if (snd_interval_test(r, rate)) + sieve->rates |= 1u << i; + } +} + +static void dcpaud_consult_elements(struct dcp_audio *dcpaud, + struct snd_pcm_hw_params *params, + struct dcp_sound_format_mask *hits) +{ + struct dcp_sound_format_mask sieve; + struct dcp_parse_ctx elements = { + .dcp = dev_get_drvdata(dcpaud->pdata->dcp_dev), + .blob = dcpaud->elements + 4, + .len = DCPAUD_ELEMENTS_MAXSIZE - 4, + .pos = 0, + }; + + dcpaud_fill_fmt_sieve(params, &sieve); + dev_dbg(dcpaud->dev, "elements in: %llx %x %x\n", sieve.formats, sieve.nchans, sieve.rates); + parse_sound_constraints(&elements, &sieve, hits); + dev_dbg(dcpaud->dev, "elements out: %llx %x %x\n", hits->formats, hits->nchans, hits->rates); +} + +static int dcpaud_select_cookie(struct dcp_audio *dcpaud, + struct snd_pcm_hw_params *params) +{ + struct dcp_sound_format_mask sieve; + struct dcp_parse_ctx elements = { + .dcp = dev_get_drvdata(dcpaud->pdata->dcp_dev), + .blob = dcpaud->elements + 4, + .len = DCPAUD_ELEMENTS_MAXSIZE - 4, + .pos = 0, + }; + + dcpaud_fill_fmt_sieve(params, &sieve); + return parse_sound_mode(&elements, &sieve, &dcpaud->selected_chmap, + &dcpaud->selected_cookie); +} + +static int dcpaud_rule_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct dcp_audio *dcpaud = rule->private; + struct snd_interval *c = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct dcp_sound_format_mask hits = {0, 0, 0}; + + dcpaud_consult_elements(dcpaud, params, &hits); + + return dcpaud_interval_bitmask(c, hits.nchans); +} + +static int dcpaud_refine_fmt_mask(struct snd_mask *m, u64 mask) +{ + struct snd_mask mask_mask; + + if (!mask) + return -EINVAL; + mask_mask.bits[0] = mask; + mask_mask.bits[1] = mask >> 32; + + return snd_mask_refine(m, &mask_mask); +} + +static int dcpaud_rule_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct dcp_audio *dcpaud = rule->private; + struct snd_mask *f = hw_param_mask(params, + SNDRV_PCM_HW_PARAM_FORMAT); + struct dcp_sound_format_mask hits; + + dcpaud_consult_elements(dcpaud, params, &hits); + + return dcpaud_refine_fmt_mask(f, hits.formats); +} + +static int dcpaud_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct dcp_audio *dcpaud = rule->private; + struct snd_interval *r = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct dcp_sound_format_mask hits; + + dcpaud_consult_elements(dcpaud, params, &hits); + + return snd_interval_rate_bits(r, hits.rates); +} + +static int dcp_pcm_open(struct snd_pcm_substream *substream) +{ + struct dcp_audio *dcpaud = substream->pcm->private_data; + struct dma_chan *chan = dcpaud->chan; + struct snd_dmaengine_dai_dma_data dma_data = { + .flags = SND_DMAENGINE_PCM_DAI_FLAG_PACK, + }; + struct snd_pcm_hardware hw; + int ret; + + mutex_lock(&dcpaud->data_lock); + if (!dcpaud->connected) { + mutex_unlock(&dcpaud->data_lock); + return -ENXIO; + } + dcpaud->open_cookie = dcpaud->connection_cookie; + mutex_unlock(&dcpaud->data_lock); + + ret = dcpaud_read_remote_info(dcpaud); + if (ret < 0) + return ret; + + snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + dcpaud_rule_format, dcpaud, + SNDRV_PCM_HW_PARAM_CHANNELS, SNDRV_PCM_HW_PARAM_RATE, -1); + snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + dcpaud_rule_channels, dcpaud, + SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_RATE, -1); + snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + dcpaud_rule_rate, dcpaud, + SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_CHANNELS, -1); + + hw = dcp_pcm_hw; + hw.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED; + hw.periods_min = 2; + hw.periods_max = UINT_MAX; + hw.period_bytes_min = 256; + hw.period_bytes_max = SIZE_MAX; // TODO dma_get_max_seg_size(dma_dev); + hw.buffer_bytes_max = SIZE_MAX; + hw.fifo_size = 16; + ret = snd_dmaengine_pcm_refine_runtime_hwparams(substream, &dma_data, + &hw, chan); + if (ret) + return ret; + substream->runtime->hw = hw; + + return snd_dmaengine_pcm_open(substream, chan); +} + +static int dcp_pcm_close(struct snd_pcm_substream *substream) +{ + struct dcp_audio *dcpaud = substream->pcm->private_data; + dcpaud->selected_chmap.channels = 0; + + return snd_dmaengine_pcm_close(substream); +} + +static int dcpaud_connection_up(struct dcp_audio *dcpaud) +{ + bool ret; + mutex_lock(&dcpaud->data_lock); + ret = dcpaud->connected && + dcpaud->open_cookie == dcpaud->connection_cookie; + mutex_unlock(&dcpaud->data_lock); + return ret; +} + +static int dcp_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct dcp_audio *dcpaud = substream->pcm->private_data; + struct dma_slave_config slave_config; + struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); + int ret; + + if (!dcpaud_connection_up(dcpaud)) + return -ENXIO; + + ret = dcpaud_select_cookie(dcpaud, params); + if (ret < 0) + return ret; + if (!ret) + return -EINVAL; + + memset(&slave_config, 0, sizeof(slave_config)); + ret = snd_hwparams_to_dma_slave_config(substream, params, &slave_config); + dev_info(dcpaud->dev, "snd_hwparams_to_dma_slave_config: %d\n", ret); + if (ret < 0) + return ret; + + slave_config.direction = DMA_MEM_TO_DEV; + /* + * The data entry from the DMA controller to the DPA peripheral + * is 32-bit wide no matter the actual sample size. + */ + slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + + ret = dmaengine_slave_config(chan, &slave_config); + dev_info(dcpaud->dev, "dmaengine_slave_config: %d\n", ret); + return ret; +} + +static int dcp_pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct dcp_audio *dcpaud = substream->pcm->private_data; + + if (!dcpaud_connection_up(dcpaud)) + return 0; + + return dcp_audiosrv_unprepare(dcpaud->pdata->dcp_dev); +} + +static int dcp_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct dcp_audio *dcpaud = substream->pcm->private_data; + + if (!dcpaud_connection_up(dcpaud)) + return -ENXIO; + + return dcp_audiosrv_prepare(dcpaud->pdata->dcp_dev, + &dcpaud->selected_cookie); +} + +static int dcp_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct dcp_audio *dcpaud = substream->pcm->private_data; + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (!dcpaud_connection_up(dcpaud)) + return -ENXIO; + + ret = dcp_audiosrv_startlink(dcpaud->pdata->dcp_dev, + &dcpaud->selected_cookie); + if (ret < 0) + return ret; + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + break; + + default: + return -EINVAL; + } + + ret = snd_dmaengine_pcm_trigger(substream, cmd); + if (ret < 0) + return ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + ret = dcp_audiosrv_stoplink(dcpaud->pdata->dcp_dev); + if (ret < 0) + return ret; + break; + } + + return 0; +} + +struct snd_pcm_ops dcp_playback_ops = { + .open = dcp_pcm_open, + .close = dcp_pcm_close, + .hw_params = dcp_pcm_hw_params, + .hw_free = dcp_pcm_hw_free, + .prepare = dcp_pcm_prepare, + .trigger = dcp_pcm_trigger, + .pointer = snd_dmaengine_pcm_pointer, +}; + +// Transitional workaround: for the chmap control TLV, advertise options +// copied from hdmi-codec.c +#include "hdmi-codec-chmap.h" + +static int dcpaud_chmap_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct dcp_audio *dcpaud = info->private_data; + unsigned int i; + + for (i = 0; i < info->max_channels; i++) + ucontrol->value.integer.value[i] = \ + (i < dcpaud->selected_chmap.channels) ? + dcpaud->selected_chmap.map[i] : SNDRV_CHMAP_UNKNOWN; + + return 0; +} + + +static int dcpaud_create_chmap_ctl(struct dcp_audio *dcpaud) +{ + struct snd_pcm *pcm = dcpaud->substream->pcm; + struct snd_pcm_chmap *chmap_info; + int ret; + + ret = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, NULL, + dcp_pcm_hw.channels_max, 0, &chmap_info); + + chmap_info->kctl->get = dcpaud_chmap_ctl_get; + chmap_info->chmap = hdmi_codec_8ch_chmaps; + chmap_info->private_data = dcpaud; + + return 0; +} + +static int dcpaud_create_pcm(struct dcp_audio *dcpaud) +{ + struct snd_card *card = dcpaud->card; + struct snd_pcm *pcm; + struct dma_chan *chan; + int ret; + + chan = of_dma_request_slave_channel(dcpaud->pdata->dpaudio_node, "tx"); + if (IS_ERR_OR_NULL(chan)) { + if (!chan) + return -EINVAL; + + dev_err(dcpaud->dev, "can't request audio TX DMA channel: %pE\n", chan); + return PTR_ERR(chan); + } + dcpaud->chan = chan; + +#define NUM_PLAYBACK 1 +#define NUM_CAPTURE 0 + + ret = snd_pcm_new(card, card->shortname, 0, NUM_PLAYBACK, NUM_CAPTURE, &pcm); + if (ret) + return ret; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &dcp_playback_ops); + dcpaud->substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + snd_pcm_set_managed_buffer(dcpaud->substream, SNDRV_DMA_TYPE_DEV_IRAM, + chan->device->dev, 1024 * 1024, + SIZE_MAX); + + pcm->nonatomic = true; + pcm->private_data = dcpaud; + strcpy(pcm->name, card->shortname); + + return 0; +} + +static void dcpaud_report_hotplug(struct device *dev, bool connected) +{ + struct dcp_audio *dcpaud = dev_get_drvdata(dev); + struct snd_pcm_substream *substream = dcpaud->substream; + + mutex_lock(&dcpaud->data_lock); + if (dcpaud->connected == connected) { + mutex_unlock(&dcpaud->data_lock); + return; + } + + dcpaud->connected = connected; + if (connected) + dcpaud->connection_cookie++; + mutex_unlock(&dcpaud->data_lock); + + snd_jack_report(dcpaud->jack, connected ? SND_JACK_AVOUT : 0); + + if (!connected) { + snd_pcm_stream_lock(substream); + snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); + snd_pcm_stream_unlock(substream); + } +} + +static int dcpaud_create_jack(struct dcp_audio *dcpaud) +{ + struct snd_card *card = dcpaud->card; + + return snd_jack_new(card, "HDMI/DP", SND_JACK_AVOUT, + &dcpaud->jack, true, false); +} + +static void dcpaud_set_card_names(struct dcp_audio *dcpaud) +{ + struct snd_card *card = dcpaud->card; + + strcpy(card->driver, "apple_dcp"); + strcpy(card->longname, "Apple DisplayPort"); + strcpy(card->shortname, "Apple DisplayPort"); +} + +#ifdef CONFIG_SND_DEBUG +static void dcpaud_expose_debugfs_blob(struct dcp_audio *dcpaud, const char *name, void *base, size_t size) +{ + struct debugfs_blob_wrapper *wrapper; + wrapper = devm_kzalloc(dcpaud->dev, sizeof(*wrapper), GFP_KERNEL); + if (!wrapper) + return; + wrapper->data = base; + wrapper->size = size; + debugfs_create_blob(name, 0600, dcpaud->card->debugfs_root, wrapper); +} +#else +static void dcpaud_expose_debugfs_blob(struct dcp_audio *dcpaud, const char *name, void *base, size_t size) {} +#endif + +static int dcpaud_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dcp_audio_pdata *pdata = dev->platform_data; + struct dcp_audio *dcpaud; + int ret; + + dcpaud = devm_kzalloc(dev, sizeof(*dcpaud), GFP_KERNEL); + if (!dcpaud) + return -ENOMEM; + dcpaud->dev = dev; + dcpaud->pdata = pdata; + mutex_init(&dcpaud->data_lock); + platform_set_drvdata(pdev, dcpaud); + + dcpaud->elements = devm_kzalloc(dev, DCPAUD_ELEMENTS_MAXSIZE, + GFP_KERNEL); + if (!dcpaud->elements) + return -ENOMEM; + + dcpaud->productattrs = devm_kzalloc(dev, DCPAUD_PRODUCTATTRS_MAXSIZE, + GFP_KERNEL); + if (!dcpaud->productattrs) + return -ENOMEM; + + ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &dcpaud->card); + if (ret) + return ret; + + dcpaud_set_card_names(dcpaud); + + ret = dcpaud_create_pcm(dcpaud); + if (ret) + goto err_free_card; + + ret = dcpaud_create_chmap_ctl(dcpaud); + if (ret) + goto err_free_card; + + ret = dcpaud_create_jack(dcpaud); + if (ret) + goto err_free_card; + + ret = snd_card_register(dcpaud->card); + if (ret) + goto err_free_card; + + dcpaud_expose_debugfs_blob(dcpaud, "selected_cookie", &dcpaud->selected_cookie, + sizeof(dcpaud->selected_cookie)); + dcpaud_expose_debugfs_blob(dcpaud, "elements", dcpaud->elements, + DCPAUD_ELEMENTS_MAXSIZE); + dcpaud_expose_debugfs_blob(dcpaud, "product_attrs", dcpaud->productattrs, + DCPAUD_PRODUCTATTRS_MAXSIZE); + + dcp_audiosrv_set_hotplug_cb(pdata->dcp_dev, dev, dcpaud_report_hotplug); + + return 0; + +err_free_card: + snd_card_free(dcpaud->card); + return ret; +} + +static int dcpaud_remove(struct platform_device *dev) +{ + struct dcp_audio *dcpaud = platform_get_drvdata(dev); + + dcp_audiosrv_set_hotplug_cb(dcpaud->pdata->dcp_dev, NULL, NULL); + snd_card_free(dcpaud->card); + + return 0; +} + +static struct platform_driver dcpaud_driver = { + .driver = { + .name = DRV_NAME, + }, + .probe = dcpaud_probe, + .remove = dcpaud_remove, +}; + +module_platform_driver(dcpaud_driver); + +MODULE_AUTHOR("Martin Povišer "); +MODULE_DESCRIPTION("Apple DCP HDMI Audio Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/gpu/drm/apple/hdmi-codec-chmap.h b/drivers/gpu/drm/apple/hdmi-codec-chmap.h new file mode 100644 index 00000000000000..f98e1e86b89602 --- /dev/null +++ b/drivers/gpu/drm/apple/hdmi-codec-chmap.h @@ -0,0 +1,123 @@ +// copied from sound/soc/codecs/hdmi-codec.c + +#include + +/* Channel maps for multi-channel playbacks, up to 8 n_ch */ +static const struct snd_pcm_chmap_elem hdmi_codec_8ch_chmaps[] = { + { .channels = 2, /* CA_ID 0x00 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, + { .channels = 4, /* CA_ID 0x01 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA } }, + { .channels = 4, /* CA_ID 0x02 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC } }, + { .channels = 4, /* CA_ID 0x03 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC } }, + { .channels = 6, /* CA_ID 0x04 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 6, /* CA_ID 0x05 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 6, /* CA_ID 0x06 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 6, /* CA_ID 0x07 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 6, /* CA_ID 0x08 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { .channels = 6, /* CA_ID 0x09 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { .channels = 6, /* CA_ID 0x0A */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { .channels = 6, /* CA_ID 0x0B */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { .channels = 8, /* CA_ID 0x0C */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 8, /* CA_ID 0x0D */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 8, /* CA_ID 0x0E */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 8, /* CA_ID 0x0F */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 8, /* CA_ID 0x10 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, + { .channels = 8, /* CA_ID 0x11 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, + { .channels = 8, /* CA_ID 0x12 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, + { .channels = 8, /* CA_ID 0x13 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, + { .channels = 8, /* CA_ID 0x14 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x15 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x16 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x17 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x18 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x19 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x1A */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x1B */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x1C */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x1D */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x1E */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x1F */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { } +}; From c2fd2154a1851c8edc4e5d984a91768d0ae1512f Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 13 Apr 2024 12:48:42 +0200 Subject: [PATCH 0622/1020] fixup! drm/apple: Add support for the macOS 13.2 DCP firmware Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb_template.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index ace4dc74996987..fc036e79acb0ce 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -797,7 +797,7 @@ void DCP_FW_NAME(iomfb_poweron)(struct apple_dcp *dcp) struct dcp_wait_cookie *cookie; int ret; u32 handle; - dev_err(dcp->dev, "dcp_poweron() starting\n"); + dev_info(dcp->dev, "dcp_poweron() starting\n"); cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); if (!cookie) @@ -939,7 +939,7 @@ void DCP_FW_NAME(iomfb_poweroff)(struct apple_dcp *dcp) kref_put(&poff_cookie->refcount, release_wait_cookie); - dev_err(dcp->dev, "dcp_poweroff() done\n"); + dev_info(dcp->dev, "dcp_poweroff() done\n"); } static void last_client_closed_sleep(struct apple_dcp *dcp, void *out, void *cookie) @@ -984,7 +984,7 @@ void DCP_FW_NAME(iomfb_sleep)(struct apple_dcp *dcp) dev_warn(dcp->dev, "setDCPPower(0) timeout %u ms\n", 1000); kref_put(&cookie->refcount, release_wait_cookie); - dev_err(dcp->dev, "dcp_sleep() done\n"); + dev_info(dcp->dev, "dcp_sleep() done\n"); } static void dcpep_cb_hotplug(struct apple_dcp *dcp, u64 *connected) From 39436a7e0100f91abc4ad4eb619c054c6612a293 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 14 Apr 2024 16:22:25 +0200 Subject: [PATCH 0623/1020] drm: apple: dptx: Remove DPTX disconnect/connect on init This was only necessary for dcp0 on M2* devices presumably because the reset in m1n1 doesn't work as intended. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 8558423169a44d..a0ac1906ec1a93 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -386,10 +386,17 @@ int dcp_start(struct platform_device *pdev) ret); ret = dptxep_init(dcp); - if (ret) + if (ret) { dev_warn(dcp->dev, "Failed to start DPTX endpoint: %d\n", ret); - else if (dcp->dptxport[0].enabled) { +#ifdef DCP_DPTX_DISCONNECT_ON_INIT + /* + * This disconnect / connect cycle on init is only necessary + * when using dcp0 on j473, j474s and presumedly j475c. + * Since dcp0 is not used at the moment let's avoid this + * since it is possibly the cause for startup issues. + */ + } else if (dcp->dptxport[0].enabled) { bool connected; /* force disconnect on start - necessary if the display * is already up from m1n1 @@ -404,10 +411,11 @@ int dcp_start(struct platform_device *pdev) // necessary on j473/j474 but not on j314c if (connected) dcp_dptx_connect(dcp, 0); +#endif } - } else if (dcp->phy) + } else if (dcp->phy) { dev_warn(dcp->dev, "OS firmware incompatible with dptxport EP\n"); - + } ret = iomfb_start_rtkit(dcp); if (ret) dev_err(dcp->dev, "Failed to start IOMFB endpoint: %d\n", ret); From d341bcafb757b28460d1686d4058feb926feed1c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 14 Apr 2024 16:47:01 +0200 Subject: [PATCH 0624/1020] drm: apple: audio: init AV endpoint later This seems to get rid of initialization timeouts / failures. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index a0ac1906ec1a93..176c8c09c0f454 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -371,14 +371,6 @@ int dcp_start(struct platform_device *pdev) if (ret) dev_warn(dcp->dev, "Failed to start system endpoint: %d\n", ret); -#if IS_ENABLED(CONFIG_DRM_APPLE_AUDIO) - if (!noaudio) { - ret = avep_init(dcp); - if (ret) - dev_warn(dcp->dev, "Failed to start AV endpoint: %d", ret); - } -#endif - if (dcp->phy && dcp->fw_compat >= DCP_FIRMWARE_V_13_5) { ret = ibootep_init(dcp); if (ret) @@ -420,6 +412,15 @@ int dcp_start(struct platform_device *pdev) if (ret) dev_err(dcp->dev, "Failed to start IOMFB endpoint: %d\n", ret); +#if IS_ENABLED(CONFIG_DRM_APPLE_AUDIO) + if (!noaudio) { + ret = avep_init(dcp); + if (ret) + dev_warn(dcp->dev, "Failed to start AV endpoint: %d", ret); + ret = 0; + } +#endif + return ret; } EXPORT_SYMBOL(dcp_start); From bc56f1d2820151225feae2307941c761d1018490 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 20 Apr 2024 21:00:37 +0200 Subject: [PATCH 0625/1020] drm: apple: av: Use a workqueue Functionally a revert of "drm: apple: av: Do not open AV service from afk receive handler" with more workqueues. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/av.c | 63 ++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/apple/av.c b/drivers/gpu/drm/apple/av.c index 3f3a806fc523bd..492f5dc8404999 100644 --- a/drivers/gpu/drm/apple/av.c +++ b/drivers/gpu/drm/apple/av.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "audio.h" #include "afk.h" @@ -51,9 +52,10 @@ struct audiosrv_data { bool plugged; struct mutex plug_lock; - struct completion init_completion; struct apple_epic_service *srv; struct rw_semaphore srv_rwsem; + /* Workqueue for starting the audio service */ + struct work_struct start_av_service_wq; struct dcp_av_audio_cmds cmds; }; @@ -75,9 +77,9 @@ static void av_audiosrv_init(struct apple_epic_service *service, const char *nam asrv->srv = service; up_write(&asrv->srv_rwsem); - complete(&asrv->init_completion); asrv->plugged = true; mutex_unlock(&asrv->plug_lock); + schedule_work(&asrv->start_av_service_wq); } static void av_audiosrv_teardown(struct apple_epic_service *service) @@ -280,6 +282,37 @@ static const struct apple_epic_service_ops avep_ops[] = { {} }; +static void av_work_service_start(struct work_struct *work) +{ + int ret; + struct audiosrv_data *audiosrv_data; + struct apple_dcp *dcp; + + audiosrv_data = container_of(work, struct audiosrv_data, start_av_service_wq); + if (!audiosrv_data->srv || + !audiosrv_data->srv->ep || + !audiosrv_data->srv->ep->dcp) { + pr_err("%s: dcp: av: NULL ptr during startup\n", __func__); + return; + } + dcp = audiosrv_data->srv->ep->dcp; + + /* open AV audio service */ + dev_info(dcp->dev, "%s: starting audio service\n", __func__); + ret = afk_service_call(dcp->audiosrv->srv, 0, dcp->audiosrv->cmds.open, + NULL, 0, 32, NULL, 0, 32); + if (ret) { + dev_err(dcp->dev, "error opening audio service: %d\n", ret); + return; + } + + mutex_lock(&dcp->audiosrv->plug_lock); + if (dcp->audiosrv->hotplug_cb) + dcp->audiosrv->hotplug_cb(dcp->audiosrv->audio_dev, + dcp->audiosrv->plugged); + mutex_unlock(&dcp->audiosrv->plug_lock); +} + int avep_init(struct apple_dcp *dcp) { struct dcp_audio_pdata *audio_pdata; @@ -306,7 +339,7 @@ int avep_init(struct apple_dcp *dcp) dev_err(dcp->dev, "Audio not supported for firmware\n"); return -ENODEV; } - init_completion(&audiosrv_data->init_completion); + INIT_WORK(&audiosrv_data->start_av_service_wq, av_work_service_start); dcp->audiosrv = audiosrv_data; @@ -334,29 +367,5 @@ int avep_init(struct apple_dcp *dcp) if (ret) return ret; - ret = wait_for_completion_timeout(&dcp->audiosrv->init_completion, - msecs_to_jiffies(500)); - if (ret < 0) { - dev_err(dcp->dev, "error waiting on audio service init: %d\n", ret); - return ret; - } else if (!ret) { - dev_err(dcp->dev, "timeout while waiting for audio service init\n"); - return -ETIMEDOUT; - } - - /* open AV audio service */ - ret = afk_service_call(dcp->audiosrv->srv, 0, dcp->audiosrv->cmds.open, - NULL, 0, 32, NULL, 0, 32); - if (ret) { - dev_err(dcp->dev, "error opening audio service: %d\n", ret); - return ret; - } - - mutex_lock(&dcp->audiosrv->plug_lock); - if (dcp->audiosrv->hotplug_cb) - dcp->audiosrv->hotplug_cb(dcp->audiosrv->audio_dev, - dcp->audiosrv->plugged); - mutex_unlock(&dcp->audiosrv->plug_lock); - return 0; } From bd1c2aac4859fd1b92ca62b405eed11600ef0154 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 20 Apr 2024 21:06:19 +0200 Subject: [PATCH 0626/1020] drm: apple: audio: move the audio driver into the DCP module Those two drivers are closely linked and should always exists together. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/Makefile | 6 +----- drivers/gpu/drm/apple/audio.c | 14 +++++++++----- drivers/gpu/drm/apple/dcp.c | 22 +++++++++++++++++++++- drivers/gpu/drm/apple/dcp.h | 4 ++++ 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/apple/Makefile b/drivers/gpu/drm/apple/Makefile index 95fb8005745f48..3e03c86f814312 100644 --- a/drivers/gpu/drm/apple/Makefile +++ b/drivers/gpu/drm/apple/Makefile @@ -5,6 +5,7 @@ CFLAGS_trace.o = -I$(src) appledrm-y := apple_drv.o apple_dcp-y := afk.o dcp.o dcp_backlight.o dptxep.o iomfb.o parser.o systemep.o +apple_dcp-$(CONFIG_DRM_APPLE_AUDIO) += audio.o apple_dcp-$(CONFIG_DRM_APPLE_AUDIO) += av.o apple_dcp-y += connector.o apple_dcp-y += ibootep.o @@ -12,13 +13,8 @@ apple_dcp-y += iomfb_v12_3.o apple_dcp-y += iomfb_v13_3.o apple_dcp-$(CONFIG_TRACING) += trace.o -apple_dcp_audio-y := audio.o - obj-$(CONFIG_DRM_APPLE) += appledrm.o obj-$(CONFIG_DRM_APPLE) += apple_dcp.o -ifeq ($(CONFIG_DRM_APPLE_AUDIO),y) -obj-$(CONFIG_DRM_APPLE) += apple_dcp_audio.o -endif # header test diff --git a/drivers/gpu/drm/apple/audio.c b/drivers/gpu/drm/apple/audio.c index 6b931214463e6e..2c82a1a47a6d51 100644 --- a/drivers/gpu/drm/apple/audio.c +++ b/drivers/gpu/drm/apple/audio.c @@ -598,9 +598,13 @@ static struct platform_driver dcpaud_driver = { .remove = dcpaud_remove, }; -module_platform_driver(dcpaud_driver); +void __init dcp_audio_register(void) +{ + platform_driver_register(&dcpaud_driver); +} + +void __exit dcp_audio_unregister(void) +{ + platform_driver_unregister(&dcpaud_driver); +} -MODULE_AUTHOR("Martin Povišer "); -MODULE_DESCRIPTION("Apple DCP HDMI Audio Driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 176c8c09c0f454..84fae2ca1246cf 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -1140,7 +1140,27 @@ static struct platform_driver apple_platform_driver = { }, }; -drm_module_platform_driver(apple_platform_driver); +static int __init apple_dcp_register(void) +{ + if (drm_firmware_drivers_only()) + return -ENODEV; + +#if IS_ENABLED(CONFIG_DRM_APPLE_AUDIO) + dcp_audio_register(); +#endif + return platform_driver_register(&apple_platform_driver); +} + +static void __exit apple_dcp_unregister(void) +{ + platform_driver_unregister(&apple_platform_driver); +#if IS_ENABLED(CONFIG_DRM_APPLE_AUDIO) + dcp_audio_unregister(); +#endif +} + +module_init(apple_dcp_register); +module_exit(apple_dcp_unregister); MODULE_AUTHOR("Alyssa Rosenzweig "); MODULE_DESCRIPTION("Apple Display Controller DRM driver"); diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h index 0a1981040996dc..c284a089b6e0bf 100644 --- a/drivers/gpu/drm/apple/dcp.h +++ b/drivers/gpu/drm/apple/dcp.h @@ -62,4 +62,8 @@ int dptxep_init(struct apple_dcp *dcp); int ibootep_init(struct apple_dcp *dcp); int avep_init(struct apple_dcp *dcp); + +void __init dcp_audio_register(void); +void __exit dcp_audio_unregister(void); + #endif From 8fcc7abeab70dfa91d08156c9176495f9422e85b Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 21 Apr 2024 15:47:20 +0200 Subject: [PATCH 0627/1020] drm: apple: audio: Make the DP/HDMI audio driver a full driver The main advantage is that it allows runtime PM which would have been manually implemented with the ad-hoc instantiated platform driver. This also probes the devices as component of the DRM driver which allows to simplify the the interface between the av endpoint and the audio driver. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 20 +++- drivers/gpu/drm/apple/audio.c | 146 +++++++++++++++++++++--------- drivers/gpu/drm/apple/audio.h | 14 +-- drivers/gpu/drm/apple/av.c | 72 ++++++--------- 4 files changed, 155 insertions(+), 97 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index ec966774b1e80d..cd628ae25b1728 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -574,7 +575,7 @@ const struct component_master_ops apple_drm_ops = { static int add_dcp_components(struct device *dev, struct component_match **matchptr) { - struct device_node *np; + struct device_node *np, *endpoint, *port; int num = 0; for_each_matching_node(np, apple_dcp_id_tbl) { @@ -582,6 +583,23 @@ static int add_dcp_components(struct device *dev, drm_of_component_match_add(dev, matchptr, component_compare_of, np); num++; + for_each_endpoint_of_node(np, endpoint) { + port = of_graph_get_remote_port_parent(endpoint); + if (!port) + continue; + +#if !IS_ENABLED(CONFIG_DRM_APPLE_AUDIO) + if (of_device_is_compatible(port, "apple,dpaudio")) { + of_node_put(port); + continue; + } +#endif + if (of_device_is_available(port)) + drm_of_component_match_add(dev, matchptr, + component_compare_of, + port); + of_node_put(port); + } } of_node_put(np); } diff --git a/drivers/gpu/drm/apple/audio.c b/drivers/gpu/drm/apple/audio.c index 2c82a1a47a6d51..ca738354333be5 100644 --- a/drivers/gpu/drm/apple/audio.c +++ b/drivers/gpu/drm/apple/audio.c @@ -11,9 +11,12 @@ #define DEBUG +#include #include #include #include +#include +#include #include #include #include @@ -22,17 +25,16 @@ #include #include "av.h" +#include "dcp.h" #include "audio.h" #include "parser.h" #define DCPAUD_ELEMENTS_MAXSIZE 16384 #define DCPAUD_PRODUCTATTRS_MAXSIZE 1024 -#define DRV_NAME "dcp-hdmi-audio" - struct dcp_audio { struct device *dev; - struct dcp_audio_pdata *pdata; + struct device *dcp_dev; struct dma_chan *chan; struct snd_card *card; struct snd_jack *jack; @@ -72,12 +74,12 @@ static int dcpaud_read_remote_info(struct dcp_audio *dcpaud) { int ret; - ret = dcp_audiosrv_get_elements(dcpaud->pdata->dcp_dev, dcpaud->elements, + ret = dcp_audiosrv_get_elements(dcpaud->dcp_dev, dcpaud->elements, DCPAUD_ELEMENTS_MAXSIZE); if (ret < 0) return ret; - ret = dcp_audiosrv_get_product_attrs(dcpaud->pdata->dcp_dev, dcpaud->productattrs, + ret = dcp_audiosrv_get_product_attrs(dcpaud->dcp_dev, dcpaud->productattrs, DCPAUD_PRODUCTATTRS_MAXSIZE); if (ret < 0) return ret; @@ -128,7 +130,7 @@ static void dcpaud_consult_elements(struct dcp_audio *dcpaud, { struct dcp_sound_format_mask sieve; struct dcp_parse_ctx elements = { - .dcp = dev_get_drvdata(dcpaud->pdata->dcp_dev), + .dcp = dev_get_drvdata(dcpaud->dcp_dev), .blob = dcpaud->elements + 4, .len = DCPAUD_ELEMENTS_MAXSIZE - 4, .pos = 0, @@ -145,7 +147,7 @@ static int dcpaud_select_cookie(struct dcp_audio *dcpaud, { struct dcp_sound_format_mask sieve; struct dcp_parse_ctx elements = { - .dcp = dev_get_drvdata(dcpaud->pdata->dcp_dev), + .dcp = dev_get_drvdata(dcpaud->dcp_dev), .blob = dcpaud->elements + 4, .len = DCPAUD_ELEMENTS_MAXSIZE - 4, .pos = 0, @@ -317,7 +319,7 @@ static int dcp_pcm_hw_free(struct snd_pcm_substream *substream) if (!dcpaud_connection_up(dcpaud)) return 0; - return dcp_audiosrv_unprepare(dcpaud->pdata->dcp_dev); + return dcp_audiosrv_unprepare(dcpaud->dcp_dev); } static int dcp_pcm_prepare(struct snd_pcm_substream *substream) @@ -327,7 +329,7 @@ static int dcp_pcm_prepare(struct snd_pcm_substream *substream) if (!dcpaud_connection_up(dcpaud)) return -ENXIO; - return dcp_audiosrv_prepare(dcpaud->pdata->dcp_dev, + return dcp_audiosrv_prepare(dcpaud->dcp_dev, &dcpaud->selected_cookie); } @@ -342,7 +344,7 @@ static int dcp_pcm_trigger(struct snd_pcm_substream *substream, int cmd) if (!dcpaud_connection_up(dcpaud)) return -ENXIO; - ret = dcp_audiosrv_startlink(dcpaud->pdata->dcp_dev, + ret = dcp_audiosrv_startlink(dcpaud->dcp_dev, &dcpaud->selected_cookie); if (ret < 0) return ret; @@ -367,7 +369,7 @@ static int dcp_pcm_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: - ret = dcp_audiosrv_stoplink(dcpaud->pdata->dcp_dev); + ret = dcp_audiosrv_stoplink(dcpaud->dcp_dev); if (ret < 0) return ret; break; @@ -429,7 +431,7 @@ static int dcpaud_create_pcm(struct dcp_audio *dcpaud) struct dma_chan *chan; int ret; - chan = of_dma_request_slave_channel(dcpaud->pdata->dpaudio_node, "tx"); + chan = of_dma_request_slave_channel(dcpaud->dev->of_node, "tx"); if (IS_ERR_OR_NULL(chan)) { if (!chan) return -EINVAL; @@ -459,9 +461,8 @@ static int dcpaud_create_pcm(struct dcp_audio *dcpaud) return 0; } -static void dcpaud_report_hotplug(struct device *dev, bool connected) +static void dcpaud_report_hotplug(struct dcp_audio *dcpaud, bool connected) { - struct dcp_audio *dcpaud = dev_get_drvdata(dev); struct snd_pcm_substream *substream = dcpaud->substream; mutex_lock(&dcpaud->data_lock); @@ -516,30 +517,44 @@ static void dcpaud_expose_debugfs_blob(struct dcp_audio *dcpaud, const char *nam static void dcpaud_expose_debugfs_blob(struct dcp_audio *dcpaud, const char *name, void *base, size_t size) {} #endif -static int dcpaud_probe(struct platform_device *pdev) +void dcpaud_connect(struct platform_device *pdev, bool connected) { - struct device *dev = &pdev->dev; - struct dcp_audio_pdata *pdata = dev->platform_data; - struct dcp_audio *dcpaud; - int ret; + struct dcp_audio *dcpaud = platform_get_drvdata(pdev); + dcpaud_report_hotplug(dcpaud, connected); +} - dcpaud = devm_kzalloc(dev, sizeof(*dcpaud), GFP_KERNEL); - if (!dcpaud) - return -ENOMEM; - dcpaud->dev = dev; - dcpaud->pdata = pdata; - mutex_init(&dcpaud->data_lock); - platform_set_drvdata(pdev, dcpaud); +void dcpaud_disconnect(struct platform_device *pdev) +{ + struct dcp_audio *dcpaud = platform_get_drvdata(pdev); + dcpaud_report_hotplug(dcpaud, false); +} - dcpaud->elements = devm_kzalloc(dev, DCPAUD_ELEMENTS_MAXSIZE, - GFP_KERNEL); - if (!dcpaud->elements) - return -ENOMEM; +static int dcpaud_comp_bind(struct device *dev, struct device *main, void *data) +{ + struct dcp_audio *dcpaud = dev_get_drvdata(dev); + struct device_node *endpoint, *dcp_node = NULL; + struct platform_device *dcp_pdev; + int ret; - dcpaud->productattrs = devm_kzalloc(dev, DCPAUD_PRODUCTATTRS_MAXSIZE, - GFP_KERNEL); - if (!dcpaud->productattrs) - return -ENOMEM; + /* find linked DCP instance */ + endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0); + if (endpoint) { + dcp_node = of_graph_get_remote_port_parent(endpoint); + of_node_put(endpoint); + } + if (!dcp_node || !of_device_is_available(dcp_node)) { + of_node_put(dcp_node); + dev_info(dev, "No audio support\n"); + return 0; + } + + dcp_pdev = of_find_device_by_node(dcp_node); + of_node_put(dcp_node); + if (!dcp_pdev) { + dev_info(dev, "No DP/HDMI audio device not ready\n"); + return 0; + } + dcpaud->dcp_dev = &dcp_pdev->dev; ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, 0, &dcpaud->card); @@ -571,8 +586,6 @@ static int dcpaud_probe(struct platform_device *pdev) dcpaud_expose_debugfs_blob(dcpaud, "product_attrs", dcpaud->productattrs, DCPAUD_PRODUCTATTRS_MAXSIZE); - dcp_audiosrv_set_hotplug_cb(pdata->dcp_dev, dev, dcpaud_report_hotplug); - return 0; err_free_card: @@ -580,22 +593,71 @@ static int dcpaud_probe(struct platform_device *pdev) return ret; } -static int dcpaud_remove(struct platform_device *dev) +static void dcpaud_comp_unbind(struct device *dev, struct device *main, + void *data) { - struct dcp_audio *dcpaud = platform_get_drvdata(dev); + struct dcp_audio *dcpaud = dev_get_drvdata(dev); - dcp_audiosrv_set_hotplug_cb(dcpaud->pdata->dcp_dev, NULL, NULL); snd_card_free(dcpaud->card); +} + +static const struct component_ops dcpaud_comp_ops = { + .bind = dcpaud_comp_bind, + .unbind = dcpaud_comp_unbind, +}; + +static int dcpaud_probe(struct platform_device *pdev) +{ + struct dcp_audio *dcpaud; + + dcpaud = devm_kzalloc(&pdev->dev, sizeof(*dcpaud), GFP_KERNEL); + if (!dcpaud) + return -ENOMEM; + + dcpaud->elements = devm_kzalloc(&pdev->dev, DCPAUD_ELEMENTS_MAXSIZE, + GFP_KERNEL); + if (!dcpaud->elements) + return -ENOMEM; + + dcpaud->productattrs = devm_kzalloc(&pdev->dev, DCPAUD_PRODUCTATTRS_MAXSIZE, + GFP_KERNEL); + if (!dcpaud->productattrs) + return -ENOMEM; + + dcpaud->dev = &pdev->dev; + mutex_init(&dcpaud->data_lock); + platform_set_drvdata(pdev, dcpaud); + + return component_add(&pdev->dev, &dcpaud_comp_ops); +} + +static int dcpaud_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dcpaud_comp_ops); return 0; } +static void dcpaud_shutdown(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dcpaud_comp_ops); +} + +// static DEFINE_SIMPLE_DEV_PM_OPS(dcpaud_pm_ops, dcpaud_suspend, dcpaud_resume); + +static const struct of_device_id dcpaud_of_match[] = { + { .compatible = "apple,dpaudio" }, + {} +}; + static struct platform_driver dcpaud_driver = { .driver = { - .name = DRV_NAME, + .name = "dcp-dp-audio", + .of_match_table = dcpaud_of_match, }, - .probe = dcpaud_probe, - .remove = dcpaud_remove, + .probe = dcpaud_probe, + .remove = dcpaud_remove, + .shutdown = dcpaud_shutdown, }; void __init dcp_audio_register(void) diff --git a/drivers/gpu/drm/apple/audio.h b/drivers/gpu/drm/apple/audio.h index 3cf4d31417694e..83b990dc6c343f 100644 --- a/drivers/gpu/drm/apple/audio.h +++ b/drivers/gpu/drm/apple/audio.h @@ -4,18 +4,9 @@ #include struct device; -struct device_node; +struct platform_device; struct dcp_sound_cookie; -typedef void (*dcp_audio_hotplug_callback)(struct device *dev, bool connected); - -struct dcp_audio_pdata { - struct device *dcp_dev; - struct device_node *dpaudio_node; -}; - -void dcp_audiosrv_set_hotplug_cb(struct device *dev, struct device *audio_dev, - dcp_audio_hotplug_callback cb); int dcp_audiosrv_prepare(struct device *dev, struct dcp_sound_cookie *cookie); int dcp_audiosrv_startlink(struct device *dev, struct dcp_sound_cookie *cookie); int dcp_audiosrv_stoplink(struct device *dev); @@ -23,4 +14,7 @@ int dcp_audiosrv_unprepare(struct device *dev); int dcp_audiosrv_get_elements(struct device *dev, void *elements, size_t maxsize); int dcp_audiosrv_get_product_attrs(struct device *dev, void *attrs, size_t maxsize); +void dcpaud_connect(struct platform_device *pdev, bool connected); +void dcpaud_disconnect(struct platform_device *pdev); + #endif /* __AUDIO_H__ */ diff --git a/drivers/gpu/drm/apple/av.c b/drivers/gpu/drm/apple/av.c index 492f5dc8404999..ea80e839d8ea7e 100644 --- a/drivers/gpu/drm/apple/av.c +++ b/drivers/gpu/drm/apple/av.c @@ -5,6 +5,8 @@ #include #include +#include +#include #include #include #include @@ -47,8 +49,7 @@ static const struct dcp_av_audio_cmds dcp_av_audio_cmds_v13_5 = { }; struct audiosrv_data { - struct device *audio_dev; - dcp_audio_hotplug_callback hotplug_cb; + struct platform_device *audio_dev; bool plugged; struct mutex plug_lock; @@ -94,28 +95,12 @@ static void av_audiosrv_teardown(struct apple_epic_service *service) up_write(&asrv->srv_rwsem); asrv->plugged = false; - if (asrv->hotplug_cb) - asrv->hotplug_cb(asrv->audio_dev, false); + if (asrv->audio_dev) + dcpaud_disconnect(asrv->audio_dev); mutex_unlock(&asrv->plug_lock); } -void dcp_audiosrv_set_hotplug_cb(struct device *dev, struct device *audio_dev, - dcp_audio_hotplug_callback cb) -{ - struct apple_dcp *dcp = dev_get_drvdata(dev); - struct audiosrv_data *asrv = dcp->audiosrv; - - mutex_lock(&asrv->plug_lock); - asrv->audio_dev = audio_dev; - asrv->hotplug_cb = cb; - - if (cb) - cb(audio_dev, asrv->plugged); - mutex_unlock(&asrv->plug_lock); -} -EXPORT_SYMBOL_GPL(dcp_audiosrv_set_hotplug_cb); - int dcp_audiosrv_prepare(struct device *dev, struct dcp_sound_cookie *cookie) { struct apple_dcp *dcp = dev_get_drvdata(dev); @@ -130,7 +115,6 @@ int dcp_audiosrv_prepare(struct device *dev, struct dcp_sound_cookie *cookie) return ret; } -EXPORT_SYMBOL_GPL(dcp_audiosrv_prepare); int dcp_audiosrv_startlink(struct device *dev, struct dcp_sound_cookie *cookie) { @@ -146,7 +130,6 @@ int dcp_audiosrv_startlink(struct device *dev, struct dcp_sound_cookie *cookie) return ret; } -EXPORT_SYMBOL_GPL(dcp_audiosrv_startlink); int dcp_audiosrv_stoplink(struct device *dev) { @@ -161,7 +144,6 @@ int dcp_audiosrv_stoplink(struct device *dev) return ret; } -EXPORT_SYMBOL_GPL(dcp_audiosrv_stoplink); int dcp_audiosrv_unprepare(struct device *dev) { @@ -176,7 +158,6 @@ int dcp_audiosrv_unprepare(struct device *dev) return ret; } -EXPORT_SYMBOL_GPL(dcp_audiosrv_unprepare); static int dcp_audiosrv_osobject_call(struct apple_epic_service *service, u16 group, @@ -233,7 +214,6 @@ int dcp_audiosrv_get_elements(struct device *dev, void *elements, size_t maxsize return ret; } -EXPORT_SYMBOL_GPL(dcp_audiosrv_get_elements); int dcp_audiosrv_get_product_attrs(struct device *dev, void *attrs, size_t maxsize) { @@ -255,7 +235,6 @@ int dcp_audiosrv_get_product_attrs(struct device *dev, void *attrs, size_t maxsi return ret; } -EXPORT_SYMBOL_GPL(dcp_audiosrv_get_product_attrs); static int av_audiosrv_report(struct apple_epic_service *service, u32 idx, const void *data, size_t data_size) @@ -307,23 +286,22 @@ static void av_work_service_start(struct work_struct *work) } mutex_lock(&dcp->audiosrv->plug_lock); - if (dcp->audiosrv->hotplug_cb) - dcp->audiosrv->hotplug_cb(dcp->audiosrv->audio_dev, - dcp->audiosrv->plugged); + if (dcp->audiosrv->audio_dev) + dcpaud_connect(dcp->audiosrv->audio_dev, dcp->audiosrv->plugged); mutex_unlock(&dcp->audiosrv->plug_lock); } int avep_init(struct apple_dcp *dcp) { - struct dcp_audio_pdata *audio_pdata; - struct platform_device *audio_pdev; struct audiosrv_data *audiosrv_data; + struct platform_device *audio_pdev; struct device *dev = dcp->dev; + struct device_node *endpoint, *audio_node = NULL; + struct device_link *dev_link; int ret; audiosrv_data = devm_kzalloc(dcp->dev, sizeof(*audiosrv_data), GFP_KERNEL); - audio_pdata = devm_kzalloc(dcp->dev, sizeof(*audio_pdata), GFP_KERNEL); - if (!audiosrv_data || !audio_pdata) + if (!audiosrv_data) return -ENOMEM; init_rwsem(&audiosrv_data->srv_rwsem); mutex_init(&audiosrv_data->plug_lock); @@ -343,21 +321,27 @@ int avep_init(struct apple_dcp *dcp) dcp->audiosrv = audiosrv_data; - audio_pdata->dcp_dev = dcp->dev; - /* TODO: free OF reference */ - audio_pdata->dpaudio_node = \ - of_parse_phandle(dev->of_node, "apple,audio-xmitter", 0); - if (!audio_pdata->dpaudio_node || - !of_device_is_available(audio_pdata->dpaudio_node)) { + endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0); + if (endpoint) { + audio_node = of_graph_get_remote_port_parent(endpoint); + of_node_put(endpoint); + } + if (!audio_node || !of_device_is_available(audio_node)) { + of_node_put(audio_node); dev_info(dev, "No audio support\n"); return 0; } - audio_pdev = platform_device_register_data(dev, "dcp-hdmi-audio", - PLATFORM_DEVID_AUTO, - audio_pdata, sizeof(*audio_pdata)); - if (IS_ERR(audio_pdev)) - return dev_err_probe(dev, PTR_ERR(audio_pdev), "registering audio device\n"); + audio_pdev = of_find_device_by_node(audio_node); + of_node_put(audio_node); + if (!audio_pdev) { + dev_info(dev, "No DP/HDMI audio device not ready\n"); + return 0; + } + dcp->audiosrv->audio_dev = audio_pdev; + + dev_link = device_link_add(&audio_pdev->dev, dev, + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME); dcp->avep = afk_init(dcp, AV_ENDPOINT, avep_ops); if (IS_ERR(dcp->avep)) From 9d6c6727d485b1619154840935297eeb3254c795 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 22 Apr 2024 19:47:22 +0200 Subject: [PATCH 0628/1020] drm: apple: audio: Avoid probe errors Now that the DP audio driver is a component of the display sub-system probe errors will bring down the whole display initialization. To prevent that the audio driver must not fail. Allow delayed sound card initialization if the DMA controller is not ready, for example because the apple-sio module is missing (at all or just in the initeramfs). In the case apple-sio is available later provide as sysfs file "probe_snd_card" to trigger initialization. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/audio.c | 147 ++++++++++++++++++++++++---------- 1 file changed, 105 insertions(+), 42 deletions(-) diff --git a/drivers/gpu/drm/apple/audio.c b/drivers/gpu/drm/apple/audio.c index ca738354333be5..06fb20dd847de4 100644 --- a/drivers/gpu/drm/apple/audio.c +++ b/drivers/gpu/drm/apple/audio.c @@ -42,6 +42,7 @@ struct dcp_audio { unsigned int open_cookie; struct mutex data_lock; + bool dcp_connected; /// dcp status keep for delayed initialization bool connected; unsigned int connection_cookie; @@ -428,19 +429,8 @@ static int dcpaud_create_pcm(struct dcp_audio *dcpaud) { struct snd_card *card = dcpaud->card; struct snd_pcm *pcm; - struct dma_chan *chan; int ret; - chan = of_dma_request_slave_channel(dcpaud->dev->of_node, "tx"); - if (IS_ERR_OR_NULL(chan)) { - if (!chan) - return -EINVAL; - - dev_err(dcpaud->dev, "can't request audio TX DMA channel: %pE\n", chan); - return PTR_ERR(chan); - } - dcpaud->chan = chan; - #define NUM_PLAYBACK 1 #define NUM_CAPTURE 0 @@ -451,7 +441,7 @@ static int dcpaud_create_pcm(struct dcp_audio *dcpaud) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &dcp_playback_ops); dcpaud->substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; snd_pcm_set_managed_buffer(dcpaud->substream, SNDRV_DMA_TYPE_DEV_IRAM, - chan->device->dev, 1024 * 1024, + dcpaud->chan->device->dev, 1024 * 1024, SIZE_MAX); pcm->nonatomic = true; @@ -461,12 +451,12 @@ static int dcpaud_create_pcm(struct dcp_audio *dcpaud) return 0; } +/* expects to be called with data_lock locked and unlocks it */ static void dcpaud_report_hotplug(struct dcp_audio *dcpaud, bool connected) { struct snd_pcm_substream *substream = dcpaud->substream; - mutex_lock(&dcpaud->data_lock); - if (dcpaud->connected == connected) { + if (!dcpaud->card || dcpaud->connected == connected) { mutex_unlock(&dcpaud->data_lock); return; } @@ -502,6 +492,53 @@ static void dcpaud_set_card_names(struct dcp_audio *dcpaud) strcpy(card->shortname, "Apple DisplayPort"); } +static int dcpaud_init_snd_card(struct dcp_audio *dcpaud) +{ + int ret; + struct dma_chan *chan; + + chan = of_dma_request_slave_channel(dcpaud->dev->of_node, "tx"); + /* squelch dma channel request errors, the driver will try again alter */ + if (!chan) { + dev_warn(dcpaud->dev, "audio TX DMA channel request failed\n"); + return 0; + } else if (IS_ERR(chan)) { + dev_warn(dcpaud->dev, "audio TX DMA channel request failed: %pE\n", chan); + return 0; + } + dcpaud->chan = chan; + + ret = snd_card_new(dcpaud->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &dcpaud->card); + if (ret) + return ret; + + dcpaud_set_card_names(dcpaud); + + ret = dcpaud_create_pcm(dcpaud); + if (ret) + goto err_free_card; + + ret = dcpaud_create_chmap_ctl(dcpaud); + if (ret) + goto err_free_card; + + ret = dcpaud_create_jack(dcpaud); + if (ret) + goto err_free_card; + + ret = snd_card_register(dcpaud->card); + if (ret) + goto err_free_card; + + return 0; +err_free_card: + dev_warn(dcpaud->dev, "Failed to initialize sound card: %d\n", ret); + snd_card_free(dcpaud->card); + dcpaud->card = NULL; + return ret; +} + #ifdef CONFIG_SND_DEBUG static void dcpaud_expose_debugfs_blob(struct dcp_audio *dcpaud, const char *name, void *base, size_t size) { @@ -520,15 +557,59 @@ static void dcpaud_expose_debugfs_blob(struct dcp_audio *dcpaud, const char *nam void dcpaud_connect(struct platform_device *pdev, bool connected) { struct dcp_audio *dcpaud = platform_get_drvdata(pdev); + + mutex_lock(&dcpaud->data_lock); + + if (!dcpaud->chan) { + int ret = dcpaud_init_snd_card(dcpaud); + if (ret) { + dcpaud->dcp_connected = connected; + mutex_unlock(&dcpaud->data_lock); + return; + } + } dcpaud_report_hotplug(dcpaud, connected); } void dcpaud_disconnect(struct platform_device *pdev) { struct dcp_audio *dcpaud = platform_get_drvdata(pdev); + + mutex_lock(&dcpaud->data_lock); + + dcpaud->dcp_connected = false; dcpaud_report_hotplug(dcpaud, false); } +static ssize_t probe_snd_card_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + bool connected = false; + struct dcp_audio *dcpaud = dev_get_drvdata(dev); + + mutex_lock(&dcpaud->data_lock); + + if (!dcpaud->chan) { + ret = dcpaud_init_snd_card(dcpaud); + if (ret) + goto out_unlock; + + connected = dcpaud->dcp_connected; + if (connected) { + dcpaud_report_hotplug(dcpaud, connected); + goto out; + } + } +out_unlock: + mutex_unlock(&dcpaud->data_lock); +out: + return count; +} + +static const DEVICE_ATTR_WO(probe_snd_card); + static int dcpaud_comp_bind(struct device *dev, struct device *main, void *data) { struct dcp_audio *dcpaud = dev_get_drvdata(dev); @@ -551,34 +632,11 @@ static int dcpaud_comp_bind(struct device *dev, struct device *main, void *data) dcp_pdev = of_find_device_by_node(dcp_node); of_node_put(dcp_node); if (!dcp_pdev) { - dev_info(dev, "No DP/HDMI audio device not ready\n"); + dev_info(dev, "No DP/HDMI audio device, dcp not ready\n"); return 0; } dcpaud->dcp_dev = &dcp_pdev->dev; - ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, - THIS_MODULE, 0, &dcpaud->card); - if (ret) - return ret; - - dcpaud_set_card_names(dcpaud); - - ret = dcpaud_create_pcm(dcpaud); - if (ret) - goto err_free_card; - - ret = dcpaud_create_chmap_ctl(dcpaud); - if (ret) - goto err_free_card; - - ret = dcpaud_create_jack(dcpaud); - if (ret) - goto err_free_card; - - ret = snd_card_register(dcpaud->card); - if (ret) - goto err_free_card; - dcpaud_expose_debugfs_blob(dcpaud, "selected_cookie", &dcpaud->selected_cookie, sizeof(dcpaud->selected_cookie)); dcpaud_expose_debugfs_blob(dcpaud, "elements", dcpaud->elements, @@ -586,11 +644,16 @@ static int dcpaud_comp_bind(struct device *dev, struct device *main, void *data) dcpaud_expose_debugfs_blob(dcpaud, "product_attrs", dcpaud->productattrs, DCPAUD_PRODUCTATTRS_MAXSIZE); - return 0; + mutex_lock(&dcpaud->data_lock); + /* ignore errors to prevent audio issues affecting the display side */ + dcpaud_init_snd_card(dcpaud); + mutex_unlock(&dcpaud->data_lock); -err_free_card: - snd_card_free(dcpaud->card); - return ret; + ret = device_create_file(dev, &dev_attr_probe_snd_card); + if (ret) + dev_info(dev, "creating force probe sysfs file failed: %d\n", ret); + + return 0; } static void dcpaud_comp_unbind(struct device *dev, struct device *main, From 3884e2bef2445b5c7c4d702e2bb9071eae641e17 Mon Sep 17 00:00:00 2001 From: Jonathan Gray Date: Sun, 21 Apr 2024 11:15:04 +1000 Subject: [PATCH 0629/1020] drm/apple: fix double words in comments Signed-off-by: Jonathan Gray --- drivers/gpu/drm/apple/afk.c | 2 +- drivers/gpu/drm/apple/dcp-internal.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/apple/afk.c b/drivers/gpu/drm/apple/afk.c index 218c28dfe84249..d3e45a6180af69 100644 --- a/drivers/gpu/drm/apple/afk.c +++ b/drivers/gpu/drm/apple/afk.c @@ -641,7 +641,7 @@ static bool afk_recv(struct apple_dcp_afkep *ep) * TODO: this is theoretically unsafe since DCP could overwrite data * after the read pointer was updated above. Do it anyway since * it avoids 2 problems in the DCP tracer: - * 1. the tracer sees replies before the the notifies from dcp + * 1. the tracer sees replies before the notifies from dcp * 2. the tracer tries to read buffers after they are unmapped. */ afk_recv_handle(ep, channel, type, hdr->data, size); diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 4c352fd7791dec..8f1a55279597b3 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -128,7 +128,7 @@ struct apple_dcp { /************* IOMFB ************************************************** * everything below is mostly used inside IOMFB but it could make * - * sense keep some of the the members in apple_dcp. * + * sense to keep some of the members in apple_dcp. * **********************************************************************/ /* clock rate request by dcp in */ @@ -212,7 +212,7 @@ struct apple_dcp { struct list_head swapped_out_fbs; struct dcp_brightness brightness; - /* Workqueue for updating the initial initial brightness */ + /* Workqueue for updating the initial brightness */ struct work_struct bl_register_wq; struct mutex bl_register_mutex; /* Workqueue for updating the brightness */ From b25ffeaa936814f73a539adaf4aab5f5d1a67dda Mon Sep 17 00:00:00 2001 From: Caspar Schutijser Date: Thu, 18 Apr 2024 22:26:58 +0100 Subject: [PATCH 0630/1020] drm: apple: backlight: release lock in error path Signed-off-by: Caspar Schutijser --- drivers/gpu/drm/apple/dcp_backlight.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp_backlight.c b/drivers/gpu/drm/apple/dcp_backlight.c index ed3b240ead8557..1397000c27935c 100644 --- a/drivers/gpu/drm/apple/dcp_backlight.c +++ b/drivers/gpu/drm/apple/dcp_backlight.c @@ -150,8 +150,10 @@ static int drm_crtc_set_brightness(struct apple_dcp *dcp) goto done; state = drm_atomic_state_alloc(crtc->dev); - if (!state) - return -ENOMEM; + if (!state) { + ret = -ENOMEM; + goto done; + } state->acquire_ctx = &ctx; crtc_state = drm_atomic_get_crtc_state(state, crtc); From c1fa8a2374f704b5ee5267ad76e51e3002063d8e Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 27 Apr 2024 17:55:25 +0200 Subject: [PATCH 0631/1020] drm: apple: Switch back to drm_atomic_helper_commit_tail_rpm() The custom commit_tail implementation stopped making after "drm/apple: Disable fake vblank IRQ machinery" which stopped calling drm_vblank_init(). Revert back to the standard helper implementation. Avoids or at least significantly reduces page flips taking approximately one frame time in kwin_wayland 6. Fixes: ("drm/apple: Switch to nonblocking commit handling") Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/apple_drv.c | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index cd628ae25b1728..1e4dbef86fef08 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -238,26 +238,6 @@ static void apple_crtc_atomic_begin(struct drm_crtc *crtc, } } -static void dcp_atomic_commit_tail(struct drm_atomic_state *old_state) -{ - struct drm_device *dev = old_state->dev; - - drm_atomic_helper_commit_modeset_disables(dev, old_state); - - drm_atomic_helper_commit_modeset_enables(dev, old_state); - - drm_atomic_helper_commit_planes(dev, old_state, - DRM_PLANE_COMMIT_ACTIVE_ONLY); - - drm_atomic_helper_fake_vblank(old_state); - - drm_atomic_helper_commit_hw_done(old_state); - - drm_atomic_helper_wait_for_flip_done(dev, old_state); - - drm_atomic_helper_cleanup_planes(dev, old_state); -} - static void apple_crtc_cleanup(struct drm_crtc *crtc) { drm_crtc_cleanup(crtc); @@ -280,7 +260,7 @@ static const struct drm_mode_config_funcs apple_mode_config_funcs = { }; static const struct drm_mode_config_helper_funcs apple_mode_config_helpers = { - .atomic_commit_tail = dcp_atomic_commit_tail, + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, }; static void appledrm_connector_cleanup(struct drm_connector *connector) From 1e2de68be097249ba2e99500a0f9b7ce7e7337f3 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 29 Apr 2024 22:50:16 +0200 Subject: [PATCH 0632/1020] fixup! drm: apple: audio: Make the DP/HDMI audio driver a full driver Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/audio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/audio.c b/drivers/gpu/drm/apple/audio.c index 06fb20dd847de4..9eb320c9fab13e 100644 --- a/drivers/gpu/drm/apple/audio.c +++ b/drivers/gpu/drm/apple/audio.c @@ -661,7 +661,8 @@ static void dcpaud_comp_unbind(struct device *dev, struct device *main, { struct dcp_audio *dcpaud = dev_get_drvdata(dev); - snd_card_free(dcpaud->card); + /* snd_card_free_when_closed() checks for NULL */ + snd_card_free_when_closed(dcpaud->card); } static const struct component_ops dcpaud_comp_ops = { From a4d6e1ee51d263718317ce651601bcddccba2fb6 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 29 Apr 2024 22:59:26 +0200 Subject: [PATCH 0633/1020] fixup! gpu: drm: apple: Add DCP audio driver Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/audio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/apple/audio.c b/drivers/gpu/drm/apple/audio.c index 9eb320c9fab13e..e0a59f7f9373b5 100644 --- a/drivers/gpu/drm/apple/audio.c +++ b/drivers/gpu/drm/apple/audio.c @@ -446,7 +446,7 @@ static int dcpaud_create_pcm(struct dcp_audio *dcpaud) pcm->nonatomic = true; pcm->private_data = dcpaud; - strcpy(pcm->name, card->shortname); + strscpy(pcm->name, card->shortname, sizeof(pcm->name)); return 0; } @@ -487,9 +487,9 @@ static void dcpaud_set_card_names(struct dcp_audio *dcpaud) { struct snd_card *card = dcpaud->card; - strcpy(card->driver, "apple_dcp"); - strcpy(card->longname, "Apple DisplayPort"); - strcpy(card->shortname, "Apple DisplayPort"); + strscpy(card->driver, "apple_dcp", sizeof(card->driver)); + strscpy(card->longname, "Apple DisplayPort", sizeof(card->longname)); + strscpy(card->shortname, "Apple DisplayPort", sizeof(card->shortname)); } static int dcpaud_init_snd_card(struct dcp_audio *dcpaud) From d6609eed066aa4e0e2ee42f21802a4e6fffb3a21 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 4 May 2024 13:09:15 +0200 Subject: [PATCH 0634/1020] drm: apple: Fix broken MemDescRelay::release_descriptor callback number Two callbacks for IOMFB::MemDescRelay seems to be dropped between 12.3 and 13.5 DCP firmware. This results in the renumbering of MemDescRelay::release_descriptor from D456 to D454. Noticed while when switching the display refresh rate to 50 Hz with a 14.5 system firmware on a M1 Max Macbook Pro. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/iomfb_v13_3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/iomfb_v13_3.c b/drivers/gpu/drm/apple/iomfb_v13_3.c index 115490fd9cc6e3..0ac869d24eb01b 100644 --- a/drivers/gpu/drm/apple/iomfb_v13_3.c +++ b/drivers/gpu/drm/apple/iomfb_v13_3.c @@ -81,7 +81,7 @@ static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = { [415] = trampoline_true, /* sr_set_property_bool */ [451] = trampoline_allocate_buffer, [452] = trampoline_map_physical, - [456] = trampoline_release_mem_desc, + [454] = trampoline_release_mem_desc, [552] = trampoline_true, /* set_property_dict_0 */ [561] = trampoline_true, /* set_property_dict */ [563] = trampoline_true, /* set_property_int */ From caab959f4f1c0c6fedede17333cdd66de13c7406 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 4 May 2024 13:23:03 +0200 Subject: [PATCH 0635/1020] drm: apple: Reduce log spam about busy command channel The most likely cause for this is an unexpected callback form which the current driver doesn't recover. Warn only once about it. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp-internal.h | 2 ++ drivers/gpu/drm/apple/iomfb.c | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 8f1a55279597b3..1c82201f6e934c 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -71,6 +71,8 @@ struct dcp_channel { /* Current depth of the call stack. Less than DCP_MAX_CALL_DEPTH */ u8 depth; + /* Already warned about busy channel */ + bool warned_busy; }; struct dcp_fb_reference { diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 9fe8487053efeb..0941ff69873235 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -482,12 +482,17 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) if (dcp_channel_busy(&dcp->ch_cmd)) { - dev_err(dcp->dev, "unexpected busy command channel\n"); + if (!dcp->ch_cmd.warned_busy) { + dev_err(dcp->dev, "unexpected busy command channel\n"); + dcp->ch_cmd.warned_busy = true; + } /* HACK: issue a delayed vblank event to avoid timeouts in * drm_atomic_helper_wait_for_vblanks(). */ schedule_work(&dcp->vblank_wq); return; + } else if (dcp->ch_cmd.warned_busy) { + dcp->ch_cmd.warned_busy = false; } switch (dcp->fw_compat) { From 5b5bd795bc50e384a153fdbefb7ec9048969173b Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sat, 4 May 2024 13:33:57 +0200 Subject: [PATCH 0636/1020] drm: apple: av: Warn only once about failed calls Reduce log spam while errors are still likely due missing state checks. --- drivers/gpu/drm/apple/av.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/apple/av.c b/drivers/gpu/drm/apple/av.c index ea80e839d8ea7e..0370a9ebe58ca1 100644 --- a/drivers/gpu/drm/apple/av.c +++ b/drivers/gpu/drm/apple/av.c @@ -59,6 +59,9 @@ struct audiosrv_data { struct work_struct start_av_service_wq; struct dcp_av_audio_cmds cmds; + + bool warned_get_elements; + bool warned_get_product_attrs; }; static void av_interface_init(struct apple_epic_service *service, const char *name, @@ -207,10 +210,12 @@ int dcp_audiosrv_get_elements(struct device *dev, void *elements, size_t maxsize elements, maxsize, &size); up_write(&asrv->srv_rwsem); - if (ret) + if (ret && asrv->warned_get_elements) { dev_err(dev, "audiosrv: error getting elements: %d\n", ret); - else + asrv->warned_get_elements = true; + } else { dev_dbg(dev, "audiosrv: got %zd bytes worth of elements\n", size); + } return ret; } @@ -228,10 +233,12 @@ int dcp_audiosrv_get_product_attrs(struct device *dev, void *attrs, size_t maxsi maxsize, &size); up_write(&asrv->srv_rwsem); - if (ret) + if (ret && asrv->warned_get_product_attrs) { dev_err(dev, "audiosrv: error getting product attributes: %d\n", ret); - else + asrv->warned_get_product_attrs = true; + } else { dev_dbg(dev, "audiosrv: got %zd bytes worth of product attributes\n", size); + } return ret; } From cee82baf91381087d5bee84a76ac0f65065b8c01 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 8 May 2024 16:55:11 +0200 Subject: [PATCH 0637/1020] drm: apple: disable HDMI audio by default Can be still enabled by adding `apple_dcp.hdmi_audio` the kernel command line. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/audio.c | 5 +++++ drivers/gpu/drm/apple/dcp.c | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/apple/audio.c b/drivers/gpu/drm/apple/audio.c index e0a59f7f9373b5..3d016fa3b11136 100644 --- a/drivers/gpu/drm/apple/audio.c +++ b/drivers/gpu/drm/apple/audio.c @@ -492,11 +492,16 @@ static void dcpaud_set_card_names(struct dcp_audio *dcpaud) strscpy(card->shortname, "Apple DisplayPort", sizeof(card->shortname)); } +extern bool hdmi_audio; + static int dcpaud_init_snd_card(struct dcp_audio *dcpaud) { int ret; struct dma_chan *chan; + if (!hdmi_audio) + return -ENODEV; + chan = of_dma_request_slave_channel(dcpaud->dev->of_node, "tx"); /* squelch dma channel request errors, the driver will try again alter */ if (!chan) { diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 84fae2ca1246cf..e7290628eafed7 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -46,9 +46,9 @@ static bool show_notch; module_param(show_notch, bool, 0644); MODULE_PARM_DESC(show_notch, "Use the full display height and shows the notch"); -static bool noaudio; -module_param(noaudio, bool, 0644); -MODULE_PARM_DESC(noaudio, "Skip audio support"); +bool hdmi_audio; +module_param(hdmi_audio, bool, 0644); +MODULE_PARM_DESC(hdmi_audio, "Enable unstable HDMI audio support"); /* HACK: moved here to avoid circular dependency between apple_drv and dcp */ void dcp_drm_crtc_vblank(struct apple_crtc *crtc) @@ -413,7 +413,7 @@ int dcp_start(struct platform_device *pdev) dev_err(dcp->dev, "Failed to start IOMFB endpoint: %d\n", ret); #if IS_ENABLED(CONFIG_DRM_APPLE_AUDIO) - if (!noaudio) { + if (hdmi_audio) { ret = avep_init(dcp); if (ret) dev_warn(dcp->dev, "Failed to start AV endpoint: %d", ret); From 4934e235763e7f8be8e01b8255190585899dd9ae Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 12 May 2024 09:39:59 +0200 Subject: [PATCH 0638/1020] drm: apple: Override drm_vblank's page flip event handling [HACK] Since we don't init/uses drm's vblank support our page flip timestamps are CLOCK_MONOTONIC timestamps during the event generation. Since compositors use the timestamp to schedule their next kms commit this is timing sensitive sop move it under the drivers control. Take the timestamp directly in the swap_complete callback. Framebuffer swaps are unfortunately not fast with DCP. Measured time from swap_submit to swap_complete is ~1.5 ms for dcp and ~2.3 ms for dcpext. This warrants further investigation. Presentation timestamps might help if delay on dcp firmware side occurs after the actual swap. In the meantime doctor the time stamps and move the page flip completion up to 1 ms earler. This fixes half rate refresh on external displays displays using dcpext. Signed-off-by: Janne Grunau --- drivers/gpu/drm/apple/dcp-internal.h | 3 + drivers/gpu/drm/apple/dcp.c | 87 ++++++++++++++++++++++++++ drivers/gpu/drm/apple/iomfb_template.c | 4 +- 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 1c82201f6e934c..1a465138104d57 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -175,6 +175,7 @@ struct apple_dcp { /* swap id of the last completed swap */ u32 last_swap_id; + ktime_t swap_start; /* Current display mode */ bool during_modeset; @@ -253,6 +254,8 @@ struct apple_dcp { int hdmi_hpd_irq; }; +void dcp_drm_crtc_page_flip(struct apple_dcp *dcp, ktime_t now); + int dcp_backlight_register(struct apple_dcp *dcp); int dcp_backlight_update(struct apple_dcp *dcp); bool dcp_has_panel(struct apple_dcp *dcp); diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index e7290628eafed7..4aaf34b2fcb85e 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -50,6 +50,76 @@ bool hdmi_audio; module_param(hdmi_audio, bool, 0644); MODULE_PARM_DESC(hdmi_audio, "Enable unstable HDMI audio support"); +/* copied and simplified from drm_vblank.c */ +static void send_vblank_event(struct drm_device *dev, + struct drm_pending_vblank_event *e, + u64 seq, ktime_t now) +{ + struct timespec64 tv; + + if (e->event.base.type != DRM_EVENT_FLIP_COMPLETE) + return; + + tv = ktime_to_timespec64(now); + e->event.vbl.sequence = seq; + /* + * e->event is a user space structure, with hardcoded unsigned + * 32-bit seconds/microseconds. This is safe as we always use + * monotonic timestamps since linux-4.15 + */ + e->event.vbl.tv_sec = tv.tv_sec; + e->event.vbl.tv_usec = tv.tv_nsec / 1000; + + /* + * Use the same timestamp for any associated fence signal to avoid + * mismatch in timestamps for vsync & fence events triggered by the + * same HW event. Frameworks like SurfaceFlinger in Android expects the + * retire-fence timestamp to match exactly with HW vsync as it uses it + * for its software vsync modeling. + */ + drm_send_event_timestamp_locked(dev, &e->base, now); +} + +/** + * dcp_crtc_send_page_flip_event - helper to send vblank event after pageflip + * + * Compensate for unknown slack between page flip and arrival of the + * swap_complete callback. Minimal observed duration on DCP with HDMI output + * was around 2.3 ms. If the fb swap was submitted closer to the expected + * swap_complete it gets a penalty of one frame duration. This is on the border + * of unreasonable considering that Apple advertises support for 240 Hz (frame + * duration of 4.167 ms). + * It is unreasonable considering kwin's kms commit scheduling. Kwin commits + * 1.5 ms + the mode's vblank time before the expected next page flip + * completion. This results in presenting at half the display's rate for HDMI + * outputs. + * This might be a difference between dcp and dcpext. + */ +static void dcp_crtc_send_page_flip_event(struct apple_crtc *crtc, + struct drm_pending_vblank_event *e, + ktime_t now, ktime_t start) +{ + struct drm_device *dev = crtc->base.dev; + u64 seq; + unsigned int pipe = drm_crtc_index(&crtc->base); + ktime_t flip; + + seq = 0; + if (start != KTIME_MIN) { + s64 delta = ktime_us_delta(now, start); + if (delta <= 500) + flip = now; + else if (delta >= 2500) + flip = ktime_sub_us(now, 1000); + else + flip = ktime_sub_us(now, (delta - 500) / 2); + } else { + flip = now; + } + e->pipe = pipe; + send_vblank_event(dev, e, seq, flip); +} + /* HACK: moved here to avoid circular dependency between apple_drv and dcp */ void dcp_drm_crtc_vblank(struct apple_crtc *crtc) { @@ -63,6 +133,23 @@ void dcp_drm_crtc_vblank(struct apple_crtc *crtc) spin_unlock_irqrestore(&crtc->base.dev->event_lock, flags); } +void dcp_drm_crtc_page_flip(struct apple_dcp *dcp, ktime_t now) +{ + unsigned long flags; + struct apple_crtc *crtc = dcp->crtc; + + spin_lock_irqsave(&crtc->base.dev->event_lock, flags); + if (crtc->event) { + if (crtc->event->event.base.type == DRM_EVENT_FLIP_COMPLETE) + dcp_crtc_send_page_flip_event(crtc, crtc->event, now, dcp->swap_start); + else + drm_crtc_send_vblank_event(&crtc->base, crtc->event); + crtc->event = NULL; + dcp->swap_start = KTIME_MIN; + } + spin_unlock_irqrestore(&crtc->base.dev->event_lock, flags); +} + void dcp_set_dimensions(struct apple_dcp *dcp) { int i; diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index fc036e79acb0ce..7f363ffcd7d489 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -120,10 +120,11 @@ static u32 dcpep_cb_zero(struct apple_dcp *dcp) static void dcpep_cb_swap_complete(struct apple_dcp *dcp, struct DCP_FW_NAME(dc_swap_complete_resp) *resp) { + ktime_t now = ktime_get(); trace_iomfb_swap_complete(dcp, resp->swap_id); dcp->last_swap_id = resp->swap_id; - dcp_drm_crtc_vblank(dcp->crtc); + dcp_drm_crtc_page_flip(dcp, now); } /* special */ @@ -1124,6 +1125,7 @@ static void dcp_swapped(struct apple_dcp *dcp, void *data, void *cookie) dcp_drm_crtc_vblank(dcp->crtc); return; } + dcp->swap_start = ktime_get(); while (!list_empty(&dcp->swapped_out_fbs)) { struct dcp_fb_reference *entry; From 27c863f5254be19aad9abbcfc71e895269a0ebc5 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 5 Mar 2023 23:40:22 +0900 Subject: [PATCH 0639/1020] rust: helpers: Fix spinlock helper for various spinlock modes Signed-off-by: Hector Martin --- rust/helpers.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rust/helpers.c b/rust/helpers.c index 70e59efd92bc43..a22bf3e6b8eeba 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -48,7 +48,12 @@ void rust_helper___spin_lock_init(spinlock_t *lock, const char *name, struct lock_class_key *key) { #ifdef CONFIG_DEBUG_SPINLOCK +# ifndef CONFIG_PREEMPT_RT __raw_spin_lock_init(spinlock_check(lock), name, key, LD_WAIT_CONFIG); +# else + rt_mutex_base_init(&lock->lock); + __rt_spin_lock_init(lock, name, key, false); +# endif #else spin_lock_init(lock); #endif From 92bceb9c6a3b987f35c4ba654c97feb61330c32e Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Fri, 28 Apr 2023 20:11:26 +0900 Subject: [PATCH 0640/1020] rust: Enable type_alias_impl_trait This is required to make PinInit work as a return value from a trait function. We indirect via an associated type to avoid return_position_impl_trait_in_trait, which is an incomplete feature. Signed-off-by: Asahi Lina --- rust/kernel/lib.rs | 1 + scripts/Makefile.build | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 6858e2f8a3ed97..338fee153d2753 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -18,6 +18,7 @@ #![feature(new_uninit)] #![feature(offset_of)] #![feature(receiver_trait)] +#![feature(type_alias_impl_trait)] #![feature(unsize)] // Ensure conditional compilation based on the kernel configuration works; diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 533a7799fdfe6d..04e9d38813b459 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -263,7 +263,7 @@ $(obj)/%.lst: $(src)/%.c FORCE # Compile Rust sources (.rs) # --------------------------------------------------------------------------- -rust_allowed_features := new_uninit,offset_of +rust_allowed_features := new_uninit,offset_of,type_alias_impl_trait # `--out-dir` is required to avoid temporaries being created by `rustc` in the # current working directory, which may be not accessible in the out-of-tree From 454abe0e41c6d6c4bbe4dfb16873ce35cdface19 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Fri, 28 Apr 2023 20:12:35 +0900 Subject: [PATCH 0641/1020] rust: kernel: lock: Add Lock::pin_init() This allows initializing a lock using pin_init!(), instead of requiring the inner data to be passed through the stack. Signed-off-by: Asahi Lina --- rust/kernel/sync/lock.rs | 30 +++++++++++++++++++++++++++++- rust/kernel/sync/lock/mutex.rs | 13 +++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs index 5b5c8efe427ae2..9dd41f941435a7 100644 --- a/rust/kernel/sync/lock.rs +++ b/rust/kernel/sync/lock.rs @@ -6,7 +6,9 @@ //! spinlocks, raw spinlocks) to be provided with minimal effort. use super::LockClassKey; -use crate::{bindings, init::PinInit, pin_init, str::CStr, types::Opaque, types::ScopeGuard}; +use crate::{ + bindings, init::PinInit, pin_init, str::CStr, try_pin_init, types::Opaque, types::ScopeGuard, +}; use core::{cell::UnsafeCell, marker::PhantomData, marker::PhantomPinned}; use macros::pin_data; @@ -94,6 +96,7 @@ pub struct Lock { _pin: PhantomPinned, /// The data protected by the lock. + #[pin] pub(crate) data: UnsafeCell, } @@ -117,6 +120,31 @@ impl Lock { }), }) } + + /// Constructs a new lock initialiser taking an initialiser. + pub fn pin_init( + t: impl PinInit, + name: &'static CStr, + key: &'static LockClassKey, + ) -> impl PinInit + where + E: core::convert::From, + { + try_pin_init!(Self { + // SAFETY: We are just forwarding the initialization across a + // cast away from UnsafeCell, so the pin_init_from_closure and + // __pinned_init() requirements are in sync. + data <- unsafe { crate::init::pin_init_from_closure(move |slot: *mut UnsafeCell| { + t.__pinned_init(slot as *mut T) + })}, + _pin: PhantomPinned, + // SAFETY: `slot` is valid while the closure is called and both `name` and `key` have + // static lifetimes so they live indefinitely. + state <- Opaque::ffi_init(|slot| unsafe { + B::init(slot, name.as_char_ptr(), key.as_ptr()) + }), + }? E) + } } impl Lock { diff --git a/rust/kernel/sync/lock/mutex.rs b/rust/kernel/sync/lock/mutex.rs index ef4c4634d294ee..9a85a156db9a1f 100644 --- a/rust/kernel/sync/lock/mutex.rs +++ b/rust/kernel/sync/lock/mutex.rs @@ -19,6 +19,19 @@ macro_rules! new_mutex { } pub use new_mutex; +/// Creates a [`Mutex`] initialiser with the given name and a newly-created lock class, +/// given an initialiser for the inner type. +/// +/// It uses the name if one is given, otherwise it generates one based on the file name and line +/// number. +#[macro_export] +macro_rules! new_mutex_pinned { + ($inner:expr $(, $name:literal)? $(,)?) => { + $crate::sync::Mutex::pin_init( + $inner, $crate::optional_name!($($name)?), $crate::static_lock_class!()) + }; +} + /// A mutual exclusion primitive. /// /// Exposes the kernel's [`struct mutex`]. When multiple threads attempt to lock the same mutex, From ad8a0b70212905b41bd0898379498ff48bbcbab3 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Fri, 19 May 2023 15:24:04 +0900 Subject: [PATCH 0642/1020] rust: init: Implement Zeroable::zeroed() By analogy to Default::default(), this just returns the zeroed representation of the type directly. init::zeroed() is the version that returns an initializer. Signed-off-by: Asahi Lina --- rust/kernel/init.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs index 09004b56fb65c7..9ac57967b2c4a1 100644 --- a/rust/kernel/init.rs +++ b/rust/kernel/init.rs @@ -1261,7 +1261,14 @@ pub unsafe trait PinnedDrop: __internal::HasPinData { /// ```rust,ignore /// let val: Self = unsafe { core::mem::zeroed() }; /// ``` -pub unsafe trait Zeroable {} +pub unsafe trait Zeroable: core::marker::Sized { + /// Create a new zeroed T. + /// + /// Directly returns a zeroed T, analogous to Default::default(). + fn zeroed() -> Self { + unsafe { core::mem::zeroed() } + } +} /// Create a new zeroed T. /// From 0ee8cfe6676c76fc91012855dad733701c5637f2 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Fri, 19 May 2023 16:22:09 +0900 Subject: [PATCH 0643/1020] rust: init: Support type paths in pin_init!() and friends This makes directly initializing structures with a type name that isn't in the current scope work properly. Signed-off-by: Asahi Lina --- rust/kernel/init.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs index 9ac57967b2c4a1..59b270409068ff 100644 --- a/rust/kernel/init.rs +++ b/rust/kernel/init.rs @@ -554,12 +554,12 @@ macro_rules! stack_try_pin_init { // module `__internal` inside of `init/__internal.rs`. #[macro_export] macro_rules! pin_init { - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { + ($(&$this:ident in)? $t:ident $(::$p:ident)* $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) => { $crate::__init_internal!( @this($($this)?), - @typ($t $(::<$($generics),*>)?), + @typ($t $(::$p)* $(::<$($generics),*>)?), @fields($($fields)*), @error(::core::convert::Infallible), @data(PinData, use_data), @@ -611,12 +611,12 @@ macro_rules! pin_init { // module `__internal` inside of `init/__internal.rs`. #[macro_export] macro_rules! try_pin_init { - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { + ($(&$this:ident in)? $t:ident $(::$p:ident)* $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) => { $crate::__init_internal!( @this($($this)?), - @typ($t $(::<$($generics),*>)? ), + @typ($t $(::$p)* $(::<$($generics),*>)? ), @fields($($fields)*), @error($crate::error::Error), @data(PinData, use_data), @@ -625,12 +625,12 @@ macro_rules! try_pin_init { @munch_fields($($fields)*), ) }; - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { + ($(&$this:ident in)? $t:ident $(::$p:ident)* $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }? $err:ty) => { $crate::__init_internal!( @this($($this)?), - @typ($t $(::<$($generics),*>)? ), + @typ($t $(::$p)* $(::<$($generics),*>)? ), @fields($($fields)*), @error($err), @data(PinData, use_data), @@ -660,12 +660,12 @@ macro_rules! try_pin_init { // module `__internal` inside of `init/__internal.rs`. #[macro_export] macro_rules! init { - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { + ($(&$this:ident in)? $t:ident $(::$p:ident)* $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) => { $crate::__init_internal!( @this($($this)?), - @typ($t $(::<$($generics),*>)?), + @typ($t $(::$p)* $(::<$($generics),*>)?), @fields($($fields)*), @error(::core::convert::Infallible), @data(InitData, /*no use_data*/), @@ -711,12 +711,12 @@ macro_rules! init { // module `__internal` inside of `init/__internal.rs`. #[macro_export] macro_rules! try_init { - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { + ($(&$this:ident in)? $t:ident $(::$p:ident)* $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) => { $crate::__init_internal!( @this($($this)?), - @typ($t $(::<$($generics),*>)?), + @typ($t $(::$p)* $(::<$($generics),*>)?), @fields($($fields)*), @error($crate::error::Error), @data(InitData, /*no use_data*/), @@ -725,12 +725,12 @@ macro_rules! try_init { @munch_fields($($fields)*), ) }; - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { + ($(&$this:ident in)? $t:ident $(::$p:ident)* $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }? $err:ty) => { $crate::__init_internal!( @this($($this)?), - @typ($t $(::<$($generics),*>)?), + @typ($t $(::$p)* $(::<$($generics),*>)?), @fields($($fields)*), @error($err), @data(InitData, /*no use_data*/), From 681825b9d388c3ee2e94d851560bf9831095c51b Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Sat, 27 May 2023 22:30:51 +0900 Subject: [PATCH 0644/1020] rust: types: Add Opaque::zeroed() Signed-off-by: Asahi Lina --- rust/kernel/types.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index aa77bad9bce486..42b7dccfcf6dd8 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -250,6 +250,14 @@ impl Opaque { } } + /// Creates a zeroed value. + pub fn zeroed() -> Self { + Self { + value: UnsafeCell::new(MaybeUninit::zeroed()), + _pin: PhantomPinned, + } + } + /// Creates a pin-initializer from the given initializer closure. /// /// The returned initializer calls the given closure with the pointer to the inner `T` of this From a6393d4d75fe0fb7b8e2941577879e79ad81670f Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Sat, 27 May 2023 20:13:42 +0900 Subject: [PATCH 0645/1020] rust: Use absolute paths to build Rust objects We want to use caller_location to uniquely identify callsites, to automatically create lockdep classes without macros. The location filename in local code uses the relative path passed to the compiler, but if that code is generic and instantiated from another crate, the path becomes absolute. To make this work and keep the paths consistent, always pass an absolute path to the compiler. Then the Location path is always identical regardless of how the code is being compiled. Signed-off-by: Asahi Lina --- rust/Makefile | 2 +- scripts/Makefile.build | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rust/Makefile b/rust/Makefile index 86a125c4243cb2..4e21711dddf0ee 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -406,7 +406,7 @@ quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L --emit=dep-info=$(depfile) --emit=obj=$@ \ --emit=metadata=$(dir $@)$(patsubst %.o,lib%.rmeta,$(notdir $@)) \ --crate-type rlib -L$(objtree)/$(obj) \ - --crate-name $(patsubst %.o,%,$(notdir $@)) $< \ + --crate-name $(patsubst %.o,%,$(notdir $@)) $(abspath $<) \ --sysroot=/dev/null \ $(if $(rustc_objcopy),;$(OBJCOPY) $(rustc_objcopy) $@) diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 04e9d38813b459..2183b3bb2d1b19 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -288,27 +288,27 @@ rust_common_cmd = \ # would not match each other. quiet_cmd_rustc_o_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@ - cmd_rustc_o_rs = $(rust_common_cmd) --emit=obj=$@ $< + cmd_rustc_o_rs = $(rust_common_cmd) --emit=obj=$@ $(abspath $<) $(obj)/%.o: $(src)/%.rs FORCE +$(call if_changed_dep,rustc_o_rs) quiet_cmd_rustc_rsi_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@ cmd_rustc_rsi_rs = \ - $(rust_common_cmd) -Zunpretty=expanded $< >$@; \ + $(rust_common_cmd) -Zunpretty=expanded $(abspath $<) >$@; \ command -v $(RUSTFMT) >/dev/null && $(RUSTFMT) $@ $(obj)/%.rsi: $(src)/%.rs FORCE +$(call if_changed_dep,rustc_rsi_rs) quiet_cmd_rustc_s_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@ - cmd_rustc_s_rs = $(rust_common_cmd) --emit=asm=$@ $< + cmd_rustc_s_rs = $(rust_common_cmd) --emit=asm=$@ $(abspath $<) $(obj)/%.s: $(src)/%.rs FORCE +$(call if_changed_dep,rustc_s_rs) quiet_cmd_rustc_ll_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@ - cmd_rustc_ll_rs = $(rust_common_cmd) --emit=llvm-ir=$@ $< + cmd_rustc_ll_rs = $(rust_common_cmd) --emit=llvm-ir=$@ $(abspath $<) $(obj)/%.ll: $(src)/%.rs FORCE +$(call if_changed_dep,rustc_ll_rs) From bc5e138e99717b933e567ca9ae057ead498878cf Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 11 Apr 2024 09:51:20 +0900 Subject: [PATCH 0646/1020] prctl: Introduce PR_{SET,GET}_MEM_MODEL On some architectures, it is possible to query and/or change the CPU memory model. This allows userspace to switch to a stricter memory model for performance reasons, such as when emulating code for another architecture where that model is the default. Introduce two prctls to allow userspace to query and set the memory model for a thread. Two models are initially defined: - PR_SET_MEM_MODEL_DEFAULT requests the default memory model for the architecture. - PR_SET_MEM_MODEL_TSO requests the x86 TSO memory model. PR_SET_MEM_MODEL is allowed to set a stricter memory model than requested if available, in which case it will return successfully. If the requested memory model cannot be fulfilled, it will return an error. The memory model that was actually set can be queried by a subsequent call to PR_GET_MEM_MODEL. Examples: - On a CPU with not support for a memory model at least as strong as TSO, PR_SET_MEM_MODEL(PR_SET_MEM_MODEL_TSO) fails. - On a CPU with runtime-configurable TSO support, PR_SET_MEM_MODEL can toggle the memory model between DEFAULT and TSO at will. - On a CPU where the only memory model is at least as strict as TSO, PR_GET_MEM_MODEL will return PR_SET_MEM_MODEL_DEFAULT, and PR_SET_MEM_MODEL(PR_SET_MEM_MODEL_TSO) will return success but leave the memory model at PR_SET_MEM_MODEL_DEFAULT. This implies that the default is in fact at least as strict as TSO. Signed-off-by: Hector Martin Reviewed-by: Neal Gompa --- include/linux/memory_ordering_model.h | 11 +++++++++++ include/uapi/linux/prctl.h | 5 +++++ kernel/sys.c | 21 +++++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 include/linux/memory_ordering_model.h diff --git a/include/linux/memory_ordering_model.h b/include/linux/memory_ordering_model.h new file mode 100644 index 00000000000000..267a12ca66307e --- /dev/null +++ b/include/linux/memory_ordering_model.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_MEMORY_ORDERING_MODEL_H +#define __ASM_MEMORY_ORDERING_MODEL_H + +/* Arch hooks to implement the PR_{GET_SET}_MEM_MODEL prctls */ + +struct task_struct; +int arch_prctl_mem_model_get(struct task_struct *t); +int arch_prctl_mem_model_set(struct task_struct *t, unsigned long val); + +#endif diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h index 370ed14b1ae092..961216093f11ab 100644 --- a/include/uapi/linux/prctl.h +++ b/include/uapi/linux/prctl.h @@ -306,4 +306,9 @@ struct prctl_mm_map { # define PR_RISCV_V_VSTATE_CTRL_NEXT_MASK 0xc # define PR_RISCV_V_VSTATE_CTRL_MASK 0x1f +#define PR_GET_MEM_MODEL 0x6d4d444c +#define PR_SET_MEM_MODEL 0x4d4d444c +# define PR_SET_MEM_MODEL_DEFAULT 0 +# define PR_SET_MEM_MODEL_TSO 1 + #endif /* _LINUX_PRCTL_H */ diff --git a/kernel/sys.c b/kernel/sys.c index 8bb106a56b3a5f..0cf327d7c1002a 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -2445,6 +2446,16 @@ static int prctl_get_auxv(void __user *addr, unsigned long len) return sizeof(mm->saved_auxv); } +int __weak arch_prctl_mem_model_get(struct task_struct *t) +{ + return -EINVAL; +} + +int __weak arch_prctl_mem_model_set(struct task_struct *t, unsigned long val) +{ + return -EINVAL; +} + SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, unsigned long, arg4, unsigned long, arg5) { @@ -2760,6 +2771,16 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, case PR_RISCV_V_GET_CONTROL: error = RISCV_V_GET_CONTROL(); break; + case PR_GET_MEM_MODEL: + if (arg2 || arg3 || arg4 || arg5) + return -EINVAL; + error = arch_prctl_mem_model_get(me); + break; + case PR_SET_MEM_MODEL: + if (arg3 || arg4 || arg5) + return -EINVAL; + error = arch_prctl_mem_model_set(me, arg2); + break; default: error = -EINVAL; break; From f1eb4fc271aec23a9714b5694ced7308828ebefe Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 11 Apr 2024 09:51:21 +0900 Subject: [PATCH 0647/1020] arm64: Implement PR_{GET,SET}_MEM_MODEL for always-TSO CPUs Some ARM64 implementations are known to always use the TSO memory model. Add trivial support for the PR_{GET,SET}_MEM_MODEL prctl, which allows userspace to learn this fact. Known TSO implementations: - Nvidia Denver - Nvidia Carmel - Fujitsu A64FX Signed-off-by: Hector Martin Reviewed-by: Neal Gompa --- arch/arm64/Kconfig | 9 +++++++ arch/arm64/include/asm/cpufeature.h | 4 +++ arch/arm64/kernel/Makefile | 3 ++- arch/arm64/kernel/cpufeature.c | 11 ++++---- arch/arm64/kernel/cpufeature_impdef.c | 38 +++++++++++++++++++++++++++ arch/arm64/kernel/process.c | 24 +++++++++++++++++ arch/arm64/tools/cpucaps | 1 + 7 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 arch/arm64/kernel/cpufeature_impdef.c diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 7b11c98b3e84bf..f8e66fe44ff408 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -2162,6 +2162,15 @@ config ARM64_DEBUG_PRIORITY_MASKING If unsure, say N endif # ARM64_PSEUDO_NMI +config ARM64_MEMORY_MODEL_CONTROL + bool "Runtime memory model control" + help + Some ARM64 CPUs support runtime switching of the CPU memory + model, which can be useful to emulate other CPU architectures + which have different memory models. Say Y to enable support + for the PR_SET_MEM_MODEL/PR_GET_MEM_MODEL prctl() calls on + CPUs with this feature. + config RELOCATABLE bool "Build a relocatable kernel image" if EXPERT select ARCH_HAS_RELR diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index 8b904a757bd341..fb215b0e7529dd 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -1032,6 +1032,10 @@ static inline bool cpu_has_lpa2(void) #endif } +void __init init_cpucap_indirect_list_impdef(void); +void __init init_cpucap_indirect_list_from_array(const struct arm64_cpu_capabilities *caps); +bool cpufeature_matches(u64 reg, const struct arm64_cpu_capabilities *entry); + #endif /* __ASSEMBLY__ */ #endif diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 763824963ed157..f0471d0ab85355 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -33,7 +33,8 @@ obj-y := debug-monitors.o entry.o irq.o fpsimd.o \ return_address.o cpuinfo.o cpu_errata.o \ cpufeature.o alternative.o cacheinfo.o \ smp.o smp_spin_table.o topology.o smccc-call.o \ - syscall.o proton-pack.o idle.o patching.o pi/ + syscall.o proton-pack.o idle.o patching.o \ + cpufeature_impdef.o pi/ obj-$(CONFIG_COMPAT) += sys32.o signal32.o \ sys_compat.o diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 56583677c1f294..e39ab93ad68355 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -1028,7 +1028,7 @@ static void init_cpu_ftr_reg(u32 sys_reg, u64 new) extern const struct arm64_cpu_capabilities arm64_errata[]; static const struct arm64_cpu_capabilities arm64_features[]; -static void __init +void __init init_cpucap_indirect_list_from_array(const struct arm64_cpu_capabilities *caps) { for (; caps->matches; caps++) { @@ -1540,8 +1540,8 @@ has_always(const struct arm64_cpu_capabilities *entry, int scope) return true; } -static bool -feature_matches(u64 reg, const struct arm64_cpu_capabilities *entry) +bool +cpufeature_matches(u64 reg, const struct arm64_cpu_capabilities *entry) { int val, min, max; u64 tmp; @@ -1594,14 +1594,14 @@ has_user_cpuid_feature(const struct arm64_cpu_capabilities *entry, int scope) if (!mask) return false; - return feature_matches(val, entry); + return cpufeature_matches(val, entry); } static bool has_cpuid_feature(const struct arm64_cpu_capabilities *entry, int scope) { u64 val = read_scoped_sysreg(entry, scope); - return feature_matches(val, entry); + return cpufeature_matches(val, entry); } const struct cpumask *system_32bit_el0_cpumask(void) @@ -3486,6 +3486,7 @@ void __init setup_boot_cpu_features(void) * handle the boot CPU. */ init_cpucap_indirect_list(); + init_cpucap_indirect_list_impdef(); /* * Detect broken pseudo-NMI. Must be called _before_ the call to diff --git a/arch/arm64/kernel/cpufeature_impdef.c b/arch/arm64/kernel/cpufeature_impdef.c new file mode 100644 index 00000000000000..bb04a8e3d79d85 --- /dev/null +++ b/arch/arm64/kernel/cpufeature_impdef.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Contains implementation-defined CPU feature definitions. + */ + +#include + +#ifdef CONFIG_ARM64_MEMORY_MODEL_CONTROL +static bool has_tso_fixed(const struct arm64_cpu_capabilities *entry, int scope) +{ + /* List of CPUs that always use the TSO memory model */ + static const struct midr_range fixed_tso_list[] = { + MIDR_ALL_VERSIONS(MIDR_NVIDIA_DENVER), + MIDR_ALL_VERSIONS(MIDR_NVIDIA_CARMEL), + MIDR_ALL_VERSIONS(MIDR_FUJITSU_A64FX), + { /* sentinel */ } + }; + + return is_midr_in_range_list(read_cpuid_id(), fixed_tso_list); +} +#endif + +static const struct arm64_cpu_capabilities arm64_impdef_features[] = { +#ifdef CONFIG_ARM64_MEMORY_MODEL_CONTROL + { + .desc = "TSO memory model (Fixed)", + .capability = ARM64_HAS_TSO_FIXED, + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .matches = has_tso_fixed, + }, +#endif + {}, +}; + +void __init init_cpucap_indirect_list_impdef(void) +{ + init_cpucap_indirect_list_from_array(arm64_impdef_features); +} diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 4ae31b7af6c311..7920056bad3ed1 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -513,6 +514,25 @@ void update_sctlr_el1(u64 sctlr) isb(); } +#ifdef CONFIG_ARM64_MEMORY_MODEL_CONTROL +int arch_prctl_mem_model_get(struct task_struct *t) +{ + return PR_SET_MEM_MODEL_DEFAULT; +} + +int arch_prctl_mem_model_set(struct task_struct *t, unsigned long val) +{ + if (alternative_has_cap_unlikely(ARM64_HAS_TSO_FIXED) && + val == PR_SET_MEM_MODEL_TSO) + return 0; + + if (val == PR_SET_MEM_MODEL_DEFAULT) + return 0; + + return -EINVAL; +} +#endif + /* * Thread switching. */ @@ -651,6 +671,10 @@ void arch_setup_new_exec(void) arch_prctl_spec_ctrl_set(current, PR_SPEC_STORE_BYPASS, PR_SPEC_ENABLE); } + +#ifdef CONFIG_ARM64_MEMORY_MODEL_CONTROL + arch_prctl_mem_model_set(current, PR_SET_MEM_MODEL_DEFAULT); +#endif } #ifdef CONFIG_ARM64_TAGGED_ADDR_ABI diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps index 62b2838a231ada..daa6b94954028d 100644 --- a/arch/arm64/tools/cpucaps +++ b/arch/arm64/tools/cpucaps @@ -52,6 +52,7 @@ HAS_STAGE2_FWB HAS_TCR2 HAS_TIDCP1 HAS_TLB_RANGE +HAS_TSO_FIXED HAS_VA52 HAS_VIRT_HOST_EXTN HAS_WFXT From 8bde8f667f979d69507793af007103500659a0ea Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 11 Apr 2024 09:51:22 +0900 Subject: [PATCH 0648/1020] arm64: Introduce scaffolding to add ACTLR_EL1 to thread state Some CPUs expose IMPDEF features in ACTLR_EL1 that can be meaningfully controlled per-thread (like TSO control on Apple cores). Add the basic scaffolding to save/restore this register as part of context switching. This mechanism is disabled by default both by config symbol and via a runtime check, which ensures it is never triggered unless the system is known to need it for some feature (which also implies that the layout of ACTLR_EL1 is uniform between all CPU core types). Signed-off-by: Hector Martin Reviewed-by: Neal Gompa --- arch/arm64/Kconfig | 3 +++ arch/arm64/include/asm/cpufeature.h | 5 +++++ arch/arm64/include/asm/processor.h | 3 +++ arch/arm64/kernel/process.c | 25 +++++++++++++++++++++++++ arch/arm64/kernel/setup.c | 8 ++++++++ 5 files changed, 44 insertions(+) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index f8e66fe44ff408..9b3593b34ccecf 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -408,6 +408,9 @@ config KASAN_SHADOW_OFFSET config UNWIND_TABLES bool +config ARM64_ACTLR_STATE + bool + source "arch/arm64/Kconfig.platforms" menu "Kernel Features" diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index fb215b0e7529dd..46ab37f8f4d882 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -909,6 +909,11 @@ static inline unsigned int get_vmid_bits(u64 mmfr1) return 8; } +static __always_inline bool system_has_actlr_state(void) +{ + return false; +} + s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new, s64 cur); struct arm64_ftr_reg *get_arm64_ftr_reg(u32 sys_id); diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h index f77371232d8c6d..d43c5791a35e22 100644 --- a/arch/arm64/include/asm/processor.h +++ b/arch/arm64/include/asm/processor.h @@ -184,6 +184,9 @@ struct thread_struct { u64 sctlr_user; u64 svcr; u64 tpidr2_el0; +#ifdef CONFIG_ARM64_ACTLR_STATE + u64 actlr; +#endif }; static inline unsigned int thread_get_vl(struct thread_struct *thread, diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 7920056bad3ed1..117f80e16aac87 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -372,6 +372,11 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args) if (system_supports_tpidr2()) p->thread.tpidr2_el0 = read_sysreg_s(SYS_TPIDR2_EL0); +#ifdef CONFIG_ARM64_ACTLR_STATE + if (system_has_actlr_state()) + p->thread.actlr = read_sysreg(actlr_el1); +#endif + if (stack_start) { if (is_compat_thread(task_thread_info(p))) childregs->compat_sp = stack_start; @@ -533,6 +538,25 @@ int arch_prctl_mem_model_set(struct task_struct *t, unsigned long val) } #endif +#ifdef CONFIG_ARM64_ACTLR_STATE +/* + * IMPDEF control register ACTLR_EL1 handling. Some CPUs use this to + * expose features that can be controlled by userspace. + */ +static void actlr_thread_switch(struct task_struct *next) +{ + if (!system_has_actlr_state()) + return; + + current->thread.actlr = read_sysreg(actlr_el1); + write_sysreg(next->thread.actlr, actlr_el1); +} +#else +static inline void actlr_thread_switch(struct task_struct *next) +{ +} +#endif + /* * Thread switching. */ @@ -550,6 +574,7 @@ struct task_struct *__switch_to(struct task_struct *prev, ssbs_thread_switch(next); erratum_1418040_thread_switch(next); ptrauth_thread_switch_user(next); + actlr_thread_switch(next); /* * Complete any pending TLB or cache maintenance on this CPU in case diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 65a052bf741f04..35342f633a8508 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -359,6 +359,14 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p) */ init_task.thread_info.ttbr0 = phys_to_ttbr(__pa_symbol(reserved_pg_dir)); #endif +#ifdef CONFIG_ARM64_ACTLR_STATE + /* Store the boot CPU ACTLR_EL1 value as the default. This will only + * be actually restored during context switching iff the platform is + * known to use ACTLR_EL1 for exposable features and its layout is + * known to be the same on all CPUs. + */ + init_task.thread.actlr = read_sysreg(actlr_el1); +#endif if (boot_args[1] || boot_args[2] || boot_args[3]) { pr_err("WARNING: x1-x3 nonzero in violation of boot protocol:\n" From c1c120a4fcefa339591770e05eb471cc12efc793 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 11 Apr 2024 09:51:23 +0900 Subject: [PATCH 0649/1020] arm64: Implement Apple IMPDEF TSO memory model control Apple CPUs may implement the TSO memory model as an optional configurable mode. This allows x86 emulators to simplify their load/store handling, greatly increasing performance. Expose this via the prctl PR_SET_MEM_MODEL_TSO mechanism. We use the Apple IMPDEF AIDR_EL1 register to check for the availability of TSO mode, and enable this codepath on all CPUs with an Apple implementer. This relies on the ACTLR_EL1 thread state scaffolding introduced earlier. Signed-off-by: Hector Martin Reviewed-by: Neal Gompa --- arch/arm64/Kconfig | 2 ++ arch/arm64/include/asm/apple_cpufeature.h | 15 +++++++++++++++ arch/arm64/include/asm/cpufeature.h | 3 ++- arch/arm64/kernel/cpufeature_impdef.c | 23 +++++++++++++++++++++++ arch/arm64/kernel/process.c | 22 ++++++++++++++++++++++ arch/arm64/tools/cpucaps | 1 + 6 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 arch/arm64/include/asm/apple_cpufeature.h diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 9b3593b34ccecf..2f3eedd955c94b 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -2167,6 +2167,8 @@ endif # ARM64_PSEUDO_NMI config ARM64_MEMORY_MODEL_CONTROL bool "Runtime memory model control" + default ARCH_APPLE + select ARM64_ACTLR_STATE help Some ARM64 CPUs support runtime switching of the CPU memory model, which can be useful to emulate other CPU architectures diff --git a/arch/arm64/include/asm/apple_cpufeature.h b/arch/arm64/include/asm/apple_cpufeature.h new file mode 100644 index 00000000000000..4370d91ffa3ec9 --- /dev/null +++ b/arch/arm64/include/asm/apple_cpufeature.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 + +#ifndef __ASM_APPLE_CPUFEATURES_H +#define __ASM_APPLE_CPUFEATURES_H + +#include +#include + +#define AIDR_APPLE_TSO_SHIFT 9 +#define AIDR_APPLE_TSO BIT(9) + +#define ACTLR_APPLE_TSO_SHIFT 1 +#define ACTLR_APPLE_TSO BIT(1) + +#endif diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index 46ab37f8f4d882..a191000d88c285 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -911,7 +911,8 @@ static inline unsigned int get_vmid_bits(u64 mmfr1) static __always_inline bool system_has_actlr_state(void) { - return false; + return IS_ENABLED(CONFIG_ARM64_ACTLR_STATE) && + alternative_has_cap_unlikely(ARM64_HAS_TSO_APPLE); } s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new, s64 cur); diff --git a/arch/arm64/kernel/cpufeature_impdef.c b/arch/arm64/kernel/cpufeature_impdef.c index bb04a8e3d79d85..9325d1eb12f402 100644 --- a/arch/arm64/kernel/cpufeature_impdef.c +++ b/arch/arm64/kernel/cpufeature_impdef.c @@ -4,8 +4,21 @@ */ #include +#include #ifdef CONFIG_ARM64_MEMORY_MODEL_CONTROL +static bool has_apple_feature(const struct arm64_cpu_capabilities *entry, int scope) +{ + u64 val; + WARN_ON(scope != SCOPE_SYSTEM); + + if (read_cpuid_implementor() != ARM_CPU_IMP_APPLE) + return false; + + val = read_sysreg(aidr_el1); + return cpufeature_matches(val, entry); +} + static bool has_tso_fixed(const struct arm64_cpu_capabilities *entry, int scope) { /* List of CPUs that always use the TSO memory model */ @@ -22,6 +35,16 @@ static bool has_tso_fixed(const struct arm64_cpu_capabilities *entry, int scope) static const struct arm64_cpu_capabilities arm64_impdef_features[] = { #ifdef CONFIG_ARM64_MEMORY_MODEL_CONTROL + { + .desc = "TSO memory model (Apple)", + .capability = ARM64_HAS_TSO_APPLE, + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .matches = has_apple_feature, + .field_pos = AIDR_APPLE_TSO_SHIFT, + .field_width = 1, + .sign = FTR_UNSIGNED, + .min_field_value = 1, + }, { .desc = "TSO memory model (Fixed)", .capability = ARM64_HAS_TSO_FIXED, diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 117f80e16aac87..34a19ecfb630ba 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -44,6 +44,7 @@ #include #include +#include #include #include #include @@ -522,6 +523,10 @@ void update_sctlr_el1(u64 sctlr) #ifdef CONFIG_ARM64_MEMORY_MODEL_CONTROL int arch_prctl_mem_model_get(struct task_struct *t) { + if (alternative_has_cap_unlikely(ARM64_HAS_TSO_APPLE) && + t->thread.actlr & ACTLR_APPLE_TSO) + return PR_SET_MEM_MODEL_TSO; + return PR_SET_MEM_MODEL_DEFAULT; } @@ -531,6 +536,23 @@ int arch_prctl_mem_model_set(struct task_struct *t, unsigned long val) val == PR_SET_MEM_MODEL_TSO) return 0; + if (alternative_has_cap_unlikely(ARM64_HAS_TSO_APPLE)) { + WARN_ON(!system_has_actlr_state()); + + switch (val) { + case PR_SET_MEM_MODEL_TSO: + t->thread.actlr |= ACTLR_APPLE_TSO; + break; + case PR_SET_MEM_MODEL_DEFAULT: + t->thread.actlr &= ~ACTLR_APPLE_TSO; + break; + default: + return -EINVAL; + } + write_sysreg(t->thread.actlr, actlr_el1); + return 0; + } + if (val == PR_SET_MEM_MODEL_DEFAULT) return 0; diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps index daa6b94954028d..62f9ca9ce44b41 100644 --- a/arch/arm64/tools/cpucaps +++ b/arch/arm64/tools/cpucaps @@ -52,6 +52,7 @@ HAS_STAGE2_FWB HAS_TCR2 HAS_TIDCP1 HAS_TLB_RANGE +HAS_TSO_APPLE HAS_TSO_FIXED HAS_VA52 HAS_VIRT_HOST_EXTN From c49dd66a7c9240806d4cf0b63b9caed24a99f33a Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Tue, 18 Apr 2023 22:59:53 +0300 Subject: [PATCH 0650/1020] gpu: drm: adp: Add Apple Display Pipe driver This display controller is present on M-series chips and is used to drive the touchbar display. Signed-off-by: Sasha Finkelstein Signed-off-by: Janne Grunau --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/adp/Kconfig | 12 + drivers/gpu/drm/adp/Makefile | 5 + drivers/gpu/drm/adp/adp_drv.c | 762 ++++++++++++++++++++++++++++++++++ 5 files changed, 782 insertions(+) create mode 100644 drivers/gpu/drm/adp/Kconfig create mode 100644 drivers/gpu/drm/adp/Makefile create mode 100644 drivers/gpu/drm/adp/adp_drv.c diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 5a0c476361c300..79b7b0bb9e214e 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -377,6 +377,8 @@ source "drivers/gpu/drm/mcde/Kconfig" source "drivers/gpu/drm/tidss/Kconfig" +source "drivers/gpu/drm/adp/Kconfig" + source "drivers/gpu/drm/xlnx/Kconfig" source "drivers/gpu/drm/gud/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 104b42df2e956b..8bb59738f14ee1 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -175,6 +175,7 @@ obj-y += mxsfb/ obj-y += tiny/ obj-$(CONFIG_DRM_PL111) += pl111/ obj-$(CONFIG_DRM_TVE200) += tve200/ +obj-$(CONFIG_DRM_ADP) += adp/ obj-$(CONFIG_DRM_XEN) += xen/ obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/ obj-$(CONFIG_DRM_LIMA) += lima/ diff --git a/drivers/gpu/drm/adp/Kconfig b/drivers/gpu/drm/adp/Kconfig new file mode 100644 index 00000000000000..739029bde31919 --- /dev/null +++ b/drivers/gpu/drm/adp/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +config DRM_ADP + tristate "DRM Support for pre-DCP Apple display controllers" + depends on DRM && OF && ARM64 + depends on ARCH_APPLE || COMPILE_TEST + select DRM_KMS_HELPER + select DRM_KMS_DMA_HELPER + select DRM_GEM_DMA_HELPER + select VIDEOMODE_HELPERS + select DRM_MIPI_DSI + help + Say Y if you have an Apple Arm laptop with a touchbar. diff --git a/drivers/gpu/drm/adp/Makefile b/drivers/gpu/drm/adp/Makefile new file mode 100644 index 00000000000000..28a5d4b4a267f3 --- /dev/null +++ b/drivers/gpu/drm/adp/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT + +adpdrm-y := adp_drv.o +obj-$(CONFIG_DRM_ADP) += adpdrm.o +obj-$(CONFIG_DRM_ADP) += panel-summit.o diff --git a/drivers/gpu/drm/adp/adp_drv.c b/drivers/gpu/drm/adp/adp_drv.c new file mode 100644 index 00000000000000..a70e7b31c795a7 --- /dev/null +++ b/drivers/gpu/drm/adp/adp_drv.c @@ -0,0 +1,762 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADP_INT_STATUS 0x34 +#define ADP_INT_STATUS_INT_MASK 0x7 +#define ADP_INT_STATUS_VBLANK 0x1 +#define ADP_CTRL 0x100 +#define ADP_CTRL_VBLANK_ON 0x12 +#define ADP_CTRL_FIFO_ON 0x601 +#define ADP_SCREEN_SIZE 0x0c + +#define ADBE_FIFO 0x10c0 +#define ADBE_FIFO_SYNC 0xc0000000 + +#define ADBE_BLEND_BYPASS 0x2020 +#define ADBE_BLEND_EN1 0x2028 +#define ADBE_BLEND_EN2 0x2074 +#define ADBE_BLEND_EN3 0x202c +#define ADBE_BLEND_EN4 0x2034 +#define ADBE_MASK_BUF 0x2200 + +#define ADBE_SRC_START 0x4040 +#define ADBE_SRC_SIZE 0x4048 +#define ADBE_DST_START 0x4050 +#define ADBE_DST_SIZE 0x4054 +#define ADBE_STRIDE 0x4038 +#define ADBE_FB_BASE 0x4030 + +#define ADBE_LAYER_EN1 0x4020 +#define ADBE_LAYER_EN2 0x4068 +#define ADBE_LAYER_EN3 0x40b4 +#define ADBE_LAYER_EN4 0x40f4 +#define ADBE_SCALE_CTL 0x40ac +#define ADBE_SCALE_CTL_BYPASS 0x100000 + +#define ADBE_LAYER_CTL 0x1038 +#define ADBE_LAYER_CTL_ENABLE 0x10000 + +#define ADBE_PIX_FMT 0x402c +#define ADBE_PIX_FMT_XRGB32 0x53e4001 + +#define DSI_GEN_HDR 0x6c +#define DSI_GEN_PLD_DATA 0x70 + +#define DSI_CMD_PKT_STATUS 0x74 + +#define GEN_PLD_R_EMPTY BIT(4) +#define GEN_PLD_W_FULL BIT(3) +#define GEN_PLD_W_EMPTY BIT(2) +#define GEN_CMD_FULL BIT(1) +#define GEN_CMD_EMPTY BIT(0) +#define GEN_RD_CMD_BUSY BIT(6) +#define CMD_PKT_STATUS_TIMEOUT_US 20000 + +DEFINE_DRM_GEM_DMA_FOPS(adp_fops); + +static int adp_drm_gem_dumb_create(struct drm_file *file_priv, + struct drm_device *drm, + struct drm_mode_create_dumb *args) +{ + args->height = ALIGN(args->height, 64); + args->size = args->pitch * args->height; + + return drm_gem_dma_dumb_create_internal(file_priv, drm, args); +} + +static const struct drm_driver adp_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, + .fops = &adp_fops, + DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE(adp_drm_gem_dumb_create), + .name = "adp", + .desc = "Apple Display Pipe DRM Driver", + .date = "20230412", + .major = 0, + .minor = 1, +}; + +struct adp_drv_private { + struct drm_device drm; + struct drm_crtc crtc; + struct drm_encoder encoder; + struct drm_connector connector; + struct mipi_dsi_host dsi; + void __iomem *be; + void __iomem *fe; + void __iomem *mipi; + u32 *mask_buf; + u64 mask_buf_size; + dma_addr_t mask_iova; + int be_irq; + int fe_irq; + spinlock_t irq_lock; + struct drm_pending_vblank_event *event; +}; + +struct adp_plane { + struct drm_plane base_plane; + u8 id; +}; + +#define to_adp(x) container_of(x, struct adp_drv_private, drm) +#define crtc_to_adp(x) container_of(x, struct adp_drv_private, crtc) +#define conn_to_adp(x) container_of(x, struct adp_drv_private, connector) +#define mipi_to_adp(x) container_of(x, struct adp_drv_private, dsi) + +static int adp_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state; + struct drm_crtc_state *crtc_state; + + new_plane_state = drm_atomic_get_new_plane_state(state, plane); + + if (!new_plane_state->crtc) + return 0; + + crtc_state = drm_atomic_get_crtc_state(state, new_plane_state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + return drm_atomic_helper_check_plane_state(new_plane_state, + crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + true, true); +} + +static void adp_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct adp_drv_private *adp; + struct drm_rect src_rect; + struct drm_gem_dma_object *obj; + struct drm_framebuffer *fb; + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); + u32 src_pos, src_size, dst_pos, dst_size; + if (!plane || !new_state) + return; + + fb = new_state->fb; + if (!fb) + return; + adp = to_adp(plane->dev); + + drm_rect_fp_to_int(&src_rect, &new_state->src); + src_pos = src_rect.x1 << 16 | src_rect.y1; + dst_pos = new_state->dst.x1 << 16 | new_state->dst.y1; + src_size = drm_rect_width(&src_rect) << 16 | drm_rect_height(&src_rect); + dst_size = drm_rect_width(&new_state->dst) << 16 | + drm_rect_height(&new_state->dst); + writel(src_pos, adp->be + ADBE_SRC_START); + writel(src_size, adp->be + ADBE_SRC_SIZE); + writel(dst_pos, adp->be + ADBE_DST_START); + writel(dst_size, adp->be + ADBE_DST_SIZE); + writel(fb->pitches[0], adp->be + ADBE_STRIDE); + obj = drm_fb_dma_get_gem_obj(fb, 0); + if (obj) + writel(obj->dma_addr + fb->offsets[0], adp->be + ADBE_FB_BASE); + + writel(0x1, adp->be + ADBE_LAYER_EN1); + writel(0x1, adp->be + ADBE_LAYER_EN2); + writel(0x1, adp->be + ADBE_LAYER_EN3); + writel(0x1, adp->be + ADBE_LAYER_EN4); + writel(ADBE_SCALE_CTL_BYPASS, adp->be + ADBE_SCALE_CTL); + writel(ADBE_LAYER_CTL_ENABLE | 0x1, adp->be + ADBE_LAYER_CTL); + writel(ADBE_PIX_FMT_XRGB32, adp->be + ADBE_PIX_FMT); + +} + +static void adp_plane_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct adp_drv_private *adp = to_adp(plane->dev); + writel(0x0, adp->be + ADBE_LAYER_EN1); + writel(0x0, adp->be + ADBE_LAYER_EN2); + writel(0x0, adp->be + ADBE_LAYER_EN3); + writel(0x0, adp->be + ADBE_LAYER_EN4); + writel(ADBE_LAYER_CTL_ENABLE, adp->be + ADBE_LAYER_CTL); +} + +static const struct drm_plane_helper_funcs adp_plane_helper_funcs = { + .atomic_check = adp_plane_atomic_check, + .atomic_update = adp_plane_atomic_update, + .atomic_disable = adp_plane_atomic_disable, + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS +}; + +static const struct drm_plane_funcs adp_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + DRM_GEM_SHADOW_PLANE_FUNCS +}; + +static const u32 plane_formats[] = { + DRM_FORMAT_XRGB8888, +}; + +#define ALL_CRTCS 1 + +static struct adp_plane *adp_plane_new(struct adp_drv_private *adp, u8 id) +{ + struct drm_device *drm = &adp->drm; + struct adp_plane *plane; + enum drm_plane_type plane_type; + + plane_type = (id == 0) ? DRM_PLANE_TYPE_PRIMARY : + DRM_PLANE_TYPE_OVERLAY; + + plane = drmm_universal_plane_alloc(drm, struct adp_plane, base_plane, + ALL_CRTCS, &adp_plane_funcs, + plane_formats, ARRAY_SIZE(plane_formats), + NULL, plane_type, "plane %d", id); + if (!plane) { + drm_err(drm, "failed to allocate plane"); + return ERR_PTR(-ENOMEM); + } + plane->id = id; + + drm_plane_helper_add(&plane->base_plane, &adp_plane_helper_funcs); + return plane; +} + +static void adp_enable_vblank(struct adp_drv_private *adp) +{ + u32 cur_ctrl; + + writel(ADP_INT_STATUS_INT_MASK, adp->fe + ADP_INT_STATUS); + + cur_ctrl = readl(adp->fe + ADP_CTRL); + writel(cur_ctrl | ADP_CTRL_VBLANK_ON, adp->fe + ADP_CTRL); +} + +static int adp_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct adp_drv_private *adp = to_adp(dev); + adp_enable_vblank(adp); + + return 0; +} + +static void adp_disable_vblank(struct adp_drv_private *adp) +{ + u32 cur_ctrl; + + cur_ctrl = readl(adp->fe + ADP_CTRL); + writel(cur_ctrl & ~ADP_CTRL_VBLANK_ON, adp->fe + ADP_CTRL); + writel(ADP_INT_STATUS_INT_MASK, adp->fe + ADP_INT_STATUS); +} + +static void adp_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct adp_drv_private *adp = to_adp(dev); + + adp_disable_vblank(adp); +} + + +static void adp_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct adp_drv_private *adp = crtc_to_adp(crtc); + writel(0x1, adp->be + ADBE_BLEND_EN2); + writel(0x10, adp->be + ADBE_BLEND_EN1); + writel(0x1, adp->be + ADBE_BLEND_EN3); + writel(0x1, adp->be + ADBE_BLEND_BYPASS); + writel(0x1, adp->be + ADBE_BLEND_EN4); +} + +static void adp_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct adp_drv_private *adp = crtc_to_adp(crtc); + struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc); + + drm_atomic_helper_disable_planes_on_crtc(old_state, false); + + writel(0x0, adp->be + ADBE_BLEND_EN2); + writel(0x0, adp->be + ADBE_BLEND_EN1); + writel(0x0, adp->be + ADBE_BLEND_EN3); + writel(0x0, adp->be + ADBE_BLEND_BYPASS); + writel(0x0, adp->be + ADBE_BLEND_EN4); + drm_crtc_vblank_off(crtc); +} + +static void adp_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + u32 frame_num = 1; + struct adp_drv_private *adp = crtc_to_adp(crtc); + struct drm_crtc_state *new_state = drm_atomic_get_new_crtc_state(state, crtc); + u64 new_size = ALIGN(new_state->mode.hdisplay * + new_state->mode.vdisplay * 4, PAGE_SIZE); + + if (new_size != adp->mask_buf_size) { + if (adp->mask_buf) + dma_free_coherent(crtc->dev->dev, adp->mask_buf_size, + adp->mask_buf, adp->mask_iova); + adp->mask_buf = NULL; + if (new_size != 0) { + adp->mask_buf = dma_alloc_coherent(crtc->dev->dev, new_size, + &adp->mask_iova, GFP_KERNEL); + memset(adp->mask_buf, 0xFF, new_size); + writel(adp->mask_iova, adp->be + ADBE_MASK_BUF); + } + adp->mask_buf_size = new_size; + } + writel(ADBE_FIFO_SYNC | frame_num, adp->be + ADBE_FIFO); + //FIXME: use adbe flush interrupt + spin_lock_irq(&crtc->dev->event_lock); + if (crtc->state->event) { + drm_crtc_vblank_get(crtc); + adp->event = crtc->state->event; + } + crtc->state->event = NULL; + spin_unlock_irq(&crtc->dev->event_lock); +} + +static const struct drm_crtc_funcs adp_crtc_funcs = { + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .reset = drm_atomic_helper_crtc_reset, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .enable_vblank = adp_crtc_enable_vblank, + .disable_vblank = adp_crtc_disable_vblank, +}; + + +static const struct drm_crtc_helper_funcs adp_crtc_helper_funcs = { + .atomic_enable = adp_crtc_atomic_enable, + .atomic_disable = adp_crtc_atomic_disable, + .atomic_flush = adp_crtc_atomic_flush, +}; + +static int adp_setup_crtc(struct adp_drv_private *adp) +{ + struct drm_device *drm = &adp->drm; + struct adp_plane *primary; + int ret; + + primary = adp_plane_new(adp, 0); + if (IS_ERR(primary)) + return PTR_ERR(primary); + + ret = drm_crtc_init_with_planes(drm, &adp->crtc, &primary->base_plane, + NULL, &adp_crtc_funcs, NULL); + if (ret) + return ret; + + drm_crtc_helper_add(&adp->crtc, &adp_crtc_helper_funcs); + return 0; +} + +static int adp_get_modes(struct drm_connector *connector) +{ + struct adp_drv_private *adp = conn_to_adp(connector); + struct drm_display_mode *mode; + u32 size; + + size = readl(adp->fe + ADP_SCREEN_SIZE); + mode = drm_mode_create(connector->dev); + + mode->vdisplay = size >> 16; + mode->hdisplay = size & 0xFFFF; + mode->hsync_start = mode->hdisplay + 8; + mode->hsync_end = mode->hsync_start + 80; + mode->htotal = mode->hsync_end + 40; + mode->vsync_start = mode->vdisplay + 1; + mode->vsync_end = mode->vsync_start + 15; + mode->vtotal = mode->vsync_end + 6; + mode->clock = (mode->vtotal * mode->htotal * 60) / 1000; + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + mode->flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC; + drm_mode_set_name(mode); + drm_mode_probed_add(connector, mode); + return 1; +} + +static int adp_detect_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) { + connector->display_info.non_desktop = true; + drm_object_property_set_value(&connector->base, + connector->dev->mode_config.non_desktop_property, + connector->display_info.non_desktop); + return connector_status_connected; +} + +static const struct drm_connector_funcs adp_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static const struct drm_connector_helper_funcs adp_connector_helper_funcs = { + .get_modes = adp_get_modes, + .detect_ctx = adp_detect_ctx, +}; + +static const struct drm_mode_config_funcs adp_mode_config_funcs = { + .fb_create = drm_gem_fb_create_with_dirty, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static int adp_setup_mode_config(struct adp_drv_private *adp) +{ + struct drm_device *drm = &adp->drm; + int ret; + + ret = drmm_mode_config_init(drm); + if (ret) + return ret; + + drm->mode_config.min_width = 32; + drm->mode_config.min_height = 32; + drm->mode_config.max_width = 16384; + drm->mode_config.max_height = 16384; + drm->mode_config.preferred_depth = 24; + drm->mode_config.prefer_shadow = 0; + drm->mode_config.funcs = &adp_mode_config_funcs; + + ret = adp_setup_crtc(adp); + if (ret) { + drm_err(drm, "failed to create crtc"); + return ret; + } + + adp->encoder.possible_crtcs = ALL_CRTCS; + ret = drm_simple_encoder_init(drm, &adp->encoder, DRM_MODE_ENCODER_DSI); + if (ret) { + drm_err(drm, "failed to init encoder"); + return ret; + } + drm_connector_helper_add(&adp->connector, + &adp_connector_helper_funcs); + ret = drm_connector_init(drm, &adp->connector, &adp_connector_funcs, + DRM_MODE_CONNECTOR_DSI); + if (ret) + return ret; + + drm_connector_attach_encoder(&adp->connector, &adp->encoder); + + ret = drm_vblank_init(drm, drm->mode_config.num_crtc); + if (ret < 0) { + drm_err(drm, "failed to initialize vblank"); + return ret; + } + + drm_mode_config_reset(drm); + + return 0; +} + +static int adp_parse_of(struct platform_device *pdev, struct adp_drv_private *adp) +{ + adp->be = devm_platform_ioremap_resource_byname(pdev, "be"); + if (IS_ERR(adp->be)) { + dev_err(&pdev->dev, "failed to map display backend mmio"); + return PTR_ERR(adp->be); + } + + adp->fe = devm_platform_ioremap_resource_byname(pdev, "fe"); + if (IS_ERR(adp->fe)) { + dev_err(&pdev->dev, "failed to map display pipe mmio"); + return PTR_ERR(adp->fe); + } + + adp->mipi = devm_platform_ioremap_resource_byname(pdev, "mipi"); + if (IS_ERR(adp->mipi)) { + dev_err(&pdev->dev, "failed to map mipi mmio"); + return PTR_ERR(adp->mipi); + } + + adp->be_irq = platform_get_irq_byname(pdev, "be"); + if (adp->be_irq < 0) { + dev_err(&pdev->dev, "failed to find be irq"); + return adp->be_irq; + } + + adp->fe_irq = platform_get_irq_byname(pdev, "fe"); + if (adp->fe_irq < 0) { + dev_err(&pdev->dev, "failed to find fe irq"); + return adp->fe_irq; + } + return 0; +} + + +static int adp_dsi_gen_pkt_hdr_write(struct adp_drv_private *adp, u32 hdr_val) +{ + int ret; + u32 val, mask; + + ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS, + val, !(val & GEN_CMD_FULL), 1000, + CMD_PKT_STATUS_TIMEOUT_US); + if (ret) { + dev_err(adp->drm.dev, "failed to get available command FIFO\n"); + return ret; + } + + writel(hdr_val, adp->mipi + DSI_GEN_HDR); + + mask = GEN_CMD_EMPTY | GEN_PLD_W_EMPTY; + ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS, + val, (val & mask) == mask, + 1000, CMD_PKT_STATUS_TIMEOUT_US); + if (ret) { + dev_err(adp->drm.dev, "failed to write command FIFO\n"); + return ret; + } + + return 0; +} + +static int adp_dsi_write(struct adp_drv_private *adp, + const struct mipi_dsi_packet *packet) +{ + const u8 *tx_buf = packet->payload; + int len = packet->payload_length, pld_data_bytes = sizeof(u32), ret; + __le32 word; + u32 val; + + while (len) { + if (len < pld_data_bytes) { + word = 0; + memcpy(&word, tx_buf, len); + writel(le32_to_cpu(word), adp->mipi + DSI_GEN_PLD_DATA); + len = 0; + } else { + memcpy(&word, tx_buf, pld_data_bytes); + writel(le32_to_cpu(word), adp->mipi + DSI_GEN_PLD_DATA); + tx_buf += pld_data_bytes; + len -= pld_data_bytes; + } + + ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS, + val, !(val & GEN_PLD_W_FULL), 1000, + CMD_PKT_STATUS_TIMEOUT_US); + if (ret) { + dev_err(adp->drm.dev, + "failed to get available write payload FIFO\n"); + return ret; + } + } + + word = 0; + memcpy(&word, packet->header, sizeof(packet->header)); + return adp_dsi_gen_pkt_hdr_write(adp, le32_to_cpu(word)); +} + +static int adp_dsi_read(struct adp_drv_private *adp, + const struct mipi_dsi_msg *msg) +{ + int i, j, ret, len = msg->rx_len; + u8 *buf = msg->rx_buf; + u32 val; + + /* Wait end of the read operation */ + ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS, + val, !(val & GEN_RD_CMD_BUSY), + 1000, CMD_PKT_STATUS_TIMEOUT_US); + if (ret) { + dev_err(adp->drm.dev, "Timeout during read operation\n"); + return ret; + } + + for (i = 0; i < len; i += 4) { + /* Read fifo must not be empty before all bytes are read */ + ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS, + val, !(val & GEN_PLD_R_EMPTY), + 1000, CMD_PKT_STATUS_TIMEOUT_US); + if (ret) { + dev_err(adp->drm.dev, "Read payload FIFO is empty\n"); + return ret; + } + + val = readl(adp->mipi + DSI_GEN_PLD_DATA); + for (j = 0; j < 4 && j + i < len; j++) + buf[i + j] = val >> (8 * j); + } + + return ret; +} + +static ssize_t adp_dsi_host_transfer(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg) +{ + struct adp_drv_private *adp = mipi_to_adp(host); + struct mipi_dsi_packet packet; + int ret, nb_bytes; + + ret = mipi_dsi_create_packet(&packet, msg); + if (ret) { + dev_err(adp->drm.dev, "failed to create packet: %d\n", ret); + return ret; + } + + ret = adp_dsi_write(adp, &packet); + if (ret) + return ret; + + if (msg->rx_buf && msg->rx_len) { + ret = adp_dsi_read(adp, msg); + if (ret) + return ret; + nb_bytes = msg->rx_len; + } else { + nb_bytes = packet.size; + } + + return nb_bytes; +} + +static int adp_dsi_host_attach(struct mipi_dsi_host *host, + struct mipi_dsi_device *dev) +{ + return 0; +} + +static int adp_dsi_host_detach(struct mipi_dsi_host *host, + struct mipi_dsi_device *dev) +{ + return 0; +} + +static const struct mipi_dsi_host_ops adp_dsi_host_ops = { + .transfer = adp_dsi_host_transfer, + .attach = adp_dsi_host_attach, + .detach = adp_dsi_host_detach, +}; + +static irqreturn_t adp_fe_irq(int irq, void *arg) +{ + struct adp_drv_private *adp = (struct adp_drv_private *)arg; + u32 int_status; + u32 int_ctl; + + spin_lock(&adp->irq_lock); + + int_status = readl(adp->fe + ADP_INT_STATUS); + if (int_status & ADP_INT_STATUS_VBLANK) { + drm_crtc_handle_vblank(&adp->crtc); + spin_lock(&adp->crtc.dev->event_lock); + if (adp->event) { + int_ctl = readl(adp->fe + ADP_CTRL); + if ((int_ctl & 0xF00) == 0x600) { + drm_crtc_send_vblank_event(&adp->crtc, adp->event); + adp->event = NULL; + drm_crtc_vblank_put(&adp->crtc); + } + } + spin_unlock(&adp->crtc.dev->event_lock); + } + + writel(int_status, adp->fe + ADP_INT_STATUS); + + spin_unlock(&adp->irq_lock); + + return IRQ_HANDLED; +} + +static int adp_probe(struct platform_device *pdev) +{ + struct adp_drv_private *adp; + int err; + + adp = devm_drm_dev_alloc(&pdev->dev, &adp_driver, struct adp_drv_private, drm); + if (IS_ERR(adp)) + return PTR_ERR(adp); + + spin_lock_init(&adp->irq_lock); + + dev_set_drvdata(&pdev->dev, &adp->drm); + + err = adp_parse_of(pdev, adp); + if (err < 0) + return err; + + adp->dsi.dev = &pdev->dev; + adp->dsi.ops = &adp_dsi_host_ops; + err = mipi_dsi_host_register(&adp->dsi); + if (err < 0) + return err; + + adp_disable_vblank(adp); + writel(ADP_CTRL_FIFO_ON | ADP_CTRL_VBLANK_ON, adp->fe + ADP_CTRL); + + err = adp_setup_mode_config(adp); + if (err < 0) + return err; + + err = devm_request_irq(&pdev->dev, adp->fe_irq, adp_fe_irq, 0, + "adp-fe", adp); + if (err) + return err; + + err = drm_dev_register(&adp->drm, 0); + if (err) + return err; + return 0; +} + +static int adp_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct drm_device *drm = dev_get_drvdata(dev); + struct adp_drv_private *adp = to_adp(drm); + + adp_disable_vblank(adp); + mipi_dsi_host_unregister(&adp->dsi); + drm_dev_unregister(drm); + dev_set_drvdata(dev, NULL); + drm_atomic_helper_shutdown(drm); + return 0; +} + +static const struct of_device_id adp_of_match[] = { + { .compatible = "apple,h7-display-pipe", }, + { }, +}; +MODULE_DEVICE_TABLE(of, adp_of_match); + +static struct platform_driver adp_platform_driver = { + .driver = { + .name = "adp", + .of_match_table = adp_of_match, + }, + .probe = adp_probe, + .remove = adp_remove, +}; + +module_platform_driver(adp_platform_driver); + +MODULE_DESCRIPTION("Apple Display Pipe DRM driver"); +MODULE_LICENSE("GPL v2"); From 87ca97e25c9222d62c343802d8916df46636e58a Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Tue, 18 Apr 2023 23:03:15 +0300 Subject: [PATCH 0651/1020] gpu: drm: adp: Add a backlight driver for the Summit LCD This is the display panel used for the touchbar on laptops that have it. Signed-off-by: Sasha Finkelstein --- drivers/gpu/drm/adp/panel-summit.c | 104 +++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 drivers/gpu/drm/adp/panel-summit.c diff --git a/drivers/gpu/drm/adp/panel-summit.c b/drivers/gpu/drm/adp/panel-summit.c new file mode 100644 index 00000000000000..633651fea92445 --- /dev/null +++ b/drivers/gpu/drm/adp/panel-summit.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include