| /* |
| * Copyright (c) 2015 Intel Corporation |
| * Copyright (c) 2022 Nordic Semiconductor ASA |
| * Copyright (c) 2022-2023 Jamie McCrae |
| * Copyright (c) 2023 Chen Xingyu <hi@xingrz.me> |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT ptc_pt6314 |
| |
| #include <string.h> |
| #include <zephyr/device.h> |
| #include <zephyr/devicetree.h> |
| #include <zephyr/drivers/spi.h> |
| #include <zephyr/drivers/auxdisplay.h> |
| #include <zephyr/kernel.h> |
| #include <zephyr/sys/util.h> |
| |
| /* Defines for the PT6314_INST_DISPLAY_ON_OFF */ |
| #define PT6314_DO_BLINKING_ON (1 << 0) |
| #define PT6314_DO_CURSOR_ON (1 << 1) |
| #define PT6314_DO_DISPLAY_ON (1 << 2) |
| |
| /* Defines for the PT6314_INST_FUNCTION_SET */ |
| #define PT6314_FS_BRIGHTNESS(BR) (4 - (BR & BIT_MASK(2))) |
| #define PT6314_FS_ROWS_1 (0 << 3) |
| #define PT6314_FS_ROWS_2 (1 << 3) |
| #define PT6314_FS_8BIT_MODE (1 << 4) |
| |
| #define PT6314_BRIGHTNESS_MIN 1 |
| #define PT6314_BRIGHTNESS_MAX 4 |
| |
| /* Defines for the PT6314_INST_DDRAM_ADDRESS_SET */ |
| #define PT6314_DA_BASE_ROW_1 (0x00) |
| #define PT6314_DA_BASE_ROW_2 (0x40) |
| |
| /* Display Commands */ |
| #define PT6314_INST_CLEAR_DISPLAY BIT(0) |
| #define PT6314_INST_CURSOR_HOME BIT(1) |
| #define PT6314_INST_ENTRY_MODE_SET BIT(2) |
| #define PT6314_INST_DISPLAY_ON_OFF BIT(3) |
| #define PT6314_INST_CURSOR_OR_DISPLAY_SHIFT BIT(4) |
| #define PT6314_INST_FUNCTION_SET BIT(5) |
| #define PT6314_INST_CGRAM_ADDRESS_SET BIT(6) |
| #define PT6314_INST_DDRAM_ADDRESS_SET BIT(7) |
| |
| /* Start Byte */ |
| #define PT6314_SB_RS_INST (0 << 1) |
| #define PT6314_SB_RS_DATA (1 << 1) |
| #define PT6314_SB_RW_WRITE (0 << 2) |
| #define PT6314_SB_RW_READ (1 << 2) |
| #define PT6314_SB_SYNC_BITS (BIT_MASK(5) << 3) |
| |
| struct auxdisplay_pt6314_data { |
| bool power; |
| bool cursor; |
| bool blinking; |
| uint8_t brightness; |
| uint16_t cursor_x; |
| uint16_t cursor_y; |
| }; |
| |
| struct auxdisplay_pt6314_config { |
| struct auxdisplay_capabilities capabilities; |
| struct spi_dt_spec bus; |
| }; |
| |
| static int auxdisplay_pt6314_spi_write(const struct device *dev, uint8_t flags, uint8_t val) |
| { |
| const struct auxdisplay_pt6314_config *config = dev->config; |
| |
| uint8_t buf[2] = {PT6314_SB_SYNC_BITS | PT6314_SB_RW_WRITE | flags, val}; |
| |
| struct spi_buf tx_buf[] = {{.buf = buf, .len = sizeof(buf)}}; |
| const struct spi_buf_set tx = {.buffers = tx_buf, .count = 1}; |
| |
| return spi_write_dt(&config->bus, &tx); |
| } |
| |
| static inline int auxdisplay_pt6314_inst(const struct device *dev, uint8_t inst) |
| { |
| return auxdisplay_pt6314_spi_write(dev, PT6314_SB_RS_INST, inst); |
| } |
| |
| static inline int auxdisplay_pt6314_data(const struct device *dev, uint8_t data) |
| { |
| return auxdisplay_pt6314_spi_write(dev, PT6314_SB_RS_DATA, data); |
| } |
| |
| static int auxdisplay_pt6314_display_on_off(const struct device *dev) |
| { |
| struct auxdisplay_pt6314_data *data = dev->data; |
| uint8_t inst; |
| |
| inst = (data->power ? PT6314_DO_DISPLAY_ON : 0) | (data->cursor ? PT6314_DO_CURSOR_ON : 0) | |
| (data->blinking ? PT6314_DO_BLINKING_ON : 0); |
| |
| return auxdisplay_pt6314_inst(dev, PT6314_INST_DISPLAY_ON_OFF | inst); |
| } |
| |
| static int auxdisplay_pt6314_function_set(const struct device *dev) |
| { |
| const struct auxdisplay_pt6314_config *config = dev->config; |
| struct auxdisplay_pt6314_data *data = dev->data; |
| uint8_t inst; |
| |
| inst = PT6314_FS_8BIT_MODE | |
| (config->capabilities.rows == 2 ? PT6314_FS_ROWS_2 : PT6314_FS_ROWS_1) | |
| PT6314_FS_BRIGHTNESS(data->brightness); |
| |
| return auxdisplay_pt6314_inst(dev, PT6314_INST_FUNCTION_SET | inst); |
| } |
| |
| static int auxdisplay_pt6314_ddram_address_set(const struct device *dev) |
| { |
| struct auxdisplay_pt6314_data *data = dev->data; |
| uint8_t inst; |
| |
| inst = (data->cursor_y == 0 ? PT6314_DA_BASE_ROW_1 : PT6314_DA_BASE_ROW_2) + data->cursor_x; |
| |
| return auxdisplay_pt6314_inst(dev, PT6314_INST_DDRAM_ADDRESS_SET | inst); |
| } |
| |
| static int auxdisplay_pt6314_display_on(const struct device *dev) |
| { |
| struct auxdisplay_pt6314_data *data = dev->data; |
| |
| data->power = true; |
| |
| return auxdisplay_pt6314_display_on_off(dev); |
| } |
| |
| static int auxdisplay_pt6314_display_off(const struct device *dev) |
| { |
| struct auxdisplay_pt6314_data *data = dev->data; |
| |
| data->power = false; |
| |
| return auxdisplay_pt6314_display_on_off(dev); |
| } |
| |
| static int auxdisplay_pt6314_cursor_set_enabled(const struct device *dev, bool enable) |
| { |
| struct auxdisplay_pt6314_data *data = dev->data; |
| |
| data->cursor = enable; |
| |
| return auxdisplay_pt6314_display_on_off(dev); |
| } |
| |
| static int auxdisplay_pt6314_position_blinking_set_enabled(const struct device *dev, bool enable) |
| { |
| struct auxdisplay_pt6314_data *data = dev->data; |
| |
| data->blinking = enable; |
| |
| return auxdisplay_pt6314_display_on_off(dev); |
| } |
| |
| static int auxdisplay_pt6314_cursor_position_set(const struct device *dev, |
| enum auxdisplay_position type, int16_t x, |
| int16_t y) |
| { |
| const struct auxdisplay_pt6314_config *config = dev->config; |
| struct auxdisplay_pt6314_data *data = dev->data; |
| |
| if (type == AUXDISPLAY_POSITION_RELATIVE) { |
| x += data->cursor_x; |
| y += data->cursor_y; |
| } else if (type == AUXDISPLAY_POSITION_RELATIVE_DIRECTION) { |
| return -EINVAL; |
| } |
| |
| if (x < 0 || y < 0) { |
| return -EINVAL; |
| } else if (x >= config->capabilities.columns || y >= config->capabilities.rows) { |
| return -EINVAL; |
| } |
| |
| data->cursor_x = (uint16_t)x; |
| data->cursor_y = (uint16_t)y; |
| |
| return auxdisplay_pt6314_ddram_address_set(dev); |
| } |
| |
| static int auxdisplay_pt6314_cursor_position_get(const struct device *dev, int16_t *x, int16_t *y) |
| { |
| struct auxdisplay_pt6314_data *data = dev->data; |
| |
| *x = (int16_t)data->cursor_x; |
| *y = (int16_t)data->cursor_y; |
| |
| return 0; |
| } |
| |
| static int auxdisplay_pt6314_capabilities_get(const struct device *dev, |
| struct auxdisplay_capabilities *capabilities) |
| { |
| const struct auxdisplay_pt6314_config *config = dev->config; |
| |
| memcpy(capabilities, &config->capabilities, sizeof(struct auxdisplay_capabilities)); |
| |
| return 0; |
| } |
| |
| static int auxdisplay_pt6314_clear(const struct device *dev) |
| { |
| struct auxdisplay_pt6314_data *data = dev->data; |
| |
| data->cursor_x = 0; |
| data->cursor_y = 0; |
| |
| return auxdisplay_pt6314_inst(dev, PT6314_INST_CLEAR_DISPLAY); |
| } |
| |
| static int auxdisplay_pt6314_brightness_set(const struct device *dev, uint8_t brightness) |
| { |
| struct auxdisplay_pt6314_data *data = dev->data; |
| |
| if (brightness < PT6314_BRIGHTNESS_MIN || brightness > PT6314_BRIGHTNESS_MAX) { |
| return -EINVAL; |
| } |
| |
| data->brightness = brightness; |
| |
| return auxdisplay_pt6314_function_set(dev); |
| } |
| |
| static int auxdisplay_pt6314_brightness_get(const struct device *dev, uint8_t *brightness) |
| { |
| struct auxdisplay_pt6314_data *data = dev->data; |
| |
| *brightness = data->brightness; |
| |
| return 0; |
| } |
| |
| static int auxdisplay_pt6314_write(const struct device *dev, const uint8_t *text, uint16_t len) |
| { |
| const struct auxdisplay_pt6314_config *config = dev->config; |
| struct auxdisplay_pt6314_data *data = dev->data; |
| int ret; |
| int16_t i; |
| |
| for (i = 0; i < len; i++) { |
| ret = auxdisplay_pt6314_data(dev, text[i]); |
| if (ret) { |
| return ret; |
| } |
| |
| data->cursor_x++; |
| |
| if (data->cursor_x == config->capabilities.columns) { |
| data->cursor_x = 0; |
| data->cursor_y++; |
| |
| if (data->cursor_y == config->capabilities.rows) { |
| data->cursor_y = 0; |
| } |
| |
| ret = auxdisplay_pt6314_ddram_address_set(dev); |
| if (ret) { |
| return ret; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int auxdisplay_pt6314_init(const struct device *dev) |
| { |
| const struct auxdisplay_pt6314_config *config = dev->config; |
| |
| if (!device_is_ready(config->bus.bus)) { |
| return -ENODEV; |
| } |
| |
| auxdisplay_pt6314_function_set(dev); |
| auxdisplay_pt6314_display_on_off(dev); |
| auxdisplay_pt6314_clear(dev); |
| |
| return 0; |
| } |
| |
| static const struct auxdisplay_driver_api auxdisplay_pt6314_auxdisplay_api = { |
| .display_on = auxdisplay_pt6314_display_on, |
| .display_off = auxdisplay_pt6314_display_off, |
| .cursor_set_enabled = auxdisplay_pt6314_cursor_set_enabled, |
| .position_blinking_set_enabled = auxdisplay_pt6314_position_blinking_set_enabled, |
| .cursor_position_set = auxdisplay_pt6314_cursor_position_set, |
| .cursor_position_get = auxdisplay_pt6314_cursor_position_get, |
| .capabilities_get = auxdisplay_pt6314_capabilities_get, |
| .clear = auxdisplay_pt6314_clear, |
| .brightness_get = auxdisplay_pt6314_brightness_get, |
| .brightness_set = auxdisplay_pt6314_brightness_set, |
| .write = auxdisplay_pt6314_write, |
| }; |
| |
| #define AUXDISPLAY_PT6314_INST(n) \ |
| static const struct auxdisplay_pt6314_config auxdisplay_pt6314_config_##n = { \ |
| .capabilities = \ |
| { \ |
| .columns = DT_INST_PROP(n, columns), \ |
| .rows = DT_INST_PROP(n, rows), \ |
| .mode = 0, \ |
| .brightness.minimum = PT6314_BRIGHTNESS_MIN, \ |
| .brightness.maximum = PT6314_BRIGHTNESS_MAX, \ |
| .backlight.minimum = AUXDISPLAY_LIGHT_NOT_SUPPORTED, \ |
| .backlight.maximum = AUXDISPLAY_LIGHT_NOT_SUPPORTED, \ |
| .custom_characters = 0, \ |
| }, \ |
| .bus = SPI_DT_SPEC_INST_GET(n, \ |
| SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA | \ |
| SPI_TRANSFER_MSB | SPI_WORD_SET(8), \ |
| 0), \ |
| }; \ |
| \ |
| static struct auxdisplay_pt6314_data auxdisplay_pt6314_data_##n = { \ |
| .power = true, \ |
| .cursor = false, \ |
| .blinking = false, \ |
| .brightness = PT6314_BRIGHTNESS_MAX, \ |
| .cursor_x = 0, \ |
| .cursor_y = 0, \ |
| }; \ |
| \ |
| DEVICE_DT_INST_DEFINE(n, &auxdisplay_pt6314_init, NULL, &auxdisplay_pt6314_data_##n, \ |
| &auxdisplay_pt6314_config_##n, POST_KERNEL, \ |
| CONFIG_AUXDISPLAY_INIT_PRIORITY, &auxdisplay_pt6314_auxdisplay_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(AUXDISPLAY_PT6314_INST) |