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 <chandanu@codeaurora.org>
This commit is contained in:
parent
a240321fd6
commit
3ee6103a8d
12 changed files with 1208 additions and 28 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
708
drivers/clk/msm/mdss/mdss-dp-pll-cobalt-util.c
Normal file
708
drivers/clk/msm/mdss/mdss-dp-pll-cobalt-util.c
Normal file
|
@ -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 <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk/msm-clock-generic.h>
|
||||
|
||||
#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;
|
||||
}
|
253
drivers/clk/msm/mdss/mdss-dp-pll-cobalt.c
Normal file
253
drivers/clk/msm/mdss/mdss-dp-pll-cobalt.c
Normal file
|
@ -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 <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk/msm-clk-provider.h>
|
||||
#include <linux/clk/msm-clk.h>
|
||||
#include <linux/clk/msm-clock-generic.h>
|
||||
#include <dt-bindings/clock/msm-clocks-cobalt.h>
|
||||
|
||||
#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;
|
||||
}
|
166
drivers/clk/msm/mdss/mdss-dp-pll-cobalt.h
Normal file
166
drivers/clk/msm/mdss/mdss-dp-pll-cobalt.h
Normal file
|
@ -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 */
|
36
drivers/clk/msm/mdss/mdss-dp-pll.h
Normal file
36
drivers/clk/msm/mdss/mdss-dp-pll.h
Normal file
|
@ -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 */
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"},
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#define __MDSS_PLL_H
|
||||
|
||||
#include <linux/mdss_io_util.h>
|
||||
#include <linux/clk/msm-clock-generic.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue