diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2017-01-28 00:46:33 -0800 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2017-01-28 00:46:32 -0800 |
commit | 46b116edc8854af481be5045f0d7aeab798eb5a2 (patch) | |
tree | 57a83d8d9965bc2e2406857d72c7e3133fbc3a1b /drivers/clk | |
parent | 252a3a5bb6f6994d3ccfb8a489b2d33c34bde0e9 (diff) | |
parent | e2d34afade748addcc6f82d84493121ecebcc6cc (diff) |
Merge "clk: qcom: mdss: add support for MDSS DP PLL for SDM660"
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/qcom/mdss/Makefile | 2 | ||||
-rw-r--r-- | drivers/clk/qcom/mdss/mdss-dp-pll-14nm-util.c | 624 | ||||
-rw-r--r-- | drivers/clk/qcom/mdss/mdss-dp-pll-14nm.c | 234 | ||||
-rw-r--r-- | drivers/clk/qcom/mdss/mdss-dp-pll-14nm.h | 193 | ||||
-rw-r--r-- | drivers/clk/qcom/mdss/mdss-dp-pll.h | 35 | ||||
-rw-r--r-- | drivers/clk/qcom/mdss/mdss-pll.c | 10 | ||||
-rw-r--r-- | drivers/clk/qcom/mdss/mdss-pll.h | 3 |
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, |