Adapting evkmimxrt1170_lvgl_demo_widgets example to SN65DSI83 MIPI DSI to LVDS bridge

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Adapting evkmimxrt1170_lvgl_demo_widgets example to SN65DSI83 MIPI DSI to LVDS bridge

593 Views
transistor32
Contributor III

I'm trying to use an LVDS LCD with the i.MXRT117x by using an SN65DSI83 to convert the MIPI DSI output to LVDS. I've been modifying the evkmimxrt1170_lvgl_demo_widgets_cm7 demo as a starting point but it is not working very well, and I would like to understand why not. I see a few people have used the SN65DSI83 on IMXRT on this forum before but only with Linux or Android, which isn't very useful to me.

The test pattern built into the SN65DSI83 works, but when I switch to the video output of the NXP processor, the expected image appears but it starts to fade away from the corners after a short while. Checking the signals with the scope suggests that the signals from the SN65DSI83 to the LCD stop carrying any data after the first frame is complete, but the data appears to be there in the DSI signals.

I've used fsl_hx8394.c as a starting point. From what I understand, the SN65DSI83 should receive the MIPI DSI signals in 'video mode' with no commands, so I have removed all the commands which write the hx8394Cmds[] array to the DSI interface and instead write the relevant configuration data over I2C. Is there any other configuration of the MIPI DSI peripheral I need to do to ensure it is in 'video mode' and not 'command mode'?

Reading the error register (0xE5) returns a value of 0x30 (Uncorrectable ECC error and Correctable ECC error).

0 Kudos
5 Replies

574 Views
transistor32
Contributor III

This wasn't an NXP issue after all.

I had the LVDS FlatLink data lines (+ and -) swapped due to a design error and I was expecting to just be able to invert the DE, HSync and VSync bits (a feature offered by the TI chip) and have it work albeit with inverted colours because the clock lines were correct.

I fixed it so it's now working correctly by reworking the board so that + and - were the correct polarity and using the regular polarity for the DE, HSync and VSync bits, but I don't understand why inverting those bits did not allow it to work with + and - swapped. Do the colour bits need to be set to a specific value during the blanking intervals? Even though the problem is fixed now, it would be good to learn why that method didn't work.

0 Kudos

362 Views
yuex
Contributor I

hi transistor32,
    i have the same problem with you ,SN65DSI83 can generate PATTEN on LCD,but when i exit test mode,nothing displayed on the screen,and E5 reg value is 80,do you know what reason cause this error? thank you.

yuex.

0 Kudos

344 Views
transistor32
Contributor III

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;
}

 

0 Kudos

341 Views
yuex
Contributor I

Hi transistor32,

    Thanks a lot.

     when I check the datasheet of SN65DSI83,there is a Initialization Sequence,i wonder if seq 2 and seq 8 is necessary? I don't find something about "Change DSI data lanes to HS state" in you code.Do you apply the Initialization Sequence stated in the data sheet?

0 Kudos

338 Views
transistor32
Contributor III

I think this is already the default state of the NXP pins but that's just from my memory and I cannot 100% guarantee that is true. I didn't add any extra code for this.

0 Kudos