From a124eb9f9e364aa617fc7ee71cb72ede42d1f326 Mon Sep 17 00:00:00 2001
From: Darryldh <info@lowprokb.ca>
Date: Tue, 3 Aug 2021 20:26:58 -0400
Subject: [PATCH] feat(display): IL0323 driver for EPD displays.

* Basic driver, using the GD7965 driver as a basis, since the ICs
  are very similar.
---
 app/drivers/CMakeLists.txt                    |   3 +-
 app/drivers/Kconfig                           |   3 +-
 app/drivers/display/CMakeLists.txt            |   4 +
 app/drivers/display/Kconfig                   |   4 +
 app/drivers/display/Kconfig.il0323            |  11 +
 app/drivers/display/il0323.c                  | 432 ++++++++++++++++++
 app/drivers/display/il0323_regs.h             |  81 ++++
 .../bindings/display/gooddisplay,il0323.yaml  |  61 +++
 8 files changed, 597 insertions(+), 2 deletions(-)
 create mode 100644 app/drivers/display/CMakeLists.txt
 create mode 100644 app/drivers/display/Kconfig
 create mode 100644 app/drivers/display/Kconfig.il0323
 create mode 100644 app/drivers/display/il0323.c
 create mode 100644 app/drivers/display/il0323_regs.h
 create mode 100644 app/dts/bindings/display/gooddisplay,il0323.yaml

diff --git a/app/drivers/CMakeLists.txt b/app/drivers/CMakeLists.txt
index b47e87a2..c1625f2f 100644
--- a/app/drivers/CMakeLists.txt
+++ b/app/drivers/CMakeLists.txt
@@ -2,4 +2,5 @@
 # SPDX-License-Identifier: MIT
 
 add_subdirectory(kscan)
-add_subdirectory(sensor)
\ No newline at end of file
+add_subdirectory(sensor)
+add_subdirectory(display)
\ No newline at end of file
diff --git a/app/drivers/Kconfig b/app/drivers/Kconfig
index 7ad76992..5bcc522d 100644
--- a/app/drivers/Kconfig
+++ b/app/drivers/Kconfig
@@ -2,4 +2,5 @@
 # SPDX-License-Identifier: MIT
 
 rsource "kscan/Kconfig"
-rsource "sensor/Kconfig"
\ No newline at end of file
+rsource "sensor/Kconfig"
+rsource "display/Kconfig"
\ No newline at end of file
diff --git a/app/drivers/display/CMakeLists.txt b/app/drivers/display/CMakeLists.txt
new file mode 100644
index 00000000..13b97193
--- /dev/null
+++ b/app/drivers/display/CMakeLists.txt
@@ -0,0 +1,4 @@
+# Copyright (c) 2021 The ZMK Contributors
+# SPDX-License-Identifier: MIT
+
+zephyr_sources_ifdef(CONFIG_IL0323		il0323.c)
\ No newline at end of file
diff --git a/app/drivers/display/Kconfig b/app/drivers/display/Kconfig
new file mode 100644
index 00000000..efa064d4
--- /dev/null
+++ b/app/drivers/display/Kconfig
@@ -0,0 +1,4 @@
+# Copyright (c) 2021 The ZMK Contributors
+# SPDX-License-Identifier: MIT
+
+rsource "Kconfig.il0323"
\ No newline at end of file
diff --git a/app/drivers/display/Kconfig.il0323 b/app/drivers/display/Kconfig.il0323
new file mode 100644
index 00000000..f39015ef
--- /dev/null
+++ b/app/drivers/display/Kconfig.il0323
@@ -0,0 +1,11 @@
+# Copyright (c) 2020 Phytec Messtechnik GmbH, Peter Johanson
+# SPDX-License-Identifier: Apache-2.0
+
+# IL0323 display controller configuration options
+
+config IL0323
+	bool "IL0323 compatible display controller driver"
+	depends on SPI
+	depends on HEAP_MEM_POOL_SIZE != 0
+	help
+	  Enable driver for IL0323 compatible controller.
\ No newline at end of file
diff --git a/app/drivers/display/il0323.c b/app/drivers/display/il0323.c
new file mode 100644
index 00000000..9ec8162f
--- /dev/null
+++ b/app/drivers/display/il0323.c
@@ -0,0 +1,432 @@
+/*
+ * Copyright (c) 2020 PHYTEC Messtechnik GmbHH, Peter Johanson
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT gooddisplay_il0323
+
+#include <string.h>
+#include <device.h>
+#include <init.h>
+#include <drivers/display.h>
+#include <drivers/gpio.h>
+#include <drivers/spi.h>
+#include <sys/byteorder.h>
+
+#include "il0323_regs.h"
+
+#include <logging/log.h>
+LOG_MODULE_REGISTER(il0323, CONFIG_DISPLAY_LOG_LEVEL);
+
+/**
+ * IL0323 compatible EPD controller driver.
+ *
+ */
+
+#define IL0323_SPI_FREQ DT_INST_PROP(0, spi_max_frequency)
+#define IL0323_BUS_NAME DT_INST_BUS_LABEL(0)
+#define IL0323_DC_PIN DT_INST_GPIO_PIN(0, dc_gpios)
+#define IL0323_DC_FLAGS DT_INST_GPIO_FLAGS(0, dc_gpios)
+#define IL0323_DC_CNTRL DT_INST_GPIO_LABEL(0, dc_gpios)
+#define IL0323_CS_PIN DT_INST_SPI_DEV_CS_GPIOS_PIN(0)
+#define IL0323_CS_FLAGS DT_INST_SPI_DEV_CS_GPIOS_FLAGS(0)
+#if DT_INST_SPI_DEV_HAS_CS_GPIOS(0)
+#define IL0323_CS_CNTRL DT_INST_SPI_DEV_CS_GPIOS_LABEL(0)
+#endif
+#define IL0323_BUSY_PIN DT_INST_GPIO_PIN(0, busy_gpios)
+#define IL0323_BUSY_CNTRL DT_INST_GPIO_LABEL(0, busy_gpios)
+#define IL0323_BUSY_FLAGS DT_INST_GPIO_FLAGS(0, busy_gpios)
+#define IL0323_RESET_PIN DT_INST_GPIO_PIN(0, reset_gpios)
+#define IL0323_RESET_CNTRL DT_INST_GPIO_LABEL(0, reset_gpios)
+#define IL0323_RESET_FLAGS DT_INST_GPIO_FLAGS(0, reset_gpios)
+
+#define EPD_PANEL_WIDTH DT_INST_PROP(0, width)
+#define EPD_PANEL_HEIGHT DT_INST_PROP(0, height)
+#define IL0323_PIXELS_PER_BYTE 8U
+
+/* Horizontally aligned page! */
+#define IL0323_NUMOF_PAGES (EPD_PANEL_WIDTH / IL0323_PIXELS_PER_BYTE)
+#define IL0323_PANEL_FIRST_GATE 0U
+#define IL0323_PANEL_LAST_GATE (EPD_PANEL_HEIGHT - 1)
+#define IL0323_PANEL_FIRST_PAGE 0U
+#define IL0323_PANEL_LAST_PAGE (IL0323_NUMOF_PAGES - 1)
+#define IL0323_BUFFER_SIZE 1280
+
+struct il0323_data {
+    const struct device *reset;
+    const struct device *dc;
+    const struct device *busy;
+    const struct device *spi_dev;
+    struct spi_config spi_config;
+#if defined(IL0323_CS_CNTRL)
+    struct spi_cs_control cs_ctrl;
+#endif
+};
+
+static uint8_t il0323_pwr[] = DT_INST_PROP(0, pwr);
+
+static uint8_t last_buffer[IL0323_BUFFER_SIZE];
+static bool blanking_on = true;
+
+static inline int il0323_write_cmd(struct il0323_data *driver, uint8_t cmd, uint8_t *data,
+                                   size_t len) {
+    struct spi_buf buf = {.buf = &cmd, .len = sizeof(cmd)};
+    struct spi_buf_set buf_set = {.buffers = &buf, .count = 1};
+
+    gpio_pin_set(driver->dc, IL0323_DC_PIN, 1);
+    if (spi_write(driver->spi_dev, &driver->spi_config, &buf_set)) {
+        return -EIO;
+    }
+
+    if (data != NULL) {
+        buf.buf = data;
+        buf.len = len;
+        gpio_pin_set(driver->dc, IL0323_DC_PIN, 0);
+        if (spi_write(driver->spi_dev, &driver->spi_config, &buf_set)) {
+            return -EIO;
+        }
+    }
+
+    return 0;
+}
+
+static inline void il0323_busy_wait(struct il0323_data *driver) {
+    int pin = gpio_pin_get(driver->busy, IL0323_BUSY_PIN);
+
+    while (pin > 0) {
+        __ASSERT(pin >= 0, "Failed to get pin level");
+        // LOG_DBG("wait %u", pin);
+        k_msleep(IL0323_BUSY_DELAY);
+        pin = gpio_pin_get(driver->busy, IL0323_BUSY_PIN);
+    }
+}
+
+static int il0323_update_display(const struct device *dev) {
+    struct il0323_data *driver = dev->data;
+
+    LOG_DBG("Trigger update sequence");
+    if (il0323_write_cmd(driver, IL0323_CMD_DRF, NULL, 0)) {
+        return -EIO;
+    }
+
+    k_msleep(IL0323_BUSY_DELAY);
+
+    return 0;
+}
+
+static int il0323_blanking_off(const struct device *dev) {
+    struct il0323_data *driver = dev->data;
+
+    if (blanking_on) {
+        /* Update EPD pannel in normal mode */
+        il0323_busy_wait(driver);
+        if (il0323_update_display(dev)) {
+            return -EIO;
+        }
+    }
+
+    blanking_on = false;
+
+    return 0;
+}
+
+static int il0323_blanking_on(const struct device *dev) {
+    blanking_on = true;
+
+    return 0;
+}
+
+static int il0323_write(const struct device *dev, const uint16_t x, const uint16_t y,
+                        const struct display_buffer_descriptor *desc, const void *buf) {
+    struct il0323_data *driver = dev->data;
+    uint16_t x_end_idx = x + desc->width - 1;
+    uint16_t y_end_idx = y + desc->height - 1;
+    uint8_t ptl[IL0323_PTL_REG_LENGTH] = {0};
+    size_t buf_len;
+
+    LOG_DBG("x %u, y %u, height %u, width %u, pitch %u", x, y, desc->height, desc->width,
+            desc->pitch);
+
+    buf_len = MIN(desc->buf_size, desc->height * desc->width / IL0323_PIXELS_PER_BYTE);
+    __ASSERT(desc->width <= desc->pitch, "Pitch is smaller then width");
+    __ASSERT(buf != NULL, "Buffer is not available");
+    __ASSERT(buf_len != 0U, "Buffer of length zero");
+    __ASSERT(!(desc->width % IL0323_PIXELS_PER_BYTE), "Buffer width not multiple of %d",
+             IL0323_PIXELS_PER_BYTE);
+
+    LOG_DBG("buf_len %d", buf_len);
+    if ((y_end_idx > (EPD_PANEL_HEIGHT - 1)) || (x_end_idx > (EPD_PANEL_WIDTH - 1))) {
+        LOG_ERR("Position out of bounds");
+        return -EINVAL;
+    }
+
+    /* Setup Partial Window and enable Partial Mode */
+    ptl[IL0323_PTL_HRST_IDX] = x;
+    ptl[IL0323_PTL_HRED_IDX] = x_end_idx;
+    ptl[IL0323_PTL_VRST_IDX] = y;
+    ptl[IL0323_PTL_VRED_IDX] = y_end_idx;
+    ptl[sizeof(ptl) - 1] = IL0323_PTL_PT_SCAN;
+    LOG_HEXDUMP_DBG(ptl, sizeof(ptl), "ptl");
+
+    il0323_busy_wait(driver);
+    if (il0323_write_cmd(driver, IL0323_CMD_PIN, NULL, 0)) {
+        return -EIO;
+    }
+
+    if (il0323_write_cmd(driver, IL0323_CMD_PTL, ptl, sizeof(ptl))) {
+        return -EIO;
+    }
+
+    if (il0323_write_cmd(driver, IL0323_CMD_DTM1, last_buffer, IL0323_BUFFER_SIZE)) {
+        return -EIO;
+    }
+
+    if (il0323_write_cmd(driver, IL0323_CMD_DTM2, (uint8_t *)buf, buf_len)) {
+        return -EIO;
+    }
+
+    memcpy(last_buffer, (uint8_t *)buf, IL0323_BUFFER_SIZE);
+
+    /* Update partial window and disable Partial Mode */
+    if (blanking_on == false) {
+        if (il0323_update_display(dev)) {
+            return -EIO;
+        }
+    }
+
+    if (il0323_write_cmd(driver, IL0323_CMD_POUT, NULL, 0)) {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+static int il0323_read(const struct device *dev, const uint16_t x, const uint16_t y,
+                       const struct display_buffer_descriptor *desc, void *buf) {
+    LOG_ERR("not supported");
+    return -ENOTSUP;
+}
+
+static void *il0323_get_framebuffer(const struct device *dev) {
+    LOG_ERR("not supported");
+    return NULL;
+}
+
+static int il0323_set_brightness(const struct device *dev, const uint8_t brightness) {
+    LOG_WRN("not supported");
+    return -ENOTSUP;
+}
+
+static int il0323_set_contrast(const struct device *dev, uint8_t contrast) {
+    LOG_WRN("not supported");
+    return -ENOTSUP;
+}
+
+static void il0323_get_capabilities(const struct device *dev, struct display_capabilities *caps) {
+    memset(caps, 0, sizeof(struct display_capabilities));
+    caps->x_resolution = EPD_PANEL_WIDTH;
+    caps->y_resolution = EPD_PANEL_HEIGHT;
+    caps->supported_pixel_formats = PIXEL_FORMAT_MONO10;
+    caps->current_pixel_format = PIXEL_FORMAT_MONO10;
+    caps->screen_info = SCREEN_INFO_MONO_MSB_FIRST | SCREEN_INFO_EPD;
+}
+
+static int il0323_set_orientation(const struct device *dev,
+                                  const enum display_orientation orientation) {
+    LOG_ERR("Unsupported");
+    return -ENOTSUP;
+}
+
+static int il0323_set_pixel_format(const struct device *dev, const enum display_pixel_format pf) {
+    if (pf == PIXEL_FORMAT_MONO10) {
+        return 0;
+    }
+
+    LOG_ERR("not supported");
+    return -ENOTSUP;
+}
+
+static int il0323_clear_and_write_buffer(const struct device *dev, uint8_t pattern, bool update) {
+    struct display_buffer_descriptor desc = {
+        .buf_size = IL0323_NUMOF_PAGES,
+        .width = EPD_PANEL_WIDTH,
+        .height = 1,
+        .pitch = EPD_PANEL_WIDTH,
+    };
+    uint8_t *line;
+
+    line = k_malloc(IL0323_NUMOF_PAGES);
+    if (line == NULL) {
+        return -ENOMEM;
+    }
+
+    memset(line, pattern, IL0323_NUMOF_PAGES);
+    for (int i = 0; i < EPD_PANEL_HEIGHT; i++) {
+        il0323_write(dev, 0, i, &desc, line);
+    }
+
+    k_free(line);
+
+    if (update == true) {
+        if (il0323_update_display(dev)) {
+            return -EIO;
+        }
+    }
+
+    return 0;
+}
+
+static int il0323_controller_init(const struct device *dev) {
+    struct il0323_data *driver = dev->data;
+    uint8_t tmp[IL0323_TRES_REG_LENGTH];
+
+    LOG_DBG("");
+
+    gpio_pin_set(driver->reset, IL0323_RESET_PIN, 1);
+    k_msleep(IL0323_RESET_DELAY);
+    gpio_pin_set(driver->reset, IL0323_RESET_PIN, 0);
+    k_msleep(IL0323_RESET_DELAY);
+    il0323_busy_wait(driver);
+
+    LOG_DBG("Initialize IL0323 controller");
+
+    if (il0323_write_cmd(driver, IL0323_CMD_PWR, il0323_pwr, sizeof(il0323_pwr))) {
+        return -EIO;
+    }
+
+    /* Turn on: booster, controller, regulators, and sensor. */
+    if (il0323_write_cmd(driver, IL0323_CMD_PON, NULL, 0)) {
+        return -EIO;
+    }
+
+    k_msleep(IL0323_PON_DELAY);
+    il0323_busy_wait(driver);
+
+    /* Pannel settings, KW mode */
+    tmp[0] = IL0323_PSR_UD | IL0323_PSR_SHL | IL0323_PSR_SHD | IL0323_PSR_RST;
+#if EPD_PANEL_WIDTH == 80
+
+#if EPD_PANEL_HEIGHT == 128
+    tmp[0] |= IL0323_PSR_RES_HEIGHT;
+#endif /* panel height */
+
+#else
+    tmp[0] |= IL0323_PSR_RES_WIDTH;
+#if EPD_PANEL_HEIGHT == 96
+    tmp[0] |= IL0323_PSR_RES_HEIGHT;
+#else
+#endif /* panel height */
+
+#endif /* panel width */
+
+    LOG_HEXDUMP_DBG(tmp, 1, "PSR");
+    if (il0323_write_cmd(driver, IL0323_CMD_PSR, tmp, 1)) {
+        return -EIO;
+    }
+
+    /* Set panel resolution */
+    tmp[IL0323_TRES_HRES_IDX] = EPD_PANEL_WIDTH;
+    tmp[IL0323_TRES_VRES_IDX] = EPD_PANEL_HEIGHT;
+    LOG_HEXDUMP_DBG(tmp, IL0323_TRES_REG_LENGTH, "TRES");
+    if (il0323_write_cmd(driver, IL0323_CMD_TRES, tmp, IL0323_TRES_REG_LENGTH)) {
+        return -EIO;
+    }
+
+    tmp[IL0323_CDI_CDI_IDX] = DT_INST_PROP(0, cdi);
+    LOG_HEXDUMP_DBG(tmp, IL0323_CDI_REG_LENGTH, "CDI");
+    if (il0323_write_cmd(driver, IL0323_CMD_CDI, tmp, IL0323_CDI_REG_LENGTH)) {
+        return -EIO;
+    }
+
+    tmp[0] = DT_INST_PROP(0, tcon);
+    if (il0323_write_cmd(driver, IL0323_CMD_TCON, tmp, 1)) {
+        return -EIO;
+    }
+
+    /* Enable Auto Sequence */
+    tmp[0] = IL0323_AUTO_PON_DRF_POF;
+    if (il0323_write_cmd(driver, IL0323_CMD_AUTO, tmp, 1)) {
+        return -EIO;
+    }
+
+    if (il0323_clear_and_write_buffer(dev, 0xff, false)) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static int il0323_init(const struct device *dev) {
+    struct il0323_data *driver = dev->data;
+
+    LOG_DBG("");
+
+    driver->spi_dev = device_get_binding(IL0323_BUS_NAME);
+    if (driver->spi_dev == NULL) {
+        LOG_ERR("Could not get SPI device for IL0323");
+        return -EIO;
+    }
+
+    driver->spi_config.frequency = IL0323_SPI_FREQ;
+    driver->spi_config.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8);
+    driver->spi_config.slave = DT_INST_REG_ADDR(0);
+    driver->spi_config.cs = NULL;
+
+    driver->reset = device_get_binding(IL0323_RESET_CNTRL);
+    if (driver->reset == NULL) {
+        LOG_ERR("Could not get GPIO port for IL0323 reset");
+        return -EIO;
+    }
+
+    gpio_pin_configure(driver->reset, IL0323_RESET_PIN, GPIO_OUTPUT_INACTIVE | IL0323_RESET_FLAGS);
+
+    driver->dc = device_get_binding(IL0323_DC_CNTRL);
+    if (driver->dc == NULL) {
+        LOG_ERR("Could not get GPIO port for IL0323 DC signal");
+        return -EIO;
+    }
+
+    gpio_pin_configure(driver->dc, IL0323_DC_PIN, GPIO_OUTPUT_INACTIVE | IL0323_DC_FLAGS);
+
+    driver->busy = device_get_binding(IL0323_BUSY_CNTRL);
+    if (driver->busy == NULL) {
+        LOG_ERR("Could not get GPIO port for IL0323 busy signal");
+        return -EIO;
+    }
+
+    gpio_pin_configure(driver->busy, IL0323_BUSY_PIN, GPIO_INPUT | IL0323_BUSY_FLAGS);
+
+#if defined(IL0323_CS_CNTRL)
+    driver->cs_ctrl.gpio_dev = device_get_binding(IL0323_CS_CNTRL);
+    if (!driver->cs_ctrl.gpio_dev) {
+        LOG_ERR("Unable to get SPI GPIO CS device");
+        return -EIO;
+    }
+
+    driver->cs_ctrl.gpio_pin = IL0323_CS_PIN;
+    driver->cs_ctrl.gpio_dt_flags = IL0323_CS_FLAGS;
+    driver->cs_ctrl.delay = 0U;
+    driver->spi_config.cs = &driver->cs_ctrl;
+#endif
+
+    return il0323_controller_init(dev);
+}
+
+static struct il0323_data il0323_driver;
+
+static struct display_driver_api il0323_driver_api = {
+    .blanking_on = il0323_blanking_on,
+    .blanking_off = il0323_blanking_off,
+    .write = il0323_write,
+    .read = il0323_read,
+    .get_framebuffer = il0323_get_framebuffer,
+    .set_brightness = il0323_set_brightness,
+    .set_contrast = il0323_set_contrast,
+    .get_capabilities = il0323_get_capabilities,
+    .set_pixel_format = il0323_set_pixel_format,
+    .set_orientation = il0323_set_orientation,
+};
+
+DEVICE_DT_INST_DEFINE(0, il0323_init, device_pm_control_nop, &il0323_driver, NULL, POST_KERNEL,
+                      CONFIG_APPLICATION_INIT_PRIORITY, &il0323_driver_api);
\ No newline at end of file
diff --git a/app/drivers/display/il0323_regs.h b/app/drivers/display/il0323_regs.h
new file mode 100644
index 00000000..3eb19755
--- /dev/null
+++ b/app/drivers/display/il0323_regs.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 PHYTEC Messtechnik GmbH, Peter Johanson
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef ZEPHYR_DRIVERS_DISPLAY_IL0323_REGS_H_
+#define ZEPHYR_DRIVERS_DISPLAY_IL0323_REGS_H_
+
+#define IL0323_CMD_PSR 0x00
+#define IL0323_CMD_PWR 0x01
+#define IL0323_CMD_POF 0x02
+#define IL0323_CMD_PFS 0x03
+#define IL0323_CMD_PON 0x04
+#define IL0323_CMD_PMES 0x05
+#define IL0323_CMD_CPSET 0x06
+#define IL0323_CMD_DSLP 0x07
+#define IL0323_CMD_DTM1 0x10
+#define IL0323_CMD_DSP 0x11
+#define IL0323_CMD_DRF 0x12
+#define IL0323_CMD_DTM2 0x13
+#define IL0323_CMD_AUTO 0x17
+#define IL0323_CMD_LUTOPT 0x2A
+#define IL0323_CMD_PLL 0x30
+#define IL0323_CMD_TSC 0x40
+#define IL0323_CMD_TSE 0x41
+#define IL0323_CMD_PBC 0x44
+#define IL0323_CMD_CDI 0x50
+#define IL0323_CMD_LPD 0x51
+#define IL0323_CMD_TCON 0x60
+#define IL0323_CMD_TRES 0x61
+#define IL0323_CMD_GSST 0x65
+#define IL0323_CMD_REV 0x70
+#define IL0323_CMD_FLG 0x71
+#define IL0323_CMD_CRC 0x72
+#define IL0323_CMD_AMV 0x80
+#define IL0323_CMD_VV 0x81
+#define IL0323_CMD_VDCS 0x82
+#define IL0323_CMD_PTL 0x90
+#define IL0323_CMD_PIN 0x91
+#define IL0323_CMD_POUT 0x92
+#define IL0323_CMD_PGM 0xA0
+#define IL0323_CMD_APG 0xA1
+#define IL0323_CMD_ROTP 0xA2
+#define IL0323_CMD_CCSET 0xE0
+#define IL0323_CMD_PWS 0xE3
+#define IL0323_CMD_LVSEL 0xE4
+#define IL0323_CMD_TSSET 0xE5
+
+#define IL0323_PSR_RES_WIDTH BIT(7)
+#define IL0323_PSR_RES_HEIGHT BIT(6)
+#define IL0323_PSR_LUT_REG BIT(5)
+#define IL0323_PSR_LUT_OTP BIT(4)
+#define IL0323_PSR_UD BIT(3)
+#define IL0323_PSR_SHL BIT(2)
+#define IL0323_PSR_SHD BIT(1)
+#define IL0323_PSR_RST BIT(0)
+
+#define IL0323_AUTO_PON_DRF_POF 0xA5
+#define IL0323_AUTO_PON_DRF_POF_DSLP 0xA7
+
+#define IL0323_CDI_REG_LENGTH 1U
+#define IL0323_CDI_CDI_IDX 0
+
+#define IL0323_TRES_REG_LENGTH 2U
+#define IL0323_TRES_HRES_IDX 0
+#define IL0323_TRES_VRES_IDX 1
+
+#define IL0323_PTL_REG_LENGTH 5U
+#define IL0323_PTL_HRST_IDX 0
+#define IL0323_PTL_HRED_IDX 1
+#define IL0323_PTL_VRST_IDX 2
+#define IL0323_PTL_VRED_IDX 3
+#define IL0323_PTL_PT_SCAN BIT(0)
+
+/* Time constants in ms */
+#define IL0323_RESET_DELAY 10U
+#define IL0323_PON_DELAY 100U
+#define IL0323_BUSY_DELAY 1U
+
+#endif /* ZEPHYR_DRIVERS_DISPLAY_IL0323_REGS_H_ */
\ No newline at end of file
diff --git a/app/dts/bindings/display/gooddisplay,il0323.yaml b/app/dts/bindings/display/gooddisplay,il0323.yaml
new file mode 100644
index 00000000..d4a9ac7d
--- /dev/null
+++ b/app/dts/bindings/display/gooddisplay,il0323.yaml
@@ -0,0 +1,61 @@
+# Copyright (c) 2020, Phytec Messtechnik GmbH, Peter Johanson
+# SPDX-License-Identifier: Apache-2.0
+
+description: IL0323 EPD display controller
+
+compatible: "gooddisplay,il0323"
+
+include: spi-device.yaml
+
+properties:
+    height:
+      type: int
+      required: true
+      description: Height in pixel of the panel driven by the controller
+
+    width:
+      type: int
+      required: true
+      description: Width in pixel of the panel driven by the controller
+
+    reset-gpios:
+      type: phandle-array
+      required: true
+      description: RESET pin.
+
+        The RESET pin of GD7965 is active low.
+        If connected directly the MCU pin should be configured
+        as active low.
+
+    dc-gpios:
+      type: phandle-array
+      required: true
+      description: DC pin.
+
+        The DC pin of GD7965 is active low (transmission command byte).
+        If connected directly the MCU pin should be configured
+        as active low.
+
+    busy-gpios:
+      type: phandle-array
+      required: true
+      description: BUSY pin.
+
+        The BUSY pin of GD7965 is active low.
+        If connected directly the MCU pin should be configured
+        as active low.
+
+    pwr:
+      type: uint8-array
+      required: true
+      description: Power Setting (PWR) values
+
+    cdi:
+      type: int
+      required: true
+      description: VCOM and data interval value
+
+    tcon:
+      type: int
+      required: true
+      description: TCON setting value
\ No newline at end of file