diff options
| -rw-r--r-- | Documentation/devicetree/bindings/fb/mdss-pll.txt | 68 | ||||
| -rw-r--r-- | drivers/clk/msm/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/clk/msm/Makefile | 1 | ||||
| -rw-r--r-- | drivers/clk/msm/mdss/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/clk/msm/mdss/Makefile | 5 | ||||
| -rw-r--r-- | drivers/clk/msm/mdss/mdss-dsi-pll-28hpm.c | 880 | ||||
| -rw-r--r-- | drivers/clk/msm/mdss/mdss-dsi-pll.h | 40 | ||||
| -rw-r--r-- | drivers/clk/msm/mdss/mdss-edp-pll-28hpm.c | 585 | ||||
| -rw-r--r-- | drivers/clk/msm/mdss/mdss-edp-pll.h | 27 | ||||
| -rw-r--r-- | drivers/clk/msm/mdss/mdss-hdmi-pll-28hpm.c | 1094 | ||||
| -rw-r--r-- | drivers/clk/msm/mdss/mdss-hdmi-pll.h | 29 | ||||
| -rw-r--r-- | drivers/clk/msm/mdss/mdss-pll-util.c | 337 | ||||
| -rw-r--r-- | drivers/clk/msm/mdss/mdss-pll.c | 325 | ||||
| -rw-r--r-- | drivers/clk/msm/mdss/mdss-pll.h | 89 |
14 files changed, 3488 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/fb/mdss-pll.txt b/Documentation/devicetree/bindings/fb/mdss-pll.txt new file mode 100644 index 000000000000..96f1aeed47f5 --- /dev/null +++ b/Documentation/devicetree/bindings/fb/mdss-pll.txt @@ -0,0 +1,68 @@ +Qualcomm MDSS pll for DSI/EDP/HDMI + +mdss-pll is a pll controller device which supports pll devices that +are compatable with MIPI display serial interface specification, +HDMI and edp. + +Required properties: +- compatible: Compatible name used in the driver +- cell-index: Specifies the controller used +- reg: offset and length of the register set for the device. +- reg-names : names to refer to register sets related to this device +- gdsc-supply: Phandle for gdsc regulator device node. +- vddio-supply: Phandle for vddio regulator device node. +- clock-names: List of clock names needed by the device. +- clock-rate: List of clock rates in Hz. + +Optional properties: +- label: A string used to describe the driver used. + +- qcom,platform-supply-entries: A node that lists the elements of the supply. There + can be more than one instance of this binding, + in which case the entry would be appended with + the supply entry index. + e.g. qcom,platform-supply-entry@0 + - reg: offset and length of the register set for the device. + -- qcom,supply-name: name of the supply (vdd/vdda/vddio) + -- qcom,supply-min-voltage: minimum voltage level (uV) + -- qcom,supply-max-voltage: maximum voltage level (uV) + -- qcom,supply-enable-load: load drawn (uA) from enabled supply + -- qcom,supply-disable-load: load drawn (uA) from disabled supply + -- qcom,supply-pre-on-sleep: time to sleep (ms) before turning on + -- qcom,supply-post-on-sleep: time to sleep (ms) after turning on + -- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off + -- qcom,supply-post-off-sleep: time to sleep (ms) after turning off + +Example: + mdss_dsi0_pll: qcom,mdss_dsi_pll@fd922A00 { + compatible = "qcom,mdss_dsi_pll"; + label = "MDSS DSI 0 PLL"; + cell-index = <0>; + + reg = <0xfd922A00 0xD4>; + reg-names = "pll_base"; + gdsc-supply = <&gdsc_mdss>; + vddio-supply = <&pm8941_l12>; + + clock-names = "mdp_core_clk", "iface_clk", "bus_clk"; + clock-rate = <0>, <0>, <0>; + + qcom,platform-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,platform-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vddio"; + qcom,supply-min-voltage = <1800000>; + qcom,supply-max-voltage = <1800000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + qcom,supply-pre-on-sleep = <0>; + qcom,supply-post-on-sleep = <20>; + qcom,supply-pre-off-sleep = <0>; + qcom,supply-post-off-sleep = <0>; + }; + }; + }; + diff --git a/drivers/clk/msm/Kconfig b/drivers/clk/msm/Kconfig index 7928fc7cfd85..17102dd1b3d1 100644 --- a/drivers/clk/msm/Kconfig +++ b/drivers/clk/msm/Kconfig @@ -3,3 +3,5 @@ config MSM_CLK_CONTROLLER_V2 ---help--- Generate clock data structures from definitions found in device tree. + +source "drivers/clk/msm/mdss/Kconfig" diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile index 5f6b59189236..ae6f5d58c142 100644 --- a/drivers/clk/msm/Makefile +++ b/drivers/clk/msm/Makefile @@ -23,3 +23,4 @@ obj-$(CONFIG_ARCH_QCOM) += clock-gcc-cobalt.o # CPU clocks obj-y += gdsc.o +obj-y += mdss/ diff --git a/drivers/clk/msm/mdss/Kconfig b/drivers/clk/msm/mdss/Kconfig new file mode 100644 index 000000000000..229780e45bb8 --- /dev/null +++ b/drivers/clk/msm/mdss/Kconfig @@ -0,0 +1,6 @@ +config MSM_MDSS_PLL + bool "MDSS pll programming" + ---help--- + It provides support for DSI, eDP and HDMI interface pll programming on MDSS + hardware. It also handles the pll specific resources and turn them on/off when + mdss pll client tries to enable/disable pll clocks. diff --git a/drivers/clk/msm/mdss/Makefile b/drivers/clk/msm/mdss/Makefile new file mode 100644 index 000000000000..25fb1d1e86f9 --- /dev/null +++ b/drivers/clk/msm/mdss/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_MSM_MDSS_PLL) += mdss-pll-util.o +obj-$(CONFIG_MSM_MDSS_PLL) += mdss-pll.o +obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-28hpm.o +obj-$(CONFIG_MSM_MDSS_PLL) += mdss-edp-pll-28hpm.o +obj-$(CONFIG_MSM_MDSS_PLL) += mdss-hdmi-pll-28hpm.o diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-28hpm.c b/drivers/clk/msm/mdss/mdss-dsi-pll-28hpm.c new file mode 100644 index 000000000000..685c0863f7f1 --- /dev/null +++ b/drivers/clk/msm/mdss/mdss-dsi-pll-28hpm.c @@ -0,0 +1,880 @@ +/* Copyright (c) 2012-2014, 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 <linux/kernel.h> +#include <linux/err.h> +#include <linux/iopoll.h> +#include <linux/delay.h> +#include <linux/clk/msm-clk-provider.h> +#include <linux/clk/msm-clk.h> +#include <linux/clk/msm-clock-generic.h> + +#include <dt-bindings/clock/msm-clocks-8974.h> + +#include "mdss-pll.h" +#include "mdss-dsi-pll.h" + +#define DSI_PHY_PLL_UNIPHY_PLL_REFCLK_CFG (0x0) +#define DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG (0x0004) +#define DSI_PHY_PLL_UNIPHY_PLL_CHGPUMP_CFG (0x0008) +#define DSI_PHY_PLL_UNIPHY_PLL_VCOLPF_CFG (0x000C) +#define DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG (0x0010) +#define DSI_PHY_PLL_UNIPHY_PLL_PWRGEN_CFG (0x0014) +#define DSI_PHY_PLL_UNIPHY_PLL_DMUX_CFG (0x0018) +#define DSI_PHY_PLL_UNIPHY_PLL_AMUX_CFG (0x001C) +#define DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG (0x0020) +#define DSI_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG (0x0024) +#define DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG (0x0028) +#define DSI_PHY_PLL_UNIPHY_PLL_LPFR_CFG (0x002C) +#define DSI_PHY_PLL_UNIPHY_PLL_LPFC1_CFG (0x0030) +#define DSI_PHY_PLL_UNIPHY_PLL_LPFC2_CFG (0x0034) +#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0 (0x0038) +#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1 (0x003C) +#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2 (0x0040) +#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3 (0x0044) +#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG4 (0x0048) +#define DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG0 (0x004C) +#define DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG1 (0x0050) +#define DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG2 (0x0054) +#define DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG3 (0x0058) +#define DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG0 (0x005C) +#define DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG1 (0x0060) +#define DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2 (0x0064) +#define DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG (0x0068) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG0 (0x006C) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1 (0x0070) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG2 (0x0074) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG3 (0x0078) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG4 (0x007C) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG5 (0x0080) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG6 (0x0084) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG7 (0x0088) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG8 (0x008C) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG9 (0x0090) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG10 (0x0094) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG11 (0x0098) +#define DSI_PHY_PLL_UNIPHY_PLL_EFUSE_CFG (0x009C) +#define DSI_PHY_PLL_UNIPHY_PLL_STATUS (0x00C0) + + +#define DSI_PLL_POLL_MAX_READS 10 +#define DSI_PLL_POLL_TIMEOUT_US 50 + +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; + +int set_byte_mux_sel(struct mux_clk *clk, int sel) +{ + int rc; + struct mdss_pll_resources *dsi_pll_res = clk->priv; + + rc = mdss_pll_resource_enable(dsi_pll_res, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + pr_debug("byte mux set to %s mode\n", sel ? "indirect" : "direct"); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG, (sel << 1)); + + mdss_pll_resource_enable(dsi_pll_res, false); + return 0; +} + +int get_byte_mux_sel(struct mux_clk *clk) +{ + int mux_mode, rc; + struct mdss_pll_resources *dsi_pll_res = clk->priv; + + rc = mdss_pll_resource_enable(dsi_pll_res, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + mux_mode = MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG) & BIT(1); + + pr_debug("byte mux mode = %s", mux_mode ? "indirect" : "direct"); + mdss_pll_resource_enable(dsi_pll_res, false); + + return !!mux_mode; +} + +static inline struct dsi_pll_vco_clk *to_vco_clk(struct clk *clk) +{ + return container_of(clk, struct dsi_pll_vco_clk, c); +} + +int dsi_pll_div_prepare(struct clk *c) +{ + struct div_clk *div = to_div_clk(c); + /* Restore the divider's value */ + return div->ops->set_div(div, div->data.div); +} + +int dsi_pll_mux_prepare(struct clk *c) +{ + struct mux_clk *mux = to_mux_clk(c); + int i, rc, sel = 0; + + for (i = 0; i < mux->num_parents; i++) + if (mux->parents[i].src == c->parent) { + sel = mux->parents[i].sel; + break; + } + + if (i == mux->num_parents) { + pr_err("Failed to select the parent clock\n"); + rc = -EINVAL; + goto error; + } + + /* Restore the mux source select value */ + rc = mux->ops->set_mux_sel(mux, sel); + +error: + return rc; +} + +static int fixed_4div_set_div(struct div_clk *clk, int div) +{ + int rc; + struct mdss_pll_resources *dsi_pll_res = clk->priv; + + rc = mdss_pll_resource_enable(dsi_pll_res, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG, (div - 1)); + + mdss_pll_resource_enable(dsi_pll_res, false); + return rc; +} + +static int fixed_4div_get_div(struct div_clk *clk) +{ + int div = 0, rc; + struct mdss_pll_resources *dsi_pll_res = clk->priv; + + rc = mdss_pll_resource_enable(dsi_pll_res, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + div = MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG); + + mdss_pll_resource_enable(dsi_pll_res, false); + return div + 1; +} + +static int digital_set_div(struct div_clk *clk, int div) +{ + int rc; + struct mdss_pll_resources *dsi_pll_res = clk->priv; + + rc = mdss_pll_resource_enable(dsi_pll_res, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG, (div - 1)); + + mdss_pll_resource_enable(dsi_pll_res, false); + return rc; +} + +static int digital_get_div(struct div_clk *clk) +{ + int div = 0, rc; + struct mdss_pll_resources *dsi_pll_res = clk->priv; + + rc = mdss_pll_resource_enable(dsi_pll_res, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + div = MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG); + + mdss_pll_resource_enable(dsi_pll_res, false); + return div + 1; +} + +static int analog_set_div(struct div_clk *clk, int div) +{ + int rc; + struct mdss_pll_resources *dsi_pll_res = clk->priv; + + rc = mdss_pll_resource_enable(dsi_pll_res, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG, div - 1); + + mdss_pll_resource_enable(dsi_pll_res, false); + return rc; +} + +static int analog_get_div(struct div_clk *clk) +{ + int div = 0, rc; + struct mdss_pll_resources *dsi_pll_res = clk->priv; + + rc = mdss_pll_resource_enable(clk->priv, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + div = MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG) + 1; + + mdss_pll_resource_enable(dsi_pll_res, false); + + return div; +} + +static int dsi_pll_lock_status(struct mdss_pll_resources *dsi_pll_res) +{ + u32 status; + int pll_locked; + + /* poll for PLL ready status */ + if (readl_poll_timeout_noirq((dsi_pll_res->pll_base + + DSI_PHY_PLL_UNIPHY_PLL_STATUS), + status, + ((status & BIT(0)) == 1), + DSI_PLL_POLL_MAX_READS, + DSI_PLL_POLL_TIMEOUT_US)) { + pr_debug("DSI PLL status=%x failed to Lock\n", status); + pll_locked = 0; + } else { + pll_locked = 1; + } + + return pll_locked; +} + +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 dsi_pll_enable_seq_8974(struct mdss_pll_resources *dsi_pll_res) +{ + int i, rc = 0; + u32 max_reads, timeout_us; + 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); + /* poll for PLL ready status */ + max_reads = 5; + timeout_us = 100; + + 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; +} + +static int dsi_pll_enable(struct clk *c) +{ + int i, 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; + } + + /* Try all enable sequences until one succeeds */ + for (i = 0; i < vco->pll_en_seq_cnt; i++) { + rc = vco->pll_enable_seqs[i](dsi_pll_res); + pr_debug("DSI PLL %s after sequence #%d\n", + rc ? "unlocked" : "locked", i + 1); + if (!rc) + break; + } + + if (rc) { + mdss_pll_resource_enable(dsi_pll_res, false); + pr_err("DSI PLL failed to lock\n"); + } + dsi_pll_res->pll_on = true; + + return rc; +} + +static void dsi_pll_disable(struct clk *c) +{ + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *dsi_pll_res = vco->priv; + + if (!dsi_pll_res->pll_on && + mdss_pll_resource_enable(dsi_pll_res, true)) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return; + } + + dsi_pll_res->handoff_resources = false; + + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x00); + + mdss_pll_resource_enable(dsi_pll_res, false); + dsi_pll_res->pll_on = false; + + pr_debug("DSI PLL Disabled\n"); + return; +} + +static int vco_set_rate(struct clk *c, unsigned long rate) +{ + s64 vco_clk_rate = rate; + s32 rem; + s64 refclk_cfg, frac_n_mode, ref_doubler_en_b; + s64 ref_clk_to_pll, div_fbx1000, frac_n_value; + s64 sdm_cfg0, sdm_cfg1, sdm_cfg2, sdm_cfg3; + s64 gen_vco_clk, cal_cfg10, cal_cfg11; + u32 res; + int i, 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; + } + + /* Configure the Loop filter resistance */ + for (i = 0; i < vco->lpfr_lut_size; i++) + if (vco_clk_rate <= vco->lpfr_lut[i].vco_rate) + break; + if (i == vco->lpfr_lut_size) { + pr_err("unable to get loop filter resistance. vco=%ld\n", rate); + rc = -EINVAL; + goto error; + } + res = vco->lpfr_lut[i].r; + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_LPFR_CFG, res); + + /* Loop filter capacitance values : c1 and c2 */ + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_LPFC1_CFG, 0x70); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_LPFC2_CFG, 0x15); + + div_s64_rem(vco_clk_rate, vco->ref_clk_rate, &rem); + if (rem) { + refclk_cfg = 0x1; + frac_n_mode = 1; + ref_doubler_en_b = 0; + } else { + refclk_cfg = 0x0; + frac_n_mode = 0; + ref_doubler_en_b = 1; + } + + pr_debug("refclk_cfg = %lld\n", refclk_cfg); + + ref_clk_to_pll = ((vco->ref_clk_rate * 2 * (refclk_cfg)) + + (ref_doubler_en_b * vco->ref_clk_rate)); + div_fbx1000 = div_s64((vco_clk_rate * 1000), ref_clk_to_pll); + + div_s64_rem(div_fbx1000, 1000, &rem); + frac_n_value = div_s64((rem * (1 << 16)), 1000); + gen_vco_clk = div_s64(div_fbx1000 * ref_clk_to_pll, 1000); + + pr_debug("ref_clk_to_pll = %lld\n", ref_clk_to_pll); + pr_debug("div_fb = %lld\n", div_fbx1000); + pr_debug("frac_n_value = %lld\n", frac_n_value); + + pr_debug("Generated VCO Clock: %lld\n", gen_vco_clk); + rem = 0; + if (frac_n_mode) { + sdm_cfg0 = (0x0 << 5); + sdm_cfg0 |= (0x0 & 0x3f); + sdm_cfg1 = (div_s64(div_fbx1000, 1000) & 0x3f) - 1; + sdm_cfg3 = div_s64_rem(frac_n_value, 256, &rem); + sdm_cfg2 = rem; + } else { + sdm_cfg0 = (0x1 << 5); + sdm_cfg0 |= (div_s64(div_fbx1000, 1000) & 0x3f) - 1; + sdm_cfg1 = (0x0 & 0x3f); + sdm_cfg2 = 0; + sdm_cfg3 = 0; + } + + pr_debug("sdm_cfg0=%lld\n", sdm_cfg0); + pr_debug("sdm_cfg1=%lld\n", sdm_cfg1); + pr_debug("sdm_cfg2=%lld\n", sdm_cfg2); + pr_debug("sdm_cfg3=%lld\n", sdm_cfg3); + + cal_cfg11 = div_s64_rem(gen_vco_clk, 256 * 1000000, &rem); + cal_cfg10 = rem / 1000000; + pr_debug("cal_cfg10=%lld, cal_cfg11=%lld\n", cal_cfg10, cal_cfg11); + + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CHGPUMP_CFG, 0x02); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG3, 0x2b); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG4, 0x66); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x0d); + + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1, (u32)(sdm_cfg1 & 0xff)); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2, (u32)(sdm_cfg2 & 0xff)); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3, (u32)(sdm_cfg3 & 0xff)); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG4, 0x00); + + /* Add hardware recommended delay for correct PLL configuration */ + udelay(1); + + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_REFCLK_CFG, (u32)refclk_cfg); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_PWRGEN_CFG, 0x00); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_VCOLPF_CFG, 0x71); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0, (u32)sdm_cfg0); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG0, 0x12); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG6, 0x30); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG7, 0x00); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG8, 0x60); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG9, 0x00); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG10, (u32)(cal_cfg10 & 0xff)); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG11, (u32)(cal_cfg11 & 0xff)); + MDSS_PLL_REG_W(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_EFUSE_CFG, 0x20); + +error: + mdss_pll_resource_enable(dsi_pll_res, false); + return rc; +} + +static long vco_round_rate(struct clk *c, unsigned long rate) +{ + unsigned long rrate = rate; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + + if (rate < vco->min_rate) + rrate = vco->min_rate; + if (rate > vco->max_rate) + rrate = vco->max_rate; + + return rrate; +} + +static unsigned long vco_get_rate(struct clk *c) +{ + u32 sdm0, doubler, sdm_byp_div; + u64 vco_rate; + u32 sdm_dc_off, sdm_freq_seed, sdm2, sdm3; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + u64 ref_clk = vco->ref_clk_rate; + int rc; + 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; + } + + /* Check to see if the ref clk doubler is enabled */ + doubler = MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_REFCLK_CFG) & BIT(0); + ref_clk += (doubler * vco->ref_clk_rate); + + /* see if it is integer mode or sdm mode */ + sdm0 = MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0); + if (sdm0 & BIT(6)) { + /* integer mode */ + sdm_byp_div = (MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0) & 0x3f) + 1; + vco_rate = ref_clk * sdm_byp_div; + } else { + /* sdm mode */ + sdm_dc_off = MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1) & 0xFF; + pr_debug("sdm_dc_off = %d\n", sdm_dc_off); + sdm2 = MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2) & 0xFF; + sdm3 = MDSS_PLL_REG_R(dsi_pll_res->pll_base, + DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3) & 0xFF; + sdm_freq_seed = (sdm3 << 8) | sdm2; + pr_debug("sdm_freq_seed = %d\n", sdm_freq_seed); + + vco_rate = (ref_clk * (sdm_dc_off + 1)) + + mult_frac(ref_clk, sdm_freq_seed, BIT(16)); + pr_debug("vco rate = %lld", vco_rate); + } + + pr_debug("returning vco rate = %lu\n", (unsigned long)vco_rate); + + mdss_pll_resource_enable(dsi_pll_res, false); + + return (unsigned long)vco_rate; +} + +static enum handoff vco_handoff(struct clk *c) +{ + int rc; + enum handoff ret = HANDOFF_DISABLED_CLK; + 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 ret; + } + + if (dsi_pll_lock_status(dsi_pll_res)) { + dsi_pll_res->handoff_resources = true; + dsi_pll_res->pll_on = true; + c->rate = vco_get_rate(c); + ret = HANDOFF_ENABLED_CLK; + } else { + mdss_pll_resource_enable(dsi_pll_res, false); + } + + return ret; +} + +static int vco_prepare(struct clk *c) +{ + int rc = 0; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *dsi_pll_res = vco->priv; + + if (!dsi_pll_res) { + pr_err("Dsi pll resources are not available\n"); + return -EINVAL; + } + + if ((dsi_pll_res->vco_cached_rate != 0) + && (dsi_pll_res->vco_cached_rate == c->rate)) { + rc = vco_set_rate(c, dsi_pll_res->vco_cached_rate); + if (rc) { + pr_err("vco_set_rate failed. rc=%d\n", rc); + goto error; + } + } + + rc = dsi_pll_enable(c); + +error: + return rc; +} + +static void vco_unprepare(struct clk *c) +{ + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *dsi_pll_res = vco->priv; + + if (!dsi_pll_res) { + pr_err("Dsi pll resources are not available\n"); + return; + } + + dsi_pll_res->vco_cached_rate = c->rate; + dsi_pll_disable(c); +} + +/* Op structures */ + +static struct clk_ops clk_ops_dsi_vco = { + .set_rate = vco_set_rate, + .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 = (struct lpfr_cfg[]){ + {479500000, 8}, + {480000000, 11}, + {575500000, 8}, + {576000000, 12}, + {610500000, 8}, + {659500000, 9}, + {671500000, 10}, + {672000000, 14}, + {708500000, 10}, + {750000000, 11}, + }, + .c = { + .dbg_name = "dsi_vco_clk", + .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), + }, +}; + +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", + .ops = &pixel_clk_src_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(pixel_clk_src_8974.c), + }, +}; + +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", + .ops = &byte_mux_clk_ops, + CLK_INIT(byte_mux_8974.c), + }, +}; + +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", + .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(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int rc; + + if (!pll_res || !pll_res->pll_base) { + pr_err("Invalide input parameters\n"); + return -EPROBE_DEFER; + } + + /* Set client data to mux, div and vco clocks */ + byte_clk_src_8974.priv = pll_res; + byte_mux_8974.priv = pll_res; + pixel_clk_src_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; + + /* 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; + + 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; + } + + return rc; +} diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll.h b/drivers/clk/msm/mdss/mdss-dsi-pll.h new file mode 100644 index 000000000000..15ca2fbd5281 --- /dev/null +++ b/drivers/clk/msm/mdss/mdss-dsi-pll.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2012-2014, 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. + */ + +#ifndef __MDSS_DSI_PLL_H +#define __MDSS_DSI_PLL_H + +#define MAX_DSI_PLL_EN_SEQS 10 + +struct lpfr_cfg { + unsigned long vco_rate; + u32 r; +}; + +struct dsi_pll_vco_clk { + unsigned long ref_clk_rate; + unsigned long min_rate; + unsigned long max_rate; + u32 pll_en_seq_cnt; + struct lpfr_cfg *lpfr_lut; + u32 lpfr_lut_size; + void *priv; + + struct clk c; + + int (*pll_enable_seqs[MAX_DSI_PLL_EN_SEQS]) + (struct mdss_pll_resources *dsi_pll_Res); +}; + +int dsi_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +#endif diff --git a/drivers/clk/msm/mdss/mdss-edp-pll-28hpm.c b/drivers/clk/msm/mdss/mdss-edp-pll-28hpm.c new file mode 100644 index 000000000000..c037cc5595ca --- /dev/null +++ b/drivers/clk/msm/mdss/mdss-edp-pll-28hpm.c @@ -0,0 +1,585 @@ +/* Copyright (c) 2012-2014, 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 <linux/kernel.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/iopoll.h> +#include <linux/clk/msm-clk-provider.h> +#include <linux/clk/msm-clk.h> +#include <linux/clk/msm-clock-generic.h> + +#include <dt-bindings/clock/msm-clocks-8974.h> + +#include "mdss-pll.h" +#include "mdss-edp-pll.h" + +#define EDP_PHY_PLL_UNIPHY_PLL_REFCLK_CFG (0x0) +#define EDP_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG (0x0004) +#define EDP_PHY_PLL_UNIPHY_PLL_VCOLPF_CFG (0x000C) +#define EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG (0x0020) +#define EDP_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG (0x0024) +#define EDP_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG (0x0028) +#define EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG0 (0x0038) +#define EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG1 (0x003C) +#define EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG2 (0x0040) +#define EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG3 (0x0044) +#define EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG4 (0x0048) +#define EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG0 (0x004C) +#define EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG1 (0x0050) +#define EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG2 (0x0054) +#define EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG3 (0x0058) +#define EDP_PHY_PLL_UNIPHY_PLL_LKDET_CFG2 (0x0064) +#define EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG0 (0x006C) +#define EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG2 (0x0074) +#define EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG6 (0x0084) +#define EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG7 (0x0088) +#define EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG8 (0x008C) +#define EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG9 (0x0090) +#define EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG10 (0x0094) +#define EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG11 (0x0098) +#define EDP_PHY_PLL_UNIPHY_PLL_LKDET_CFG0 (0x005C) +#define EDP_PHY_PLL_UNIPHY_PLL_LKDET_CFG1 (0x0060) + +#define EDP_PLL_POLL_MAX_READS 10 +#define EDP_PLL_POLL_TIMEOUT_US 50 + +static struct clk_ops edp_mainlink_clk_src_ops; +static struct clk_div_ops fixed_5div_ops; /* null ops */ +static struct clk_ops edp_pixel_clk_ops; + +static inline struct edp_pll_vco_clk *to_edp_vco_clk(struct clk *clk) +{ + return container_of(clk, struct edp_pll_vco_clk, c); +} + +int edp_div_prepare(struct clk *c) +{ + struct div_clk *div = to_div_clk(c); + /* Restore the divider's value */ + return div->ops->set_div(div, div->data.div); +} + +static int edp_vco_set_rate(struct clk *c, unsigned long vco_rate) +{ + struct edp_pll_vco_clk *vco = to_edp_vco_clk(c); + struct mdss_pll_resources *edp_pll_res = vco->priv; + int rc; + + pr_debug("vco_rate=%d\n", (int)vco_rate); + + rc = mdss_pll_resource_enable(edp_pll_res, true); + if (rc) { + pr_err("failed to enable edp pll res rc=%d\n", rc); + rc = -EINVAL; + } + + if (vco_rate == 810000000) { + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_VCOLPF_CFG, 0x18); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x05); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_REFCLK_CFG, 0x00); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG0, 0x36); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG1, 0x69); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG2, 0xff); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG3, 0x2f); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG4, 0x00); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG0, 0x80); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG1, 0x00); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG2, 0x00); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG3, 0x00); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG0, 0x0a); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG2, 0x01); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG6, 0x5a); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG7, 0x0); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG8, 0x60); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG9, 0x0); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG10, 0x2a); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG11, 0x3); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_LKDET_CFG0, 0x10); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_LKDET_CFG1, 0x1a); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG, 0x00); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG, 0x00); + } else if (vco_rate == 1350000000) { + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x05); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_REFCLK_CFG, 0x01); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG0, 0x36); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG1, 0x62); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG2, 0x00); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG3, 0x28); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG4, 0x00); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG0, 0x80); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG1, 0x00); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG2, 0x00); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG3, 0x00); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG0, 0x0a); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG2, 0x01); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG6, 0x5a); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG7, 0x0); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG8, 0x60); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG9, 0x0); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG10, 0x46); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG11, 0x5); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_LKDET_CFG0, 0x10); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_LKDET_CFG1, 0x1a); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG, 0x00); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG, 0x00); + } else { + pr_err("rate=%d is NOT supported\n", (int)vco_rate); + vco_rate = 0; + rc = -EINVAL; + } + + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01); + udelay(100); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05); + udelay(100); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07); + udelay(100); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f); + udelay(100); + mdss_pll_resource_enable(edp_pll_res, false); + + vco->rate = vco_rate; + + return rc; +} + +static int edp_pll_ready_poll(struct mdss_pll_resources *edp_pll_res) +{ + int cnt; + u32 status; + + cnt = 100; + while (cnt--) { + udelay(100); + status = MDSS_PLL_REG_R(edp_pll_res->pll_base, 0xc0); + status &= 0x01; + if (status) + break; + } + pr_debug("cnt=%d status=%d\n", cnt, (int)status); + + if (status) + return 1; + + return 0; +} + +static int edp_vco_enable(struct clk *c) +{ + int i, ready; + int rc; + struct edp_pll_vco_clk *vco = to_edp_vco_clk(c); + struct mdss_pll_resources *edp_pll_res = vco->priv; + + rc = mdss_pll_resource_enable(edp_pll_res, true); + if (rc) { + pr_err("edp pll resources not available\n"); + return rc; + } + + for (i = 0; i < 3; i++) { + ready = edp_pll_ready_poll(edp_pll_res); + if (ready) + break; + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01); + udelay(100); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05); + udelay(100); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07); + udelay(100); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f); + udelay(100); + } + + if (ready) { + pr_debug("EDP PLL lock success\n"); + edp_pll_res->pll_on = true; + rc = 0; + } else { + pr_err("EDP PLL failed to lock\n"); + mdss_pll_resource_enable(edp_pll_res, false); + rc = -EINVAL; + } + + return rc; +} + +static void edp_vco_disable(struct clk *c) +{ + struct edp_pll_vco_clk *vco = to_edp_vco_clk(c); + struct mdss_pll_resources *edp_pll_res = vco->priv; + + if (!edp_pll_res) { + pr_err("Invalid input parameter\n"); + return; + } + + if (!edp_pll_res->pll_on && + mdss_pll_resource_enable(edp_pll_res, true)) { + pr_err("edp pll resources not available\n"); + return; + } + + MDSS_PLL_REG_W(edp_pll_res->pll_base, 0x20, 0x00); + + edp_pll_res->handoff_resources = false; + edp_pll_res->pll_on = false; + + mdss_pll_resource_enable(edp_pll_res, false); + + pr_debug("EDP PLL Disabled\n"); + return; +} + +static unsigned long edp_vco_get_rate(struct clk *c) +{ + struct edp_pll_vco_clk *vco = to_edp_vco_clk(c); + struct mdss_pll_resources *edp_pll_res = vco->priv; + u32 pll_status, div2; + int rc; + + rc = mdss_pll_resource_enable(edp_pll_res, true); + if (rc) { + pr_err("edp pll resources not available\n"); + return rc; + } + + if (vco->rate == 0) { + pll_status = MDSS_PLL_REG_R(edp_pll_res->pll_base, 0xc0); + if (pll_status & 0x01) { + div2 = MDSS_PLL_REG_R(edp_pll_res->pll_base, 0x24); + if (div2 & 0x01) + vco->rate = 1350000000; + else + vco->rate = 810000000; + } + } + mdss_pll_resource_enable(edp_pll_res, false); + + pr_debug("rate=%d\n", (int)vco->rate); + + return vco->rate; +} + +static long edp_vco_round_rate(struct clk *c, unsigned long rate) +{ + struct edp_pll_vco_clk *vco = to_edp_vco_clk(c); + unsigned long rrate = -ENOENT; + unsigned long *lp; + + lp = vco->rate_list; + while (*lp) { + rrate = *lp; + if (rate <= rrate) + break; + lp++; + } + + pr_debug("rrate=%d\n", (int)rrate); + + return rrate; +} + +static int edp_vco_prepare(struct clk *c) +{ + struct edp_pll_vco_clk *vco = to_edp_vco_clk(c); + + pr_debug("rate=%d\n", (int)vco->rate); + + return edp_vco_set_rate(c, vco->rate); +} + +static void edp_vco_unprepare(struct clk *c) +{ + struct edp_pll_vco_clk *vco = to_edp_vco_clk(c); + + pr_debug("rate=%d\n", (int)vco->rate); + + edp_vco_disable(c); +} + +static int edp_pll_lock_status(struct mdss_pll_resources *edp_pll_res) +{ + u32 status; + int pll_locked = 0; + int rc; + + rc = mdss_pll_resource_enable(edp_pll_res, true); + if (rc) { + pr_err("edp pll resources not available\n"); + return rc; + } + + /* poll for PLL ready status */ + if (readl_poll_timeout_noirq((edp_pll_res->pll_base + 0xc0), + status, ((status & BIT(0)) == 1), + EDP_PLL_POLL_MAX_READS, + EDP_PLL_POLL_TIMEOUT_US)) { + pr_debug("EDP PLL status=%x failed to Lock\n", status); + pll_locked = 0; + } else { + pll_locked = 1; + } + mdss_pll_resource_enable(edp_pll_res, false); + + return pll_locked; +} + +static enum handoff edp_vco_handoff(struct clk *c) +{ + enum handoff ret = HANDOFF_DISABLED_CLK; + struct edp_pll_vco_clk *vco = to_edp_vco_clk(c); + struct mdss_pll_resources *edp_pll_res = vco->priv; + + if (mdss_pll_resource_enable(edp_pll_res, true)) { + pr_err("edp pll resources not available\n"); + return ret; + } + + edp_pll_res->handoff_resources = true; + + if (edp_pll_lock_status(edp_pll_res)) { + c->rate = edp_vco_get_rate(c); + edp_pll_res->pll_on = true; + ret = HANDOFF_ENABLED_CLK; + } else { + edp_pll_res->handoff_resources = false; + mdss_pll_resource_enable(edp_pll_res, false); + } + + pr_debug("done, ret=%d\n", ret); + return ret; +} + +static unsigned long edp_vco_rate_list[] = { + 810000000, 1350000000, 0}; + +struct clk_ops edp_vco_clk_ops = { + .enable = edp_vco_enable, + .set_rate = edp_vco_set_rate, + .get_rate = edp_vco_get_rate, + .round_rate = edp_vco_round_rate, + .prepare = edp_vco_prepare, + .unprepare = edp_vco_unprepare, + .handoff = edp_vco_handoff, +}; + +struct edp_pll_vco_clk edp_vco_clk = { + .ref_clk_rate = 19200000, + .rate = 0, + .rate_list = edp_vco_rate_list, + .c = { + .dbg_name = "edp_vco_clk", + .ops = &edp_vco_clk_ops, + CLK_INIT(edp_vco_clk.c), + }, +}; + +static unsigned long edp_mainlink_get_rate(struct clk *c) +{ + struct div_clk *mclk = to_div_clk(c); + struct clk *pclk; + unsigned long rate = 0; + + pclk = clk_get_parent(c); + + if (pclk->ops->get_rate) { + rate = pclk->ops->get_rate(pclk); + rate /= mclk->data.div; + } + + pr_debug("rate=%d div=%d\n", (int)rate, mclk->data.div); + + return rate; +} + + +struct div_clk edp_mainlink_clk_src = { + .ops = &fixed_5div_ops, + .data = { + .div = 5, + .min_div = 5, + .max_div = 5, + }, + .c = { + .parent = &edp_vco_clk.c, + .dbg_name = "edp_mainlink_clk_src", + .ops = &edp_mainlink_clk_src_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(edp_mainlink_clk_src.c), + } +}; + +/* + * this rate is from pll to clock controller + * output from pll to CC has two possibilities + * 1: if mainlink rate is 270M, then 675M + * 2: if mainlink rate is 162M, then 810M + */ +static int edp_pixel_set_div(struct div_clk *clk, int div) +{ + int rc; + struct mdss_pll_resources *edp_pll_res = clk->priv; + + rc = mdss_pll_resource_enable(edp_pll_res, true); + if (rc) { + pr_err("edp pll resources not available\n"); + return rc; + } + + pr_debug("div=%d\n", div); + MDSS_PLL_REG_W(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG, (div - 1)); + mdss_pll_resource_enable(edp_pll_res, false); + + return 0; +} + +static int edp_pixel_get_div(struct div_clk *clk) +{ + int div = 0; + int rc; + struct mdss_pll_resources *edp_pll_res = clk->priv; + + rc = mdss_pll_resource_enable(edp_pll_res, true); + if (rc) { + pr_err("edp pll resources not available\n"); + return rc; + } + + div = MDSS_PLL_REG_R(edp_pll_res->pll_base, + EDP_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG); + mdss_pll_resource_enable(edp_pll_res, false); + div &= 0x01; + pr_debug("div=%d\n", div); + return div + 1; +} + +static struct clk_div_ops edp_pixel_ops = { + .set_div = edp_pixel_set_div, + .get_div = edp_pixel_get_div, +}; + +struct div_clk edp_pixel_clk_src = { + .data = { + .max_div = 2, + .min_div = 1, + }, + .ops = &edp_pixel_ops, + .c = { + .parent = &edp_vco_clk.c, + .dbg_name = "edp_pixel_clk_src", + .ops = &edp_pixel_clk_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(edp_pixel_clk_src.c), + }, +}; + +static struct clk_lookup mdss_edp_pllcc_8974[] = { + CLK_LOOKUP("edp_pixel_src", edp_pixel_clk_src.c, + "fd8c0000.qcom,mmsscc-mdss"), + CLK_LOOKUP("edp_mainlink_src", edp_mainlink_clk_src.c, + "fd8c0000.qcom,mmsscc-mdss"), +}; + +int edp_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int rc = -ENOTSUPP; + + if (!pll_res || !pll_res->pll_base) { + pr_err("Invalid input parameters\n"); + return -EPROBE_DEFER; + } + + /* Set client data to div and vco clocks */ + edp_pixel_clk_src.priv = pll_res; + edp_mainlink_clk_src.priv = pll_res; + edp_vco_clk.priv = pll_res; + + /* Set clock operation for mainlink and pixel clock */ + edp_mainlink_clk_src_ops = clk_ops_div; + edp_mainlink_clk_src_ops.get_parent = clk_get_parent; + edp_mainlink_clk_src_ops.get_rate = edp_mainlink_get_rate; + + edp_pixel_clk_ops = clk_ops_slave_div; + edp_pixel_clk_ops.prepare = edp_div_prepare; + + rc = of_msm_clock_register(pdev->dev.of_node, mdss_edp_pllcc_8974, + ARRAY_SIZE(mdss_edp_pllcc_8974)); + if (rc) { + pr_err("Clock register failed rc=%d\n", rc); + rc = -EPROBE_DEFER; + } + + return rc; +} diff --git a/drivers/clk/msm/mdss/mdss-edp-pll.h b/drivers/clk/msm/mdss/mdss-edp-pll.h new file mode 100644 index 000000000000..c1f4f91db4eb --- /dev/null +++ b/drivers/clk/msm/mdss/mdss-edp-pll.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2012-2014, 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. + */ + +#ifndef __MDSS_EDP_PLL_H +#define __MDSS_EDP_PLL_H + +struct edp_pll_vco_clk { + unsigned long ref_clk_rate; + unsigned long rate; /* vco rate */ + unsigned long *rate_list; + void *priv; + + struct clk c; +}; + +int edp_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +#endif diff --git a/drivers/clk/msm/mdss/mdss-hdmi-pll-28hpm.c b/drivers/clk/msm/mdss/mdss-hdmi-pll-28hpm.c new file mode 100644 index 000000000000..d53c357e3936 --- /dev/null +++ b/drivers/clk/msm/mdss/mdss-hdmi-pll-28hpm.c @@ -0,0 +1,1094 @@ +/* Copyright (c) 2012-2014, 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 <linux/kernel.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/iopoll.h> +#include <linux/clk/msm-clk-provider.h> +#include <linux/clk/msm-clk.h> +#include <linux/clk/msm-clock-generic.h> + +#include "mdss-pll.h" +#include "mdss-hdmi-pll.h" + +/* hdmi phy registers */ +#define HDMI_PHY_ANA_CFG0 (0x0000) +#define HDMI_PHY_ANA_CFG1 (0x0004) +#define HDMI_PHY_ANA_CFG2 (0x0008) +#define HDMI_PHY_ANA_CFG3 (0x000C) +#define HDMI_PHY_PD_CTRL0 (0x0010) +#define HDMI_PHY_PD_CTRL1 (0x0014) +#define HDMI_PHY_GLB_CFG (0x0018) +#define HDMI_PHY_DCC_CFG0 (0x001C) +#define HDMI_PHY_DCC_CFG1 (0x0020) +#define HDMI_PHY_TXCAL_CFG0 (0x0024) +#define HDMI_PHY_TXCAL_CFG1 (0x0028) +#define HDMI_PHY_TXCAL_CFG2 (0x002C) +#define HDMI_PHY_TXCAL_CFG3 (0x0030) +#define HDMI_PHY_BIST_CFG0 (0x0034) +#define HDMI_PHY_BIST_CFG1 (0x0038) +#define HDMI_PHY_BIST_PATN0 (0x003C) +#define HDMI_PHY_BIST_PATN1 (0x0040) +#define HDMI_PHY_BIST_PATN2 (0x0044) +#define HDMI_PHY_BIST_PATN3 (0x0048) +#define HDMI_PHY_STATUS (0x005C) + +/* hdmi phy unified pll registers */ +#define HDMI_UNI_PLL_REFCLK_CFG (0x0000) +#define HDMI_UNI_PLL_POSTDIV1_CFG (0x0004) +#define HDMI_UNI_PLL_CHFPUMP_CFG (0x0008) +#define HDMI_UNI_PLL_VCOLPF_CFG (0x000C) +#define HDMI_UNI_PLL_VREG_CFG (0x0010) +#define HDMI_UNI_PLL_PWRGEN_CFG (0x0014) +#define HDMI_UNI_PLL_GLB_CFG (0x0020) +#define HDMI_UNI_PLL_POSTDIV2_CFG (0x0024) +#define HDMI_UNI_PLL_POSTDIV3_CFG (0x0028) +#define HDMI_UNI_PLL_LPFR_CFG (0x002C) +#define HDMI_UNI_PLL_LPFC1_CFG (0x0030) +#define HDMI_UNI_PLL_LPFC2_CFG (0x0034) +#define HDMI_UNI_PLL_SDM_CFG0 (0x0038) +#define HDMI_UNI_PLL_SDM_CFG1 (0x003C) +#define HDMI_UNI_PLL_SDM_CFG2 (0x0040) +#define HDMI_UNI_PLL_SDM_CFG3 (0x0044) +#define HDMI_UNI_PLL_SDM_CFG4 (0x0048) +#define HDMI_UNI_PLL_SSC_CFG0 (0x004C) +#define HDMI_UNI_PLL_SSC_CFG1 (0x0050) +#define HDMI_UNI_PLL_SSC_CFG2 (0x0054) +#define HDMI_UNI_PLL_SSC_CFG3 (0x0058) +#define HDMI_UNI_PLL_LKDET_CFG0 (0x005C) +#define HDMI_UNI_PLL_LKDET_CFG1 (0x0060) +#define HDMI_UNI_PLL_LKDET_CFG2 (0x0064) +#define HDMI_UNI_PLL_CAL_CFG0 (0x006C) +#define HDMI_UNI_PLL_CAL_CFG1 (0x0070) +#define HDMI_UNI_PLL_CAL_CFG2 (0x0074) +#define HDMI_UNI_PLL_CAL_CFG3 (0x0078) +#define HDMI_UNI_PLL_CAL_CFG4 (0x007C) +#define HDMI_UNI_PLL_CAL_CFG5 (0x0080) +#define HDMI_UNI_PLL_CAL_CFG6 (0x0084) +#define HDMI_UNI_PLL_CAL_CFG7 (0x0088) +#define HDMI_UNI_PLL_CAL_CFG8 (0x008C) +#define HDMI_UNI_PLL_CAL_CFG9 (0x0090) +#define HDMI_UNI_PLL_CAL_CFG10 (0x0094) +#define HDMI_UNI_PLL_CAL_CFG11 (0x0098) +#define HDMI_UNI_PLL_STATUS (0x00C0) + +#define HDMI_PLL_POLL_MAX_READS 10 +#define HDMI_PLL_POLL_TIMEOUT_US 50 + +static inline struct hdmi_pll_vco_clk *to_hdmi_vco_clk(struct clk *clk) +{ + return container_of(clk, struct hdmi_pll_vco_clk, c); +} + +static void hdmi_vco_disable(struct clk *c) +{ + struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c); + struct mdss_pll_resources *hdmi_pll_res = vco->priv; + + if (!hdmi_pll_res) { + pr_err("Invalid input parameter\n"); + return; + } + + if (!hdmi_pll_res->pll_on && + mdss_pll_resource_enable(hdmi_pll_res, true)) { + pr_err("pll resource can't be enabled\n"); + return; + } + + MDSS_PLL_REG_W(hdmi_pll_res->pll_base, HDMI_UNI_PLL_GLB_CFG, 0x0); + udelay(5); + MDSS_PLL_REG_W(hdmi_pll_res->phy_base, HDMI_PHY_GLB_CFG, 0x0); + + hdmi_pll_res->handoff_resources = false; + mdss_pll_resource_enable(hdmi_pll_res, false); + hdmi_pll_res->pll_on = false; +} /* hdmi_vco_disable */ + +static int hdmi_vco_enable(struct clk *c) +{ + u32 status; + u32 max_reads, timeout_us; + int rc; + struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c); + struct mdss_pll_resources *hdmi_pll_res = vco->priv; + + rc = mdss_pll_resource_enable(hdmi_pll_res, true); + if (rc) { + pr_err("pll resource can't be enabled\n"); + return rc; + } + + /* Global Enable */ + MDSS_PLL_REG_W(hdmi_pll_res->phy_base, HDMI_PHY_GLB_CFG, 0x81); + /* Power up power gen */ + MDSS_PLL_REG_W(hdmi_pll_res->phy_base, HDMI_PHY_PD_CTRL0, 0x00); + udelay(350); + + /* PLL Power-Up */ + MDSS_PLL_REG_W(hdmi_pll_res->pll_base, HDMI_UNI_PLL_GLB_CFG, 0x01); + udelay(5); + /* Power up PLL LDO */ + MDSS_PLL_REG_W(hdmi_pll_res->pll_base, HDMI_UNI_PLL_GLB_CFG, 0x03); + udelay(350); + + /* PLL Power-Up */ + MDSS_PLL_REG_W(hdmi_pll_res->pll_base, HDMI_UNI_PLL_GLB_CFG, 0x0F); + udelay(350); + + /* poll for PLL ready status */ + max_reads = 20; + timeout_us = 100; + if (readl_poll_timeout_noirq( + (hdmi_pll_res->pll_base + HDMI_UNI_PLL_STATUS), + status, ((status & BIT(0)) == 1), max_reads, timeout_us)) { + pr_err("hdmi phy pll status=%x failed to Lock\n", status); + hdmi_vco_disable(c); + mdss_pll_resource_enable(hdmi_pll_res, false); + return -EINVAL; + } + pr_debug("hdmi phy pll is locked\n"); + + udelay(350); + /* poll for PHY ready status */ + max_reads = 20; + timeout_us = 100; + if (readl_poll_timeout_noirq( + (hdmi_pll_res->phy_base + HDMI_PHY_STATUS), + status, ((status & BIT(0)) == 1), max_reads, timeout_us)) { + pr_err("hdmi phy status=%x failed to Lock\n", status); + hdmi_vco_disable(c); + mdss_pll_resource_enable(hdmi_pll_res, false); + return -EINVAL; + } + hdmi_pll_res->pll_on = true; + pr_debug("hdmi phy is locked\n"); + + return 0; +} /* hdmi_vco_enable */ + + +static void hdmi_phy_pll_calculator(u32 vco_freq, + struct mdss_pll_resources *hdmi_pll_res) +{ + u32 ref_clk = 19200000; + u32 sdm_mode = 1; + u32 ref_clk_multiplier = sdm_mode == 1 ? 2 : 1; + u32 int_ref_clk_freq = ref_clk * ref_clk_multiplier; + u32 fbclk_pre_div = 1; + u32 ssc_mode = 0; + u32 kvco = 270; + u32 vdd = 95; + u32 ten_power_six = 1000000; + u32 ssc_ds_ppm = ssc_mode ? 5000 : 0; + u32 sdm_res = 16; + u32 ssc_tri_step = 32; + u32 ssc_freq = 2; + u64 ssc_ds = vco_freq * ssc_ds_ppm; + u32 div_in_freq = vco_freq / fbclk_pre_div; + u64 dc_offset = (div_in_freq / int_ref_clk_freq - 1) * + ten_power_six * 10; + u32 ssc_kdiv = (int_ref_clk_freq / ssc_freq) - + ten_power_six; + u64 sdm_freq_seed; + u32 ssc_tri_inc; + u64 fb_div_n; + void __iomem *pll_base = hdmi_pll_res->pll_base; + u32 val; + + pr_debug("vco_freq = %u\n", vco_freq); + + do_div(ssc_ds, (u64)ten_power_six); + + fb_div_n = (u64)div_in_freq * (u64)ten_power_six * 10; + do_div(fb_div_n, int_ref_clk_freq); + + sdm_freq_seed = ((fb_div_n - dc_offset - ten_power_six * 10) * + (1 << sdm_res) * 10) + 5; + do_div(sdm_freq_seed, ((u64)ten_power_six * 100)); + + ssc_tri_inc = (u32)ssc_ds; + ssc_tri_inc = (ssc_tri_inc / int_ref_clk_freq) * (1 << 16) / + ssc_tri_step; + + val = (ref_clk_multiplier == 2 ? 1 : 0) + + ((fbclk_pre_div == 2 ? 1 : 0) * 16); + pr_debug("HDMI_UNI_PLL_REFCLK_CFG = 0x%x\n", val); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_REFCLK_CFG, val); + + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CHFPUMP_CFG, 0x02); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_VCOLPF_CFG, 0x19); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_VREG_CFG, 0x04); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_PWRGEN_CFG, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_POSTDIV2_CFG, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_POSTDIV3_CFG, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFR_CFG, 0x0E); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFC1_CFG, 0x20); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFC2_CFG, 0x0D); + + do_div(dc_offset, (u64)ten_power_six * 10); + val = sdm_mode == 0 ? 64 + dc_offset : 0; + pr_debug("HDMI_UNI_PLL_SDM_CFG0 = 0x%x\n", val); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG0, val); + + val = 64 + dc_offset; + pr_debug("HDMI_UNI_PLL_SDM_CFG1 = 0x%x\n", val); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG1, val); + + val = sdm_freq_seed & 0xFF; + pr_debug("HDMI_UNI_PLL_SDM_CFG2 = 0x%x\n", val); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG2, val); + + val = (sdm_freq_seed >> 8) & 0xFF; + pr_debug("HDMI_UNI_PLL_SDM_CFG3 = 0x%x\n", val); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG3, val); + + val = (sdm_freq_seed >> 16) & 0xFF; + pr_debug("HDMI_UNI_PLL_SDM_CFG4 = 0x%x\n", val); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG4, val); + + val = (ssc_mode == 0 ? 128 : 0) + (ssc_kdiv / ten_power_six); + pr_debug("HDMI_UNI_PLL_SSC_CFG0 = 0x%x\n", val); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SSC_CFG0, val); + + val = ssc_tri_inc & 0xFF; + pr_debug("HDMI_UNI_PLL_SSC_CFG1 = 0x%x\n", val); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SSC_CFG1, val); + + val = (ssc_tri_inc >> 8) & 0xFF; + pr_debug("HDMI_UNI_PLL_SSC_CFG2 = 0x%x\n", val); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SSC_CFG2, val); + + pr_debug("HDMI_UNI_PLL_SSC_CFG3 = 0x%x\n", ssc_tri_step); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SSC_CFG3, ssc_tri_step); + + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG0, 0x10); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG1, 0x1A); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG2, 0x05); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG0, 0x0A); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG1, 0x04); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG2, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG3, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG4, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG5, 0x00); + + val = (kvco * vdd * 10000) / 6; + val += 500000; + val /= ten_power_six; + pr_debug("HDMI_UNI_PLL_CAL_CFG6 = 0x%x\n", val); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG6, val & 0xFF); + + val = (kvco * vdd * 10000) / 6; + val -= ten_power_six; + val /= ten_power_six; + val = (val >> 8) & 0xFF; + pr_debug("HDMI_UNI_PLL_CAL_CFG7 = 0x%x\n", val); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG7, val); + + val = (ref_clk * 5) / ten_power_six; + pr_debug("HDMI_UNI_PLL_CAL_CFG8 = 0x%x\n", val); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG8, val); + + val = ((ref_clk * 5) / ten_power_six) >> 8; + pr_debug("HDMI_UNI_PLL_CAL_CFG9 = 0x%x\n", val); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG9, val); + + vco_freq /= ten_power_six; + val = vco_freq & 0xFF; + pr_debug("HDMI_UNI_PLL_CAL_CFG10 = 0x%x\n", val); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG10, val); + + val = vco_freq >> 8; + pr_debug("HDMI_UNI_PLL_CAL_CFG11 = 0x%x\n", val); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG11, val); +} /* hdmi_phy_pll_calculator */ + +static int hdmi_vco_set_rate(struct clk *c, unsigned long rate) +{ + struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c); + struct mdss_pll_resources *hdmi_pll_res = vco->priv; + void __iomem *pll_base; + void __iomem *phy_base; + unsigned int set_power_dwn = 0; + int rc; + + rc = mdss_pll_resource_enable(hdmi_pll_res, true); + if (rc) { + pr_err("pll resource can't be enabled\n"); + return rc; + } + + if (hdmi_pll_res->pll_on) { + hdmi_vco_disable(c); + set_power_dwn = 1; + } + + pll_base = hdmi_pll_res->pll_base; + phy_base = hdmi_pll_res->phy_base; + + pr_debug("rate=%ld\n", rate); + + switch (rate) { + case 0: + break; + + case 756000000: + /* 640x480p60 */ + MDSS_PLL_REG_W(phy_base, HDMI_PHY_GLB_CFG, 0x81); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_GLB_CFG, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_REFCLK_CFG, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_VCOLPF_CFG, 0x19); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFR_CFG, 0x0E); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFC1_CFG, 0x20); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFC2_CFG, 0x0D); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG0, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG1, 0x52); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG2, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG3, 0xB0); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG4, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG0, 0x10); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG1, 0x1A); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG2, 0x05); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_POSTDIV2_CFG, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_POSTDIV3_CFG, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG2, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG8, 0x60); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG9, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG10, 0xF4); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG11, 0x02); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_PD_CTRL0, 0x1F); + udelay(50); + + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_GLB_CFG, 0x0F); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_PD_CTRL1, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x10); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG0, 0xDB); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG1, 0x43); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x02); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG3, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_VREG_CFG, 0x04); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_DCC_CFG0, 0xD0); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_DCC_CFG1, 0x1A); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG0, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG1, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG2, 0x02); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG3, 0x05); + udelay(200); + break; + + case 810000000: + /* 576p50/576i50 case */ + MDSS_PLL_REG_W(phy_base, HDMI_PHY_GLB_CFG, 0x81); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_GLB_CFG, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_REFCLK_CFG, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_VCOLPF_CFG, 0x19); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFR_CFG, 0x0E); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFC1_CFG, 0x20); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFC2_CFG, 0x0D); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG0, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG1, 0x54); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG2, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG3, 0x18); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG4, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG0, 0x10); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG1, 0x1A); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG2, 0x05); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_POSTDIV2_CFG, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_POSTDIV3_CFG, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG2, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG8, 0x60); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG9, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG10, 0x2A); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG11, 0x03); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_PD_CTRL0, 0x1F); + udelay(50); + + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_GLB_CFG, 0x0F); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_PD_CTRL1, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x10); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG0, 0xDB); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG1, 0x43); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x02); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG3, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_VREG_CFG, 0x04); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_DCC_CFG0, 0xD0); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_DCC_CFG1, 0x1A); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG0, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG1, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG2, 0x02); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG3, 0x05); + udelay(200); + break; + + case 810900000: + /* 480p60/480i60 case */ + MDSS_PLL_REG_W(phy_base, HDMI_PHY_GLB_CFG, 0x81); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_GLB_CFG, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_REFCLK_CFG, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_VCOLPF_CFG, 0x19); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFR_CFG, 0x0E); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFC1_CFG, 0x20); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFC2_CFG, 0x0D); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG0, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG1, 0x54); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG2, 0x66); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG3, 0x1D); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG4, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG0, 0x10); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG1, 0x1A); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG2, 0x05); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_POSTDIV2_CFG, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_POSTDIV3_CFG, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG2, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG8, 0x60); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG9, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG10, 0x2A); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG11, 0x03); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_PD_CTRL0, 0x1F); + udelay(50); + + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_GLB_CFG, 0x0F); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_PD_CTRL1, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x10); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG0, 0xDB); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG1, 0x43); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x02); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG3, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_VREG_CFG, 0x04); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_DCC_CFG0, 0xD0); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_DCC_CFG1, 0x1A); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG0, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG1, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG2, 0x02); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG3, 0x05); + udelay(200); + break; + + case 650000000: + MDSS_PLL_REG_W(phy_base, HDMI_PHY_GLB_CFG, 0x81); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_GLB_CFG, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_REFCLK_CFG, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_VCOLPF_CFG, 0x19); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFR_CFG, 0x0E); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFC1_CFG, 0x20); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFC2_CFG, 0x0D); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG0, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG1, 0x4F); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG2, 0x55); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG3, 0xED); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG4, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG0, 0x10); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG1, 0x1A); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG2, 0x05); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_POSTDIV2_CFG, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_POSTDIV3_CFG, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG2, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG8, 0x60); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG9, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG10, 0x8A); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG11, 0x02); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_PD_CTRL0, 0x1F); + udelay(50); + + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_GLB_CFG, 0x0F); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_PD_CTRL1, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x10); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG0, 0xDB); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG1, 0x43); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x02); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_VREG_CFG, 0x04); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_DCC_CFG0, 0xD0); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_DCC_CFG1, 0x1A); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG0, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG1, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG2, 0x02); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG3, 0x05); + udelay(200); + break; + + case 742500000: + /* + * 720p60/720p50/1080i60/1080i50 + * 1080p24/1080p30/1080p25 case + */ + MDSS_PLL_REG_W(phy_base, HDMI_PHY_GLB_CFG, 0x81); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_GLB_CFG, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_REFCLK_CFG, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_VCOLPF_CFG, 0x19); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFR_CFG, 0x0E); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFC1_CFG, 0x20); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFC2_CFG, 0x0D); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG0, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG1, 0x52); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG2, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG3, 0x56); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG4, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG0, 0x10); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG1, 0x1A); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG2, 0x05); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_POSTDIV2_CFG, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_POSTDIV3_CFG, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG2, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG8, 0x60); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG9, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG10, 0xE6); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG11, 0x02); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_PD_CTRL0, 0x1F); + udelay(50); + + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_GLB_CFG, 0x0F); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_PD_CTRL1, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x10); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG0, 0xDB); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG1, 0x43); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x02); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG3, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_VREG_CFG, 0x04); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_DCC_CFG0, 0xD0); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_DCC_CFG1, 0x1A); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG0, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG1, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG2, 0x02); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG3, 0x05); + udelay(200); + break; + + case 1080000000: + MDSS_PLL_REG_W(phy_base, HDMI_PHY_GLB_CFG, 0x81); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_GLB_CFG, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_REFCLK_CFG, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_VCOLPF_CFG, 0x19); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFR_CFG, 0x0E); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFC1_CFG, 0x20); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFC2_CFG, 0x0D); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG0, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG1, 0x5B); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG2, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG3, 0x20); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG4, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG0, 0x10); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG1, 0x1A); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG2, 0x05); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_POSTDIV2_CFG, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_POSTDIV3_CFG, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG2, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG8, 0x60); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG9, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG10, 0x38); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG11, 0x04); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_PD_CTRL0, 0x1F); + udelay(50); + + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_GLB_CFG, 0x0F); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_PD_CTRL1, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x10); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG0, 0xDB); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG1, 0x43); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x02); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG3, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_VREG_CFG, 0x04); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_DCC_CFG0, 0xD0); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_DCC_CFG1, 0x1A); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG0, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG1, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG2, 0x02); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG3, 0x05); + udelay(200); + break; + + case 1342500000: + MDSS_PLL_REG_W(phy_base, HDMI_PHY_GLB_CFG, 0x81); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_GLB_CFG, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_REFCLK_CFG, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_VCOLPF_CFG, 0x19); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFR_CFG, 0x0E); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFC1_CFG, 0x20); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFC2_CFG, 0x0D); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG0, 0x36); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG1, 0x61); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG2, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG3, 0xF6); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG4, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG0, 0x10); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG1, 0x1A); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG2, 0x05); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_POSTDIV2_CFG, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_POSTDIV3_CFG, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG2, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG8, 0x60); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG9, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG10, 0x3E); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG11, 0x05); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_PD_CTRL0, 0x1F); + udelay(50); + + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_GLB_CFG, 0x0F); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_PD_CTRL1, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x10); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG0, 0xDB); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG1, 0x43); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x05); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG3, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_VREG_CFG, 0x04); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_DCC_CFG0, 0xD0); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_DCC_CFG1, 0x1A); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG0, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG1, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG2, 0x11); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG3, 0x05); + udelay(200); + break; + + case 1485000000: + MDSS_PLL_REG_W(phy_base, HDMI_PHY_GLB_CFG, 0x81); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_GLB_CFG, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_REFCLK_CFG, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_VCOLPF_CFG, 0x19); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFR_CFG, 0x0E); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFC1_CFG, 0x20); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LPFC2_CFG, 0x0D); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG0, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG1, 0x65); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG2, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG3, 0xAC); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_SDM_CFG4, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG0, 0x10); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG1, 0x1A); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_LKDET_CFG2, 0x05); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_POSTDIV2_CFG, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_POSTDIV3_CFG, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG2, 0x01); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG8, 0x60); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG9, 0x00); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG10, 0xCD); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_CAL_CFG11, 0x05); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_PD_CTRL0, 0x1F); + udelay(50); + + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_GLB_CFG, 0x0F); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_PD_CTRL1, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x10); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG0, 0xDB); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG1, 0x43); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x06); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG3, 0x03); + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_VREG_CFG, 0x04); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_DCC_CFG0, 0xD0); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_DCC_CFG1, 0x1A); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG0, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG1, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG2, 0x02); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG3, 0x05); + udelay(200); + break; + + default: + pr_debug("Use pll settings calculator for rate=%ld\n", rate); + + MDSS_PLL_REG_W(phy_base, HDMI_PHY_GLB_CFG, 0x81); + hdmi_phy_pll_calculator(rate, hdmi_pll_res); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_PD_CTRL0, 0x1F); + udelay(50); + + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_GLB_CFG, 0x0F); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_PD_CTRL1, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x10); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG0, 0xDB); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG1, 0x43); + + if (rate < 825000000) { + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x01); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG3, 0x00); + } else if (rate >= 825000000 && rate < 1342500000) { + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x05); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG3, 0x03); + } else { + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG2, 0x06); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_ANA_CFG3, 0x03); + } + + MDSS_PLL_REG_W(pll_base, HDMI_UNI_PLL_VREG_CFG, 0x04); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_DCC_CFG0, 0xD0); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_DCC_CFG1, 0x1A); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG0, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG1, 0x00); + + if (rate < 825000000) + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG2, 0x01); + else + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG2, 0x00); + + MDSS_PLL_REG_W(phy_base, HDMI_PHY_TXCAL_CFG3, 0x05); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_BIST_PATN0, 0x62); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_BIST_PATN1, 0x03); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_BIST_PATN2, 0x69); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_BIST_PATN3, 0x02); + + udelay(200); + + MDSS_PLL_REG_W(phy_base, HDMI_PHY_BIST_CFG1, 0x00); + MDSS_PLL_REG_W(phy_base, HDMI_PHY_BIST_CFG0, 0x00); + } + + /* Make sure writes complete before disabling iface clock */ + mb(); + + mdss_pll_resource_enable(hdmi_pll_res, false); + + if (set_power_dwn) + hdmi_vco_enable(c); + + vco->rate = rate; + vco->rate_set = true; + + return 0; +} /* hdmi_pll_set_rate */ + +/* HDMI PLL DIV CLK */ + +static unsigned long hdmi_vco_get_rate(struct clk *c) +{ + unsigned long freq = 0; + int rc; + struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c); + struct mdss_pll_resources *hdmi_pll_res = vco->priv; + + rc = mdss_pll_resource_enable(hdmi_pll_res, true); + if (rc) { + pr_err("pll resource can't be enabled\n"); + return rc; + } + + freq = MDSS_PLL_REG_R(hdmi_pll_res->pll_base, + HDMI_UNI_PLL_CAL_CFG11) << 8 | + MDSS_PLL_REG_R(hdmi_pll_res->pll_base, HDMI_UNI_PLL_CAL_CFG10); + + switch (freq) { + case 742: + freq = 742500000; + break; + case 810: + if (MDSS_PLL_REG_R(hdmi_pll_res->pll_base, + HDMI_UNI_PLL_SDM_CFG3) == 0x18) + freq = 810000000; + else + freq = 810900000; + break; + case 1342: + freq = 1342500000; + break; + default: + freq *= 1000000; + } + mdss_pll_resource_enable(hdmi_pll_res, false); + + return freq; +} + +static long hdmi_vco_round_rate(struct clk *c, unsigned long rate) +{ + unsigned long rrate = rate; + struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c); + + if (rate < vco->min_rate) + rrate = vco->min_rate; + if (rate > vco->max_rate) + rrate = vco->max_rate; + + pr_debug("rrate=%ld\n", rrate); + + return rrate; +} + +static int hdmi_vco_prepare(struct clk *c) +{ + struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c); + int ret = 0; + + pr_debug("rate=%ld\n", vco->rate); + + if (!vco->rate_set && vco->rate) + ret = hdmi_vco_set_rate(c, vco->rate); + + return ret; +} + +static void hdmi_vco_unprepare(struct clk *c) +{ + struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c); + + vco->rate_set = false; +} + +static int hdmi_pll_lock_status(struct mdss_pll_resources *hdmi_pll_res) +{ + u32 status; + int pll_locked = 0; + int rc; + + rc = mdss_pll_resource_enable(hdmi_pll_res, true); + if (rc) { + pr_err("pll resource can't be enabled\n"); + return rc; + } + + /* poll for PLL ready status */ + if (readl_poll_timeout_noirq( + (hdmi_pll_res->phy_base + HDMI_PHY_STATUS), + status, ((status & BIT(0)) == 1), + HDMI_PLL_POLL_MAX_READS, + HDMI_PLL_POLL_TIMEOUT_US)) { + pr_debug("HDMI PLL status=%x failed to Lock\n", status); + pll_locked = 0; + } else { + pll_locked = 1; + } + mdss_pll_resource_enable(hdmi_pll_res, false); + + return pll_locked; +} + +static enum handoff hdmi_vco_handoff(struct clk *c) +{ + enum handoff ret = HANDOFF_DISABLED_CLK; + struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c); + struct mdss_pll_resources *hdmi_pll_res = vco->priv; + + if (mdss_pll_resource_enable(hdmi_pll_res, true)) { + pr_err("pll resource can't be enabled\n"); + return ret; + } + + hdmi_pll_res->handoff_resources = true; + + if (hdmi_pll_lock_status(hdmi_pll_res)) { + hdmi_pll_res->pll_on = true; + c->rate = hdmi_vco_get_rate(c); + ret = HANDOFF_ENABLED_CLK; + } else { + hdmi_pll_res->handoff_resources = false; + mdss_pll_resource_enable(hdmi_pll_res, false); + } + + pr_debug("done, ret=%d\n", ret); + return ret; +} + +static struct clk_ops hdmi_vco_clk_ops = { + .enable = hdmi_vco_enable, + .set_rate = hdmi_vco_set_rate, + .get_rate = hdmi_vco_get_rate, + .round_rate = hdmi_vco_round_rate, + .prepare = hdmi_vco_prepare, + .unprepare = hdmi_vco_unprepare, + .disable = hdmi_vco_disable, + .handoff = hdmi_vco_handoff, +}; + +static struct hdmi_pll_vco_clk hdmi_vco_clk = { + .min_rate = 600000000, + .max_rate = 1800000000, + .c = { + .dbg_name = "hdmi_vco_clk", + .ops = &hdmi_vco_clk_ops, + CLK_INIT(hdmi_vco_clk.c), + }, +}; + +struct div_clk hdmipll_div1_clk = { + .data = { + .div = 1, + .min_div = 1, + .max_div = 1, + }, + .c = { + .parent = &hdmi_vco_clk.c, + .dbg_name = "hdmipll_div1_clk", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(hdmipll_div1_clk.c), + }, +}; + +struct div_clk hdmipll_div2_clk = { + .data = { + .div = 2, + .min_div = 2, + .max_div = 2, + }, + .c = { + .parent = &hdmi_vco_clk.c, + .dbg_name = "hdmipll_div2_clk", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(hdmipll_div2_clk.c), + }, +}; + +struct div_clk hdmipll_div4_clk = { + .data = { + .div = 4, + .min_div = 4, + .max_div = 4, + }, + .c = { + .parent = &hdmi_vco_clk.c, + .dbg_name = "hdmipll_div4_clk", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(hdmipll_div4_clk.c), + }, +}; + +struct div_clk hdmipll_div6_clk = { + .data = { + .div = 6, + .min_div = 6, + .max_div = 6, + }, + .c = { + .parent = &hdmi_vco_clk.c, + .dbg_name = "hdmipll_div6_clk", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(hdmipll_div6_clk.c), + }, +}; + +static int hdmipll_set_mux_sel(struct mux_clk *clk, int mux_sel) +{ + struct mdss_pll_resources *hdmi_pll_res = clk->priv; + int rc; + + rc = mdss_pll_resource_enable(hdmi_pll_res, true); + if (rc) { + pr_err("pll resource can't be enabled\n"); + return rc; + } + + pr_debug("mux_sel=%d\n", mux_sel); + MDSS_PLL_REG_W(hdmi_pll_res->pll_base, + HDMI_UNI_PLL_POSTDIV1_CFG, mux_sel); + mdss_pll_resource_enable(hdmi_pll_res, false); + + return 0; +} + +static int hdmipll_get_mux_sel(struct mux_clk *clk) +{ + int rc; + int mux_sel = 0; + struct mdss_pll_resources *hdmi_pll_res = clk->priv; + + rc = mdss_pll_resource_enable(hdmi_pll_res, true); + if (rc) { + pr_err("pll resource can't be enabled\n"); + return rc; + } + + mux_sel = MDSS_PLL_REG_R(hdmi_pll_res->pll_base, + HDMI_UNI_PLL_POSTDIV1_CFG); + mdss_pll_resource_enable(hdmi_pll_res, false); + mux_sel &= 0x03; + pr_debug("mux_sel=%d\n", mux_sel); + + return mux_sel; +} + +static struct clk_mux_ops hdmipll_mux_ops = { + .set_mux_sel = hdmipll_set_mux_sel, + .get_mux_sel = hdmipll_get_mux_sel, +}; + +static struct clk_ops hdmi_mux_ops; + +static int hdmi_mux_prepare(struct clk *c) +{ + int ret = 0; + + if (c && c->ops && c->ops->set_rate) + ret = c->ops->set_rate(c, c->rate); + + return ret; +} + +struct mux_clk hdmipll_mux_clk = { + MUX_SRC_LIST( + { &hdmipll_div1_clk.c, 0 }, + { &hdmipll_div2_clk.c, 1 }, + { &hdmipll_div4_clk.c, 2 }, + { &hdmipll_div6_clk.c, 3 }, + ), + .ops = &hdmipll_mux_ops, + .c = { + .parent = &hdmipll_div1_clk.c, + .dbg_name = "hdmipll_mux_clk", + .ops = &hdmi_mux_ops, + CLK_INIT(hdmipll_mux_clk.c), + }, +}; + +struct div_clk hdmipll_clk_src = { + .data = { + .div = 5, + .min_div = 5, + .max_div = 5, + }, + .c = { + .parent = &hdmipll_mux_clk.c, + .dbg_name = "hdmipll_clk_src", + .ops = &clk_ops_div, + CLK_INIT(hdmipll_clk_src.c), + }, +}; + +static struct clk_lookup hdmipllcc_8974[] = { + CLK_LOOKUP("extp_clk_src", hdmipll_clk_src.c, + "fd8c0000.qcom,mmsscc-mdss"), +}; + +int hdmi_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int rc = -ENOTSUPP; + + if (!pll_res || !pll_res->phy_base || !pll_res->pll_base) { + pr_err("Invalide input parameters\n"); + return -EPROBE_DEFER; + } + + /* Set client data for vco, mux and div clocks */ + hdmipll_clk_src.priv = pll_res; + hdmipll_mux_clk.priv = pll_res; + hdmipll_div1_clk.priv = pll_res; + hdmipll_div2_clk.priv = pll_res; + hdmipll_div4_clk.priv = pll_res; + hdmipll_div6_clk.priv = pll_res; + hdmi_vco_clk.priv = pll_res; + + /* Set hdmi mux clock operation */ + hdmi_mux_ops = clk_ops_gen_mux; + hdmi_mux_ops.prepare = hdmi_mux_prepare; + + rc = of_msm_clock_register(pdev->dev.of_node, hdmipllcc_8974, + ARRAY_SIZE(hdmipllcc_8974)); + if (rc) { + pr_err("Clock register failed rc=%d\n", rc); + rc = -EPROBE_DEFER; + } + + return rc; +} diff --git a/drivers/clk/msm/mdss/mdss-hdmi-pll.h b/drivers/clk/msm/mdss/mdss-hdmi-pll.h new file mode 100644 index 000000000000..70630cc68834 --- /dev/null +++ b/drivers/clk/msm/mdss/mdss-hdmi-pll.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2012-2014, 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. + */ + +#ifndef __MDSS_HDMI_PLL_H +#define __MDSS_HDMI_PLL_H + +struct hdmi_pll_vco_clk { + unsigned long rate; /* current vco rate */ + unsigned long min_rate; /* min vco rate */ + unsigned long max_rate; /* max vco rate */ + bool rate_set; + void *priv; + + struct clk c; +}; + +int hdmi_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); + +#endif diff --git a/drivers/clk/msm/mdss/mdss-pll-util.c b/drivers/clk/msm/mdss/mdss-pll-util.c new file mode 100644 index 000000000000..c5d5cf385248 --- /dev/null +++ b/drivers/clk/msm/mdss/mdss-pll-util.c @@ -0,0 +1,337 @@ +/* Copyright (c) 2013-2014, 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 <linux/kernel.h> +#include <linux/err.h> +#include <linux/string.h> +#include <linux/clk/msm-clock-generic.h> + +#include "mdss-pll.h" + +int mdss_pll_util_resource_init(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int rc = 0; + struct dss_module_power *mp = &pll_res->mp; + + rc = msm_dss_config_vreg(&pdev->dev, + mp->vreg_config, mp->num_vreg, 1); + if (rc) { + pr_err("Vreg config failed rc=%d\n", rc); + goto vreg_err; + } + + rc = msm_dss_get_clk(&pdev->dev, mp->clk_config, mp->num_clk); + if (rc) { + pr_err("Clock get failed rc=%d\n", rc); + goto clk_err; + } + + return rc; + +clk_err: + msm_dss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg, 0); +vreg_err: + return rc; +} + +void mdss_pll_util_resource_deinit(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + struct dss_module_power *mp = &pll_res->mp; + + msm_dss_put_clk(mp->clk_config, mp->num_clk); + + msm_dss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg, 0); +} + +void mdss_pll_util_resource_release(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + struct dss_module_power *mp = &pll_res->mp; + + devm_kfree(&pdev->dev, mp->clk_config); + devm_kfree(&pdev->dev, mp->vreg_config); + mp->num_vreg = 0; + mp->num_clk = 0; +} + +int mdss_pll_util_resource_enable(struct mdss_pll_resources *pll_res, + bool enable) +{ + int rc = 0; + struct dss_module_power *mp = &pll_res->mp; + + if (enable) { + if (pll_res->resource_refcount) { + pll_res->resource_refcount++; + return 0; + } + + rc = msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, enable); + if (rc) { + pr_err("Failed to enable vregs rc=%d\n", rc); + goto vreg_err; + } + + rc = msm_dss_clk_set_rate(mp->clk_config, mp->num_clk); + if (rc) { + pr_err("Failed to set clock rate rc=%d\n", rc); + goto clk_err; + } + + rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable); + if (rc) { + pr_err("clock enable failed rc:%d\n", rc); + goto clk_err; + } + pll_res->resource_refcount++; + } else { + if (pll_res->resource_refcount) { + pll_res->resource_refcount--; + } else { + pr_err("Trying to disable the resources without enabling them\n"); + return -EINVAL; + } + + if (!pll_res->resource_refcount) { + msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable); + + msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, + enable); + } + } + + return rc; + +clk_err: + msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, 0); +vreg_err: + return rc; +} + +static int mdss_pll_util_parse_dt_supply(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int i = 0, rc = 0; + u32 tmp = 0; + struct device_node *of_node = NULL, *supply_root_node = NULL; + struct device_node *supply_node = NULL; + struct dss_module_power *mp = &pll_res->mp; + + of_node = pdev->dev.of_node; + + mp->num_vreg = 0; + supply_root_node = of_get_child_by_name(of_node, + "qcom,platform-supply-entries"); + if (!supply_root_node) { + pr_err("no supply entry present\n"); + return rc; + } + + for_each_child_of_node(supply_root_node, supply_node) { + mp->num_vreg++; + } + + if (mp->num_vreg == 0) { + pr_debug("no vreg\n"); + return rc; + } else { + pr_debug("vreg found. count=%d\n", mp->num_vreg); + } + + mp->vreg_config = devm_kzalloc(&pdev->dev, sizeof(struct dss_vreg) * + mp->num_vreg, GFP_KERNEL); + if (!mp->vreg_config) { + pr_err("can't alloc vreg mem\n"); + rc = -ENOMEM; + return rc; + } + + for_each_child_of_node(supply_root_node, supply_node) { + + const char *st = NULL; + + rc = of_property_read_string(supply_node, + "qcom,supply-name", &st); + if (rc) { + pr_err(":error reading name. rc=%d\n", rc); + goto error; + } + + strlcpy(mp->vreg_config[i].vreg_name, st, + sizeof(mp->vreg_config[i].vreg_name)); + + rc = of_property_read_u32(supply_node, + "qcom,supply-min-voltage", &tmp); + if (rc) { + pr_err(": error reading min volt. rc=%d\n", rc); + goto error; + } + mp->vreg_config[i].min_voltage = tmp; + + rc = of_property_read_u32(supply_node, + "qcom,supply-max-voltage", &tmp); + if (rc) { + pr_err(": error reading max volt. rc=%d\n", rc); + goto error; + } + mp->vreg_config[i].max_voltage = tmp; + + rc = of_property_read_u32(supply_node, + "qcom,supply-enable-load", &tmp); + if (rc) { + pr_err(": error reading enable load. rc=%d\n", rc); + goto error; + } + mp->vreg_config[i].enable_load = tmp; + + rc = of_property_read_u32(supply_node, + "qcom,supply-disable-load", &tmp); + if (rc) { + pr_err(": error reading disable load. rc=%d\n", rc); + goto error; + } + mp->vreg_config[i].disable_load = tmp; + + rc = of_property_read_u32(supply_node, + "qcom,supply-pre-on-sleep", &tmp); + if (rc) + pr_debug("error reading supply pre sleep value. rc=%d\n", + rc); + + mp->vreg_config[i].pre_on_sleep = (!rc ? tmp : 0); + + rc = of_property_read_u32(supply_node, + "qcom,supply-pre-off-sleep", &tmp); + if (rc) + pr_debug("error reading supply pre sleep value. rc=%d\n", + rc); + + mp->vreg_config[i].pre_off_sleep = (!rc ? tmp : 0); + + rc = of_property_read_u32(supply_node, + "qcom,supply-post-on-sleep", &tmp); + if (rc) + pr_debug("error reading supply post sleep value. rc=%d\n", + rc); + + mp->vreg_config[i].post_on_sleep = (!rc ? tmp : 0); + + rc = of_property_read_u32(supply_node, + "qcom,supply-post-off-sleep", &tmp); + if (rc) + pr_debug("error reading supply post sleep value. rc=%d\n", + rc); + + mp->vreg_config[i].post_off_sleep = (!rc ? tmp : 0); + + pr_debug("%s min=%d, max=%d, enable=%d, disable=%d, preonsleep=%d, postonsleep=%d, preoffsleep=%d, postoffsleep=%d\n", + mp->vreg_config[i].vreg_name, + mp->vreg_config[i].min_voltage, + mp->vreg_config[i].max_voltage, + mp->vreg_config[i].enable_load, + mp->vreg_config[i].disable_load, + mp->vreg_config[i].pre_on_sleep, + mp->vreg_config[i].post_on_sleep, + mp->vreg_config[i].pre_off_sleep, + mp->vreg_config[i].post_off_sleep); + ++i; + + rc = 0; + } + + return rc; + +error: + if (mp->vreg_config) { + devm_kfree(&pdev->dev, mp->vreg_config); + mp->vreg_config = NULL; + mp->num_vreg = 0; + } + + return rc; +} + +static int mdss_pll_util_parse_dt_clock(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + u32 i = 0, rc = 0; + struct dss_module_power *mp = &pll_res->mp; + const char *clock_name; + u32 clock_rate; + + mp->num_clk = of_property_count_strings(pdev->dev.of_node, + "clock-names"); + if (mp->num_clk <= 0) { + pr_err("clocks are not defined\n"); + goto clk_err; + } + + mp->clk_config = devm_kzalloc(&pdev->dev, + sizeof(struct dss_clk) * mp->num_clk, GFP_KERNEL); + if (!mp->clk_config) { + pr_err("clock configuration allocation failed\n"); + rc = -ENOMEM; + mp->num_clk = 0; + goto clk_err; + } + + for (i = 0; i < mp->num_clk; i++) { + of_property_read_string_index(pdev->dev.of_node, "clock-names", + i, &clock_name); + strlcpy(mp->clk_config[i].clk_name, clock_name, + sizeof(mp->clk_config[i].clk_name)); + + of_property_read_u32_index(pdev->dev.of_node, "clock-rate", + i, &clock_rate); + mp->clk_config[i].rate = clock_rate; + + if (!clock_rate) + mp->clk_config[i].type = DSS_CLK_AHB; + else + mp->clk_config[i].type = DSS_CLK_PCLK; + } + +clk_err: + return rc; +} + +int mdss_pll_util_resource_parse(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int rc = 0; + struct dss_module_power *mp = &pll_res->mp; + + rc = mdss_pll_util_parse_dt_supply(pdev, pll_res); + if (rc) { + pr_err("vreg parsing failed rc=%d\n", rc); + goto end; + } + + rc = mdss_pll_util_parse_dt_clock(pdev, pll_res); + if (rc) { + pr_err("clock name parsing failed rc=%d", rc); + goto clk_err; + } + + return rc; + +clk_err: + devm_kfree(&pdev->dev, mp->vreg_config); + mp->num_vreg = 0; +end: + return rc; +} diff --git a/drivers/clk/msm/mdss/mdss-pll.c b/drivers/clk/msm/mdss/mdss-pll.c new file mode 100644 index 000000000000..8af5fb974989 --- /dev/null +++ b/drivers/clk/msm/mdss/mdss-pll.c @@ -0,0 +1,325 @@ +/* Copyright (c) 2013-2014, 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 <linux/module.h> +#include <linux/of_device.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/iopoll.h> +#include <linux/clk/msm-clock-generic.h> +#include <mach/msm_iomap.h> + +#include "mdss-pll.h" +#include "mdss-edp-pll.h" +#include "mdss-dsi-pll.h" +#include "mdss-hdmi-pll.h" + +int mdss_pll_resource_enable(struct mdss_pll_resources *pll_res, bool enable) +{ + int rc = 0; + if (!pll_res) { + pr_err("Invalid input parameters\n"); + return -EINVAL; + } + + /* + * Don't turn off resources during handoff or add more than + * 1 refcount. + */ + if (pll_res->handoff_resources && + (!enable || (enable && pll_res->resource_refcount == 1))) { + pr_debug("Do not turn on/off pll resources during handoff case\n"); + return rc; + } + + mutex_lock(&pll_res->resource_lock); + rc = mdss_pll_util_resource_enable(pll_res, enable); + if (rc) + pr_err("Resource update failed rc=%d\n", rc); + else + pll_res->resource_enable = enable; + mutex_unlock(&pll_res->resource_lock); + + return rc; +} + +static int mdss_pll_resource_init(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + if (!pdev || !pll_res) { + pr_err("Invalid input parameters\n"); + return -EINVAL; + } + + return mdss_pll_util_resource_init(pdev, pll_res); +} + +static void mdss_pll_resource_deinit(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + if (!pdev || !pll_res) { + pr_err("Invalid input parameters\n"); + return; + } + + mdss_pll_util_resource_deinit(pdev, pll_res); +} + +static void mdss_pll_resource_release(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + if (!pdev || !pll_res) { + pr_err("Invalid input parameters\n"); + return; + } + + mdss_pll_util_resource_release(pdev, pll_res); +} + +static int mdss_pll_resource_parse(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int rc = 0; + const char *compatible_stream; + + if (!pdev || !pll_res) { + pr_err("Invalid input parameters\n"); + return -EINVAL; + } + + rc = mdss_pll_util_resource_parse(pdev, pll_res); + if (rc) { + pr_err("Failed to parse the resources rc=%d\n", rc); + goto end; + } + + compatible_stream = of_get_property(pdev->dev.of_node, + "compatible", NULL); + if (!compatible_stream) { + pr_err("Failed to parse the compatible stream\n"); + goto err; + } + + if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll")) + pll_res->pll_interface_type = MDSS_DSI_PLL; + else if (!strcmp(compatible_stream, "qcom,mdss_edp_pll")) + pll_res->pll_interface_type = MDSS_EDP_PLL; + else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll")) + pll_res->pll_interface_type = MDSS_HDMI_PLL; + else + goto err; + + return rc; + +err: + mdss_pll_resource_release(pdev, pll_res); +end: + return rc; +} + +static int mdss_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int rc; + + if (!pdev || !pll_res) { + pr_err("Invalid input parameters\n"); + return -EINVAL; + } + + switch (pll_res->pll_interface_type) { + case MDSS_DSI_PLL: + rc = dsi_pll_clock_register(pdev, pll_res); + break; + case MDSS_EDP_PLL: + rc = edp_pll_clock_register(pdev, pll_res); + break; + case MDSS_HDMI_PLL: + rc = hdmi_pll_clock_register(pdev, pll_res); + break; + case MDSS_UNKNOWN_PLL: + default: + rc = -EINVAL; + break; + } + + if (rc) + pr_err("Pll parent clock register failed rc=%d\n", rc); + + return rc; +} + +static int mdss_pll_probe(struct platform_device *pdev) +{ + int rc = 0; + const char *label; + struct resource *pll_base_reg; + struct resource *phy_base_reg; + struct mdss_pll_resources *pll_res; + + if (!pdev->dev.of_node) { + pr_err("MDSS pll driver only supports device tree probe\n"); + rc = -ENOTSUPP; + goto error; + } + + label = of_get_property(pdev->dev.of_node, "label", NULL); + if (!label) + pr_info("%d: MDSS pll label not specified\n", __LINE__); + else + pr_info("MDSS pll label = %s\n", label); + + pll_res = devm_kzalloc(&pdev->dev, sizeof(struct mdss_pll_resources), + GFP_KERNEL); + if (!pll_res) { + pr_err("Failed to allocate the clock pll\n"); + rc = -ENOMEM; + goto error; + } + platform_set_drvdata(pdev, pll_res); + + mutex_init(&pll_res->resource_lock); + pll_res->resource_refcount = 0; + + pll_base_reg = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "pll_base"); + if (!pll_base_reg) { + pr_err("Unable to get the pll base resources\n"); + rc = -ENOMEM; + goto io_error; + } + + pll_res->pll_base = ioremap(pll_base_reg->start, + resource_size(pll_base_reg)); + if (!pll_res->pll_base) { + pr_err("Unable to remap pll base resources\n"); + rc = -ENOMEM; + goto io_error; + } + + rc = mdss_pll_resource_parse(pdev, pll_res); + if (rc) { + pr_err("Pll resource parsing from dt failed rc=%d\n", rc); + goto res_parse_error; + } + + phy_base_reg = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "phy_base"); + if (!phy_base_reg) { + /* This resource is mandatory for HDMI pll */ + if (pll_res->pll_interface_type == MDSS_HDMI_PLL) { + pr_err("Unable to get the phy base resources\n"); + rc = -ENOMEM; + goto phy_io_error; + } + } else { + pll_res->phy_base = ioremap(pll_base_reg->start, + resource_size(pll_base_reg)); + if (!pll_res->phy_base) { + pr_err("Unable to remap pll phy base resources\n"); + rc = -ENOMEM; + goto phy_io_error; + } + } + + rc = mdss_pll_resource_init(pdev, pll_res); + if (rc) { + pr_err("Pll resource init failed rc=%d\n", rc); + goto res_init_error; + } + + rc = mdss_pll_clock_register(pdev, pll_res); + if (rc) { + pr_err("Pll clock register failed rc=%d\n", rc); + goto clock_register_error; + } + + return rc; + +clock_register_error: + mdss_pll_resource_deinit(pdev, pll_res); +res_init_error: + if (pll_res->phy_base) + iounmap(pll_res->phy_base); +phy_io_error: + mdss_pll_resource_release(pdev, pll_res); +res_parse_error: + iounmap(pll_res->pll_base); +io_error: + mutex_destroy(&pll_res->resource_lock); + devm_kfree(&pdev->dev, pll_res); +error: + return rc; +} + +static int mdss_pll_remove(struct platform_device *pdev) +{ + struct mdss_pll_resources *pll_res; + + pll_res = platform_get_drvdata(pdev); + if (!pll_res) { + pr_err("Invalid PLL resource data"); + return 0; + } + + mdss_pll_resource_deinit(pdev, pll_res); + if (pll_res->phy_base) + iounmap(pll_res->phy_base); + mdss_pll_resource_release(pdev, pll_res); + iounmap(pll_res->pll_base); + mutex_destroy(&pll_res->resource_lock); + devm_kfree(&pdev->dev, pll_res); + return 0; +} + +static const struct of_device_id mdss_pll_dt_match[] = { + {.compatible = "qcom,mdss_dsi_pll"}, + {.compatible = "qcom,mdss_edp_pll"}, + {.compatible = "qcom,mdss_hdmi_pll"}, + {} +}; + +MODULE_DEVICE_TABLE(of, mdss_clock_dt_match); + +static struct platform_driver mdss_pll_driver = { + .probe = mdss_pll_probe, + .remove = mdss_pll_remove, + .driver = { + .name = "mdss_pll", + .of_match_table = mdss_pll_dt_match, + }, +}; + +static int __init mdss_pll_driver_init(void) +{ + int rc; + + rc = platform_driver_register(&mdss_pll_driver); + if (rc) + pr_err("mdss_register_pll_driver() failed!\n"); + + return rc; +} +fs_initcall(mdss_pll_driver_init); + +static void __exit mdss_pll_driver_deinit(void) +{ + platform_driver_unregister(&mdss_pll_driver); +} +module_exit(mdss_pll_driver_deinit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("mdss pll driver"); diff --git a/drivers/clk/msm/mdss/mdss-pll.h b/drivers/clk/msm/mdss/mdss-pll.h new file mode 100644 index 000000000000..023eb0bbb4f6 --- /dev/null +++ b/drivers/clk/msm/mdss/mdss-pll.h @@ -0,0 +1,89 @@ +/* Copyright (c) 2013-2014, 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. + */ + +#ifndef __MDSS_PLL_H +#define __MDSS_PLL_H + +#include <linux/mdss_io_util.h> +#include <linux/io.h> + +#define MDSS_PLL_REG_W(base, offset, data) \ + writel_relaxed((data), (base) + (offset)) +#define MDSS_PLL_REG_R(base, offset) readl_relaxed((base) + (offset)) + +enum { + MDSS_DSI_PLL, + MDSS_EDP_PLL, + MDSS_HDMI_PLL, + MDSS_UNKNOWN_PLL, +}; + +struct mdss_pll_resources { + + /* Pll specific resources like GPIO, power supply, clocks, etc*/ + struct dss_module_power mp; + + /* dsi/edp/hmdi plls' base register and phy register mapping */ + void __iomem *pll_base; + void __iomem *phy_base; + + /* + * Certain pll's needs to update the same vco rate after resume in + * suspend/resume scenario. Cached the vco rate for such plls. + */ + unsigned long vco_cached_rate; + + /* dsi/edp/hmdi pll interface type */ + u32 pll_interface_type; + + /* + * Keep track to resource status to avoid updating same status for the + * pll from different paths + */ + bool resource_enable; + + /* + * Certain plls' do not allow vco rate update if it is on. Keep track of + * status for them to turn on/off after set rate success. + */ + bool pll_on; + + /* + * handoff_status is true of pll is already enabled by bootloader with + * continuous splash enable case. Clock API will call the handoff API + * to enable the status. It is disabled if continuous splash + * feature is disabled. + */ + bool handoff_resources; + + /* + * Keep refrence count of pll resource client to avoid releasing them + * before all clients are finished with their tasks + */ + unsigned int resource_refcount; + + /* Lock status to provide updated resource status to all clients */ + struct mutex resource_lock; +}; + +int mdss_pll_resource_enable(struct mdss_pll_resources *pll_res, bool enable); +int mdss_pll_util_resource_init(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +void mdss_pll_util_resource_deinit(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +void mdss_pll_util_resource_release(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +int mdss_pll_util_resource_enable(struct mdss_pll_resources *pll_res, + bool enable); +int mdss_pll_util_resource_parse(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +#endif |
