From: sashiko-bot@kernel.org
To: "Devarsh Thakkar" <devarsht@ti.com>
Cc: dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org,
robh@kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH v3 07/14] drm/solomon: ssd16xx: Add power management support
Date: Mon, 19 Oct 2026 21:57:45 +0000 [thread overview]
Message-ID: <sashiko-outbox-178283@kernel.org> (raw)
In-Reply-To: <20261019213726.4145532-8-devarsht@ti.com>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] drm/solomon: ssd16xx: missing error rollback in ssd16xx_pm_suspend()
- [Medium] drm/solomon: ssd16xx: missing state restore in ssd16xx_pm_runtime_resume()
- [Medium] drm/solomon: ssd16xx: premature runtime suspend in ssd16xx_probe()
- [Medium] drm/solomon: ssd16xx: incomplete PM error cleanup in ssd16xx_probe()
--
commit 7bb59d973ba345233d1093837dd50809ed285f26
Author: Devarsh Thakkar <devarsht@ti.com>
drm/solomon: ssd16xx: Add power management support
This patch adds runtime PM with a 35-second autosuspend delay and system
suspend/resume support to the SSD16xx e-paper display driver.
> diff --git a/drivers/gpu/drm/solomon/ssd16xx.c b/drivers/gpu/drm/solomon/ssd16xx.c
> index ff0e459082dea..99f11b7ec03e9 100644
> --- a/drivers/gpu/drm/solomon/ssd16xx.c
> +++ b/drivers/gpu/drm/solomon/ssd16xx.c
[ ... ]
> @@ -2011,9 +2059,26 @@ static int ssd16xx_probe(struct spi_device *spi)
>
> drm_mode_config_reset(drm);
>
> + /*
> + * Mark the device active before enabling runtime PM. The SPI device
> + * persists across module reload cycles; pm_runtime_enable() alone
> + * does not clear a stale dev->power.runtime_error from a previous
> + * module load, which would cause pm_runtime_resume_and_get() to
> + * return -EINVAL on every subsequent call.
> + */
> + /* Use level1 (RAM retained) for runtime PM idle. */
> + device->deep_sleep_mode = device->controller_cfg->deep_sleep_mode_level1;
> +
> + pm_runtime_set_active(dev);
> + pm_runtime_enable(dev);
> + pm_runtime_set_autosuspend_delay(dev, SSD16XX_PM_AUTOSUSPEND_DELAY_MS);
> + pm_runtime_use_autosuspend(dev);
[Severity: Medium]
Does calling pm_runtime_set_autosuspend_delay() after pm_runtime_enable()
here cause an immediate synchronous runtime suspend during ssd16xx_probe()?
At the point pm_runtime_set_autosuspend_delay() runs, the device is enabled
with usage_count == 0, dev->power.use_autosuspend is still false, and
dev->power.last_busy has not been marked. Inside the runtime PM core:
pm_runtime_set_autosuspend_delay()
-> update_autosuspend()
-> rpm_idle(dev, RPM_AUTO)
-> rpm_suspend(dev, RPM_AUTO)
-> pm_runtime_autosuspend_expiration()
Because use_autosuspend is false (and last_busy is 0),
pm_runtime_autosuspend_expiration() returns 0, so rpm_suspend() invokes
ssd16xx_pm_runtime_suspend() synchronously and puts the panel into deep
sleep right after ssd16xx_hw_reset(), only for drm_client_setup() to
immediately wake and hardware-reset the panel a second time.
Should pm_runtime_set_autosuspend_delay(), pm_runtime_use_autosuspend(), and
pm_runtime_mark_last_busy() be called before pm_runtime_enable()?
> +
> ret = drm_dev_register(drm, 0);
> - if (ret)
> + if (ret) {
> + pm_runtime_disable(dev);
> return ret;
> + }
[Severity: Medium]
If drm_dev_register() fails in ssd16xx_probe(), is pm_runtime_disable(dev)
alone sufficient to clean up the runtime PM state?
Because pm_runtime_reinit() on unbind does not clear
dev->power.use_autosuspend or run the runtime_suspend callback, only calling
pm_runtime_disable(dev) leaves use_autosuspend enabled on the persistent
spi_device and leaves the panel powered if it is still active. Should this
error path call pm_runtime_dont_use_autosuspend(dev) and
pm_runtime_force_suspend(dev) to match ssd16xx_remove() and
ssd16xx_shutdown()?
>
> drm_dbg(drm, "SSD16xx e-paper display initialized (%dx%d, %d° rotation)\n",
> device->width, device->height, device->orientation);
> @@ -2029,6 +2094,12 @@ static void ssd16xx_remove(struct spi_device *spi)
>
> drm_dev_unplug(&device->drm);
> drm_atomic_helper_shutdown(&device->drm);
> + pm_runtime_dont_use_autosuspend(&spi->dev);
> + /*
> + * Put the panel to sleep regardless of clear_on_disable, which can make
> + * atomic_disable skip touching the hardware.
> + */
> + pm_runtime_force_suspend(&spi->dev);
> }
>
> static void ssd16xx_shutdown(struct spi_device *spi)
> @@ -2036,8 +2107,139 @@ static void ssd16xx_shutdown(struct spi_device *spi)
> struct ssd16xx_device *device = spi_get_drvdata(spi);
>
> drm_atomic_helper_shutdown(&device->drm);
> + pm_runtime_dont_use_autosuspend(&spi->dev);
> + pm_runtime_force_suspend(&spi->dev);
> }
>
> +static int ssd16xx_pm_suspend(struct device *dev)
> +{
> + struct ssd16xx_device *device = dev_get_drvdata(dev);
> + struct drm_device *drm = &device->drm;
> + int ret;
> +
> + /* Use level2 deep sleep for system suspend; SSD1683 loses RAM but saves more power. */
> + device->deep_sleep_mode = device->controller_cfg->deep_sleep_mode_level2;
> + dev_dbg(dev, "system suspend: entering deep sleep mode 0x%02x\n",
> + device->deep_sleep_mode);
> +
> + ret = drm_mode_config_helper_suspend(drm);
> + if (ret)
> + return ret;
> +
> + /*
> + * If the autosuspend timer already fired the device is RPM_SUSPENDED
> + * at level1. pm_runtime_force_suspend() would skip its callback, so
> + * level2 would never reach the hardware.
> + *
> + * SPI is unresponsive in deep sleep; HWRESET is the only exit.
> + * After the reset, level2 can be sent directly without a full hw_init.
> + *
> + * Skip this for controllers where level1 == level2 (e.g. SSD1677 which
> + * has only one deep sleep mode): the device is already in the deepest
> + * available state and no upgrade is needed.
> + */
> + if (pm_runtime_status_suspended(dev)) {
> + int err = 0;
> +
> + if (device->controller_cfg->deep_sleep_mode_level1 !=
> + device->controller_cfg->deep_sleep_mode_level2) {
> + dev_dbg(dev, "system suspend: upgrading level1->level2 via HWRESET\n");
> + ssd16xx_hw_reset(device);
> + ssd16xx_wait_for_device(device, &err);
> + ssd16xx_send_cmd(device, SSD16XX_CMD_DEEP_SLEEP_MODE, &err);
> + ssd16xx_send_data(device,
> + device->controller_cfg->deep_sleep_mode_level2,
> + &err);
> + } else {
> + dev_dbg(dev, "system suspend: already at deepest sleep mode\n");
> + }
> + device->initialized = false;
> + device->pm_force_suspended = false; /* Skip force_resume on resume */
> + return err;
> + }
> +
> + /* Force runtime-suspended state for clean resume. */
> + ret = pm_runtime_force_suspend(dev);
> + if (ret) {
> + drm_mode_config_helper_resume(drm);
> + return ret;
> + }
> + device->pm_force_suspended = true; /* Resume must call force_resume */
> +
> + return 0;
> +}
[Severity: High]
What happens in ssd16xx_pm_suspend() if an error occurs during the
pm_runtime_status_suspended(dev) upgrade path, or if
drm_mode_config_helper_suspend() or pm_runtime_force_suspend() fails?
In the pm_runtime_status_suspended(dev) branch, if ssd16xx_wait_for_device()
times out or ssd16xx_send_cmd()/ssd16xx_send_data() fails,
ssd16xx_pm_suspend() returns err directly without calling
drm_mode_config_helper_resume(drm). Because the suspend callback returns an
error, the PM core aborts system suspend and will not call
ssd16xx_pm_resume(), which leaves the DRM modeset pipeline and in-kernel
clients suspended, leaks drm->mode_config.suspend_state, and leaves the
panel awake after ssd16xx_hw_reset() while runtime PM status remains
RPM_SUSPENDED.
In addition, device->deep_sleep_mode is overwritten with
deep_sleep_mode_level2 at the start of ssd16xx_pm_suspend() and is never
restored to deep_sleep_mode_level1 on any of the three failure paths
(drm_mode_config_helper_suspend() failure, the level1->level2 upgrade
failure, or pm_runtime_force_suspend() failure). Wouldn't that cause all
subsequent runtime PM idle suspends to enter level-2 sleep (0x03) and lose
display RAM?
[ ... ]
> +static int ssd16xx_pm_runtime_resume(struct device *dev)
> +{
> + struct ssd16xx_device *device = dev_get_drvdata(dev);
> + int ret;
> +
> + dev_dbg(dev, "runtime resume: initialized=%d%s\n",
> + device->initialized,
> + !device->initialized ? " (running hw_init)" : "");
> +
> + /*
> + * pm_runtime_suspend clears initialized after sending the deep sleep
> + * command. If a new app opens before the suspend fires (the put was
> + * cancelled), initialized is still true and hw_init is unnecessary.
> + */
> + if (!device->initialized) {
> + ret = ssd16xx_hw_init(device);
> + if (ret)
> + return ret;
> +
> + device->initialized = true;
> + }
> +
> + return 0;
> +}
[Severity: Medium]
When ssd16xx_pm_runtime_resume() runs ssd16xx_hw_init() while the CRTC is
already enabled, does it leave the border waveform register and refresh
state out of sync with the driver state?
ssd16xx_hw_init() issues both ssd16xx_hw_reset() and SSD16XX_CMD_SW_RESET,
which resets SSD16XX_CMD_BORDER_WAVEFORM_CONTROL (0x3C) to its power-on
default without reprogramming it. In ssd16xx_fb_dirty(), register 0x3C is
only written when border_refresh_on_every_update, init_refresh_pending, or
border_waveform_pending is set:
if (device->border_refresh_on_every_update || device->init_refresh_pending ||
device->border_waveform_pending) {
u8 idx = device->border_waveform_update_idx;
u8 border = device->controller_cfg->border_waveform_table[idx];
...
ssd16xx_send_cmd(device, SSD16XX_CMD_BORDER_WAVEFORM_CONTROL, &err);
ssd16xx_send_data(device, border, &err);
device->border_waveform_pending = false;
}
When waking from runtime suspend in ssd16xx_plane_atomic_update(),
ssd16xx_crtc_atomic_enable() does not run, so if
border_refresh_on_every_update is false and the pending flags were already
consumed before autosuspend, register 0x3C is never restored to
border_waveform_update_idx.
Also, on controllers where deep_sleep_mode_level1 is 0x03 (such as SSD1677),
controller RAM is lost during runtime suspend. Without re-arming
init_refresh_pending (or forcing a full refresh), wouldn't the next update
in SSD16XX_REFRESH_PARTIAL mode compare the new BW RAM against lost RED RAM?
Finally, the commit message mentions that level-1 sleep allows fast resume
via SPI wakeup, whereas ssd16xx_pm_runtime_resume() always calls
ssd16xx_hw_init() which performs a full GPIO hardware reset.
[ ... ]
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019213726.4145532-1-devarsht@ti.com?part=7
next prev parent reply other threads:[~2026-10-19 21:58 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-19 21:37 [PATCH v3 00/14] Add DRM driver for Solomon SSD16xx e-paper display controllers Devarsh Thakkar
2026-10-19 21:37 ` [PATCH v3 01/14] dt-bindings: vendor-prefixes: Add Dalian Good Display Co., Ltd Devarsh Thakkar
2026-10-19 21:37 ` [PATCH v3 02/14] dt-bindings: display: Add Solomon SSD16xx e-paper controller Devarsh Thakkar
2026-10-20 8:34 ` Krzysztof Kozlowski
2026-10-19 21:37 ` [PATCH v3 03/14] dt-bindings: display: solomon, ssd16xx: Add Solomon SSD1677 controller Devarsh Thakkar
2026-10-19 21:44 ` [PATCH v3 03/14] dt-bindings: display: solomon,ssd16xx: " sashiko-bot
2026-10-21 3:37 ` Rob Herring (Arm)
2026-10-19 21:37 ` [PATCH v3 04/14] drm/solomon: Add DRM driver for Solomon SSD16xx e-paper display controllers Devarsh Thakkar
2026-10-19 22:02 ` sashiko-bot
2026-10-19 21:37 ` [PATCH v3 05/14] drm/solomon: ssd16xx: Add clear_on_init/close/disable session management Devarsh Thakkar
2026-10-19 21:59 ` sashiko-bot
2026-10-19 21:37 ` [PATCH v3 06/14] drm/solomon: ssd16xx: Add support for Solomon SSD1677 controller Devarsh Thakkar
2026-10-19 21:37 ` [PATCH v3 07/14] drm/solomon: ssd16xx: Add power management support Devarsh Thakkar
2026-10-19 21:57 ` sashiko-bot [this message]
2026-10-19 21:37 ` [PATCH v3 08/14] drm/solomon: ssd16xx: Expose refresh mode as plane property Devarsh Thakkar
2026-10-19 22:02 ` sashiko-bot
2026-10-19 21:37 ` [PATCH v3 09/14] drm/solomon: ssd16xx: Expose color " Devarsh Thakkar
2026-10-19 21:52 ` sashiko-bot
2026-10-19 21:37 ` [PATCH v3 10/14] drm/solomon: ssd16xx: Expose session management as plane properties Devarsh Thakkar
2026-10-19 21:58 ` sashiko-bot
2026-10-20 10:32 ` Markus Elfring
2026-10-19 21:37 ` [PATCH v3 11/14] drm/solomon: ssd16xx: support panels whose RAM X order is reversed Devarsh Thakkar
2026-10-19 21:37 ` [PATCH v3 12/14] MAINTAINERS: Add entry for Solomon SSD16xx DRM driver Devarsh Thakkar
2026-10-19 21:37 ` [PATCH v3 13/14] arm64: defconfig: Enable DRM_SSD16XX for AM62L3 EVM Devarsh Thakkar
2026-10-19 21:37 ` [PATCH v3 14/14] arm64: dts: ti: [DO_NOT_MERGE] Add AM62L3 EVM overlay for GDEY042T81 e-paper display Devarsh Thakkar
2026-10-19 21:59 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-178283@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devarsht@ti.com \
--cc=devicetree@vger.kernel.org \
--cc=dri-devel@lists.freedesktop.org \
--cc=robh@kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox