void display_init(void) { dprintf(ALWAYS, "%s: lcd_type = %d\n", __func__, lcd_type); if (display_enable) return; mipi_global_video_init(&(panel.panel_info), lcd_type); // this function is implemented by specific lcd driver panel.clk_func = msm8960_mipi_panel_clock; panel.power_func = msm8960_mipi_panel_power; panel.fb.base = 0x89000000; 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 = FB_FORMAT_RGB888; panel.mdp_rev = MDP_REV_42; if (msm_display_init(&panel, display_enable)) { dprintf(CRITICAL, "Display init failed!\n"); return; } display_enable = 1; }
Above code is the first initialization of lcd. The file is at lk/target/msm8960/target_display.c. So how to open power and clock, as follow:
void msm8960_mipi_panel_reset(void) { gpio_tlmm_config(LCD_GPIO_RST, 0, GPIO_OUTPUT, GPIO_NO_PULL,GPIO_8MA, 1); gpio_tlmm_config(LCD_GPIO_IM, 0, GPIO_OUTPUT, GPIO_NO_PULL,GPIO_8MA, 1); /*select video mode*/ gpio_set(LCD_GPIO_IM, 1<<1); mdelay(10); /*hw reset sign*/ gpio_set(LCD_GPIO_RST, 1<<1); mdelay(10); gpio_set(LCD_GPIO_RST, 0); mdelay(10); gpio_set(LCD_GPIO_RST, 1<<1); mdelay(120); }
static int msm8960_mipi_panel_clock(int enable) { if (enable) { mdp_clock_init(); // acpuclock.c mmss_clock_init(); // acpuclock.c } else if (!target_cont_splash_screen()) { mmss_clock_disable(); // acpuclock.c } return 0; } static int msm8960_mipi_panel_power(int enable) { if (enable) { pm8921_ldo_set_voltage(LDO_11, LDO_VOLTAGE_1_8V); // L11 pm8921_low_voltage_switch_enable(lvs_2); // lvs_2 /* Turn on LDO2 for vdda_mipi_dsi */ pm8921_ldo_set_voltage(LDO_2, LDO_VOLTAGE_1_2V); // L2 pm8921_ldo_set_voltage(LDO_9, LDO_VOLTAGE_2_85V); // L9 } return 0; }
pm8921_ldo_lvs_disable(LDO_9); // pm8921.c pm8921_ldo_lvs_disable(LDO_2); pm8921_ldo_lvs_disable(lvs_2); msm_display_off(1/0); // check if lcd should be closedThe following implementation can be placed at lk/dev/pmic/pm8921/pm8921.c
int pm8921_ldo_lvs_disable(uint32_t ldo_id) { uint32_t ldo_num = (ldo_id & ~LDO_P_MASK); uint32_t val = 0; if (ldo_id == lvs_2) { pm8921_masked_write(PM8921_LVS_CTRL_REG(lvs_2), 0x80, val); // shut down lvs2 voltage, TEST_REG can be used for reading status return 0; } pm8921_masked_write(PM8921_LDO_CTRL_REG(ldo_num), 0x80, val); // shut down ldo voltage, TEST_REG can be used for reading status return 0; }How to enable lvs voltage, as follow:
int pm8921_low_voltage_switch_enable(uint8_t lvs_id) { int ret = NO_ERROR; uint8_t val; if (lvs_id < lvs_start || lvs_id > lvs_end) { dprintf(CRITICAL, "Requested unsupported LVS.\n"); return ERROR; } if (lvs_id == lvs_2) { dprintf(CRITICAL, "No support for LVS2 yet!\n"); // return ERROR; // lvs_2 should be enabled } /* Read LVS_TEST Reg first*/ ret = dev->read(&val, 1, PM8921_LVS_TEST_REG(lvs_id)); if (ret) { dprintf(CRITICAL, "Failed to read LVS_TEST Reg ret=%d.\n", ret); return ret; } /* Check if switch is already ON */ val = val & PM8921_LVS_100_TEST_VOUT_OK; if (val) return ret; /* Turn on switch in normal mode */ val = 0; val |= PM8921_LVS_100_CTRL_SW_EN; /* Enable Switch */ val |= PM8921_LVS_100_CTRL_SLEEP_B_IGNORE; /* Ignore sleep mode pin */ ret = dev->write(&val, 1, PM8921_LVS_CTRL_REG(lvs_id)); if (ret) dprintf(CRITICAL, "Failed to write LVS_CTRL Reg ret=%d.\n", ret); return ret; }
1.1 clock enable
mdp clock enable
void mdp_clock_init(void) { /* Turn on the PLL2, to ramp up the MDP clock to max (200MHz) */ nt_pll_enable(PLL_2, 1); config_mdp_clk(MDP_NS_VAL, MDP_MD_VAL, MDP_CC_VAL, MDP_NS_REG, MDP_MD_REG, MDP_CC_REG); }
/* Initialize all clocks needed by Display */ void mmss_clock_init(void) { /* enable AHB clk */ writel(0x205116,AHB_NS_REG); writel(0xd5040600,AHB_EN_REG); /* Configure Pixel clock */ config_mmss_clk(PIXEL_NS_VAL, PIXEL_MD_VAL, PIXEL_CC_VAL,DSI_PIXEL_NS_REG, DSI_PIXEL_MD_REG, DSI_PIXEL_CC_REG); /* Configure DSI clock */ config_mmss_clk(DSI_NS_VAL, DSI_MD_VAL, DSI_CC_VAL, DSI_NS_REG,DSI_MD_REG, DSI_CC_REG); /* Configure Byte clock */ config_mmss_clk(BYTE_NS_VAL, 0x0, BYTE_CC_VAL, DSI1_BYTE_NS_REG, 0x0,DSI1_BYTE_CC_REG); /* Configure ESC clock */ config_mmss_clk(ESC_NS_VAL, 0x0, ESC_CC_VAL, DSI1_ESC_NS_REG, 0x0,DSI1_ESC_CC_REG); }
void mmss_clock_disable(void) { writel(0x0, DSI1_BYTE_CC_REG); writel(0x0, DSI_PIXEL_CC_REG); writel(0x0, DSI1_ESC_CC_REG); /* Disable root clock */ writel(0x0, DSI_CC_REG); }
The function is at lk/platform/msm_shared/display.c, as follow:
int msm_display_init(struct msm_fb_panel_data *pdata, int mode) { int ret = NO_ERROR; dprintf(CRITICAL, "init mode = %d!\n", mode); if (!mode) { panel = pdata; if (!panel) { ret = ERR_INVALID_ARGS; goto msm_display_init_out; } /* Enable clock */ if (pdata->clk_func) ret = pdata->clk_func(1); if (ret) goto msm_display_init_out; /* Turn on panel */ if (pdata->power_func) ret = pdata->power_func(1); if (ret) goto msm_display_init_out; ret = msm_fb_alloc(&(panel->fb)); // allocate memory: the base address is 0x89000000 if (ret) goto msm_display_init_out; fbcon_setup(&(panel->fb)); } ret = msm_display_config(); // This function is important. It will call mipi_config, mdp_dsi_video_config in order. if (ret) goto msm_display_init_out; ret = msm_display_on(); // Enable host dsi. It will call mdp_dsi_video_on, mipi_dsi_on in order. if (ret) goto msm_display_init_out; mdelay(60); msm_display_init_out: return ret; }
The function msm_display_config is at lk/platform/msm_shared/display.c, as follow:
int msm_display_config()
{
int ret = NO_ERROR;
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 MIPI_VIDEO_PANEL:
dprintf(INFO, "Config MIPI_VIDEO_PANEL.\n");
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;
}
if (pinfo->config) // mipi_global_video_init:pinfo->config = mipi_global_video_config;
ret = pinfo->config((void *)pinfo);
}
mipi_global_video_config will call mipi_dsi_video_mode_config, which will get these parametersrelated to lcd and whose driver ic, such as x resolution, y resolution, HFP, HBP, VFP, VBP, dst_format, traffic_mode, lane, interleave, h_pulse_width, v_pulse_width and so on. All of these parameters related to driver ic and lcd will write into corresponding registers of dsi engine. This function is at /lk/platform/msm_shared/mipi_dsi.c
int mipi_dsi_video_mode_config(unsigned short disp_width, unsigned short disp_height, unsigned short img_width, unsigned short img_height, unsigned short hsync_porch0_fp, unsigned short hsync_porch0_bp, unsigned short vsync_porch0_fp, unsigned short vsync_porch0_bp, unsigned short hsync_width, unsigned short vsync_width, unsigned short dst_format, unsigned short traffic_mode, unsigned char lane_en, unsigned low_pwr_stop_mode, unsigned char eof_bllp_pwr, unsigned char interleav) { int status = 0; /* disable mdp first */ mdp_disable(); writel(0x00000000, DSI_CLK_CTRL); writel(0x00000000, DSI_CLK_CTRL); writel(0x00000000, DSI_CLK_CTRL); writel(0x00000000, DSI_CLK_CTRL); writel(0x00000002, DSI_CLK_CTRL); writel(0x00000006, DSI_CLK_CTRL); writel(0x0000000e, DSI_CLK_CTRL); writel(0x0000001e, DSI_CLK_CTRL); writel(0x0000003e, DSI_CLK_CTRL); writel(0, DSI_CTRL); writel(0, DSI_ERR_INT_MASK0); writel(0x02020202, DSI_INT_CTRL); writel(((disp_width + hsync_porch0_bp) << 16) | hsync_porch0_bp, DSI_VIDEO_MODE_ACTIVE_H); writel(((disp_height + vsync_porch0_bp) << 16) | (vsync_porch0_bp), DSI_VIDEO_MODE_ACTIVE_V); if (mdp_get_revision() >= MDP_REV_41) { writel(((disp_height + vsync_porch0_fp + vsync_porch0_bp - 1) << 16) | (disp_width + hsync_porch0_fp + hsync_porch0_bp - 1), DSI_VIDEO_MODE_TOTAL); } else { writel(((disp_height + vsync_porch0_fp + vsync_porch0_bp) << 16) | (disp_width + hsync_porch0_fp + hsync_porch0_bp), DSI_VIDEO_MODE_TOTAL); } writel((hsync_width << 16) | 0, DSI_VIDEO_MODE_HSYNC); writel(0 << 16 | 0, DSI_VIDEO_MODE_VSYNC); writel(vsync_width << 16 | 0, DSI_VIDEO_MODE_VSYNC_VPOS); writel(1, DSI_EOT_PACKET_CTRL); writel(0x00000100, DSI_MISR_VIDEO_CTRL); if (mdp_get_revision() >= MDP_REV_41) { writel(low_pwr_stop_mode << 16 | eof_bllp_pwr << 12 | traffic_mode << 8 | dst_format << 4 | 0x0, DSI_VIDEO_MODE_CTRL); } else { writel(1 << 28 | 1 << 24 | 1 << 20 | low_pwr_stop_mode << 16 | eof_bllp_pwr << 12 | traffic_mode << 8 | dst_format << 4 | 0x0, DSI_VIDEO_MODE_CTRL); } writel(0x67, DSI_CAL_STRENGTH_CTRL); writel(0x80006711, DSI_CAL_CTRL); writel(0x00010100, DSI_MISR_VIDEO_CTRL); writel(0x00010100, DSI_INT_CTRL); writel(0x02010202, DSI_INT_CTRL); writel(0x02030303, DSI_INT_CTRL); writel(interleav << 30 | 0 << 24 | 0 << 20 | lane_en << 4 | 0x103, DSI_CTRL); return status; }mipi_config is at lk/platform/msm_shared/mipi_dsi.c.
int mipi_config(struct msm_fb_panel_data *panel) { int ret = NO_ERROR; struct msm_panel_info *pinfo; struct mipi_dsi_panel_config mipi_pinfo; if (!panel) return ERR_INVALID_ARGS; pinfo = &(panel->panel_info); mipi_pinfo.mode = pinfo->mipi.mode; mipi_pinfo.num_of_lanes = pinfo->mipi.num_of_lanes; mipi_pinfo.dsi_phy_config = pinfo->mipi.dsi_phy_db; mipi_pinfo.panel_cmds = pinfo->mipi.panel_cmds; mipi_pinfo.num_of_panel_cmds = pinfo->mipi.num_of_panel_cmds; mipi_pinfo.lane_swap = pinfo->mipi.lane_swap; /* Enable MMSS_AHB_ARB_MATER_PORT_E for arbiter master0 and master 1 request */ #if (!DISPLAY_MIPI_PANEL_RENESAS) writel(0x00001800, MMSS_SFPB_GPREG); #endif mipi_dsi_phy_init(&mipi_pinfo); // This is related to phy timing and ctrl, regulator, strength, pll control ret += mipi_dsi_panel_initialize(&mipi_pinfo); if (pinfo->rotate && panel->rotate) pinfo->rotate(); return ret; }
The following parameters are used for initializing dsi phy, the function is mipi_dsi_phy_init
static struct mipi_dsi_phy_ctrl dsi_video_mode_otm9605a_boyi_lg_500_phy_db = { /* DSI Bit Clock at 500 MHz, 2 lane, RGB888 */ /* 0x0500: regulator */ {0x02, 0x0a, 0x04, 0x00, 0x20}, /* 0x0440: phy timing */ {0x8a, 0x47, 0x14, 0x00, 0x55, 0x56, 0x19, 0x4b, 0x1f, 0x03, 0x04}, /* 0x0470: phy ctrl */ {0x5f, 0x00, 0x00, 0x10}, /* 0x0480: strength */ {0xff, 0x00, 0x06, 0x00}, /* 0x0204: pll control */ {0x00, 0xb4, 0x30, 0xda, 0x00, 0x40, 0x03, 0x62, 0x01, 0x0f, 0x07, 0x00, 0x1a, 0x00, 0x0, 0x02, 0x0, 0x20, 0x0, 0x01, 0x0}, }; #endifNow we can look at how to initialize phy (at platform/msm_shared/mipi_dsi_phy.c)
int mipi_dsi_phy_init(struct mipi_dsi_panel_config *pinfo) { struct mipi_dsi_phy_ctrl *pd; uint32_t i, off = 0; int mdp_rev; mdp_rev = mdp_get_revision(); writel(0x0001, MIPI_DSI_BASE + 0x128); /* start phy sw reset */ udelay(1); writel(0x0000, MIPI_DSI_BASE + 0x128); /* end phy sw reset */ udelay(1); /* disable DCDC & enable LDO */ writel(0x0002, MIPI_DSI_BASE + 0x500); writel(0x0001, MIPI_DSI_BASE + 0x504); /* regulator_ctrl_1 */ writel(0x0001, MIPI_DSI_BASE + 0x508); /* regulator_ctrl_2 */ writel(0x0000, MIPI_DSI_BASE + 0x50c); /* regulator_ctrl_3 */ writel(0x0100, MIPI_DSI_BASE + 0x510); /* regulator_ctrl_4 */ /* enable LDO mode */ writel(0x05, MIPI_DSI_BASE + 0x4b0); pd = (pinfo->dsi_phy_config); off = 0x0480; /* strength 0 - 2 */ for (i = 0; i < 3; i++) { writel(pd->strength[i], MIPI_DSI_BASE + off); off += 4; } off = 0x0470; /* ctrl 0 - 3 */ for (i = 0; i < 4; i++) { writel(pd->ctrl[i], MIPI_DSI_BASE + off); off += 4; } off = 0x0500; /* regulator ctrl 0 - 4 */ for (i = 0; i < 5; i++) { writel(pd->regulator[i], MIPI_DSI_BASE + off); off += 4; } mipi_dsi_calibration(); mipi_dsi_lane_cfg(); mipi_dsi_bist_ctrl(); off = 0x0204; /* pll ctrl 1 - 19, skip 0 */ for (i = 1; i < 20; i++) { writel(pd->pll[i], MIPI_DSI_BASE + off); off += 4; } /* pll ctrl 0 */ writel(pd->pll[0], MIPI_DSI_BASE + 0x200); writel((pd->pll[0] | 0x01), MIPI_DSI_BASE + 0x200); /* Check that PHY is ready */ while (!(readl(DSIPHY_PLL_RDY) & 0x01)) udelay(1); writel(0x202D, DSI_CLKOUT_TIMING_CTRL); off = 0x0440; /* phy timing ctrl 0 - 11 */ for (i = 0; i < 12; i++) { writel(pd->timing[i], MIPI_DSI_BASE + off); off += 4; } if (pinfo->lane_swap) writel(pinfo->lane_swap, MIPI_DSI_BASE + 0xac); return 0; }
mdp_dsi_video_config is at lk/platform/msm_shared/mdp4.c. (porch, vsync, hsync, framebuffer base address and so on)
/* MDP_AXI_RDMASTER_CONFIG set all master to read from AXI port 0, that's the only port connected */ writel(0x00290000, MDP_AXI_RDMASTER_CONFIG); writel(0x00000004, MDP_AXI_WRMASTER_CONFIG); writel(0x00007777, MDP_MAX_RD_PENDING_CMD_CONFIG); /* Set up CMD_INTF_SEL, VIDEO_INTF_SEL, EXT_INTF_SEL, SEC_INTF_SEL, PRIM_INTF_SEL */ writel(0x00000049, MDP_DISP_INTF_SEL); /* DMA P */ writel(0x0000000b, MDP_OVERLAYPROC0_CFG); /* write fb addr in MDP_DMA_P_BUF_ADDR */ writel(fb->base, MDP_DMA_P_BUF_ADDR); /* write active region size*/ mdp_rgb_size = (fb->height << 16) + fb->width; writel(mdp_rgb_size, MDP_DMA_P_SIZE); /* set Y-stride value in bytes */ /* Y-stride is defined as the number of bytes in a line. */ writel((fb->stride * fb->bpp/8), MDP_DMA_P_BUF_Y_STRIDE); /* Start XY coordinates */ writel(0, MDP_DMA_P_OUT_XY);
mdp_dsi_video_on is at lk/platform/msm_shared/mdp4.c.
mipi_dsi_on is at lk/platform/msm_shared/mipi_dsi.c.
case MIPI_VIDEO_PANEL: dprintf(INFO, "Turn on MIPI_VIDEO_PANEL.\n"); ret = mdp_dsi_video_on(); if (ret) goto msm_display_on_out; ret = mipi_dsi_on(); if (ret) goto msm_display_on_out; break;
int mdp_dsi_video_on() { int ret = NO_ERROR; writel(0x00000001, MDP_DSI_VIDEO_EN); return ret; }
int mipi_dsi_on() { int ret = NO_ERROR; unsigned long ReadValue; unsigned long count = 0; ReadValue = readl(DSI_INT_CTRL) & 0x00010000; mdelay(10); while (ReadValue != 0x00010000) { ReadValue = readl(DSI_INT_CTRL) & 0x00010000; count++; if (count > 0xffff) { dprintf(CRITICAL, "Video lane test failed\n"); return ERROR; } } dprintf(INFO, "Video lane tested successfully\n"); return ret; }
lcd off, (msm_display_off at lk/platform/msm_shared/display.c)as follow:
int msm_display_off(int enable) { int ret = NO_ERROR; struct msm_panel_info *pinfo; dprintf(CRITICAL, "display off mode = %d!\n", enable); if (!panel) return ERR_INVALID_ARGS; pinfo = &(panel->panel_info); switch (pinfo->type) { case MIPI_VIDEO_PANEL: dprintf(INFO, "Turn off MIPI_VIDEO_PANEL.\n"); if (!enable) mipi_panel_off(); ret = mdp_dsi_video_off(enable); if (ret) goto msm_display_off_out; ret = mipi_dsi_off(enable); if (ret) goto msm_display_off_out; break; default: return ERR_INVALID_ARGS; }; if (pinfo->off) ret = pinfo->off(); /* Disable clock */ if (panel->clk_func) ret = panel->clk_func(0); if (ret) goto msm_display_off_out; /* Disable panel */ if (panel->power_func) ret = panel->power_func(0); msm_display_off_out: return ret; }
The function system_boot at lk/platform/msm8960/power_on_event.c, as follows:
display_init(); fbcon_clear(); show_white_color(0, 0); mipi_backlight_tx(100); msm_display_off(0 or 1);
4.3 residual[rɪ'zɪdjʊəl] shadows
4.4 blank screen
4.5 backlight good
4.6 color cast (偏色)
4.7 gamma larger or smaller
4.8 backlight too bright
4.9 power consumption too much
4.10 color inversion
4.11 VBP, VFP, HBP, HFP
a. If the picture jitters up and down and you can see clearly the content, maybe VBP or VFP is too small. So by means of adjusting the value of VBP and VFP, this scene can be fixed.
b. If you can see some additional lines on the edge of lcd, maybe HBP or HFP is not appropriate. So by means of adjusting the value of HBP and HFP, this scene can be fixed.
int pm8921_ldo_lvs_disable(uint32_t ldo_id) { uint32_t ldo_num = (ldo_id & ~LDO_P_MASK); uint32_t val = 0; if (ldo_id == lvs_2) { pm8921_masked_write(PM8921_LVS_CTRL_REG(lvs_2), 0x80, val); return 0; } pm8921_masked_write(PM8921_LDO_CTRL_REG(ldo_num), 0x80, val); return 0; }