高通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
;
同样的道理先从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\n", panel_name);
return;
}
dprintf(INFO, "panel_name is %s\n", 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!\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;
} 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!\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;
}
根据屏幕的类型,增加panel;如链接所示,可以看到,步骤如下:
2. 在gcdb_display_init()函数中有一个函数oem_panel_select()函数:
(这个函数需要做的工作是:主要是识别不同IC,赋值给参数panel_id
,panel_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\n",
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\n",
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!\n");
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\n");
return ret;
}
/* Panel Reset */
ret = target_panel_reset(enable, panelstruct.panelresetseq,
&panel.panel_info);
if (ret) {
dprintf(CRITICAL, "panel reset failed\n");
return ret;
}
dprintf(INFO, "Panel power on done\n");
} else {
/* Disable panel and ldo */
ret = target_panel_reset(enable, panelstruct.panelresetseq,
&panel.panel_info);
if (ret) {
dprintf(CRITICAL, "panel reset disable failed\n");
return ret;
}
ret = target_ldo_ctrl(enable, pinfo);
if (ret) {
dprintf(CRITICAL, "ldo control disable failed\n");
return ret;
}
dprintf(INFO, "Panel power off done\n");
}
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\n", 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\n");
if (!config) {
dprintf(CRITICAL,"NULL configuration, image cannot be displayed\n");
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.\n");
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.\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);
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.\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);
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.\n");
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.\n");
ret = mdss_spi_panel_init(pinfo);
if (ret)
goto msm_display_config_out;
break;
case HDMI_PANEL:
dprintf(INFO, "Config HDMI PANEL.\n");
ret = mdss_hdmi_config(pinfo, &(panel->fb));
if (ret)
goto msm_display_config_out;
break;
case EDP_PANEL:
dprintf(INFO, "Config EDP PANEL.\n");
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\n");
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\n",qup_id);
ASSERT(0);
};
} else {
dprintf(CRITICAL, "Incorrect BLSP id %d\n",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\n", 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\n", __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\n", __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.\n");
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.\n");
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.\n");
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.\n");
ret = mdp_lcdc_on(panel);
if (ret)
goto msm_display_on_out;
break;
case HDMI_PANEL:
dprintf(INFO, "Turn on HDMI PANEL.\n");
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.\n");
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.\n");
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\n");
return ret;
}
/* Panel Reset */
ret = target_panel_reset(enable, panelstruct.panelresetseq,
&panel.panel_info);
if (ret) {
dprintf(CRITICAL, "panel reset failed\n");
return ret;
}
dprintf(INFO, "Panel power on done\n");
} else {
/* Disable panel and ldo */
ret = target_panel_reset(enable, panelstruct.panelresetseq,
&panel.panel_info);
if (ret) {
dprintf(CRITICAL, "panel reset disable failed\n");
return ret;
}
ret = target_ldo_ctrl(enable, pinfo);
if (ret) {
dprintf(CRITICAL, "ldo control disable failed\n");
return ret;
}
dprintf(INFO, "Panel power off done\n");
}
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地址
高通spi 屏幕 -lk代码分析的更多相关文章
- 高通平台msm8909 LK 实现LCD 兼容
前段时间小米出现红米note2 换屏门,现在我们公司也要上演了:有两个供应商提供不同IC 的LCD panel. 软件区分的办法是读取LCD IC 的ID 寄存器,下面解析高通平台LK中LCD兼容的过 ...
- 高通Quick Charge高速充电原理分析
1 QC 2.0 1.1 高通Quick Charge 2.0 高速充电原理分析 高通的QC2.0高速充电须要手机端和充电器都要支持才行. 当将充电器端通过数据线连到手机上时,充电器默认的是将D+和D ...
- 高通 android平台LCD驱动分析
目前手机芯片厂家提供的源码里包含整个LCD驱动框架,一般厂家会定义一个xxx_fb.c的源文件,注册一个平台设备和平台驱动,在驱动的probe函数中来调用register_framebuffer(), ...
- 高通Android display架构分析
目录(?)[-] Kernel Space Display架构介绍 函数和数据结构介绍 函数和数据结构介绍 函数和数据结构介绍 数据流分析 初始化过程分析 User Space display接口 K ...
- 高通调试 SPI 屏的 bug
1. spi调试问题: 问题描述: spi屏幕lk启动的时候正常出现小企鹅,到kernel启动的过程黑屏并且花屏才到开机动画: 2. 黑屏的三个阶段: 参照:黑屏分析 分析开机过程黑屏,首先需要定位黑 ...
- 高通Android display分析【转】
本文转载自:http://blog.csdn.net/zhangchiytu/article/details/6777039 高通7系列硬件架构分析 如上图,高通7系列 Display的硬件部分主要由 ...
- 高通安卓调试LCD几方面总结
来公司上班现在已经整整一个月了,蔽人不才,能力有限,学习进度缓慢,不过也是有一点点的收获与心得,在这里写出来与大家分享,养成良好的记录习惯也免得后忘记. 不啰嗦了,开入正题.来公司一个月左右的时间,主 ...
- 【转】高通平台android 环境配置编译及开发经验总结
原文网址:http://blog.csdn.net/dongwuming/article/details/12784535 1.高通平台android开发总结 1.1 搭建高通平台环境开发环境 在高通 ...
- 高通 MSM8K bootloader : SBL1 .
一. MSM8K Boot Flow 图1: 高通MSM8K平台bootloader启动流程基本类似,但具体各平台,比如MSM8974.MSM8916.MSM8994等,会有微小区别. 从上图,可以看 ...
随机推荐
- python函数参数是值传递还是引用传递(以及变量间复制后是否保持一致):取决于对象内容可变不可变
函数参数传递本质上和变量整体复制一样,只是两个变量分别为形参a和实参b.那么,a=b后,a变了,b值是否跟着变呢?这取决于对象内容可变不可变 首先解释一下,什么是python对象的内容可变不可变? p ...
- 【matlab编程】matlab随机数函数
Matlab内部函数 a. 基本随机数 Matlab中有两个最基本生成随机数的函数. 1.rand() 生成(0,1)区间上均匀分布的随机变量.基本语法: rand([M,N,P ...]) 生成排列 ...
- 【翻译】在Ext JS应用程序中使用自定义图标
原文:Using Custom Icons in Your Ext JS App 作者:Lee BoonstraLee is a technical trainer at Sencha. She's ...
- 客户信息全SQL
SELECT hp.party_name "客户名称", --客户名称 hca.account_number "客户编号", --客户编号 hca.cust_a ...
- 【Java编程】随机数的不重复选择
随机数的不重复选择就是从n个数中随机选取m(m<n)个数.在本文中,我们用Java来实现.因此我们先介绍Java的相关知识. 在Java中,Java.util.Set接口和Java.util.L ...
- windows linux—unix 跨平台通信集成控制系统
首先,我们可以用到这个开源的开发包: mdk(Micro-Development-Kit)微量级软件开发包,提供几个常用类,主要实现了一个高性能的并发服务器引擎 使用c++开发,是一个跨平台的开发包, ...
- eclipse中Debug简单记忆
最左边:代码一步一步的走,进入函数也是一步一步的走: 最中间:在断点开始一步一步的走,遇到函数不会进入函数,而是直接跳过函数(但是把函数中的代码整体走完的): 最右边:断点开始一部迅速返回上一级函数调 ...
- Linux 系统应用编程——线程基础
传统多任务操作系统中一个可以独立调度的任务(或称之为顺序执行流)是一个进程.每个程序加载到内存后只可以唯一地对应创建一个顺序执行流,即传统意义的进程.每个进程的全部系统资源是私有的,如虚拟地址空间,文 ...
- centos 7下安装python 3.6笔记
每次在centos上安装python 3都需要重新查资料,这次索性自己记下笔记. 首先安装gcc yum -y install gccyum install zlib-devel./configure ...
- python lock, semaphore, event实现线程同步
lock 机制不管你是java, C#, 还是python都是常用的线程同步机制, 相比较C# 的锁机制, python的加锁显得比较简单, 直接调用threading 标准库的lock 就可以了. ...