From 3ee6103a8dcace1b8f6aa20fc3c5844ca51dd829 Mon Sep 17 00:00:00 2001 From: Chandan Uddaraju Date: Mon, 22 Feb 2016 16:43:23 -0800 Subject: [PATCH] clk: qcom: mdss: add Display-port pll clock driver support Add support for new Display-port PLL clock driver to handle different DP panel resolutions in msmcobalt. Add separate files to support this new PHY PLL block. CRs-Fixed: 1009740 Change-Id: Ic282c7e14fc6e23f4d044cb6a58249bdb4c8c2d8 Signed-off-by: Chandan Uddaraju --- .../devicetree/bindings/fb/mdss-pll.txt | 2 +- drivers/clk/msm/mdss/Makefile | 2 + .../clk/msm/mdss/mdss-dp-pll-cobalt-util.c | 708 ++++++++++++++++++ drivers/clk/msm/mdss/mdss-dp-pll-cobalt.c | 253 +++++++ drivers/clk/msm/mdss/mdss-dp-pll-cobalt.h | 166 ++++ drivers/clk/msm/mdss/mdss-dp-pll.h | 36 + drivers/clk/msm/mdss/mdss-dsi-pll-8996.c | 8 +- drivers/clk/msm/mdss/mdss-dsi-pll-cobalt.c | 22 +- drivers/clk/msm/mdss/mdss-dsi-pll.h | 7 - drivers/clk/msm/mdss/mdss-pll.c | 6 + drivers/clk/msm/mdss/mdss-pll.h | 19 + include/dt-bindings/clock/msm-clocks-cobalt.h | 7 + 12 files changed, 1208 insertions(+), 28 deletions(-) create mode 100644 drivers/clk/msm/mdss/mdss-dp-pll-cobalt-util.c create mode 100644 drivers/clk/msm/mdss/mdss-dp-pll-cobalt.c create mode 100644 drivers/clk/msm/mdss/mdss-dp-pll-cobalt.h create mode 100644 drivers/clk/msm/mdss/mdss-dp-pll.h diff --git a/Documentation/devicetree/bindings/fb/mdss-pll.txt b/Documentation/devicetree/bindings/fb/mdss-pll.txt index 3d774f921a8c..bdb7a595f95e 100644 --- a/Documentation/devicetree/bindings/fb/mdss-pll.txt +++ b/Documentation/devicetree/bindings/fb/mdss-pll.txt @@ -14,7 +14,7 @@ Required properties: "qcom,mdss_dsi_pll_8996", "qcom,mdss_hdmi_pll_8996", "qcom,mdss_hdmi_pll_8996_v2", "qcom,mdss_dsi_pll_8996_v2", "qcom,mdss_hdmi_pll_8996_v3", "qcom,mdss_hdmi_pll_8996_v3_1p8", - "qcom,mdss_dsi_pll_cobalt" + "qcom,mdss_dsi_pll_cobalt", "qcom,mdss_dp_pll_cobalt" - cell-index: Specifies the controller used - reg: offset and length of the register set for the device. - reg-names : names to refer to register sets related to this device diff --git a/drivers/clk/msm/mdss/Makefile b/drivers/clk/msm/mdss/Makefile index c39aa7779e02..c026e9383800 100644 --- a/drivers/clk/msm/mdss/Makefile +++ b/drivers/clk/msm/mdss/Makefile @@ -3,4 +3,6 @@ obj-$(CONFIG_MSM_MDSS_PLL) += mdss-pll.o obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-8996.o obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-8996-util.o obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-cobalt.o +obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dp-pll-cobalt.o +obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dp-pll-cobalt-util.o obj-$(CONFIG_MSM_MDSS_PLL) += mdss-hdmi-pll-8996.o diff --git a/drivers/clk/msm/mdss/mdss-dp-pll-cobalt-util.c b/drivers/clk/msm/mdss/mdss-dp-pll-cobalt-util.c new file mode 100644 index 000000000000..02bc71fd9fab --- /dev/null +++ b/drivers/clk/msm/mdss/mdss-dp-pll-cobalt-util.c @@ -0,0 +1,708 @@ +/* Copyright (c) 2016, 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 +#include +#include +#include +#include + +#include "mdss-pll.h" +#include "mdss-dp-pll.h" +#include "mdss-dp-pll-cobalt.h" + +int link2xclk_divsel_set_div(struct div_clk *clk, int div) +{ + int rc; + u32 link2xclk_div_tx0, link2xclk_div_tx1; + u32 phy_mode; + struct mdss_pll_resources *dp_res = clk->priv; + + rc = mdss_pll_resource_enable(dp_res, true); + if (rc) { + pr_err("Failed to enable mdss DP PLL resources\n"); + return rc; + } + + link2xclk_div_tx0 = MDSS_PLL_REG_R(dp_res->phy_base, + QSERDES_TX0_OFFSET + TXn_TX_BAND); + link2xclk_div_tx1 = MDSS_PLL_REG_R(dp_res->phy_base, + QSERDES_TX1_OFFSET + TXn_TX_BAND); + + link2xclk_div_tx0 &= ~0x07; /* bits 0 to 2 */ + link2xclk_div_tx1 &= ~0x07; /* bits 0 to 2 */ + + /* Configure TX band Mux */ + link2xclk_div_tx0 |= 0x4; + link2xclk_div_tx1 |= 0x4; + + /*configure DP PHY MODE */ + phy_mode = 0x48; + + if (div == 10) { + link2xclk_div_tx0 |= 1; + link2xclk_div_tx1 |= 1; + phy_mode |= 1; + } + MDSS_PLL_REG_W(dp_res->phy_base, + QSERDES_TX0_OFFSET + TXn_TX_BAND, + link2xclk_div_tx0); + MDSS_PLL_REG_W(dp_res->phy_base, + QSERDES_TX1_OFFSET + TXn_TX_BAND, + link2xclk_div_tx1); + MDSS_PLL_REG_W(dp_res->phy_base, + DP_PHY_MODE, phy_mode); + + + pr_debug("%s: div=%d link2xclk_div_tx0=%x, link2xclk_div_tx1=%x\n", + __func__, div, link2xclk_div_tx0, link2xclk_div_tx1); + + mdss_pll_resource_enable(dp_res, false); + + return rc; +} + +int link2xclk_divsel_get_div(struct div_clk *clk) +{ + int rc; + u32 div = 0, phy_mode; + struct mdss_pll_resources *dp_res = clk->priv; + + rc = mdss_pll_resource_enable(dp_res, true); + if (rc) { + pr_err("Failed to enable dp_res resources\n"); + return rc; + } + + phy_mode = MDSS_PLL_REG_R(dp_res->phy_base, DP_PHY_MODE); + + if (phy_mode & 0x48) + pr_err("%s: DP PAR Rate not correct\n", __func__); + + if ((phy_mode & 0x3) == 1) + div = 10; + else if ((phy_mode & 0x3) == 0) + div = 5; + else + pr_err("%s: unsupported div: %d\n", __func__, phy_mode); + + mdss_pll_resource_enable(dp_res, false); + pr_debug("%s: phy_mode=%d, div=%d\n", __func__, + phy_mode, div); + + return div; +} + +int hsclk_divsel_set_div(struct div_clk *clk, int div) +{ + int rc; + u32 hsclk_div; + struct mdss_pll_resources *dp_res = clk->priv; + + rc = mdss_pll_resource_enable(dp_res, true); + if (rc) { + pr_err("Failed to enable mdss DP PLL resources\n"); + return rc; + } + + hsclk_div = MDSS_PLL_REG_R(dp_res->pll_base, QSERDES_COM_HSCLK_SEL); + hsclk_div &= ~0x0f; /* bits 0 to 3 */ + + if (div == 3) + hsclk_div |= 4; + else + hsclk_div |= 0; + + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_HSCLK_SEL, hsclk_div); + + pr_debug("%s: div=%d hsclk_div=%x\n", __func__, div, hsclk_div); + + mdss_pll_resource_enable(dp_res, false); + + return rc; +} + +int hsclk_divsel_get_div(struct div_clk *clk) +{ + int rc; + u32 hsclk_div, div; + struct mdss_pll_resources *dp_res = clk->priv; + + rc = mdss_pll_resource_enable(dp_res, true); + if (rc) { + pr_err("Failed to enable dp_res resources\n"); + return rc; + } + + hsclk_div = MDSS_PLL_REG_R(dp_res->pll_base, QSERDES_COM_HSCLK_SEL); + hsclk_div &= 0x0f; + + if (hsclk_div == 4) + div = 3; + else + div = 2; + + mdss_pll_resource_enable(dp_res, false); + + pr_debug("%s: hsclk_div:%d, div=%d\n", __func__, hsclk_div, div); + + return hsclk_div; +} + +int vco_divided_clk_set_div(struct div_clk *clk, int div) +{ + int rc; + u32 auxclk_div; + struct mdss_pll_resources *dp_res = clk->priv; + + 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->pll_base, DP_PHY_VCO_DIV); + auxclk_div &= ~0x03; /* bits 0 to 1 */ + if (div == 4) + auxclk_div |= 2; + else + auxclk_div |= 1; + + MDSS_PLL_REG_W(dp_res->pll_base, + DP_PHY_VCO_DIV, auxclk_div); + + pr_debug("%s: div=%d auxclk_div=%x\n", __func__, div, auxclk_div); + + mdss_pll_resource_enable(dp_res, false); + + return rc; +} + +int vco_divided_clk_get_div(struct div_clk *clk) +{ + int rc; + u32 div, auxclk_div; + struct mdss_pll_resources *dp_res = clk->priv; + + 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->pll_base, DP_PHY_VCO_DIV); + auxclk_div &= 0x03; + + if (auxclk_div == 2) + div = 4; + else + div = 2; + mdss_pll_resource_enable(dp_res, false); + + pr_debug("%s: auxclk_div=%d, div=%d\n", __func__, auxclk_div, div); + + return div; +} + +int dp_config_vco_rate(struct dp_pll_vco_clk *vco, unsigned long rate) +{ + u32 res = 0; + struct mdss_pll_resources *dp_res = vco->priv; + + MDSS_PLL_REG_W(dp_res->phy_base, + DP_PHY_PD_CTL, 0x39); + 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_SYS_CLK_CTRL, 0x02); + + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_CLK_ENABLE1, 0x0e); + 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_SEL, 0x30); + + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_LOCK_CMP_EN, 0x08); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_PLL_CCTRL_MODE0, 0x34); + 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, 0x08); + /* Different for 2700 & 5400 Mhz clock rates */ + if (rate == DP_VCO_RATE_8100MHz) { + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_DEC_START_MODE0, 0x69); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_DIV_FRAC_START1_MODE0, 0x9a); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_DIV_FRAC_START2_MODE0, 0x29); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_DIV_FRAC_START3_MODE0, 0x07); + } else if (rate == DP_VCO_RATE_9720MHz) { + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_DEC_START_MODE0, 0x7e); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_DIV_FRAC_START1_MODE0, 0x52); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_DIV_FRAC_START2_MODE0, 0x98); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_DIV_FRAC_START3_MODE0, 0x08); + } else if (rate == DP_VCO_RATE_10800MHz) { + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_DEC_START_MODE0, 0x8c); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_DIV_FRAC_START1_MODE0, 0xcd); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_DIV_FRAC_START2_MODE0, 0x8c); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_DIV_FRAC_START3_MODE0, 0x09); + } else { + pr_err("%s: unsupported rate: %ld\n", __func__, rate); + return -EINVAL; + } + + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x3f); + 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); + + if (rate == DP_VCO_RATE_8100MHz) { + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_LOCK_CMP1_MODE0, 0x3f); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_LOCK_CMP2_MODE0, 0x38); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_LOCK_CMP3_MODE0, 0x00); + } else if (rate == DP_VCO_RATE_9720MHz) { + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_LOCK_CMP1_MODE0, 0x7f); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_LOCK_CMP2_MODE0, 0x43); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_LOCK_CMP3_MODE0, 0x00); + } else { + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_LOCK_CMP1_MODE0, 0x7f); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_LOCK_CMP2_MODE0, 0x70); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_LOCK_CMP3_MODE0, 0x00); + } + + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_BG_TIMER, 0x00); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_BG_TIMER, 0x0a); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_CORECLK_DIV_MODE0, 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_BIAS_EN_CLKBUFLR_EN, 0x37); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_CORE_CLK_EN, 0x0f); + + /* Different for 2700 Mhz clock rate */ + if (rate == DP_VCO_RATE_8100MHz) { + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_CMN_CONFIG, 0x02); + } else if (rate == DP_VCO_RATE_9720MHz) { + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_CMN_CONFIG, 0x42); + } else { + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_CMN_CONFIG, 0x12); + } + + /* Configuration for 1620Mhz VCO frequency */ + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX0_OFFSET + TXn_TRANSCEIVER_BIAS_EN, + 0x1a); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX1_OFFSET + TXn_TRANSCEIVER_BIAS_EN, + 0x1a); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX0_OFFSET + TXn_HIGHZ_DRVR_EN, + 0x00); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX1_OFFSET + TXn_HIGHZ_DRVR_EN, + 0x00); + + MDSS_PLL_REG_W(dp_res->pll_base, + DP_PHY_TX0_TX1_LANE_CTL, 0x05); + MDSS_PLL_REG_W(dp_res->pll_base, + DP_PHY_TX2_TX3_LANE_CTL, 0x05); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX0_OFFSET + TXn_VMODE_CTRL1, + 0x40); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX1_OFFSET + TXn_VMODE_CTRL1, + 0x40); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX0_OFFSET + TXn_PRE_STALL_LDO_BOOST_EN, + 0x30); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX1_OFFSET + TXn_PRE_STALL_LDO_BOOST_EN, + 0x30); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX0_OFFSET + TXn_INTERFACE_SELECT, + 0x3d); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX1_OFFSET + TXn_INTERFACE_SELECT, + 0x3d); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX0_OFFSET + TXn_CLKBUF_ENABLE, + 0x0f); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX1_OFFSET + TXn_CLKBUF_ENABLE, + 0x0f); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX0_OFFSET + TXn_RESET_TSYNC_EN, + 0x03); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX1_OFFSET + TXn_RESET_TSYNC_EN, + 0x03); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX0_OFFSET + TXn_TRAN_DRVR_EMP_EN, + 0x03); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX1_OFFSET + TXn_TRAN_DRVR_EMP_EN, + 0x03); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX0_OFFSET + TXn_PARRATE_REC_DETECT_IDLE_EN, + 0x00); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX1_OFFSET + TXn_PARRATE_REC_DETECT_IDLE_EN, + 0x00); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX0_OFFSET + TXn_TX_INTERFACE_MODE, + 0x00); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX1_OFFSET + TXn_TX_INTERFACE_MODE, + 0x00); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX0_OFFSET + TXn_TX_EMP_POST1_LVL, + 0x23); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX1_OFFSET + TXn_TX_EMP_POST1_LVL, + 0x23); + + return res; +} + +static bool dp_pll_lock_status(struct mdss_pll_resources *dp_res) +{ + u32 status; + bool pll_locked; + + /* poll for PLL ready 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 if (readl_poll_timeout_atomic((dp_res->pll_base + + DP_PHY_STATUS), + status, + ((status & BIT(1)) > 0), + DP_PLL_POLL_SLEEP_US, + DP_PLL_POLL_TIMEOUT_US)) { + pr_err("%s: Phy_ready is not high. Status=%x\n", + __func__, status); + pll_locked = false; + } else { + pll_locked = true; + } + + return pll_locked; +} + + +static int dp_pll_enable(struct clk *c) +{ + int rc = 0; + u32 status; + struct dp_pll_vco_clk *vco = mdss_dp_to_vco_clk(c); + struct mdss_pll_resources *dp_res = vco->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); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_COM_RESETSM_CNTRL, 0x20); + + /* poll for PLL ready 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); + rc = -EINVAL; + goto lock_err; + } + + MDSS_PLL_REG_W(dp_res->phy_base, + DP_PHY_CFG, 0x19); + + /* poll for PHY ready status */ + if (readl_poll_timeout_atomic((dp_res->phy_base + + DP_PHY_STATUS), + status, + ((status & BIT(1)) > 0), + DP_PLL_POLL_SLEEP_US, + DP_PLL_POLL_TIMEOUT_US)) { + pr_err("%s: Phy_ready is not high. Status=%x\n", + __func__, status); + rc = -EINVAL; + goto lock_err; + } + + pr_debug("%s: PLL is locked\n", __func__); + + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX0_OFFSET + TXn_TRANSCEIVER_BIAS_EN, + 0x3f); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX1_OFFSET + TXn_TRANSCEIVER_BIAS_EN, + 0x3f); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX0_OFFSET + TXn_HIGHZ_DRVR_EN, + 0x10); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX1_OFFSET + TXn_HIGHZ_DRVR_EN, + 0x10); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX0_OFFSET + TXn_TX_POL_INV, + 0x0a); + MDSS_PLL_REG_W(dp_res->pll_base, + QSERDES_TX1_OFFSET + TXn_TX_POL_INV, + 0x0a); + MDSS_PLL_REG_W(dp_res->phy_base, + DP_PHY_CFG, 0x18); + MDSS_PLL_REG_W(dp_res->phy_base, + DP_PHY_CFG, 0x19); + + /* + * Make sure all the register writes are completed before + * doing any other operation + */ + wmb(); + +lock_err: + return rc; +} + +static int dp_pll_disable(struct clk *c) +{ + int rc = 0; + struct dp_pll_vco_clk *vco = mdss_dp_to_vco_clk(c); + 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, 0x3c); + /* + * Make sure all the register writes to disable PLL are + * completed before doing any other operation + */ + wmb(); + + return rc; +} + + +int dp_vco_prepare(struct clk *c) +{ + int rc = 0; + struct dp_pll_vco_clk *vco = mdss_dp_to_vco_clk(c); + struct mdss_pll_resources *dp_pll_res = vco->priv; + + DEV_DBG("rate=%ld\n", vco->rate); + rc = mdss_pll_resource_enable(dp_pll_res, true); + if (rc) { + pr_err("Failed to enable mdss DP pll resources\n"); + goto error; + } + + rc = dp_pll_enable(c); + if (rc) { + mdss_pll_resource_enable(dp_pll_res, false); + pr_err("ndx=%d failed to enable dsi pll\n", + dp_pll_res->index); + } + +error: + return rc; +} + +void dp_vco_unprepare(struct clk *c) +{ + struct dp_pll_vco_clk *vco = mdss_dp_to_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + + if (!io) { + DEV_ERR("Invalid input parameter\n"); + return; + } + + if (!io->pll_on && + mdss_pll_resource_enable(io, true)) { + DEV_ERR("pll resource can't be enabled\n"); + return; + } + dp_pll_disable(c); + + io->handoff_resources = false; + mdss_pll_resource_enable(io, false); + io->pll_on = false; +} + +int dp_vco_set_rate(struct clk *c, unsigned long rate) +{ + struct dp_pll_vco_clk *vco = mdss_dp_to_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + int rc; + + rc = mdss_pll_resource_enable(io, 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(vco, rate); + if (rc) + DEV_ERR("%s: Failed to set clk rate\n", __func__); + + mdss_pll_resource_enable(io, false); + + vco->rate = rate; + + return 0; +} + +unsigned long dp_vco_get_rate(struct clk *c) +{ + struct dp_pll_vco_clk *vco = mdss_dp_to_vco_clk(c); + int rc; + u32 div, hsclk_div, link2xclk_div; + u64 vco_rate; + struct mdss_pll_resources *pll = vco->priv; + + rc = mdss_pll_resource_enable(pll, true); + if (rc) { + pr_err("Failed to enable mdss DP pll=%d\n", pll->index); + return rc; + } + + div = MDSS_PLL_REG_R(pll->pll_base, QSERDES_COM_HSCLK_SEL); + div &= 0x0f; + + if (div == 4) + hsclk_div = 3; + else + hsclk_div = 2; + + div = MDSS_PLL_REG_R(pll->phy_base, DP_PHY_MODE); + + if (div & 0x48) + 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_RATE_9720MHz; + } else { + if (hsclk_div == 3) + vco_rate = DP_VCO_RATE_8100MHz; + else + vco_rate = DP_VCO_RATE_10800MHz; + } + + pr_debug("returning vco rate = %lu\n", (unsigned long)vco_rate); + + mdss_pll_resource_enable(pll, false); + + return (unsigned long)vco_rate; +} + +long dp_vco_round_rate(struct clk *c, unsigned long rate) +{ + unsigned long rrate = rate; + struct dp_pll_vco_clk *vco = mdss_dp_to_vco_clk(c); + + if (rate <= vco->min_rate) + rrate = vco->min_rate; + else if (rate <= DP_VCO_RATE_9720MHz) + rrate = DP_VCO_RATE_9720MHz; + else + rrate = vco->max_rate; + + pr_debug("%s: rrate=%ld\n", __func__, rrate); + + return rrate; +} + +enum handoff dp_vco_handoff(struct clk *c) +{ + enum handoff ret = HANDOFF_DISABLED_CLK; + struct dp_pll_vco_clk *vco = mdss_dp_to_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + + if (mdss_pll_resource_enable(io, true)) { + DEV_ERR("pll resource can't be enabled\n"); + return ret; + } + + if (dp_pll_lock_status(io)) { + io->pll_on = true; + c->rate = dp_vco_get_rate(c); + io->handoff_resources = true; + ret = HANDOFF_ENABLED_CLK; + } else { + io->handoff_resources = false; + mdss_pll_resource_enable(io, false); + DEV_DBG("%s: PLL not locked\n", __func__); + } + + DEV_DBG("done, ret=%d\n", ret); + return ret; +} diff --git a/drivers/clk/msm/mdss/mdss-dp-pll-cobalt.c b/drivers/clk/msm/mdss/mdss-dp-pll-cobalt.c new file mode 100644 index 000000000000..1465ea1efe59 --- /dev/null +++ b/drivers/clk/msm/mdss/mdss-dp-pll-cobalt.c @@ -0,0 +1,253 @@ +/* Copyright (c) 2016, 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----------+ +|vco_divided_clk_src | | dp_link_2x_clk | | dp_link_2x_clk | +| (aux_clk_ops) | | | | | +v----------+----------v | divsel_five | | divsel_ten | + | +----------+----------+ +----------+----------+ + | | | + v v v + | +--------------------+ | + Input to MMSSCC block | | | | + for DP pixel clock +--> dp_link_2x_clk_mux <--+ + | | + +----------+---------+ + | + v + Input to MMSSCC block + for link clk, crypto clk + and interface clock + + +****************************************************************************** +*/ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include + +#include "mdss-pll.h" +#include "mdss-dp-pll.h" +#include "mdss-dp-pll-cobalt.h" + +static struct clk_ops clk_ops_gen_mux_dp; +static struct clk_ops clk_ops_hsclk_divsel_clk_src_c; +static struct clk_ops clk_ops_vco_divided_clk_src_c; +static struct clk_ops clk_ops_link_2x_clk_div_c; + +static struct clk_div_ops hsclk_divsel_ops = { + .set_div = hsclk_divsel_set_div, + .get_div = hsclk_divsel_get_div, +}; + +static struct clk_div_ops link2xclk_divsel_ops = { + .set_div = link2xclk_divsel_set_div, + .get_div = link2xclk_divsel_get_div, +}; + +static struct clk_div_ops vco_divided_clk_ops = { + .set_div = vco_divided_clk_set_div, + .get_div = vco_divided_clk_get_div, +}; + +static struct clk_ops dp_cobalt_vco_clk_ops = { + .set_rate = dp_vco_set_rate, + .round_rate = dp_vco_round_rate, + .prepare = dp_vco_prepare, + .unprepare = dp_vco_unprepare, + .handoff = dp_vco_handoff, +}; + +static struct clk_mux_ops mdss_mux_ops = { + .set_mux_sel = mdss_set_mux_sel, + .get_mux_sel = mdss_get_mux_sel, +}; + +static struct dp_pll_vco_clk dp_vco_clk = { + .min_rate = DP_VCO_RATE_8100MHz, + .max_rate = DP_VCO_RATE_10800MHz, + .c = { + .dbg_name = "dp_vco_clk", + .ops = &dp_cobalt_vco_clk_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dp_vco_clk.c), + }, +}; + +static struct div_clk hsclk_divsel_clk_src = { + .data = { + .min_div = 2, + .max_div = 3, + }, + .ops = &hsclk_divsel_ops, + .c = { + .parent = &dp_vco_clk.c, + .dbg_name = "hsclk_divsel_clk_src", + .ops = &clk_ops_hsclk_divsel_clk_src_c, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(hsclk_divsel_clk_src.c), + }, +}; + +static struct div_clk dp_link_2x_clk_divsel_five = { + .data = { + .div = 5, + .min_div = 5, + .max_div = 5, + }, + .ops = &link2xclk_divsel_ops, + .c = { + .parent = &hsclk_divsel_clk_src.c, + .dbg_name = "dp_link_2x_clk_divsel_five", + .ops = &clk_ops_link_2x_clk_div_c, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dp_link_2x_clk_divsel_five.c), + }, +}; + +static struct div_clk dp_link_2x_clk_divsel_ten = { + .data = { + .div = 10, + .min_div = 10, + .max_div = 10, + }, + .ops = &link2xclk_divsel_ops, + .c = { + .parent = &hsclk_divsel_clk_src.c, + .dbg_name = "dp_link_2x_clk_divsel_ten", + .ops = &clk_ops_link_2x_clk_div_c, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dp_link_2x_clk_divsel_ten.c), + }, +}; + +static struct mux_clk dp_link_2x_clk_mux = { + .num_parents = 2, + .parents = (struct clk_src[]) { + {&dp_link_2x_clk_divsel_five.c, 0}, + {&dp_link_2x_clk_divsel_ten.c, 1}, + }, + .ops = &mdss_mux_ops, + .c = { + .parent = &dp_link_2x_clk_divsel_five.c, + .dbg_name = "dp_link_2x_clk_mux", + .ops = &clk_ops_gen_mux_dp, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dp_link_2x_clk_mux.c), + } +}; + +static struct div_clk vco_divided_clk_src = { + .data = { + .div = 4, + .min_div = 4, + .max_div = 4, + }, + .ops = &vco_divided_clk_ops, + .c = { + .parent = &hsclk_divsel_clk_src.c, + .dbg_name = "vco_divided_clk", + .ops = &clk_ops_vco_divided_clk_src_c, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(vco_divided_clk_src.c), + }, +}; + +static struct clk_lookup dp_pllcc_cobalt[] = { + CLK_LIST(dp_vco_clk), + CLK_LIST(hsclk_divsel_clk_src), + CLK_LIST(dp_link_2x_clk_divsel_five), + CLK_LIST(dp_link_2x_clk_divsel_ten), + CLK_LIST(dp_link_2x_clk_mux), + CLK_LIST(vco_divided_clk_src), +}; + +int dp_pll_clock_register_cobalt(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int rc = -ENOTSUPP; + + if (!pll_res || !pll_res->pll_base || !pll_res->phy_base) { + DEV_ERR("%s: Invalid input parameters\n", __func__); + return -EINVAL; + } + + /* Set client data for vco, mux and div clocks */ + dp_vco_clk.priv = pll_res; + hsclk_divsel_clk_src.priv = pll_res; + dp_link_2x_clk_mux.priv = pll_res; + vco_divided_clk_src.priv = pll_res; + dp_link_2x_clk_divsel_five.priv = pll_res; + dp_link_2x_clk_divsel_ten.priv = pll_res; + + clk_ops_gen_mux_dp = clk_ops_gen_mux; + clk_ops_gen_mux_dp.round_rate = parent_round_rate; + clk_ops_gen_mux_dp.set_rate = parent_set_rate; + + clk_ops_hsclk_divsel_clk_src_c = clk_ops_div; + clk_ops_hsclk_divsel_clk_src_c.prepare = mdss_pll_div_prepare; + + clk_ops_link_2x_clk_div_c = clk_ops_div; + clk_ops_link_2x_clk_div_c.prepare = mdss_pll_div_prepare; + + /* + * Set the ops for the divider in the pixel clock tree to the + * slave_div to ensure that a set rate on this divider clock will not + * be propagated to it's parent. This is needed ensure that when we set + * the rate for pixel clock, the vco is not reconfigured + */ + clk_ops_vco_divided_clk_src_c = clk_ops_slave_div; + clk_ops_vco_divided_clk_src_c.prepare = mdss_pll_div_prepare; + + /* We can select different clock ops for future versions */ + dp_vco_clk.c.ops = &dp_cobalt_vco_clk_ops; + + rc = of_msm_clock_register(pdev->dev.of_node, dp_pllcc_cobalt, + ARRAY_SIZE(dp_pllcc_cobalt)); + if (rc) { + DEV_ERR("%s: Clock register failed rc=%d\n", __func__, rc); + rc = -EPROBE_DEFER; + } else { + DEV_DBG("%s SUCCESS\n", __func__); + } + + return rc; +} diff --git a/drivers/clk/msm/mdss/mdss-dp-pll-cobalt.h b/drivers/clk/msm/mdss/mdss-dp-pll-cobalt.h new file mode 100644 index 000000000000..3a6f07a8237b --- /dev/null +++ b/drivers/clk/msm/mdss/mdss-dp-pll-cobalt.h @@ -0,0 +1,166 @@ +/* Copyright (c) 2016, 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_COBALT_H +#define __MDSS_DP_PLL_COBALT_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_PD_CTL 0x0014 +#define DP_PHY_MODE 0x0018 + +#define DP_PHY_AUX_CFG0 0x001C +#define DP_PHY_AUX_CFG1 0x0020 +#define DP_PHY_AUX_CFG2 0x0024 +#define DP_PHY_AUX_CFG3 0x0028 +#define DP_PHY_AUX_CFG4 0x002C +#define DP_PHY_AUX_CFG5 0x0030 +#define DP_PHY_AUX_CFG6 0x0034 +#define DP_PHY_AUX_CFG7 0x0038 +#define DP_PHY_AUX_CFG8 0x003C +#define DP_PHY_AUX_CFG9 0x0040 +#define DP_PHY_AUX_INTERRUPT_MASK 0x0044 +#define DP_PHY_AUX_INTERRUPT_CLEAR 0x0048 +#define DP_PHY_AUX_BIST_CFG 0x004C + +#define DP_PHY_VCO_DIV 0x0064 +#define DP_PHY_TX0_TX1_LANE_CTL 0x0068 + +#define DP_PHY_TX2_TX3_LANE_CTL 0x0084 +#define DP_PHY_STATUS 0x00BC + +/* 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_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_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_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_CMN_IETRIM 0x004C +#define QSERDES_COM_CMN_IPTRIM 0x0050 + +#define QSERDES_COM_CP_CTRL_MODE0 0x0060 +#define QSERDES_COM_CP_CTRL_MODE1 0x0064 +#define QSERDES_COM_PLL_RCTRL_MODE0 0x0068 +#define QSERDES_COM_PLL_RCTRL_MODE1 0x006C +#define QSERDES_COM_PLL_CCTRL_MODE0 0x0070 +#define QSERDES_COM_PLL_CCTRL_MODE1 0x0074 +#define QSERDES_COM_PLL_CNTRL 0x0078 +#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM 0x007C +#define QSERDES_COM_SYSCLK_EN_SEL 0x0080 +#define QSERDES_COM_CML_SYSCLK_SEL 0x0084 +#define QSERDES_COM_RESETSM_CNTRL 0x0088 +#define QSERDES_COM_RESETSM_CNTRL2 0x008C +#define QSERDES_COM_LOCK_CMP_EN 0x0090 +#define QSERDES_COM_LOCK_CMP_CFG 0x0094 +#define QSERDES_COM_LOCK_CMP1_MODE0 0x0098 +#define QSERDES_COM_LOCK_CMP2_MODE0 0x009C +#define QSERDES_COM_LOCK_CMP3_MODE0 0x00A0 + +#define QSERDES_COM_DEC_START_MODE0 0x00B0 +#define QSERDES_COM_DEC_START_MODE1 0x00B4 +#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x00B8 +#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x00BC +#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x00C0 +#define QSERDES_COM_DIV_FRAC_START1_MODE1 0x00C4 +#define QSERDES_COM_DIV_FRAC_START2_MODE1 0x00C8 +#define QSERDES_COM_DIV_FRAC_START3_MODE1 0x00CC +#define QSERDES_COM_INTEGLOOP_INITVAL 0x00D0 +#define QSERDES_COM_INTEGLOOP_EN 0x00D4 +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x00D8 +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x00DC +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x00E0 +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 0x00E4 +#define QSERDES_COM_VCOCAL_DEADMAN_CTRL 0x00E8 +#define QSERDES_COM_VCO_TUNE_CTRL 0x00EC +#define QSERDES_COM_VCO_TUNE_MAP 0x00F0 + +#define QSERDES_COM_CMN_STATUS 0x0124 +#define QSERDES_COM_RESET_SM_STATUS 0x0128 + +#define QSERDES_COM_CLK_SEL 0x0138 +#define QSERDES_COM_HSCLK_SEL 0x013C + +#define QSERDES_COM_CORECLK_DIV_MODE0 0x0148 + +#define QSERDES_COM_SW_RESET 0x0150 +#define QSERDES_COM_CORE_CLK_EN 0x0154 +#define QSERDES_COM_C_READY_STATUS 0x0158 +#define QSERDES_COM_CMN_CONFIG 0x015C + +#define QSERDES_COM_SVS_MODE_CLK_SEL 0x0164 + +#define DP_PLL_POLL_SLEEP_US 500 +#define DP_PLL_POLL_TIMEOUT_US 10000 + +#define DP_VCO_RATE_8100MHz 8100000000ULL +#define DP_VCO_RATE_9720MHz 9720000000ULL +#define DP_VCO_RATE_10800MHz 10800000000ULL + +int dp_vco_set_rate(struct clk *c, unsigned long rate); +unsigned long dp_vco_get_rate(struct clk *c); +long dp_vco_round_rate(struct clk *c, unsigned long rate); +enum handoff dp_vco_handoff(struct clk *c); +int dp_vco_prepare(struct clk *c); +void dp_vco_unprepare(struct clk *c); +int hsclk_divsel_set_div(struct div_clk *clk, int div); +int hsclk_divsel_get_div(struct div_clk *clk); +int link2xclk_divsel_set_div(struct div_clk *clk, int div); +int link2xclk_divsel_get_div(struct div_clk *clk); +int vco_divided_clk_set_div(struct div_clk *clk, int div); +int vco_divided_clk_get_div(struct div_clk *clk); + +#endif /* __MDSS_DP_PLL_COBALT_H */ diff --git a/drivers/clk/msm/mdss/mdss-dp-pll.h b/drivers/clk/msm/mdss/mdss-dp-pll.h new file mode 100644 index 000000000000..3abc4c29c17e --- /dev/null +++ b/drivers/clk/msm/mdss/mdss-dp-pll.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2016, 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 { + unsigned long rate; /* current vco rate */ + u64 min_rate; /* min vco rate */ + u64 max_rate; /* max vco rate */ + void *priv; + + struct clk c; +}; + +static inline struct dp_pll_vco_clk *mdss_dp_to_vco_clk(struct clk *clk) +{ + return container_of(clk, struct dp_pll_vco_clk, c); +} + +int dp_pll_clock_register_cobalt(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); + + +#endif /* __MDSS_DP_PLL_H */ + diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-8996.c b/drivers/clk/msm/mdss/mdss-dsi-pll-8996.c index dc80edd04725..1de1b997a041 100644 --- a/drivers/clk/msm/mdss/mdss-dsi-pll-8996.c +++ b/drivers/clk/msm/mdss/mdss-dsi-pll-8996.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2016, 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 @@ -496,18 +496,18 @@ int dsi_pll_clock_register_8996(struct platform_device *pdev, /* hr_oclk3, pixel */ n2_clk_src_ops = clk_ops_slave_div; - n2_clk_src_ops.prepare = dsi_pll_div_prepare; + n2_clk_src_ops.prepare = mdss_pll_div_prepare; shadow_n2_clk_src_ops = clk_ops_slave_div; /* hr_ockl2, byte, vco pll */ post_n1_div_clk_src_ops = clk_ops_div; - post_n1_div_clk_src_ops.prepare = dsi_pll_div_prepare; + post_n1_div_clk_src_ops.prepare = mdss_pll_div_prepare; shadow_post_n1_div_clk_src_ops = clk_ops_div; byte_clk_src_ops = clk_ops_div; - byte_clk_src_ops.prepare = dsi_pll_div_prepare; + byte_clk_src_ops.prepare = mdss_pll_div_prepare; clk_ops_gen_mux_dsi = clk_ops_gen_mux; clk_ops_gen_mux_dsi.round_rate = parent_round_rate; diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-cobalt.c b/drivers/clk/msm/mdss/mdss-dsi-pll-cobalt.c index 594bc4e19033..64b7f4d6dae5 100644 --- a/drivers/clk/msm/mdss/mdss-dsi-pll-cobalt.c +++ b/drivers/clk/msm/mdss/mdss-dsi-pll-cobalt.c @@ -901,19 +901,9 @@ static struct clk_ops clk_ops_vco_cobalt = { .unprepare = vco_cobalt_unprepare, }; -static int set_mdss_mux_sel(struct mux_clk *clk, int sel) -{ - return 0; -} - -static int get_mdss_mux_sel(struct mux_clk *clk) -{ - return 0; -} - static struct clk_mux_ops mdss_mux_ops = { - .set_mux_sel = set_mdss_mux_sel, - .get_mux_sel = get_mdss_mux_sel, + .set_mux_sel = mdss_set_mux_sel, + .get_mux_sel = mdss_get_mux_sel, }; /* @@ -1290,7 +1280,7 @@ int dsi_pll_clock_register_cobalt(struct platform_device *pdev, clk_ops_gen_mux_dsi.set_rate = parent_set_rate; clk_ops_bitclk_src_c = clk_ops_div; - clk_ops_bitclk_src_c.prepare = dsi_pll_div_prepare; + clk_ops_bitclk_src_c.prepare = mdss_pll_div_prepare; /* * Set the ops for the two dividers in the pixel clock tree to the @@ -1299,13 +1289,13 @@ int dsi_pll_clock_register_cobalt(struct platform_device *pdev, * the rate for pixel clock, the vco is not reconfigured */ clk_ops_post_vco_div_c = clk_ops_slave_div; - clk_ops_post_vco_div_c.prepare = dsi_pll_div_prepare; + clk_ops_post_vco_div_c.prepare = mdss_pll_div_prepare; clk_ops_post_bit_div_c = clk_ops_slave_div; - clk_ops_post_bit_div_c.prepare = dsi_pll_div_prepare; + clk_ops_post_bit_div_c.prepare = mdss_pll_div_prepare; clk_ops_pclk_src_c = clk_ops_div; - clk_ops_pclk_src_c.prepare = dsi_pll_div_prepare; + clk_ops_pclk_src_c.prepare = mdss_pll_div_prepare; pll_res->vco_delay = VCO_DELAY_USEC; if (ndx == 0) { diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll.h b/drivers/clk/msm/mdss/mdss-dsi-pll.h index d02bba9226a8..f88ae4d0eea1 100644 --- a/drivers/clk/msm/mdss/mdss-dsi-pll.h +++ b/drivers/clk/msm/mdss/mdss-dsi-pll.h @@ -50,13 +50,6 @@ static inline struct dsi_pll_vco_clk *to_vco_clk(struct clk *clk) return container_of(clk, struct dsi_pll_vco_clk, c); } -static inline 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_clock_register_hpm(struct platform_device *pdev, struct mdss_pll_resources *pll_res); int dsi_pll_clock_register_20nm(struct platform_device *pdev, diff --git a/drivers/clk/msm/mdss/mdss-pll.c b/drivers/clk/msm/mdss/mdss-pll.c index 75422f823d39..4da06899bab1 100644 --- a/drivers/clk/msm/mdss/mdss-pll.c +++ b/drivers/clk/msm/mdss/mdss-pll.c @@ -24,6 +24,7 @@ #include "mdss-pll.h" #include "mdss-dsi-pll.h" #include "mdss-hdmi-pll.h" +#include "mdss-dp-pll.h" int mdss_pll_resource_enable(struct mdss_pll_resources *pll_res, bool enable) { @@ -136,6 +137,8 @@ static int mdss_pll_resource_parse(struct platform_device *pdev, pll_res->revision = 2; } else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_cobalt")) { pll_res->pll_interface_type = MDSS_DSI_PLL_COBALT; + } else if (!strcmp(compatible_stream, "qcom,mdss_dp_pll_cobalt")) { + pll_res->pll_interface_type = MDSS_DP_PLL_COBALT; } 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")) { @@ -173,6 +176,8 @@ static int mdss_pll_clock_register(struct platform_device *pdev, break; case MDSS_DSI_PLL_COBALT: rc = dsi_pll_clock_register_cobalt(pdev, pll_res); + case MDSS_DP_PLL_COBALT: + rc = dp_pll_clock_register_cobalt(pdev, pll_res); break; case MDSS_HDMI_PLL_8996: rc = hdmi_8996_v1_pll_clock_register(pdev, pll_res); @@ -389,6 +394,7 @@ static const struct of_device_id mdss_pll_dt_match[] = { {.compatible = "qcom,mdss_hdmi_pll_8996_v2"}, {.compatible = "qcom,mdss_hdmi_pll_8996_v3"}, {.compatible = "qcom,mdss_hdmi_pll_8996_v3_1p8"}, + {.compatible = "qcom,mdss_dp_pll_cobalt"}, {} }; diff --git a/drivers/clk/msm/mdss/mdss-pll.h b/drivers/clk/msm/mdss/mdss-pll.h index 73d3d19ceea9..c0599f5a0c04 100644 --- a/drivers/clk/msm/mdss/mdss-pll.h +++ b/drivers/clk/msm/mdss/mdss-pll.h @@ -14,6 +14,7 @@ #define __MDSS_PLL_H #include +#include #include #define MDSS_PLL_REG_W(base, offset, data) \ @@ -31,6 +32,7 @@ enum { MDSS_DSI_PLL_8996, MDSS_DSI_PLL_COBALT, + MDSS_DP_PLL_COBALT, MDSS_HDMI_PLL_8996, MDSS_HDMI_PLL_8996_V2, MDSS_HDMI_PLL_8996_V3, @@ -197,6 +199,23 @@ static inline bool is_gdsc_disabled(struct mdss_pll_resources *pll_res) (!(readl_relaxed(pll_res->gdsc_base) & BIT(0)))) ? false : true; } +static inline int mdss_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); +} + +static inline int mdss_set_mux_sel(struct mux_clk *clk, int sel) +{ + return 0; +} + +static inline int mdss_get_mux_sel(struct mux_clk *clk) +{ + return 0; +} + 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); diff --git a/include/dt-bindings/clock/msm-clocks-cobalt.h b/include/dt-bindings/clock/msm-clocks-cobalt.h index 6899733515c3..eb4251cf5e06 100644 --- a/include/dt-bindings/clock/msm-clocks-cobalt.h +++ b/include/dt-bindings/clock/msm-clocks-cobalt.h @@ -451,6 +451,13 @@ #define clk_dsi1pll_bitclk_src 0x13ab045b #define clk_dsi1pll_vco_clk 0x99797b50 +#define clk_dp_vco_clk 0xfcaaeec7 +#define clk_hsclk_divsel_clk_src 0x0a325543 +#define clk_dp_link_2x_clk_divsel_five 0xcfe3f5dd +#define clk_dp_link_2x_clk_divsel_ten 0xfeb9924d +#define clk_dp_link_2x_clk_mux 0xce4c4fc6 +#define clk_vco_divided_clk_src 0x3da6cb51 + /* clock_gpu controlled clocks*/ #define clk_gpucc_xo 0xc4e1a890 #define clk_gpucc_gpll0 0x0db0e37f