eink full refresh patch

This commit is contained in:
liquidraver
2026-06-19 11:24:47 +02:00
parent 9f8f13e648
commit 0f98ecec93
3 changed files with 98 additions and 16 deletions
+1 -1
View File
@@ -762,7 +762,7 @@ config ZEPHCORE_DISPLAY_INSET
config ZEPHCORE_DISPLAY_EPD_FULL_REFRESH_INTERVAL
int "E-paper full-refresh interval (partial refreshes)"
default 8
default 16
depends on ZEPHCORE_UI_DISPLAY
help
On e-paper panels, partial refreshes never fully clear the previous
+37 -15
View File
@@ -26,6 +26,19 @@
#include <stdio.h>
#include <limits.h>
/* The SSD16xx EPD driver exposes ssd16xx_clear_red_ram() to reset the
* partial-refresh old-frame reference after a periodic full refresh. It is
* only compiled when an SSD16xx-family panel is present in devicetree. */
#define ZEPHCORE_DISPLAY_HAS_SSD16XX \
(DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1608) || \
DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1673) || \
DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1675a) || \
DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1680) || \
DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1681))
#if ZEPHCORE_DISPLAY_HAS_SSD16XX
#include <zephyr/display/ssd16xx.h>
#endif
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(zephcore_display, CONFIG_ZEPHCORE_BOARD_LOG_LEVEL);
@@ -522,27 +535,36 @@ void mc_display_finalize(void)
if (is_epd && full_interval > 0 && ++epd_partial_count >= (uint32_t)full_interval) {
/* Periodic full refresh to clear accumulated ghosting.
* blanking_on selects the FULL profile and loads RAM without
* updating the panel; cfb_framebuffer_finalize writes the
* current framebuffer into both RAM banks; blanking_off then
* triggers the full-refresh update with that content. */
*
* Replicate the clean post-boot sequence: full-refresh the panel
* to WHITE, then redraw the current page as a partial. blanking_on
* selects the FULL profile; ssd16xx_fill_ram_white clears BOTH RAM
* banks to white; blanking_off runs the full-refresh waveform,
* leaving the panel (and both RAM banks) white. The trailing
* cfb_framebuffer_finalize then redraws the current frame — still
* held in the CFB buffer — as a partial against the all-white
* reference: every content pixel is actively driven and there is
* nothing stale to erase.
*
* Going through white is required. A partial after a full refresh
* of *content* leaves unchanged regions (e.g. the top bar) on the
* neutral waveform → they fade; and forcing the old-frame reference
* white while the panel still shows content fails to erase pixels
* that should clear → letters overlap. The brief white flash is
* the intended clean between frames. */
display_blanking_on(disp_dev);
cfb_framebuffer_finalize(disp_dev);
#if ZEPHCORE_DISPLAY_HAS_SSD16XX
ssd16xx_fill_ram_white(disp_dev);
#endif
display_blanking_off(disp_dev);
/* Prime the partial-refresh reference frame. After a full
* refresh the SSD1681's "old frame" RAM is stale, so the first
* partial diff produces a wrong delta and the partial waveform
* smears static regions (e.g. the top bar). One extra partial
* finalize with the same content lets the controller record the
* post-full-refresh state as its reference; it's a visual no-op
* (zero-diff → no pixel transitions) but fixes subsequent diffs. */
cfb_framebuffer_finalize(disp_dev);
epd_partial_count = 0;
} else {
cfb_framebuffer_finalize(disp_dev);
epd_last_frame_hash = epd_frame_hash;
return;
}
cfb_framebuffer_finalize(disp_dev);
if (is_epd) {
epd_last_frame_hash = epd_frame_hash;
}
@@ -0,0 +1,60 @@
diff --git a/drivers/display/ssd16xx.c b/drivers/display/ssd16xx.c
index 0b9a3559dd5..e7f52c19ec3 100644
--- a/drivers/display/ssd16xx.c
+++ b/drivers/display/ssd16xx.c
@@ -672,6 +672,27 @@ static int ssd16xx_clear_cntlr_mem(const struct device *dev, uint8_t ram_cmd)
return 0;
}
+int ssd16xx_fill_ram_white(const struct device *dev)
+{
+ /*
+ * Clear both RAM banks (Black and Red) to white, identical to the
+ * post-boot ssd16xx_controller_init state, WITHOUT updating the panel.
+ * Used by the ZephCore periodic full refresh: the caller then triggers
+ * a full refresh (panel goes white) and redraws the current page as a
+ * partial against the all-white reference, so every content pixel is
+ * driven and nothing stale is left behind — matching the clean
+ * post-boot first partial. Leaves entry mode at XIYDY; the following
+ * set_profile()/set_window() restore it.
+ */
+ int err = ssd16xx_clear_cntlr_mem(dev, SSD16XX_CMD_WRITE_RAM);
+
+ if (err < 0) {
+ return err;
+ }
+
+ return ssd16xx_clear_cntlr_mem(dev, SSD16XX_CMD_WRITE_RED_RAM);
+}
+
static inline int ssd16xx_load_ws_from_otp_tssv(const struct device *dev)
{
const struct ssd16xx_config *config = dev->config;
diff --git a/include/zephyr/display/ssd16xx.h b/include/zephyr/display/ssd16xx.h
index 53eb05b07ca..7e6690f82e8 100644
--- a/include/zephyr/display/ssd16xx.h
+++ b/include/zephyr/display/ssd16xx.h
@@ -58,6 +58,23 @@ int ssd16xx_read_ram(const struct device *dev, enum ssd16xx_ram ram_type,
const struct display_buffer_descriptor *desc,
void *buf);
+/**
+ * @brief Clear both RAM banks (Black and Red) to white without refreshing.
+ *
+ * Fills the controller's Black and Red RAM with white, matching the state
+ * established by ssd16xx_controller_init() at boot, without updating the
+ * panel. Intended for a full-refresh-to-white followed by a partial redraw:
+ * the all-white reference makes the next partial drive every content pixel
+ * with nothing stale to erase, avoiding both the fade (unchanged regions left
+ * on the neutral waveform) and the overlap (stale pixels not cleared) that
+ * occur when a partial follows a full refresh of non-white content.
+ *
+ * @param dev Pointer to device structure
+ *
+ * @return 0 on success, negative errno on failure.
+ */
+int ssd16xx_fill_ram_white(const struct device *dev);
+
/** @} */
#endif /* ZEPHYR_INCLUDE_DISPLAY_SSD16XX_H_ */