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