mipi code analysis and new screen addition in Qualcomm lk stage

Posted by p0pb0b on Thu, 20 Jan 2022 21:55:12 +0100

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

Topics: Android