zoukankan      html  css  js  c++  java
  • 高通lk阶段mipi 代码解析以及新屏幕添加

    参考:https://www.cnblogs.com/linhaostudy/p/9237526.html

    背景

    前段时间做了这块的工作,但总结完以后领导说我的认识还是过于肤浅,因此重新再看了一下。

    确实是有一些当时不知道的部分。

    代码分析

    以 MIPI屏幕中的VIDEO类型为例。

    函数入口

    aboot_init()来到target_display_init()

    然后根据target中的不同,来判断是否进入哪一个函数来处理(见附录):

    target_display_init() 函数里有很重要函数就是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
    ",
                    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());    
    }
    

    这就是高通原生lk LCD 兼容的关键所在。

    有关的兼容文档:高通平台 在lk阶段通过读取屏的ID实现多LCD屏兼容

    gcdb_display_init

    GCDB:Global Component Database全局组件数据库

    gcdb_display_init会根据不同的PANEL类型(由oem_panel_select的返回值决定,类似多态)初始化不同的参数

    1、决定pan的行为(以函数指针的形式):

    • pll_clk_func(如果有)
    • power_func
    • bl_func

    2、设置对应的pan属性

    • 宽高
    • 数据类型
    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!
    ");
                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!
    ");
            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!
    ");
            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;
    }
    

    初始化好之后就调用msm_display_init()函数。

    msm_display_init

    刚刚执行的东西会在这里调用:

    msm_display_init()里先Turn on panel,再Turn on backlight;

    所有的行为都在上面进行了初始化。

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

    给L2、L6、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 */
    
    

    配置时钟

    调用calculate_clock_config(pinfo)计算时钟配置

    调用target_panel_clock(enable, pinfo)配置目标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);
    

    分配并设置帧缓存

    dev/gcdb/display/gcdb_display.c

    msm_fb_alloc(&(panel->fb))分配帧缓冲器(frame buffer)所需的内存

    fbcon_setup(&(panel->fb))为分配内存。

    msm_display_config根据细分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.
    ");
            ret = mdp_lcdc_config(pinfo, &(panel->fb));
    
            break;
        case MIPI_VIDEO_PANEL:
            dprintf(INFO, "Config MIPI_VIDEO_PANEL.
    ");
            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); // 看下面
            else
                ret = mipi_config(panel);
            ret = mdp_dsi_video_config(pinfo, &(panel->fb));
            break;
        case MIPI_CMD_PANEL:
            dprintf(INFO, "Config MIPI_CMD_PANEL.
    ");
            //...
    
            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;
    }
    
    配置控制器
    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;
        
        // 如果有两个MIPI DSI接口MIPI_DSI0和MIPI_DSI1就调用两次mdss_dsi_phy_init(),msm8909只有MIPI_DSI0,MSM8994等有两个DSI接口。
        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);
        
        //初始化DSI接口的host控制器。
        ret = mdss_dsi_host_init(&mipi_pinfo, pinfo->mipi.dual_dsi,
                            pinfo->mipi.broadcast);
        if (ret) {
            dprintf(CRITICAL, "dsi host init error
    ");
            goto error;
        }
    
        mdss_dsi_phy_contention_detection(&mipi_pinfo, DSI0_PHY_BASE);
        // 看下面
        if (panel->pre_init_func) {
            ret = panel->pre_init_func();
            if (ret) {
                dprintf(CRITICAL, "pre_init_func error
    ");
                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
    ");
                goto error;
            }
        }
    
        if (pinfo->rotate && panel->rotate)
            pinfo->rotate();
    #endif
    
    error:
        return ret;
    }
    

    调用if (panel->pre_init_func) {}函数:

    因为panelstruct.paneldata->panel_lp11_initinit_panel_data()函数赋值为1,

    所以调用mdss_dsi_panel_reset()根据reset时序来复位panel。

    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
    ");
                return ret;
            }
        }
    
        if(panelstruct.paneldata->panel_init_delay)
            udelay(panelstruct.paneldata->panel_init_delay);
    
        dprintf(SPEW,"Panel pre init done
    ");
        return ret;
    }
    

    display_image_on_screen调用fetch_image_from_partition()从splash分区获取lk logo图片,如果splash分区没有满足要求的数据,就显示默认的logo。

    display_image_on_screen
        fetch_image_from_partition
    

    初始化panel

    主要部分见注释:

    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.
    ");
            ret = mdp_dsi_video_on(pinfo); // 使能DSI VIDEO
        
            ret = mdss_dsi_post_on(panel); // 使用初始化命令来初始化panel,包括识别屏幕(兼容有关)
    
            ret = mipi_dsi_on();
            break;
        case MIPI_CMD_PANEL:
            dprintf(INFO, "Turn on MIPI_CMD_PANEL.
    ");
            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 backlight */
    if (pdata->bl_func)
        ret = pdata->bl_func(1);
    

    gcdb_display_init()函数中有一个函数指针mdss_dsi_bl_enable

    这个函数是用来调整背光的和使能背光的,通过PWM来作用:(具体要看原理图)

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

    所以bootloader的背光是通过target_backlight_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;
    }
    

    如何增加一个panel

    以 video类型的 xxx 为例。

    声明panel枚举

    oem_panel.c中添加一个panel枚举

    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, // 举个例子
        UNKNOWN_PANEL
    };
    

    绑定panel枚举与具体的panel属性

    1、增加相应的头文件#include "include/xxx_video.h"

    头文件可以通过GCDB工具配置生成、懒得做也可以找供应商要。

    头文件放在:bootable/bootloader/lk/dev/gcdb/display/include

    2、在supp_panels中添加绑定

    3、照猫画虎添加对应的属性。对应的属性来自xx_panel.h(具体由GCDB生成)

    #include "include/xxx_video.h"
    
    // 如果要增加一个panel就需要在这里增加一个supp_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}, // 新增绑定
    };
    
    
    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;
        // 添加 属性
        case XXX_VIDEO_PANEL:
            // 照猫画虎添加对应的属性。
        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;
    }
    

    为平台指定Panel枚举

    在oem_panel_select()函数中指定对应平台的panel_id = XXX_VIDEO_PANEL;

    这个函数需要做的工作是:主要是识别不同IC,赋值给参数panel_id,panel_id的使用在同一文件中的 init_panel_data()函数中。

    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
    ",
                hw_id);
            return PANEL_TYPE_UNKNOWN;
        }
        //...
    }
    

    附录:LCD参数说明

    VBPD(verticalback porch):表示在一帧图像开始时,垂直同步信号以后的无效的行数。

    VFPD(verticalfront porch):表示在一帧图像结束后,垂直同步信号以前的无效的行数。

    VSPW(verticalsync pulse width):表示垂直同步脉冲的宽度,用行数计算。

    HBPD(horizontal back porch):表示从水平同步信号开始到一行的有效数据开始之间的VCL的个数。

    HFPD(horizontal front porch):表示一行的有效数据结束到下一个水平同步信号开始 之间的VCLK的个数。

    HSPW(horizontalsync pulse width):表示水平同步信号的宽度,用VCLK计算

    附录:根据target选择不同的target_display

    不同的平台有相同的target_display.c,例如:

     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
    

    实际上根据编译系统,很容易找到对应的文件是怎么编译的。

    • 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
    

    最终由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
    
    如果说我的文章对你有用,只不过是我站在巨人的肩膀上再继续努力罢了。
    若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
    博客地址:https://www.cnblogs.com/schips/
  • 相关阅读:
    1-1 10:超级玛丽游戏
    1-1 09:字符菱形
    【Lucene4.8教程之四】分析
    【Lucene4.8教程之六】QueryParser与Query子类:如何生成Query对象
    【Lucene4.8教程之三】搜索
    Java路径问题最终解决方案—可定位所有资源的相对路径寻址
    java.util.logging.Logger基础教程
    【Lucene4.8教程之二】索引
    【Lucene4.8教程之一】使用Lucene4.8进行索引及搜索的基本操作
    重要学习参考资料
  • 原文地址:https://www.cnblogs.com/schips/p/qualcomm_stage_of_little_kernel_display_function_analysis_and_how_to_add_a_new_panel.html
Copyright © 2011-2022 走看看