summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorDhaval Patel <pdhaval@codeaurora.org>2013-12-19 14:52:24 -0800
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-23 20:41:10 -0700
commitbd4e625e49a47d7a41dc3f937975b1b9c0d0bf4a (patch)
treeea9fab733ccac2c011bb015ac893db52ffb9dc9a /drivers
parent60553269d14f6dc9ca3a0d3285060aa67d4ad424 (diff)
clk: qcom: mdss: Add mdss pll clock driver support
Each display output interface such as eDP, HDMI and DSI are clocked by different pll clocks to support various displays at different resolution simultaneously. The mdss pll driver handles all these display output interfaces' pll clocks separately. It also handles their resources through dtsi configuration. Change-Id: I1de2ae9a0549de901a6c82ea489199a722344dc4 Signed-off-by: Dhaval Patel <pdhaval@codeaurora.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/msm/Kconfig2
-rw-r--r--drivers/clk/msm/Makefile1
-rw-r--r--drivers/clk/msm/mdss/Kconfig6
-rw-r--r--drivers/clk/msm/mdss/Makefile5
-rw-r--r--drivers/clk/msm/mdss/mdss-dsi-pll-28hpm.c880
-rw-r--r--drivers/clk/msm/mdss/mdss-dsi-pll.h40
-rw-r--r--drivers/clk/msm/mdss/mdss-edp-pll-28hpm.c585
-rw-r--r--drivers/clk/msm/mdss/mdss-edp-pll.h27
-rw-r--r--drivers/clk/msm/mdss/mdss-hdmi-pll-28hpm.c1094
-rw-r--r--drivers/clk/msm/mdss/mdss-hdmi-pll.h29
-rw-r--r--drivers/clk/msm/mdss/mdss-pll-util.c337
-rw-r--r--drivers/clk/msm/mdss/mdss-pll.c325
-rw-r--r--drivers/clk/msm/mdss/mdss-pll.h89
13 files changed, 3420 insertions, 0 deletions
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