diff --git a/arch/arm64/boot/dts/apple/t8140-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8140-pmgr.dtsi index eacb2caf324cc..cac4c68fd3d9f 100644 --- a/arch/arm64/boot/dts/apple/t8140-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8140-pmgr.dtsi @@ -274,7 +274,8 @@ * enables between ISP_FE and ISP_SECURE. Plain power * gating only: a local domain parked with DEV_DISABLE (the t8112 * apple,force-disable pattern) never reached ACTIVE again on J700 - * (isp_cpu_core0 stuck at ACTUAL=0 on the second firmware boot). + * (isp_cpu_core0 stuck at ACTUAL=0 on the second firmware boot). No + * automatic power management either: macOS writes them exactly 0xf. */ ps_sep: power-controller@c00 { compatible = "apple,t8140-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; @@ -290,6 +291,7 @@ reg = <0x4000 4>; #power-domain-cells = <0>; #reset-cells = <0>; + apple,no-auto-pm; label = "isp_cpu"; power-domains = <&ps_isp_sys>; }; @@ -299,6 +301,7 @@ reg = <0x4008 4>; #power-domain-cells = <0>; #reset-cells = <0>; + apple,no-auto-pm; label = "isp_cpu_core0"; power-domains = <&ps_isp_cpu>; }; @@ -308,6 +311,7 @@ reg = <0x4010 4>; #power-domain-cells = <0>; #reset-cells = <0>; + apple,no-auto-pm; label = "isp_cpu_core1"; power-domains = <&ps_isp_cpu>; }; @@ -317,6 +321,7 @@ reg = <0x4018 4>; #power-domain-cells = <0>; #reset-cells = <0>; + apple,no-auto-pm; label = "isp_fe"; power-domains = <&ps_isp_sys>; }; @@ -326,6 +331,7 @@ reg = <0x4020 4>; #power-domain-cells = <0>; #reset-cells = <0>; + apple,no-auto-pm; label = "isp_dprx"; power-domains = <&ps_isp_fe>; }; @@ -335,6 +341,7 @@ reg = <0x4028 4>; #power-domain-cells = <0>; #reset-cells = <0>; + apple,no-auto-pm; label = "isp_secure"; power-domains = <&ps_isp_fe>; }; @@ -344,6 +351,7 @@ reg = <0x4030 4>; #power-domain-cells = <0>; #reset-cells = <0>; + apple,no-auto-pm; label = "isp_sts0"; power-domains = <&ps_isp_fe>; }; @@ -353,6 +361,7 @@ reg = <0x4038 4>; #power-domain-cells = <0>; #reset-cells = <0>; + apple,no-auto-pm; label = "isp_sts1"; power-domains = <&ps_isp_fe>; }; @@ -362,6 +371,7 @@ reg = <0x4040 4>; #power-domain-cells = <0>; #reset-cells = <0>; + apple,no-auto-pm; label = "isp_pearl"; power-domains = <&ps_isp_fe>; }; @@ -371,6 +381,7 @@ reg = <0x4048 4>; #power-domain-cells = <0>; #reset-cells = <0>; + apple,no-auto-pm; label = "isp_be"; power-domains = <&ps_isp_fe>; }; @@ -380,6 +391,7 @@ reg = <0x4050 4>; #power-domain-cells = <0>; #reset-cells = <0>; + apple,no-auto-pm; label = "isp_clr"; power-domains = <&ps_isp_be>; }; @@ -389,6 +401,7 @@ reg = <0x4058 4>; #power-domain-cells = <0>; #reset-cells = <0>; + apple,no-auto-pm; label = "isp_cvd"; power-domains = <&ps_isp_be>; }; @@ -398,6 +411,7 @@ reg = <0x4060 4>; #power-domain-cells = <0>; #reset-cells = <0>; + apple,no-auto-pm; label = "isp_cvm"; power-domains = <&ps_isp_cvd>; }; diff --git a/arch/arm64/boot/dts/apple/t8140.dtsi b/arch/arm64/boot/dts/apple/t8140.dtsi index 79f1b67184539..6247d9c31aa47 100644 --- a/arch/arm64/boot/dts/apple/t8140.dtsi +++ b/arch/arm64/boot/dts/apple/t8140.dtsi @@ -2131,9 +2131,14 @@ interrupt-parent = <&aic>; /* ADT [323 324 325 326 336]; 323 carries the IPC mailbox */ interrupts = ; + /* + * In macOS's order: the camera sensor domains come up + * after ISP_FE and before the rest of the pipeline. + */ power-domains = <&ps_isp_sys>, <&ps_isp_cpu>, <&ps_isp_cpu_core0>, <&ps_isp_cpu_core1>, - <&ps_isp_fe>, <&ps_isp_dprx>, <&ps_isp_secure>, + <&ps_isp_fe>, <&ps_ispsens0>, <&ps_ispsens1>, + <&ps_isp_dprx>, <&ps_isp_secure>, <&ps_isp_sts0>, <&ps_isp_sts1>, <&ps_isp_pearl>, <&ps_isp_be>, <&ps_isp_clr>, <&ps_isp_cvd>, <&ps_isp_cvm>; diff --git a/drivers/media/platform/apple/isp/isp-cam.c b/drivers/media/platform/apple/isp/isp-cam.c index 0f8ae7eb2f554..d065d007a0840 100644 --- a/drivers/media/platform/apple/isp/isp-cam.c +++ b/drivers/media/platform/apple/isp/isp-cam.c @@ -587,11 +587,14 @@ int apple_isp_detect_camera(struct apple_isp *isp) err = isp_detect_camera(isp); - /* Resident firmware stays up; the channel is set up per stream. */ + /* + * Resident firmware stays up, the channel set up per stream, unless it starts + * again: then it's off until the first stream, as on macOS. + */ if (isp->hw->resident_fw) { - if (err) + if (err || isp->hw->fw_restart) apple_isp_firmware_halt(isp); - else + if (!err) isp->camera_detected = true; return err; } diff --git a/drivers/media/platform/apple/isp/isp-drv.c b/drivers/media/platform/apple/isp/isp-drv.c index 2006351c49a53..245af9fa32f35 100644 --- a/drivers/media/platform/apple/isp/isp-drv.c +++ b/drivers/media/platform/apple/isp/isp-drv.c @@ -969,6 +969,8 @@ static const struct apple_isp_hw apple_isp_hw_t8140 = { .capture_meta_size = ISP_CAPTURE_META_SIZE_T8140, .resident_fw = true, .fw_restart = true, + /* As on macOS, the firmware first starts when a client opens the camera. */ + .deferred_start = true, .meta_exposure = true, }; diff --git a/drivers/media/platform/apple/isp/isp-fw.c b/drivers/media/platform/apple/isp/isp-fw.c index 610e5c3ad72db..144c2a4ae3883 100644 --- a/drivers/media/platform/apple/isp/isp-fw.c +++ b/drivers/media/platform/apple/isp/isp-fw.c @@ -475,7 +475,13 @@ static void isp_firmware_shutdown_stage1(struct apple_isp *isp) dev_warn(isp->dev, "coproc not in WFI before power-down (status 0x%x)\n", val); - isp_coproc_write32(isp, isp_coproc_control(isp), 0x0); + /* + * macOS powers T8140's ISP off from WFI with the run bit still set. The sleep + * engine's own restore of the ISP in a wake that kept DRAM up depends on it: + * with the bit cleared that wake stalls until the SoC's watchdog resets it. + */ + if (isp->hw->gen != ISP_GEN_T8140) + isp_coproc_write32(isp, isp_coproc_control(isp), 0x0); if (started) { /* The request bit alone asks for it, and reads back once done */ @@ -1299,7 +1305,8 @@ int apple_isp_firmware_boot(struct apple_isp *isp) if (err) { dev_err(isp->dev, "failed to boot firmware: %d\n", err); pm_runtime_put_sync(isp->dev); - if (isp->hw->resident_fw) + /* Firmware that starts again can be tried again at the next open. */ + if (isp->hw->resident_fw && !isp->hw->fw_restart) isp->fw_state = ISP_FW_DEAD; return err; } @@ -1326,6 +1333,14 @@ void apple_isp_firmware_shutdown(struct apple_isp *isp) if (isp->fw_state == ISP_FW_RUNNING) synchronize_irq(isp->irq); isp_free_released_surfaces(isp); + + /* + * Firmware that starts again goes down with the stream, as macOS's camera close + * takes it, and boots again for the next one. A system sleep then finds it off: + * stopped on the way into a sleep that keeps DRAM up, the SoC doesn't wake. + */ + if (isp->hw->fw_restart) + apple_isp_firmware_halt(isp); } /* Stop the firmware; resident firmware without hw->fw_restart stays down. */ diff --git a/drivers/pmdomain/apple/pmgr-pwrstate.c b/drivers/pmdomain/apple/pmgr-pwrstate.c index 2ee6c3b850098..403161b5224d4 100644 --- a/drivers/pmdomain/apple/pmgr-pwrstate.c +++ b/drivers/pmdomain/apple/pmgr-pwrstate.c @@ -134,6 +134,22 @@ static int apple_pmgr_ps_wait_parent(struct apple_pmgr_ps *ps, u32 *cur) * power-on then fails as it did before, and genpd, which ignores errors in * that phase, keeps the domain requested on. */ +/* Pulse the PS reset of a domain that is off with BUSY latched; it stays off. */ +static void apple_pmgr_ps_unlatch_off(struct apple_pmgr_ps *ps, u32 *cur) +{ + u32 off = *cur & ~(APPLE_PMGR_AUTO_ENABLE | APPLE_PMGR_FLAGS | APPLE_PMGR_BUSY | + APPLE_PMGR_PS_RESET | APPLE_PMGR_DEV_DISABLE | APPLE_PMGR_PS_TARGET); + + regmap_write(ps->regmap, ps->offset, off | APPLE_PMGR_PS_RESET); + regmap_read_poll_timeout_atomic(ps->regmap, ps->offset, *cur, + !(*cur & APPLE_PMGR_BUSY), 1, APPLE_PMGR_PS_SET_TIMEOUT); + regmap_write(ps->regmap, ps->offset, off); + regmap_read(ps->regmap, ps->offset, cur); + if (*cur & APPLE_PMGR_BUSY) + dev_warn_ratelimited(ps->dev, "PS %s: off with BUSY latched: %#x\n", + ps->genpd.name, *cur); +} + static int apple_pmgr_ps_clear_latch(struct apple_pmgr_ps *ps, u32 reg, u32 *cur) { u32 off = (reg & ~APPLE_PMGR_PS_TARGET) | APPLE_PMGR_PS_PWRGATE; @@ -266,6 +282,19 @@ static int apple_pmgr_ps_set(struct generic_pm_domain *genpd, u32 pstate, bool a } } + /* + * A domain can reach off with BUSY latched: on T8140 both ISP CPU cores do, + * every time they go down under Linux. macOS leaves them off with it clear, + * and the sleep engine, which powers them up itself in a wake that keeps DRAM + * up, stalls on a latched one until its watchdog resets the SoC. Only a PS + * reset clears it, and the domain is off, so pulse one as on the power-on + * path (not in a system sleep transition, see apple_pmgr_ps_clear_latch()). + */ + if (!ret && pstate != APPLE_PMGR_PS_ACTIVE && (cur & APPLE_PMGR_BUSY) && + !FIELD_GET(APPLE_PMGR_PS_ACTUAL, cur) && !ps->system_sleep && + !ps->wait_parent && !ps->strict_state && !ps->externally_clocked) + apple_pmgr_ps_unlatch_off(ps, &cur); + report: if (ret < 0) dev_err_ratelimited(ps->dev, diff --git a/drivers/soc/apple/sleep.c b/drivers/soc/apple/sleep.c index 3838cedf471ef..0041ab2476a55 100644 --- a/drivers/soc/apple/sleep.c +++ b/drivers/soc/apple/sleep.c @@ -617,11 +617,11 @@ static bool sleep_slp_ddr_usb; static void __iomem *sleep_slp_ddr_word; /* - * The SLP_DDR wake runs on the SoC-side state the system left. After a Linux boot it stalls in - * the sleep engine, and once one deep sleep has rebuilt that state it gets through every time. - * So the word stays clear, whatever the host votes, until a wake shows the deep path ran. + * The SLP_DDR wake runs on the SoC-side state the system left. The first one of a boot used to + * stall in the sleep engine, which came from the ISP's firmware boot at probe (see the ISP + * driver). Cleared while a deep path is owed, and set again by a wake that shows it ran. */ -static bool sleep_slp_ddr_ready; +static bool sleep_slp_ddr_ready = true; /* * A coprocessor that runs through a sleep crashed. The AOP is the always-on master SLP_DDR keeps @@ -672,9 +672,10 @@ void apple_sleep_coproc_crashed(struct device *dev) EXPORT_SYMBOL_GPL(apple_sleep_coproc_crashed); /* - * After the camera ran, the next SLP_DDR wake stalled in the sleep engine, 6 of 6 times, whether - * the ISP's firmware was stopped at the sleep or about 50 s before it; a deep sleep rebuilds - * what it leaves, as after boot. So from the ISP's start, the next sleep takes the deep path. + * T8140's camera firmware doesn't start again after any SLP_DDR wake until a deep sleep: it + * boots, but its first command never completes. So from an ISP start, even a failed one, the + * next sleep takes the deep path: a camera used before a sleep works after it, and one that + * failed after a SLP_DDR wake works after the next sleep. */ void apple_sleep_next_deep(struct device *dev) {