1 /*
2  * Copyright 2023 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nxp_mipi_dbi_flexio_lcdif
8 
9 #include <zephyr/drivers/dma.h>
10 #include <zephyr/drivers/display.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/drivers/pinctrl.h>
13 #include <zephyr/drivers/mipi_dbi.h>
14 #include <zephyr/drivers/misc/nxp_flexio/nxp_flexio.h>
15 #include <zephyr/kernel.h>
16 #include <zephyr/logging/log.h>
17 #include <fsl_edma.h>
18 #include <fsl_flexio_mculcd.h>
19 
20 LOG_MODULE_REGISTER(display_mcux_flexio_lcdif, CONFIG_DISPLAY_LOG_LEVEL);
21 
22 struct stream {
23 	const struct device *dma_dev;
24 	uint32_t channel; /* stores the channel for dma */
25 	struct dma_config dma_cfg;
26 	struct dma_block_config dma_blk_cfg;
27 };
28 
29 struct mcux_flexio_lcdif_config {
30 	FLEXIO_MCULCD_Type *flexio_lcd_dev;
31 	const struct device *flexio_dev;
32 	const struct pinctrl_dev_config *pincfg;
33 	const struct nxp_flexio_child *child;
34 	/* Reset GPIO */
35 	const struct gpio_dt_spec reset;
36 	const struct gpio_dt_spec cs_gpio;
37 	const struct gpio_dt_spec rs_gpio;
38 	const struct gpio_dt_spec rdwr_gpio;
39 };
40 
41 struct mcux_flexio_lcdif_data {
42 	struct stream dma_tx;
43 	struct k_sem transfer_done;
44 	const struct mipi_dbi_config *active_cfg;
45 	uint8_t data_bus_width;
46 };
47 
flexio_lcdif_dma_callback(const struct device * dev,void * arg,uint32_t channel,int status)48 static void flexio_lcdif_dma_callback(const struct device *dev, void *arg,
49 				      uint32_t channel, int status)
50 {
51 	const struct device *flexio_dev = (struct device *)arg;
52 	struct mcux_flexio_lcdif_data *lcdif_data = flexio_dev->data;
53 	const struct mcux_flexio_lcdif_config *config = flexio_dev->config;
54 	FLEXIO_MCULCD_Type *flexio_lcd = config->flexio_lcd_dev;
55 
56 	FLEXIO_MCULCD_EnableTxDMA(flexio_lcd, false);
57 
58 	/* Now the data are in shifter, wait for the data send out from the shifter. */
59 	FLEXIO_MCULCD_WaitTransmitComplete();
60 
61 	/* Disable the TX shifter and the timer. */
62 	FLEXIO_MCULCD_ClearMultiBeatsWriteConfig(flexio_lcd);
63 
64 	/* De-assert nCS. */
65 	FLEXIO_MCULCD_StopTransfer(flexio_lcd);
66 
67 	k_sem_give(&lcdif_data->transfer_done);
68 }
69 
70 
flexio_lcdif_set_cs(bool set,void * param)71 static void flexio_lcdif_set_cs(bool set, void *param)
72 {
73 	const struct device *flexio_dev = (struct device *)param;
74 	const struct mcux_flexio_lcdif_config *config = flexio_dev->config;
75 
76 	gpio_pin_set_dt(&config->cs_gpio, (int)set);
77 }
78 
flexio_lcdif_set_rs(bool set,void * param)79 static void flexio_lcdif_set_rs(bool set, void *param)
80 {
81 	const struct device *flexio_dev = (struct device *)param;
82 	const struct mcux_flexio_lcdif_config *config = flexio_dev->config;
83 
84 	gpio_pin_set_dt(&config->rs_gpio, (int)set);
85 }
86 
flexio_lcdif_set_rd_wr(bool set,void * param)87 static void flexio_lcdif_set_rd_wr(bool set, void *param)
88 {
89 	const struct device *flexio_dev = (struct device *)param;
90 	const struct mcux_flexio_lcdif_config *config = flexio_dev->config;
91 
92 	gpio_pin_set_dt(&config->rdwr_gpio, (int)set);
93 }
94 
flexio_lcdif_get_edma_modulo(uint8_t shifterNum)95 static edma_modulo_t flexio_lcdif_get_edma_modulo(uint8_t shifterNum)
96 {
97 	edma_modulo_t ret = kEDMA_ModuloDisable;
98 
99 	switch (shifterNum) {
100 	case 1U:
101 		ret = kEDMA_Modulo4bytes;
102 		break;
103 	case 2U:
104 		ret = kEDMA_Modulo8bytes;
105 		break;
106 	case 4U:
107 		ret = kEDMA_Modulo16bytes;
108 		break;
109 	case 8U:
110 		ret = kEDMA_Modulo32bytes;
111 		break;
112 	default:
113 		ret = kEDMA_ModuloDisable;
114 		break;
115 	}
116 
117 	return ret;
118 }
119 
flexio_lcdif_write_data_array(FLEXIO_MCULCD_Type * base,const void * data,size_t size)120 static void flexio_lcdif_write_data_array(FLEXIO_MCULCD_Type *base,
121 					  const void *data,
122 					  size_t size)
123 {
124 	assert(size > 0U);
125 
126 	uint32_t i;
127 	const uint8_t *data8Bit;
128 	FLEXIO_Type *flexioBase = base->flexioBase;
129 
130 	/* Assert the RS pin. */
131 	base->setRSPin(true, base->userData);
132 	/* For 6800, de-assert the RDWR pin. */
133 	if (kFLEXIO_MCULCD_6800 == base->busType) {
134 		base->setRDWRPin(false, base->userData);
135 	}
136 
137 	/* Configure the timer and TX shifter. */
138 	FLEXIO_MCULCD_SetSingleBeatWriteConfig(base);
139 
140 	data8Bit = (const uint8_t *)data;
141 
142 	for (i = 0; i < size; i++) {
143 		flexioBase->SHIFTBUF[base->txShifterStartIndex] = data8Bit[i];
144 
145 		/* Wait for the data send out. */
146 		while (0U == ((1UL << base->timerIndex) & flexioBase->TIMSTAT)) {
147 		}
148 
149 		/* Clear the timer stat. */
150 		flexioBase->TIMSTAT = 1UL << base->timerIndex;
151 	}
152 
153 	/* Stop the timer and TX shifter. */
154 	FLEXIO_MCULCD_ClearSingleBeatWriteConfig(base);
155 }
156 
mipi_dbi_flexio_lcdif_configure(const struct device * dev,const struct mipi_dbi_config * dbi_config)157 static int mipi_dbi_flexio_lcdif_configure(const struct device *dev,
158 					   const struct mipi_dbi_config *dbi_config)
159 {
160 	const struct mcux_flexio_lcdif_config *config = dev->config;
161 	struct mcux_flexio_lcdif_data *lcdif_data = dev->data;
162 	flexio_mculcd_config_t flexioMcuLcdConfig;
163 	int err;
164 	uint32_t clock_freq;
165 	uint32_t mipi_mode = dbi_config->mode;
166 	status_t status;
167 
168 	/* 9-bit mode is not supported by the SDK driver */
169 	if ((mipi_mode == MIPI_DBI_MODE_6800_BUS_9_BIT) ||
170 	    (mipi_mode == MIPI_DBI_MODE_8080_BUS_9_BIT)) {
171 		return -EINVAL;
172 	}
173 
174 	if (dbi_config == lcdif_data->active_cfg) {
175 		return 0;
176 	}
177 
178 	err = gpio_pin_configure_dt(&config->cs_gpio, GPIO_OUTPUT_HIGH);
179 	if (err) {
180 		return err;
181 	}
182 
183 	err = gpio_pin_configure_dt(&config->rs_gpio, GPIO_OUTPUT_HIGH);
184 	if (err) {
185 		return err;
186 	}
187 
188 	if ((mipi_mode == MIPI_DBI_MODE_6800_BUS_16_BIT) ||
189 	    (mipi_mode == MIPI_DBI_MODE_6800_BUS_8_BIT)) {
190 		/* RDWR GPIO is only used in 68K mode */
191 		err = gpio_pin_configure_dt(&config->rdwr_gpio, GPIO_OUTPUT_HIGH);
192 		if (err) {
193 			return err;
194 		}
195 		config->flexio_lcd_dev->busType = kFLEXIO_MCULCD_6800;
196 	} else {
197 		config->flexio_lcd_dev->busType = kFLEXIO_MCULCD_8080;
198 	}
199 
200 	if ((mipi_mode == MIPI_DBI_MODE_6800_BUS_8_BIT) ||
201 	    (mipi_mode == MIPI_DBI_MODE_8080_BUS_8_BIT)) {
202 		lcdif_data->data_bus_width = 8;
203 	} else {
204 		lcdif_data->data_bus_width = 16;
205 	}
206 
207 	FLEXIO_MCULCD_GetDefaultConfig(&flexioMcuLcdConfig);
208 	flexioMcuLcdConfig.baudRate_Bps = dbi_config->config.frequency *
209 					  lcdif_data->data_bus_width;
210 
211 	if (nxp_flexio_get_rate(config->flexio_dev, &clock_freq)) {
212 		return -EINVAL;
213 	}
214 
215 	nxp_flexio_lock(config->flexio_dev);
216 	/* Resets the FlexIO module, then configures FlexIO MCULCD */
217 	status = FLEXIO_MCULCD_Init(config->flexio_lcd_dev, &flexioMcuLcdConfig, clock_freq);
218 	nxp_flexio_unlock(config->flexio_dev);
219 
220 	if (kStatus_Success != status) {
221 		return -EINVAL;
222 	}
223 
224 	lcdif_data->active_cfg = dbi_config;
225 
226 	return 0;
227 }
228 
mipi_dbi_flexio_ldcif_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)229 static int mipi_dbi_flexio_ldcif_write_display(const struct device *dev,
230 					       const struct mipi_dbi_config *dbi_config,
231 					       const uint8_t *framebuf,
232 					       struct display_buffer_descriptor *desc,
233 					       enum display_pixel_format pixfmt)
234 {
235 	const struct mcux_flexio_lcdif_config *config = dev->config;
236 	struct mcux_flexio_lcdif_data *lcdif_data = dev->data;
237 	FLEXIO_MCULCD_Type *flexio_lcd = config->flexio_lcd_dev;
238 	struct dma_block_config *blk_cfg;
239 	struct stream *stream = &lcdif_data->dma_tx;
240 	uint8_t num_of_shifters = 0;
241 	int ret;
242 
243 	ARG_UNUSED(pixfmt);
244 
245 	ret = mipi_dbi_flexio_lcdif_configure(dev, dbi_config);
246 	if (ret) {
247 		return ret;
248 	}
249 
250 	num_of_shifters = (flexio_lcd->txShifterEndIndex - flexio_lcd->txShifterStartIndex + 1);
251 
252 	blk_cfg = &stream->dma_blk_cfg;
253 
254 	/* Assert the nCS. */
255 	FLEXIO_MCULCD_StartTransfer(config->flexio_lcd_dev);
256 
257 	/* prepare the block for this TX DMA channel */
258 	memset(blk_cfg, 0, sizeof(struct dma_block_config));
259 
260 	/* tx direction has memory as source and periph as dest. */
261 	blk_cfg->source_address = (uint32_t)framebuf;
262 
263 	/* Destination is FLEXIO Shifters */
264 	blk_cfg->dest_address = FLEXIO_MCULCD_GetTxDataRegisterAddress(flexio_lcd);
265 	blk_cfg->block_size = desc->buf_size;
266 	/* Transfer in each DMA loop is based on the number of shifters used */
267 	stream->dma_cfg.source_burst_length = num_of_shifters * 4;
268 
269 	stream->dma_cfg.head_block = &stream->dma_blk_cfg;
270 	/* Give the client dev as arg, as the callback comes from the dma */
271 	stream->dma_cfg.user_data = (struct device *)dev;
272 
273 	/* Set the source size in bytes */
274 	stream->dma_cfg.source_data_size = lcdif_data->data_bus_width / 8;
275 
276 	/* Configure the DMA */
277 	dma_config(lcdif_data->dma_tx.dma_dev, lcdif_data->dma_tx.channel, &stream->dma_cfg);
278 
279 	/* The DMA driver does not support setting this Modulo value which is required
280 	 * in case of the flexio module to form a circular chain between the Shift buffer
281 	 * in the FLEXIO module.
282 	 */
283 	EDMA_SetModulo(DMA0, lcdif_data->dma_tx.channel, kEDMA_ModuloDisable,
284 		       flexio_lcdif_get_edma_modulo(num_of_shifters));
285 
286 	/* For 6800, de-assert the RDWR pin. */
287 	if (kFLEXIO_MCULCD_6800 == flexio_lcd->busType) {
288 		flexio_lcdif_set_rd_wr(false, (void *)dev);
289 	}
290 
291 	nxp_flexio_lock(config->flexio_dev);
292 	FLEXIO_MCULCD_SetMultiBeatsWriteConfig(flexio_lcd);
293 	FLEXIO_MCULCD_EnableTxDMA(flexio_lcd, true);
294 	nxp_flexio_unlock(config->flexio_dev);
295 
296 	/* Start the data transfer */
297 	dma_start(lcdif_data->dma_tx.dma_dev, lcdif_data->dma_tx.channel);
298 
299 	/* Wait for transfer done. */
300 	k_sem_take(&lcdif_data->transfer_done, K_FOREVER);
301 
302 	return 0;
303 }
304 
mipi_dbi_flexio_lcdif_command_write(const struct device * dev,const struct mipi_dbi_config * dbi_config,uint8_t cmd,const uint8_t * data_buf,size_t len)305 static int mipi_dbi_flexio_lcdif_command_write(const struct device *dev,
306 					       const struct mipi_dbi_config *dbi_config,
307 					       uint8_t cmd, const uint8_t *data_buf,
308 					       size_t len)
309 {
310 	const struct mcux_flexio_lcdif_config *config = dev->config;
311 	FLEXIO_MCULCD_Type *flexio_lcd = config->flexio_lcd_dev;
312 	int ret;
313 
314 	ARG_UNUSED(dbi_config);
315 
316 	ret = mipi_dbi_flexio_lcdif_configure(dev, dbi_config);
317 	if (ret) {
318 		return ret;
319 	}
320 
321 	FLEXIO_MCULCD_StartTransfer(flexio_lcd);
322 
323 	nxp_flexio_lock(config->flexio_dev);
324 
325 	FLEXIO_MCULCD_WriteCommandBlocking(flexio_lcd, cmd);
326 
327 	if ((data_buf != NULL) && (len != 0)) {
328 		flexio_lcdif_write_data_array(flexio_lcd, data_buf, len);
329 	}
330 
331 	nxp_flexio_unlock(config->flexio_dev);
332 
333 	FLEXIO_MCULCD_StopTransfer(flexio_lcd);
334 
335 	return kStatus_Success;
336 
337 }
338 
mipi_dbi_flexio_lcdif_reset(const struct device * dev,k_timeout_t delay)339 static int mipi_dbi_flexio_lcdif_reset(const struct device *dev, k_timeout_t delay)
340 {
341 	int err;
342 	const struct mcux_flexio_lcdif_config *config = dev->config;
343 
344 	/* Check if a reset port is provided to reset the LCD controller */
345 	if (config->reset.port == NULL) {
346 		return 0;
347 	}
348 
349 	/* Reset the LCD controller. */
350 	err = gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_HIGH);
351 	if (err) {
352 		return err;
353 	}
354 
355 	err = gpio_pin_set_dt(&config->reset, 0);
356 	if (err < 0) {
357 		return err;
358 	}
359 
360 	k_sleep(delay);
361 
362 	err = gpio_pin_set_dt(&config->reset, 1);
363 	if (err < 0) {
364 		return err;
365 	}
366 
367 	LOG_DBG("%s device reset complete", dev->name);
368 
369 	return 0;
370 }
371 
flexio_lcdif_init(const struct device * dev)372 static int flexio_lcdif_init(const struct device *dev)
373 {
374 	const struct mcux_flexio_lcdif_config *config = dev->config;
375 	struct mcux_flexio_lcdif_data *lcdif_data = dev->data;
376 	int err;
377 	uint8_t shifter_end = config->child->res.shifter_count - 1;
378 
379 	if (!device_is_ready(lcdif_data->dma_tx.dma_dev)) {
380 		LOG_ERR("%s device is not ready", lcdif_data->dma_tx.dma_dev->name);
381 		return -ENODEV;
382 	}
383 
384 	err = nxp_flexio_child_attach(config->flexio_dev, config->child);
385 	if (err < 0) {
386 		return err;
387 	}
388 
389 	config->flexio_lcd_dev->txShifterStartIndex = config->child->res.shifter_index[0];
390 	config->flexio_lcd_dev->txShifterEndIndex = config->child->res.shifter_index[shifter_end];
391 
392 	config->flexio_lcd_dev->rxShifterStartIndex = config->flexio_lcd_dev->txShifterStartIndex;
393 	config->flexio_lcd_dev->rxShifterEndIndex = config->flexio_lcd_dev->txShifterEndIndex;
394 
395 	config->flexio_lcd_dev->timerIndex = config->child->res.timer_index[0];
396 
397 	if (config->flexio_lcd_dev->txShifterEndIndex !=
398 	    config->flexio_lcd_dev->txShifterStartIndex + shifter_end) {
399 		LOG_ERR("Shifters should be continuous");
400 		return -ENODEV;
401 	}
402 	err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
403 	if (err) {
404 		return err;
405 	}
406 
407 	/* Pass the FlexIO LCD device as parameter to the function
408 	 * callbacks for setting GPIO signals.
409 	 */
410 	config->flexio_lcd_dev->userData = (void *)dev;
411 
412 
413 	k_sem_init(&lcdif_data->transfer_done, 0, 1);
414 
415 	LOG_DBG("%s device init complete", dev->name);
416 
417 	return 0;
418 }
419 
420 static DEVICE_API(mipi_dbi, mipi_dbi_lcdif_driver_api) = {
421 	.reset = mipi_dbi_flexio_lcdif_reset,
422 	.command_write = mipi_dbi_flexio_lcdif_command_write,
423 	.write_display = mipi_dbi_flexio_ldcif_write_display,
424 };
425 
426 #define MCUX_FLEXIO_LCDIF_DEVICE_INIT(n)						\
427 	PINCTRL_DT_INST_DEFINE(n);							\
428 											\
429 	static FLEXIO_MCULCD_Type flexio_mculcd_##n = {					\
430 		.flexioBase = (FLEXIO_Type *)DT_REG_ADDR(DT_INST_PARENT(n)),		\
431 		.dataPinStartIndex = DT_INST_PROP(n, data_pin_start),			\
432 		.ENWRPinIndex = DT_INST_PROP(n, enwr_pin),				\
433 		.RDPinIndex = DT_INST_PROP_OR(n, rd_pin, 0),				\
434 		.setCSPin = flexio_lcdif_set_cs,					\
435 		.setRSPin = flexio_lcdif_set_rs,					\
436 		.setRDWRPin = flexio_lcdif_set_rd_wr,					\
437 	};										\
438 											\
439 	static uint8_t mcux_flexio_lcdif_shifters_##n[DT_INST_PROP(n, shifters_count)];	\
440 	static uint8_t mcux_flexio_lcdif_timers_##n[DT_INST_PROP(n, timers_count)];	\
441 											\
442 	static const struct nxp_flexio_child lcdif_child_##n = {			\
443 		.isr = NULL,								\
444 		.user_data = (void *)DEVICE_DT_INST_GET(n),				\
445 		.res = {								\
446 			.shifter_index = mcux_flexio_lcdif_shifters_##n,		\
447 			.shifter_count = ARRAY_SIZE(mcux_flexio_lcdif_shifters_##n),	\
448 			.timer_index = mcux_flexio_lcdif_timers_##n,			\
449 			.timer_count = ARRAY_SIZE(mcux_flexio_lcdif_timers_##n),	\
450 		}									\
451 	};										\
452 											\
453 	struct mcux_flexio_lcdif_config mcux_flexio_lcdif_config_##n = {		\
454 		.flexio_lcd_dev = &flexio_mculcd_##n,					\
455 		.flexio_dev = DEVICE_DT_GET(DT_INST_PARENT(n)),				\
456 		.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n),				\
457 		.child = &lcdif_child_##n,						\
458 		.reset = GPIO_DT_SPEC_INST_GET(n, reset_gpios),				\
459 		.cs_gpio = GPIO_DT_SPEC_INST_GET(n, cs_gpios),				\
460 		.rs_gpio = GPIO_DT_SPEC_INST_GET(n, rs_gpios),				\
461 		.rdwr_gpio = GPIO_DT_SPEC_INST_GET_OR(n, rdwr_gpios, {0}),		\
462 	};										\
463 	struct mcux_flexio_lcdif_data mcux_flexio_lcdif_data_##n = {			\
464 		.dma_tx = {								\
465 			.dma_dev = DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_NAME(n, tx)),	\
466 			.channel = DT_INST_DMAS_CELL_BY_NAME(n, tx, mux),		\
467 			.dma_cfg = {							\
468 				.channel_direction = MEMORY_TO_MEMORY,			\
469 				.dma_callback = flexio_lcdif_dma_callback,		\
470 				.dest_data_size = 4,					\
471 				.block_count = 1,					\
472 				.dma_slot = DT_INST_DMAS_CELL_BY_NAME(n, tx, source)	\
473 			}								\
474 		},									\
475 	};										\
476 	DEVICE_DT_INST_DEFINE(n,							\
477 		&flexio_lcdif_init,							\
478 		NULL,									\
479 		&mcux_flexio_lcdif_data_##n,						\
480 		&mcux_flexio_lcdif_config_##n,						\
481 		POST_KERNEL,								\
482 		CONFIG_MIPI_DBI_INIT_PRIORITY,						\
483 		&mipi_dbi_lcdif_driver_api);
484 
485 DT_INST_FOREACH_STATUS_OKAY(MCUX_FLEXIO_LCDIF_DEVICE_INIT)
486