// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015-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_14nm.h" #include #define VCO_DELAY_USEC 1 static struct dsi_pll_db pll_db[DSI_PLL_NUM]; static struct regmap_config dsi_pll_14nm_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, .max_register = 0x588, }; static struct regmap_bus post_n1_div_regmap_bus = { .reg_write = post_n1_div_set_div, .reg_read = post_n1_div_get_div, }; static struct regmap_bus n2_div_regmap_bus = { .reg_write = n2_div_set_div, .reg_read = n2_div_get_div, }; static struct regmap_bus shadow_n2_div_regmap_bus = { .reg_write = shadow_n2_div_set_div, .reg_read = n2_div_get_div, }; static struct regmap_bus dsi_mux_regmap_bus = { .reg_write = dsi_mux_set_parent_14nm, .reg_read = dsi_mux_get_parent_14nm, }; /* Op structures */ static const struct clk_ops clk_ops_dsi_vco = { .recalc_rate = pll_vco_recalc_rate_14nm, .set_rate = pll_vco_set_rate_14nm, .round_rate = pll_vco_round_rate_14nm, .prepare = pll_vco_prepare_14nm, .unprepare = pll_vco_unprepare_14nm, }; /* Shadow ops for dynamic refresh */ static const struct clk_ops clk_ops_shadow_dsi_vco = { .recalc_rate = pll_vco_recalc_rate_14nm, .set_rate = shadow_pll_vco_set_rate_14nm, .round_rate = pll_vco_round_rate_14nm, }; static struct dsi_pll_vco_clk dsi0pll_vco_clk = { .ref_clk_rate = 19200000UL, .min_rate = 1300000000UL, .max_rate = 2600000000UL, .pll_en_seq_cnt = 1, .pll_enable_seqs[0] = dsi_pll_enable_seq_14nm, .hw.init = &(struct clk_init_data){ .name = "dsi0pll_vco_clk_14nm", .parent_names = (const char *[]){ "bi_tcxo" }, .num_parents = 1, .ops = &clk_ops_dsi_vco, }, }; static struct dsi_pll_vco_clk dsi0pll_shadow_vco_clk = { .ref_clk_rate = 19200000u, .min_rate = 1300000000u, .max_rate = 2600000000u, .hw.init = &(struct clk_init_data){ .name = "dsi0pll_shadow_vco_clk_14nm", .parent_names = (const char *[]){ "bi_tcxo" }, .num_parents = 1, .ops = &clk_ops_shadow_dsi_vco, }, }; static struct dsi_pll_vco_clk dsi1pll_vco_clk = { .ref_clk_rate = 19200000UL, .min_rate = 1300000000UL, .max_rate = 2600000000UL, .pll_en_seq_cnt = 1, .pll_enable_seqs[0] = dsi_pll_enable_seq_14nm, .hw.init = &(struct clk_init_data){ .name = "dsi1pll_vco_clk_14nm", .parent_names = (const char *[]){ "bi_tcxo" }, .num_parents = 1, .ops = &clk_ops_dsi_vco, }, }; static struct dsi_pll_vco_clk dsi1pll_shadow_vco_clk = { .ref_clk_rate = 19200000u, .min_rate = 1300000000u, .max_rate = 2600000000u, .pll_en_seq_cnt = 1, .pll_enable_seqs[0] = dsi_pll_enable_seq_14nm, .hw.init = &(struct clk_init_data){ .name = "dsi1pll_shadow_vco_clk_14nm", .parent_names = (const char *[]){ "bi_tcxo" }, .num_parents = 1, .ops = &clk_ops_shadow_dsi_vco, }, }; static struct clk_regmap_div dsi0pll_post_n1_div_clk = { .reg = 0x48, .shift = 0, .width = 4, .clkr = { .hw.init = &(struct clk_init_data){ .name = "dsi0pll_post_n1_div_clk", .parent_names = (const char *[]){ "dsi0pll_vco_clk_14nm" }, .num_parents = 1, .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT), .ops = &clk_regmap_div_ops, }, }, }; static struct clk_regmap_div dsi0pll_shadow_post_n1_div_clk = { .reg = 0x48, .shift = 0, .width = 4, .clkr = { .hw.init = &(struct clk_init_data){ .name = "dsi0pll_shadow_post_n1_div_clk", .parent_names = (const char *[]){"dsi0pll_shadow_vco_clk_14nm"}, .num_parents = 1, .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT), .ops = &clk_regmap_div_ops, }, }, }; static struct clk_regmap_div dsi1pll_post_n1_div_clk = { .reg = 0x48, .shift = 0, .width = 4, .clkr = { .hw.init = &(struct clk_init_data){ .name = "dsi1pll_post_n1_div_clk", .parent_names = (const char *[]){ "dsi1pll_vco_clk_14nm" }, .num_parents = 1, .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT), .ops = &clk_regmap_div_ops, }, }, }; static struct clk_regmap_div dsi1pll_shadow_post_n1_div_clk = { .reg = 0x48, .shift = 0, .width = 4, .clkr = { .hw.init = &(struct clk_init_data){ .name = "dsi1pll_shadow_post_n1_div_clk", .parent_names = (const char *[]){"dsi1pll_shadow_vco_clk_14nm"}, .num_parents = 1, .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT), .ops = &clk_regmap_div_ops, }, }, }; static struct clk_regmap_div dsi0pll_n2_div_clk = { .reg = 0x48, .shift = 0, .width = 4, .clkr = { .hw.init = &(struct clk_init_data){ .name = "dsi0pll_n2_div_clk", .parent_names = (const char *[]){ "dsi0pll_post_n1_div_clk" }, .num_parents = 1, .flags = CLK_GET_RATE_NOCACHE, .ops = &clk_regmap_div_ops, }, }, }; static struct clk_regmap_div dsi0pll_shadow_n2_div_clk = { .reg = 0x48, .shift = 0, .width = 4, .clkr = { .hw.init = &(struct clk_init_data){ .name = "dsi0pll_shadow_n2_div_clk", .parent_names = (const char *[]){ "dsi0pll_shadow_post_n1_div_clk" }, .num_parents = 1, .flags = CLK_GET_RATE_NOCACHE, .ops = &clk_regmap_div_ops, }, }, }; static struct clk_regmap_div dsi1pll_n2_div_clk = { .reg = 0x48, .shift = 0, .width = 4, .clkr = { .hw.init = &(struct clk_init_data){ .name = "dsi1pll_n2_div_clk", .parent_names = (const char *[]){ "dsi1pll_post_n1_div_clk" }, .num_parents = 1, .flags = CLK_GET_RATE_NOCACHE, .ops = &clk_regmap_div_ops, }, }, }; static struct clk_regmap_div dsi1pll_shadow_n2_div_clk = { .reg = 0x48, .shift = 0, .width = 4, .clkr = { .hw.init = &(struct clk_init_data){ .name = "dsi1pll_shadow_n2_div_clk", .parent_names = (const char *[]){ "dsi1pll_shadow_post_n1_div_clk" }, .num_parents = 1, .flags = CLK_GET_RATE_NOCACHE, .ops = &clk_regmap_div_ops, }, }, }; static struct clk_fixed_factor dsi0pll_pixel_clk_src = { .div = 2, .mult = 1, .hw.init = &(struct clk_init_data){ .name = "dsi0pll_pixel_clk_src", .parent_names = (const char *[]){ "dsi0pll_n2_div_clk" }, .num_parents = 1, .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT), .ops = &clk_fixed_factor_ops, }, }; static struct clk_fixed_factor dsi0pll_shadow_pixel_clk_src = { .div = 2, .mult = 1, .hw.init = &(struct clk_init_data){ .name = "dsi0pll_shadow_pixel_clk_src", .parent_names = (const char *[]){ "dsi0pll_shadow_n2_div_clk" }, .num_parents = 1, .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT), .ops = &clk_fixed_factor_ops, }, }; static struct clk_fixed_factor dsi1pll_pixel_clk_src = { .div = 2, .mult = 1, .hw.init = &(struct clk_init_data){ .name = "dsi1pll_pixel_clk_src", .parent_names = (const char *[]){ "dsi1pll_n2_div_clk" }, .num_parents = 1, .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT), .ops = &clk_fixed_factor_ops, }, }; static struct clk_fixed_factor dsi1pll_shadow_pixel_clk_src = { .div = 2, .mult = 1, .hw.init = &(struct clk_init_data){ .name = "dsi1pll_shadow_pixel_clk_src", .parent_names = (const char *[]){ "dsi1pll_shadow_n2_div_clk" }, .num_parents = 1, .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT), .ops = &clk_fixed_factor_ops, }, }; static struct clk_regmap_mux dsi0pll_pixel_clk_mux = { .reg = 0x48, .shift = 0, .width = 1, .clkr = { .hw.init = &(struct clk_init_data){ .name = "dsi0_phy_pll_out_dsiclk", .parent_names = (const char *[]){ "dsi0pll_pixel_clk_src", "dsi0pll_shadow_pixel_clk_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_pixel_clk_mux = { .reg = 0x48, .shift = 0, .width = 1, .clkr = { .hw.init = &(struct clk_init_data){ .name = "dsi1pll_pixel_clk_mux", .parent_names = (const char *[]){ "dsi1pll_pixel_clk_src", "dsi1pll_shadow_pixel_clk_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_byte_clk_src = { .div = 8, .mult = 1, .hw.init = &(struct clk_init_data){ .name = "dsi0pll_byte_clk_src", .parent_names = (const char *[]){ "dsi0pll_post_n1_div_clk" }, .num_parents = 1, .flags = (CLK_SET_RATE_PARENT), .ops = &clk_fixed_factor_ops, }, }; static struct clk_fixed_factor dsi0pll_shadow_byte_clk_src = { .div = 8, .mult = 1, .hw.init = &(struct clk_init_data){ .name = "dsi0pll_shadow_byte_clk_src", .parent_names = (const char *[]){ "dsi0pll_shadow_post_n1_div_clk" }, .num_parents = 1, .flags = (CLK_SET_RATE_PARENT), .ops = &clk_fixed_factor_ops, }, }; static struct clk_fixed_factor dsi1pll_byte_clk_src = { .div = 8, .mult = 1, .hw.init = &(struct clk_init_data){ .name = "dsi1pll_byte_clk_src", .parent_names = (const char *[]){ "dsi1pll_post_n1_div_clk" }, .num_parents = 1, .flags = (CLK_SET_RATE_PARENT), .ops = &clk_fixed_factor_ops, }, }; static struct clk_fixed_factor dsi1pll_shadow_byte_clk_src = { .div = 8, .mult = 1, .hw.init = &(struct clk_init_data){ .name = "dsi1pll_shadow_byte_clk_src", .parent_names = (const char *[]){ "dsi1pll_shadow_post_n1_div_clk" }, .num_parents = 1, .flags = (CLK_SET_RATE_PARENT), .ops = &clk_fixed_factor_ops, }, }; static struct clk_regmap_mux dsi0pll_byte_clk_mux = { .reg = 0x48, .shift = 0, .width = 1, .clkr = { .hw.init = &(struct clk_init_data){ .name = "dsi0_phy_pll_out_byteclk", .parent_names = (const char *[]){"dsi0pll_byte_clk_src", "dsi0pll_shadow_byte_clk_src"}, .num_parents = 2, .ops = &clk_regmap_mux_closest_ops, .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT), }, }, }; static struct clk_regmap_mux dsi1pll_byte_clk_mux = { .reg = 0x48, .shift = 0, .width = 1, .clkr = { .hw.init = &(struct clk_init_data){ .name = "dsi1pll_byte_clk_mux", .parent_names = (const char *[]){"dsi1pll_byte_clk_src", "dsi1pll_shadow_byte_clk_src"}, .num_parents = 2, .ops = &clk_regmap_mux_closest_ops, .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT), }, }, }; static struct clk_hw *mdss_dsi_pllcc_14nm[] = { [BYTE0_MUX_CLK] = &dsi0pll_byte_clk_mux.clkr.hw, [BYTE0_SRC_CLK] = &dsi0pll_byte_clk_src.hw, [PIX0_MUX_CLK] = &dsi0pll_pixel_clk_mux.clkr.hw, [PIX0_SRC_CLK] = &dsi0pll_pixel_clk_src.hw, [N2_DIV_0_CLK] = &dsi0pll_n2_div_clk.clkr.hw, [POST_N1_DIV_0_CLK] = &dsi0pll_post_n1_div_clk.clkr.hw, [VCO_CLK_0_CLK] = &dsi0pll_vco_clk.hw, [SHADOW_BYTE0_SRC_CLK] = &dsi0pll_shadow_byte_clk_src.hw, [SHADOW_PIX0_SRC_CLK] = &dsi0pll_shadow_pixel_clk_src.hw, [SHADOW_N2_DIV_0_CLK] = &dsi0pll_shadow_n2_div_clk.clkr.hw, [SHADOW_POST_N1_DIV_0_CLK] = &dsi0pll_shadow_post_n1_div_clk.clkr.hw, [SHADOW_VCO_CLK_0_CLK] = &dsi0pll_shadow_vco_clk.hw, [BYTE1_MUX_CLK] = &dsi1pll_byte_clk_mux.clkr.hw, [BYTE1_SRC_CLK] = &dsi1pll_byte_clk_src.hw, [PIX1_MUX_CLK] = &dsi1pll_pixel_clk_mux.clkr.hw, [PIX1_SRC_CLK] = &dsi1pll_pixel_clk_src.hw, [N2_DIV_1_CLK] = &dsi1pll_n2_div_clk.clkr.hw, [POST_N1_DIV_1_CLK] = &dsi1pll_post_n1_div_clk.clkr.hw, [VCO_CLK_1_CLK] = &dsi1pll_vco_clk.hw, [SHADOW_BYTE1_SRC_CLK] = &dsi1pll_shadow_byte_clk_src.hw, [SHADOW_PIX1_SRC_CLK] = &dsi1pll_shadow_pixel_clk_src.hw, [SHADOW_N2_DIV_1_CLK] = &dsi1pll_shadow_n2_div_clk.clkr.hw, [SHADOW_POST_N1_DIV_1_CLK] = &dsi1pll_shadow_post_n1_div_clk.clkr.hw, [SHADOW_VCO_CLK_1_CLK] = &dsi1pll_shadow_vco_clk.hw, }; int dsi_pll_clock_register_14nm(struct platform_device *pdev, struct mdss_pll_resources *pll_res) { int rc = 0, ndx, i; int const ssc_freq_default = 31500; /* default h/w recommended value */ int const ssc_ppm_default = 5000; /* default h/w recommended value */ struct dsi_pll_db *pdb; struct clk_onecell_data *clk_data; struct clk *clk; struct regmap *regmap; int num_clks = ARRAY_SIZE(mdss_dsi_pllcc_14nm); if (pll_res->index >= DSI_PLL_NUM) { pr_err("pll ndx=%d is NOT supported\n", pll_res->index); return -EINVAL; } ndx = pll_res->index; pdb = &pll_db[ndx]; pll_res->priv = pdb; pdb->pll = pll_res; ndx++; ndx %= DSI_PLL_NUM; pdb->next = &pll_db[ndx]; if (pll_res->ssc_en) { if (!pll_res->ssc_freq) pll_res->ssc_freq = ssc_freq_default; if (!pll_res->ssc_ppm) pll_res->ssc_ppm = ssc_ppm_default; } 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; /* Set client data to mux, div and vco clocks. */ if (pll_res->index == DSI_PLL_1) { regmap = devm_regmap_init(&pdev->dev, &post_n1_div_regmap_bus, pll_res, &dsi_pll_14nm_config); dsi1pll_post_n1_div_clk.clkr.regmap = regmap; dsi1pll_shadow_post_n1_div_clk.clkr.regmap = regmap; regmap = devm_regmap_init(&pdev->dev, &n2_div_regmap_bus, pll_res, &dsi_pll_14nm_config); dsi1pll_n2_div_clk.clkr.regmap = regmap; regmap = devm_regmap_init(&pdev->dev, &shadow_n2_div_regmap_bus, pll_res, &dsi_pll_14nm_config); dsi1pll_shadow_n2_div_clk.clkr.regmap = regmap; regmap = devm_regmap_init(&pdev->dev, &dsi_mux_regmap_bus, pll_res, &dsi_pll_14nm_config); dsi1pll_byte_clk_mux.clkr.regmap = regmap; dsi1pll_pixel_clk_mux.clkr.regmap = regmap; dsi1pll_vco_clk.priv = pll_res; dsi1pll_shadow_vco_clk.priv = pll_res; pll_res->vco_delay = VCO_DELAY_USEC; for (i = BYTE1_MUX_CLK; i <= SHADOW_VCO_CLK_1_CLK; i++) { pr_debug("register clk: %d index: %d\n", i, pll_res->index); clk = devm_clk_register(&pdev->dev, mdss_dsi_pllcc_14nm[i]); if (IS_ERR(clk)) { pr_err("clk registration failed for DSI: %d\n", pll_res->index); rc = -EINVAL; goto clk_reg_fail; } clk_data->clks[i] = clk; } rc = of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get, clk_data); } else { regmap = devm_regmap_init(&pdev->dev, &post_n1_div_regmap_bus, pll_res, &dsi_pll_14nm_config); dsi0pll_post_n1_div_clk.clkr.regmap = regmap; dsi0pll_shadow_post_n1_div_clk.clkr.regmap = regmap; regmap = devm_regmap_init(&pdev->dev, &n2_div_regmap_bus, pll_res, &dsi_pll_14nm_config); dsi0pll_n2_div_clk.clkr.regmap = regmap; regmap = devm_regmap_init(&pdev->dev, &shadow_n2_div_regmap_bus, pll_res, &dsi_pll_14nm_config); dsi0pll_shadow_n2_div_clk.clkr.regmap = regmap; regmap = devm_regmap_init(&pdev->dev, &dsi_mux_regmap_bus, pll_res, &dsi_pll_14nm_config); dsi0pll_byte_clk_mux.clkr.regmap = regmap; dsi0pll_pixel_clk_mux.clkr.regmap = regmap; dsi0pll_vco_clk.priv = pll_res; dsi0pll_shadow_vco_clk.priv = pll_res; pll_res->vco_delay = VCO_DELAY_USEC; for (i = BYTE0_MUX_CLK; i <= SHADOW_VCO_CLK_0_CLK; i++) { pr_debug("reg clk: %d index: %d\n", i, pll_res->index); clk = devm_clk_register(&pdev->dev, mdss_dsi_pllcc_14nm[i]); if (IS_ERR(clk)) { pr_err("clk registration failed for DSI: %d\n", pll_res->index); rc = -EINVAL; goto clk_reg_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", pll_res->index); return rc; } clk_reg_fail: return rc; }