/* * Copyright 2022, NXP * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nxp_imx_mipi_dsi #include #include #include #include #include LOG_MODULE_REGISTER(dsi_mcux, CONFIG_MIPI_DSI_LOG_LEVEL); #define MIPI_DPHY_REF_CLK DT_INST_PROP(0, dphy_ref_frequency) /* * The DPHY bit clock must be fast enough to send out the pixels, it should be * larger than: * * (Pixel clock * bit per output pixel) / number of MIPI data lane * * Here the desired DPHY bit clock multiplied by ( 9 / 8 = 1.125) to ensure * it is fast enough. */ #define MIPI_DPHY_BIT_CLK_ENLARGE(origin) (((origin) / 8) * 9) struct display_mcux_mipi_dsi_config { MIPI_DSI_Type base; dsi_dpi_config_t dpi_config; bool auto_insert_eotp; }; struct display_mcux_mipi_dsi_data { const struct device *dev; }; static int dsi_mcux_attach(const struct device *dev, uint8_t channel, const struct mipi_dsi_device *mdev) { const struct display_mcux_mipi_dsi_config *config = dev->config; dsi_dphy_config_t dphy_config; dsi_config_t dsi_config; uint32_t mipi_dsi_esc_clk_hz; uint32_t mipi_dsi_tx_esc_clk_hz; uint32_t mipi_dsi_dphy_ref_clk_hz = MIPI_DPHY_REF_CLK; DSI_GetDefaultConfig(&dsi_config); dsi_config.numLanes = mdev->data_lanes; dsi_config.autoInsertEoTp = config->auto_insert_eotp; /* Init the DSI module. */ DSI_Init((MIPI_DSI_Type *)&config->base, &dsi_config); /* Init DPHY. * * The DPHY bit clock must be fast enough to send out the pixels, it should be * larger than: * * (Pixel clock * bit per output pixel) / number of MIPI data lane * * Here the desired DPHY bit clock multiplied by ( 9 / 8 = 1.125) to ensure * it is fast enough. * * Note that the DSI output pixel is 24bit per pixel. */ uint32_t mipi_dsi_dpi_clk_hz = CLOCK_GetRootClockFreq(kCLOCK_Root_Lcdif); uint32_t mipi_dsi_dphy_bit_clk_hz = mipi_dsi_dpi_clk_hz * (24 / mdev->data_lanes); mipi_dsi_dphy_bit_clk_hz = MIPI_DPHY_BIT_CLK_ENLARGE(mipi_dsi_dphy_bit_clk_hz); mipi_dsi_esc_clk_hz = CLOCK_GetRootClockFreq(kCLOCK_Root_Mipi_Esc); mipi_dsi_tx_esc_clk_hz = mipi_dsi_esc_clk_hz / 3; DSI_GetDphyDefaultConfig(&dphy_config, mipi_dsi_dphy_bit_clk_hz, mipi_dsi_tx_esc_clk_hz); mipi_dsi_dphy_bit_clk_hz = DSI_InitDphy((MIPI_DSI_Type *)&config->base, &dphy_config, mipi_dsi_dphy_ref_clk_hz); /* Init DPI interface. */ DSI_SetDpiConfig((MIPI_DSI_Type *)&config->base, &config->dpi_config, mdev->data_lanes, mipi_dsi_dpi_clk_hz, mipi_dsi_dphy_bit_clk_hz); imxrt_post_init_display_interface(); return 0; } static ssize_t dsi_mcux_transfer(const struct device *dev, uint8_t channel, struct mipi_dsi_msg *msg) { const struct display_mcux_mipi_dsi_config *config = dev->config; dsi_transfer_t dsi_xfer = {0}; status_t status; dsi_xfer.virtualChannel = channel; dsi_xfer.txDataSize = msg->tx_len; dsi_xfer.txData = msg->tx_buf; dsi_xfer.rxDataSize = msg->rx_len; dsi_xfer.rxData = msg->rx_buf; switch (msg->type) { case MIPI_DSI_DCS_READ: LOG_ERR("DCS Read not yet implemented or used"); return -ENOTSUP; case MIPI_DSI_DCS_SHORT_WRITE: dsi_xfer.sendDscCmd = true; dsi_xfer.dscCmd = msg->cmd; dsi_xfer.txDataType = kDSI_TxDataDcsShortWrNoParam; break; case MIPI_DSI_DCS_SHORT_WRITE_PARAM: __fallthrough; case MIPI_DSI_DCS_LONG_WRITE: dsi_xfer.sendDscCmd = true; dsi_xfer.dscCmd = msg->cmd; dsi_xfer.txDataType = kDSI_TxDataDcsShortWrOneParam; break; case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM: dsi_xfer.txDataType = kDSI_TxDataGenShortWrNoParam; break; case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM: dsi_xfer.txDataType = kDSI_TxDataGenShortWrOneParam; break; case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM: dsi_xfer.txDataType = kDSI_TxDataGenShortWrTwoParam; break; case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM: __fallthrough; case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM: __fallthrough; case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM: LOG_ERR("Generic Read not yet implemented or used"); return -ENOTSUP; default: LOG_ERR("Unsupported message type (%d)", msg->type); return -ENOTSUP; } status = DSI_TransferBlocking(&config->base, &dsi_xfer); if (status != kStatus_Success) { LOG_ERR("Transmission failed"); return -EIO; } if (msg->rx_len != 0) { /* Return rx_len on a read */ return msg->rx_len; } /* Return tx_len on a write */ return msg->tx_len; } static struct mipi_dsi_driver_api dsi_mcux_api = { .attach = dsi_mcux_attach, .transfer = dsi_mcux_transfer, }; static int display_mcux_mipi_dsi_init(const struct device *dev) { imxrt_pre_init_display_interface(); return 0; } #define MCUX_MIPI_DSI_DEVICE(id) \ static const struct display_mcux_mipi_dsi_config display_mcux_mipi_dsi_config_##id = { \ .base = { \ .host = (DSI_HOST_Type *)DT_INST_REG_ADDR_BY_IDX(id, 0), \ .dpi = (DSI_HOST_DPI_INTFC_Type *)DT_INST_REG_ADDR_BY_IDX(id, 1), \ .apb = (DSI_HOST_APB_PKT_IF_Type *)DT_INST_REG_ADDR_BY_IDX(id, 2), \ .dphy = (DSI_HOST_NXP_FDSOI28_DPHY_INTFC_Type *) \ DT_INST_REG_ADDR_BY_IDX(id, 3), \ }, \ .dpi_config = { \ .dpiColorCoding = DT_INST_ENUM_IDX(id, dpi_color_coding), \ .pixelPacket = DT_INST_ENUM_IDX(id, dpi_pixel_packet), \ .videoMode = DT_INST_ENUM_IDX(id, dpi_video_mode), \ .bllpMode = DT_INST_ENUM_IDX(id, dpi_bllp_mode), \ .pixelPayloadSize = DT_INST_PROP_BY_PHANDLE(id, nxp_lcdif, width), \ .panelHeight = DT_INST_PROP_BY_PHANDLE(id, nxp_lcdif, height), \ .polarityFlags = DT_INST_PROP_BY_PHANDLE_IDX( \ id, nxp_lcdif, id, polarity) >> 2, \ .hfp = DT_INST_PROP_BY_PHANDLE(id, nxp_lcdif, hfp), \ .hbp = DT_INST_PROP_BY_PHANDLE(id, nxp_lcdif, hbp), \ .hsw = DT_INST_PROP_BY_PHANDLE(id, nxp_lcdif, hsync), \ .vfp = DT_INST_PROP_BY_PHANDLE(id, nxp_lcdif, vfp), \ .vbp = DT_INST_PROP_BY_PHANDLE(id, nxp_lcdif, vbp), \ }, \ .auto_insert_eotp = DT_INST_PROP(id, autoinsert_eotp), \ }; \ static struct display_mcux_mipi_dsi_data display_mcux_mipi_dsi_data_##id; \ DEVICE_DT_INST_DEFINE(id, \ &display_mcux_mipi_dsi_init, \ NULL, \ &display_mcux_mipi_dsi_data_##id, \ &display_mcux_mipi_dsi_config_##id, \ POST_KERNEL, \ CONFIG_MIPI_DSI_INIT_PRIORITY, \ &dsi_mcux_api); DT_INST_FOREACH_STATUS_OKAY(MCUX_MIPI_DSI_DEVICE)