summaryrefslogtreecommitdiff
path: root/drivers/clk/qcom
diff options
context:
space:
mode:
authorPadmanabhan Komanduru <pkomandu@codeaurora.org>2017-01-06 12:52:52 +0530
committerPadmanabhan Komanduru <pkomandu@codeaurora.org>2017-01-25 14:45:26 +0530
commite2d34afade748addcc6f82d84493121ecebcc6cc (patch)
tree7d564575e4324d056fa971af80dab5601fee69c7 /drivers/clk/qcom
parent4709f01bbd7b5412d1220d00945ba1fcdbff331b (diff)
clk: qcom: mdss: add support for MDSS DP PLL for SDM660
Model and configure MDSS Display Port PLL for SDM660 target. Add changes to define and register DP VCO, divider and mux clocks as per common clock infrastructure. Change-Id: Ice83e21323087e81e2f30998260be85120e41fa8 Signed-off-by: Padmanabhan Komanduru <pkomandu@codeaurora.org>
Diffstat (limited to 'drivers/clk/qcom')
-rw-r--r--drivers/clk/qcom/mdss/Makefile2
-rw-r--r--drivers/clk/qcom/mdss/mdss-dp-pll-14nm-util.c624
-rw-r--r--drivers/clk/qcom/mdss/mdss-dp-pll-14nm.c234
-rw-r--r--drivers/clk/qcom/mdss/mdss-dp-pll-14nm.h193
-rw-r--r--drivers/clk/qcom/mdss/mdss-dp-pll.h35
-rw-r--r--drivers/clk/qcom/mdss/mdss-pll.c10
-rw-r--r--drivers/clk/qcom/mdss/mdss-pll.h3
7 files changed, 1099 insertions, 2 deletions
diff --git a/drivers/clk/qcom/mdss/Makefile b/drivers/clk/qcom/mdss/Makefile
index 6a0a1de1e942..733b91b993d6 100644
--- a/drivers/clk/qcom/mdss/Makefile
+++ b/drivers/clk/qcom/mdss/Makefile
@@ -2,3 +2,5 @@ obj-$(CONFIG_QCOM_MDSS_PLL) += mdss-pll-util.o
obj-$(CONFIG_QCOM_MDSS_PLL) += mdss-pll.o
obj-$(CONFIG_QCOM_MDSS_PLL) += mdss-dsi-pll-14nm.o
obj-$(CONFIG_QCOM_MDSS_PLL) += mdss-dsi-pll-14nm-util.o
+obj-$(CONFIG_QCOM_MDSS_PLL) += mdss-dp-pll-14nm.o
+obj-$(CONFIG_QCOM_MDSS_PLL) += mdss-dp-pll-14nm-util.o
diff --git a/drivers/clk/qcom/mdss/mdss-dp-pll-14nm-util.c b/drivers/clk/qcom/mdss/mdss-dp-pll-14nm-util.c
new file mode 100644
index 000000000000..0965d5590a28
--- /dev/null
+++ b/drivers/clk/qcom/mdss/mdss-dp-pll-14nm-util.c
@@ -0,0 +1,624 @@
+/* Copyright (c) 2016-2017, 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/usb/usbpd.h>
+
+#include "mdss-pll.h"
+#include "mdss-dp-pll.h"
+#include "mdss-dp-pll-14nm.h"
+
+int dp_mux_set_parent_14nm(void *context, unsigned int reg, unsigned int val)
+{
+ struct mdss_pll_resources *dp_res = context;
+ int rc;
+ u32 auxclk_div;
+
+ rc = mdss_pll_resource_enable(dp_res, true);
+ if (rc) {
+ pr_err("Failed to enable mdss DP PLL resources\n");
+ return rc;
+ }
+
+ auxclk_div = MDSS_PLL_REG_R(dp_res->phy_base, DP_PHY_VCO_DIV);
+ auxclk_div &= ~0x03; /* bits 0 to 1 */
+
+ if (val == 0) /* mux parent index = 0 */
+ auxclk_div |= 1;
+ else if (val == 1) /* mux parent index = 1 */
+ auxclk_div |= 2;
+
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ DP_PHY_VCO_DIV, auxclk_div);
+ /* Make sure the PHY registers writes are done */
+ wmb();
+ pr_debug("%s: mux=%d auxclk_div=%x\n", __func__, val, auxclk_div);
+
+ mdss_pll_resource_enable(dp_res, false);
+
+ return 0;
+}
+
+int dp_mux_get_parent_14nm(void *context, unsigned int reg, unsigned int *val)
+{
+ int rc;
+ u32 auxclk_div = 0;
+ struct mdss_pll_resources *dp_res = context;
+
+ rc = mdss_pll_resource_enable(dp_res, true);
+ if (rc) {
+ pr_err("Failed to enable dp_res resources\n");
+ return rc;
+ }
+
+ auxclk_div = MDSS_PLL_REG_R(dp_res->phy_base, DP_PHY_VCO_DIV);
+ auxclk_div &= 0x03;
+
+ if (auxclk_div == 1) /* Default divider */
+ *val = 0;
+ else if (auxclk_div == 2)
+ *val = 1;
+
+ mdss_pll_resource_enable(dp_res, false);
+
+ pr_debug("%s: auxclk_div=%d, val=%d\n", __func__, auxclk_div, *val);
+
+ return 0;
+}
+
+static int dp_vco_pll_init_db_14nm(struct dp_pll_db *pdb,
+ unsigned long rate)
+{
+ struct mdss_pll_resources *dp_res = pdb->pll;
+ u32 spare_value = 0;
+
+ spare_value = MDSS_PLL_REG_R(dp_res->phy_base, DP_PHY_SPARE0);
+ pdb->lane_cnt = spare_value & 0x0F;
+ pdb->orientation = (spare_value & 0xF0) >> 4;
+
+ pr_debug("%s: spare_value=0x%x, ln_cnt=0x%x, orientation=0x%x\n",
+ __func__, spare_value, pdb->lane_cnt, pdb->orientation);
+
+ switch (rate) {
+ case DP_VCO_HSCLK_RATE_1620MHZDIV1000:
+ pdb->hsclk_sel = 0x2c;
+ pdb->dec_start_mode0 = 0x69;
+ pdb->div_frac_start1_mode0 = 0x00;
+ pdb->div_frac_start2_mode0 = 0x80;
+ pdb->div_frac_start3_mode0 = 0x07;
+ pdb->lock_cmp1_mode0 = 0xbf;
+ pdb->lock_cmp2_mode0 = 0x21;
+ pdb->lock_cmp3_mode0 = 0x00;
+ pdb->phy_vco_div = 0x1;
+ break;
+ case DP_VCO_HSCLK_RATE_2700MHZDIV1000:
+ pdb->hsclk_sel = 0x24;
+ pdb->dec_start_mode0 = 0x69;
+ pdb->div_frac_start1_mode0 = 0x00;
+ pdb->div_frac_start2_mode0 = 0x80;
+ pdb->div_frac_start3_mode0 = 0x07;
+ pdb->lock_cmp1_mode0 = 0x3f;
+ pdb->lock_cmp2_mode0 = 0x38;
+ pdb->lock_cmp3_mode0 = 0x00;
+ pdb->phy_vco_div = 0x1;
+ break;
+ case DP_VCO_HSCLK_RATE_5400MHZDIV1000:
+ pdb->hsclk_sel = 0x20;
+ pdb->dec_start_mode0 = 0x8c;
+ pdb->div_frac_start1_mode0 = 0x00;
+ pdb->div_frac_start2_mode0 = 0x00;
+ pdb->div_frac_start3_mode0 = 0x0a;
+ pdb->lock_cmp1_mode0 = 0x7f;
+ pdb->lock_cmp2_mode0 = 0x70;
+ pdb->lock_cmp3_mode0 = 0x00;
+ pdb->phy_vco_div = 0x2;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int dp_config_vco_rate_14nm(struct dp_pll_vco_clk *vco,
+ unsigned long rate)
+{
+ u32 res = 0;
+ struct mdss_pll_resources *dp_res = vco->priv;
+ struct dp_pll_db *pdb = (struct dp_pll_db *)dp_res->priv;
+
+ res = dp_vco_pll_init_db_14nm(pdb, rate);
+ if (res) {
+ pr_err("VCO Init DB failed\n");
+ return res;
+ }
+
+ if (pdb->lane_cnt != 4) {
+ if (pdb->orientation == ORIENTATION_CC2)
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ DP_PHY_PD_CTL, 0x2d);
+ else
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ DP_PHY_PD_CTL, 0x35);
+ } else {
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ DP_PHY_PD_CTL, 0x3d);
+ }
+
+ /* Make sure the PHY register writes are done */
+ wmb();
+
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_SVS_MODE_CLK_SEL, 0x01);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_SYSCLK_EN_SEL, 0x37);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_CLK_SELECT, 0x00);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_SYS_CLK_CTRL, 0x06);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3f);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_CLK_ENABLE1, 0x0e);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_BG_CTRL, 0x0f);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_SYSCLK_BUF_ENABLE, 0x06);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_CLK_SELECT, 0x30);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_PLL_IVCO, 0x0f);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_PLL_CCTRL_MODE0, 0x28);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_PLL_RCTRL_MODE0, 0x16);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_CP_CTRL_MODE0, 0x0b);
+
+ /* Parameters dependent on vco clock frequency */
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_HSCLK_SEL, pdb->hsclk_sel);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_DEC_START_MODE0, pdb->dec_start_mode0);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_DIV_FRAC_START1_MODE0, pdb->div_frac_start1_mode0);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_DIV_FRAC_START2_MODE0, pdb->div_frac_start2_mode0);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_DIV_FRAC_START3_MODE0, pdb->div_frac_start3_mode0);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_LOCK_CMP1_MODE0, pdb->lock_cmp1_mode0);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_LOCK_CMP2_MODE0, pdb->lock_cmp2_mode0);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_LOCK_CMP3_MODE0, pdb->lock_cmp3_mode0);
+
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x40);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_VCO_TUNE_MAP, 0x00);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_BG_TIMER, 0x08);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_CORECLK_DIV, 0x05);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_VCO_TUNE_CTRL, 0x00);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_VCO_TUNE1_MODE0, 0x00);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_VCO_TUNE2_MODE0, 0x00);
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_VCO_TUNE_CTRL, 0x00);
+ wmb(); /* make sure write happens */
+
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_CORE_CLK_EN, 0x0f);
+ wmb(); /* make sure write happens */
+
+ if (pdb->orientation == ORIENTATION_CC2)
+ MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_MODE, 0xc8);
+ else
+ MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_MODE, 0xd8);
+ wmb(); /* make sure write happens */
+
+ /* TX Lane configuration */
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ DP_PHY_TX0_TX1_LANE_CTL, 0x05);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ DP_PHY_TX2_TX3_LANE_CTL, 0x05);
+
+ /* TX-0 register configuration */
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX0_OFFSET + TXn_TRANSCEIVER_BIAS_EN, 0x1a);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX0_OFFSET + TXn_VMODE_CTRL1, 0x40);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX0_OFFSET + TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX0_OFFSET + TXn_INTERFACE_SELECT, 0x3d);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX0_OFFSET + TXn_CLKBUF_ENABLE, 0x0f);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX0_OFFSET + TXn_RESET_TSYNC_EN, 0x03);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX0_OFFSET + TXn_TRAN_DRVR_EMP_EN, 0x03);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX0_OFFSET + TXn_PARRATE_REC_DETECT_IDLE_EN, 0x00);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX0_OFFSET + TXn_TX_INTERFACE_MODE, 0x00);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX0_OFFSET + TXn_TX_EMP_POST1_LVL, 0x23);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX0_OFFSET + TXn_TX_DRV_LVL, 0x3f);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX0_OFFSET + TXn_TX_BAND, 0x4);
+
+ /* TX-1 register configuration */
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX1_OFFSET + TXn_TRANSCEIVER_BIAS_EN, 0x1a);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX1_OFFSET + TXn_VMODE_CTRL1, 0x40);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX1_OFFSET + TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX1_OFFSET + TXn_INTERFACE_SELECT, 0x3d);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX1_OFFSET + TXn_CLKBUF_ENABLE, 0x0f);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX1_OFFSET + TXn_RESET_TSYNC_EN, 0x03);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX1_OFFSET + TXn_TRAN_DRVR_EMP_EN, 0x03);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX1_OFFSET + TXn_PARRATE_REC_DETECT_IDLE_EN, 0x00);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX1_OFFSET + TXn_TX_INTERFACE_MODE, 0x00);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX1_OFFSET + TXn_TX_EMP_POST1_LVL, 0x23);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX1_OFFSET + TXn_TX_DRV_LVL, 0x3f);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX1_OFFSET + TXn_TX_BAND, 0x4);
+ wmb(); /* make sure write happens */
+
+ /* PHY VCO divider programming */
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ DP_PHY_VCO_DIV, pdb->phy_vco_div);
+ wmb(); /* make sure write happens */
+
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_CMN_CONFIG, 0x02);
+ wmb(); /* make sure write happens */
+
+ return res;
+}
+
+static bool dp_14nm_pll_lock_status(struct mdss_pll_resources *dp_res)
+{
+ u32 status;
+ bool pll_locked;
+
+ /* poll for PLL lock status */
+ if (readl_poll_timeout_atomic((dp_res->pll_base +
+ QSERDES_COM_C_READY_STATUS),
+ status,
+ ((status & BIT(0)) > 0),
+ DP_PLL_POLL_SLEEP_US,
+ DP_PLL_POLL_TIMEOUT_US)) {
+ pr_err("%s: C_READY status is not high. Status=%x\n",
+ __func__, status);
+ pll_locked = false;
+ } else {
+ pll_locked = true;
+ }
+
+ return pll_locked;
+}
+
+static bool dp_14nm_phy_rdy_status(struct mdss_pll_resources *dp_res)
+{
+ u32 status;
+ bool phy_ready = true;
+
+ /* poll for PHY ready status */
+ if (readl_poll_timeout_atomic((dp_res->phy_base +
+ DP_PHY_STATUS),
+ status,
+ ((status & (BIT(1) | BIT(0))) > 0),
+ DP_PHY_POLL_SLEEP_US,
+ DP_PHY_POLL_TIMEOUT_US)) {
+ pr_err("%s: Phy_ready is not high. Status=%x\n",
+ __func__, status);
+ phy_ready = false;
+ }
+
+ return phy_ready;
+}
+
+static int dp_pll_enable_14nm(struct clk_hw *hw)
+{
+ int rc = 0;
+ u32 bias_en, drvr_en;
+ struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
+ struct mdss_pll_resources *dp_res = vco->priv;
+ struct dp_pll_db *pdb = (struct dp_pll_db *)dp_res->priv;
+
+ MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x01);
+ MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x05);
+ MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x01);
+ MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x09);
+ wmb(); /* Make sure the PHY register writes are done */
+
+ MDSS_PLL_REG_W(dp_res->pll_base,
+ QSERDES_COM_RESETSM_CNTRL, 0x20);
+ wmb(); /* Make sure the PLL register writes are done */
+
+ udelay(900); /* hw recommended delay for full PU */
+
+ if (!dp_14nm_pll_lock_status(dp_res)) {
+ rc = -EINVAL;
+ goto lock_err;
+ }
+
+ MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x19);
+ wmb(); /* Make sure the PHY register writes are done */
+
+ udelay(10); /* hw recommended delay */
+
+ if (!dp_14nm_phy_rdy_status(dp_res)) {
+ rc = -EINVAL;
+ goto lock_err;
+ }
+
+ pr_debug("%s: PLL is locked\n", __func__);
+
+ if (pdb->lane_cnt == 1) {
+ bias_en = 0x3e;
+ drvr_en = 0x13;
+ } else {
+ bias_en = 0x3f;
+ drvr_en = 0x10;
+ }
+
+ if (pdb->lane_cnt != 4) {
+ if (pdb->orientation == ORIENTATION_CC1) {
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX1_OFFSET + TXn_TRANSCEIVER_BIAS_EN, bias_en);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX1_OFFSET + TXn_HIGHZ_DRVR_EN, drvr_en);
+ } else {
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX0_OFFSET + TXn_TRANSCEIVER_BIAS_EN, bias_en);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX0_OFFSET + TXn_HIGHZ_DRVR_EN, drvr_en);
+ }
+ } else {
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX0_OFFSET + TXn_TRANSCEIVER_BIAS_EN, bias_en);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX0_OFFSET + TXn_HIGHZ_DRVR_EN, drvr_en);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX1_OFFSET + TXn_TRANSCEIVER_BIAS_EN, bias_en);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX1_OFFSET + TXn_HIGHZ_DRVR_EN, drvr_en);
+ }
+
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX0_OFFSET + TXn_TX_POL_INV, 0x0a);
+ MDSS_PLL_REG_W(dp_res->phy_base,
+ QSERDES_TX1_OFFSET + TXn_TX_POL_INV, 0x0a);
+
+ /*
+ * Switch DP Mainlink clock (cc_dpphy_link_clk) from DP
+ * controller side with final frequency
+ */
+ MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x18);
+ wmb(); /* Make sure the PHY register writes are done */
+ MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x19);
+ wmb(); /* Make sure the PHY register writes are done */
+
+lock_err:
+ return rc;
+}
+
+static int dp_pll_disable_14nm(struct clk_hw *hw)
+{
+ int rc = 0;
+ struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
+ struct mdss_pll_resources *dp_res = vco->priv;
+
+ /* Assert DP PHY power down */
+ MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_PD_CTL, 0x2);
+ /*
+ * Make sure all the register writes to disable PLL are
+ * completed before doing any other operation
+ */
+ wmb();
+
+ return rc;
+}
+
+
+int dp_vco_prepare_14nm(struct clk_hw *hw)
+{
+ int rc = 0;
+ struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
+ struct mdss_pll_resources *dp_res = vco->priv;
+
+ DEV_DBG("rate=%ld\n", vco->rate);
+ rc = mdss_pll_resource_enable(dp_res, true);
+ if (rc) {
+ pr_err("Failed to enable mdss DP pll resources\n");
+ goto error;
+ }
+
+ if ((dp_res->vco_cached_rate != 0)
+ && (dp_res->vco_cached_rate == vco->rate)) {
+ rc = vco->hw.init->ops->set_rate(hw,
+ dp_res->vco_cached_rate, dp_res->vco_cached_rate);
+ if (rc) {
+ pr_err("index=%d vco_set_rate failed. rc=%d\n",
+ rc, dp_res->index);
+ mdss_pll_resource_enable(dp_res, false);
+ goto error;
+ }
+ }
+
+ rc = dp_pll_enable_14nm(hw);
+ if (rc) {
+ mdss_pll_resource_enable(dp_res, false);
+ pr_err("ndx=%d failed to enable dp pll\n",
+ dp_res->index);
+ goto error;
+ }
+
+ mdss_pll_resource_enable(dp_res, false);
+error:
+ return rc;
+}
+
+void dp_vco_unprepare_14nm(struct clk_hw *hw)
+{
+ struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
+ struct mdss_pll_resources *dp_res = vco->priv;
+
+ if (!dp_res) {
+ DEV_ERR("Invalid input parameter\n");
+ return;
+ }
+
+ if (!dp_res->pll_on &&
+ mdss_pll_resource_enable(dp_res, true)) {
+ DEV_ERR("pll resource can't be enabled\n");
+ return;
+ }
+ dp_res->vco_cached_rate = vco->rate;
+ dp_pll_disable_14nm(hw);
+
+ dp_res->handoff_resources = false;
+ mdss_pll_resource_enable(dp_res, false);
+ dp_res->pll_on = false;
+}
+
+int dp_vco_set_rate_14nm(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
+ struct mdss_pll_resources *dp_res = vco->priv;
+ int rc;
+
+ rc = mdss_pll_resource_enable(dp_res, true);
+ if (rc) {
+ DEV_ERR("pll resource can't be enabled\n");
+ return rc;
+ }
+
+ DEV_DBG("DP lane CLK rate=%ld\n", rate);
+
+ rc = dp_config_vco_rate_14nm(vco, rate);
+ if (rc)
+ DEV_ERR("%s: Failed to set clk rate\n", __func__);
+
+ mdss_pll_resource_enable(dp_res, false);
+
+ vco->rate = rate;
+
+ return 0;
+}
+
+unsigned long dp_vco_recalc_rate_14nm(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
+ int rc;
+ u32 div, hsclk_div, link2xclk_div = 0;
+ u64 vco_rate;
+ struct mdss_pll_resources *dp_res = vco->priv;
+
+ if (is_gdsc_disabled(dp_res))
+ return 0;
+
+ rc = mdss_pll_resource_enable(dp_res, true);
+ if (rc) {
+ pr_err("Failed to enable mdss DP pll=%d\n", dp_res->index);
+ return rc;
+ }
+
+ div = MDSS_PLL_REG_R(dp_res->pll_base, QSERDES_COM_HSCLK_SEL);
+ div &= 0x0f;
+
+ if (div == 12)
+ hsclk_div = 5; /* Default */
+ else if (div == 4)
+ hsclk_div = 3;
+ else if (div == 0)
+ hsclk_div = 2;
+ else {
+ pr_debug("unknown divider. forcing to default\n");
+ hsclk_div = 5;
+ }
+
+ div = MDSS_PLL_REG_R(dp_res->phy_base, DP_PHY_MODE);
+
+ if (div & 0xd8)
+ pr_err("%s: DP PAR Rate not correct\n", __func__);
+
+ if ((div & 0x3) == 1)
+ link2xclk_div = 10;
+ else if ((div & 0x3) == 0)
+ link2xclk_div = 5;
+ else
+ pr_err("%s: unsupported div. Phy_mode: %d\n", __func__, div);
+
+ if (link2xclk_div == 10) {
+ vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
+ } else {
+ if (hsclk_div == 5)
+ vco_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000;
+ else if (hsclk_div == 3)
+ vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
+ else
+ vco_rate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
+ }
+
+ pr_debug("returning vco rate = %lu\n", (unsigned long)vco_rate);
+
+ mdss_pll_resource_enable(dp_res, false);
+
+ return (unsigned long)vco_rate;
+}
+
+long dp_vco_round_rate_14nm(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned long rrate = rate;
+ struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
+
+ if (rate <= vco->min_rate)
+ rrate = vco->min_rate;
+ else if (rate <= DP_VCO_HSCLK_RATE_2700MHZDIV1000)
+ rrate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
+ else
+ rrate = vco->max_rate;
+
+ pr_debug("%s: rrate=%ld\n", __func__, rrate);
+
+ *parent_rate = rrate;
+ return rrate;
+}
+
diff --git a/drivers/clk/qcom/mdss/mdss-dp-pll-14nm.c b/drivers/clk/qcom/mdss/mdss-dp-pll-14nm.c
new file mode 100644
index 000000000000..ffe8286cfaaa
--- /dev/null
+++ b/drivers/clk/qcom/mdss/mdss-dp-pll-14nm.c
@@ -0,0 +1,234 @@
+/* Copyright (c) 2016-2017, 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.
+ *
+ */
+
+/*
+***************************************************************************
+******** Display Port PLL driver block diagram for branch clocks **********
+***************************************************************************
+
+ +--------------------------+
+ | DP_VCO_CLK |
+ | |
+ | +-------------------+ |
+ | | (DP PLL/VCO) | |
+ | +---------+---------+ |
+ | v |
+ | +----------+-----------+ |
+ | | hsclk_divsel_clk_src | |
+ | +----------+-----------+ |
+ +--------------------------+
+ |
+ v
+ +------------<------------|------------>-------------+
+ | | |
++----------v----------+ +----------v----------+ +----------v----------+
+| dp_link_2x_clk | | vco_divided_clk_src | | vco_divided_clk_src |
+| divsel_five | | | | |
+v----------+----------v | divsel_two | | divsel_four |
+ | +----------+----------+ +----------+----------+
+ | | |
+ v v v
+ | +---------------------+ |
+ Input to MMSSCC block | | (aux_clk_ops) | |
+ for link clk, crypto clk +--> vco_divided_clk <-+
+ and interface clock | _src_mux |
+ +----------+----------+
+ |
+ v
+ Input to MMSSCC block
+ for DP pixel clock
+
+******************************************************************************
+*/
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+
+#include "mdss-pll.h"
+#include "mdss-dp-pll.h"
+#include "mdss-dp-pll-14nm.h"
+
+static struct dp_pll_db dp_pdb;
+
+static struct regmap_config dp_pll_14nm_cfg = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x910,
+};
+
+static struct regmap_bus dp_pixel_mux_regmap_ops = {
+ .reg_write = dp_mux_set_parent_14nm,
+ .reg_read = dp_mux_get_parent_14nm,
+};
+
+/* Op structures */
+static struct clk_ops dp_14nm_vco_clk_ops = {
+ .recalc_rate = dp_vco_recalc_rate_14nm,
+ .set_rate = dp_vco_set_rate_14nm,
+ .round_rate = dp_vco_round_rate_14nm,
+ .prepare = dp_vco_prepare_14nm,
+ .unprepare = dp_vco_unprepare_14nm,
+};
+
+static struct dp_pll_vco_clk dp_vco_clk = {
+ .min_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000,
+ .max_rate = DP_VCO_HSCLK_RATE_5400MHZDIV1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "dp_vco_clk",
+ .parent_names = (const char *[]){ "xo_board" },
+ .num_parents = 1,
+ .ops = &dp_14nm_vco_clk_ops,
+ },
+};
+
+static struct clk_fixed_factor dp_link_2x_clk_divsel_five = {
+ .div = 5,
+ .mult = 1,
+
+ .hw.init = &(struct clk_init_data){
+ .name = "dp_link_2x_clk_divsel_five",
+ .parent_names =
+ (const char *[]){ "dp_vco_clk" },
+ .num_parents = 1,
+ .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
+ .ops = &clk_fixed_factor_ops,
+ },
+};
+
+static struct clk_fixed_factor dp_vco_divsel_two_clk_src = {
+ .div = 2,
+ .mult = 1,
+
+ .hw.init = &(struct clk_init_data){
+ .name = "dp_vco_divsel_two_clk_src",
+ .parent_names =
+ (const char *[]){ "dp_vco_clk" },
+ .num_parents = 1,
+ .flags = (CLK_GET_RATE_NOCACHE),
+ .ops = &clk_fixed_factor_ops,
+ },
+};
+
+static struct clk_fixed_factor dp_vco_divsel_four_clk_src = {
+ .div = 4,
+ .mult = 1,
+
+ .hw.init = &(struct clk_init_data){
+ .name = "dp_vco_divsel_four_clk_src",
+ .parent_names =
+ (const char *[]){ "dp_vco_clk" },
+ .num_parents = 1,
+ .flags = (CLK_GET_RATE_NOCACHE),
+ .ops = &clk_fixed_factor_ops,
+ },
+};
+
+static struct clk_regmap_mux dp_vco_divided_clk_src_mux = {
+ .reg = 0x64,
+ .shift = 0,
+ .width = 1,
+
+ .clkr = {
+ .hw.init = &(struct clk_init_data){
+ .name = "dp_vco_divided_clk_src_mux",
+ .parent_names =
+ (const char *[]){"dp_vco_divsel_two_clk_src",
+ "dp_vco_divsel_four_clk_src"},
+ .num_parents = 2,
+ .ops = &clk_regmap_mux_closest_ops,
+ .flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
+ },
+ },
+};
+
+static struct clk_hw *mdss_dp_pllcc_14nm[] = {
+ [DP_VCO_CLK] = &dp_vco_clk.hw,
+ [DP_LINK_2X_CLK_DIVSEL_FIVE] = &dp_link_2x_clk_divsel_five.hw,
+ [DP_VCO_DIVSEL_FOUR_CLK_SRC] = &dp_vco_divsel_four_clk_src.hw,
+ [DP_VCO_DIVSEL_TWO_CLK_SRC] = &dp_vco_divsel_two_clk_src.hw,
+ [DP_VCO_DIVIDED_CLK_SRC_MUX] = &dp_vco_divided_clk_src_mux.clkr.hw,
+};
+
+int dp_pll_clock_register_14nm(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ int rc = -ENOTSUPP, i = 0;
+ struct clk_onecell_data *clk_data;
+ struct clk *clk;
+ struct regmap *regmap;
+ int num_clks = ARRAY_SIZE(mdss_dp_pllcc_14nm);
+
+ if (!pdev || !pdev->dev.of_node) {
+ pr_err("Invalid input parameters\n");
+ return -EINVAL;
+ }
+
+ if (!pll_res || !pll_res->pll_base || !pll_res->phy_base) {
+ DEV_ERR("%s: Invalid input parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ clk_data = devm_kzalloc(&pdev->dev, sizeof(struct clk_onecell_data),
+ GFP_KERNEL);
+ if (!clk_data)
+ return -ENOMEM;
+
+ clk_data->clks = devm_kzalloc(&pdev->dev, (num_clks *
+ sizeof(struct clk *)), GFP_KERNEL);
+ if (!clk_data->clks) {
+ devm_kfree(&pdev->dev, clk_data);
+ return -ENOMEM;
+ }
+ clk_data->clk_num = num_clks;
+
+ pll_res->priv = &dp_pdb;
+ dp_pdb.pll = pll_res;
+
+ /* Set client data for vco, mux and div clocks */
+ regmap = devm_regmap_init(&pdev->dev, &dp_pixel_mux_regmap_ops,
+ pll_res, &dp_pll_14nm_cfg);
+ dp_vco_divided_clk_src_mux.clkr.regmap = regmap;
+
+ dp_vco_clk.priv = pll_res;
+
+ for (i = DP_VCO_CLK; i <= DP_VCO_DIVIDED_CLK_SRC_MUX; i++) {
+ pr_debug("reg clk: %d index: %d\n", i, pll_res->index);
+ clk = devm_clk_register(&pdev->dev,
+ mdss_dp_pllcc_14nm[i]);
+ if (IS_ERR(clk)) {
+ pr_err("clk registration failed for DP: %d\n",
+ pll_res->index);
+ rc = -EINVAL;
+ goto clk_reg_fail;
+ }
+ clk_data->clks[i] = clk;
+ }
+
+ rc = of_clk_add_provider(pdev->dev.of_node,
+ of_clk_src_onecell_get, clk_data);
+ if (rc) {
+ DEV_ERR("%s: Clock register failed rc=%d\n", __func__, rc);
+ rc = -EPROBE_DEFER;
+ } else {
+ DEV_DBG("%s SUCCESS\n", __func__);
+ }
+ return 0;
+clk_reg_fail:
+ devm_kfree(&pdev->dev, clk_data->clks);
+ devm_kfree(&pdev->dev, clk_data);
+ return rc;
+}
diff --git a/drivers/clk/qcom/mdss/mdss-dp-pll-14nm.h b/drivers/clk/qcom/mdss/mdss-dp-pll-14nm.h
new file mode 100644
index 000000000000..08dee8909da4
--- /dev/null
+++ b/drivers/clk/qcom/mdss/mdss-dp-pll-14nm.h
@@ -0,0 +1,193 @@
+/* Copyright (c) 2016-2017, 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_DP_PLL_14NM_H
+#define __MDSS_DP_PLL_14NM_H
+
+#define DP_PHY_REVISION_ID0 0x0000
+#define DP_PHY_REVISION_ID1 0x0004
+#define DP_PHY_REVISION_ID2 0x0008
+#define DP_PHY_REVISION_ID3 0x000C
+
+#define DP_PHY_CFG 0x0010
+#define DP_PHY_CFG_1 0x0014
+#define DP_PHY_PD_CTL 0x0018
+#define DP_PHY_MODE 0x001C
+
+#define DP_PHY_AUX_CFG0 0x0020
+#define DP_PHY_AUX_CFG1 0x0024
+#define DP_PHY_AUX_CFG2 0x0028
+#define DP_PHY_AUX_CFG3 0x002C
+#define DP_PHY_AUX_CFG4 0x0030
+#define DP_PHY_AUX_CFG5 0x0034
+#define DP_PHY_AUX_CFG6 0x0038
+#define DP_PHY_AUX_CFG7 0x003C
+#define DP_PHY_AUX_CFG8 0x0040
+#define DP_PHY_AUX_CFG9 0x0044
+#define DP_PHY_AUX_INTERRUPT_MASK 0x0048
+#define DP_PHY_AUX_INTERRUPT_CLEAR 0x004C
+#define DP_PHY_AUX_BIST_CFG 0x0050
+
+#define DP_PHY_VCO_DIV 0x0068
+#define DP_PHY_TX0_TX1_LANE_CTL 0x006C
+
+#define DP_PHY_TX2_TX3_LANE_CTL 0x0088
+#define DP_PHY_SPARE0 0x00AC
+#define DP_PHY_STATUS 0x00C0
+
+/* Tx registers */
+#define QSERDES_TX0_OFFSET 0x0400
+#define QSERDES_TX1_OFFSET 0x0800
+
+#define TXn_BIST_MODE_LANENO 0x0000
+#define TXn_CLKBUF_ENABLE 0x0008
+#define TXn_TX_EMP_POST1_LVL 0x000C
+
+#define TXn_TX_DRV_LVL 0x001C
+
+#define TXn_RESET_TSYNC_EN 0x0024
+#define TXn_PRE_STALL_LDO_BOOST_EN 0x0028
+#define TXn_TX_BAND 0x002C
+#define TXn_SLEW_CNTL 0x0030
+#define TXn_INTERFACE_SELECT 0x0034
+
+#define TXn_RES_CODE_LANE_TX 0x003C
+#define TXn_RES_CODE_LANE_RX 0x0040
+#define TXn_RES_CODE_LANE_OFFSET_TX 0x0044
+#define TXn_RES_CODE_LANE_OFFSET_RX 0x0048
+
+#define TXn_DEBUG_BUS_SEL 0x0058
+#define TXn_TRANSCEIVER_BIAS_EN 0x005C
+#define TXn_HIGHZ_DRVR_EN 0x0060
+#define TXn_TX_POL_INV 0x0064
+#define TXn_PARRATE_REC_DETECT_IDLE_EN 0x0068
+
+#define TXn_LANE_MODE_1 0x008C
+
+#define TXn_TRAN_DRVR_EMP_EN 0x00C0
+#define TXn_TX_INTERFACE_MODE 0x00C4
+
+#define TXn_VMODE_CTRL1 0x00F0
+
+
+/* PLL register offset */
+#define QSERDES_COM_ATB_SEL1 0x0000
+#define QSERDES_COM_ATB_SEL2 0x0004
+#define QSERDES_COM_FREQ_UPDATE 0x0008
+#define QSERDES_COM_BG_TIMER 0x000C
+#define QSERDES_COM_SSC_EN_CENTER 0x0010
+#define QSERDES_COM_SSC_ADJ_PER1 0x0014
+#define QSERDES_COM_SSC_ADJ_PER2 0x0018
+#define QSERDES_COM_SSC_PER1 0x001C
+#define QSERDES_COM_SSC_PER2 0x0020
+#define QSERDES_COM_SSC_STEP_SIZE1 0x0024
+#define QSERDES_COM_SSC_STEP_SIZE2 0x0028
+#define QSERDES_COM_POST_DIV 0x002C
+#define QSERDES_COM_POST_DIV_MUX 0x0030
+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x0034
+#define QSERDES_COM_CLK_ENABLE1 0x0038
+#define QSERDES_COM_SYS_CLK_CTRL 0x003C
+#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x0040
+#define QSERDES_COM_PLL_EN 0x0044
+#define QSERDES_COM_PLL_IVCO 0x0048
+#define QSERDES_COM_LOCK_CMP1_MODE0 0x004C
+#define QSERDES_COM_LOCK_CMP2_MODE0 0x0050
+#define QSERDES_COM_LOCK_CMP3_MODE0 0x0054
+
+#define QSERDES_COM_CP_CTRL_MODE0 0x0078
+#define QSERDES_COM_CP_CTRL_MODE1 0x007C
+#define QSERDES_COM_PLL_RCTRL_MODE0 0x0084
+#define QSERDES_COM_PLL_CCTRL_MODE0 0x0090
+#define QSERDES_COM_PLL_CNTRL 0x009C
+
+#define QSERDES_COM_SYSCLK_EN_SEL 0x00AC
+#define QSERDES_COM_CML_SYSCLK_SEL 0x00B0
+#define QSERDES_COM_RESETSM_CNTRL 0x00B4
+#define QSERDES_COM_RESETSM_CNTRL2 0x00B8
+#define QSERDES_COM_LOCK_CMP_EN 0x00C8
+#define QSERDES_COM_LOCK_CMP_CFG 0x00CC
+
+
+#define QSERDES_COM_DEC_START_MODE0 0x00D0
+#define QSERDES_COM_DEC_START_MODE1 0x00D4
+#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x00DC
+#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x00E0
+#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x00E4
+
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x0108
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x010C
+#define QSERDES_COM_VCO_TUNE_CTRL 0x0124
+#define QSERDES_COM_VCO_TUNE_MAP 0x0128
+#define QSERDES_COM_VCO_TUNE1_MODE0 0x012C
+#define QSERDES_COM_VCO_TUNE2_MODE0 0x0130
+
+#define QSERDES_COM_CMN_STATUS 0x015C
+#define QSERDES_COM_RESET_SM_STATUS 0x0160
+
+#define QSERDES_COM_BG_CTRL 0x0170
+#define QSERDES_COM_CLK_SELECT 0x0174
+#define QSERDES_COM_HSCLK_SEL 0x0178
+#define QSERDES_COM_CORECLK_DIV 0x0184
+#define QSERDES_COM_SW_RESET 0x0188
+#define QSERDES_COM_CORE_CLK_EN 0x018C
+#define QSERDES_COM_C_READY_STATUS 0x0190
+#define QSERDES_COM_CMN_CONFIG 0x0194
+#define QSERDES_COM_SVS_MODE_CLK_SEL 0x019C
+
+#define DP_PLL_POLL_SLEEP_US 500
+#define DP_PLL_POLL_TIMEOUT_US 10000
+
+#define DP_PHY_POLL_SLEEP_US 500
+#define DP_PHY_POLL_TIMEOUT_US 10000
+
+#define DP_VCO_RATE_8100MHZDIV1000 8100000UL
+#define DP_VCO_RATE_10800MHZDIV1000 10800000UL
+
+#define DP_VCO_HSCLK_RATE_1620MHZDIV1000 1620000UL
+#define DP_VCO_HSCLK_RATE_2700MHZDIV1000 2700000UL
+#define DP_VCO_HSCLK_RATE_5400MHZDIV1000 5400000UL
+
+struct dp_pll_db {
+ struct mdss_pll_resources *pll;
+
+ /* lane and orientation settings */
+ u8 lane_cnt;
+ u8 orientation;
+
+ /* COM PHY settings */
+ u32 hsclk_sel;
+ u32 dec_start_mode0;
+ u32 div_frac_start1_mode0;
+ u32 div_frac_start2_mode0;
+ u32 div_frac_start3_mode0;
+ u32 lock_cmp1_mode0;
+ u32 lock_cmp2_mode0;
+ u32 lock_cmp3_mode0;
+
+ /* PHY vco divider */
+ u32 phy_vco_div;
+};
+
+int dp_vco_set_rate_14nm(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate);
+unsigned long dp_vco_recalc_rate_14nm(struct clk_hw *hw,
+ unsigned long parent_rate);
+long dp_vco_round_rate_14nm(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate);
+int dp_vco_prepare_14nm(struct clk_hw *hw);
+void dp_vco_unprepare_14nm(struct clk_hw *hw);
+int dp_mux_set_parent_14nm(void *context,
+ unsigned int reg, unsigned int val);
+int dp_mux_get_parent_14nm(void *context,
+ unsigned int reg, unsigned int *val);
+#endif /* __MDSS_DP_PLL_14NM_H */
diff --git a/drivers/clk/qcom/mdss/mdss-dp-pll.h b/drivers/clk/qcom/mdss/mdss-dp-pll.h
new file mode 100644
index 000000000000..a05e27af456e
--- /dev/null
+++ b/drivers/clk/qcom/mdss/mdss-dp-pll.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2016-2017, 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_DP_PLL_H
+#define __MDSS_DP_PLL_H
+
+struct dp_pll_vco_clk {
+ struct clk_hw hw;
+ unsigned long rate; /* current vco rate */
+ u64 min_rate; /* min vco rate */
+ u64 max_rate; /* max vco rate */
+ void *priv;
+};
+
+static inline struct dp_pll_vco_clk *to_dp_vco_hw(struct clk_hw *hw)
+{
+ return container_of(hw, struct dp_pll_vco_clk, hw);
+}
+
+int dp_pll_clock_register_14nm(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+
+
+#endif /* __MDSS_DP_PLL_H */
+
diff --git a/drivers/clk/qcom/mdss/mdss-pll.c b/drivers/clk/qcom/mdss/mdss-pll.c
index f356be38a25c..2f3a020a761a 100644
--- a/drivers/clk/qcom/mdss/mdss-pll.c
+++ b/drivers/clk/qcom/mdss/mdss-pll.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, 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
@@ -22,6 +22,7 @@
#include "mdss-pll.h"
#include "mdss-dsi-pll.h"
+#include "mdss-dp-pll.h"
int mdss_pll_resource_enable(struct mdss_pll_resources *pll_res, bool enable)
{
@@ -141,6 +142,9 @@ static int mdss_pll_resource_parse(struct platform_device *pdev,
pll_res->pll_interface_type = MDSS_DSI_PLL_8998;
} else if (!strcmp(compatible_stream, "qcom,mdss_dp_pll_8998")) {
pll_res->pll_interface_type = MDSS_DP_PLL_8998;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_dp_pll_sdm660")) {
+ pll_res->target_id = MDSS_PLL_TARGET_SDM660;
+ pll_res->pll_interface_type = MDSS_DP_PLL_SDM660;
} else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8996")) {
pll_res->pll_interface_type = MDSS_HDMI_PLL_8996;
} else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8996_v2")) {
@@ -178,6 +182,9 @@ static int mdss_pll_clock_register(struct platform_device *pdev,
case MDSS_DSI_PLL_8996:
rc = dsi_pll_clock_register_14nm(pdev, pll_res);
break;
+ case MDSS_DP_PLL_SDM660:
+ rc = dp_pll_clock_register_14nm(pdev, pll_res);
+ break;
case MDSS_UNKNOWN_PLL:
default:
rc = -EINVAL;
@@ -383,6 +390,7 @@ static const struct of_device_id mdss_pll_dt_match[] = {
{.compatible = "qcom,mdss_dp_pll_8998"},
{.compatible = "qcom,mdss_hdmi_pll_8998"},
{.compatible = "qcom,mdss_dsi_pll_sdm660"},
+ {.compatible = "qcom,mdss_dp_pll_sdm660"},
{}
};
diff --git a/drivers/clk/qcom/mdss/mdss-pll.h b/drivers/clk/qcom/mdss/mdss-pll.h
index e0e62a0f379b..9cf785d43504 100644
--- a/drivers/clk/qcom/mdss/mdss-pll.h
+++ b/drivers/clk/qcom/mdss/mdss-pll.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, 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
@@ -41,6 +41,7 @@ enum {
MDSS_DSI_PLL_8996,
MDSS_DSI_PLL_8998,
MDSS_DP_PLL_8998,
+ MDSS_DP_PLL_SDM660,
MDSS_HDMI_PLL_8996,
MDSS_HDMI_PLL_8996_V2,
MDSS_HDMI_PLL_8996_V3,