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:57 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:37 ` [PATCH v3 03/14] dt-bindings: display: solomon, ssd16xx: " Devarsh Thakkar
2026-10-19 21:44 ` [PATCH v3 03/14] dt-bindings: display: solomon,ssd16xx: " sashiko-bot
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.