/* * Copyright 2023 NXP * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nxp_mipi_dbi_flexio_lcdif #include #include #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(display_mcux_flexio_lcdif, CONFIG_DISPLAY_LOG_LEVEL); struct stream { const struct device *dma_dev; uint32_t channel; /* stores the channel for dma */ struct dma_config dma_cfg; struct dma_block_config dma_blk_cfg; }; struct mcux_flexio_lcdif_config { FLEXIO_MCULCD_Type *flexio_lcd_dev; const struct device *flexio_dev; const struct pinctrl_dev_config *pincfg; const struct nxp_flexio_child *child; /* Reset GPIO */ const struct gpio_dt_spec reset; const struct gpio_dt_spec cs_gpio; const struct gpio_dt_spec rs_gpio; const struct gpio_dt_spec rdwr_gpio; }; struct mcux_flexio_lcdif_data { struct stream dma_tx; struct k_sem transfer_done; const struct mipi_dbi_config *active_cfg; uint8_t data_bus_width; }; static void flexio_lcdif_dma_callback(const struct device *dev, void *arg, uint32_t channel, int status) { const struct device *flexio_dev = (struct device *)arg; struct mcux_flexio_lcdif_data *lcdif_data = flexio_dev->data; const struct mcux_flexio_lcdif_config *config = flexio_dev->config; FLEXIO_MCULCD_Type *flexio_lcd = config->flexio_lcd_dev; FLEXIO_MCULCD_EnableTxDMA(flexio_lcd, false); /* Now the data are in shifter, wait for the data send out from the shifter. */ FLEXIO_MCULCD_WaitTransmitComplete(); /* Disable the TX shifter and the timer. */ FLEXIO_MCULCD_ClearMultiBeatsWriteConfig(flexio_lcd); /* De-assert nCS. */ FLEXIO_MCULCD_StopTransfer(flexio_lcd); k_sem_give(&lcdif_data->transfer_done); } static void flexio_lcdif_set_cs(bool set, void *param) { const struct device *flexio_dev = (struct device *)param; const struct mcux_flexio_lcdif_config *config = flexio_dev->config; gpio_pin_set_dt(&config->cs_gpio, (int)set); } static void flexio_lcdif_set_rs(bool set, void *param) { const struct device *flexio_dev = (struct device *)param; const struct mcux_flexio_lcdif_config *config = flexio_dev->config; gpio_pin_set_dt(&config->rs_gpio, (int)set); } static void flexio_lcdif_set_rd_wr(bool set, void *param) { const struct device *flexio_dev = (struct device *)param; const struct mcux_flexio_lcdif_config *config = flexio_dev->config; gpio_pin_set_dt(&config->rdwr_gpio, (int)set); } static edma_modulo_t flexio_lcdif_get_edma_modulo(uint8_t shifterNum) { edma_modulo_t ret = kEDMA_ModuloDisable; switch (shifterNum) { case 1U: ret = kEDMA_Modulo4bytes; break; case 2U: ret = kEDMA_Modulo8bytes; break; case 4U: ret = kEDMA_Modulo16bytes; break; case 8U: ret = kEDMA_Modulo32bytes; break; default: ret = kEDMA_ModuloDisable; break; } return ret; } static void flexio_lcdif_write_data_array(FLEXIO_MCULCD_Type *base, const void *data, size_t size) { assert(size > 0U); uint32_t i; const uint8_t *data8Bit; FLEXIO_Type *flexioBase = base->flexioBase; /* Assert the RS pin. */ base->setRSPin(true, base->userData); /* For 6800, de-assert the RDWR pin. */ if (kFLEXIO_MCULCD_6800 == base->busType) { base->setRDWRPin(false, base->userData); } /* Configure the timer and TX shifter. */ FLEXIO_MCULCD_SetSingleBeatWriteConfig(base); data8Bit = (const uint8_t *)data; for (i = 0; i < size; i++) { flexioBase->SHIFTBUF[base->txShifterStartIndex] = data8Bit[i]; /* Wait for the data send out. */ while (0U == ((1UL << base->timerIndex) & flexioBase->TIMSTAT)) { } /* Clear the timer stat. */ flexioBase->TIMSTAT = 1UL << base->timerIndex; } /* Stop the timer and TX shifter. */ FLEXIO_MCULCD_ClearSingleBeatWriteConfig(base); } static int mipi_dbi_flexio_lcdif_configure(const struct device *dev, const struct mipi_dbi_config *dbi_config) { const struct mcux_flexio_lcdif_config *config = dev->config; struct mcux_flexio_lcdif_data *lcdif_data = dev->data; flexio_mculcd_config_t flexioMcuLcdConfig; int err; uint32_t clock_freq; uint32_t mipi_mode = dbi_config->mode; status_t status; /* 9-bit mode is not supported by the SDK driver */ if ((mipi_mode == MIPI_DBI_MODE_6800_BUS_9_BIT) || (mipi_mode == MIPI_DBI_MODE_8080_BUS_9_BIT)) { return -EINVAL; } if (dbi_config == lcdif_data->active_cfg) { return 0; } err = gpio_pin_configure_dt(&config->cs_gpio, GPIO_OUTPUT_HIGH); if (err) { return err; } err = gpio_pin_configure_dt(&config->rs_gpio, GPIO_OUTPUT_HIGH); if (err) { return err; } if ((mipi_mode == MIPI_DBI_MODE_6800_BUS_16_BIT) || (mipi_mode == MIPI_DBI_MODE_6800_BUS_8_BIT)) { /* RDWR GPIO is only used in 68K mode */ err = gpio_pin_configure_dt(&config->rdwr_gpio, GPIO_OUTPUT_HIGH); if (err) { return err; } config->flexio_lcd_dev->busType = kFLEXIO_MCULCD_6800; } else { config->flexio_lcd_dev->busType = kFLEXIO_MCULCD_8080; } if ((mipi_mode == MIPI_DBI_MODE_6800_BUS_8_BIT) || (mipi_mode == MIPI_DBI_MODE_8080_BUS_8_BIT)) { lcdif_data->data_bus_width = 8; } else { lcdif_data->data_bus_width = 16; } FLEXIO_MCULCD_GetDefaultConfig(&flexioMcuLcdConfig); flexioMcuLcdConfig.baudRate_Bps = dbi_config->config.frequency * lcdif_data->data_bus_width; if (nxp_flexio_get_rate(config->flexio_dev, &clock_freq)) { return -EINVAL; } nxp_flexio_lock(config->flexio_dev); /* Resets the FlexIO module, then configures FlexIO MCULCD */ status = FLEXIO_MCULCD_Init(config->flexio_lcd_dev, &flexioMcuLcdConfig, clock_freq); nxp_flexio_unlock(config->flexio_dev); if (kStatus_Success != status) { return -EINVAL; } lcdif_data->active_cfg = dbi_config; return 0; } static int 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) { const struct mcux_flexio_lcdif_config *config = dev->config; struct mcux_flexio_lcdif_data *lcdif_data = dev->data; FLEXIO_MCULCD_Type *flexio_lcd = config->flexio_lcd_dev; struct dma_block_config *blk_cfg; struct stream *stream = &lcdif_data->dma_tx; uint8_t num_of_shifters = 0; int ret; ARG_UNUSED(pixfmt); ret = mipi_dbi_flexio_lcdif_configure(dev, dbi_config); if (ret) { return ret; } num_of_shifters = (flexio_lcd->txShifterEndIndex - flexio_lcd->txShifterStartIndex + 1); blk_cfg = &stream->dma_blk_cfg; /* Assert the nCS. */ FLEXIO_MCULCD_StartTransfer(config->flexio_lcd_dev); /* prepare the block for this TX DMA channel */ memset(blk_cfg, 0, sizeof(struct dma_block_config)); /* tx direction has memory as source and periph as dest. */ blk_cfg->source_address = (uint32_t)framebuf; /* Destination is FLEXIO Shifters */ blk_cfg->dest_address = FLEXIO_MCULCD_GetTxDataRegisterAddress(flexio_lcd); blk_cfg->block_size = desc->buf_size; /* Transfer in each DMA loop is based on the number of shifters used */ stream->dma_cfg.source_burst_length = num_of_shifters * 4; stream->dma_cfg.head_block = &stream->dma_blk_cfg; /* Give the client dev as arg, as the callback comes from the dma */ stream->dma_cfg.user_data = (struct device *)dev; /* Set the source size in bytes */ stream->dma_cfg.source_data_size = lcdif_data->data_bus_width / 8; /* Configure the DMA */ dma_config(lcdif_data->dma_tx.dma_dev, lcdif_data->dma_tx.channel, &stream->dma_cfg); /* The DMA driver does not support setting this Modulo value which is required * in case of the flexio module to form a circular chain between the Shift buffer * in the FLEXIO module. */ EDMA_SetModulo(DMA0, lcdif_data->dma_tx.channel, kEDMA_ModuloDisable, flexio_lcdif_get_edma_modulo(num_of_shifters)); /* For 6800, de-assert the RDWR pin. */ if (kFLEXIO_MCULCD_6800 == flexio_lcd->busType) { flexio_lcdif_set_rd_wr(false, (void *)dev); } nxp_flexio_lock(config->flexio_dev); FLEXIO_MCULCD_SetMultiBeatsWriteConfig(flexio_lcd); FLEXIO_MCULCD_EnableTxDMA(flexio_lcd, true); nxp_flexio_unlock(config->flexio_dev); /* Start the data transfer */ dma_start(lcdif_data->dma_tx.dma_dev, lcdif_data->dma_tx.channel); /* Wait for transfer done. */ k_sem_take(&lcdif_data->transfer_done, K_FOREVER); return 0; } static int 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) { const struct mcux_flexio_lcdif_config *config = dev->config; FLEXIO_MCULCD_Type *flexio_lcd = config->flexio_lcd_dev; int ret; ARG_UNUSED(dbi_config); ret = mipi_dbi_flexio_lcdif_configure(dev, dbi_config); if (ret) { return ret; } FLEXIO_MCULCD_StartTransfer(flexio_lcd); nxp_flexio_lock(config->flexio_dev); FLEXIO_MCULCD_WriteCommandBlocking(flexio_lcd, cmd); if ((data_buf != NULL) && (len != 0)) { flexio_lcdif_write_data_array(flexio_lcd, data_buf, len); } nxp_flexio_unlock(config->flexio_dev); FLEXIO_MCULCD_StopTransfer(flexio_lcd); return kStatus_Success; } static int mipi_dbi_flexio_lcdif_reset(const struct device *dev, k_timeout_t delay) { int err; const struct mcux_flexio_lcdif_config *config = dev->config; /* Check if a reset port is provided to reset the LCD controller */ if (config->reset.port == NULL) { return 0; } /* Reset the LCD controller. */ err = gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_HIGH); if (err) { return err; } err = gpio_pin_set_dt(&config->reset, 0); if (err < 0) { return err; } k_sleep(delay); err = gpio_pin_set_dt(&config->reset, 1); if (err < 0) { return err; } LOG_DBG("%s device reset complete", dev->name); return 0; } static int flexio_lcdif_init(const struct device *dev) { const struct mcux_flexio_lcdif_config *config = dev->config; struct mcux_flexio_lcdif_data *lcdif_data = dev->data; int err; uint8_t shifter_end = config->child->res.shifter_count - 1; if (!device_is_ready(lcdif_data->dma_tx.dma_dev)) { LOG_ERR("%s device is not ready", lcdif_data->dma_tx.dma_dev->name); return -ENODEV; } err = nxp_flexio_child_attach(config->flexio_dev, config->child); if (err < 0) { return err; } config->flexio_lcd_dev->txShifterStartIndex = config->child->res.shifter_index[0]; config->flexio_lcd_dev->txShifterEndIndex = config->child->res.shifter_index[shifter_end]; config->flexio_lcd_dev->rxShifterStartIndex = config->flexio_lcd_dev->txShifterStartIndex; config->flexio_lcd_dev->rxShifterEndIndex = config->flexio_lcd_dev->txShifterEndIndex; config->flexio_lcd_dev->timerIndex = config->child->res.timer_index[0]; if (config->flexio_lcd_dev->txShifterEndIndex != config->flexio_lcd_dev->txShifterStartIndex + shifter_end) { LOG_ERR("Shifters should be continuous"); return -ENODEV; } err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); if (err) { return err; } /* Pass the FlexIO LCD device as parameter to the function * callbacks for setting GPIO signals. */ config->flexio_lcd_dev->userData = (void *)dev; k_sem_init(&lcdif_data->transfer_done, 0, 1); LOG_DBG("%s device init complete", dev->name); return 0; } static struct mipi_dbi_driver_api mipi_dbi_lcdif_driver_api = { .reset = mipi_dbi_flexio_lcdif_reset, .command_write = mipi_dbi_flexio_lcdif_command_write, .write_display = mipi_dbi_flexio_ldcif_write_display, }; #define MCUX_FLEXIO_LCDIF_DEVICE_INIT(n) \ PINCTRL_DT_INST_DEFINE(n); \ \ static FLEXIO_MCULCD_Type flexio_mculcd_##n = { \ .flexioBase = (FLEXIO_Type *)DT_REG_ADDR(DT_INST_PARENT(n)), \ .dataPinStartIndex = DT_INST_PROP(n, data_pin_start), \ .ENWRPinIndex = DT_INST_PROP(n, enwr_pin), \ .RDPinIndex = DT_INST_PROP_OR(n, rd_pin, 0), \ .setCSPin = flexio_lcdif_set_cs, \ .setRSPin = flexio_lcdif_set_rs, \ .setRDWRPin = flexio_lcdif_set_rd_wr, \ }; \ \ static uint8_t mcux_flexio_lcdif_shifters_##n[DT_INST_PROP(n, shifters_count)]; \ static uint8_t mcux_flexio_lcdif_timers_##n[DT_INST_PROP(n, timers_count)]; \ \ static const struct nxp_flexio_child lcdif_child_##n = { \ .isr = NULL, \ .user_data = (void *)DEVICE_DT_INST_GET(n), \ .res = { \ .shifter_index = mcux_flexio_lcdif_shifters_##n, \ .shifter_count = ARRAY_SIZE(mcux_flexio_lcdif_shifters_##n), \ .timer_index = mcux_flexio_lcdif_timers_##n, \ .timer_count = ARRAY_SIZE(mcux_flexio_lcdif_timers_##n), \ } \ }; \ \ struct mcux_flexio_lcdif_config mcux_flexio_lcdif_config_##n = { \ .flexio_lcd_dev = &flexio_mculcd_##n, \ .flexio_dev = DEVICE_DT_GET(DT_INST_PARENT(n)), \ .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ .child = &lcdif_child_##n, \ .reset = GPIO_DT_SPEC_INST_GET(n, reset_gpios), \ .cs_gpio = GPIO_DT_SPEC_INST_GET(n, cs_gpios), \ .rs_gpio = GPIO_DT_SPEC_INST_GET(n, rs_gpios), \ .rdwr_gpio = GPIO_DT_SPEC_INST_GET_OR(n, rdwr_gpios, {0}), \ }; \ struct mcux_flexio_lcdif_data mcux_flexio_lcdif_data_##n = { \ .dma_tx = { \ .dma_dev = DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_NAME(n, tx)), \ .channel = DT_INST_DMAS_CELL_BY_NAME(n, tx, mux), \ .dma_cfg = { \ .channel_direction = MEMORY_TO_MEMORY, \ .dma_callback = flexio_lcdif_dma_callback, \ .dest_data_size = 4, \ .block_count = 1, \ .dma_slot = DT_INST_DMAS_CELL_BY_NAME(n, tx, source) \ } \ }, \ }; \ DEVICE_DT_INST_DEFINE(n, \ &flexio_lcdif_init, \ NULL, \ &mcux_flexio_lcdif_data_##n, \ &mcux_flexio_lcdif_config_##n, \ POST_KERNEL, \ CONFIG_MIPI_DBI_INIT_PRIORITY, \ &mipi_dbi_lcdif_driver_api); DT_INST_FOREACH_STATUS_OKAY(MCUX_FLEXIO_LCDIF_DEVICE_INIT)