A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

1. Summary of Basic Parameter Meaning

1.1 Clock

> Basic Conversion:

(1)Clock frequency:clock-frequency = fframe*(hfront+hback+hsync+xres)*(vfront+vback+vsync+yres)(Unit: Hz 1MHz=106Hz)

Clock period: pixclock = 1012/clock-frequency = 1012/fframe*(hfront+hback+hsync+xres)*(vfront+vback+vsync+yres)(Unit: picosecond 1ps=10-12s)

Frequency × period = 1

(2)CPU manual: The LVDS clock input is 7 times the output

rate(bit_clk)= clock-frequency×7

A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

mipi:rate = clock-frequency×24 / 4 = clock-frequency×6

A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

> PLL(pms):

Fout = ((M+K/65536)*Fin)/(P*2 S)

A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

In general, K = 0 can be defaulted, so the above formula can be modified as: (M * Fin)/ (P * 2S)8mp:Fin = 24;8mm:Fin = 27。 A set of ready-made PMS value can be substituted for that conversion.

In addition to Fout, Fvco needs to be considered: FFVCO = ( (M + K/65536) × FFIN)/P

Must meet: 25 < FOUT < 3200MHz, 1600 < FFVCO < 3200MHz

If the value calculated by the PMS cannot be equal to the actual clock, it must be greater than the actual clock.

1.2 Screen Reference

hback-porch		=;				// Back-porch of the horizontal axis
hfront-porch		=;				// Front-porch of the horizontal axis
hactive			=;				// Horizontal axis resolution
hsync-len		=;					// Hsync-len pulse width
vback-porch		=;					// Back-porch of vertical axis
vfront-porch		=;					// Front-porch of vertical axis
vactive			=;				// Vertical axis resolution
vsync-len		=;					// Field sync pulse width
clock-frequency	=;			     // Clock
vsync-active		=;					// 
hsync-active		=;					// 
de-active		=;					   // 
pixelclk-active	=;					   //

2. Mipi Screen Transplant

Note: The most important thing for mipi screen migration is to determine if the screen needs an initialization sequence!

2.1 Initialization Sequence

Most of the mipi screens need to be initialized in the driver to light up normally, the initialization sequence can be in the uboot driver or in the kernel driver, to light up the screen as early as possible, it is recommended that the initialization sequence be put into the Uboot driver.

Usually, the screen manufacturer will provide the initialization sequence document. Still, only the register assignment part is provided in the document, and the probability can not be directly used in the driver. You can look in the source code to see if there is a mipi driver with an initialization sequence for reference, and analyze whether the format of the initialization sequence in the driver matches the format of the initialization sequence in the ported screen, and if it does, you can apply it directly.

Reference Drive: uboot/drivers/video/orisetech_otm8009a.c

uboot/drivers/video/raydium-rm67191.c

Take orisetech_otm8009a.c as an example to analyse the initialization sequence format:

The initialization sequence provided by the screen manufacturer:

A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

> Case one: Requires 0 x00 enable

Two rows are a group, and the following group is an example:

SSD2825_Gen_write_1A_1P(0x00,0x00);

SSD2825_Gen_write_1A_3P(0xff,0x12,0x87,0x01); //EXTC=1

0x00: Enable switch for some instructions, these instructions have to turn on 0x00 first, then enter the address of the register, then enter the relevant parameter value.

0x00 and 0xff together form the complete register ---->0xff00

0x12,0x87,0x01: values written to 0xff00.

The driver needs to follow the same logic to write the initialization sequence.

  • Define an address 0x00:
  • A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

  • Function calculations:
  • A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

    A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

    A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

    Use the dcs_write_cmd_at function to write the value, the first bit is the dev, the second bit is the register address, and the third bit onwards is the data to be written. The dcs_write_cmd_at function calls dcs_write_seq for arithmetic:

  • dcs_write_seq(device, MCS_ADRSFT, (c) & 0xFF);
  • The first value is dev, the second is 0X00, and the third is the lower eight bits taken from the register address filled in the dcs_write_cmd_at function (i.e., the lower eight bits of 0xff00 are taken to be 0x00).

  • dcs_write_seq(device, (c) >> 8, seq);
  • The first value is dev, and the second value is the registered address filled in the dcs_write_cmd_at function shifted eight bits to the right, which is equivalent to taking the high octet of the registered address (i.e., taking the high octet of 0xff00 to be 0xff). The third value is the start of the writing of the data that needs to be written in the register.

Drive operation part:

A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

> Case 2: 0 x00 enable is not required

Since the initialization sequence also has such registers that do not require 0x00 to enable them:

A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

Therefore, it is necessary to add another function:

A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

The first value is dev, the second value is the register address filled in the dcs_write_cmd_at2 function, and the third value starts writing the data to be written in the register.

A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

2.2 Power-on Reset Mode

You need to check the mipi manual for the ported screen to see if there is a description of reset mode in there, for example:

A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

The reset pin needs to be configured in the mipi driver to pull high and low as per the manual.However, there is also a possibility that even following the time configuration in the manual may not result in a normal reset. In such cases, you can seek help from the screen manufacturer to obtain reference drivers and see how the reset is configured inside.

Reset command reference:

A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

2.3 Transplantation Complete Process (eg. 8MP)

2.3.1 Internal Kernel Phase Modifications

(1) Modify the corresponding screen parameters in the device tree.

vi kernel/arch/arm64/boot/dts/freescale/OK8MP-C.dts

Find the node panel7_1024x600 and modify the screen reference and clock according to the parameters in the screen manual.

This contains the resolution of the screen, the front and back, the clock, the system derives the refresh rate by itself through the relationship between the above variables, so fill in the rigorous, allowing a small range of error, but the error should not be too large. The formulae are as follows:

clock-frequency= fframe*(hfront+hback+hsync+xres)*(vfront+vback+vsync+yres)

2.3.2 Uboot Stage Modifications

(1)Add and modify the mipi driver, the main modifications i.e. initialization sequence and reset state.

(2)Calculate the clock and modify it according to the actual situation of the ported screen.

                        vi uboot/arch/arm/mach-imx/imx8m/clock_imx8mm.c //The file modified during the U-Boot phase of the 8MP is also named 8mm.
P45:
Add the clock to port the screen in the imx8mm _ fracpll _ tbl:
PLL_1443X_RATE(
                        clock-frequency×6, m, p, s, k),
P279:
#define VIDEO_PLL_RATE clock-frequency×6U             //Modify the parameters here to be consistent with the above.

The value here is rate, mdiv, pdiv, sdiv, kdiv.

The value of rate is clock-frequency × 6 for single-channel screen and clock-frequency × 6 × 2 for dual-channel screen, in MHz;

Calculate the pms value from the clock value, Fout = (M*Fin)/(P*2S), Fin = 24;The default kdiv value is calculated as 0.

                        vi uboot/drivers/video/imx/sec_mipi_dsim.c P814:
dsim_host->pms = 0x4322; //300M
Add under:
dsim_host->pms = ( ( p << 13) | ( m << 3) | ( s << 0)); //300M

The value of m here must be greater than in clock_imx8mm.c!!!! (dsi clock must be greater than lcd clock)

Example: clock_imx8mm.c in m is written 68, then here must be greater than 68, can write 70.

                        vi uboot/drivers/video/imx/sec_mipi_dsim.c P146-151:
      ret = panel_enable_backlight(priv->panel);
      if (ret) {
              dev_err(dev, "panel %s enable backlight error %d\n",
                      priv->panel->name, ret);
              return ret;
      }
If blocked, unblock to make this code effective

(3)Full compilation and burn-in verification.

3. LVDS Screen Migration

3.1 Single Octal/Dual Octal/Single Hex/Dual Hex Differences

(1)The actual clock for dual-screen operation is twice the clock specified in the display manual, then multiplied by 7 (for single-screen operation, multiply only by 7, not by 2).

vi uboot/arch/arm/mach-imx/imx8m/clock_imx8mm.c
P50:
Clock configuration added to imx8mm_fracpll_tbl function
PLL_1443X_RATE(756000000U, 126, 2, 1, 0),    //The first value is: clock x 7; the last three are: pms
P279:
#define VIDEO_PLL_RATE 756000000U                                 //Modified according to clock
 
P330:
clock_set_target_val(base_addr, CLK_ROOT_ON | CLK_ROOT_SOURCE_SEL(1) | CLK_ROOT_PRE_DIV(best_pre - 1) | CLK_ROOT_POST_DIV(best_post - 1));
Modify to:
clock_set_target_val(base_addr, CLK_ROOT_ON | CLK_ROOT_SOURCE_SEL(1) | CLK_ROOT_PRE_DIV(1 - 1) | CLK_ROOT_POST_DIV(7 - 1));

(2)8mp porting dual requires register changes in Uboot (single octal does not require register changes)

LDB_CTRL(32ec005c)和LVDS_CTRL(32ec0128)The registers are described below:

LDB_CTRL(32ec005c):

A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

LVDS_CTRL(32ec0128):

A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

Calculate the register values and modify them using the dual octal example:

                        vi uboot/drivers/video/imx/imx8mp_lvds.c P63:
writel(priv->ldb_ctrl, 0x32ec005c);
writel(0x1074 | (priv->chan[0].is_valid?1:0) | (priv->chan[1].is_valid?2:0) , 0x32ec0128);
Modify to:
writel(0xbd, 0x32ec005c);
writel(
                        0x1077, 0x32ec0128);
P149:
clk_set_rate(&priv->ldb_clk, priv->dual? priv->pix_rate*7/2:priv->pix_rate*7);
Masking, adding: 
                        writel(0x17000001, 0x3038bf00);

"LDB_CTRL" settings for dual six-way: 0x1d (0001 1101); for dual eight-way: 0xbd (1011 1101).

3.2 Display Modes Differences

A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

The display format is configured in the uboot registers and in the kernel device tree, you need to determine the exact format of the screen you are porting, if it is mismatched there will be colour issues.

3.3 Transplantation of the Complete Process

3.3.1 Kernel Stage Modifications

(1) Modify the corresponding screen reference in the device tree.

vi kernel/arch/arm64/boot/dts/freescale/OK8MP-C.dts

Find the node lvds0_panel and modify the screen reference and clock according to the parameters inthe screen manual (the clock cannot be lower than 25MHz).

This contains the resolution of the screen, the front and back, the clock, the system derives the refresh rate by itself through the relationship between the above variables, so fill in the rigorous, allowing a small range of error, but the error should not be too large. The formulae are as follows:

clock-frequency= fframe*(hfront+hback+hsync+xres)*(vfront+vback+vsync+yres)

(2) Calculate the clock and modify it according to the actual situation of the transplanted screen.

                        vi kernel/drivers/clk/imx/clk-imx8mp.c P57:
Add the clock for the ported screen to imx8mp_videopll_tbl:
PLL_1443X_RATE(
                        clock-frequency×7, m, p, s, k),

The value here is rate, mdiv, pdiv, sdiv, kdiv.

The value of rate is clock-frequency × 7 for single-channel screen and clock-frequency × 7 × 2 for dual-channel screen, in MHz;

Calculate the pms value from the clock value, Fout = (M*Fin)/(P*2S), Fin = 24;

The default kdiv value is calculated as 0.

                        vi kernel/arch/arm64/boot/dts/freescale/imx8mp.dtsi P714:
In assigned-clock-rates change the last value; to the rate value calculated above

(3) Write off the restrictions on the clock.

                        vi kernel/drivers/gpu/drm/imx/imx8mp-ldb.c P200:
/* if (ldb->dual)
                mode->clock = mode->clock > 100000 ? 148500 : 74250;
        else
                mode->clock = 74250;
*/
P226:
/* if (ldb->dual && mode->clock != 74250 && mode->clock != 148500)
                return MODE_NOCLOCK;
        if (!ldb->dual && mode->clock != 74250)
                return MODE_NOCLOCK;
*/

3.3.2 Modifications to the Uboot Stage

The steps to modify the uboot stage are basically the same as the kernel stage, both require changing the device tree and adding clocks.

(1) Modify the corresponding screen reference in the device tree.

vi uboot/arch/arm/dts/OK8MP-C.dts

Find the node lvds0_panel and modify the screen reference and clock.

(2) Calculate the clock and modify it according to the actual situation of the transplanted screen.

                        vi uboot/arch/arm/mach-imx/imx8m/clock_imx8mm.c //The file modified during the U-Boot phase of the 8MP is also named 8mm.
P45:
Add the clock to port the screen in the imx8mm _ fracpll _ tbl:
PLL_1443X_RATE(
                        clock-frequency×7, m, p, s, k),
P279:
#define VIDEO_PLL_RATE clock-frequency×7U             //Modify the parameters here to be consistent with the above.

(3) Two-way screen plus register calculations in 3.1.

(4) Full compilation and burn-in verification.

If the screen display is still faulty, take care to check that the pms as well as the FFVCO are within range.

4.Mipi to LVDS Migration

4.1 Uboot Non-open Source Situation

Uboot display parameter settings

Uboot is not open source. If the customer adjusts the uboot phase by himself, he only needs to modify the panel to MIPI2HDMI _ CUSTOM, and then modify the display parameters such as xres through the environment variable custom _ video _ mode:

Verify whether the screen parameters are correct by adding environment variables in the uboot phase:

                        setenv panel MIPI2HDMI_CUSTOM
setenv custom_video_mode 'refresh=60,xres=1024,yres=768,pixclock=15384,left_margin=160,right_margin=24,upper_margin=29,lower_margin=3,hsync_len=136,vsync_len=6'

After verification, modify the source code.

4.2 Transplantation of the Complete Process

Customer's Screen Reference:

A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

VESA

4.2.1 Uboot Stage Modifications

Modify the uboot menu to add the desired resolution option:

                        vi vendor/nxp-opensource/uboot-imx/cmd/forlinx.c static void show_main_menu(void)
{
	printf("--------------------------\n");
	printf("0:reboot\n");
	printf("1:exit to shell\n");
	printf("2:MIPI7\n");
	printf("3:MIPI2HDMI 1080P\n");
	printf("4:MIPI2HDMI 720P\n");
	printf("5:MIPI2HDMI 1280x800\n");
	printf("6:MIPI2HDMI 1024x768\n");
	printf("--------------------------\n");
}
[……]
static int do_forlinx(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
			case '5':
				run_command("setenv panel MIPI2HDMI_1280x800", 0);
				run_command("setenv lcd_density androidboot.lcd_density=160", 0);
				run_command("setenv displaymode androidboot.displaymode=wxga", 0);
				run_command("setenv touchscreen gt928", 0);
				break; 
                        case '6':
				run_command("setenv panel MIPI2HDMI_CUSTOM", 0);
				run_command("setenv lcd_density androidboot.lcd_density=160", 0);
				run_command("setenv displaymode androidboot.displaymode=custom", 0);
				run_command("setenv touchscreen gt928", 0);
				break;
			default:
				break; }
		run_command("saveenv", 0);
	}
	return 0;
}

The MIPI2HDMI_CUSTOM and custom definitions mentioned in the bolded section are in respectively:

MIPI2HDMI_CUSTOM:vendor/nxp-opensource/uboot-imx/board/freescale/imx8mm_evk/imx8mm_evk.ccustom:vendor/nxp-opensource/imx/display/display/KmsDisplay.cpp

Modify the screen reference in the above two files:

                        vi vendor/nxp-opensource/uboot-imx/board/freescale/imx8mm_evk/imx8mm_evk.c struct display_info_t const displays[] = {
[……]
	{
		.bus = LCDIF_BASE_ADDR,
		.addr = 0,
		.pixfmt = 24,
		.detect = NULL,
		.enable = do_enable_mipi2hdmi,
		.mode = {
				.name					= "MIPI2HDMI_CUSTOM",
				.refresh					= 60,
				.xres					= 1024,
				.yres					= 768,
				.pixclock					= 15385, //65000000
				.left_margin		= 160,
				.right_margin		= 24,
				.upper_margin	= 29,
				.lower_margin	= 3,
				.hsync_len				= 136,
				.vsync_len				= 6,
				.sync					= FB_SYNC_EXT,
				.vmode					= FB_VMODE_NONINTERLACED
	},
}

Modify the android layer configuration to add custom:

                        vi vendor/nxp-opensource/imx/display/display/KmsDisplay.cpp DisplayMode gDisplayModes[18] = {
 {"4kp60", 3840, 2160, 60},
 {"4kp50", 3840, 2160, 50},
 {"4kp30", 3840, 2160, 30},
 {"1080p60", 1920, 1080, 60},
 {"1080p50", 1920, 1080, 50},
 {"1080p30", 1920, 1080, 30},
 {"720p60", 1280, 720, 60},
 {"720p50", 1280, 720, 50},
 {"720p30", 1280, 720, 30},
 {"480p60", 640, 480, 60},
 {"480p50", 640, 480, 50},
 {"480p30", 640, 480, 30},
 {"4k", 3840, 2160, 60}, // default as 60fps
 {"1080p", 1920, 1080, 60}, // default as 60fps
 {"720p", 1280, 720, 60}, // default as 60fps
 {"480p", 640, 480, 60}, // default as 60fps
 {"wxga", 1280, 800, 60}, // default as 60fps
 {"custom", 1024, 768, 60},
};

Modify the environment variables to show lvds by default:

                        vi vendor/nxp-opensource/uboot-imx/include/configs/imx8mm_evk_android.h #define CONFIG_EXTRA_ENV_SETTINGS               \
        “splashpos=m,m\0"                       \
        "panel=MIPI2HDMI_CUSTOM\0" \
        “fdt_high=0xffffffffffffffff\0"         \

panel=MIPI7 modify to panel=MIPI2HDMI_CUSTOM

[Can be changed or not] If only one screen is used, you can also directly delete all the screen judgment parts in the following documents:

vi vendor/nxp-opensource/uboot-imx/board/freescale/imx8mm_evk/imx8mm_evk.c

A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

4.2.2 Internal Kernel Phase Modifications

(1) Set screen parameters

Modify the screen parameters in the driver file corresponding to the kernel stage:

                        vi vendor/nxp-opensource/kernel_imx/drivers/gpu/drm/bridge/lt8912.c static struct drm_display_mode forlinx_mipi7_mode[5] = {
[……] 
	{
		.name = "lt8912_custom",
		.clock = 65000,
		.hdisplay = 1024,
		.hsync_start = 1024 + 160,
		.hsync_end = 1024 + 136 + 160,
		.htotal = 1024 + 24 + 160 + 136,
		.vdisplay = 768,
		.vsync_start = 768 + 29,
		.vsync_end = 768 + 6 + 29,
		.vtotal = 768 + 3 + 29 + 6,
		.vrefresh = 60,
	}
};

Algorithm

The addition in the above parameter is: back

lens+back

front+back+lens

You can refer to the existing parameters in the source code to try to modify them:

                        vi vendor/nxp-opensource/kernel_imx/drivers/video/fbdev/via/viamode.c static const struct fb_videomode viafb_modes[] = {
[……]
	{NULL, 60, 1024, 768, 15385, 160, 24, 29, 3, 136, 6, 0, 0, 0},
};

The meaning of each parameter:

A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

Screen reference modification in the device tree:

vi vendor/nxp-opensource/kernel_imx/arch/arm64/boot/dts/freescale/ok8mm-evk.dts
lt8912_bridge_CUSTOM: lt8912_custom@48 {
                compatible = "lontium,lt8912-CUSTOM";
                reg =;
                status = "disabled";
                port {
                        lt8912_from_dsim_custom: endpoint {
                                remote-endpoint = <&dsim_to_lt8912_custom>;
                        };
                };
                display-timings {
                        timing {
                                clock-frequency =;
                                hactive =;
                                vactive =;
                                hfront-porch =;
                                hsync-len =;
                                hback-porch =;
                                vfront-porch =;
                                vsync-len =;
                                vback-porch =;
                                hsync-active =;
                                vsync-active =;
                                de-active =;
                                pixelclk-active =;
                        };
                };
        };

(2) Kernel modification of dsi driver

> PLL(PMS)

After calculating the bit_clk and pms values, add them to the code:

Driver file: OK8MM-android-source/

                        vi vendor/nxp-opensource/kernel_imx/drivers/gpu/drm/bridge/sec-dsim.c static const struct dsim_pll_pms pll_pms[] = {
        { DSIM_PLL_PMS(445056, 1, 66, 2), },
        { DSIM_PLL_PMS(390000, 3, 87, 1), },
        { DSIM_PLL_PMS(324000, 3, 72, 1), },
};

Since frequency=65MHz, bit_clk=Fout=390

p=3,m=87,s=1,Fout=(27*M)/(P * 2^S)

> Hpar Addition

Add the hpar value inside the code:

                        vi vendor/nxp-opensource/kernel_imx/drivers/gpu/drm/bridge/sec-dsim.c static const struct dsim_hblank_par hblank_4lanes[] = { 
                         /* { 1024x768 } */
        { DSIM_HBLANK_PARAM("1024x768" , 60,  12, 114,  96, 4), }, };

Add Hpar parameter, mainly to calculate the value of hfp_wc, hbp_wc, hsa_wc in general:

A Comprehensive Guide to Screen Porting for the Forlinx Embedded 8M Series

  • Name is 1024x768;
  • Vf is 60;
  • Hfp_wc is the display mode parameter inside the hfront_porch * 3 /4 - 6=12;
  • hbp_wc is the display mode parameter inside hback_porch * 3 /4 - 6=114;
  • hsa_wc is the display mode parameter inside hsync_len * 3 /4 - 6=96;
  • The last digit 4 refers to a four-way signal.

Modify to single 6-way

                        vi vendor/nxp-opensource/uboot-imx/board/freescale/imx8mm_evk/imx8mm_evk.c void lt8912_lvds_bypass_cfg(struct udevice *main_dev){
[……]
        lt8912_i2c_reg_write(main_dev, 0x04, 0xff, 0xff);
        lt8912_i2c_reg_write(main_dev, 0x7f, 0xff, 0x00);//disable scaler
        lt8912_i2c_reg_write(main_dev, 0xa8, 0xff, 0x17);//0x13 : JEIDA, 0x33:VSEA
[……]
}
                        vi vendor/nxp-opensource/kernel_imx/drivers/gpu/drm/bridge/lt8912.c static struct lt8912_reg_cfg lt8912_lvds_bypass_cfg[] = {
[……]
        {0x04, 0xff, 0},
        {0x7f, 0x00, 0},//disable scaler
        {0xa8, 0x17, 0},//0x13 : JEIDA, 0x33:VSEA
//      {0x02, 0xf7, 0},//lvds pll reset
[……]
};

The above two places are where the mipi to lvds 8912 module switches modes from 0x13 to 0x17 i.e. from single eight to single six.

After completing all the modifications you can compile and burn the verification.