Add infrastructure to support the Solomon SSD1677 e-paper controller
(up to 960x680 pixels, 10-bit pixel-level X/Y RAM addressing).

The SSD1677 shares the same command address space as SSD1683 but
differs in several controller-level characteristics:

  - RAM X/Y addressing: SSD1677 uses 10-bit pixel-level addresses
    whereas SSD1683 uses 6-bit byte-level X and 9-bit Y.  The
    existing ram_x_address_bits / ram_y_address_bits fields in
    ssd16xx_controller_config already handle the different format;
    ssd16xx_fb_dirty is updated to compute the correct end address
    from device->width using the addressing model:
      byte-addressed: XEnd = panel_width/8 - 1
      pixel-addressed: XEnd = panel_width - 1

  - Deep sleep: only mode 0x03 is documented in SSD1677 (no level-1
    RAM-retain mode 0x01 as in SSD1683).  Both level1/level2 point
    to SSD1683_DEEP_SLEEP_MODE_2 (0x03).

  - ctrl2 full refresh uses 0xF4 (omits DISABLE_ANALOG/DISABLE_CLK
    bits) per the reference implementation.  0xFF for partial refresh
    is listed as valid in the SSD1677 datasheet; BIT(3) semantics are
    undocumented — included pending hardware verification.

  - Border waveform byte encoding is identical to SSD1683; the same
    ssd1683_border_waveform_table[] is reused.

A new booster_soft_start_data / booster_soft_start_len field is added
to ssd16xx_device_config for panels that require BOOSTER_SOFT_START
(0x0C) initialisation.  The command exists in both SSD1683 and SSD1677
but the tuning values are board/panel-specific; panels that do not need
it set the pointer to NULL.  hw_init sends the command when non-NULL.

Controller limit validation is added in probe: panel dimensions must
not exceed the controller hardware maximum, and for byte-addressed X
controllers the panel width must be a multiple of 8.

Driver Output Control MUX, tx_buf allocation, and fb_dirty RAM window
end addresses now use the actual panel dimensions rather than the
controller maximum, correctly handling panels smaller than the
controller's rated capacity.

Co-developed-by: LiangCheng Wang <[email protected]>
Signed-off-by: Devarsh Thakkar <[email protected]>
---
Changes from v1:
  - New patch: Extracted SSD1677 controller support into dedicated patch

 drivers/gpu/drm/solomon/ssd16xx.c | 78 +++++++++++++++++++++++++++----
 1 file changed, 68 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/solomon/ssd16xx.c 
b/drivers/gpu/drm/solomon/ssd16xx.c
index c478309d08e9..93fa064cf589 100644
--- a/drivers/gpu/drm/solomon/ssd16xx.c
+++ b/drivers/gpu/drm/solomon/ssd16xx.c
@@ -39,6 +39,7 @@
 
 /* SPI command codes (common) */
 #define SSD16XX_CMD_DRIVER_OUTPUT_CONTROL              0x01
+#define SSD16XX_CMD_BOOSTER_SOFT_START                 0x0C
 #define SSD16XX_CMD_DATA_ENTRY_MODE                    0x11
 #define SSD16XX_CMD_SW_RESET                           0x12
 #define SSD16XX_CMD_MASTER_ACTIVATION                  0x20
@@ -181,6 +182,7 @@
 
 enum ssd16xx_controller {
        SSD1683 = 1,
+       SSD1677,
 };
 
 enum ssd16xx_model {
@@ -332,6 +334,10 @@ struct ssd16xx_device_config {
         */
        enum ssd16xx_color_mode default_color_mode;
 
+       /* Optional BOOSTER_SOFT_START (0x0C) tuning bytes; NULL if unused. */
+       const u8 *booster_soft_start_data;
+       u8        booster_soft_start_len;
+
        /* Panel-specific display mode (resolution and physical dimensions) */
        const struct drm_display_mode *mode;
 };
@@ -420,6 +426,29 @@ static const struct ssd16xx_controller_config 
ssd16xx_controller_configs[] = {
                },
                .ctrl2_load_temp_lut     = SSD1683_CTRL2_LOAD_TEMP_LUT,
        },
+       [SSD1677] = {
+               /*
+                * 10-bit pixel-level X/Y addressing; only deep sleep mode
+                * 0x03 is supported (no RAM-retain mode). Border waveform
+                * encoding matches SSD1683.
+                */
+               .max_width  = 960,
+               .max_height = 680,
+               .ram_x_address_bits = 10,
+               .ram_y_address_bits = 10,
+               .has_temp_sensor_ctrl    = true,
+               .deep_sleep_mode_level1  = SSD1683_DEEP_SLEEP_MODE_2,  /* 0x03 
*/
+               .deep_sleep_mode_level2  = SSD1683_DEEP_SLEEP_MODE_2,  /* 0x03 
*/
+               .border_waveform_table   = ssd1683_border_waveform_table,
+               .ctrl1_normal            = SSD1683_CTRL1_NORMAL,
+               .ctrl1_bypass_red_ram    = SSD1683_CTRL1_BYPASS_RED_RAM,
+               .ctrl2_refresh = {
+                       [SSD16XX_REFRESH_PARTIAL] = 0xFF,
+                       [SSD16XX_REFRESH_FULL]    = 0xF4,
+                       [SSD16XX_REFRESH_FAST]    = SSD1683_CTRL2_FAST_REFRESH,
+               },
+               .ctrl2_load_temp_lut     = SSD1683_CTRL2_LOAD_TEMP_LUT,
+       },
 };
 
 /* GDEY042T81: 4.2" 400x300 panel, 84.8x63.6mm active area */
@@ -553,28 +582,44 @@ static void ssd16xx_send_data(struct ssd16xx_device 
*device, u8 data,
 static void ssd16xx_send_x_param(struct ssd16xx_device *device, u16 x,
                                 int *err)
 {
+       u8 bits = device->controller_cfg->ram_x_address_bits;
+       u16 max_val = (1U << bits) - 1;
+
        if (*err)
                return;
 
-       if (device->controller_cfg->ram_x_address_bits == 8) {
+       if (x > max_val)
+               x = max_val;
+
+       if (bits <= 8) {
                ssd16xx_send_data(device, (u8)x, err);
        } else {
+               u8 hi_mask = (1U << (bits - 8)) - 1;
+
                ssd16xx_send_data(device, x & 0xFF, err);
-               ssd16xx_send_data(device, (x >> 8) & 0xFF, err);
+               ssd16xx_send_data(device, (x >> 8) & hi_mask, err);
        }
 }
 
 static void ssd16xx_send_y_param(struct ssd16xx_device *device, u16 y,
                                 int *err)
 {
+       u8 bits = device->controller_cfg->ram_y_address_bits;
+       u16 max_val = (1U << bits) - 1;
+
        if (*err)
                return;
 
-       if (device->controller_cfg->ram_y_address_bits == 8) {
+       if (y > max_val)
+               y = max_val;
+
+       if (bits <= 8) {
                ssd16xx_send_data(device, (u8)y, err);
        } else {
+               u8 hi_mask = (1U << (bits - 8)) - 1;
+
                ssd16xx_send_data(device, y & 0xFF, err);
-               ssd16xx_send_data(device, (y >> 8) & 0xFF, err);
+               ssd16xx_send_data(device, (y >> 8) & hi_mask, err);
        }
 }
 
@@ -691,21 +736,34 @@ static int ssd16xx_hw_init(struct ssd16xx_device *device)
 
        ssd16xx_hw_reset(device);
 
-       /* Software reset */
+       /* Software reset (0x12): resets command/parameter registers to 
defaults. */
        ssd16xx_send_cmd(device, SSD16XX_CMD_SW_RESET, &err);
        ssd16xx_wait_for_device(device, &err);
 
-       /* Driver output control (0x01): MUX ratio and scan direction. */
-       ssd16xx_send_cmd(device, SSD16XX_CMD_DRIVER_OUTPUT_CONTROL, &err);
-       ssd16xx_send_y_param(device, device->height - 1, &err);
-       ssd16xx_send_data(device, device->device_cfg->driver_output_ctrl_byte3, 
&err);
-
        /* Internal temperature sensor (SSD1683/SSD1680 only; not present in 
SSD1673) */
        if (device->controller_cfg->has_temp_sensor_ctrl) {
                ssd16xx_send_cmd(device, 
SSD1683_CMD_TEMPERATURE_SENSOR_CONTROL, &err);
                ssd16xx_send_data(device, SSD1683_TEMP_SENSOR_INTERNAL, &err);
        }
 
+       /*
+        * Booster soft-start (0x0C): panel-specific charge pump tuning.
+        * Some panels (e.g. PIXPAPER 4.26m on SSD1677) require this step;
+        * others (e.g. GDEY042T81 on SSD1683) omit it.
+        */
+       if (device->device_cfg->booster_soft_start_data) {
+               ssd16xx_send_cmd(device, SSD16XX_CMD_BOOSTER_SOFT_START, &err);
+               ssd16xx_send_data_bulk(device,
+                                      
device->device_cfg->booster_soft_start_data,
+                                      
device->device_cfg->booster_soft_start_len,
+                                      &err);
+       }
+
+       /* Driver output control (0x01): MUX ratio and scan direction. */
+       ssd16xx_send_cmd(device, SSD16XX_CMD_DRIVER_OUTPUT_CONTROL, &err);
+       ssd16xx_send_y_param(device, device->height - 1, &err);
+       ssd16xx_send_data(device, device->device_cfg->driver_output_ctrl_byte3, 
&err);
+
        /*
         * For FAST refresh mode, pre-load the LUT once here during 
initialization.
         * FAST mode ctrl2 (0xC7) omits LOAD_LUT on every update for speed, so 
the
-- 
2.39.1

Reply via email to