• 高通spi 屏幕 -lk代码分析


    lk SPI驱动

    1. 初始化时钟

    在lk中,我们是从kmain开始执行下来的,而执行顺序则是先初始化时钟,也就是在platform_early_init函数中开始执行的:
    在这里我们需要修改这个函数中的platform_clock_init();,我们来这里看这个函数,平台为msm8909:

    void platform_clock_init(void)
    {
        clk_init(msm_clocks_msm8909, ARRAY_SIZE(msm_clocks_msm8909));
    }
    

    msm_clocks_msm8909这个数组增加可以参考链接来增加,之后会提供patch来显示,相关寄存器文档参考80_NU767_1_H_Linux BAM Low-Speed Peripherals Configuration and Debug Guide.pdf

    高通msm8916 LK阶段配置使用i2c5

    同样的道理先从aboot_init分析起,进入target_display_init函数中来:

    target_display_init(device.display_panel); 这里进行屏幕的初始化。

    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;
        }
    
        dprintf(INFO, "panel_name is %s
    ", panel_name);
    
        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());
            }
    

    gcdb_display_init调用pan_type = oem_panel_select(panel_name, &panelstruct, &(panel.panel_info), &dsi_video_mode_phy_db),选择具体的屏幕;

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

    根据屏幕的类型,增加panel;如链接所示,可以看到,步骤如下:

    2. 在gcdb_display_init()函数中有一个函数oem_panel_select()函数:

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

    pan_type = oem_panel_select(panel_name, &panelstruct, &(panel.panel_info), &dsi_video_mode_phy_db);
    

    3. 在oem_panel_select()函数中需要根据你的hw_id来确定使用哪一款的LCD:

    panel_override_id = panel_name_to_id(supp_panels, ARRAY_SIZE(supp_panels), panel_name);
    

    supp_panels是struct panel_list,如果要增加一个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},
    {"a914_nhd_video", A914_NHD_VIDEO_PANEL}, //这是我们新增的
    };
    

    4. 在这个枚举中也需要增加相应的panel:

    /*---------------------------------------------------------------------------*/
    enum {
    TRULY_1080P_VIDEO_PANEL,
    TRULY_1080P_CMD_PANEL,
    R69006_1080P_VIDEO_PANEL,
    R69006_1080P_CMD_PANEL,
    TRULY_WUXGA_VIDEO_PANEL,
    NT35523_720P_VIDEO_PANEL,
    A914_NHD_VIDEO_PANEL, //这是我们新增的
    UNKNOWN_PANEL
    };
    

    继续向下看:

    if (panel_name) {
        panel_override_id = panel_name_to_id(supp_panels,  ARRAY_SIZE(supp_panels), panel_name);
    
        if (panel_override_id < 0) {
            dprintf(CRITICAL, "Not able to search the panel:%s
    ",
            panel_name + strspn(panel_name, " "));
        } else if (panel_override_id < UNKNOWN_PANEL) {
            /* panel override using fastboot oem command */
            panel_id = panel_override_id;
    
            dprintf(INFO, "OEM panel override:%s
    ",
            panel_name + strspn(panel_name, " "));
            goto panel_init;
        }
    }
    ……
    panel_init:
    /*
    * Update all data structures after 'panel_init' label. Only panel
    * selection is supposed to happen before that.
    */
    pinfo->pipe_type = MDSS_MDP_PIPE_TYPE_RGB;
    return init_panel_data(panelstruct, pinfo, phy_db);
    

    确保能直接跳到panel_init函数中来;

    5. 来到init_panel_data()函数中来:

    在这里也需要增加一个panel:(当然了,要增加相应的头文件)
    #include "include/panel_a914_nhd_video.h",在target/msm8909/oem_panel.c中增加在这个头文件;
    (LCM供应商给的上电顺序,一般来说都要自己根据上电初始化代码来参照)

    case TRULY_WUXGA_VIDEO_PANEL:
    	panelstruct->paneldata = &truly_wuxga_video_panel_data;
    	panelstruct->paneldata->panel_with_enable_gpio = 1;
    	panelstruct->panelres = &truly_wuxga_video_panel_res;
    	panelstruct->color = &truly_wuxga_video_color;
    	panelstruct->videopanel = &truly_wuxga_video_video_panel;
    	panelstruct->commandpanel = &truly_wuxga_video_command_panel;
    	panelstruct->state = &truly_wuxga_video_state;
    	panelstruct->laneconfig = &truly_wuxga_video_lane_config;
    	panelstruct->paneltiminginfo = &truly_wuxga_video_timing_info;
    	panelstruct->panelresetseq = &truly_wuxga_video_panel_reset_seq;
    	panelstruct->backlightinfo = &truly_wuxga_video_backlight;
    	pinfo->mipi.panel_on_cmds= truly_wuxga_video_on_command;
    	pinfo->mipi.num_of_panel_on_cmds= TRULY_WUXGA_VIDEO_ON_COMMAND;
    	pinfo->mipi.panel_off_cmds= truly_wuxga_video_off_command;
    	pinfo->mipi.num_of_panel_off_cmds= TRULY_WUXGA_VIDEO_OFF_COMMAND;
    	memcpy(phy_db->timing, truly_wuxga_14nm_video_timings, MAX_TIMING_CONFIG * sizeof(uint32_t));
    	pinfo->dfps.panel_dfps = truly_wuxga_video_dfps;
    	pinfo->mipi.signature = TRULY_WUXGA_VIDEO_SIGNATURE;
    	break;
    /*下面这段代码是我们增加的*/
    case A914_NHD_VIDEO_PANEL:
    	panelstruct->paneldata = &a914_nhd_video_panel_data;
    	panelstruct->panelres = &a914_nhd_video_panel_res;
    	panelstruct->color = &a914_nhd_video_color;
    	panelstruct->videopanel = &a914_nhd_video_video_panel;
    	panelstruct->commandpanel = &a914_nhd_video_command_panel;
    	panelstruct->state = &a914_nhd_video_state;
    	panelstruct->laneconfig = &a914_nhd_video_lane_config;
    	panelstruct->paneltiminginfo = &a914_nhd_video_timing_info;
    	panelstruct->panelresetseq = &a914_nhd_video_panel_reset_seq;
    	panelstruct->backlightinfo = &a914_nhd_video_backlight;
    	pinfo->mipi.panel_on_cmds = a914_nhd_video_on_command;
    	pinfo->mipi.num_of_panel_on_cmds = A914_NHD_VIDEO_ON_COMMAND;
    	pinfo->mipi.panel_off_cmds = a914_nhd_video_off_command;
    	pinfo->mipi.num_of_panel_off_cmds = A914_NHD_VIDEO_OFF_COMMAND;
    	memcpy(phy_db->timing,a914_nhd_video_timings, MAX_TIMING_CONFIG * sizeof(uint32_t));
    	pinfo->mipi.signature = A914_NHD_VIDEO_SIGNATURE;
    	break;
    

    我们上面分析了这个函数里面的内容,并为其增加了一个panel:

    pan_type = oem_panel_select(panel_name, &panelstruct, &(panel.panel_info), &dsi_video_mode_phy_db);
    

    6. 为pan_type也增加一个SPI类型:

    else if (pan_type == PANEL_TYPE_SPI ) {
    	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_RGB888;
    	panel.panel_info.type = SPI_PANEL;
    }
    else {
    	dprintf(CRITICAL, "Target panel init not found!
    ");
    	ret = ERR_NOT_SUPPORTED;
    	goto error_gcdb_display_init;
    }
    

    我们增加了mdss_spi_panel_power、mdss_spi_bl_enable函数如下所示:

    static int mdss_spi_panel_power(uint8_t enable,
    struct msm_panel_info *pinfo)
    {
    	int ret = NO_ERROR;
    
    	if (enable) {
    	ret = target_ldo_ctrl(enable, pinfo);
    	if (ret) {
    	dprintf(CRITICAL, "LDO control enable failed
    ");
    	return ret;
    	}
    	/* Panel Reset */
    		ret = target_panel_reset(enable, panelstruct.panelresetseq,
    		&panel.panel_info);
    		if (ret) {
    			dprintf(CRITICAL, "panel reset failed
    ");
    			return ret;
    		}
    		dprintf(INFO, "Panel power on done
    ");
    	} else {
    		/* Disable panel and ldo */
    		ret = target_panel_reset(enable, panelstruct.panelresetseq,
    		&panel.panel_info);
    		if (ret) {
    		dprintf(CRITICAL, "panel reset disable failed
    ");
    		return ret;
    	}
    
    	ret = target_ldo_ctrl(enable, pinfo);
    	if (ret) {
    		dprintf(CRITICAL, "ldo control disable failed
    ");
    		return ret;
    	}
    		dprintf(INFO, "Panel power off done
    ");
    	}
    
    	return ret;
    }
    

    打开显示面板,必须在mdss_dsi_panel_reset前有相应供电,而关闭则相反;

    static int mdss_spi_bl_enable(uint8_t enable)
    {
    	int ret = NO_ERROR;
    
    	mdelay(100);
    	ret = panel_backlight_ctrl(enable);
    	if (ret)
    	dprintf(CRITICAL, "Backlight %s failed
    ", enable ? "enable" : "disable");
    	return ret;
    }
    

    7. 来到msm_display_init函数中来,配置spi 的相应管脚:

    在上提到的函数中:

    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)); //执行turn on的函数
    
        if (ret)
            goto msm_display_init_out;
    
         /* 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));
    
        if (ret)
            goto msm_display_init_out;
    
        /* 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;
    	}	
    
        if (ret)
            goto msm_display_init_out;
    
        ret = msm_fb_alloc(&(panel->fb));
        if (ret)
            goto msm_display_init_out;
    
        ret = msm_display_config();
        if (ret)
            goto msm_display_init_out;
    
        fbcon_setup(&(panel->fb));
        display_image_on_screen();
        ret = msm_display_on();
        if (ret)
            goto msm_display_init_out;
    
        if (pdata->post_power_func)
            ret = pdata->post_power_func(1);
        if (ret)
            goto msm_display_init_out;
    
        /* Turn on backlight */
        if (pdata->bl_func)
            ret = pdata->bl_func(1);
    
        if (ret)
            goto msm_display_init_out;
    
        msm_display_init_out:
            return ret;
    }
    

    其中msm_fb_alloc函数是分配framebuffer空间;
    下面我们来分析display_image_on_screen();函数:

    void display_image_on_screen(void)
    {
    #if DISPLAY_TYPE_MIPI
        int fetch_image_from_partition();
    
        if (fetch_image_from_partition() < 0) {
            display_default_image_on_screen();
        } else {
        /* data has been put into the right place */
        fbcon_flush();
        }
    #else
        display_default_image_on_screen();
    #endif
    }
    

    由于我们的已经改为SPI方式传送,不是MIPI;所以会直接进入display_default_image_on_screen();函数;

    void display_default_image_on_screen(void)
    {
        unsigned i = 0;
        unsigned total_x;
        unsigned total_y;
        unsigned bytes_per_bpp;
        unsigned image_base;
    
        dprintf("CRITICAL", "linhao display_default_image_on_screen
    ");
    
        if (!config) {
        dprintf(CRITICAL,"NULL configuration, image cannot be displayed
    ");
        return;
        }
    	fbcon_clear(); // clear screen with Black color
    
    	total_x = config->width;
    	total_y = config->height;
    	bytes_per_bpp = ((config->bpp) / 8);
    	image_base = ((((total_y/2) - (SPLASH_IMAGE_HEIGHT / 2) - 1) *
    	(config->width)) + (total_x/2 - (SPLASH_IMAGE_WIDTH / 2)));
    
    	//24 bit bpp
    	if (bytes_per_bpp == 3) {
    	for (i = 0; i < SPLASH_IMAGE_HEIGHT; i++) {
    		memcpy (config->base + ((image_base + (i * (config->width))) * bytes_per_bpp),
    		imageBuffer_rgb888 + (i * SPLASH_IMAGE_WIDTH * bytes_per_bpp),
    		SPLASH_IMAGE_WIDTH * bytes_per_bpp);
    		}
    	}
    	fbcon_flush();
    #if DISPLAY_MIPI_PANEL_NOVATEK_BLUE
    	if(is_cmd_mode_enabled())
    	mipi_dsi_cmd_mode_trigger();
    #endif
    //16 bit bpp
    if (bytes_per_bpp == 2) {
    	for (i = 0; i < SPLASH_IMAGE_HEIGHT; i++) {
    		memcpy (config->base + ((image_base + (i * (config->width))) * bytes_per_bpp),
    		imageBuffer + (i * SPLASH_IMAGE_WIDTH * bytes_per_bpp),
    		SPLASH_IMAGE_WIDTH * bytes_per_bpp);
    		}
    	}
    	fbcon_flush();
    }
    
    
    

    在这里由于没有mipi,所以去掉了#if DISPLAY_TYPE_MIPI宏定义,然后根据24bit真彩色和16bit颜色深度进行相应处理;
    最后使用fbcon_flush刷新framebuffer缓冲区;
    这样就可以显示默认图片了~~;

    display_image_on_screen函数分析完毕之后,继续回到我们的函数msm_display_init函数当中来:
    下一步分析msm_display_config函数中来:

    int msm_display_config()
    {
    	int ret = NO_ERROR;
    	int mdp_rev;
    	struct msm_panel_info *pinfo;
    
    	if (!panel)
    		return ERR_INVALID_ARGS;
    
    	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));
    		if (ret)
    			goto msm_display_config_out;
    		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);
    
    		if (ret)
    			goto msm_display_config_out;
    
    		if (pinfo->early_config)
    			ret = pinfo->early_config((void *)pinfo);
    
    		ret = mdp_dsi_video_config(pinfo, &(panel->fb));
    		if (ret)
    			goto msm_display_config_out;
    		break;
    	case MIPI_CMD_PANEL:
    		dprintf(INFO, "Config MIPI_CMD_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);
    		if (ret)
    			goto msm_display_config_out;
    
    		ret = mdp_dsi_cmd_config(pinfo, &(panel->fb));
    		if (ret)
    			goto msm_display_config_out;
    		break;
    	case LCDC_PANEL:
    		dprintf(INFO, "Config LCDC PANEL.
    ");
    		ret = mdp_lcdc_config(pinfo, &(panel->fb));
    	if (ret)
    		goto msm_display_config_out;
    	break;
    	//added by linhao
    	case SPI_PANEL:
    		dprintf(INFO, "Config SPI PANEL.
    ");
    		ret = mdss_spi_panel_init(pinfo);
    	if (ret)
    		goto msm_display_config_out;
    	break;
    	case HDMI_PANEL:
    		dprintf(INFO, "Config HDMI PANEL.
    ");
    		ret = mdss_hdmi_config(pinfo, &(panel->fb));
    	if (ret)
    		goto msm_display_config_out;
    	break;
    	case EDP_PANEL:
    		dprintf(INFO, "Config EDP PANEL.
    ");
    		ret = mdp_edp_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;
    }
    

    在SPI_PANEL中进入了mdss_spi_panel_init函数中来:

    int mdss_spi_panel_init(struct msm_panel_info *pinfo)
    {
    	int cmd_count = 0;
    	int ret = 0;
    
    	if(!dev) {
    		//传入参数为SPI_BLSP_ID_1,SPI_QUP_ID_5
    		dev = qup_blsp_spi_init(SPI_BLSP_ID_1, SPI_QUP_ID_5);
    		if (!dev) {
    		dprintf(CRITICAL, "Failed initializing SPI
    ");
    		return -ENODEV;
    		}
    	}
    
    	gpio_tlmm_config(dc_gpio.pin_id, 0,
    	dc_gpio.pin_direction, dc_gpio.pin_pull,
    	dc_gpio.pin_strength, dc_gpio.pin_state);
    
    
    	while (cmd_count < pinfo->spi.num_of_panel_cmds) {
    
    		mdss_spi_write_cmd(pinfo->spi.panel_cmds[cmd_count].payload);
    
    		if (pinfo->spi.panel_cmds[cmd_count].size > 1)
    		mdss_spi_write_data(pinfo->spi.panel_cmds[cmd_count].payload + 1,
    		pinfo->spi.panel_cmds[cmd_count].size - 1);
    
    		if (pinfo->spi.panel_cmds[cmd_count].wait)
    		mdelay(pinfo->spi.panel_cmds[cmd_count].wait);
    
    		cmd_count ++;
    	}
    
    	return 0;
    }
    

    这个函数在lk/platform/msm_shared/mdss_spi.c中,如有需要,则添加这个文件即可;(Android7.0中没有这个文件,之后需要的话,使用patch来补充)
    首先增加qup_blsp_spi_init函数,这个函数的作用是配置高通的blsp,高通msm8909的有12个blsp,每一个BLSP含有两个QUP, 每一个QUP可以被配置为I2C, SPI, UART, UIM接口, BLSP是高通对于低速接口的一种管理方式。参考文档为80_NU767_1_H_Linux BAM Low-Speed Peripherals Configuration and Debug Guide.pdf,该文档适用类型为MSM8996, MSM8994,
    MSM8992, MSM8952, MSM8916, MSM8936/ MSM8939, MSM8909, MDM9x35, and
    MDM9x40/MDM9x45 chipsets.:

    struct qup_spi_dev *qup_blsp_spi_init(uint8_t blsp_id, uint8_t qup_id)
    {
    struct qup_spi_dev *dev;
    
    dev = malloc(sizeof(struct qup_spi_dev));
    if (!dev) {
    return NULL;
    }
    dev = memset(dev, 0, sizeof(struct qup_spi_dev));
    
    /* Platform uses BLSP */
    dev->qup_irq = BLSP_QUP_IRQ(blsp_id, qup_id);
    dev->qup_base = BLSP_QUP_BASE(blsp_id, qup_id);
    
    /* Initialize the GPIO for BLSP spi */
    gpio_config_blsp_spi(blsp_id, qup_id);
    
    clock_config_blsp_spi(blsp_id, qup_id);
    
    qup_spi_sec_init(dev);
    
    return dev;
    }
    

    对着文档中的表来看:

    因为我们选择的是BLSP1(一般为BLSP1),所以QUP_BASE_ADDRESS 为0x78B5000;我们选择的是qup5,所以根据下面公式来计算:
    QUP_BASE_ADDRESS的计算公式为:

    #define PERIPH_SS_BASE              0x07800000
    #define BLSP_QUP_BASE(blsp_id, qup_id) (PERIPH_SS_BASE + 0xB5000 + 0x1000 * qup_id)
    

    根据硬件,进入QUP_ID_5,配置spi的管脚:

    void gpio_config_blsp_spi(uint8_t blsp_id, uint8_t qup_id)
    {
    	if(blsp_id == BLSP_ID_1) {
    		switch (qup_id) {
    
    			case QUP_ID_4:
    				/* configure SPI MOSI gpio */
    				gpio_tlmm_config(16, 1, GPIO_OUTPUT, GPIO_NO_PULL,
    					GPIO_16MA, GPIO_DISABLE);
    
    					/* configure SPI MISO gpio */
    				gpio_tlmm_config(17, 1, GPIO_OUTPUT, GPIO_NO_PULL,
    					GPIO_16MA, GPIO_DISABLE);
    
    				/* configure SPI CS_N gpio */
    				gpio_tlmm_config(18, 1, GPIO_OUTPUT, GPIO_NO_PULL,
    					GPIO_16MA, GPIO_DISABLE);
    
    				/* configure SPI CLK gpio */
    				gpio_tlmm_config(19, 1, GPIO_OUTPUT, GPIO_NO_PULL,
    					GPIO_16MA, GPIO_DISABLE);
    				break;
    			case QUP_ID_0:
    				break;
    			case QUP_ID_1:
    				break;
    			case QUP_ID_2:
    				break;
    			case QUP_ID_3:
    				break;
    			case QUP_ID_5:
    				/* configure SPI MOSI gpio */
    				gpio_tlmm_config(8, 1, GPIO_OUTPUT, GPIO_NO_PULL,
    					GPIO_16MA, GPIO_DISABLE);
    
    					/* configure SPI MISO gpio */
    				gpio_tlmm_config(9, 1, GPIO_OUTPUT, GPIO_NO_PULL,
    					GPIO_16MA, GPIO_DISABLE);
    
    				/* configure SPI CS_N gpio */
    				gpio_tlmm_config(10, 1, GPIO_OUTPUT, GPIO_NO_PULL,
    					GPIO_16MA, GPIO_DISABLE);
    
    				/* configure SPI CLK gpio */
    				gpio_tlmm_config(11, 1, GPIO_OUTPUT, GPIO_NO_PULL,
    					GPIO_16MA, GPIO_DISABLE);
    				break;
    			default:
    				dprintf(CRITICAL, "Incorrect QUP id %d
    ",qup_id);
    				ASSERT(0);
    		};
    	} else {
    		dprintf(CRITICAL, "Incorrect BLSP id %d
    ",blsp_id);
    		ASSERT(0);
    	}
    }
    

    继续向下看,配置spi时钟:

    /* Configure spi clock */
    void clock_config_blsp_spi(uint8_t blsp_id, uint8_t qup_id)
    {
    	uint8_t ret = 0;
    	char clk_name[64];
    
    	struct clk *qup_clk;
    
    	if((blsp_id != BLSP_ID_1)) {
    		dprintf(CRITICAL, "Incorrect BLSP-%d configuration
    ", blsp_id);
    		ASSERT(0);
    	}
    
    	snprintf(clk_name, sizeof(clk_name), "blsp1_ahb_iface_clk");
    
    	ret = clk_get_set_enable(clk_name, 0 , 1);
    
    	if (ret) {
    		dprintf(CRITICAL, "%s: Failed to enable %s clock
    ", __func__, clk_name);
    		return;
    	}
    
    	snprintf(clk_name, sizeof(clk_name), "gcc_blsp1_qup%u_spi_apps_clk", qup_id + 1);
    
    	/* Set the highest clk frequency by default for good performance. */
    	ret = clk_get_set_enable(clk_name, 50000000, 1);
    
    	if (ret) {
    		dprintf(CRITICAL, "%s: Failed to enable %s
    ", __func__, clk_name);
    		return;
    	}
    }
    

    接下来看qup_spi_sec_init(dev);
    这里都是寄存器配置的东西,直接看patch就行了;
    至此:msm_display_config()函数分析完毕;
    我们接下来分析msm_display_on函数:

    int msm_display_on()
    {
    	int ret = NO_ERROR;
    	int mdp_rev;
    	struct msm_panel_info *pinfo;
    
    	if (!panel)
    		return ERR_INVALID_ARGS;
    
    	bs_set_timestamp(BS_SPLASH_SCREEN_DISPLAY);
    
    	pinfo = &(panel->panel_info);
    
    	if (pinfo->pre_on) {
    		ret = pinfo->pre_on();
    		if (ret)
    			goto msm_display_on_out;
    	}
    
    	switch (pinfo->type) {
    	case LVDS_PANEL:
    		dprintf(INFO, "Turn on LVDS PANEL.
    ");
    		ret = mdp_lcdc_on(panel);
    		if (ret)
    			goto msm_display_on_out;
    		ret = lvds_on(panel);
    		if (ret)
    			goto msm_display_on_out;
    		break;
    	case MIPI_VIDEO_PANEL:
    		dprintf(INFO, "Turn on MIPI_VIDEO_PANEL.
    ");
    		ret = mdp_dsi_video_on(pinfo);
    		if (ret)
    			goto msm_display_on_out;
    
    		ret = mdss_dsi_post_on(panel);
    		if (ret)
    			goto msm_display_on_out;
    
    		ret = mipi_dsi_on();
    		if (ret)
    			goto msm_display_on_out;
    		break;
    	case MIPI_CMD_PANEL:
    		dprintf(INFO, "Turn on MIPI_CMD_PANEL.
    ");
    		ret = mdp_dma_on(pinfo);
    		if (ret)
    			goto msm_display_on_out;
    		mdp_rev = mdp_get_revision();
    		if (mdp_rev != MDP_REV_50 && mdp_rev != MDP_REV_304 &&
    						mdp_rev != MDP_REV_305) {
    			ret = mipi_cmd_trigger();
    			if (ret)
    				goto msm_display_on_out;
    		}
    
    		ret = mdss_dsi_post_on(panel);
    		if (ret)
    			goto msm_display_on_out;
    
    		break;
    	case LCDC_PANEL:
    		dprintf(INFO, "Turn on LCDC PANEL.
    ");
    		ret = mdp_lcdc_on(panel);
    		if (ret)
    			goto msm_display_on_out;
    		break;
    	case HDMI_PANEL:
    		dprintf(INFO, "Turn on HDMI PANEL.
    ");
    		ret = mdss_hdmi_init();
    		if (ret)
    			goto msm_display_on_out;
    
    		ret = mdss_hdmi_on();
    		if (ret)
    			goto msm_display_on_out;
    		break;
    	case EDP_PANEL:
    		dprintf(INFO, "Turn on EDP PANEL.
    ");
    		ret = mdp_edp_on(pinfo);
    		if (ret)
    			goto msm_display_on_out;
    		break;
    	//added by linhao,support spi
    	case SPI_PANEL:
    		dprintf(INFO, "Turn on SPI PANEL.
    ");
    		ret = mdss_spi_on(pinfo, &(panel->fb));
    		if (ret)
    			goto msm_display_on_out;
    		break;
    	default:
    		return ERR_INVALID_ARGS;
    	};
    
    	if (pinfo->on)
    		ret = pinfo->on();
    
    msm_display_on_out:
    	return ret;
    }
    

    接下来就继续执行这两个函数了:

    if (pdata->post_power_func)
    	ret = pdata->post_power_func(1);
    if (ret)
    	goto msm_display_init_out;
    
    /* Turn on backlight */
    if (pdata->bl_func)
    	ret = pdata->bl_func(1);
    

    看它们的函数指针内容:
    第一个函数mdss_spi_panel_power,这个函数实现了:

    static int mdss_spi_panel_power(uint8_t enable,
    				struct msm_panel_info *pinfo)
    {
    	int ret = NO_ERROR;
    
    	if (enable) {
    		ret = target_ldo_ctrl(enable, pinfo);
    		if (ret) {
    			dprintf(CRITICAL, "LDO control enable failed
    ");
    			return ret;
    		}
    
    		/* Panel Reset */
    		ret = target_panel_reset(enable, panelstruct.panelresetseq,
    						&panel.panel_info);
    		if (ret) {
    			dprintf(CRITICAL, "panel reset failed
    ");
    			return ret;
    		}
    		dprintf(INFO, "Panel power on done
    ");
    	} else {
    		/* Disable panel and ldo */
    		ret = target_panel_reset(enable, panelstruct.panelresetseq,
    						&panel.panel_info);
    		if (ret) {
    			dprintf(CRITICAL, "panel reset disable failed
    ");
    			return ret;
    		}
    
    		ret = target_ldo_ctrl(enable, pinfo);
    		if (ret) {
    			dprintf(CRITICAL, "ldo control disable failed
    ");
    			return ret;
    		}
    		dprintf(INFO, "Panel power off done
    ");
    	}
    
    	return ret;
    }
    

    看其中的target_ldo_ctrl函数,这个函数是控制电源的:

    int target_ldo_ctrl(uint8_t enable, struct msm_panel_info *pinfo)
    {
    	if (enable){
    		if (pinfo->type == SPI_PANEL)
    			spi_panel_regulator_enable();	 /* L6, and L17 */		
    		else			
    			regulator_enable();     /* L2, L6, and L17 */	
    	}	
    	return NO_ERROR;
    }
    

    由于pinfo->type == SPI_PANEL进入了spi_panel_regulator_enable函数中来,根据电路图,需要使能L6、L17:

    void spi_panel_regulator_enable()
    {	
    	rpm_send_data(&ldo17[GENERIC_ENABLE][0], 36, RPM_REQUEST_TYPE);	
    	rpm_send_data(&ldo6[GENERIC_ENABLE][0], 36, RPM_REQUEST_TYPE);
    }
    

    然后使用target_panel_reset函数对面板进行重置;
    至此电源已经完成;
    背光函数mdss_spi_bl_enable->panel_backlight_ctrl->target_backlight_ctrl函数中:
    通过pwm或者WLED方式控制背光,不支持BL_DCS:

    if (bl->bl_interface_type == BL_DCS)
    		return 0;
    

    终于,msm_display_init函数已经分析完毕,随之gcdb_display_init也分析完毕;target_display_init也分析完了;

    8. patch地址

    patch地址

  • 相关阅读:
    3.14周末作业
    3.13作业
    文件处理
    字符编码
    基本数据类型总结
    基本数据类型--------------------集合set()
    python入门009
    作业009
    python入门008
    作业008
  • 原文地址:https://www.cnblogs.com/linhaostudy/p/9032053.html
Copyright © 2020-2023  润新知