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