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:
Chandan Uddaraju 2016-02-22 16:43:23 -08:00 committed by Jeevan Shriram
parent a240321fd6
commit 3ee6103a8d
12 changed files with 1208 additions and 28 deletions

View file

@ -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

View file

@ -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

View 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;
}

View 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;
}

View 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 */

View 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 */

View file

@ -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;

View file

@ -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) {

View file

@ -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,

View file

@ -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"},
{}
};

View file

@ -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);

View file

@ -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