Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion arch/arm64/boot/dts/apple/t8140-pmgr.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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>;
};
Expand All @@ -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>;
};
Expand All @@ -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>;
};
Expand All @@ -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>;
};
Expand All @@ -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>;
};
Expand All @@ -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>;
};
Expand All @@ -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>;
};
Expand All @@ -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>;
};
Expand All @@ -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>;
};
Expand All @@ -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>;
};
Expand All @@ -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>;
};
Expand All @@ -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>;
};
Expand All @@ -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>;
};
Expand Down
7 changes: 6 additions & 1 deletion arch/arm64/boot/dts/apple/t8140.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -2131,9 +2131,14 @@
interrupt-parent = <&aic>;
/* ADT [323 324 325 326 336]; 323 carries the IPC mailbox */
interrupts = <AIC_IRQ 323 IRQ_TYPE_LEVEL_HIGH>;
/*
* 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>;
Expand Down
9 changes: 6 additions & 3 deletions drivers/media/platform/apple/isp/isp-cam.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
2 changes: 2 additions & 0 deletions drivers/media/platform/apple/isp/isp-drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down
19 changes: 17 additions & 2 deletions drivers/media/platform/apple/isp/isp-fw.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
}
Expand All @@ -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. */
Expand Down
29 changes: 29 additions & 0 deletions drivers/pmdomain/apple/pmgr-pwrstate.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
15 changes: 8 additions & 7 deletions drivers/soc/apple/sleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down
Loading