1 /*
2 * Copyright 2023 NXP
3 * Copyright 2024 TiaC Systems
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #define DT_DRV_COMPAT zephyr_mipi_dbi_spi
9
10 #include <zephyr/drivers/mipi_dbi.h>
11 #include <zephyr/drivers/spi.h>
12 #include <zephyr/drivers/gpio.h>
13 #include <zephyr/sys/byteorder.h>
14
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(mipi_dbi_spi, CONFIG_MIPI_DBI_LOG_LEVEL);
17
18 struct mipi_dbi_spi_config {
19 /* SPI hardware used to send data */
20 const struct device *spi_dev;
21 /* Command/Data gpio */
22 const struct gpio_dt_spec cmd_data;
23 /* Reset GPIO */
24 const struct gpio_dt_spec reset;
25 /* Minimum transfer bits */
26 const uint8_t xfr_min_bits;
27 };
28
29 struct mipi_dbi_spi_data {
30 struct k_mutex lock;
31 /* Used for 3 wire mode */
32 uint16_t spi_byte;
33 };
34
35 /* Expands to 1 if the node does not have the `write-only` property */
36 #define _WRITE_ONLY_ABSENT(n) (!DT_INST_PROP(n, write_only)) |
37
38 /* This macro will evaluate to 1 if any of the nodes with zephyr,mipi-dbi-spi
39 * lack a `write-only` property. The intention here is to allow the entire
40 * command_read function to be optimized out when it is not needed.
41 */
42 #define MIPI_DBI_SPI_READ_REQUIRED DT_INST_FOREACH_STATUS_OKAY(_WRITE_ONLY_ABSENT) 0
43 uint32_t var = MIPI_DBI_SPI_READ_REQUIRED;
44
45 /* Expands to 1 if the node does reflect the enum in `xfr-min-bits` property */
46 #define _XFR_8BITS(n) (DT_INST_PROP(n, xfr_min_bits) == MIPI_DBI_SPI_XFR_8BIT) |
47 #define _XFR_16BITS(n) (DT_INST_PROP(n, xfr_min_bits) == MIPI_DBI_SPI_XFR_16BIT) |
48
49 /* This macros will evaluate to 1 if any of the nodes with zephyr,mipi-dbi-spi
50 * have the `xfr-min-bits` property to corresponding enum value. The intention
51 * here is to allow the write helper functions to be optimized out when not all
52 * minimum transfer bits will be needed.
53 */
54 #define MIPI_DBI_SPI_WRITE_8BIT_REQUIRED DT_INST_FOREACH_STATUS_OKAY(_XFR_8BITS) 0
55 #define MIPI_DBI_SPI_WRITE_16BIT_REQUIRED DT_INST_FOREACH_STATUS_OKAY(_XFR_16BITS) 0
56
57 /* In Type C mode 1 MIPI BIT communication, the 9th bit of the word
58 * (first bit sent in each word) indicates if the word is a command or
59 * data. Typically 0 indicates a command and 1 indicates data, but some
60 * displays may vary.
61 * Index starts from 0 so that BIT(8) means 9th bit.
62 */
63 #define MIPI_DBI_DC_BIT BIT(8)
64
65 static inline int
mipi_dbi_spi_write_helper_3wire(const struct device * dev,const struct mipi_dbi_config * dbi_config,bool cmd_present,uint8_t cmd,const uint8_t * data_buf,size_t len)66 mipi_dbi_spi_write_helper_3wire(const struct device *dev,
67 const struct mipi_dbi_config *dbi_config,
68 bool cmd_present, uint8_t cmd,
69 const uint8_t *data_buf, size_t len)
70 {
71 const struct mipi_dbi_spi_config *config = dev->config;
72 struct mipi_dbi_spi_data *data = dev->data;
73 struct spi_buf buffer;
74 struct spi_buf_set buf_set = {
75 .buffers = &buffer,
76 .count = 1,
77 };
78 int ret = 0;
79
80 /*
81 * 9 bit word mode must be used, as the command/data bit
82 * is stored before the data word.
83 */
84
85 if ((dbi_config->config.operation & SPI_WORD_SIZE_MASK)
86 != SPI_WORD_SET(9)) {
87 return -ENOTSUP;
88 }
89 buffer.buf = &data->spi_byte;
90 buffer.len = 2;
91
92 /* Send command */
93 if (cmd_present) {
94 data->spi_byte = cmd;
95 ret = spi_write(config->spi_dev, &dbi_config->config, &buf_set);
96 if (ret < 0) {
97 goto out;
98 }
99 }
100 /* Write data, byte by byte */
101 for (size_t i = 0; i < len; i++) {
102 data->spi_byte = MIPI_DBI_DC_BIT | data_buf[i];
103 ret = spi_write(config->spi_dev, &dbi_config->config, &buf_set);
104 if (ret < 0) {
105 goto out;
106 }
107 }
108 out:
109 return ret;
110 }
111
112 #if MIPI_DBI_SPI_WRITE_8BIT_REQUIRED
113
114 static inline int
mipi_dbi_spi_write_helper_4wire_8bit(const struct device * dev,const struct mipi_dbi_config * dbi_config,bool cmd_present,uint8_t cmd,const uint8_t * data_buf,size_t len)115 mipi_dbi_spi_write_helper_4wire_8bit(const struct device *dev,
116 const struct mipi_dbi_config *dbi_config,
117 bool cmd_present, uint8_t cmd,
118 const uint8_t *data_buf, size_t len)
119 {
120 const struct mipi_dbi_spi_config *config = dev->config;
121 struct spi_buf buffer;
122 struct spi_buf_set buf_set = {
123 .buffers = &buffer,
124 .count = 1,
125 };
126 int ret = 0;
127
128 /*
129 * 4 wire mode is much simpler. We just toggle the
130 * command/data GPIO to indicate if we are sending
131 * a command or data
132 */
133
134 buffer.buf = &cmd;
135 buffer.len = sizeof(cmd);
136
137 if (cmd_present) {
138 /* Set CD pin low for command */
139 gpio_pin_set_dt(&config->cmd_data, 0);
140 ret = spi_write(config->spi_dev, &dbi_config->config, &buf_set);
141 if (ret < 0) {
142 goto out;
143 }
144 }
145
146 if (len > 0) {
147 buffer.buf = (void *)data_buf;
148 buffer.len = len;
149
150 /* Set CD pin high for data */
151 gpio_pin_set_dt(&config->cmd_data, 1);
152 ret = spi_write(config->spi_dev, &dbi_config->config, &buf_set);
153 if (ret < 0) {
154 goto out;
155 }
156 }
157 out:
158 return ret;
159 }
160
161 #endif /* MIPI_DBI_SPI_WRITE_8BIT_REQUIRED */
162
163 #if MIPI_DBI_SPI_WRITE_16BIT_REQUIRED
164
165 static inline int
mipi_dbi_spi_write_helper_4wire_16bit(const struct device * dev,const struct mipi_dbi_config * dbi_config,bool cmd_present,uint8_t cmd,const uint8_t * data_buf,size_t len)166 mipi_dbi_spi_write_helper_4wire_16bit(const struct device *dev,
167 const struct mipi_dbi_config *dbi_config,
168 bool cmd_present, uint8_t cmd,
169 const uint8_t *data_buf, size_t len)
170 {
171 const struct mipi_dbi_spi_config *config = dev->config;
172 struct spi_buf buffer;
173 struct spi_buf_set buf_set = {
174 .buffers = &buffer,
175 .count = 1,
176 };
177 uint16_t data16;
178 int ret = 0;
179
180 /*
181 * 4 wire mode with toggle the command/data GPIO
182 * to indicate if we are sending a command or data
183 * but send 16-bit blocks (with bit stuffing).
184 */
185
186 if (cmd_present) {
187 data16 = sys_cpu_to_be16(cmd);
188 buffer.buf = &data16;
189 buffer.len = sizeof(data16);
190
191 /* Set CD pin low for command */
192 gpio_pin_set_dt(&config->cmd_data, 0);
193 ret = spi_write(config->spi_dev, &dbi_config->config,
194 &buf_set);
195 if (ret < 0) {
196 goto out;
197 }
198
199 /* Set CD pin high for data, if there are any */
200 if (len > 0) {
201 gpio_pin_set_dt(&config->cmd_data, 1);
202 }
203
204 /* iterate command data */
205 for (int i = 0; i < len; i++) {
206 data16 = sys_cpu_to_be16(data_buf[i]);
207
208 ret = spi_write(config->spi_dev, &dbi_config->config,
209 &buf_set);
210 if (ret < 0) {
211 goto out;
212 }
213 }
214 } else {
215 int stuffing = len % sizeof(data16);
216
217 /* Set CD pin high for data, if there are any */
218 if (len > 0) {
219 gpio_pin_set_dt(&config->cmd_data, 1);
220 }
221
222 /* pass through generic device data */
223 if (len - stuffing > 0) {
224 buffer.buf = (void *)data_buf;
225 buffer.len = len - stuffing;
226
227 ret = spi_write(config->spi_dev, &dbi_config->config,
228 &buf_set);
229 if (ret < 0) {
230 goto out;
231 }
232 }
233
234 /* iterate remaining data with stuffing */
235 for (int i = len - stuffing; i < len; i++) {
236 data16 = sys_cpu_to_be16(data_buf[i]);
237 buffer.buf = &data16;
238 buffer.len = sizeof(data16);
239
240 ret = spi_write(config->spi_dev, &dbi_config->config,
241 &buf_set);
242 if (ret < 0) {
243 goto out;
244 }
245 }
246 }
247 out:
248 return ret;
249 }
250
251 #endif /* MIPI_DBI_SPI_WRITE_16BIT_REQUIRED */
252
mipi_dbi_spi_write_helper(const struct device * dev,const struct mipi_dbi_config * dbi_config,bool cmd_present,uint8_t cmd,const uint8_t * data_buf,size_t len)253 static int mipi_dbi_spi_write_helper(const struct device *dev,
254 const struct mipi_dbi_config *dbi_config,
255 bool cmd_present, uint8_t cmd,
256 const uint8_t *data_buf, size_t len)
257 {
258 const struct mipi_dbi_spi_config *config = dev->config;
259 struct mipi_dbi_spi_data *data = dev->data;
260 int ret = 0;
261
262 ret = k_mutex_lock(&data->lock, K_FOREVER);
263 if (ret < 0) {
264 return ret;
265 }
266
267 if (dbi_config->mode == MIPI_DBI_MODE_SPI_3WIRE &&
268 IS_ENABLED(CONFIG_MIPI_DBI_SPI_3WIRE)) {
269 ret = mipi_dbi_spi_write_helper_3wire(dev, dbi_config,
270 cmd_present, cmd,
271 data_buf, len);
272 goto out;
273 }
274
275 if (dbi_config->mode == MIPI_DBI_MODE_SPI_4WIRE) {
276
277 #if MIPI_DBI_SPI_WRITE_8BIT_REQUIRED
278 if (config->xfr_min_bits == MIPI_DBI_SPI_XFR_8BIT) {
279 ret = mipi_dbi_spi_write_helper_4wire_8bit(
280 dev, dbi_config,
281 cmd_present, cmd,
282 data_buf, len);
283 goto out;
284 }
285 #endif
286
287 #if MIPI_DBI_SPI_WRITE_16BIT_REQUIRED
288 if (config->xfr_min_bits == MIPI_DBI_SPI_XFR_16BIT) {
289 ret = mipi_dbi_spi_write_helper_4wire_16bit(
290 dev, dbi_config,
291 cmd_present, cmd,
292 data_buf, len);
293 goto out;
294 }
295 #endif
296
297 }
298
299 /* Otherwise, unsupported mode */
300 ret = -ENOTSUP;
301
302 out:
303 k_mutex_unlock(&data->lock);
304 return ret;
305 }
306
mipi_dbi_spi_command_write(const struct device * dev,const struct mipi_dbi_config * dbi_config,uint8_t cmd,const uint8_t * data_buf,size_t len)307 static int mipi_dbi_spi_command_write(const struct device *dev,
308 const struct mipi_dbi_config *dbi_config,
309 uint8_t cmd, const uint8_t *data_buf,
310 size_t len)
311 {
312 return mipi_dbi_spi_write_helper(dev, dbi_config, true, cmd,
313 data_buf, len);
314 }
315
mipi_dbi_spi_write_display(const struct device * dev,const struct mipi_dbi_config * dbi_config,const uint8_t * framebuf,struct display_buffer_descriptor * desc,enum display_pixel_format pixfmt)316 static int mipi_dbi_spi_write_display(const struct device *dev,
317 const struct mipi_dbi_config *dbi_config,
318 const uint8_t *framebuf,
319 struct display_buffer_descriptor *desc,
320 enum display_pixel_format pixfmt)
321 {
322 ARG_UNUSED(pixfmt);
323
324 return mipi_dbi_spi_write_helper(dev, dbi_config, false, 0x0,
325 framebuf, desc->buf_size);
326 }
327
328 #if MIPI_DBI_SPI_READ_REQUIRED
329
330 static inline int
mipi_dbi_spi_read_helper_3wire(const struct device * dev,const struct mipi_dbi_config * dbi_config,uint8_t * cmds,size_t num_cmds,uint8_t * response,size_t len)331 mipi_dbi_spi_read_helper_3wire(const struct device *dev,
332 const struct mipi_dbi_config *dbi_config,
333 uint8_t *cmds, size_t num_cmds,
334 uint8_t *response, size_t len)
335 {
336 const struct mipi_dbi_spi_config *config = dev->config;
337 struct mipi_dbi_spi_data *data = dev->data;
338 struct spi_config tmp_config;
339 struct spi_buf buffer;
340 struct spi_buf_set buf_set = {
341 .buffers = &buffer,
342 .count = 1,
343 };
344 int ret = 0;
345
346 /*
347 * We have to emulate 3 wire mode by packing the data/command
348 * bit into the upper bit of the SPI transfer, switch SPI to
349 * 9 bit mode, and write the transfer.
350 */
351
352 if ((dbi_config->config.operation & SPI_WORD_SIZE_MASK)
353 != SPI_WORD_SET(9)) {
354 return -ENOTSUP;
355 }
356
357 memcpy(&tmp_config, &dbi_config->config, sizeof(tmp_config));
358 tmp_config.operation &= ~SPI_WORD_SIZE_MASK;
359 tmp_config.operation |= SPI_WORD_SET(9);
360
361 buffer.buf = &data->spi_byte;
362 buffer.len = 1;
363
364 /* Send each command */
365 for (size_t i = 0; i < num_cmds; i++) {
366 data->spi_byte = cmds[i];
367 ret = spi_write(config->spi_dev, &tmp_config, &buf_set);
368 if (ret < 0) {
369 goto out;
370 }
371 }
372
373 /* Now, we can switch to 8 bit mode, and read data */
374 buffer.buf = (void *)response;
375 buffer.len = len;
376 ret = spi_read(config->spi_dev, &dbi_config->config, &buf_set);
377
378 out:
379 spi_release(config->spi_dev, &tmp_config); /* Really necessary here? */
380 return ret;
381 }
382
383 static inline int
mipi_dbi_spi_read_helper_4wire(const struct device * dev,const struct mipi_dbi_config * dbi_config,uint8_t * cmds,size_t num_cmds,uint8_t * response,size_t len)384 mipi_dbi_spi_read_helper_4wire(const struct device *dev,
385 const struct mipi_dbi_config *dbi_config,
386 uint8_t *cmds, size_t num_cmds,
387 uint8_t *response, size_t len)
388 {
389 const struct mipi_dbi_spi_config *config = dev->config;
390 struct spi_config tmp_config;
391 struct spi_buf buffer;
392 struct spi_buf_set buf_set = {
393 .buffers = &buffer,
394 .count = 1,
395 };
396 int ret = 0;
397
398 /*
399 * 4 wire mode is much simpler. We just toggle the
400 * command/data GPIO to indicate if we are sending
401 * a command or data. Note that since some SPI displays
402 * require CS to be held low for the entire read sequence,
403 * we set SPI_HOLD_ON_CS
404 */
405
406 memcpy(&tmp_config, &dbi_config->config, sizeof(tmp_config));
407 tmp_config.operation |= SPI_HOLD_ON_CS;
408
409 if (num_cmds > 0) {
410 buffer.buf = cmds;
411 buffer.len = num_cmds;
412
413 /* Set CD pin low for command */
414 gpio_pin_set_dt(&config->cmd_data, 0);
415
416 ret = spi_write(config->spi_dev, &tmp_config, &buf_set);
417 if (ret < 0) {
418 goto out;
419 }
420 }
421
422 if (len > 0) {
423 buffer.buf = (void *)response;
424 buffer.len = len;
425
426 /* Set CD pin high for data */
427 gpio_pin_set_dt(&config->cmd_data, 1);
428
429 ret = spi_read(config->spi_dev, &tmp_config, &buf_set);
430 if (ret < 0) {
431 goto out;
432 }
433 }
434
435 out:
436 spi_release(config->spi_dev, &tmp_config);
437 return ret;
438 }
439
mipi_dbi_spi_command_read(const struct device * dev,const struct mipi_dbi_config * dbi_config,uint8_t * cmds,size_t num_cmds,uint8_t * response,size_t len)440 static int mipi_dbi_spi_command_read(const struct device *dev,
441 const struct mipi_dbi_config *dbi_config,
442 uint8_t *cmds, size_t num_cmds,
443 uint8_t *response, size_t len)
444 {
445 struct mipi_dbi_spi_data *data = dev->data;
446 int ret = 0;
447
448 ret = k_mutex_lock(&data->lock, K_FOREVER);
449 if (ret < 0) {
450 return ret;
451 }
452 if (dbi_config->mode == MIPI_DBI_MODE_SPI_3WIRE &&
453 IS_ENABLED(CONFIG_MIPI_DBI_SPI_3WIRE)) {
454 ret = mipi_dbi_spi_read_helper_3wire(dev, dbi_config,
455 cmds, num_cmds,
456 response, len);
457 if (ret < 0) {
458 goto out;
459 }
460 } else if (dbi_config->mode == MIPI_DBI_MODE_SPI_4WIRE) {
461 ret = mipi_dbi_spi_read_helper_4wire(dev, dbi_config,
462 cmds, num_cmds,
463 response, len);
464 if (ret < 0) {
465 goto out;
466 }
467 } else {
468 /* Otherwise, unsupported mode */
469 ret = -ENOTSUP;
470 }
471 out:
472 k_mutex_unlock(&data->lock);
473 return ret;
474 }
475
476 #endif /* MIPI_DBI_SPI_READ_REQUIRED */
477
mipi_dbi_has_pin(const struct gpio_dt_spec * spec)478 static inline bool mipi_dbi_has_pin(const struct gpio_dt_spec *spec)
479 {
480 return spec->port != NULL;
481 }
482
mipi_dbi_spi_reset(const struct device * dev,k_timeout_t delay)483 static int mipi_dbi_spi_reset(const struct device *dev, k_timeout_t delay)
484 {
485 const struct mipi_dbi_spi_config *config = dev->config;
486 int ret;
487
488 if (!mipi_dbi_has_pin(&config->reset)) {
489 return -ENOTSUP;
490 }
491
492 ret = gpio_pin_set_dt(&config->reset, 1);
493 if (ret < 0) {
494 return ret;
495 }
496 k_sleep(delay);
497 return gpio_pin_set_dt(&config->reset, 0);
498 }
499
mipi_dbi_spi_release(const struct device * dev,const struct mipi_dbi_config * dbi_config)500 static int mipi_dbi_spi_release(const struct device *dev,
501 const struct mipi_dbi_config *dbi_config)
502 {
503 const struct mipi_dbi_spi_config *config = dev->config;
504
505 return spi_release(config->spi_dev, &dbi_config->config);
506 }
507
mipi_dbi_spi_init(const struct device * dev)508 static int mipi_dbi_spi_init(const struct device *dev)
509 {
510 const struct mipi_dbi_spi_config *config = dev->config;
511 struct mipi_dbi_spi_data *data = dev->data;
512 int ret;
513
514 if (!device_is_ready(config->spi_dev)) {
515 LOG_ERR("SPI device is not ready");
516 return -ENODEV;
517 }
518
519 if (mipi_dbi_has_pin(&config->cmd_data)) {
520 if (!gpio_is_ready_dt(&config->cmd_data)) {
521 return -ENODEV;
522 }
523 ret = gpio_pin_configure_dt(&config->cmd_data, GPIO_OUTPUT);
524 if (ret < 0) {
525 LOG_ERR("Could not configure command/data GPIO (%d)", ret);
526 return ret;
527 }
528 }
529
530 if (mipi_dbi_has_pin(&config->reset)) {
531 if (!gpio_is_ready_dt(&config->reset)) {
532 return -ENODEV;
533 }
534 ret = gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_INACTIVE);
535 if (ret < 0) {
536 LOG_ERR("Could not configure reset GPIO (%d)", ret);
537 return ret;
538 }
539 }
540
541 k_mutex_init(&data->lock);
542
543 return 0;
544 }
545
546 static DEVICE_API(mipi_dbi, mipi_dbi_spi_driver_api) = {
547 .reset = mipi_dbi_spi_reset,
548 .command_write = mipi_dbi_spi_command_write,
549 .write_display = mipi_dbi_spi_write_display,
550 .release = mipi_dbi_spi_release,
551 #if MIPI_DBI_SPI_READ_REQUIRED
552 .command_read = mipi_dbi_spi_command_read,
553 #endif
554 };
555
556 #define MIPI_DBI_SPI_INIT(n) \
557 static const struct mipi_dbi_spi_config \
558 mipi_dbi_spi_config_##n = { \
559 .spi_dev = DEVICE_DT_GET( \
560 DT_INST_PHANDLE(n, spi_dev)), \
561 .cmd_data = GPIO_DT_SPEC_INST_GET_OR(n, dc_gpios, {}), \
562 .reset = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {}), \
563 .xfr_min_bits = DT_INST_PROP(n, xfr_min_bits) \
564 }; \
565 static struct mipi_dbi_spi_data mipi_dbi_spi_data_##n; \
566 \
567 DEVICE_DT_INST_DEFINE(n, mipi_dbi_spi_init, NULL, \
568 &mipi_dbi_spi_data_##n, \
569 &mipi_dbi_spi_config_##n, \
570 POST_KERNEL, \
571 CONFIG_MIPI_DBI_INIT_PRIORITY, \
572 &mipi_dbi_spi_driver_api);
573
574 DT_INST_FOREACH_STATUS_OKAY(MIPI_DBI_SPI_INIT)
575