int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info, int frame_rate) { u32 fb_divider, rate, vco; u32 div_ratio = 0; u32 pll_analog_posDiv = 1; u32 h_period, v_period; u32 dsi_pclk_rate; u8 lanes = 0, bpp; struct dsi_clk_mnd_table const *mnd_entry = mnd_table; if (panel_info->mipi.data_lane3) lanes += 1; if (panel_info->mipi.data_lane2) lanes += 1; if (panel_info->mipi.data_lane1) lanes += 1; if (panel_info->mipi.data_lane0) lanes += 1; switch (panel_info->mipi.dst_format) { case DSI_CMD_DST_FORMAT_RGB888: case DSI_VIDEO_DST_FORMAT_RGB888: case DSI_VIDEO_DST_FORMAT_RGB666_LOOSE: bpp = 3; break; case DSI_CMD_DST_FORMAT_RGB565: case DSI_VIDEO_DST_FORMAT_RGB565: bpp = 2; break; default: bpp = 3; /* Default format set to RGB888 */ break; } h_period = mdss_panel_get_htotal(panel_info, true); v_period = mdss_panel_get_vtotal(panel_info); if ((frame_rate != panel_info->mipi.frame_rate) || (!panel_info->clk_rate)) { h_period += panel_info->lcdc.xres_pad; v_period += panel_info->lcdc.yres_pad; if (lanes > 0) { panel_info->clk_rate = ((h_period * v_period * frame_rate * bpp * 8) / lanes); } else { pr_err("%s: forcing mdss_dsi lanes to 1\n", __func__); panel_info->clk_rate = (h_period * v_period * frame_rate * bpp * 8); } } pll_divider_config.clk_rate = panel_info->clk_rate; if (pll_divider_config.clk_rate == 0) pll_divider_config.clk_rate = 454000000; rate = (pll_divider_config.clk_rate / 2) / 1000000; /* Half Bit Clock In Mhz */ if (rate < 43) { vco = rate * 16; div_ratio = 16; pll_analog_posDiv = 8; } else if (rate < 85) { vco = rate * 8; div_ratio = 8; pll_analog_posDiv = 4; } else if (rate < 170) { vco = rate * 4; div_ratio = 4; pll_analog_posDiv = 2; } else if (rate < 340) { vco = rate * 2; div_ratio = 2; pll_analog_posDiv = 1; } else { /* DSI PLL Direct path configuration */ vco = rate * 1; div_ratio = 1; pll_analog_posDiv = 1; } /* find the mnd settings from mnd_table entry */ for (; mnd_entry < mnd_table + ARRAY_SIZE(mnd_table); ++mnd_entry) { if (((mnd_entry->lanes) == lanes) && ((mnd_entry->bpp) == bpp)) break; } if (mnd_entry == mnd_table + ARRAY_SIZE(mnd_table)) { pr_err("%s: requested Lanes, %u & BPP, %u, not supported\n", __func__, lanes, bpp); return -EINVAL; } fb_divider = ((vco * PREF_DIV_RATIO) / 27); pll_divider_config.fb_divider = fb_divider; pll_divider_config.ref_divider_ratio = PREF_DIV_RATIO; pll_divider_config.bit_clk_divider = div_ratio; pll_divider_config.byte_clk_divider = pll_divider_config.bit_clk_divider * 8; pll_divider_config.analog_posDiv = pll_analog_posDiv; pll_divider_config.digital_posDiv = (mnd_entry->pll_digital_posDiv) * div_ratio; if ((mnd_entry->pclk_d == 0) || (mnd_entry->pclk_m == 1)) { dsi_pclk.mnd_mode = 0; dsi_pclk.src = 0x3; dsi_pclk.pre_div_func = (mnd_entry->pclk_n - 1); } else { dsi_pclk.mnd_mode = 2; dsi_pclk.src = 0x3; dsi_pclk.m = mnd_entry->pclk_m; dsi_pclk.n = mnd_entry->pclk_n; dsi_pclk.d = mnd_entry->pclk_d; } dsi_pclk_rate = (((pll_divider_config.clk_rate) * lanes) / (8 * bpp)); if ((dsi_pclk_rate < 3300000) || (dsi_pclk_rate > 250000000)) dsi_pclk_rate = 35000000; panel_info->mipi.dsi_pclk_rate = dsi_pclk_rate; return 0; }
static int mdss_dsi_dfps_config(struct mdss_panel_data *pdata, int new_fps) { int rc = 0; struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; u32 dsi_ctrl; pr_debug("%s+:\n", __func__); if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); return -EINVAL; } ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); if (!ctrl_pdata->panel_data.panel_info.dynamic_fps) { pr_err("%s: Dynamic fps not enabled for this panel\n", __func__); return -EINVAL; } if (new_fps != ctrl_pdata->panel_data.panel_info.mipi.frame_rate) { if (pdata->panel_info.dfps_update == DFPS_IMMEDIATE_PORCH_UPDATE_MODE) { u32 hsync_period, vsync_period; u32 new_dsi_v_total, current_dsi_v_total; vsync_period = mdss_panel_get_vtotal(&pdata->panel_info); hsync_period = mdss_panel_get_htotal(&pdata->panel_info); current_dsi_v_total = MIPI_INP((ctrl_pdata->ctrl_base) + 0x2C); new_dsi_v_total = ((vsync_period - 1) << 16) | (hsync_period - 1); MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C, (current_dsi_v_total | 0x8000000)); if (new_dsi_v_total & 0x8000000) { MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C, new_dsi_v_total); } else { MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C, (new_dsi_v_total | 0x8000000)); MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C, (new_dsi_v_total & 0x7ffffff)); } pdata->panel_info.mipi.frame_rate = new_fps; } else { rc = mdss_dsi_clk_div_config (&ctrl_pdata->panel_data.panel_info, new_fps); if (rc) { pr_err("%s: unable to initialize the clk dividers\n", __func__); return rc; } ctrl_pdata->pclk_rate = pdata->panel_info.mipi.dsi_pclk_rate; ctrl_pdata->byte_clk_rate = pdata->panel_info.clk_rate / 8; if (pdata->panel_info.dfps_update == DFPS_IMMEDIATE_CLK_UPDATE_MODE) { dsi_ctrl = MIPI_INP((ctrl_pdata->ctrl_base) + 0x0004); pdata->panel_info.mipi.frame_rate = new_fps; dsi_ctrl &= ~0x2; MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004, dsi_ctrl); mdss_dsi_controller_cfg(true, pdata); mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0); mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1); dsi_ctrl |= 0x2; MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004, dsi_ctrl); } } } else { pr_debug("%s: Panel is already at this FPS\n", __func__); } return rc; }
int mdss_dsi_phy_calc_timing_param(struct mdss_panel_info *pinfo, u32 phy_rev, u32 frate_hz) { struct dsi_phy_t_clk_param t_clk; struct dsi_phy_timing t_param; int hsync_period; int vsync_period; unsigned long inter_num; uint32_t lane_config = 0; unsigned long x, y; int rc = 0; if (!pinfo) { pr_err("invalid panel info\n"); return -EINVAL; } hsync_period = mdss_panel_get_htotal(pinfo, true); vsync_period = mdss_panel_get_vtotal(pinfo); inter_num = pinfo->bpp * frate_hz; if (pinfo->mipi.data_lane0) lane_config++; if (pinfo->mipi.data_lane1) lane_config++; if (pinfo->mipi.data_lane2) lane_config++; if (pinfo->mipi.data_lane3) lane_config++; x = mult_frac(vsync_period * hsync_period, inter_num, lane_config); y = rounddown(x, 1); t_clk.bitclk_mbps = rounddown(mult_frac(y, 1, 1000000), 1); t_clk.escclk_numer = ESC_CLK_MHZ; t_clk.escclk_denom = ESCCLK_MMSS_CC_PREDIV; t_clk.tlpx_numer_ns = TLPX_NUMER; t_clk.treot_ns = TR_EOT; pr_debug("hperiod=%d, vperiod=%d, inter_num=%lu, lane_cfg=%d\n", hsync_period, vsync_period, inter_num, lane_config); pr_debug("x=%lu, y=%lu, bitrate=%d\n", x, y, t_clk.bitclk_mbps); switch (phy_rev) { case DSI_PHY_REV_10: rc = mdss_dsi_phy_initialize_defaults(&t_clk, &t_param, phy_rev); if (rc) { pr_err("phy%d initialization failed\n", phy_rev); goto timing_calc_end; } mdss_dsi_phy_calc_param_phy_rev_1(&t_clk, &t_param); mdss_dsi_phy_update_timing_param(pinfo, &t_param); break; case DSI_PHY_REV_20: rc = mdss_dsi_phy_initialize_defaults(&t_clk, &t_param, phy_rev); if (rc) { pr_err("phy%d initialization failed\n", phy_rev); goto timing_calc_end; } rc = mdss_dsi_phy_calc_param_phy_rev_2(&t_clk, &t_param); if (rc) { pr_err("Phy timing calculations failed\n"); goto timing_calc_end; } mdss_dsi_phy_update_timing_param_rev_2(pinfo, &t_param); break; default: pr_err("phy rev %d not supported\n", phy_rev); return -EINVAL; } timing_calc_end: return rc; }