/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of The Linux Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include "gcdb_autopll.h" static struct mdss_dsi_pll_config pll_data; static void calculate_bitclock(struct msm_panel_info *pinfo) { uint32_t h_period = 0, v_period = 0; uint32_t width = pinfo->xres; struct dsc_desc *dsc = NULL; int bpp_lane; if (pinfo->mipi.dual_dsi) width /= 2; if (pinfo->compression_mode == COMPRESSION_DSC) { dsc = &pinfo->dsc; width = dsc->pclk_per_line; } else if (pinfo->compression_mode == COMPRESSION_FBC) { if (pinfo->fbc.comp_ratio) width /= pinfo->fbc.comp_ratio; } h_period = width + pinfo->lcdc.h_back_porch + pinfo->lcdc.h_front_porch + pinfo->lcdc.h_pulse_width + pinfo->lcdc.xres_pad; v_period = pinfo->yres + pinfo->lcdc.v_back_porch + pinfo->lcdc.v_front_porch + pinfo->lcdc.v_pulse_width + pinfo->lcdc.yres_pad; bpp_lane = pinfo->bpp / pinfo->mipi.num_of_lanes; /* * If a bit clock rate is not specified, calculate it based * on panel parameters */ if (pinfo->mipi.bitclock == 0) pll_data.bit_clock = (h_period * v_period * pinfo->mipi.frame_rate * bpp_lane); else pll_data.bit_clock = pinfo->mipi.bitclock; pll_data.pixel_clock = (pll_data.bit_clock / bpp_lane); dprintf(SPEW, "%s: bit_clk=%d pix_clk=%d\n", __func__, pll_data.bit_clock, pll_data.pixel_clock); pll_data.byte_clock = pll_data.bit_clock >> 3; pll_data.halfbit_clock = pll_data.bit_clock >> 1; } static uint32_t calculate_div1() { uint32_t ret = NO_ERROR; /* div1 - there is divide by 2 logic present */ if (pll_data.halfbit_clock > HALFBIT_CLOCK1) { pll_data.posdiv1 = 0x0; /*div 1 */ pll_data.vco_clock = pll_data.halfbit_clock << 1; } else if (pll_data.halfbit_clock > HALFBIT_CLOCK2) { pll_data.posdiv1 = 0x1; /*div 2 */ pll_data.vco_clock = pll_data.halfbit_clock << 2; } else if (pll_data.halfbit_clock > HALFBIT_CLOCK3) { pll_data.posdiv1 = 0x3; /*div 4 */ pll_data.vco_clock = pll_data.halfbit_clock << 3; } else if (pll_data.halfbit_clock > HALFBIT_CLOCK4) { pll_data.posdiv1 = 0x4; /*div 5 */ pll_data.vco_clock = pll_data.halfbit_clock * 10; } else { dprintf(CRITICAL, "Not able to calculate posdiv1\n"); } return ret; } static uint32_t calculate_div3(uint8_t bpp, uint8_t num_of_lanes) { pll_data.pclk_m = 0x1; /* M = 1, N= 1 */ pll_data.pclk_n = 0xFF; /* ~ (N-M) = 0xff */ pll_data.pclk_d = 0xFF; /* ~N = 0xFF */ /* formula is ( vco_clock / pdiv_digital) / mnd = pixel_clock */ /* div3 */ switch (bpp) { case BITS_18: if (num_of_lanes == 3) { pll_data.posdiv3 = pll_data.vco_clock / pll_data.pixel_clock; } else { pll_data.posdiv3 = (pll_data.pixel_clock * 2 / 9) / pll_data.vco_clock; pll_data.pclk_m = 0x2; /* M = 2,N = 9 */ pll_data.pclk_n = 0xF8; pll_data.pclk_d = 0xF6; } break; case BITS_16: if (num_of_lanes == 3) { pll_data.posdiv3 = (pll_data.pixel_clock * 3 / 8) / pll_data.vco_clock; pll_data.pclk_m = 0x3; /* M = 3, N = 9 */ pll_data.pclk_n = 0xFA; pll_data.pclk_d = 0xF7; } else { pll_data.posdiv3 = pll_data.vco_clock / pll_data.pixel_clock; } break; case BITS_24: default: pll_data.posdiv3 = pll_data.vco_clock / pll_data.pixel_clock; break; } pll_data.posdiv3--; /* Register needs one value less */ return NO_ERROR; } static uint32_t calculate_dec_frac_start() { uint32_t refclk = 19200000; uint32_t vco_rate = pll_data.vco_clock; uint32_t tmp, mod; vco_rate /= 2; pll_data.dec_start = vco_rate / refclk; tmp = vco_rate % refclk; /* module, fraction */ tmp /= 192; tmp *= 1024; tmp /= 100; tmp *= 1024; tmp /= 1000; pll_data.frac_start = tmp; vco_rate *= 2; /* restore */ if (pll_data.en_vco_zero_phase) { tmp = vco_rate / (refclk / 1000);/* div 1000 first */ tmp *= 1024; tmp /= 1000; tmp /= 10; pll_data.lock_comp = tmp - 1; } else { tmp = vco_rate / refclk; mod = vco_rate % refclk; tmp *= 127; mod *= 127; mod /= refclk; tmp += mod; tmp /= 10; pll_data.lock_comp = tmp; } dprintf(SPEW, "%s: dec_start=0x%x dec_frac=0x%x lock_comp=0x%x\n", __func__, pll_data.dec_start, pll_data.frac_start, pll_data.lock_comp); return NO_ERROR; } static uint32_t calculate_vco_28nm(uint8_t bpp, uint8_t num_of_lanes) { /* If half bitclock is more than VCO min value */ if (pll_data.halfbit_clock > VCO_MIN_CLOCK) { /* Direct Mode */ /* support vco clock to max value only */ if (pll_data.halfbit_clock > VCO_MAX_CLOCK) pll_data.vco_clock = VCO_MAX_CLOCK; else pll_data.vco_clock = pll_data.halfbit_clock; pll_data.directpath = 0x0; pll_data.posdiv1 = 0x0; /*DSI spec says 0 - div 1 */ /*1 - div 2 */ /*F - div 16 */ } else { /* Indirect Mode */ pll_data.directpath = 0x02; /* set bit 1 to enable for indirect path */ calculate_div1(); } /* calculate mnd and div3 for direct and indirect path */ calculate_div3(bpp, num_of_lanes); return NO_ERROR; } #ifndef DISPLAY_EN_20NM_PLL_90_PHASE static void config_20nm_pll_vco_range(void) { pll_data.vco_min = 300000000; pll_data.vco_max = 1500000000; pll_data.en_vco_zero_phase = 1; dprintf(SPEW, "%s: Configured VCO for zero phase\n", __func__); } #else static void config_20nm_pll_vco_range(void) { pll_data.vco_min = 1000000000; pll_data.vco_max = 2000000000; pll_data.en_vco_zero_phase = 0; dprintf(SPEW, "%s: Configured VCO for 90 phase\n", __func__); } #endif static uint32_t calculate_vco_thulium() { uint32_t rate; uint32_t mod; pll_data.vco_min = MIN_THULIUM_VCO_RATE; pll_data.vco_max = MAX_THULIUM_VCO_RATE; if (pll_data.bit_clock < pll_data.vco_min) rate = pll_data.vco_min; else if (pll_data.bit_clock > pll_data.vco_max) rate = pll_data.vco_max; else rate = pll_data.bit_clock; pll_data.ndiv = FIX_N_DIV; if (pll_data.bit_clock) { pll_data.n1div = rate / pll_data.bit_clock; } else { dprintf(ERROR, "%s: bit clock is 0, divider calculation failed\n", __func__); return ERROR; } mod = rate % pll_data.bit_clock; if (mod) pll_data.n1div++; if (pll_data.n1div < MIN_THULIUM_DIV_VAL || pll_data.n1div > MAX_THULIUM_DIV_VAL) { dprintf(ERROR, "%s: n1div is out of ranget:%d\n", __func__, pll_data.n1div); return ERROR; } pll_data.vco_clock = pll_data.bit_clock * pll_data.ndiv * pll_data.n1div; rate = pll_data.vco_clock; rate /= pll_data.n1div; rate /= FIX_PIXEL_CLOCK_DIV; pll_data.n2div = rate / pll_data.pixel_clock; mod = rate % pll_data.pixel_clock; if (mod) pll_data.n2div++; if (pll_data.n2div < MIN_THULIUM_DIV_VAL || pll_data.n2div > MAX_THULIUM_DIV_VAL) { dprintf(ERROR, "%s: n2div is out of ranget:%d\n", __func__, pll_data.n2div); return ERROR; } dprintf(SPEW, "%s: vco:%u n1div:%d n2div:%d bit_clk:%u pixel_clk:%u\n", __func__, pll_data.vco_clock, pll_data.n1div, pll_data.n2div, pll_data.bit_clock, pll_data.pixel_clock); return NO_ERROR; } static uint32_t calculate_vco_20nm(uint8_t bpp, uint8_t lanes) { uint32_t vco, dsi_clk; int mod, ndiv, hr_oclk2, hr_oclk3; int m = 1; int n = 1; int bpp_m = 3; /* bpp = 3 */ int bpp_n = 1; if (bpp == BITS_18) { bpp_m = 9; /* bpp = 2.25 */ bpp_n = 4; if (lanes == 2) { m = 2; n = 9; } else if (lanes == 4) { m = 4; n = 9; } } else if (bpp == BITS_16) { bpp_m = 2; /* bpp = 2 */ bpp_n = 1; if (lanes == 3) { m = 3; n = 8; } } hr_oclk2 = 4; /* If bitclock is more than VCO min value */ if (pll_data.halfbit_clock >= ((pll_data.vco_min) >> 1)) { /* Direct Mode */ vco = pll_data.halfbit_clock << 1; /* support vco clock to max value only */ if (vco > (pll_data.vco_max)) vco = (pll_data.vco_max); pll_data.directpath = 0x0; pll_data.byte_clock = vco / 2 / hr_oclk2; pll_data.lp_div_mux = 0x0; ndiv = 1; hr_oclk3 = hr_oclk2 * m / n * bpp_m / bpp_n / lanes; } else { /* Indirect Mode */ mod = (pll_data.vco_min) % (4 * pll_data.halfbit_clock ); ndiv = (pll_data.vco_min) / (4 * pll_data.halfbit_clock ); if (mod) ndiv += 1; vco = pll_data.halfbit_clock * 4 * ndiv; pll_data.lp_div_mux = 0x1; pll_data.directpath = 0x02; /* set bit 1 to enable for indirect path */ pll_data.byte_clock = vco / 4 / hr_oclk2 / ndiv; hr_oclk3 = hr_oclk2 * m / n * ndiv * 2 * bpp_m / bpp_n / lanes; } pll_data.vco_clock = vco; dsi_clk = vco / 2 / hr_oclk3; pll_data.ndiv = ndiv; pll_data.hr_oclk2 = hr_oclk2 - 1; /* strat from 0 */ pll_data.hr_oclk3 = hr_oclk3 - 1; /* strat from 0 */ pll_data.pclk_m = m; /* M */ pll_data.pclk_n = ~(n - m); /* ~(N-M) */ pll_data.pclk_d = ~n; /* ~N */ dprintf(SPEW, "%s: oclk2=%d oclk3=%d ndiv=%d vco=%u dsi_clk=%u byte_clk=%u\n", __func__, hr_oclk2, hr_oclk3, ndiv, vco, dsi_clk, pll_data.byte_clock); calculate_dec_frac_start(); return NO_ERROR; } uint32_t calculate_clock_config(struct msm_panel_info *pinfo) { uint32_t ret = NO_ERROR; calculate_bitclock(pinfo); switch (pinfo->mipi.mdss_dsi_phy_db->pll_type) { case DSI_PLL_TYPE_20NM: config_20nm_pll_vco_range(); ret = calculate_vco_20nm(pinfo->bpp, pinfo->mipi.num_of_lanes); break; case DSI_PLL_TYPE_THULIUM: ret = calculate_vco_thulium(pinfo->bpp, pinfo->mipi.num_of_lanes); break; case DSI_PLL_TYPE_28NM: default: ret = calculate_vco_28nm(pinfo->bpp, pinfo->mipi.num_of_lanes); break; } pinfo->mipi.dsi_pll_config = &pll_data; return ret; }