| /* |
| * Copyright (c) 2018-2020 PHYTEC Messtechnik GmbH |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT solomon_ssd16xxfb |
| |
| #define LOG_LEVEL CONFIG_DISPLAY_LOG_LEVEL |
| #include <logging/log.h> |
| LOG_MODULE_REGISTER(ssd16xx); |
| |
| #include <string.h> |
| #include <device.h> |
| #include <drivers/display.h> |
| #include <init.h> |
| #include <drivers/gpio.h> |
| #include <drivers/spi.h> |
| #include <sys/byteorder.h> |
| |
| #include "ssd16xx_regs.h" |
| #include <display/cfb.h> |
| |
| /** |
| * SSD1673, SSD1608, SSD1681, ILI3897 compatible EPD controller driver. |
| */ |
| |
| #define SSD16XX_DC_PIN DT_INST_GPIO_PIN(0, dc_gpios) |
| #define SSD16XX_DC_FLAGS DT_INST_GPIO_FLAGS(0, dc_gpios) |
| #define SSD16XX_DC_CNTRL DT_INST_GPIO_LABEL(0, dc_gpios) |
| #define SSD16XX_BUSY_PIN DT_INST_GPIO_PIN(0, busy_gpios) |
| #define SSD16XX_BUSY_CNTRL DT_INST_GPIO_LABEL(0, busy_gpios) |
| #define SSD16XX_BUSY_FLAGS DT_INST_GPIO_FLAGS(0, busy_gpios) |
| #define SSD16XX_RESET_PIN DT_INST_GPIO_PIN(0, reset_gpios) |
| #define SSD16XX_RESET_CNTRL DT_INST_GPIO_LABEL(0, reset_gpios) |
| #define SSD16XX_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 EPD_PANEL_NUMOF_COLUMS EPD_PANEL_WIDTH |
| #define EPD_PANEL_NUMOF_ROWS_PER_PAGE 8 |
| #define EPD_PANEL_NUMOF_PAGES (EPD_PANEL_HEIGHT / \ |
| EPD_PANEL_NUMOF_ROWS_PER_PAGE) |
| |
| #define SSD16XX_PANEL_FIRST_PAGE 0 |
| #define SSD16XX_PANEL_LAST_PAGE (EPD_PANEL_NUMOF_PAGES - 1) |
| #define SSD16XX_PANEL_FIRST_GATE 0 |
| #define SSD16XX_PANEL_LAST_GATE (EPD_PANEL_NUMOF_COLUMS - 1) |
| |
| #define SSD16XX_PIXELS_PER_BYTE 8 |
| #define SSD16XX_DEFAULT_TR_VALUE 25U |
| #define SSD16XX_TR_SCALE_FACTOR 256U |
| |
| struct ssd16xx_data { |
| const struct device *reset; |
| const struct device *dc; |
| const struct device *busy; |
| const struct ssd16xx_config *config; |
| uint8_t scan_mode; |
| uint8_t update_cmd; |
| }; |
| |
| struct ssd16xx_config { |
| struct spi_dt_spec bus; |
| }; |
| |
| #if DT_INST_NODE_HAS_PROP(0, lut_initial) |
| static uint8_t ssd16xx_lut_initial[] = DT_INST_PROP(0, lut_initial); |
| #endif |
| #if DT_INST_NODE_HAS_PROP(0, lut_default) |
| static uint8_t ssd16xx_lut_default[] = DT_INST_PROP(0, lut_default); |
| #endif |
| #if DT_INST_NODE_HAS_PROP(0, softstart) |
| static uint8_t ssd16xx_softstart[] = DT_INST_PROP(0, softstart); |
| #endif |
| static uint8_t ssd16xx_gdv[] = DT_INST_PROP(0, gdv); |
| static uint8_t ssd16xx_sdv[] = DT_INST_PROP(0, sdv); |
| |
| static inline int ssd16xx_write_cmd(struct ssd16xx_data *driver, |
| uint8_t cmd, uint8_t *data, size_t len) |
| { |
| int err; |
| struct spi_buf buf = {.buf = &cmd, .len = sizeof(cmd)}; |
| struct spi_buf_set buf_set = {.buffers = &buf, .count = 1}; |
| |
| gpio_pin_set(driver->dc, SSD16XX_DC_PIN, 1); |
| err = spi_write_dt(&driver->config->bus, &buf_set); |
| if (err < 0) { |
| return err; |
| } |
| |
| if (data != NULL) { |
| buf.buf = data; |
| buf.len = len; |
| gpio_pin_set(driver->dc, SSD16XX_DC_PIN, 0); |
| err = spi_write_dt(&driver->config->bus, &buf_set); |
| if (err < 0) { |
| return err; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static inline void ssd16xx_busy_wait(struct ssd16xx_data *driver) |
| { |
| int pin = gpio_pin_get(driver->busy, SSD16XX_BUSY_PIN); |
| |
| while (pin > 0) { |
| __ASSERT(pin >= 0, "Failed to get pin level"); |
| k_msleep(SSD16XX_BUSY_DELAY); |
| pin = gpio_pin_get(driver->busy, SSD16XX_BUSY_PIN); |
| } |
| } |
| |
| static inline size_t push_x_param(uint8_t *data, uint16_t x) |
| { |
| #if DT_INST_PROP(0, pp_width_bits) == 8 |
| data[0] = (uint8_t)x; |
| return 1; |
| #elif DT_INST_PROP(0, pp_width_bits) == 16 |
| sys_put_le16(sys_cpu_to_le16(x), data); |
| return 2; |
| #else |
| #error Unsupported pp_width_bits value for solomon,ssd16xxfb DTS instance 0 |
| #endif |
| } |
| |
| static inline size_t push_y_param(uint8_t *data, uint16_t y) |
| { |
| #if DT_INST_PROP(0, pp_height_bits) == 8 |
| data[0] = (uint8_t)y; |
| return 1; |
| #elif DT_INST_PROP(0, pp_height_bits) == 16 |
| sys_put_le16(sys_cpu_to_le16(y), data); |
| return 2; |
| #else |
| #error Unsupported pp_height_bits value for solomon,ssd16xxfb DTS instance 0 |
| #endif |
| } |
| |
| |
| static inline int ssd16xx_set_ram_param(struct ssd16xx_data *driver, |
| uint16_t sx, uint16_t ex, uint16_t sy, uint16_t ey) |
| { |
| int err; |
| uint8_t tmp[4]; |
| size_t len; |
| |
| len = push_x_param(tmp, sx); |
| len += push_x_param(tmp + len, ex); |
| err = ssd16xx_write_cmd(driver, SSD16XX_CMD_RAM_XPOS_CTRL, tmp, len); |
| if (err < 0) { |
| return err; |
| } |
| |
| len = push_y_param(tmp, sy); |
| len += push_y_param(tmp + len, ey); |
| err = ssd16xx_write_cmd(driver, SSD16XX_CMD_RAM_YPOS_CTRL, tmp, len); |
| if (err < 0) { |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| static inline int ssd16xx_set_ram_ptr(struct ssd16xx_data *driver, |
| uint16_t x, uint16_t y) |
| { |
| int err; |
| uint8_t tmp[2]; |
| size_t len; |
| |
| len = push_x_param(tmp, x); |
| err = ssd16xx_write_cmd(driver, SSD16XX_CMD_RAM_XPOS_CNTR, tmp, len); |
| if (err < 0) { |
| return err; |
| } |
| |
| len = push_y_param(tmp, y); |
| return ssd16xx_write_cmd(driver, SSD16XX_CMD_RAM_YPOS_CNTR, tmp, len); |
| } |
| |
| static void ssd16xx_set_orientation_internall(struct ssd16xx_data *driver) |
| |
| { |
| #if DT_INST_PROP(0, orientation_flipped) == 1 |
| driver->scan_mode = SSD16XX_DATA_ENTRY_XIYDY; |
| #else |
| driver->scan_mode = SSD16XX_DATA_ENTRY_XDYIY; |
| #endif |
| } |
| |
| static int ssd16xx_blanking_off(const struct device *dev) |
| { |
| return -ENOTSUP; |
| } |
| |
| static int ssd16xx_blanking_on(const struct device *dev) |
| { |
| return -ENOTSUP; |
| } |
| |
| static int ssd16xx_update_display(const struct device *dev) |
| { |
| struct ssd16xx_data *driver = dev->data; |
| int err; |
| |
| err = ssd16xx_write_cmd(driver, SSD16XX_CMD_UPDATE_CTRL2, |
| &driver->update_cmd, 1); |
| if (err < 0) { |
| return err; |
| } |
| |
| return ssd16xx_write_cmd(driver, SSD16XX_CMD_MASTER_ACTIVATION, |
| NULL, 0); |
| } |
| |
| static int ssd16xx_write(const struct device *dev, const uint16_t x, |
| const uint16_t y, |
| const struct display_buffer_descriptor *desc, |
| const void *buf) |
| { |
| struct ssd16xx_data *driver = dev->data; |
| int err; |
| size_t buf_len; |
| uint16_t x_start; |
| uint16_t x_end; |
| uint16_t y_start; |
| uint16_t y_end; |
| uint16_t panel_h = EPD_PANEL_HEIGHT - |
| EPD_PANEL_HEIGHT % EPD_PANEL_NUMOF_ROWS_PER_PAGE; |
| |
| if (desc->pitch < desc->width) { |
| LOG_ERR("Pitch is smaller than width"); |
| return -EINVAL; |
| } |
| |
| buf_len = MIN(desc->buf_size, desc->height * desc->width / 8); |
| if (buf == NULL || buf_len == 0U) { |
| LOG_ERR("Display buffer is not available"); |
| return -EINVAL; |
| } |
| |
| if (desc->pitch > desc->width) { |
| LOG_ERR("Unsupported mode"); |
| return -ENOTSUP; |
| } |
| |
| if ((y + desc->height) > panel_h) { |
| LOG_ERR("Buffer out of bounds (height)"); |
| return -EINVAL; |
| } |
| |
| if ((x + desc->width) > EPD_PANEL_WIDTH) { |
| LOG_ERR("Buffer out of bounds (width)"); |
| return -EINVAL; |
| } |
| |
| if ((desc->height % EPD_PANEL_NUMOF_ROWS_PER_PAGE) != 0U) { |
| LOG_ERR("Buffer height not multiple of %d", |
| EPD_PANEL_NUMOF_ROWS_PER_PAGE); |
| return -EINVAL; |
| } |
| |
| if ((y % EPD_PANEL_NUMOF_ROWS_PER_PAGE) != 0U) { |
| LOG_ERR("Y coordinate not multiple of %d", |
| EPD_PANEL_NUMOF_ROWS_PER_PAGE); |
| return -EINVAL; |
| } |
| |
| switch (driver->scan_mode) { |
| case SSD16XX_DATA_ENTRY_XIYDY: |
| x_start = y / SSD16XX_PIXELS_PER_BYTE; |
| x_end = (y + desc->height - 1) / SSD16XX_PIXELS_PER_BYTE; |
| y_start = (x + desc->width - 1); |
| y_end = x; |
| break; |
| |
| case SSD16XX_DATA_ENTRY_XDYIY: |
| x_start = (panel_h - 1 - y) / SSD16XX_PIXELS_PER_BYTE; |
| x_end = (panel_h - 1 - (y + desc->height - 1)) / |
| SSD16XX_PIXELS_PER_BYTE; |
| y_start = x; |
| y_end = (x + desc->width - 1); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| ssd16xx_busy_wait(driver); |
| |
| err = ssd16xx_write_cmd(driver, SSD16XX_CMD_ENTRY_MODE, |
| &driver->scan_mode, sizeof(driver->scan_mode)); |
| if (err < 0) { |
| return err; |
| } |
| |
| err = ssd16xx_set_ram_param(driver, x_start, x_end, y_start, y_end); |
| if (err < 0) { |
| return err; |
| } |
| |
| err = ssd16xx_set_ram_ptr(driver, x_start, y_start); |
| if (err < 0) { |
| return err; |
| } |
| |
| err = ssd16xx_write_cmd(driver, SSD16XX_CMD_WRITE_RAM, (uint8_t *)buf, |
| buf_len); |
| if (err < 0) { |
| return err; |
| } |
| |
| return ssd16xx_update_display(dev); |
| } |
| |
| static int ssd16xx_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 *ssd16xx_get_framebuffer(const struct device *dev) |
| { |
| LOG_ERR("not supported"); |
| return NULL; |
| } |
| |
| static int ssd16xx_set_brightness(const struct device *dev, |
| const uint8_t brightness) |
| { |
| LOG_WRN("not supported"); |
| return -ENOTSUP; |
| } |
| |
| static int ssd16xx_set_contrast(const struct device *dev, uint8_t contrast) |
| { |
| LOG_WRN("not supported"); |
| return -ENOTSUP; |
| } |
| |
| static void ssd16xx_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 - |
| EPD_PANEL_HEIGHT % EPD_PANEL_NUMOF_ROWS_PER_PAGE; |
| caps->supported_pixel_formats = PIXEL_FORMAT_MONO10; |
| caps->current_pixel_format = PIXEL_FORMAT_MONO10; |
| caps->screen_info = SCREEN_INFO_MONO_VTILED | |
| SCREEN_INFO_MONO_MSB_FIRST | |
| SCREEN_INFO_EPD | |
| SCREEN_INFO_DOUBLE_BUFFER; |
| } |
| |
| static int ssd16xx_set_orientation(const struct device *dev, |
| const enum display_orientation |
| orientation) |
| { |
| LOG_ERR("Unsupported"); |
| return -ENOTSUP; |
| } |
| |
| static int ssd16xx_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 ssd16xx_clear_cntlr_mem(const struct device *dev, uint8_t ram_cmd, |
| bool update) |
| { |
| struct ssd16xx_data *driver = dev->data; |
| uint8_t clear_page[EPD_PANEL_WIDTH]; |
| uint16_t panel_h = EPD_PANEL_HEIGHT / |
| EPD_PANEL_NUMOF_ROWS_PER_PAGE; |
| uint8_t scan_mode = SSD16XX_DATA_ENTRY_XIYDY; |
| |
| /* |
| * Clear unusable memory area when the resolution of the panel is not |
| * multiple of an octet. |
| */ |
| if (EPD_PANEL_HEIGHT % EPD_PANEL_NUMOF_ROWS_PER_PAGE) { |
| panel_h += 1; |
| } |
| |
| if (ssd16xx_write_cmd(driver, SSD16XX_CMD_ENTRY_MODE, &scan_mode, 1)) { |
| return -EIO; |
| } |
| |
| if (ssd16xx_set_ram_param(driver, SSD16XX_PANEL_FIRST_PAGE, |
| panel_h - 1, |
| SSD16XX_PANEL_LAST_GATE, |
| SSD16XX_PANEL_FIRST_GATE)) { |
| return -EIO; |
| } |
| |
| if (ssd16xx_set_ram_ptr(driver, SSD16XX_PANEL_FIRST_PAGE, |
| SSD16XX_PANEL_LAST_GATE)) { |
| return -EIO; |
| } |
| |
| |
| memset(clear_page, 0xff, sizeof(clear_page)); |
| for (int i = 0; i < panel_h; i++) { |
| if (ssd16xx_write_cmd(driver, ram_cmd, clear_page, |
| sizeof(clear_page))) { |
| return -EIO; |
| } |
| } |
| |
| if (update) { |
| return ssd16xx_update_display(dev); |
| } |
| |
| return 0; |
| } |
| |
| static inline int ssd16xx_load_ws_from_otp(const struct device *dev) |
| { |
| struct ssd16xx_data *driver = dev->data; |
| uint8_t tmp[2]; |
| |
| #if DT_INST_NODE_HAS_PROP(0, tssv) |
| /* |
| * Controller has an integrated temperature sensor or external |
| * temperature sensor is connected to the controller. |
| */ |
| LOG_INF("Select and load WS from OTP"); |
| |
| tmp[0] = DT_INST_PROP(0, tssv); |
| if (ssd16xx_write_cmd(driver, |
| SSD16XX_CMD_TSENSOR_SELECTION, |
| tmp, 1)) { |
| return -EIO; |
| } |
| |
| driver->update_cmd |= SSD16XX_CTRL2_LOAD_LUT | |
| SSD16XX_CTRL2_LOAD_TEMPERATURE; |
| |
| return 0; |
| #else |
| int16_t t = (SSD16XX_DEFAULT_TR_VALUE * SSD16XX_TR_SCALE_FACTOR); |
| |
| LOG_INF("Load default WS (25 degrees Celsius) from OTP"); |
| |
| tmp[0] = SSD16XX_CTRL2_ENABLE_CLK; |
| if (ssd16xx_write_cmd(driver, SSD16XX_CMD_UPDATE_CTRL2, |
| tmp, 1)) { |
| return -EIO; |
| } |
| |
| if (ssd16xx_write_cmd(driver, SSD16XX_CMD_MASTER_ACTIVATION, |
| NULL, 0)) { |
| return -EIO; |
| } |
| |
| ssd16xx_busy_wait(driver); |
| |
| /* Load temperature value */ |
| sys_put_be16(t, tmp); |
| if (ssd16xx_write_cmd(driver, SSD16XX_CMD_TSENS_CTRL, |
| tmp, 2)) { |
| return -EIO; |
| } |
| |
| tmp[0] = SSD16XX_CTRL2_DISABLE_CLK; |
| if (ssd16xx_write_cmd(driver, SSD16XX_CMD_UPDATE_CTRL2, |
| tmp, 1)) { |
| return -EIO; |
| } |
| |
| if (ssd16xx_write_cmd(driver, SSD16XX_CMD_MASTER_ACTIVATION, |
| NULL, 0)) { |
| return -EIO; |
| } |
| |
| ssd16xx_busy_wait(driver); |
| |
| driver->update_cmd |= SSD16XX_CTRL2_LOAD_LUT; |
| |
| return 0; |
| #endif |
| } |
| |
| static int ssd16xx_load_ws_initial(const struct device *dev) |
| { |
| #if DT_INST_NODE_HAS_PROP(0, lut_initial) |
| struct ssd16xx_data *driver = dev->data; |
| |
| if (ssd16xx_write_cmd(driver, SSD16XX_CMD_UPDATE_LUT, |
| ssd16xx_lut_initial, |
| sizeof(ssd16xx_lut_initial))) { |
| return -EIO; |
| } |
| |
| ssd16xx_busy_wait(driver); |
| #else |
| ssd16xx_load_ws_from_otp(dev); |
| #endif |
| |
| return 0; |
| } |
| |
| static int ssd16xx_load_ws_default(const struct device *dev) |
| { |
| #if DT_INST_NODE_HAS_PROP(0, lut_default) |
| struct ssd16xx_data *driver = dev->data; |
| |
| if (ssd16xx_write_cmd(driver, SSD16XX_CMD_UPDATE_LUT, |
| ssd16xx_lut_default, |
| sizeof(ssd16xx_lut_default))) { |
| return -EIO; |
| } |
| |
| ssd16xx_busy_wait(driver); |
| #endif |
| |
| return 0; |
| } |
| |
| static int ssd16xx_controller_init(const struct device *dev) |
| { |
| int err; |
| uint8_t tmp[3]; |
| size_t len; |
| struct ssd16xx_data *driver = dev->data; |
| |
| LOG_DBG(""); |
| |
| gpio_pin_set(driver->reset, SSD16XX_RESET_PIN, 1); |
| k_msleep(SSD16XX_RESET_DELAY); |
| gpio_pin_set(driver->reset, SSD16XX_RESET_PIN, 0); |
| k_msleep(SSD16XX_RESET_DELAY); |
| ssd16xx_busy_wait(driver); |
| |
| err = ssd16xx_write_cmd(driver, SSD16XX_CMD_SW_RESET, NULL, 0); |
| if (err < 0) { |
| return err; |
| } |
| ssd16xx_busy_wait(driver); |
| |
| len = push_y_param(tmp, SSD16XX_PANEL_LAST_GATE); |
| tmp[len++] = 0U; |
| err = ssd16xx_write_cmd(driver, SSD16XX_CMD_GDO_CTRL, tmp, len); |
| if (err < 0) { |
| return err; |
| } |
| |
| #if DT_INST_NODE_HAS_PROP(0, softstart) |
| err = ssd16xx_write_cmd(driver, SSD16XX_CMD_SOFTSTART, |
| ssd16xx_softstart, sizeof(ssd16xx_softstart)); |
| if (err < 0) { |
| return err; |
| } |
| #endif |
| |
| err = ssd16xx_write_cmd(driver, SSD16XX_CMD_GDV_CTRL, ssd16xx_gdv, |
| sizeof(ssd16xx_gdv)); |
| if (err < 0) { |
| return err; |
| } |
| |
| err = ssd16xx_write_cmd(driver, SSD16XX_CMD_SDV_CTRL, ssd16xx_sdv, |
| sizeof(ssd16xx_sdv)); |
| if (err < 0) { |
| return err; |
| } |
| |
| tmp[0] = DT_INST_PROP(0, vcom); |
| err = ssd16xx_write_cmd(driver, SSD16XX_CMD_VCOM_VOLTAGE, tmp, 1); |
| if (err < 0) { |
| return err; |
| } |
| |
| tmp[0] = SSD16XX_VAL_DUMMY_LINE; |
| err = ssd16xx_write_cmd(driver, SSD16XX_CMD_DUMMY_LINE, tmp, 1); |
| if (err < 0) { |
| return err; |
| } |
| |
| tmp[0] = SSD16XX_VAL_GATE_LWIDTH; |
| err = ssd16xx_write_cmd(driver, SSD16XX_CMD_GATE_LINE_WIDTH, tmp, 1); |
| if (err < 0) { |
| return err; |
| } |
| |
| tmp[0] = DT_INST_PROP(0, border_waveform); |
| err = ssd16xx_write_cmd(driver, SSD16XX_CMD_BWF_CTRL, tmp, 1); |
| if (err < 0) { |
| return err; |
| } |
| |
| ssd16xx_set_orientation_internall(driver); |
| driver->update_cmd = (SSD16XX_CTRL2_ENABLE_CLK | |
| SSD16XX_CTRL2_ENABLE_ANALOG | |
| SSD16XX_CTRL2_TO_PATTERN | |
| SSD16XX_CTRL2_DISABLE_ANALOG | |
| SSD16XX_CTRL2_DISABLE_CLK); |
| |
| if (ssd16xx_load_ws_initial(dev)) { |
| return -EIO; |
| } |
| |
| err = ssd16xx_clear_cntlr_mem(dev, SSD16XX_CMD_WRITE_RAM, true); |
| if (err < 0) { |
| return err; |
| } |
| |
| ssd16xx_busy_wait(driver); |
| |
| err = ssd16xx_clear_cntlr_mem(dev, SSD16XX_CMD_WRITE_RED_RAM, |
| false); |
| if (err < 0) { |
| return err; |
| } |
| |
| ssd16xx_busy_wait(driver); |
| |
| if (ssd16xx_load_ws_default(dev)) { |
| return -EIO; |
| } |
| |
| return ssd16xx_clear_cntlr_mem(dev, SSD16XX_CMD_WRITE_RAM, true); |
| } |
| |
| static int ssd16xx_init(const struct device *dev) |
| { |
| const struct ssd16xx_config *config = dev->config; |
| struct ssd16xx_data *driver = dev->data; |
| |
| LOG_DBG(""); |
| |
| if (!spi_is_ready(&config->bus)) { |
| LOG_ERR("SPI bus %s not ready", config->bus.bus->name); |
| return -ENODEV; |
| } |
| |
| driver->reset = device_get_binding(SSD16XX_RESET_CNTRL); |
| if (driver->reset == NULL) { |
| LOG_ERR("Could not get GPIO port for SSD16XX reset"); |
| return -EIO; |
| } |
| |
| gpio_pin_configure(driver->reset, SSD16XX_RESET_PIN, |
| GPIO_OUTPUT_INACTIVE | SSD16XX_RESET_FLAGS); |
| |
| driver->dc = device_get_binding(SSD16XX_DC_CNTRL); |
| if (driver->dc == NULL) { |
| LOG_ERR("Could not get GPIO port for SSD16XX DC signal"); |
| return -EIO; |
| } |
| |
| gpio_pin_configure(driver->dc, SSD16XX_DC_PIN, |
| GPIO_OUTPUT_INACTIVE | SSD16XX_DC_FLAGS); |
| |
| driver->busy = device_get_binding(SSD16XX_BUSY_CNTRL); |
| if (driver->busy == NULL) { |
| LOG_ERR("Could not get GPIO port for SSD16XX busy signal"); |
| return -EIO; |
| } |
| |
| gpio_pin_configure(driver->busy, SSD16XX_BUSY_PIN, |
| GPIO_INPUT | SSD16XX_BUSY_FLAGS); |
| |
| return ssd16xx_controller_init(dev); |
| } |
| |
| static const struct ssd16xx_config ssd16xx_config = { |
| .bus = SPI_DT_SPEC_INST_GET( |
| 0, SPI_OP_MODE_MASTER | SPI_WORD_SET(8), 0) |
| }; |
| |
| static struct ssd16xx_data ssd16xx_driver = { |
| .config = &ssd16xx_config |
| }; |
| |
| static struct display_driver_api ssd16xx_driver_api = { |
| .blanking_on = ssd16xx_blanking_on, |
| .blanking_off = ssd16xx_blanking_off, |
| .write = ssd16xx_write, |
| .read = ssd16xx_read, |
| .get_framebuffer = ssd16xx_get_framebuffer, |
| .set_brightness = ssd16xx_set_brightness, |
| .set_contrast = ssd16xx_set_contrast, |
| .get_capabilities = ssd16xx_get_capabilities, |
| .set_pixel_format = ssd16xx_set_pixel_format, |
| .set_orientation = ssd16xx_set_orientation, |
| }; |
| |
| |
| DEVICE_DT_INST_DEFINE(0, ssd16xx_init, NULL, |
| &ssd16xx_driver, NULL, |
| POST_KERNEL, CONFIG_DISPLAY_INIT_PRIORITY, |
| &ssd16xx_driver_api); |