Hi Yuex,
I found that the SN65DSI83 is quite fussy about the clock jitter when converting MIPI DSI video but not so much when displaying the test pattern. Check which PLL is being used to feed the MIPI DSI component. The Video PLL works reliably and with low jitter, but other PLLs have higher jitter which is too much for reliable operation. The use of the wrong PLL resulted in the converter frequently failing to switch out of test pattern mode, or successfully switching out of test pattern mode but the screen would go blank after operating for a while.
You can also try connecting an external oscillator to the REFCLK pin and configuring the SN65DSI83 for that instead of the DSI CLK (code for both options below) to see if that works but once the DSI CLK is correctly configured, this should not be necessary.
If it still doesn't work then make sure all MIPI DSI connections are correct and differential pair routing is good. I've also included some code snippets below so you can see how I configured it.
Below is my working SN65DSI83 initialisation sequence for reference (1024 x 600 resolution).
// Perform reset
resource->pullResetPin(false);
SN65DSI83_DelayMs(10);
resource->pullResetPin(true);
SN65DSI83_DelayMs(50U);
handle->i2cAddr = SN65DSI83_addr;
// Commands to write data over I2C
// Soft reset
status = SN65DSI83_WriteCommand(handle, 0x09, 0x01);
// PLL_EN(0) - Enable last after addr 0A and 0B configured
status |= SN65DSI83_WriteCommand(handle, 0x0D, 0x00);
// Use REFCLK if DIP switch 1 is on or DSI CLK if DIP switch 1 is off
// REFCLK originally offered better reliability but requires external oscillator on REFCLK pin of SN65DSI83,
// however, now that the PLL configuration has been changed to use the Video PLL, there doesn't seem to be
// any difference any more.
if (GPIO_PinRead(BOARD_USER_DIP_1_GPIO, BOARD_USER_DIP_1_GPIO_PIN) == BOARD_DIP_SWITCH_ON)
{
// Configuration for using REFCLK
// LVDS Clock Range - was 0x03
status |= SN65DSI83_WriteCommand(handle, 0x0A, 0x02);
// DSI Clock Divider & RefCLK multiplier - was 0x28
status |= SN65DSI83_WriteCommand(handle, 0x0B, 0x01);
}
else
{
// Configuration for using DSI clock
status |= SN65DSI83_WriteCommand(handle, 0x0A, 0x03);
status |= SN65DSI83_WriteCommand(handle, 0x0B, 0x28);
}
// DSI Channel Config & DSI Channel Mode - was 0x30
status |= SN65DSI83_WriteCommand(handle, 0x10, 0x37);
// DSI channel equalisation
status |= SN65DSI83_WriteCommand(handle, 0x11, 0xCC);
// DSI Clock Range - was 60 = 0x3C = 300 to 305MHz
status |= SN65DSI83_WriteCommand(handle, 0x12, 0x3C);
// DE and Sync Polarity
status |= SN65DSI83_WriteCommand(handle, 0x18, 0x78); // 0x78 for Default, 0x98 for inverted
// LVDS voltage setting - was 0x04
status |= SN65DSI83_WriteCommand(handle, 0x19, 0x00);
// Reverse LVDS channel order - 0x23 for reverse
status |= SN65DSI83_WriteCommand(handle, 0x1A, 0x01);
// Line Length Low Byte
status |= SN65DSI83_WriteCommand(handle, 0x20, 0x00);
// Line Length High Byte
status |= SN65DSI83_WriteCommand(handle, 0x21, 0x04);
// Vertical Display Size Low Byte (TEST PATTERN GENERATION PURPOSE ONLY)
// "TEST PATTERN GENERATION PURPOSE ONLY" means that this parameter is only used when generating the test pattern.
// For normal display, these parameters depend on the video generated by the MIPI interface.
status |= SN65DSI83_WriteCommand(handle, 0x24, 0x58);
// Vertical display size high byte (TEST PATTERN GENERATION PURPOSE ONLY)
status |= SN65DSI83_WriteCommand(handle, 0x25, 0x02);
// Sync Delay Low Byte
status |= SN65DSI83_WriteCommand(handle, 0x28, 0x53);
// Sync delay high byte
status |= SN65DSI83_WriteCommand(handle, 0x29, 0x00);
// HSync Pulse Width Low Byte
status |= SN65DSI83_WriteCommand(handle, 0x2C, 0x50);
// HSync Pulse Width High Byte
status |= SN65DSI83_WriteCommand(handle, 0x2D, 0x00);
// VSync Pulse Width Low Byte
status |= SN65DSI83_WriteCommand(handle, 0x30, 0x02);
// VSync Pulse Width High Byte
status |= SN65DSI83_WriteCommand(handle, 0x31, 0x00);
// Horizontal back porch
status |= SN65DSI83_WriteCommand(handle, 0x34, 0x50);
// Vertical back porch (TEST PATTERN GENERATION PURPOSE ONLY)
status |= SN65DSI83_WriteCommand(handle, 0x36, 0x17);
// Horizontal front porch (TEST PATTERN GENERATION PURPOSE ONLY)
status |= SN65DSI83_WriteCommand(handle, 0x38, 0x00);
// ======CHA_VER_FRONT_PORCH======== (TEST PATTERN GENERATION PURPOSE ONLY)
status |= SN65DSI83_WriteCommand(handle, 0x3A, 0x10);
// ======CHA/CHB TEST PATTERN(bit4 CHA, bit0 CHB)======== 00 = no test, 10 = test
status |= SN65DSI83_WriteCommand(handle, 0x3C, 0x00);
if (status == kStatus_Success)
{
// Break out of loop once successfully configured
break;
}
else
{
SN65DSI83_DelayMs(1000);
// If not successfully configured then stay in loop and try again
}
Outside of loop after successful configuration:
// Enable PLL
status_t status = SN65DSI83_WriteCommand(handle, 0x0D, 0x01);
VIDEO_DelayMs(10);
// Soft reset
status = SN65DSI83_WriteCommand(handle, 0x09, 0x01);
VIDEO_DelayMs(10);
Below are some relevant extracts from my edited display_support.c including my MIPI DSI configuration:
Note that this is not the whole file, this is the relevant parts of the original file that I have changed to suit the converter IC and my panel. The whole file is very long but if you've used the lvgl_demo_widgets project as your starting point then you should be able to see where these changes go.
#define DEMO_HSW 80
#define DEMO_HFP 120
#define DEMO_HBP 120
#define DEMO_VSW 2
#define DEMO_VFP 12
#define DEMO_VBP 21
#define DEMO_LCDIF_POL_FLAGS \
(kLCDIFV2_DataEnableActiveHigh | kLCDIFV2_VsyncActiveHigh | kLCDIFV2_HsyncActiveHigh | \
kLCDIFV2_DriveDataOnRisingClkEdge)
/* Definitions for MIPI. */
#define DEMO_MIPI_DSI (&g_mipiDsi)
#define DEMO_MIPI_DSI_LANE_NUM 2
/*
* 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 DEMO_MIPI_DPHY_BIT_CLK_ENLARGE(origin) (((origin) / * 9)
static const dc_fb_lcdifv2_config_t s_dcFbLcdifv2Config = {
.lcdifv2 = DEMO_LCDIF,
.width = DEMO_PANEL_WIDTH,
.height = DEMO_PANEL_HEIGHT,
.hsw = DEMO_HSW,
.hfp = DEMO_HFP,
.hbp = DEMO_HBP,
.vsw = DEMO_VSW,
.vfp = DEMO_VFP,
.vbp = DEMO_VBP,
.polarityFlags = DEMO_LCDIF_POL_FLAGS,
.lineOrder = kLCDIFV2_LineOrderRGB,
/* CM4 is domain 1, CM7 is domain 0. */
#if (__CORTEX_M <= 4)
.domain = 1,
#else
.domain = 0,
#endif
};
static void BOARD_InitLcdifClock(void)
{
/*
* The pixel clock is (height + VSW + VFP + VBP) * (width + HSW + HFP + HBP) * frame rate.
*
*/
const clock_root_config_t lcdifClockConfig = {
.clockOff = false,
.mux = 4, /*!< PLL_528. */
.div = 12,
};
static status_t BOARD_InitLcdPanel(void)
{
status_t status;
const display_config_t displayConfig = {
.resolution = FSL_VIDEO_RESOLUTION(DEMO_PANEL_WIDTH, DEMO_PANEL_HEIGHT),
.hsw = DEMO_HSW,
.hfp = DEMO_HFP,
.hbp = DEMO_HBP,
.vsw = DEMO_VSW,
.vfp = DEMO_VFP,
.vbp = DEMO_VBP,
.controlFlags = kDISPLAY_VsyncActiveHigh | kDISPLAY_HsyncActiveHigh,
.dsiLanes = DEMO_MIPI_DSI_LANE_NUM,
.I2C_SendFunc = BOARD_MIPI2LVDS_I2C_Send,
.I2C_ReceiveFunc = BOARD_MIPI2LVDS_I2C_Receive,
};
// Initialise MIPI to LVDS Converter
status = SN65DSI83_Init(&sn65dsi83Handle, &displayConfig);
if (status == kStatus_Success)
{
GPIO_PinWrite(BOARD_MIPI_PANEL_BL_GPIO, BOARD_MIPI_PANEL_BL_PIN, 1);
}
return status;
}
static void BOARD_SetMipiDsiConfig(void)
{
dsi_config_t dsiConfig;
dsi_dphy_config_t dphyConfig;
const dsi_dpi_config_t dpiConfig = {.pixelPayloadSize = DEMO_PANEL_WIDTH,
.dpiColorCoding = kDSI_Dpi24Bit,
.pixelPacket = kDSI_PixelPacket24Bit,
.videoMode = kDSI_DpiNonBurstWithSyncPulse,
.bllpMode = kDSI_DpiBllpLowPower,
.polarityFlags = kDSI_DpiVsyncActiveHigh | kDSI_DpiHsyncActiveHigh,
.hfp = DEMO_HFP,
.hbp = DEMO_HBP,
.hsw = DEMO_HSW,
.vfp = DEMO_VFP,
.vbp = DEMO_VBP,
.panelHeight = DEMO_PANEL_HEIGHT,
.virtualChannel = 0};
/*
* dsiConfig.numLanes = 4;
* dsiConfig.enableNonContinuousHsClk = false;
* dsiConfig.autoInsertEoTp = true;
* dsiConfig.numExtraEoTp = 0;
* dsiConfig.htxTo_ByteClk = 0;
* dsiConfig.lrxHostTo_ByteClk = 0;
* dsiConfig.btaTo_ByteClk = 0;
*/
DSI_GetDefaultConfig(&dsiConfig);
dsiConfig.numLanes = DEMO_MIPI_DSI_LANE_NUM;
dsiConfig.autoInsertEoTp = true;
dsiConfig.numExtraEoTp = 0;
dsiConfig.enableTxUlps = false;
/* Init the DSI module. */
DSI_Init(DEMO_MIPI_DSI, &dsiConfig);
/* 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.
*/
mipiDsiDphyBitClkFreq_Hz = mipiDsiDpiClkFreq_Hz * (24 / DEMO_MIPI_DSI_LANE_NUM);
mipiDsiDphyBitClkFreq_Hz = DEMO_MIPI_DPHY_BIT_CLK_ENLARGE(mipiDsiDphyBitClkFreq_Hz);
DSI_GetDphyDefaultConfig(&dphyConfig, mipiDsiDphyBitClkFreq_Hz, mipiDsiTxEscClkFreq_Hz);
mipiDsiDphyBitClkFreq_Hz = DSI_InitDphy(DEMO_MIPI_DSI, &dphyConfig, mipiDsiDphyRefClkFreq_Hz);
/* Init DPI interface. */
DSI_SetDpiConfig(DEMO_MIPI_DSI, &dpiConfig, DEMO_MIPI_DSI_LANE_NUM, mipiDsiDpiClkFreq_Hz, mipiDsiDphyBitClkFreq_Hz);
}
status_t BOARD_InitDisplayInterface(void)
{
status_t status;
CLOCK_EnableClock(kCLOCK_Video_Mux);
#if (DEMO_DISPLAY_CONTROLLER == DEMO_DISPLAY_CONTROLLER_LCDIFV2)
/* LCDIF v2 output to MIPI DSI. */
VIDEO_MUX->VID_MUX_CTRL.SET = VIDEO_MUX_VID_MUX_CTRL_MIPI_DSI_SEL_MASK;
#else
/* ELCDIF output to MIPI DSI. */
VIDEO_MUX->VID_MUX_CTRL.CLR = VIDEO_MUX_VID_MUX_CTRL_MIPI_DSI_SEL_MASK;
#endif
/* 1. Power on and isolation off. */
PGMC_BPC4->BPC_POWER_CTRL |= (PGMC_BPC_BPC_POWER_CTRL_PSW_ON_SOFT_MASK | PGMC_BPC_BPC_POWER_CTRL_ISO_OFF_SOFT_MASK);
/* 2. Assert MIPI reset. */
IOMUXC_GPR->GPR62 &=
~(IOMUXC_GPR_GPR62_MIPI_DSI_PCLK_SOFT_RESET_N_MASK | IOMUXC_GPR_GPR62_MIPI_DSI_ESC_SOFT_RESET_N_MASK |
IOMUXC_GPR_GPR62_MIPI_DSI_BYTE_SOFT_RESET_N_MASK | IOMUXC_GPR_GPR62_MIPI_DSI_DPI_SOFT_RESET_N_MASK);
/* 3. Setup clock. */
BOARD_InitMipiDsiClock();
/* 4. Deassert PCLK and ESC reset. */
IOMUXC_GPR->GPR62 |=
(IOMUXC_GPR_GPR62_MIPI_DSI_PCLK_SOFT_RESET_N_MASK | IOMUXC_GPR_GPR62_MIPI_DSI_ESC_SOFT_RESET_N_MASK);
/* 5. Configures peripheral. */
BOARD_SetMipiDsiConfig();
/* 6. Deassert BYTE and DBI reset. */
IOMUXC_GPR->GPR62 |=
(IOMUXC_GPR_GPR62_MIPI_DSI_BYTE_SOFT_RESET_N_MASK | IOMUXC_GPR_GPR62_MIPI_DSI_DPI_SOFT_RESET_N_MASK);
uint8_t errorVal = 0xFF;
while (errorVal)
{
/* Disable converter IC */
GPIO_PinWrite(BOARD_TI_EN_GPIO, BOARD_TI_EN_GPIO_PIN, 0U);
VIDEO_DelayMs(10);
/* Enable converter IC */
GPIO_PinWrite(BOARD_TI_EN_GPIO, BOARD_TI_EN_GPIO_PIN, 1U);
/* 7. Configure the converter IC. */
status = BOARD_InitLcdPanel();
/* 8. Clear converter chip errors */
BOARD_ClearLcdPanelErrors(&errorVal);
}
return status;
}