Skip to content
Open
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
3 changes: 3 additions & 0 deletions libs/display/FreeInkDisplay/include/FreeInkDisplay.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ class FreeInkDisplay {
bool supportsBusyGrayscaleStaging() const;
void prepareGrayscaleTarget();
bool supportsStripGrayscale() const;
// True only when the runtime-selected panel driver accepts absolute LUT plane encoding.
bool supportsAbsoluteGrayscale() const;
// True when displayGrayscaleBase() defers the base activation so the gray
// planes join it in one waveform (Paper Mono) - see PanelDriver.
bool combinesGrayscaleBase() const;
Expand Down Expand Up @@ -244,6 +246,7 @@ class FreeInkDisplay {
// EXPERIMENTAL: Windowed update - display only a rectangular region
void displayWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h, bool turnOffScreen = false);
void displayGrayBuffer(bool turnOffScreen = false, const unsigned char* lut = nullptr, bool factoryMode = false);
void displayAbsoluteGrayBuffer(bool turnOffScreen = false);
void displayGrayCalibration(uint16_t customX, uint16_t customY, uint16_t customW, uint16_t customH);

void refreshDisplay(RefreshMode mode = FAST_REFRESH, bool turnOffScreen = false);
Expand Down
17 changes: 17 additions & 0 deletions libs/display/FreeInkDisplay/src/FreeInkDisplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,19 @@ void FreeInkDisplay::displayGrayBuffer(bool turnOffScreen, const unsigned char*
_driver->displayGray(_bus, frameBuffer, turnOffScreen, lut, factoryMode);
}

void FreeInkDisplay::displayAbsoluteGrayBuffer(bool turnOffScreen) {
#if defined(SSD1677_PROBE_DEBUG) && SSD1677_PROBE_DEBUG
Serial.printf("[EPD] displayAbsoluteGrayBuffer\n");
#endif
// Inverted mode deliberately renders a crisp BW page. Writing normal
// grayscale planes afterward would partially undo the output inversion.
if (_inverted) return;
syncPendingAsync();
_shadowValid = false;
_redRamSynced = false; // grayscale leaves RED holding a gray plane, not the BW baseline
_driver->displayGrayAbsolute(_bus, frameBuffer, turnOffScreen);
}

void FreeInkDisplay::displayGrayCalibration(uint16_t customX, uint16_t customY, uint16_t customW, uint16_t customH) {
if (_inverted) return;
syncPendingAsync();
Expand Down Expand Up @@ -859,6 +872,10 @@ bool FreeInkDisplay::supportsStripGrayscale() const {
return !_inverted && _driver && _driver->supportsStripGrayscale();
}

bool FreeInkDisplay::supportsAbsoluteGrayscale() const {
return !_inverted && _driver && _driver->supportsAbsoluteGrayscale();
}

bool FreeInkDisplay::combinesGrayscaleBase() const { return _driver && _driver->combinesGrayscaleBase(); }

void FreeInkDisplay::cleanupGrayscaleBuffers(const uint8_t* bwBuffer) {
Expand Down
9 changes: 9 additions & 0 deletions libs/display/FreeInkDisplay/src/driver/PanelDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ class PanelDriver {

// --- grayscale (dual-plane LSB/MSB) ---
virtual bool supportsStripGrayscale() const { return false; }

// True when this controller accepts absolute selector planes via displayGrayAbsolute()
// This is a runtime capability because one firmware image may include several drivers
// and driver configurations and select the controller during boot.
virtual bool supportsAbsoluteGrayscale() const { return false; }
// True when displayGrayscaleBase() DEFERS the base activation so the gray
// planes join it in a single waveform (Paper Mono). Hosts should then route the
// grayscale base through displayGrayscaleBase() instead of display(): a
Expand Down Expand Up @@ -139,6 +144,10 @@ class PanelDriver {
(void)factoryMode;
display(bus, fb, nullptr, RefreshMode::Fast, turnOff);
}
virtual void displayGrayAbsolute(EpdBus& bus, const uint8_t* fb, bool turnOff)
{
displayGray(bus, fb, turnOff, nullptr, false);
}
// Diagnostic four-gray comparison. The full frame is first rendered with
// the controller's flashing OTP waveform; `custom*` is then rebuilt with the
// driver's non-flashing grayscale path. Default drivers keep the OTP frame.
Expand Down
7 changes: 7 additions & 0 deletions libs/display/FreeInkDisplay/src/driver/Ssd1677Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ const Ssd1677Config& ssd1677DefaultConfig() {
0xC0, // borderWaveformGray: written explicitly with the external AA LUT (vendor
// reference stage 2); same value the B/W paths leave in the register, so
// the wire state is unchanged — just no longer relying on carry-over
false, // grayPowerUpFirst
true, // enableAbsoluteLut
};
return cfg;
}
Expand Down Expand Up @@ -635,6 +637,11 @@ void Ssd1677Driver::displayGray(EpdBus& bus, const uint8_t* fb, bool turnOff, co
setCustomLut(bus, false, nullptr);
}

void Ssd1677Driver::displayGrayAbsolute(EpdBus& bus, const uint8_t* fb, bool turnOff)
{
displayGray(bus, fb, turnOff, lut_factory_quality, true);
}

void Ssd1677Driver::cleanupGrayscaleBuffers(EpdBus& bus, const uint8_t* bw) {
if (!bw) return;
setRamArea(bus, 0, 0, _w, _h);
Expand Down
3 changes: 3 additions & 0 deletions libs/display/FreeInkDisplay/src/driver/Ssd1677Driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ struct Ssd1677Config {
// collapsing toward B/W). The X4 keeps the panel powered between fast
// refreshes, so it never needs this and keeps stock behavior.
bool grayPowerUpFirst = false;
bool enableAbsoluteLut = false;
};

// Standard config (Xteink X4 / GDEQ0426T82). Panel mounting (mirror/180°) is NOT
Expand Down Expand Up @@ -85,11 +86,13 @@ class Ssd1677Driver : public PanelDriver {
void seedPreviousFrame(EpdBus& bus, const uint8_t* buf) override;

bool supportsStripGrayscale() const override { return true; }
bool supportsAbsoluteGrayscale() const override { return _cfg.enableAbsoluteLut; }
void copyGrayscaleLsb(EpdBus& bus, const uint8_t* lsb) override;
void copyGrayscaleMsb(EpdBus& bus, const uint8_t* msb) override;
void writeGrayscalePlaneStrip(EpdBus& bus, GrayPlane plane, const uint8_t* rows, uint16_t yStart,
uint16_t numRows) override;
void displayGray(EpdBus& bus, const uint8_t* fb, bool turnOff, const unsigned char* lut, bool factoryMode) override;
void displayGrayAbsolute(EpdBus& bus, const uint8_t* fb, bool turnOff) override;
void cleanupGrayscaleBuffers(EpdBus& bus, const uint8_t* bw) override;

// No grayscaleRevert override: stock parity — the OEM firmware has no revert
Expand Down