// 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 #include #include #include "pll_drv.h" #include "dsi_pll.h" #define VCO_DELAY_USEC 1 static struct clk_div_ops fixed_2div_ops; static const struct clk_ops byte_mux_clk_ops; static const struct clk_ops pixel_clk_src_ops; static const struct clk_ops byte_clk_src_ops; static const 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 void dsi_pll_software_reset(struct mdss_pll_resources *dsi_pll_res) { /* * 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(1); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x00); udelay(1); } static int vco_set_rate_hpm(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; } rc = vco_set_rate(vco, rate); mdss_pll_resource_enable(dsi_pll_res, false); return rc; } static int dsi_pll_enable_seq_8974(struct mdss_pll_resources *dsi_pll_res) { int i, rc = 0; int pll_locked; dsi_pll_software_reset(dsi_pll_res); /* * PLL power up sequence. * Add necessary delays recommeded by hardware. */ MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01); udelay(1); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05); udelay(200); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07); udelay(500); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f); udelay(500); for (i = 0; i < 2; i++) { udelay(100); /* DSI Uniphy lock detect setting */ MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x0c); udelay(100); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x0d); pll_locked = dsi_pll_lock_status(dsi_pll_res); if (pll_locked) break; dsi_pll_software_reset(dsi_pll_res); /* * PLL power up sequence. * Add necessary delays recommeded by hardware. */ MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x1); udelay(1); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x5); udelay(200); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x7); udelay(250); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x5); udelay(200); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x7); udelay(500); MDSS_PLL_REG_W(dsi_pll_res->pll_base, DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0xf); udelay(500); } if (!pll_locked) { pr_err("DSI PLL lock failed\n"); rc = -EINVAL; } else { pr_debug("DSI PLL Lock success\n"); } return rc; } /* Op structures */ static const struct clk_ops clk_ops_dsi_vco = { .set_rate = vco_set_rate_hpm, .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, }; static struct dsi_pll_vco_clk dsi_vco_clk_8974 = { .ref_clk_rate = 19200000, .min_rate = 350000000, .max_rate = 750000000, .pll_en_seq_cnt = 3, .pll_enable_seqs[0] = dsi_pll_enable_seq_8974, .pll_enable_seqs[1] = dsi_pll_enable_seq_8974, .pll_enable_seqs[2] = dsi_pll_enable_seq_8974, .lpfr_lut_size = 10, .lpfr_lut = lpfr_lut_struct, .c = { .dbg_name = "dsi_vco_clk_8974", .ops = &clk_ops_dsi_vco, CLK_INIT(dsi_vco_clk_8974.c), }, }; static struct div_clk analog_postdiv_clk_8974 = { .data = { .max_div = 255, .min_div = 1, }, .ops = &analog_postdiv_ops, .c = { .parent = &dsi_vco_clk_8974.c, .dbg_name = "analog_postdiv_clk", .ops = &analog_postdiv_clk_ops, .flags = CLKFLAG_NO_RATE_CACHE, CLK_INIT(analog_postdiv_clk_8974.c), }, }; static struct div_clk indirect_path_div2_clk_8974 = { .ops = &fixed_2div_ops, .data = { .div = 2, .min_div = 2, .max_div = 2, }, .c = { .parent = &analog_postdiv_clk_8974.c, .dbg_name = "indirect_path_div2_clk", .ops = &clk_ops_div, .flags = CLKFLAG_NO_RATE_CACHE, CLK_INIT(indirect_path_div2_clk_8974.c), }, }; static struct div_clk pixel_clk_src_8974 = { .data = { .max_div = 255, .min_div = 1, }, .ops = &digital_postdiv_ops, .c = { .parent = &dsi_vco_clk_8974.c, .dbg_name = "pixel_clk_src_8974", .ops = &pixel_clk_src_ops, .flags = CLKFLAG_NO_RATE_CACHE, CLK_INIT(pixel_clk_src_8974.c), }, }; static struct mux_clk byte_mux_8974 = { .num_parents = 2, .parents = (struct clk_src[]){ {&dsi_vco_clk_8974.c, 0}, {&indirect_path_div2_clk_8974.c, 1}, }, .ops = &byte_mux_ops, .c = { .parent = &dsi_vco_clk_8974.c, .dbg_name = "byte_mux_8974", .ops = &byte_mux_clk_ops, CLK_INIT(byte_mux_8974.c), }, }; static struct div_clk byte_clk_src_8974 = { .ops = &fixed_4div_ops, .data = { .min_div = 4, .max_div = 4, }, .c = { .parent = &byte_mux_8974.c, .dbg_name = "byte_clk_src_8974", .ops = &byte_clk_src_ops, CLK_INIT(byte_clk_src_8974.c), }, }; static struct clk_lookup mdss_dsi_pllcc_8974[] = { CLK_LOOKUP_OF("pixel_src", pixel_clk_src_8974, "fd8c0000.qcom,mmsscc-mdss"), CLK_LOOKUP_OF("byte_src", byte_clk_src_8974, "fd8c0000.qcom,mmsscc-mdss"), }; int dsi_pll_clock_register_hpm(struct platform_device *pdev, struct mdss_pll_resources *pll_res) { int rc; /* Set client data to mux, div and vco clocks */ byte_clk_src_8974.priv = pll_res; pixel_clk_src_8974.priv = pll_res; byte_mux_8974.priv = pll_res; indirect_path_div2_clk_8974.priv = pll_res; analog_postdiv_clk_8974.priv = pll_res; dsi_vco_clk_8974.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->target_id == MDSS_PLL_TARGET_8974) { rc = of_msm_clock_register(pdev->dev.of_node, mdss_dsi_pllcc_8974, ARRAY_SIZE(mdss_dsi_pllcc_8974)); 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 clocks successfully\n"); return rc; }