drivers: mipi: introduce MCUX MIPI DSI 2L driver
Introduce driver for MCUX MIPI DSI 2L. This IP block differs slightly from the existing MCUX MIPI peripheral, and uses a different hardware abstraction layer. For these reasons, a new driver was introduced rather than extending the existing mcux_dsi implementation. Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
This commit is contained in:
parent
b95d5cb43c
commit
5cc33d2a3c
4 changed files with 330 additions and 1 deletions
|
|
@ -1,2 +1,3 @@
|
|||
zephyr_sources_ifdef(CONFIG_MIPI_DSI mipi_dsi.c)
|
||||
zephyr_sources_ifdef(CONFIG_MIPI_DSI_MCUX dsi_mcux.c)
|
||||
zephyr_sources_ifdef(CONFIG_MIPI_DSI_MCUX_2L dsi_mcux_2l.c)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2022, NXP
|
||||
# Copyright 2022-2023 NXP
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config MIPI_DSI_MCUX
|
||||
|
|
@ -7,3 +7,10 @@ config MIPI_DSI_MCUX
|
|||
depends on DT_HAS_NXP_IMX_MIPI_DSI_ENABLED
|
||||
help
|
||||
NXP MIPI DSI controller driver
|
||||
|
||||
config MIPI_DSI_MCUX_2L
|
||||
bool "NXP MCUX MIPI-DSI 2L Controller"
|
||||
default y
|
||||
depends on DT_HAS_NXP_MIPI_DSI_2L_ENABLED
|
||||
help
|
||||
NXP MIPI DSI 2L controller driver
|
||||
|
|
|
|||
242
drivers/mipi_dsi/dsi_mcux_2l.c
Normal file
242
drivers/mipi_dsi/dsi_mcux_2l.c
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* Copyright 2023, NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* Based on dsi_mcux.c, which is (c) 2022 NXP */
|
||||
|
||||
#define DT_DRV_COMPAT nxp_mipi_dsi_2l
|
||||
|
||||
#include <zephyr/drivers/clock_control.h>
|
||||
#include <zephyr/drivers/mipi_dsi.h>
|
||||
#include <fsl_mipi_dsi.h>
|
||||
#include <fsl_clock.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include <soc.h>
|
||||
|
||||
LOG_MODULE_REGISTER(dsi_mcux_host, CONFIG_MIPI_DSI_LOG_LEVEL);
|
||||
|
||||
struct mcux_mipi_dsi_config {
|
||||
MIPI_DSI_HOST_Type *base;
|
||||
dsi_dpi_config_t dpi_config;
|
||||
bool auto_insert_eotp;
|
||||
const struct device *bit_clk_dev;
|
||||
clock_control_subsys_t bit_clk_subsys;
|
||||
const struct device *esc_clk_dev;
|
||||
clock_control_subsys_t esc_clk_subsys;
|
||||
const struct device *pixel_clk_dev;
|
||||
clock_control_subsys_t pixel_clk_subsys;
|
||||
uint32_t dphy_ref_freq;
|
||||
};
|
||||
|
||||
static int dsi_mcux_attach(const struct device *dev,
|
||||
uint8_t channel,
|
||||
const struct mipi_dsi_device *mdev)
|
||||
{
|
||||
const struct mcux_mipi_dsi_config *config = dev->config;
|
||||
dsi_dphy_config_t dphy_config;
|
||||
dsi_config_t dsi_config;
|
||||
uint32_t dphy_bit_clk_freq;
|
||||
uint32_t dphy_esc_clk_freq;
|
||||
uint32_t dsi_pixel_clk_freq;
|
||||
uint32_t bit_width;
|
||||
|
||||
DSI_GetDefaultConfig(&dsi_config);
|
||||
dsi_config.numLanes = mdev->data_lanes;
|
||||
dsi_config.autoInsertEoTp = config->auto_insert_eotp;
|
||||
|
||||
/* Init the DSI module. */
|
||||
DSI_Init(config->base, &dsi_config);
|
||||
|
||||
/* Get the DPHY bit clock frequency */
|
||||
if (clock_control_get_rate(config->bit_clk_dev,
|
||||
config->bit_clk_subsys,
|
||||
&dphy_bit_clk_freq)) {
|
||||
return -EINVAL;
|
||||
};
|
||||
/* Get the DPHY ESC clock frequency */
|
||||
if (clock_control_get_rate(config->esc_clk_dev,
|
||||
config->esc_clk_subsys,
|
||||
&dphy_esc_clk_freq)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Get the Pixel clock frequency */
|
||||
if (clock_control_get_rate(config->pixel_clk_dev,
|
||||
config->pixel_clk_subsys,
|
||||
&dsi_pixel_clk_freq)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (config->dpi_config.pixelPacket) {
|
||||
case kDSI_PixelPacket16Bit:
|
||||
bit_width = 16;
|
||||
break;
|
||||
case kDSI_PixelPacket18Bit:
|
||||
__fallthrough;
|
||||
case kDSI_PixelPacket18BitLoosely:
|
||||
bit_width = 18;
|
||||
break;
|
||||
case kDSI_PixelPacket24Bit:
|
||||
bit_width = 24;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL; /* Invalid bit width enum value? */
|
||||
}
|
||||
/* 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
|
||||
*/
|
||||
if (((dsi_pixel_clk_freq * bit_width) / mdev->data_lanes) > dphy_bit_clk_freq) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
DSI_GetDphyDefaultConfig(&dphy_config, dphy_bit_clk_freq, dphy_esc_clk_freq);
|
||||
|
||||
if (config->dphy_ref_freq != 0) {
|
||||
dphy_bit_clk_freq = DSI_InitDphy(config->base,
|
||||
&dphy_config, config->dphy_ref_freq);
|
||||
} else {
|
||||
/* DPHY PLL is not present, ref clock is unused */
|
||||
DSI_InitDphy(config->base, &dphy_config, 0);
|
||||
}
|
||||
|
||||
/* Init DPI interface. */
|
||||
DSI_SetDpiConfig(config->base, &config->dpi_config, mdev->data_lanes,
|
||||
dsi_pixel_clk_freq, dphy_bit_clk_freq);
|
||||
|
||||
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 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 mcux_mipi_dsi_init(const struct device *dev)
|
||||
{
|
||||
const struct mcux_mipi_dsi_config *config = dev->config;
|
||||
|
||||
imxrt_pre_init_display_interface();
|
||||
|
||||
if (!device_is_ready(config->bit_clk_dev) ||
|
||||
!device_is_ready(config->esc_clk_dev) ||
|
||||
!device_is_ready(config->pixel_clk_dev)) {
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MCUX_MIPI_DSI_DEVICE(id) \
|
||||
static const struct mcux_mipi_dsi_config mipi_dsi_config_##id = { \
|
||||
.base = (MIPI_DSI_HOST_Type *)DT_INST_REG_ADDR(id), \
|
||||
.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) & 0x3, \
|
||||
.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), \
|
||||
.dphy_ref_freq = DT_INST_PROP_OR(id, dphy_ref_frequency, 0), \
|
||||
.bit_clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(id, dphy)), \
|
||||
.bit_clk_subsys = \
|
||||
(clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_NAME(id, dphy, name), \
|
||||
.esc_clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(id, esc)), \
|
||||
.esc_clk_subsys = \
|
||||
(clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_NAME(id, esc, name), \
|
||||
.pixel_clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(id, pixel)), \
|
||||
.pixel_clk_subsys = \
|
||||
(clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_NAME(id, pixel, name), \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(id, \
|
||||
&mcux_mipi_dsi_init, \
|
||||
NULL, \
|
||||
NULL, \
|
||||
&mipi_dsi_config_##id, \
|
||||
POST_KERNEL, \
|
||||
CONFIG_MIPI_DSI_INIT_PRIORITY, \
|
||||
&dsi_mcux_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(MCUX_MIPI_DSI_DEVICE)
|
||||
79
dts/bindings/mipi-dsi/nxp,mipi-dsi-2l.yaml
Normal file
79
dts/bindings/mipi-dsi/nxp,mipi-dsi-2l.yaml
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#
|
||||
# Copyright 2023, NXP
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
description: NXP MCUX MIPI DSI 2L
|
||||
|
||||
compatible: "nxp,mipi-dsi-2l"
|
||||
|
||||
include: mipi-dsi-host.yaml
|
||||
|
||||
properties:
|
||||
interrupts:
|
||||
required: true
|
||||
|
||||
nxp,lcdif:
|
||||
type: phandle
|
||||
required: true
|
||||
description:
|
||||
Instance of the LCDIF peripheral.
|
||||
dpi-color-coding:
|
||||
type: string
|
||||
enum:
|
||||
- "16-bit-config-1"
|
||||
- "16-bit-config-2"
|
||||
- "16-bit-config-3"
|
||||
- "18-bit-config-1"
|
||||
- "18-bit-config-2"
|
||||
- "24-bit"
|
||||
description:
|
||||
MIPI DPI interface color coding. Sets the distribution of RGB bits within
|
||||
the 24-bit d bus, as specified by the DPI specification.
|
||||
|
||||
dpi-pixel-packet:
|
||||
type: string
|
||||
enum:
|
||||
- "16-bit"
|
||||
- "18-bit"
|
||||
- "18-bit-loose"
|
||||
- "24-bit"
|
||||
description:
|
||||
MIPI DSI pixel packet type send through DPI interface.
|
||||
|
||||
dpi-video-mode:
|
||||
type: string
|
||||
enum:
|
||||
- "non-burst-sync-pulse"
|
||||
- "non-burst-sync-event"
|
||||
- "burst"
|
||||
description:
|
||||
DPI video mode.
|
||||
|
||||
dpi-bllp-mode:
|
||||
type: string
|
||||
enum:
|
||||
- "low-power"
|
||||
- "blank"
|
||||
- "null"
|
||||
description:
|
||||
Behavior in BLLP (Blanking or Low-Power Interval).
|
||||
|
||||
autoinsert-eotp:
|
||||
type: boolean
|
||||
description:
|
||||
Automatically insert an EoTp short packet when switching from HS to LP mode.
|
||||
|
||||
dphy-clk-div:
|
||||
type: int
|
||||
required: true
|
||||
description:
|
||||
MIPI D-PHY clock divider. Must be set to ensure clock frequency is at
|
||||
least (pixel clock * bits per output pixel) / number of mipi data lanes
|
||||
|
||||
dphy-ref-frequency:
|
||||
type: int
|
||||
default: 0
|
||||
description:
|
||||
Maximum clock speed supported by the device, in Hz. Leave at default
|
||||
if no DPHY PLL is present
|
||||
Loading…
Reference in a new issue