1 /*
2  * Copyright (c) 2021, Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/drivers/display.h>
8 #include <zephyr/devicetree.h>
9 #include <zephyr/dt-bindings/gpio/gpio.h>
10 #include <soc.h>
11 #include <hal/nrf_timer.h>
12 #ifdef PWM_PRESENT
13 #include <hal/nrf_pwm.h>
14 #endif
15 #include <nrfx_gpiote.h>
16 #ifdef PPI_PRESENT
17 #include <nrfx_ppi.h>
18 #endif
19 #include <nrf_peripherals.h>
20 #include <zephyr/logging/log.h>
21 #include <zephyr/irq.h>
22 LOG_MODULE_REGISTER(nrf_led_matrix, CONFIG_DISPLAY_LOG_LEVEL);
23 
24 #define MATRIX_NODE  DT_INST(0, nordic_nrf_led_matrix)
25 #define TIMER_NODE   DT_PHANDLE(MATRIX_NODE, timer)
26 #define USE_PWM      DT_NODE_HAS_PROP(MATRIX_NODE, pwm)
27 #define PWM_NODE     DT_PHANDLE(MATRIX_NODE, pwm)
28 #define ROW_COUNT    DT_PROP_LEN(MATRIX_NODE, row_gpios)
29 #define COL_COUNT    DT_PROP_LEN(MATRIX_NODE, col_gpios)
30 #define GROUP_SIZE   DT_PROP(MATRIX_NODE, pixel_group_size)
31 #if (GROUP_SIZE > DT_PROP(TIMER_NODE, cc_num) - 1) || \
32     (USE_PWM && GROUP_SIZE > PWM0_CH_NUM)
33 #error "Invalid pixel-group-size configured."
34 #endif
35 
36 #define X_PIXELS     DT_PROP(MATRIX_NODE, width)
37 #define Y_PIXELS     DT_PROP(MATRIX_NODE, height)
38 #define PIXEL_COUNT  DT_PROP_LEN(MATRIX_NODE, pixel_mapping)
39 #if (PIXEL_COUNT != (X_PIXELS * Y_PIXELS))
40 #error "Invalid length of pixel-mapping."
41 #endif
42 
43 #define PIXEL_MAPPING(idx)  DT_PROP_BY_IDX(MATRIX_NODE, pixel_mapping, idx)
44 
45 #define GET_DT_ROW_IDX(pixel_idx) \
46 	_GET_ROW_IDX(PIXEL_MAPPING(pixel_idx))
47 #define GET_ROW_IDX(dev_config, pixel_idx) \
48 	_GET_ROW_IDX(dev_config->pixel_mapping[pixel_idx])
49 #define _GET_ROW_IDX(byte)  (byte >> 4)
50 
51 #define GET_DT_COL_IDX(pixel_idx) \
52 	_GET_COL_IDX(PIXEL_MAPPING(pixel_idx))
53 #define GET_COL_IDX(dev_config, pixel_idx) \
54 	_GET_COL_IDX(dev_config->pixel_mapping[pixel_idx])
55 #define _GET_COL_IDX(byte)  (byte & 0xF)
56 
57 #define CHECK_PIXEL(node_id, pha, idx) \
58 	BUILD_ASSERT(GET_DT_ROW_IDX(idx) < ROW_COUNT, \
59 		     "Invalid row index in pixel-mapping["#idx"]."); \
60 	BUILD_ASSERT(GET_DT_COL_IDX(idx) < COL_COUNT, \
61 		     "Invalid column index in pixel-mapping["#idx"].");
62 DT_FOREACH_PROP_ELEM(MATRIX_NODE, pixel_mapping, CHECK_PIXEL)
63 
64 #define REFRESH_FREQUENCY  DT_PROP(MATRIX_NODE, refresh_frequency)
65 #define BASE_FREQUENCY     8000000
66 #define TIMER_CLK_CONFIG   NRF_TIMER_FREQ_8MHz
67 #define PWM_CLK_CONFIG     NRF_PWM_CLK_8MHz
68 #define BRIGHTNESS_MAX     255
69 
70 /* Always round up, as even a partially filled group uses the full time slot. */
71 #define PIXEL_SLOTS   (ROW_COUNT * NRFX_CEIL_DIV(COL_COUNT, GROUP_SIZE))
72 #define QUANTUM       (BASE_FREQUENCY \
73 		       / (REFRESH_FREQUENCY * PIXEL_SLOTS * BRIGHTNESS_MAX))
74 #define PIXEL_PERIOD  (BRIGHTNESS_MAX * QUANTUM)
75 #if (PIXEL_PERIOD > BIT_MASK(16)) || \
76     (USE_PWM && PIXEL_PERIOD > PWM_COUNTERTOP_COUNTERTOP_Msk)
77 #error "Invalid pixel period. Change refresh-frequency or pixel-group-size."
78 #endif
79 
80 #define ACTIVE_LOW_MASK  0x80
81 #define PSEL_MASK        0x7F
82 
83 #if (GROUP_SIZE > 1)
84 #define ITERATION_COUNT  ROW_COUNT * COL_COUNT
85 #else
86 #define ITERATION_COUNT  PIXEL_COUNT
87 #endif
88 
89 struct display_drv_config {
90 	NRF_TIMER_Type *timer;
91 #if USE_PWM
92 	NRF_PWM_Type *pwm;
93 #else
94 	nrfx_gpiote_t gpiote;
95 #endif
96 	uint8_t rows[ROW_COUNT];
97 	uint8_t cols[COL_COUNT];
98 	uint8_t pixel_mapping[PIXEL_COUNT];
99 #if (GROUP_SIZE > 1)
100 	uint8_t refresh_order[ITERATION_COUNT];
101 #endif
102 };
103 
104 struct display_drv_data {
105 #if USE_PWM
106 	uint16_t seq[PWM0_CH_NUM];
107 #else
108 	uint8_t gpiote_ch[GROUP_SIZE];
109 #endif
110 	uint8_t framebuf[PIXEL_COUNT];
111 	uint8_t iteration;
112 	uint8_t prev_row_idx;
113 	uint8_t brightness;
114 	bool    blanking;
115 };
116 
set_pin(uint8_t pin_info,bool active)117 static void set_pin(uint8_t pin_info, bool active)
118 {
119 	uint32_t value = active ? 1 : 0;
120 
121 	if (pin_info & ACTIVE_LOW_MASK) {
122 		value = !value;
123 	}
124 	nrf_gpio_pin_write(pin_info & PSEL_MASK, value);
125 }
126 
api_blanking_on(const struct device * dev)127 static int api_blanking_on(const struct device *dev)
128 {
129 	struct display_drv_data *dev_data = dev->data;
130 	const struct display_drv_config *dev_config = dev->config;
131 
132 	if (!dev_data->blanking) {
133 		nrf_timer_task_trigger(dev_config->timer, NRF_TIMER_TASK_STOP);
134 		for (uint8_t i = 0; i < ROW_COUNT; ++i) {
135 			set_pin(dev_config->rows[i], false);
136 		}
137 		for (uint8_t i = 0; i < COL_COUNT; ++i) {
138 			set_pin(dev_config->cols[i], false);
139 		}
140 
141 		dev_data->blanking = true;
142 	}
143 
144 	return 0;
145 }
146 
api_blanking_off(const struct device * dev)147 static int api_blanking_off(const struct device *dev)
148 {
149 	struct display_drv_data *dev_data = dev->data;
150 	const struct display_drv_config *dev_config = dev->config;
151 
152 	if (dev_data->blanking) {
153 		dev_data->iteration = ITERATION_COUNT - 1;
154 
155 		nrf_timer_task_trigger(dev_config->timer, NRF_TIMER_TASK_CLEAR);
156 		nrf_timer_task_trigger(dev_config->timer, NRF_TIMER_TASK_START);
157 
158 		dev_data->blanking = false;
159 	}
160 
161 	return 0;
162 }
163 
api_get_framebuffer(const struct device * dev)164 static void *api_get_framebuffer(const struct device *dev)
165 {
166 	struct display_drv_data *dev_data = dev->data;
167 
168 	return dev_data->framebuf;
169 }
170 
api_set_brightness(const struct device * dev,const uint8_t brightness)171 static int api_set_brightness(const struct device *dev,
172 			      const uint8_t brightness)
173 {
174 	struct display_drv_data *dev_data = dev->data;
175 	uint8_t new_brightness = CLAMP(brightness, 1, BRIGHTNESS_MAX);
176 	int16_t delta = (int16_t)new_brightness - dev_data->brightness;
177 
178 	dev_data->brightness = new_brightness;
179 
180 	for (uint8_t i = 0; i < PIXEL_COUNT; ++i) {
181 		uint8_t old_val = dev_data->framebuf[i];
182 
183 		if (old_val) {
184 			int16_t new_val = old_val + delta;
185 
186 			dev_data->framebuf[i] =
187 				(uint8_t)CLAMP(new_val, 1, BRIGHTNESS_MAX);
188 		}
189 	}
190 
191 	return 0;
192 }
193 
api_set_pixel_format(const struct device * dev,const enum display_pixel_format format)194 static int api_set_pixel_format(const struct device *dev,
195 				const enum display_pixel_format format)
196 {
197 	switch (format) {
198 	case PIXEL_FORMAT_MONO01:
199 		return 0;
200 	default:
201 		return -ENOTSUP;
202 	}
203 }
204 
api_set_orientation(const struct device * dev,const enum display_orientation orientation)205 static int api_set_orientation(const struct device *dev,
206 			       const enum display_orientation orientation)
207 {
208 	switch (orientation) {
209 	case DISPLAY_ORIENTATION_NORMAL:
210 		return 0;
211 	default:
212 		return -ENOTSUP;
213 	}
214 }
215 
api_get_capabilities(const struct device * dev,struct display_capabilities * caps)216 static void api_get_capabilities(const struct device *dev,
217 				 struct display_capabilities *caps)
218 {
219 	caps->x_resolution = X_PIXELS;
220 	caps->y_resolution = Y_PIXELS;
221 	caps->supported_pixel_formats = PIXEL_FORMAT_MONO01;
222 	caps->screen_info = 0;
223 	caps->current_pixel_format = PIXEL_FORMAT_MONO01;
224 	caps->current_orientation = DISPLAY_ORIENTATION_NORMAL;
225 }
226 
move_to_next_pixel(uint8_t * mask,uint8_t * data,const uint8_t ** byte_buf)227 static inline void move_to_next_pixel(uint8_t *mask, uint8_t *data,
228 				      const uint8_t **byte_buf)
229 {
230 	*mask <<= 1;
231 	if (!*mask) {
232 		*mask = 0x01;
233 		*data = *(*byte_buf)++;
234 	}
235 }
236 
api_write(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,const void * buf)237 static int api_write(const struct device *dev,
238 		     const uint16_t x, const uint16_t y,
239 		     const struct display_buffer_descriptor *desc,
240 		     const void *buf)
241 {
242 	struct display_drv_data *dev_data = dev->data;
243 	const uint8_t *byte_buf = buf;
244 	uint16_t end_x = x + desc->width;
245 	uint16_t end_y = y + desc->height;
246 
247 	if (x >= X_PIXELS || end_x > X_PIXELS ||
248 	    y >= Y_PIXELS || end_y > Y_PIXELS) {
249 		return -EINVAL;
250 	}
251 
252 	if (desc->pitch < desc->width) {
253 		return -EINVAL;
254 	}
255 
256 	uint16_t to_skip = desc->pitch - desc->width;
257 	uint8_t mask = 0;
258 	uint8_t data = 0;
259 
260 	for (uint16_t py = y; py < end_y; ++py) {
261 		for (uint16_t px = x; px < end_x; ++px) {
262 			move_to_next_pixel(&mask, &data, &byte_buf);
263 			dev_data->framebuf[px + (py * X_PIXELS)] =
264 				(data & mask) ? dev_data->brightness : 0;
265 		}
266 
267 		if (to_skip) {
268 			uint16_t cnt = to_skip;
269 
270 			do {
271 				move_to_next_pixel(&mask, &data, &byte_buf);
272 			} while (--cnt);
273 		}
274 	}
275 
276 	return 0;
277 }
278 
279 DEVICE_API(display, driver_api) = {
280 	.blanking_on = api_blanking_on,
281 	.blanking_off = api_blanking_off,
282 	.write = api_write,
283 	.get_framebuffer = api_get_framebuffer,
284 	.set_brightness = api_set_brightness,
285 	.get_capabilities = api_get_capabilities,
286 	.set_pixel_format = api_set_pixel_format,
287 	.set_orientation = api_set_orientation,
288 };
289 
prepare_pixel_pulse(const struct device * dev,uint8_t pixel_idx,uint8_t channel_idx)290 static void prepare_pixel_pulse(const struct device *dev,
291 				uint8_t pixel_idx,
292 				uint8_t channel_idx)
293 {
294 	struct display_drv_data *dev_data = dev->data;
295 	const struct display_drv_config *dev_config = dev->config;
296 
297 	uint8_t col_idx = GET_COL_IDX(dev_config, pixel_idx);
298 	uint8_t col_pin_info = dev_config->cols[col_idx];
299 	uint8_t col_psel = (col_pin_info & PSEL_MASK);
300 	bool col_active_low = (col_pin_info & ACTIVE_LOW_MASK);
301 	uint16_t pulse = dev_data->framebuf[pixel_idx] * QUANTUM;
302 
303 #if USE_PWM
304 	dev_config->pwm->PSEL.OUT[channel_idx] = col_psel;
305 	dev_data->seq[channel_idx] = pulse | (col_active_low ? 0 : BIT(15));
306 #else
307 	uint32_t gpiote_cfg = GPIOTE_CONFIG_MODE_Task
308 			    | (col_psel << GPIOTE_CONFIG_PSEL_Pos);
309 
310 	if (col_active_low) {
311 		gpiote_cfg |= (GPIOTE_CONFIG_POLARITY_LoToHi
312 			       << GPIOTE_CONFIG_POLARITY_Pos)
313 			      /* If there should be no pulse at all for
314 			       * a given pixel, its column GPIO needs
315 			       * to be configured as initially inactive.
316 			       */
317 			   |  ((pulse == 0 ? GPIOTE_CONFIG_OUTINIT_High
318 					   : GPIOTE_CONFIG_OUTINIT_Low)
319 			       << GPIOTE_CONFIG_OUTINIT_Pos);
320 	} else {
321 		gpiote_cfg |= (GPIOTE_CONFIG_POLARITY_HiToLo
322 			       << GPIOTE_CONFIG_POLARITY_Pos)
323 			   |  ((pulse == 0 ? GPIOTE_CONFIG_OUTINIT_Low
324 					   : GPIOTE_CONFIG_OUTINIT_High)
325 			       << GPIOTE_CONFIG_OUTINIT_Pos);
326 	}
327 
328 	/* First timer channel is used for timing the period of pulses. */
329 	nrf_timer_cc_set(dev_config->timer, 1 + channel_idx, pulse);
330 	dev_config->gpiote.p_reg->CONFIG[dev_data->gpiote_ch[channel_idx]] = gpiote_cfg;
331 #endif /* USE_PWM */
332 }
333 
334 
timer_irq_handler(void * arg)335 static void timer_irq_handler(void *arg)
336 {
337 	const struct device *dev = arg;
338 	struct display_drv_data *dev_data = dev->data;
339 	const struct display_drv_config *dev_config = dev->config;
340 	uint8_t iteration = dev_data->iteration;
341 	uint8_t pixel_idx;
342 	uint8_t row_idx;
343 
344 	/* The timer is automagically stopped and cleared by shortcuts
345 	 * on the same event (COMPARE0) that generates this interrupt.
346 	 * But the event itself needs to be cleared here.
347 	 */
348 	nrf_timer_event_clear(dev_config->timer, NRF_TIMER_EVENT_COMPARE0);
349 
350 	/* Disable the row that was enabled in the previous iteration. */
351 	set_pin(dev_config->rows[dev_data->prev_row_idx], false);
352 	/* Disconnect used column pins from the peripheral that drove them. */
353 #if USE_PWM
354 	nrf_pwm_disable(dev_config->pwm);
355 	for (int i = 0; i < GROUP_SIZE; ++i) {
356 		dev_config->pwm->PSEL.OUT[i] = NRF_PWM_PIN_NOT_CONNECTED;
357 	}
358 #else
359 	for (int i = 0; i < GROUP_SIZE; ++i) {
360 		dev_config->gpiote.p_reg->CONFIG[dev_data->gpiote_ch[i]] = 0;
361 	}
362 #endif
363 
364 	for (int i = 0; i < GROUP_SIZE; ++i) {
365 #if (GROUP_SIZE > 1)
366 		do {
367 			++iteration;
368 			if (iteration >= ITERATION_COUNT) {
369 				iteration = 0;
370 			}
371 
372 			pixel_idx = dev_config->refresh_order[iteration];
373 		} while (pixel_idx >= PIXEL_COUNT);
374 #else
375 		++iteration;
376 		if (iteration >= ITERATION_COUNT) {
377 			iteration = 0;
378 		}
379 
380 		pixel_idx = iteration;
381 #endif /* (GROUP_SIZE > 1) */
382 
383 		if (i == 0) {
384 			row_idx = GET_ROW_IDX(dev_config, pixel_idx);
385 		} else {
386 			/* If the next pixel is in a different row, it cannot
387 			 * be lit within this group.
388 			 */
389 			if (row_idx != GET_ROW_IDX(dev_config, pixel_idx)) {
390 				break;
391 			}
392 		}
393 
394 		dev_data->iteration = iteration;
395 
396 		prepare_pixel_pulse(dev, pixel_idx, i);
397 	}
398 
399 	/* Enable the row drive for the current pixel. */
400 	set_pin(dev_config->rows[row_idx], true);
401 
402 	dev_data->prev_row_idx = row_idx;
403 
404 #if USE_PWM
405 	/* Now that all the channels are configured, the PWM can be started. */
406 	nrf_pwm_enable(dev_config->pwm);
407 	nrf_pwm_task_trigger(dev_config->pwm, NRF_PWM_TASK_SEQSTART0);
408 #endif
409 
410 	/* Restart the timer. */
411 	nrf_timer_task_trigger(dev_config->timer, NRF_TIMER_TASK_START);
412 }
413 
instance_init(const struct device * dev)414 static int instance_init(const struct device *dev)
415 {
416 	struct display_drv_data *dev_data = dev->data;
417 	const struct display_drv_config *dev_config = dev->config;
418 
419 #if USE_PWM
420 	uint32_t out_psels[NRF_PWM_CHANNEL_COUNT] = {
421 		NRF_PWM_PIN_NOT_CONNECTED,
422 		NRF_PWM_PIN_NOT_CONNECTED,
423 		NRF_PWM_PIN_NOT_CONNECTED,
424 		NRF_PWM_PIN_NOT_CONNECTED,
425 	};
426 	nrf_pwm_sequence_t sequence = {
427 		.values.p_raw = dev_data->seq,
428 		.length = PWM0_CH_NUM,
429 	};
430 
431 	nrf_pwm_pins_set(dev_config->pwm, out_psels);
432 	nrf_pwm_configure(dev_config->pwm,
433 		PWM_CLK_CONFIG, NRF_PWM_MODE_UP, PIXEL_PERIOD);
434 	nrf_pwm_decoder_set(dev_config->pwm,
435 		NRF_PWM_LOAD_INDIVIDUAL, NRF_PWM_STEP_TRIGGERED);
436 	nrf_pwm_sequence_set(dev_config->pwm, 0, &sequence);
437 	nrf_pwm_loop_set(dev_config->pwm, 0);
438 	nrf_pwm_shorts_set(dev_config->pwm, NRF_PWM_SHORT_SEQEND0_STOP_MASK);
439 #else
440 	nrfx_err_t err;
441 	nrf_ppi_channel_t ppi_ch;
442 
443 	for (int i = 0; i < GROUP_SIZE; ++i) {
444 		uint8_t *gpiote_ch = &dev_data->gpiote_ch[i];
445 
446 		err = nrfx_ppi_channel_alloc(&ppi_ch);
447 		if (err != NRFX_SUCCESS) {
448 			LOG_ERR("Failed to allocate PPI channel.");
449 			/* Do not bother with freeing resources allocated
450 			 * so far. The application needs to be reconfigured
451 			 * anyway.
452 			 */
453 			return -ENOMEM;
454 		}
455 
456 		err = nrfx_gpiote_channel_alloc(&dev_config->gpiote, gpiote_ch);
457 		if (err != NRFX_SUCCESS) {
458 			LOG_ERR("Failed to allocate GPIOTE channel.");
459 			/* Do not bother with freeing resources allocated
460 			 * so far. The application needs to be reconfigured
461 			 * anyway.
462 			 */
463 			return -ENOMEM;
464 		}
465 
466 		nrf_ppi_channel_endpoint_setup(NRF_PPI, ppi_ch,
467 			nrf_timer_event_address_get(dev_config->timer,
468 				nrf_timer_compare_event_get(1 + i)),
469 			nrf_gpiote_event_address_get(dev_config->gpiote.p_reg,
470 				nrf_gpiote_out_task_get(*gpiote_ch)));
471 		nrf_ppi_channel_enable(NRF_PPI, ppi_ch);
472 	}
473 #endif /* USE_PWM */
474 
475 	for (uint8_t i = 0; i < ROW_COUNT; ++i) {
476 		uint8_t row_pin_info = dev_config->rows[i];
477 
478 		set_pin(row_pin_info, false);
479 		nrf_gpio_cfg(row_pin_info & PSEL_MASK,
480 			     NRF_GPIO_PIN_DIR_OUTPUT,
481 			     NRF_GPIO_PIN_INPUT_DISCONNECT,
482 			     NRF_GPIO_PIN_NOPULL,
483 			     NRF_GPIO_PIN_H0H1,
484 			     NRF_GPIO_PIN_NOSENSE);
485 	}
486 
487 	for (uint8_t i = 0; i < COL_COUNT; ++i) {
488 		uint8_t col_pin_info = dev_config->cols[i];
489 
490 		set_pin(col_pin_info, false);
491 		nrf_gpio_cfg(col_pin_info & PSEL_MASK,
492 			     NRF_GPIO_PIN_DIR_OUTPUT,
493 			     NRF_GPIO_PIN_INPUT_DISCONNECT,
494 			     NRF_GPIO_PIN_NOPULL,
495 			     NRF_GPIO_PIN_S0S1,
496 			     NRF_GPIO_PIN_NOSENSE);
497 	}
498 
499 	nrf_timer_bit_width_set(dev_config->timer, NRF_TIMER_BIT_WIDTH_16);
500 	nrf_timer_prescaler_set(dev_config->timer, TIMER_CLK_CONFIG);
501 	nrf_timer_cc_set(dev_config->timer, 0, PIXEL_PERIOD);
502 	nrf_timer_shorts_set(dev_config->timer,
503 			     NRF_TIMER_SHORT_COMPARE0_STOP_MASK |
504 			     NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK);
505 	nrf_timer_event_clear(dev_config->timer, NRF_TIMER_EVENT_COMPARE0);
506 	nrf_timer_int_enable(dev_config->timer, NRF_TIMER_INT_COMPARE0_MASK);
507 
508 	IRQ_CONNECT(DT_IRQN(TIMER_NODE), DT_IRQ(TIMER_NODE, priority),
509 		    timer_irq_handler, DEVICE_DT_GET(MATRIX_NODE), 0);
510 	irq_enable(DT_IRQN(TIMER_NODE));
511 
512 	return 0;
513 }
514 
515 static struct display_drv_data instance_data = {
516 	.brightness = 0xFF,
517 	.blanking   = true,
518 };
519 
520 #if !USE_PWM
521 #define CHECK_GPIOTE_INST(node_id, prop, idx) \
522 	BUILD_ASSERT(NRF_DT_GPIOTE_INST_BY_IDX(node_id, prop, idx) == \
523 		     NRF_DT_GPIOTE_INST_BY_IDX(node_id, prop, 0), \
524 		"All column GPIOs must use the same GPIOTE instance");
525 DT_FOREACH_PROP_ELEM(MATRIX_NODE, col_gpios, CHECK_GPIOTE_INST)
526 #endif
527 
528 #define GET_PIN_INFO(node_id, pha, idx) \
529 	(DT_GPIO_PIN_BY_IDX(node_id, pha, idx) | \
530 	 (DT_PROP_BY_PHANDLE_IDX(node_id, pha, idx, port) << 5) | \
531 	 ((DT_GPIO_FLAGS_BY_IDX(node_id, pha, idx) & GPIO_ACTIVE_LOW) ? \
532 		ACTIVE_LOW_MASK : 0)),
533 
534 #define ADD_FF(i, _) 0xFF
535 #define FILL_ROW_WITH_FF(node_id, pha, idx)  LISTIFY(COL_COUNT, ADD_FF, (,)),
536 #define GET_PIXEL_ORDINAL(node_id, pha, idx) \
537 	[GET_DT_ROW_IDX(idx) * COL_COUNT + \
538 	 GET_DT_COL_IDX(idx)] = idx,
539 
540 static const struct display_drv_config instance_config = {
541 	.timer = (NRF_TIMER_Type *)DT_REG_ADDR(TIMER_NODE),
542 #if USE_PWM
543 	.pwm = (NRF_PWM_Type *)DT_REG_ADDR(PWM_NODE),
544 #else
545 	.gpiote = NRFX_GPIOTE_INSTANCE(
546 			NRF_DT_GPIOTE_INST_BY_IDX(MATRIX_NODE, col_gpios, 0)),
547 #endif
548 	.rows = { DT_FOREACH_PROP_ELEM(MATRIX_NODE, row_gpios, GET_PIN_INFO) },
549 	.cols = { DT_FOREACH_PROP_ELEM(MATRIX_NODE, col_gpios, GET_PIN_INFO) },
550 	.pixel_mapping = DT_PROP(MATRIX_NODE, pixel_mapping),
551 #if (GROUP_SIZE > 1)
552 	/* The whole array is by default filled with FFs, then the elements
553 	 * for the actually used row/columns pairs are overwritten (using
554 	 * designators) with the proper ordinal values for pixels.
555 	 */
556 	.refresh_order = { DT_FOREACH_PROP_ELEM(MATRIX_NODE, row_gpios,
557 						FILL_ROW_WITH_FF)
558 			   DT_FOREACH_PROP_ELEM(MATRIX_NODE, pixel_mapping,
559 						GET_PIXEL_ORDINAL) },
560 #endif
561 };
562 
563 DEVICE_DT_DEFINE(MATRIX_NODE,
564 		 instance_init, NULL,
565 		 &instance_data, &instance_config,
566 		 POST_KERNEL, CONFIG_DISPLAY_INIT_PRIORITY, &driver_api);
567