/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #define pr_fmt(fmt) "%s: " fmt, __func__ #include #include #include #include #include #include #include #include "mdss-pll.h" #include "mdss-dsi-pll.h" #define VCO_DELAY_USEC 1000 static struct clk_div_ops fixed_2div_ops; static struct clk_ops byte_mux_clk_ops; static struct clk_ops pixel_clk_src_ops; static struct clk_ops byte_clk_src_ops; static struct clk_ops analog_postdiv_clk_ops; static struct lpfr_cfg lpfr_lut_struct[] = { {479500000, 8}, {480000000, 11}, {575500000, 8}, {576000000, 12}, {610500000, 8}, {659500000, 9}, {671500000, 10}, {672000000, 14}, {708500000, 10}, {750000000, 11}, }; static int vco_set_rate_lpm(struct clk *c, unsigned long rate) { int rc; struct dsi_pll_vco_clk *vco = to_vco_clk(c); struct mdss_pll_resources *dsi_pll_res = vco->priv; rc = mdss_pll_resource_enable(dsi_pll_res, true); if (rc) { pr_err("Failed to enable mdss dsi pll resources\n"); return rc; } /* * DSI PLL software reset. Add HW recommended delays after toggling * the software reset bit off and back on. */ MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x01); udelay(1000); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x00); udelay(1000); rc = vco_set_rate(vco, rate); mdss_pll_resource_enable(dsi_pll_res, false); return rc; } static void dsi_pll_sw_reset_8916(struct mdss_pll_resources *dsi_pll_res) { /* * DSI PLL software reset. Add HW recommended delays after toggling * the software reset bit off and back on. */ MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x01); ndelay(500); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x00); } static void dsi_pll_toggle_lock_detect_8916( struct mdss_pll_resources *dsi_pll_res) { /* DSI PLL toggle lock detect setting */ MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x04); ndelay(500); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x05); udelay(512); } static int dsi_pll_check_lock_status_8916( struct mdss_pll_resources *dsi_pll_res) { int rc = 0; rc = dsi_pll_lock_status(dsi_pll_res); if (rc) pr_debug("PLL Locked\n"); else pr_err("PLL failed to lock\n"); return rc; } static int gf_2_dsi_pll_enable_seq_8916(struct mdss_pll_resources *dsi_pll_res) { int pll_locked = 0; dsi_pll_sw_reset_8916(dsi_pll_res); /* * GF PART 2 PLL power up sequence. * Add necessary delays recommended by hardware. */ MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x04); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05); udelay(3); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f); udelay(500); dsi_pll_toggle_lock_detect_8916(dsi_pll_res); pll_locked = dsi_pll_check_lock_status_8916(dsi_pll_res); return pll_locked ? 0 : -EINVAL; } static int gf_1_dsi_pll_enable_seq_8916(struct mdss_pll_resources *dsi_pll_res) { int pll_locked = 0; dsi_pll_sw_reset_8916(dsi_pll_res); /* * GF PART 1 PLL power up sequence. * Add necessary delays recommended by hardware. */ MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x14); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05); udelay(3); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f); udelay(500); dsi_pll_toggle_lock_detect_8916(dsi_pll_res); pll_locked = dsi_pll_check_lock_status_8916(dsi_pll_res); return pll_locked ? 0 : -EINVAL; } static int tsmc_dsi_pll_enable_seq_8916(struct mdss_pll_resources *dsi_pll_res) { int pll_locked = 0; dsi_pll_sw_reset_8916(dsi_pll_res); /* * TSMC PLL power up sequence. * Add necessary delays recommended by hardware. */ MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x34); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f); udelay(500); dsi_pll_toggle_lock_detect_8916(dsi_pll_res); pll_locked = dsi_pll_check_lock_status_8916(dsi_pll_res); return pll_locked ? 0 : -EINVAL; } /* Op structures */ static struct clk_ops clk_ops_dsi_vco = { .set_rate = vco_set_rate_lpm, .round_rate = vco_round_rate, .handoff = vco_handoff, .prepare = vco_prepare, .unprepare = vco_unprepare, }; static struct clk_div_ops fixed_4div_ops = { .set_div = fixed_4div_set_div, .get_div = fixed_4div_get_div, }; static struct clk_div_ops analog_postdiv_ops = { .set_div = analog_set_div, .get_div = analog_get_div, }; static struct clk_div_ops digital_postdiv_ops = { .set_div = digital_set_div, .get_div = digital_get_div, }; static struct clk_mux_ops byte_mux_ops = { .set_mux_sel = set_byte_mux_sel, .get_mux_sel = get_byte_mux_sel, }; /* DSI PLL0 clock structures */ static struct dsi_pll_vco_clk dsi_pll0_vco_clk = { .ref_clk_rate = 19200000, .min_rate = 350000000, .max_rate = 750000000, .pll_en_seq_cnt = 9, .pll_enable_seqs[0] = tsmc_dsi_pll_enable_seq_8916, .pll_enable_seqs[1] = tsmc_dsi_pll_enable_seq_8916, .pll_enable_seqs[2] = tsmc_dsi_pll_enable_seq_8916, .pll_enable_seqs[3] = gf_1_dsi_pll_enable_seq_8916, .pll_enable_seqs[4] = gf_1_dsi_pll_enable_seq_8916, .pll_enable_seqs[5] = gf_1_dsi_pll_enable_seq_8916, .pll_enable_seqs[6] = gf_2_dsi_pll_enable_seq_8916, .pll_enable_seqs[7] = gf_2_dsi_pll_enable_seq_8916, .pll_enable_seqs[8] = gf_2_dsi_pll_enable_seq_8916, .lpfr_lut_size = 10, .lpfr_lut = lpfr_lut_struct, .c = { .dbg_name = "dsi_pll0_vco_clk", .ops = &clk_ops_dsi_vco, CLK_INIT(dsi_pll0_vco_clk.c), }, }; static struct div_clk dsi_pll0_analog_postdiv_clk = { .data = { .max_div = 255, .min_div = 1, }, .ops = &analog_postdiv_ops, .c = { .parent = &dsi_pll0_vco_clk.c, .dbg_name = "dsi_pll0_analog_postdiv_clk", .ops = &analog_postdiv_clk_ops, .flags = CLKFLAG_NO_RATE_CACHE, CLK_INIT(dsi_pll0_analog_postdiv_clk.c), }, }; static struct div_clk dsi_pll0_indirect_path_div2_clk = { .ops = &fixed_2div_ops, .data = { .div = 2, .min_div = 2, .max_div = 2, }, .c = { .parent = &dsi_pll0_analog_postdiv_clk.c, .dbg_name = "dsi_pll0_indirect_path_div2_clk", .ops = &clk_ops_div, .flags = CLKFLAG_NO_RATE_CACHE, CLK_INIT(dsi_pll0_indirect_path_div2_clk.c), }, }; static struct div_clk dsi_pll0_pixel_clk_src = { .data = { .max_div = 255, .min_div = 1, }, .ops = &digital_postdiv_ops, .c = { .parent = &dsi_pll0_vco_clk.c, .dbg_name = "dsi_pll0_pixel_clk_src", .ops = &pixel_clk_src_ops, .flags = CLKFLAG_NO_RATE_CACHE, CLK_INIT(dsi_pll0_pixel_clk_src.c), }, }; static struct mux_clk dsi_pll0_byte_mux = { .num_parents = 2, .parents = (struct clk_src[]){ {&dsi_pll0_vco_clk.c, 0}, {&dsi_pll0_indirect_path_div2_clk.c, 1}, }, .ops = &byte_mux_ops, .c = { .parent = &dsi_pll0_vco_clk.c, .dbg_name = "dsi_pll0_byte_mux", .ops = &byte_mux_clk_ops, CLK_INIT(dsi_pll0_byte_mux.c), }, }; static struct div_clk dsi_pll0_byte_clk_src = { .ops = &fixed_4div_ops, .data = { .min_div = 4, .max_div = 4, }, .c = { .parent = &dsi_pll0_byte_mux.c, .dbg_name = "dsi_pll0_byte_clk_src", .ops = &byte_clk_src_ops, CLK_INIT(dsi_pll0_byte_clk_src.c), }, }; /* DSI PLL1 clock structures */ static struct dsi_pll_vco_clk dsi_pll1_vco_clk = { .ref_clk_rate = 19200000, .min_rate = 350000000, .max_rate = 750000000, .pll_en_seq_cnt = 9, .pll_enable_seqs[0] = tsmc_dsi_pll_enable_seq_8916, .pll_enable_seqs[1] = tsmc_dsi_pll_enable_seq_8916, .pll_enable_seqs[2] = tsmc_dsi_pll_enable_seq_8916, .pll_enable_seqs[3] = gf_1_dsi_pll_enable_seq_8916, .pll_enable_seqs[4] = gf_1_dsi_pll_enable_seq_8916, .pll_enable_seqs[5] = gf_1_dsi_pll_enable_seq_8916, .pll_enable_seqs[6] = gf_2_dsi_pll_enable_seq_8916, .pll_enable_seqs[7] = gf_2_dsi_pll_enable_seq_8916, .pll_enable_seqs[8] = gf_2_dsi_pll_enable_seq_8916, .lpfr_lut_size = 10, .lpfr_lut = lpfr_lut_struct, .c = { .dbg_name = "dsi_pll1_vco_clk", .ops = &clk_ops_dsi_vco, CLK_INIT(dsi_pll1_vco_clk.c), }, }; static struct div_clk dsi_pll1_analog_postdiv_clk = { .data = { .max_div = 255, .min_div = 1, }, .ops = &analog_postdiv_ops, .c = { .parent = &dsi_pll1_vco_clk.c, .dbg_name = "dsi_pll1_analog_postdiv_clk", .ops = &analog_postdiv_clk_ops, .flags = CLKFLAG_NO_RATE_CACHE, CLK_INIT(dsi_pll1_analog_postdiv_clk.c), }, }; static struct div_clk dsi_pll1_indirect_path_div2_clk = { .ops = &fixed_2div_ops, .data = { .div = 2, .min_div = 2, .max_div = 2, }, .c = { .parent = &dsi_pll1_analog_postdiv_clk.c, .dbg_name = "dsi_pll1_indirect_path_div2_clk", .ops = &clk_ops_div, .flags = CLKFLAG_NO_RATE_CACHE, CLK_INIT(dsi_pll1_indirect_path_div2_clk.c), }, }; static struct div_clk dsi_pll1_pixel_clk_src = { .data = { .max_div = 255, .min_div = 1, }, .ops = &digital_postdiv_ops, .c = { .parent = &dsi_pll1_vco_clk.c, .dbg_name = "dsi_pll1_pixel_clk_src", .ops = &pixel_clk_src_ops, .flags = CLKFLAG_NO_RATE_CACHE, CLK_INIT(dsi_pll1_pixel_clk_src.c), }, }; static struct mux_clk dsi_pll1_byte_mux = { .num_parents = 2, .parents = (struct clk_src[]){ {&dsi_pll1_vco_clk.c, 0}, {&dsi_pll1_indirect_path_div2_clk.c, 1}, }, .ops = &byte_mux_ops, .c = { .parent = &dsi_pll1_vco_clk.c, .dbg_name = "dsi_pll1_byte_mux", .ops = &byte_mux_clk_ops, CLK_INIT(dsi_pll1_byte_mux.c), }, }; static struct div_clk dsi_pll1_byte_clk_src = { .ops = &fixed_4div_ops, .data = { .min_div = 4, .max_div = 4, }, .c = { .parent = &dsi_pll1_byte_mux.c, .dbg_name = "dsi_pll1_byte_clk_src", .ops = &byte_clk_src_ops, CLK_INIT(dsi_pll1_byte_clk_src.c), }, }; static struct clk_lookup dsi_pll0_cc[] = { CLK_LIST(dsi_pll0_pixel_clk_src), CLK_LIST(dsi_pll0_byte_clk_src), }; static struct clk_lookup dsi_pll1_cc[] = { CLK_LIST(dsi_pll1_pixel_clk_src), CLK_LIST(dsi_pll1_byte_clk_src), }; int dsi_pll_clock_register_lpm(struct platform_device *pdev, struct mdss_pll_resources *pll_res) { int rc; int const ssc_freq_min = 30000; /* min. recommended freq. value */ int const ssc_freq_max = 33000; /* max. recommended freq. value */ int const ssc_ppm_max = 5000; /* max. recommended ppm */ if (!pdev || !pdev->dev.of_node) { pr_err("Invalid input parameters\n"); return -EINVAL; } if (!pll_res || !pll_res->pll_base) { pr_err("Invalid PLL resources\n"); return -EPROBE_DEFER; } /* Set client data to mux, div and vco clocks */ if (!pll_res->index) { dsi_pll0_byte_clk_src.priv = pll_res; dsi_pll0_pixel_clk_src.priv = pll_res; dsi_pll0_byte_mux.priv = pll_res; dsi_pll0_indirect_path_div2_clk.priv = pll_res; dsi_pll0_analog_postdiv_clk.priv = pll_res; dsi_pll0_vco_clk.priv = pll_res; } else { dsi_pll1_byte_clk_src.priv = pll_res; dsi_pll1_pixel_clk_src.priv = pll_res; dsi_pll1_byte_mux.priv = pll_res; dsi_pll1_indirect_path_div2_clk.priv = pll_res; dsi_pll1_analog_postdiv_clk.priv = pll_res; dsi_pll1_vco_clk.priv = pll_res; } pll_res->vco_delay = VCO_DELAY_USEC; /* Set clock source operations */ pixel_clk_src_ops = clk_ops_slave_div; pixel_clk_src_ops.prepare = dsi_pll_div_prepare; analog_postdiv_clk_ops = clk_ops_div; analog_postdiv_clk_ops.prepare = dsi_pll_div_prepare; byte_clk_src_ops = clk_ops_div; byte_clk_src_ops.prepare = dsi_pll_div_prepare; byte_mux_clk_ops = clk_ops_gen_mux; byte_mux_clk_ops.prepare = dsi_pll_mux_prepare; if (pll_res->ssc_en) { if (!pll_res->ssc_freq || (pll_res->ssc_freq < ssc_freq_min) || (pll_res->ssc_freq > ssc_freq_max)) { pll_res->ssc_freq = ssc_freq_min; pr_debug("SSC frequency out of recommended range. Set to default=%d\n", pll_res->ssc_freq); } if (!pll_res->ssc_ppm || (pll_res->ssc_ppm > ssc_ppm_max)) { pll_res->ssc_ppm = ssc_ppm_max; pr_debug("SSC PPM out of recommended range. Set to default=%d\n", pll_res->ssc_ppm); } } if ((pll_res->target_id == MDSS_PLL_TARGET_8952) || (pll_res->target_id == MDSS_PLL_TARGET_8937)) { if (!pll_res->index) rc = of_msm_clock_register(pdev->dev.of_node, dsi_pll0_cc, ARRAY_SIZE(dsi_pll0_cc)); else rc = of_msm_clock_register(pdev->dev.of_node, dsi_pll1_cc, ARRAY_SIZE(dsi_pll1_cc)); if (rc) { pr_err("Clock register failed\n"); rc = -EPROBE_DEFER; } } else { pr_err("Invalid target ID\n"); rc = -EINVAL; } if (!rc) pr_info("Registered DSI PLL:%d clocks successfully\n", pll_res->index); return rc; }