mipi code analysis and new screen addition in Qualcomm lk stage
2021-01-28 17:02:23 reading: 146 source: Internet
label: mipi lk ret high pass pinfo init PANEL panel
☺ Hungry? Take out loophole, up to 15 yuan ☺
reference resources: https://www.cnblogs.com/linhaostudy/p/9237526.html
background
I did this work some time ago, but after summing up, the leader said that my understanding was still too superficial, so I looked again.
There are some parts that I didn't know at that time.
code analysis
Take the VIDEO type in the MIPI screen as an example.
Function entry
aboot_init() to target_display_init();
Then, judge whether to enter which function to process according to different target s (see Appendix):
target_ display_ An important function in the init () function is gcdb_display_init();
void target_display_init(const char *panel_name) { uint32_t panel_loop = 0; uint32_t ret = 0; panel_name += strspn(panel_name, " "); if (!strcmp(panel_name, NO_PANEL_CONFIG) || !strcmp(panel_name, SIM_VIDEO_PANEL) || !strcmp(panel_name, SIM_CMD_PANEL)) { dprintf(INFO, "Selected %s: Skip panel configuration\n", panel_name); return; } do { target_force_cont_splash_disable(false); ret = gcdb_display_init(panel_name, MDP_REV_305, MIPI_FB_ADDR); if (!ret || ret == ERR_NOT_SUPPORTED) { break; } else { target_force_cont_splash_disable(true); msm_display_off(); } } while (++panel_loop <= oem_panel_max_auto_detect_panels()); }
This is the key to Qualcomm's native lk LCD compatibility.
Compatibility documentation for: Qualcomm platform realizes multi LCD screen compatibility by reading screen ID in lk stage
gcdb_display_init
GCDB: Global Component Database
gcdb_display_init initializes different parameters according to different PANEL types (determined by the return value of oem_panel_select, similar to polymorphism)
1. Determine the behavior of pan (in the form of function pointer):
- pll_clk_func (if any)
- power_func
- bl_func
2. Set the corresponding pan attribute
- Width and height
- data type
int gcdb_display_init(const char *panel_name, uint32_t rev, void *base) { int ret = NO_ERROR; int pan_type; pan_type = oem_panel_select(panel_name, &panelstruct, &(panel.panel_info), &dsi_video_mode_phy_db); // DSI if (pan_type == PANEL_TYPE_DSI) { init_platform_data(); if (dsi_panel_init(&(panel.panel_info), &panelstruct)) { dprintf(CRITICAL, "DSI panel init failed!\n"); ret = ERROR; goto error_gcdb_display_init; } panel.panel_info.mipi.mdss_dsi_phy_db = &dsi_video_mode_phy_db; panel.pll_clk_func = mdss_dsi_panel_clock; panel.power_func = mdss_dsi_panel_power; panel.pre_init_func = mdss_dsi_panel_pre_init; panel.bl_func = mdss_dsi_bl_enable; panel.fb.base = base; panel.fb.width = panel.panel_info.xres; panel.fb.height = panel.panel_info.yres; panel.fb.stride = panel.panel_info.xres; panel.fb.bpp = panel.panel_info.bpp; panel.fb.format = panel.panel_info.mipi.dst_format; // EDP } else if (pan_type == PANEL_TYPE_EDP) { mdss_edp_panel_init(&(panel.panel_info)); /* prepare func is set up at edp_panel_init */ panel.clk_func = mdss_edp_panel_clock; panel.power_func = mdss_edp_panel_power; panel.bl_func = mdss_edp_bl_enable; panel.fb.format = FB_FORMAT_RGB888; // SPI } else if (pan_type == PANEL_TYPE_SPI) { dprintf(CRITICAL, "add the spi panel init config data!\n"); panel.panel_info.xres = panelstruct.panelres->panel_width; panel.panel_info.yres = panelstruct.panelres->panel_height; panel.panel_info.bpp = panelstruct.color->color_format; panel.power_func = mdss_spi_panel_power; panel.bl_func = mdss_spi_bl_enable; panel.fb.base = base; panel.fb.width = panel.panel_info.xres; panel.fb.height = panel.panel_info.yres; panel.fb.bpp = panel.panel_info.bpp; panel.fb.format = FB_FORMAT_RGB565; panel.panel_info.type = SPI_PANEL; } else { dprintf(CRITICAL, "Target panel init not found!\n"); ret = ERR_NOT_SUPPORTED; goto error_gcdb_display_init; } panel.fb.base = base; panel.mdp_rev = rev; ret = msm_display_init(&panel); error_gcdb_display_init: display_enable = ret ? 0 : 1; return ret; }
After initialization, call msm_display_init() function.
msm_display_init
What has just been executed will be called here:
In MSM_ display_ In init(), Turn on panel first, then Turn on backlight;
All behaviors are initialized above.
int msm_display_init(struct msm_fb_panel_data *pdata) { int ret = NO_ERROR; panel = pdata; if (!panel) { ret = ERR_INVALID_ARGS; goto msm_display_init_out; } /* Turn on panel */ if (pdata->power_func) ret = pdata->power_func(1, &(panel->panel_info)); /* Enable clock */ if (pdata->clk_func) ret = pdata->clk_func(1); /* Only enabled for auto PLL calculation */ if (pdata->pll_clk_func) ret = pdata->pll_clk_func(1, &(panel->panel_info)); /* pinfo prepare */ if (pdata->panel_info.prepare) { /* this is for edp which pinfo derived from edid */ ret = pdata->panel_info.prepare(); panel->fb.width = panel->panel_info.xres; panel->fb.height = panel->panel_info.yres; panel->fb.stride = panel->panel_info.xres; panel->fb.bpp = panel->panel_info.bpp; } ret = msm_fb_alloc(&(panel->fb)); ret = msm_display_config(); fbcon_setup(&(panel->fb)); display_image_on_screen(); ret = msm_display_on(); if (pdata->post_power_func) ret = pdata->post_power_func(1); mdelay(100); /* Turn on backlight */ if (pdata->bl_func) ret = pdata->bl_func(1); msm_display_init_out: return ret; }
Supply power to L2, L6 and L17
/* Turn on panel */ if (pdata->power_func) ret = pdata->power_func(1, &(panel->panel_info)); mdss_dsi_panel_power ret = target_ldo_ctrl(enable, pinfo); regulator_enable(); /* L2, L6, and L17 */
Configure clock
Call calculate_clock_config(pinfo) calculate clock configuration
Call target_panel_clock(enable, pinfo) configures the clock of the target panel.
/* Enable clock */ if (pdata->clk_func) ret = pdata->clk_func(1); mdss_dsi_panel_clock ret = calculate_clock_config(pinfo); ret = target_panel_clock(enable, pinfo);
Allocate and set frame cache
dev/gcdb/display/gcdb_display.c
msm_ fb_ Alloc (& (panel - > FB)) allocates the memory required by the frame buffer
And fbcon_ Setup (& (panel - > FB)) allocates memory for.
msm_display_config is configured according to the type of subdivision panel
ret = msm_fb_alloc(&(panel->fb)); ret = msm_display_config(); fbcon_setup(&(panel->fb)); /**********************************************/ int msm_display_config() { int ret = NO_ERROR; int mdp_rev; struct msm_panel_info *pinfo; pinfo = &(panel->panel_info); /* Set MDP revision */ mdp_set_revision(panel->mdp_rev); switch (pinfo->type) { case LVDS_PANEL: dprintf(INFO, "Config LVDS_PANEL.\n"); ret = mdp_lcdc_config(pinfo, &(panel->fb)); break; case MIPI_VIDEO_PANEL: dprintf(INFO, "Config MIPI_VIDEO_PANEL.\n"); mdp_rev = mdp_get_revision(); if (mdp_rev == MDP_REV_50 || mdp_rev == MDP_REV_304 || mdp_rev == MDP_REV_305) ret = mdss_dsi_config(panel); // Look down else ret = mipi_config(panel); ret = mdp_dsi_video_config(pinfo, &(panel->fb)); break; case MIPI_CMD_PANEL: dprintf(INFO, "Config MIPI_CMD_PANEL.\n"); //... ret = mdp_dsi_cmd_config(pinfo, &(panel->fb)); if (ret) goto msm_display_config_out; break; default: return ERR_INVALID_ARGS; }; if (pinfo->config) ret = pinfo->config((void *)pinfo); msm_display_config_out: return ret; }
Configuration controller
int mdss_dsi_config(struct msm_fb_panel_data *panel) { int ret = NO_ERROR; struct msm_panel_info *pinfo; struct mipi_dsi_panel_config mipi_pinfo; #if (DISPLAY_TYPE_MDSS == 1) if (!panel) return ERR_INVALID_ARGS; memset(&mipi_pinfo, 0, sizeof(mipi_pinfo)); pinfo = &(panel->panel_info); mipi_pinfo.mode = pinfo->mipi.mode; mipi_pinfo.num_of_lanes = pinfo->mipi.num_of_lanes; mipi_pinfo.mdss_dsi_phy_config = pinfo->mipi.mdss_dsi_phy_db; mipi_pinfo.panel_cmds = pinfo->mipi.panel_cmds; mipi_pinfo.num_of_panel_cmds = pinfo->mipi.num_of_panel_cmds; mipi_pinfo.lane_swap = pinfo->mipi.lane_swap; mipi_pinfo.pack = 0; mipi_pinfo.t_clk_pre = pinfo->mipi.t_clk_pre; mipi_pinfo.t_clk_post = pinfo->mipi.t_clk_post; mipi_pinfo.signature = pinfo->mipi.signature; mipi_pinfo.force_clk_lane_hs = pinfo->mipi.force_clk_lane_hs; mipi_pinfo.cmds_post_tg = pinfo->mipi.cmds_post_tg; mipi_pinfo.panel_read_cmds = pinfo->mipi.panel_read_cmds; // If there are two MIPI DSI interfaces MIPI_DSI0 and MIPI_DSI1 calls MDSS twice_ dsi_ phy_ Init(), msm8909 only MIPI_DSI0, MSM8994, etc. have two DSI interfaces. mdss_dsi_phy_init(&mipi_pinfo, MIPI_DSI0_BASE, DSI0_PHY_BASE); if (pinfo->mipi.dual_dsi) mdss_dsi_phy_init(&mipi_pinfo, MIPI_DSI1_BASE, DSI1_PHY_BASE); //Initialize the host controller of the DSI interface. ret = mdss_dsi_host_init(&mipi_pinfo, pinfo->mipi.dual_dsi, pinfo->mipi.broadcast); if (ret) { dprintf(CRITICAL, "dsi host init error\n"); goto error; } mdss_dsi_phy_contention_detection(&mipi_pinfo, DSI0_PHY_BASE); // Look down if (panel->pre_init_func) { ret = panel->pre_init_func(); if (ret) { dprintf(CRITICAL, "pre_init_func error\n"); goto error; } } if (mipi_pinfo.force_clk_lane_hs) mdss_dsi_force_clk_lane_hs(pinfo->mipi.dual_dsi); if (!mipi_pinfo.cmds_post_tg) { ret = mdss_dsi_panel_initialize(&mipi_pinfo, pinfo->mipi.broadcast); if (ret) { dprintf(CRITICAL, "dsi panel init error\n"); goto error; } } if (pinfo->rotate && panel->rotate) pinfo->rotate(); #endif error: return ret; }
Call the if (panel - > pre_init_func) {} function:
Because panelstruct paneldata->panel_ lp11_ Init in init_ panel_ The data() function is assigned a value of 1,
So call mdss_dsi_panel_reset() resets the panel according to the reset timing.
static int mdss_dsi_panel_pre_init(void) { intret = NO_ERROR; if(panelstruct.paneldata->panel_lp11_init) { ret= mdss_dsi_panel_reset(1); if(ret) { dprintf(CRITICAL,"panel reset failed\n"); return ret; } } if(panelstruct.paneldata->panel_init_delay) udelay(panelstruct.paneldata->panel_init_delay); dprintf(SPEW,"Panel pre init done\n"); return ret; }
Display logo
display_image_on_screen calls fetch_image_from_partition() obtains lk logo image from splash partition. If the splash partition does not meet the required data, the default logo will be displayed.
display_image_on_screen fetch_image_from_partition
Initialize panel
See notes for main parts:
int msm_display_on() { int ret = NO_ERROR; int mdp_rev; struct msm_panel_info *pinfo; bs_set_timestamp(BS_SPLASH_SCREEN_DISPLAY); pinfo = &(panel->panel_info); if (pinfo->pre_on) { ret = pinfo->pre_on(); } switch (pinfo->type) { //... case MIPI_VIDEO_PANEL: dprintf(INFO, "Turn on MIPI_VIDEO_PANEL.\n"); ret = mdp_dsi_video_on(pinfo); // Enable DSI VIDEO ret = mdss_dsi_post_on(panel); // Use the initialization command to initialize the panel, including the identification screen (related to compatibility) ret = mipi_dsi_on(); break; case MIPI_CMD_PANEL: dprintf(INFO, "Turn on MIPI_CMD_PANEL.\n"); ret = mdp_dma_on(pinfo); mdp_rev = mdp_get_revision(); if (mdp_rev...) { ret = mipi_cmd_trigger(); } ret = mdss_dsi_post_on(panel); break; default: return ERR_INVALID_ARGS; }; if (pinfo->on) ret = pinfo->on(); msm_display_on_out: return ret; }
Turn on the backlight
/* Turn on backlight */ if (pdata->bl_func) ret = pdata->bl_func(1);
In gcdb_ display_ The init() function has a function pointer mdss_dsi_bl_enable:
This function is used to adjust and enable the backlight through PWM: (see the schematic diagram for details)
static int mdss_dsi_bl_enable(uint8_t enable) { int ret = NO_ERROR; ret = panel_backlight_ctrl(enable); if (ret) dprintf(CRITICAL, "Backlight %s failed\n", enable ? "enable" : "disable"); return ret; } static uint32_t panel_backlight_ctrl(uint8_t enable) { uint32_t ret = NO_ERROR; if (panelstruct.backlightinfo) ret = target_backlight_ctrl(panelstruct.backlightinfo, enable); return ret; }
So the backlight of bootloader is through target_ backlight_ Controlled by ctrl().
int target_backlight_ctrl(struct backlight *bl, uint8_t enable) { struct pm8x41_mpp mpp; uint32_t hw_id = board_hardware_id(); struct board_pmic_data pmic_info; int rc; if (bl->bl_interface_type == BL_DCS) return 0; board_pmic_info(&pmic_info, 1); if (pmic_info.pmic_version == PM8916_VER) mpp.base = PM8x41_MMP4_BASE; else mpp.base = PM8x41_MMP2_BASE; mpp.vin = MPP_VIN0; if (enable) { pm_pwm_enable(false); rc = pm_pwm_config(PWM_DUTY_US, PWM_PERIOD_US); if (rc < 0) mpp.mode = MPP_HIGH; else { mpp.mode = MPP_DTEST1; pm_pwm_enable(true); } pm8x41_config_output_mpp(&mpp); pm8x41_enable_mpp(&mpp, MPP_ENABLE); } else { pm_pwm_enable(false); pm8x41_enable_mpp(&mpp, MPP_DISABLE); } mdelay(20); if (board_hardware_subtype() == HW_PLATFORM_SUBTYPE_IOE) bkl_gpio.pin_id = 99; if (enable) { if (hw_id == HW_PLATFORM_SURF || (hw_id == HW_PLATFORM_MTP)) { /* configure backlight gpio for CDP and MTP */ gpio_tlmm_config(bkl_gpio.pin_id, 0, bkl_gpio.pin_direction, bkl_gpio.pin_pull, bkl_gpio.pin_strength, bkl_gpio.pin_state); gpio_set(bkl_gpio.pin_id, 2); } } return 0; }
How to add a panel
Take xxx of video type as an example.
Declare panel enumeration
In OEM_ panel. Add a panel enumeration in C
enum { ILI9881D_720P_VIDEO_PANEL, HX8394D_480P_VIDEO_PANEL, HX8394D_720P_VIDEO_PANEL, SHARP_QHD_VIDEO_PANEL, TRULY_WVGA_CMD_PANEL, HX8379A_FWVGA_SKUA_VIDEO_PANEL, ILI9806E_FWVGA_VIDEO_PANEL, HX8394D_QHD_VIDEO_PANEL, HX8379C_FWVGA_VIDEO_PANEL, FL10802_FWVGA_VIDEO_PANEL, AUO_QVGA_CMD_PANEL, AUO_CX_QVGA_CMD_PANEL, HX8394F_720P_VIDEO_PANEL, ILI9881C_720P_VIDEO_PANEL, GC9306_QVGA_SPI_CMD_PANEL, XXX_VIDEO_PANEL, // for instance UNKNOWN_PANEL };
Bind the panel enumeration to the specific panel attribute
1. Add the corresponding header file #include "include/xxx_video.h"
Header files can be generated through GCDB tool configuration. If you are too lazy to do so, you can ask the supplier for them.
The header file is placed in: bootable/bootloader/lk/dev/gcdb/display/include
2. In sup_ Add binding to panels
3. Add the corresponding attribute according to the cat. The corresponding attribute is from xx_panel.h (specifically generated by GCDB)
#include "include/xxx_video.h" // If you want to add a panel, you need to add a sup here_ panels static struct panel_list supp_panels[] = { {"truly_1080p_video", TRULY_1080P_VIDEO_PANEL}, {"truly_1080p_cmd", TRULY_1080P_CMD_PANEL}, {"r69006_1080p_video", R69006_1080P_VIDEO_PANEL}, {"r69006_1080p_cmd", R69006_1080P_CMD_PANEL}, {"truly_wuxga_video", TRULY_WUXGA_VIDEO_PANEL}, {"nt35523_720p_video", NT35523_720P_VIDEO_PANEL}, {"xxx_video", XXX_VIDEO_PANEL}, // New binding }; static int init_panel_data(struct panel_struct *panelstruct, struct msm_panel_info *pinfo, struct mdss_dsi_phy_ctrl *phy_db) { int pan_type = PANEL_TYPE_DSI; switch (panel_id) { case GC9306_QVGA_SPI_CMD_PANEL: panelstruct->paneldata = &gc9306_qvga_cmd_panel_data; panelstruct->panelres = &gc9306_qvga_cmd_panel_res; panelstruct->color = &gc9306_qvga_cmd_color; panelstruct->panelresetseq = &gc9306_qvga_cmd_reset_seq; panelstruct->backlightinfo = &gc9306_qvga_cmd_backlight; pinfo->spi.panel_cmds = gc9306_qvga_cmd_on_command; pinfo->spi.num_of_panel_cmds = GC9306_QVGA_CMD_ON_COMMAND; pan_type = PANEL_TYPE_SPI; break; // Add attribute case XXX_VIDEO_PANEL: // Add the corresponding attribute according to the cat. case UNKNOWN_PANEL: default: memset(panelstruct, 0, sizeof(struct panel_struct)); memset(pinfo->mipi.panel_cmds, 0, sizeof(struct mipi_dsi_cmd)); pinfo->mipi.num_of_panel_cmds = 0; memset(phy_db->timing, 0, TIMING_SIZE); pan_type = PANEL_TYPE_UNKNOWN; break; } return pan_type; }
Specifies the Panel enumeration for the platform
In OEM_ panel_ Specify the panel of the corresponding platform in the select() function_ id = XXX_ VIDEO_ PANEL;:
The main work of this function is to identify different IC s and assign values to the parameter panel_id,panel_ ID is init in the same file_ panel_ In the data() function.
int oem_panel_select(const char *panel_name, struct panel_struct *panelstruct, struct msm_panel_info *pinfo, struct mdss_dsi_phy_ctrl *phy_db) { //... switch (hw_id) { case xx: panel_id = XXX_VIDEO_PANEL; break; default: dprintf(CRITICAL, "Display not enabled for %d HW type\n", hw_id); return PANEL_TYPE_UNKNOWN; } //... }
Appendix: LCD parameter description
VBPD (vertical back porch): indicates the number of invalid lines after the vertical synchronization signal at the beginning of a frame of image.
Vfpd (vertical front porch): indicates the number of invalid lines before the vertical synchronization signal after the end of a frame of image.
VSPW(verticalsync pulse width): indicates the width of vertical synchronization pulse, calculated by the number of lines.
HBPD(horizontal back porch): indicates the number of VCL s from the beginning of the horizontal synchronization signal to the beginning of the effective data of a line.
HFPD(horizontal front porch): indicates the number of vclks between the end of valid data in one line and the beginning of the next horizontal synchronization signal.
HSPW(horizontalsync pulse width): indicates the width of the horizontal synchronization signal, which is calculated with VCLK
Appendix: select different targets according to target_display
Different platforms have the same target_display.c. For example:
bootable/bootloader/lk/target/msm8909/target_display.c bootable/bootloader/lk/target/target_display.c bootable/bootloader/lk/target/msm8974/target_display.c bootable/bootloader/lk/target/msm8994/target_display.c bootable/bootloader/lk/target/apq8084/target_display.c bootable/bootloader/lk/target/msm8610/target_display.c bootable/bootloader/lk/target/msm7627a/target_display.c bootable/bootloader/lk/target/msm8960/target_display.c bootable/bootloader/lk/target/msm8226/target_display.c bootable/bootloader/lk/target/msm8916/target_display.c
In fact, according to the compilation system, it is easy to find out how the corresponding file is compiled.
-
bootable/bootloader/lk/makefile:134:include target/$(TARGET)/rules.mk
-
bootable/bootloader/lk/target/rules.mk
LOCAL_DIR := $(GET_LOCAL_DIR) OBJS += \ $(LOCAL_DIR)/init.o \ $(LOCAL_DIR)/target_display.o
The final decision is made by the project of lunch:
project/apq8084.mk:9:ifeq ($(TARGET_BUILD_VARIANT),user) project/msm7627a.mk:5:TARGET := msm7627a project/msm8909.mk:5:TARGET := msm8909 project/msm8909.mk:9:ifeq ($(TARGET_BUILD_VARIANT),user) project/msm8960.mk:5:TARGET := msm8960 project/msm8960.mk:9:ifeq ($(TARGET_BUILD_VARIANT),user) project/qsd8250_surf.mk:5:TARGET := qsd8250_surf project/msm8226.mk:5:TARGET := msm8226 project/msm8226.mk:9:ifeq ($(TARGET_BUILD_VARIANT),user) project/aboot-surf7k.mk:5:TARGET := surf-msm7k project/fsm9010.mk:5:TARGET := fsm9010 project/fsm9010.mk:9:ifeq ($(TARGET_BUILD_VARIANT),user) project/msm8660_surf.mk:5:TARGET := msm8660_surf