高通spi 屏幕 -lk代碼分析

来源:https://www.cnblogs.com/linhaostudy/archive/2018/05/14/9032053.html
-Advertisement-
Play Games

lk SPI驅動 1. 初始化時鐘 在lk中,我們是從kmain開始執行下來的,而執行順序則是先初始化時鐘,也就是在 函數中開始執行的: 在這裡我們需要修改這個函數中的 ,我們來這裡看這個函數,平臺為msm8909: 這個數組增加可以參考鏈接來增加,之後會提供patch來顯示,相關寄存器文檔參考 ; ...


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\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_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\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地址


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • system.webServer節點下加上 <validation validateIntegratedModeConfiguration="false" /> ...
  • 這是由於當前用戶沒有許可權運行/usr/bin/dumpcap造成的。/usr/bin/dumpcap是Wireshark的包捕獲引擎。先用ls命令看一下dumpcap的許可權情況:xy@debian-vm-1:~$ ls -lah /usr/bin/dumpcap-rwxr-xr-- 1 root w ...
  • Centos 系統安裝 Centos 系統部署Nginx ...
  • "高通display驅動" 0. 關鍵字 MDSS : 高通平臺lcd multimedia Display sub system DSI: Display Serial Interface 1. 涉及文件 1. drivers\video\fbmem.c(核心層) 2. drivers\video ...
  • 環境RHEL7.4 1.搭建伺服器端主機環境 網路配置 網卡eth0 10.0.0.1 網卡eth1 10.1.0.1 網卡eth2 10.2.0.1 網卡eth3 10.3.0.1 硬碟配置 添加一塊100GB硬碟,配置成DATAVG,劃分LV併發布成iscsi硬碟 iscsi target服務配 ...
  • 本文記錄一下,在docker中安裝Mongodb的過程。 環境:物理機 ubuntu18.04 因為環境變了,我在一臺物理機上直接安裝了ubuntu(非虛擬機和雙系統),所以這次又參照在docker中初次體驗.net core 2.0 安裝了一遍docker,但是進行的不是很順利。執行命令:sudo ...
  • 1.關於Ubuntu 安裝完成後界面展示 Ubuntu 是一個南非的民族觀念,著眼於人們之間的忠誠和聯繫。該詞來自於祖魯語和科薩語。Ubuntu(發音"oo-BOON-too"--“烏班圖”) 被視為非洲人的傳統理念,也是建立新南非共和國的基本原則之一,與非洲復興的理想密切相關。 Ubuntu 精神 ...
  • 原因在於 windows 下的換行符是 \r\n,而 linux 下的換行符是 \n 解決方案: 首先運行如下命令安裝 dos2unix: 接下來運行如下命令進行轉換工作: 完成轉換後便可正常執行腳本。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...