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