1 /*
2 * Copyright (c) 2015 Intel Corporation
3 * Copyright (c) 2022 Nordic Semiconductor ASA
4 * Copyright (c) 2022-2023 Jamie McCrae
5 * Copyright (c) 2023 Chen Xingyu <hi@xingrz.me>
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 */
9
10 #define DT_DRV_COMPAT ptc_pt6314
11
12 #include <string.h>
13 #include <zephyr/device.h>
14 #include <zephyr/devicetree.h>
15 #include <zephyr/drivers/spi.h>
16 #include <zephyr/drivers/auxdisplay.h>
17 #include <zephyr/kernel.h>
18 #include <zephyr/sys/util.h>
19
20 /* Defines for the PT6314_INST_DISPLAY_ON_OFF */
21 #define PT6314_DO_BLINKING_ON (1 << 0)
22 #define PT6314_DO_CURSOR_ON (1 << 1)
23 #define PT6314_DO_DISPLAY_ON (1 << 2)
24
25 /* Defines for the PT6314_INST_FUNCTION_SET */
26 #define PT6314_FS_BRIGHTNESS(BR) (4 - (BR & BIT_MASK(2)))
27 #define PT6314_FS_ROWS_1 (0 << 3)
28 #define PT6314_FS_ROWS_2 (1 << 3)
29 #define PT6314_FS_8BIT_MODE (1 << 4)
30
31 #define PT6314_BRIGHTNESS_MIN 1
32 #define PT6314_BRIGHTNESS_MAX 4
33
34 /* Defines for the PT6314_INST_DDRAM_ADDRESS_SET */
35 #define PT6314_DA_BASE_ROW_1 (0x00)
36 #define PT6314_DA_BASE_ROW_2 (0x40)
37
38 /* Display Commands */
39 #define PT6314_INST_CLEAR_DISPLAY BIT(0)
40 #define PT6314_INST_CURSOR_HOME BIT(1)
41 #define PT6314_INST_ENTRY_MODE_SET BIT(2)
42 #define PT6314_INST_DISPLAY_ON_OFF BIT(3)
43 #define PT6314_INST_CURSOR_OR_DISPLAY_SHIFT BIT(4)
44 #define PT6314_INST_FUNCTION_SET BIT(5)
45 #define PT6314_INST_CGRAM_ADDRESS_SET BIT(6)
46 #define PT6314_INST_DDRAM_ADDRESS_SET BIT(7)
47
48 /* Start Byte */
49 #define PT6314_SB_RS_INST (0 << 1)
50 #define PT6314_SB_RS_DATA (1 << 1)
51 #define PT6314_SB_RW_WRITE (0 << 2)
52 #define PT6314_SB_RW_READ (1 << 2)
53 #define PT6314_SB_SYNC_BITS (BIT_MASK(5) << 3)
54
55 struct auxdisplay_pt6314_data {
56 bool power;
57 bool cursor;
58 bool blinking;
59 uint8_t brightness;
60 uint16_t cursor_x;
61 uint16_t cursor_y;
62 };
63
64 struct auxdisplay_pt6314_config {
65 struct auxdisplay_capabilities capabilities;
66 struct spi_dt_spec bus;
67 };
68
auxdisplay_pt6314_spi_write(const struct device * dev,uint8_t flags,uint8_t val)69 static int auxdisplay_pt6314_spi_write(const struct device *dev, uint8_t flags, uint8_t val)
70 {
71 const struct auxdisplay_pt6314_config *config = dev->config;
72
73 uint8_t buf[2] = {PT6314_SB_SYNC_BITS | PT6314_SB_RW_WRITE | flags, val};
74
75 struct spi_buf tx_buf[] = {{.buf = buf, .len = sizeof(buf)}};
76 const struct spi_buf_set tx = {.buffers = tx_buf, .count = 1};
77
78 return spi_write_dt(&config->bus, &tx);
79 }
80
auxdisplay_pt6314_inst(const struct device * dev,uint8_t inst)81 static inline int auxdisplay_pt6314_inst(const struct device *dev, uint8_t inst)
82 {
83 return auxdisplay_pt6314_spi_write(dev, PT6314_SB_RS_INST, inst);
84 }
85
auxdisplay_pt6314_data(const struct device * dev,uint8_t data)86 static inline int auxdisplay_pt6314_data(const struct device *dev, uint8_t data)
87 {
88 return auxdisplay_pt6314_spi_write(dev, PT6314_SB_RS_DATA, data);
89 }
90
auxdisplay_pt6314_display_on_off(const struct device * dev)91 static int auxdisplay_pt6314_display_on_off(const struct device *dev)
92 {
93 struct auxdisplay_pt6314_data *data = dev->data;
94 uint8_t inst;
95
96 inst = (data->power ? PT6314_DO_DISPLAY_ON : 0) | (data->cursor ? PT6314_DO_CURSOR_ON : 0) |
97 (data->blinking ? PT6314_DO_BLINKING_ON : 0);
98
99 return auxdisplay_pt6314_inst(dev, PT6314_INST_DISPLAY_ON_OFF | inst);
100 }
101
auxdisplay_pt6314_function_set(const struct device * dev)102 static int auxdisplay_pt6314_function_set(const struct device *dev)
103 {
104 const struct auxdisplay_pt6314_config *config = dev->config;
105 struct auxdisplay_pt6314_data *data = dev->data;
106 uint8_t inst;
107
108 inst = PT6314_FS_8BIT_MODE |
109 (config->capabilities.rows == 2 ? PT6314_FS_ROWS_2 : PT6314_FS_ROWS_1) |
110 PT6314_FS_BRIGHTNESS(data->brightness);
111
112 return auxdisplay_pt6314_inst(dev, PT6314_INST_FUNCTION_SET | inst);
113 }
114
auxdisplay_pt6314_ddram_address_set(const struct device * dev)115 static int auxdisplay_pt6314_ddram_address_set(const struct device *dev)
116 {
117 struct auxdisplay_pt6314_data *data = dev->data;
118 uint8_t inst;
119
120 inst = (data->cursor_y == 0 ? PT6314_DA_BASE_ROW_1 : PT6314_DA_BASE_ROW_2) + data->cursor_x;
121
122 return auxdisplay_pt6314_inst(dev, PT6314_INST_DDRAM_ADDRESS_SET | inst);
123 }
124
auxdisplay_pt6314_display_on(const struct device * dev)125 static int auxdisplay_pt6314_display_on(const struct device *dev)
126 {
127 struct auxdisplay_pt6314_data *data = dev->data;
128
129 data->power = true;
130
131 return auxdisplay_pt6314_display_on_off(dev);
132 }
133
auxdisplay_pt6314_display_off(const struct device * dev)134 static int auxdisplay_pt6314_display_off(const struct device *dev)
135 {
136 struct auxdisplay_pt6314_data *data = dev->data;
137
138 data->power = false;
139
140 return auxdisplay_pt6314_display_on_off(dev);
141 }
142
auxdisplay_pt6314_cursor_set_enabled(const struct device * dev,bool enable)143 static int auxdisplay_pt6314_cursor_set_enabled(const struct device *dev, bool enable)
144 {
145 struct auxdisplay_pt6314_data *data = dev->data;
146
147 data->cursor = enable;
148
149 return auxdisplay_pt6314_display_on_off(dev);
150 }
151
auxdisplay_pt6314_position_blinking_set_enabled(const struct device * dev,bool enable)152 static int auxdisplay_pt6314_position_blinking_set_enabled(const struct device *dev, bool enable)
153 {
154 struct auxdisplay_pt6314_data *data = dev->data;
155
156 data->blinking = enable;
157
158 return auxdisplay_pt6314_display_on_off(dev);
159 }
160
auxdisplay_pt6314_cursor_position_set(const struct device * dev,enum auxdisplay_position type,int16_t x,int16_t y)161 static int auxdisplay_pt6314_cursor_position_set(const struct device *dev,
162 enum auxdisplay_position type, int16_t x,
163 int16_t y)
164 {
165 const struct auxdisplay_pt6314_config *config = dev->config;
166 struct auxdisplay_pt6314_data *data = dev->data;
167
168 if (type == AUXDISPLAY_POSITION_RELATIVE) {
169 x += data->cursor_x;
170 y += data->cursor_y;
171 } else if (type == AUXDISPLAY_POSITION_RELATIVE_DIRECTION) {
172 return -EINVAL;
173 }
174
175 if (x < 0 || y < 0) {
176 return -EINVAL;
177 } else if (x >= config->capabilities.columns || y >= config->capabilities.rows) {
178 return -EINVAL;
179 }
180
181 data->cursor_x = (uint16_t)x;
182 data->cursor_y = (uint16_t)y;
183
184 return auxdisplay_pt6314_ddram_address_set(dev);
185 }
186
auxdisplay_pt6314_cursor_position_get(const struct device * dev,int16_t * x,int16_t * y)187 static int auxdisplay_pt6314_cursor_position_get(const struct device *dev, int16_t *x, int16_t *y)
188 {
189 struct auxdisplay_pt6314_data *data = dev->data;
190
191 *x = (int16_t)data->cursor_x;
192 *y = (int16_t)data->cursor_y;
193
194 return 0;
195 }
196
auxdisplay_pt6314_capabilities_get(const struct device * dev,struct auxdisplay_capabilities * capabilities)197 static int auxdisplay_pt6314_capabilities_get(const struct device *dev,
198 struct auxdisplay_capabilities *capabilities)
199 {
200 const struct auxdisplay_pt6314_config *config = dev->config;
201
202 memcpy(capabilities, &config->capabilities, sizeof(struct auxdisplay_capabilities));
203
204 return 0;
205 }
206
auxdisplay_pt6314_clear(const struct device * dev)207 static int auxdisplay_pt6314_clear(const struct device *dev)
208 {
209 struct auxdisplay_pt6314_data *data = dev->data;
210
211 data->cursor_x = 0;
212 data->cursor_y = 0;
213
214 return auxdisplay_pt6314_inst(dev, PT6314_INST_CLEAR_DISPLAY);
215 }
216
auxdisplay_pt6314_brightness_set(const struct device * dev,uint8_t brightness)217 static int auxdisplay_pt6314_brightness_set(const struct device *dev, uint8_t brightness)
218 {
219 struct auxdisplay_pt6314_data *data = dev->data;
220
221 if (brightness < PT6314_BRIGHTNESS_MIN || brightness > PT6314_BRIGHTNESS_MAX) {
222 return -EINVAL;
223 }
224
225 data->brightness = brightness;
226
227 return auxdisplay_pt6314_function_set(dev);
228 }
229
auxdisplay_pt6314_brightness_get(const struct device * dev,uint8_t * brightness)230 static int auxdisplay_pt6314_brightness_get(const struct device *dev, uint8_t *brightness)
231 {
232 struct auxdisplay_pt6314_data *data = dev->data;
233
234 *brightness = data->brightness;
235
236 return 0;
237 }
238
auxdisplay_pt6314_write(const struct device * dev,const uint8_t * text,uint16_t len)239 static int auxdisplay_pt6314_write(const struct device *dev, const uint8_t *text, uint16_t len)
240 {
241 const struct auxdisplay_pt6314_config *config = dev->config;
242 struct auxdisplay_pt6314_data *data = dev->data;
243 int ret;
244 int16_t i;
245
246 for (i = 0; i < len; i++) {
247 ret = auxdisplay_pt6314_data(dev, text[i]);
248 if (ret) {
249 return ret;
250 }
251
252 data->cursor_x++;
253
254 if (data->cursor_x == config->capabilities.columns) {
255 data->cursor_x = 0;
256 data->cursor_y++;
257
258 if (data->cursor_y == config->capabilities.rows) {
259 data->cursor_y = 0;
260 }
261
262 ret = auxdisplay_pt6314_ddram_address_set(dev);
263 if (ret) {
264 return ret;
265 }
266 }
267 }
268
269 return 0;
270 }
271
auxdisplay_pt6314_init(const struct device * dev)272 static int auxdisplay_pt6314_init(const struct device *dev)
273 {
274 const struct auxdisplay_pt6314_config *config = dev->config;
275
276 if (!device_is_ready(config->bus.bus)) {
277 return -ENODEV;
278 }
279
280 auxdisplay_pt6314_function_set(dev);
281 auxdisplay_pt6314_display_on_off(dev);
282 auxdisplay_pt6314_clear(dev);
283
284 return 0;
285 }
286
287 static DEVICE_API(auxdisplay, auxdisplay_pt6314_auxdisplay_api) = {
288 .display_on = auxdisplay_pt6314_display_on,
289 .display_off = auxdisplay_pt6314_display_off,
290 .cursor_set_enabled = auxdisplay_pt6314_cursor_set_enabled,
291 .position_blinking_set_enabled = auxdisplay_pt6314_position_blinking_set_enabled,
292 .cursor_position_set = auxdisplay_pt6314_cursor_position_set,
293 .cursor_position_get = auxdisplay_pt6314_cursor_position_get,
294 .capabilities_get = auxdisplay_pt6314_capabilities_get,
295 .clear = auxdisplay_pt6314_clear,
296 .brightness_get = auxdisplay_pt6314_brightness_get,
297 .brightness_set = auxdisplay_pt6314_brightness_set,
298 .write = auxdisplay_pt6314_write,
299 };
300
301 #define AUXDISPLAY_PT6314_INST(n) \
302 static const struct auxdisplay_pt6314_config auxdisplay_pt6314_config_##n = { \
303 .capabilities = \
304 { \
305 .columns = DT_INST_PROP(n, columns), \
306 .rows = DT_INST_PROP(n, rows), \
307 .mode = 0, \
308 .brightness.minimum = PT6314_BRIGHTNESS_MIN, \
309 .brightness.maximum = PT6314_BRIGHTNESS_MAX, \
310 .backlight.minimum = AUXDISPLAY_LIGHT_NOT_SUPPORTED, \
311 .backlight.maximum = AUXDISPLAY_LIGHT_NOT_SUPPORTED, \
312 .custom_characters = 0, \
313 }, \
314 .bus = SPI_DT_SPEC_INST_GET(n, \
315 SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA | \
316 SPI_TRANSFER_MSB | SPI_WORD_SET(8), \
317 0), \
318 }; \
319 \
320 static struct auxdisplay_pt6314_data auxdisplay_pt6314_data_##n = { \
321 .power = true, \
322 .cursor = false, \
323 .blinking = false, \
324 .brightness = PT6314_BRIGHTNESS_MAX, \
325 .cursor_x = 0, \
326 .cursor_y = 0, \
327 }; \
328 \
329 DEVICE_DT_INST_DEFINE(n, &auxdisplay_pt6314_init, NULL, &auxdisplay_pt6314_data_##n, \
330 &auxdisplay_pt6314_config_##n, POST_KERNEL, \
331 CONFIG_AUXDISPLAY_INIT_PRIORITY, &auxdisplay_pt6314_auxdisplay_api);
332
333 DT_INST_FOREACH_STATUS_OKAY(AUXDISPLAY_PT6314_INST)
334