// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "%s: " fmt, __func__ #include #include #include #include #include "pll_drv.h" #include "dsi_pll.h" #include "dsi_pll_28nm.h" #define VCO_DELAY_USEC 1000 enum { DSI_PLL_0, DSI_PLL_1, DSI_PLL_MAX }; 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 void dsi_pll_sw_reset(struct mdss_pll_resources *rsc) { /* * DSI PLL software reset. Add HW recommended delays after toggling * the software reset bit off and back on. */ MDSS_PLL_REG_W(rsc->pll_base, DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x01); ndelay(500); MDSS_PLL_REG_W(rsc->pll_base, DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x00); } static void dsi_pll_toggle_lock_detect( struct mdss_pll_resources *rsc) { /* DSI PLL toggle lock detect setting */ MDSS_PLL_REG_W(rsc->pll_base, DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x04); ndelay(500); MDSS_PLL_REG_W(rsc->pll_base, DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x05); udelay(512); } static int dsi_pll_check_lock_status( struct mdss_pll_resources *rsc) { int rc = 0; rc = dsi_pll_lock_status(rsc); if (rc) pr_debug("PLL Locked\n"); else pr_err("PLL failed to lock\n"); return rc; } static int dsi_pll_enable_seq_gf2(struct mdss_pll_resources *rsc) { int pll_locked = 0; dsi_pll_sw_reset(rsc); /* * GF PART 2 PLL power up sequence. * Add necessary delays recommended by hardware. */ MDSS_PLL_REG_W(rsc->pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x04); MDSS_PLL_REG_W(rsc->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01); MDSS_PLL_REG_W(rsc->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05); udelay(3); MDSS_PLL_REG_W(rsc->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f); udelay(500); dsi_pll_toggle_lock_detect(rsc); pll_locked = dsi_pll_check_lock_status(rsc); return pll_locked ? 0 : -EINVAL; } static int dsi_pll_enable_seq_gf1(struct mdss_pll_resources *rsc) { int pll_locked = 0; dsi_pll_sw_reset(rsc); /* * GF PART 1 PLL power up sequence. * Add necessary delays recommended by hardware. */ MDSS_PLL_REG_W(rsc->pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x14); MDSS_PLL_REG_W(rsc->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01); MDSS_PLL_REG_W(rsc->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05); udelay(3); MDSS_PLL_REG_W(rsc->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f); udelay(500); dsi_pll_toggle_lock_detect(rsc); pll_locked = dsi_pll_check_lock_status(rsc); return pll_locked ? 0 : -EINVAL; } static int dsi_pll_enable_seq_tsmc(struct mdss_pll_resources *rsc) { int pll_locked = 0; dsi_pll_sw_reset(rsc); /* * TSMC PLL power up sequence. * Add necessary delays recommended by hardware. */ MDSS_PLL_REG_W(rsc->pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x34); MDSS_PLL_REG_W(rsc->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01); MDSS_PLL_REG_W(rsc->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05); MDSS_PLL_REG_W(rsc->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f); udelay(500); dsi_pll_toggle_lock_detect(rsc); pll_locked = dsi_pll_check_lock_status(rsc); return pll_locked ? 0 : -EINVAL; } static struct regmap_config dsi_pll_28lpm_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, .max_register = 0xF4, }; static struct regmap_bus analog_postdiv_regmap_bus = { .reg_write = analog_postdiv_reg_write, .reg_read = analog_postdiv_reg_read, }; static struct regmap_bus byteclk_src_mux_regmap_bus = { .reg_write = byteclk_mux_write_sel, .reg_read = byteclk_mux_read_sel, }; static struct regmap_bus pclk_src_regmap_bus = { .reg_write = pixel_clk_set_div, .reg_read = pixel_clk_get_div, }; static const struct clk_ops clk_ops_vco_28lpm = { .recalc_rate = vco_28nm_recalc_rate, .set_rate = vco_28nm_set_rate, .round_rate = vco_28nm_round_rate, .prepare = vco_28nm_prepare, .unprepare = vco_28nm_unprepare, }; static struct dsi_pll_vco_clk dsi0pll_vco_clk = { .ref_clk_rate = 19200000UL, .min_rate = 350000000UL, .max_rate = 750000000UL, .pll_en_seq_cnt = 9, .pll_enable_seqs[0] = dsi_pll_enable_seq_tsmc, .pll_enable_seqs[1] = dsi_pll_enable_seq_tsmc, .pll_enable_seqs[2] = dsi_pll_enable_seq_tsmc, .pll_enable_seqs[3] = dsi_pll_enable_seq_gf1, .pll_enable_seqs[4] = dsi_pll_enable_seq_gf1, .pll_enable_seqs[5] = dsi_pll_enable_seq_gf1, .pll_enable_seqs[6] = dsi_pll_enable_seq_gf2, .pll_enable_seqs[7] = dsi_pll_enable_seq_gf2, .pll_enable_seqs[8] = dsi_pll_enable_seq_gf2, .lpfr_lut_size = 10, .lpfr_lut = lpfr_lut_struct, .hw.init = &(struct clk_init_data){ .name = "dsi0pll_vco_clk", .parent_names = (const char *[]){"cxo"}, .num_parents = 1, .ops = &clk_ops_vco_28lpm, .flags = CLK_GET_RATE_NOCACHE, }, }; static struct dsi_pll_vco_clk dsi1pll_vco_clk = { .ref_clk_rate = 19200000UL, .min_rate = 350000000UL, .max_rate = 750000000UL, .pll_en_seq_cnt = 9, .pll_enable_seqs[0] = dsi_pll_enable_seq_tsmc, .pll_enable_seqs[1] = dsi_pll_enable_seq_tsmc, .pll_enable_seqs[2] = dsi_pll_enable_seq_tsmc, .pll_enable_seqs[3] = dsi_pll_enable_seq_gf1, .pll_enable_seqs[4] = dsi_pll_enable_seq_gf1, .pll_enable_seqs[5] = dsi_pll_enable_seq_gf1, .pll_enable_seqs[6] = dsi_pll_enable_seq_gf2, .pll_enable_seqs[7] = dsi_pll_enable_seq_gf2, .pll_enable_seqs[8] = dsi_pll_enable_seq_gf2, .lpfr_lut_size = 10, .lpfr_lut = lpfr_lut_struct, .hw.init = &(struct clk_init_data){ .name = "dsi1pll_vco_clk", .parent_names = (const char *[]){"cxo"}, .num_parents = 1, .ops = &clk_ops_vco_28lpm, .flags = CLK_GET_RATE_NOCACHE, }, }; static struct clk_regmap_div dsi0pll_analog_postdiv = { .reg = DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG, .shift = 0, .width = 4, .clkr = { .hw.init = &(struct clk_init_data){ .name = "dsi0pll_analog_postdiv", .parent_names = (const char *[]){"dsi0pll_vco_clk"}, .num_parents = 1, .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT), .ops = &clk_regmap_div_ops, }, }, }; static struct clk_regmap_div dsi1pll_analog_postdiv = { .reg = DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG, .shift = 0, .width = 4, .clkr = { .hw.init = &(struct clk_init_data){ .name = "dsi1pll_analog_postdiv", .parent_names = (const char *[]){"dsi1pll_vco_clk"}, .num_parents = 1, .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT), .ops = &clk_regmap_div_ops, }, }, }; static struct clk_fixed_factor dsi0pll_indirect_path_src = { .div = 2, .mult = 1, .hw.init = &(struct clk_init_data){ .name = "dsi0pll_indirect_path_src", .parent_names = (const char *[]){"dsi0pll_analog_postdiv"}, .num_parents = 1, .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT), .ops = &clk_fixed_factor_ops, }, }; static struct clk_fixed_factor dsi1pll_indirect_path_src = { .div = 2, .mult = 1, .hw.init = &(struct clk_init_data){ .name = "dsi1pll_indirect_path_src", .parent_names = (const char *[]){"dsi1pll_analog_postdiv"}, .num_parents = 1, .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT), .ops = &clk_fixed_factor_ops, }, }; static struct clk_regmap_mux dsi0pll_byteclk_src_mux = { .reg = DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG, .shift = 1, .width = 1, .clkr = { .hw.init = &(struct clk_init_data){ .name = "dsi0pll_byteclk_src_mux", .parent_names = (const char *[]){ "dsi0pll_vco_clk", "dsi0pll_indirect_path_src"}, .num_parents = 2, .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT), .ops = &clk_regmap_mux_closest_ops, }, }, }; static struct clk_regmap_mux dsi1pll_byteclk_src_mux = { .reg = DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG, .shift = 1, .width = 1, .clkr = { .hw.init = &(struct clk_init_data){ .name = "dsi1pll_byteclk_src_mux", .parent_names = (const char *[]){ "dsi1pll_vco_clk", "dsi1pll_indirect_path_src"}, .num_parents = 2, .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT), .ops = &clk_regmap_mux_closest_ops, }, }, }; static struct clk_fixed_factor dsi0pll_byteclk_src = { .div = 4, .mult = 1, .hw.init = &(struct clk_init_data){ .name = "dsi0pll_byteclk_src", .parent_names = (const char *[]){ "dsi0pll_byteclk_src_mux"}, .num_parents = 1, .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT), .ops = &clk_fixed_factor_ops, }, }; static struct clk_fixed_factor dsi1pll_byteclk_src = { .div = 4, .mult = 1, .hw.init = &(struct clk_init_data){ .name = "dsi1pll_byteclk_src", .parent_names = (const char *[]){ "dsi1pll_byteclk_src_mux"}, .num_parents = 1, .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT), .ops = &clk_fixed_factor_ops, }, }; static struct clk_regmap_div dsi0pll_pclk_src = { .reg = DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG, .shift = 0, .width = 8, .clkr = { .hw.init = &(struct clk_init_data){ .name = "dsi0pll_pclk_src", .parent_names = (const char *[]){"dsi0pll_vco_clk"}, .num_parents = 1, .flags = CLK_GET_RATE_NOCACHE, .ops = &clk_regmap_div_ops, }, }, }; static struct clk_regmap_div dsi1pll_pclk_src = { .reg = DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG, .shift = 0, .width = 8, .clkr = { .hw.init = &(struct clk_init_data){ .name = "dsi1pll_pclk_src", .parent_names = (const char *[]){"dsi1pll_vco_clk"}, .num_parents = 1, .flags = CLK_GET_RATE_NOCACHE, .ops = &clk_regmap_div_ops, }, }, }; static struct clk_hw *mdss_dsi_pllcc_28lpm[] = { [VCO_CLK_0] = &dsi0pll_vco_clk.hw, [ANALOG_POSTDIV_0_CLK] = &dsi0pll_analog_postdiv.clkr.hw, [INDIRECT_PATH_SRC_0_CLK] = &dsi0pll_indirect_path_src.hw, [BYTECLK_SRC_MUX_0_CLK] = &dsi0pll_byteclk_src_mux.clkr.hw, [BYTECLK_SRC_0_CLK] = &dsi0pll_byteclk_src.hw, [PCLK_SRC_0_CLK] = &dsi0pll_pclk_src.clkr.hw, [VCO_CLK_1] = &dsi1pll_vco_clk.hw, [ANALOG_POSTDIV_1_CLK] = &dsi1pll_analog_postdiv.clkr.hw, [INDIRECT_PATH_SRC_1_CLK] = &dsi1pll_indirect_path_src.hw, [BYTECLK_SRC_MUX_1_CLK] = &dsi1pll_byteclk_src_mux.clkr.hw, [BYTECLK_SRC_1_CLK] = &dsi1pll_byteclk_src.hw, [PCLK_SRC_1_CLK] = &dsi1pll_pclk_src.clkr.hw, }; int dsi_pll_clock_register_28lpm(struct platform_device *pdev, struct mdss_pll_resources *pll_res) { int rc = 0, ndx, i; struct clk *clk; struct clk_onecell_data *clk_data; int num_clks = ARRAY_SIZE(mdss_dsi_pllcc_28lpm); struct regmap *rmap; 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 */ ndx = pll_res->index; if (ndx >= DSI_PLL_MAX) { pr_err("pll index(%d) NOT supported\n", ndx); return -EINVAL; } pll_res->vco_delay = VCO_DELAY_USEC; 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); } } clk_data = devm_kzalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL); if (!clk_data) return -ENOMEM; clk_data->clks = devm_kcalloc(&pdev->dev, num_clks, sizeof(struct clk *), GFP_KERNEL); if (!clk_data->clks) return -ENOMEM; clk_data->clk_num = num_clks; /* Establish client data */ if (ndx == 0) { rmap = devm_regmap_init(&pdev->dev, &byteclk_src_mux_regmap_bus, pll_res, &dsi_pll_28lpm_config); if (IS_ERR(rmap)) { pr_err("regmap init failed for DSI clock:%d\n", pll_res->index); return -EINVAL; } dsi0pll_byteclk_src_mux.clkr.regmap = rmap; rmap = devm_regmap_init(&pdev->dev, &analog_postdiv_regmap_bus, pll_res, &dsi_pll_28lpm_config); if (IS_ERR(rmap)) { pr_err("regmap init failed for DSI clock:%d\n", pll_res->index); return -EINVAL; } dsi0pll_analog_postdiv.clkr.regmap = rmap; rmap = devm_regmap_init(&pdev->dev, &pclk_src_regmap_bus, pll_res, &dsi_pll_28lpm_config); if (IS_ERR(rmap)) { pr_err("regmap init failed for DSI clock:%d\n", pll_res->index); return -EINVAL; } dsi0pll_pclk_src.clkr.regmap = rmap; dsi0pll_vco_clk.priv = pll_res; for (i = VCO_CLK_0; i <= PCLK_SRC_0_CLK; i++) { clk = devm_clk_register(&pdev->dev, mdss_dsi_pllcc_28lpm[i]); if (IS_ERR(clk)) { pr_err("clk registration failed for DSI clock:%d\n", pll_res->index); rc = -EINVAL; goto clk_register_fail; } clk_data->clks[i] = clk; } rc = of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get, clk_data); } else { rmap = devm_regmap_init(&pdev->dev, &byteclk_src_mux_regmap_bus, pll_res, &dsi_pll_28lpm_config); if (IS_ERR(rmap)) { pr_err("regmap init failed for DSI clock:%d\n", pll_res->index); return -EINVAL; } dsi1pll_byteclk_src_mux.clkr.regmap = rmap; rmap = devm_regmap_init(&pdev->dev, &analog_postdiv_regmap_bus, pll_res, &dsi_pll_28lpm_config); if (IS_ERR(rmap)) { pr_err("regmap init failed for DSI clock:%d\n", pll_res->index); return -EINVAL; } dsi1pll_analog_postdiv.clkr.regmap = rmap; rmap = devm_regmap_init(&pdev->dev, &pclk_src_regmap_bus, pll_res, &dsi_pll_28lpm_config); if (IS_ERR(rmap)) { pr_err("regmap init failed for DSI clock:%d\n", pll_res->index); return -EINVAL; } dsi1pll_pclk_src.clkr.regmap = rmap; dsi1pll_vco_clk.priv = pll_res; for (i = VCO_CLK_1; i <= PCLK_SRC_1_CLK; i++) { clk = devm_clk_register(&pdev->dev, mdss_dsi_pllcc_28lpm[i]); if (IS_ERR(clk)) { pr_err("clk registration failed for DSI clock:%d\n", pll_res->index); rc = -EINVAL; goto clk_register_fail; } clk_data->clks[i] = clk; } rc = of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get, clk_data); } if (!rc) { pr_info("Registered DSI PLL ndx=%d, clocks successfully\n", ndx); return rc; } clk_register_fail: return rc; }