diff --git a/drivers/display/ssd16xx.c b/drivers/display/ssd16xx.c index 0d2d3df1ec552..8e80fb28693c4 100644 --- a/drivers/display/ssd16xx.c +++ b/drivers/display/ssd16xx.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2022 Andreas Sandberg * Copyright (c) 2018-2020 PHYTEC Messtechnik GmbH * * SPDX-License-Identifier: Apache-2.0 @@ -18,6 +19,7 @@ LOG_MODULE_REGISTER(ssd16xx); #include #include +#include #include "ssd16xx_regs.h" /** @@ -32,6 +34,7 @@ LOG_MODULE_REGISTER(ssd16xx); #define SSD16XX_TR_SCALE_FACTOR 256U struct ssd16xx_data { + bool read_supported; uint8_t scan_mode; uint8_t update_cmd; bool blanking_on; @@ -114,6 +117,51 @@ static inline int ssd16xx_write_cmd(const struct device *dev, uint8_t cmd, return err; } +static inline int ssd16xx_read_cmd(const struct device *dev, uint8_t cmd, + uint8_t *data, size_t len) +{ + const struct ssd16xx_config *config = dev->config; + const struct ssd16xx_data *dev_data = dev->data; + struct spi_buf buf = {.buf = &cmd, .len = sizeof(cmd)}; + struct spi_buf_set buf_set = {.buffers = &buf, .count = 1}; + int err = 0; + + if (!dev_data->read_supported) { + return -ENOTSUP; + } + + ssd16xx_busy_wait(dev); + + err = gpio_pin_set_dt(&config->dc_gpio, 1); + if (err < 0) { + return err; + } + + err = spi_write_dt(&config->bus, &buf_set); + if (err < 0) { + goto spi_out; + } + + if (data != NULL) { + buf.buf = data; + buf.len = len; + + err = gpio_pin_set_dt(&config->dc_gpio, 0); + if (err < 0) { + goto spi_out; + } + + err = spi_read_dt(&config->bus, &buf_set); + if (err < 0) { + goto spi_out; + } + } + +spi_out: + spi_release_dt(&config->bus); + return err; +} + static inline size_t push_x_param(const struct device *dev, uint8_t *data, uint16_t x) { @@ -231,15 +279,13 @@ static int ssd16xx_blanking_on(const struct device *dev) return 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) +static int ssd16xx_set_window(const struct device *dev, + const uint16_t x, const uint16_t y, + const struct display_buffer_descriptor *desc) { const struct ssd16xx_config *config = dev->config; struct ssd16xx_data *data = dev->data; int err; - size_t buf_len; uint16_t x_start; uint16_t x_end; uint16_t y_start; @@ -252,12 +298,6 @@ static int ssd16xx_write(const struct device *dev, const uint16_t x, 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; @@ -320,6 +360,29 @@ static int ssd16xx_write(const struct device *dev, const uint16_t x, return err; } + return 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) +{ + const struct ssd16xx_data *data = dev->data; + const size_t buf_len = MIN(desc->buf_size, + desc->height * desc->width / 8); + int err; + + if (buf == NULL || buf_len == 0U) { + LOG_ERR("Display buffer is not available"); + return -EINVAL; + } + + err = ssd16xx_set_window(dev, x, y, desc); + if (err < 0) { + return err; + } + err = ssd16xx_write_cmd(dev, SSD16XX_CMD_WRITE_RAM, (uint8_t *)buf, buf_len); if (err < 0) { @@ -336,13 +399,65 @@ static int ssd16xx_write(const struct device *dev, const uint16_t x, return 0; } -static int ssd16xx_read(const struct device *dev, const uint16_t x, - const uint16_t y, +int ssd16xx_read_ram(const struct device *dev, enum ssd16xx_ram ram_type, + const uint16_t x, const uint16_t y, + const struct display_buffer_descriptor *desc, + void *buf) +{ + const struct ssd16xx_data *data = dev->data; + const size_t buf_len = MIN(desc->buf_size, + desc->height * desc->width / 8); + int err; + uint8_t ram_ctrl; + + if (!data->read_supported) { + return -ENOTSUP; + } + + switch (ram_type) { + case SSD16XX_RAM_BLACK: + ram_ctrl = SSD16XX_RAM_READ_CTRL_BLACK; + break; + + case SSD16XX_RAM_RED: + ram_ctrl = SSD16XX_RAM_READ_CTRL_RED; + break; + + default: + return -EINVAL; + } + + if (buf == NULL || buf_len == 0U) { + LOG_ERR("Display buffer is not available"); + return -EINVAL; + } + + err = ssd16xx_set_window(dev, x, y, desc); + if (err < 0) { + return err; + } + + err = ssd16xx_write_cmd(dev, SSD16XX_CMD_RAM_READ_CTRL, + &ram_ctrl, sizeof(ram_ctrl)); + if (err < 0) { + return err; + } + + err = ssd16xx_read_cmd(dev, SSD16XX_CMD_READ_RAM, (uint8_t *)buf, + buf_len); + if (err < 0) { + return err; + } + + return 0; +} + +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; + return ssd16xx_read_ram(dev, SSD16XX_RAM_BLACK, x, y, desc, buf); } static void *ssd16xx_get_framebuffer(const struct device *dev) @@ -674,6 +789,7 @@ static int ssd16xx_controller_init(const struct device *dev) static int ssd16xx_init(const struct device *dev) { const struct ssd16xx_config *config = dev->config; + struct ssd16xx_data *data = dev->data; int err; LOG_DBG(""); @@ -683,6 +799,9 @@ static int ssd16xx_init(const struct device *dev) return -ENODEV; } + data->read_supported = + (config->bus.config.operation & SPI_HALF_DUPLEX) != 0; + if (!device_is_ready(config->reset_gpio.port)) { LOG_ERR("Reset GPIO device not ready"); return -ENODEV; diff --git a/drivers/display/ssd16xx_regs.h b/drivers/display/ssd16xx_regs.h index 20f2847e9f174..2aaa8b015453c 100644 --- a/drivers/display/ssd16xx_regs.h +++ b/drivers/display/ssd16xx_regs.h @@ -19,15 +19,20 @@ #define SSD16XX_CMD_SW_RESET 0x12 #define SSD16XX_CMD_TSENSOR_SELECTION 0x18 #define SSD16XX_CMD_TSENS_CTRL 0x1a +#define SSD16XX_CMD_READ_TSENS_CTRL 0x1b #define SSD16XX_CMD_MASTER_ACTIVATION 0x20 #define SSD16XX_CMD_UPDATE_CTRL1 0x21 #define SSD16XX_CMD_UPDATE_CTRL2 0x22 #define SSD16XX_CMD_WRITE_RAM 0x24 #define SSD16XX_CMD_WRITE_RED_RAM 0x26 +#define SSD16XX_CMD_READ_RAM 0x27 #define SSD16XX_CMD_VCOM_SENSE 0x28 #define SSD16XX_CMD_VCOM_SENSE_DURATON 0x29 #define SSD16XX_CMD_PRGM_VCOM_OTP 0x2a #define SSD16XX_CMD_VCOM_VOLTAGE 0x2c +#define SSD16XX_CMD_READ_OTP_REG 0x2d +#define SSD16XX_CMD_READ_USER_ID 0x2e +#define SSD16XX_CMD_READ_STATUS 0x2f #define SSD16XX_CMD_PRGM_WS_OTP 0x30 #define SSD16XX_CMD_LOAD_WS_OTP 0x31 #define SSD16XX_CMD_UPDATE_LUT 0x32 @@ -36,6 +41,7 @@ #define SSD16XX_CMD_DUMMY_LINE 0x3a #define SSD16XX_CMD_GATE_LINE_WIDTH 0x3b #define SSD16XX_CMD_BWF_CTRL 0x3c +#define SSD16XX_CMD_RAM_READ_CTRL 0x41 #define SSD16XX_CMD_RAM_XPOS_CTRL 0x44 #define SSD16XX_CMD_RAM_YPOS_CTRL 0x45 #define SSD16XX_CMD_RAM_XPOS_CNTR 0x4e @@ -71,6 +77,9 @@ #define SSD16XX_SLEEP_MODE_DSM 0x01 #define SSD16XX_SLEEP_MODE_PON 0x00 +#define SSD16XX_RAM_READ_CTRL_BLACK 0 +#define SSD16XX_RAM_READ_CTRL_RED 1 + /* Default values */ #define SSD16XX_VAL_DUMMY_LINE 0x1a #define SSD16XX_VAL_GATE_LWIDTH 0x08 diff --git a/include/zephyr/display/ssd16xx.h b/include/zephyr/display/ssd16xx.h new file mode 100644 index 0000000000000..4056e0c30d394 --- /dev/null +++ b/include/zephyr/display/ssd16xx.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Andreas Sandberg + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DISPLAY_SSD16XX_H_ +#define ZEPHYR_INCLUDE_DISPLAY_SSD16XX_H_ + +#include + +/** + * SSD16xx RAM type for direct RAM access + */ +enum ssd16xx_ram { + /** The black RAM buffer. This is typically the buffer used to + * compose the contents that will be displayed after the next + * refresh. + */ + SSD16XX_RAM_BLACK = 0, + /* The red RAM buffer. This is typically the old frame buffer + * when performing partial refreshes or an additional color + * channel. + */ + SSD16XX_RAM_RED, +}; + +/** + * @brief Read data directly from the display controller's internal + * RAM. + * + * @param dev Pointer to device structure + * @param ram_type Type of RAM to read from + * @param x Coordinate relative to the upper left corner + * @param y Coordinate relative to the upper left corner + * @param desc Structure describing the buffer layout + * @param buf Output buffer + * + * @retval 0 on success, negative errno on failure. + */ +int ssd16xx_read_ram(const struct device *dev, enum ssd16xx_ram ram_type, + const uint16_t x, const uint16_t y, + const struct display_buffer_descriptor *desc, + void *buf); + +#endif /* ZEPHYR_INCLUDE_DISPLAY_SSD16XX_H_ */