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 https://blog.csdn.net/eliot_shao/article/details/53351759
同样的道理先从 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;
- }
- 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 函数中来:
在上提到的函数中:
- 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 地址
patch 地址 https://github.com/lh233/linux-kernel
来源: https://www.cnblogs.com/linhaostudy/p/9032053.html