Commit d6d1439e authored by Dmitry Baryshkov's avatar Dmitry Baryshkov Committed by Rob Clark

drm/msm/dsi: fuse dsi_pll_* code into dsi_phy_* code

Each phy version is tightly coupled with the corresponding PLL code,
there is no need to keep them separate. Fuse source files together in
order to simplify DSI code.
Signed-off-by: default avatarDmitry Baryshkov <dmitry.baryshkov@linaro.org>
Reviewed-by: default avatarAbhinav Kumar <abhinavk@codeaurora.org>
Tested-by: Stephen Boyd <swboyd@chromium.org> # on sc7180 lazor
Link: https://lore.kernel.org/r/20210331105735.3690009-5-dmitry.baryshkov@linaro.orgSigned-off-by: default avatarRob Clark <robdclark@chromium.org>
parent 266a4e58
...@@ -136,13 +136,6 @@ msm-$(CONFIG_DRM_MSM_DSI_14NM_PHY) += dsi/phy/dsi_phy_14nm.o ...@@ -136,13 +136,6 @@ msm-$(CONFIG_DRM_MSM_DSI_14NM_PHY) += dsi/phy/dsi_phy_14nm.o
msm-$(CONFIG_DRM_MSM_DSI_10NM_PHY) += dsi/phy/dsi_phy_10nm.o msm-$(CONFIG_DRM_MSM_DSI_10NM_PHY) += dsi/phy/dsi_phy_10nm.o
msm-$(CONFIG_DRM_MSM_DSI_7NM_PHY) += dsi/phy/dsi_phy_7nm.o msm-$(CONFIG_DRM_MSM_DSI_7NM_PHY) += dsi/phy/dsi_phy_7nm.o
ifeq ($(CONFIG_DRM_MSM_DSI_PLL),y) msm-$(CONFIG_DRM_MSM_DSI_PLL) += dsi/phy/dsi_pll.o
msm-y += dsi/pll/dsi_pll.o
msm-$(CONFIG_DRM_MSM_DSI_28NM_PHY) += dsi/pll/dsi_pll_28nm.o
msm-$(CONFIG_DRM_MSM_DSI_28NM_8960_PHY) += dsi/pll/dsi_pll_28nm_8960.o
msm-$(CONFIG_DRM_MSM_DSI_14NM_PHY) += dsi/pll/dsi_pll_14nm.o
msm-$(CONFIG_DRM_MSM_DSI_10NM_PHY) += dsi/pll/dsi_pll_10nm.o
msm-$(CONFIG_DRM_MSM_DSI_7NM_PHY) += dsi/pll/dsi_pll_7nm.o
endif
obj-$(CONFIG_DRM_MSM) += msm.o obj-$(CONFIG_DRM_MSM) += msm.o
...@@ -3,11 +3,884 @@ ...@@ -3,11 +3,884 @@
* Copyright (c) 2018, The Linux Foundation * Copyright (c) 2018, The Linux Foundation
*/ */
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include "dsi_pll.h"
#include "dsi_phy.h" #include "dsi_phy.h"
#include "dsi.xml.h" #include "dsi.xml.h"
/*
* DSI PLL 10nm - clock diagram (eg: DSI0):
*
* dsi0_pll_out_div_clk dsi0_pll_bit_clk
* | |
* | |
* +---------+ | +----------+ | +----+
* dsi0vco_clk ---| out_div |--o--| divl_3_0 |--o--| /8 |-- dsi0_phy_pll_out_byteclk
* +---------+ | +----------+ | +----+
* | |
* | | dsi0_pll_by_2_bit_clk
* | | |
* | | +----+ | |\ dsi0_pclk_mux
* | |--| /2 |--o--| \ |
* | | +----+ | \ | +---------+
* | --------------| |--o--| div_7_4 |-- dsi0_phy_pll_out_dsiclk
* |------------------------------| / +---------+
* | +-----+ | /
* -----------| /4? |--o----------|/
* +-----+ | |
* | |dsiclk_sel
* |
* dsi0_pll_post_out_div_clk
*/
#define DSI_BYTE_PLL_CLK 0
#define DSI_PIXEL_PLL_CLK 1
#define NUM_PROVIDED_CLKS 2
#define VCO_REF_CLK_RATE 19200000
struct dsi_pll_regs {
u32 pll_prop_gain_rate;
u32 pll_lockdet_rate;
u32 decimal_div_start;
u32 frac_div_start_low;
u32 frac_div_start_mid;
u32 frac_div_start_high;
u32 pll_clock_inverters;
u32 ssc_stepsize_low;
u32 ssc_stepsize_high;
u32 ssc_div_per_low;
u32 ssc_div_per_high;
u32 ssc_adjper_low;
u32 ssc_adjper_high;
u32 ssc_control;
};
struct dsi_pll_config {
u32 ref_freq;
bool div_override;
u32 output_div;
bool ignore_frac;
bool disable_prescaler;
bool enable_ssc;
bool ssc_center;
u32 dec_bits;
u32 frac_bits;
u32 lock_timer;
u32 ssc_freq;
u32 ssc_offset;
u32 ssc_adj_per;
u32 thresh_cycles;
u32 refclk_cycles;
};
struct pll_10nm_cached_state {
unsigned long vco_rate;
u8 bit_clk_div;
u8 pix_clk_div;
u8 pll_out_div;
u8 pll_mux;
};
struct dsi_pll_10nm {
struct msm_dsi_pll base;
int id;
struct platform_device *pdev;
void __iomem *phy_cmn_mmio;
void __iomem *mmio;
u64 vco_ref_clk_rate;
u64 vco_current_rate;
/* protects REG_DSI_10nm_PHY_CMN_CLK_CFG0 register */
spinlock_t postdiv_lock;
int vco_delay;
struct dsi_pll_config pll_configuration;
struct dsi_pll_regs reg_setup;
/* private clocks: */
struct clk_hw *out_div_clk_hw;
struct clk_hw *bit_clk_hw;
struct clk_hw *byte_clk_hw;
struct clk_hw *by_2_bit_clk_hw;
struct clk_hw *post_out_div_clk_hw;
struct clk_hw *pclk_mux_hw;
struct clk_hw *out_dsiclk_hw;
/* clock-provider: */
struct clk_hw_onecell_data *hw_data;
struct pll_10nm_cached_state cached_state;
enum msm_dsi_phy_usecase uc;
struct dsi_pll_10nm *slave;
};
#define to_pll_10nm(x) container_of(x, struct dsi_pll_10nm, base)
/*
* Global list of private DSI PLL struct pointers. We need this for Dual DSI
* mode, where the master PLL's clk_ops needs access the slave's private data
*/
static struct dsi_pll_10nm *pll_10nm_list[DSI_MAX];
static void dsi_pll_setup_config(struct dsi_pll_10nm *pll)
{
struct dsi_pll_config *config = &pll->pll_configuration;
config->ref_freq = pll->vco_ref_clk_rate;
config->output_div = 1;
config->dec_bits = 8;
config->frac_bits = 18;
config->lock_timer = 64;
config->ssc_freq = 31500;
config->ssc_offset = 5000;
config->ssc_adj_per = 2;
config->thresh_cycles = 32;
config->refclk_cycles = 256;
config->div_override = false;
config->ignore_frac = false;
config->disable_prescaler = false;
config->enable_ssc = false;
config->ssc_center = 0;
}
static void dsi_pll_calc_dec_frac(struct dsi_pll_10nm *pll)
{
struct dsi_pll_config *config = &pll->pll_configuration;
struct dsi_pll_regs *regs = &pll->reg_setup;
u64 fref = pll->vco_ref_clk_rate;
u64 pll_freq;
u64 divider;
u64 dec, dec_multiple;
u32 frac;
u64 multiplier;
pll_freq = pll->vco_current_rate;
if (config->disable_prescaler)
divider = fref;
else
divider = fref * 2;
multiplier = 1 << config->frac_bits;
dec_multiple = div_u64(pll_freq * multiplier, divider);
dec = div_u64_rem(dec_multiple, multiplier, &frac);
if (pll_freq <= 1900000000UL)
regs->pll_prop_gain_rate = 8;
else if (pll_freq <= 3000000000UL)
regs->pll_prop_gain_rate = 10;
else
regs->pll_prop_gain_rate = 12;
if (pll_freq < 1100000000UL)
regs->pll_clock_inverters = 8;
else
regs->pll_clock_inverters = 0;
regs->pll_lockdet_rate = config->lock_timer;
regs->decimal_div_start = dec;
regs->frac_div_start_low = (frac & 0xff);
regs->frac_div_start_mid = (frac & 0xff00) >> 8;
regs->frac_div_start_high = (frac & 0x30000) >> 16;
}
#define SSC_CENTER BIT(0)
#define SSC_EN BIT(1)
static void dsi_pll_calc_ssc(struct dsi_pll_10nm *pll)
{
struct dsi_pll_config *config = &pll->pll_configuration;
struct dsi_pll_regs *regs = &pll->reg_setup;
u32 ssc_per;
u32 ssc_mod;
u64 ssc_step_size;
u64 frac;
if (!config->enable_ssc) {
DBG("SSC not enabled\n");
return;
}
ssc_per = DIV_ROUND_CLOSEST(config->ref_freq, config->ssc_freq) / 2 - 1;
ssc_mod = (ssc_per + 1) % (config->ssc_adj_per + 1);
ssc_per -= ssc_mod;
frac = regs->frac_div_start_low |
(regs->frac_div_start_mid << 8) |
(regs->frac_div_start_high << 16);
ssc_step_size = regs->decimal_div_start;
ssc_step_size *= (1 << config->frac_bits);
ssc_step_size += frac;
ssc_step_size *= config->ssc_offset;
ssc_step_size *= (config->ssc_adj_per + 1);
ssc_step_size = div_u64(ssc_step_size, (ssc_per + 1));
ssc_step_size = DIV_ROUND_CLOSEST_ULL(ssc_step_size, 1000000);
regs->ssc_div_per_low = ssc_per & 0xFF;
regs->ssc_div_per_high = (ssc_per & 0xFF00) >> 8;
regs->ssc_stepsize_low = (u32)(ssc_step_size & 0xFF);
regs->ssc_stepsize_high = (u32)((ssc_step_size & 0xFF00) >> 8);
regs->ssc_adjper_low = config->ssc_adj_per & 0xFF;
regs->ssc_adjper_high = (config->ssc_adj_per & 0xFF00) >> 8;
regs->ssc_control = config->ssc_center ? SSC_CENTER : 0;
pr_debug("SCC: Dec:%d, frac:%llu, frac_bits:%d\n",
regs->decimal_div_start, frac, config->frac_bits);
pr_debug("SSC: div_per:0x%X, stepsize:0x%X, adjper:0x%X\n",
ssc_per, (u32)ssc_step_size, config->ssc_adj_per);
}
static void dsi_pll_ssc_commit(struct dsi_pll_10nm *pll)
{
void __iomem *base = pll->mmio;
struct dsi_pll_regs *regs = &pll->reg_setup;
if (pll->pll_configuration.enable_ssc) {
pr_debug("SSC is enabled\n");
pll_write(base + REG_DSI_10nm_PHY_PLL_SSC_STEPSIZE_LOW_1,
regs->ssc_stepsize_low);
pll_write(base + REG_DSI_10nm_PHY_PLL_SSC_STEPSIZE_HIGH_1,
regs->ssc_stepsize_high);
pll_write(base + REG_DSI_10nm_PHY_PLL_SSC_DIV_PER_LOW_1,
regs->ssc_div_per_low);
pll_write(base + REG_DSI_10nm_PHY_PLL_SSC_DIV_PER_HIGH_1,
regs->ssc_div_per_high);
pll_write(base + REG_DSI_10nm_PHY_PLL_SSC_DIV_ADJPER_LOW_1,
regs->ssc_adjper_low);
pll_write(base + REG_DSI_10nm_PHY_PLL_SSC_DIV_ADJPER_HIGH_1,
regs->ssc_adjper_high);
pll_write(base + REG_DSI_10nm_PHY_PLL_SSC_CONTROL,
SSC_EN | regs->ssc_control);
}
}
static void dsi_pll_config_hzindep_reg(struct dsi_pll_10nm *pll)
{
void __iomem *base = pll->mmio;
pll_write(base + REG_DSI_10nm_PHY_PLL_ANALOG_CONTROLS_ONE, 0x80);
pll_write(base + REG_DSI_10nm_PHY_PLL_ANALOG_CONTROLS_TWO, 0x03);
pll_write(base + REG_DSI_10nm_PHY_PLL_ANALOG_CONTROLS_THREE, 0x00);
pll_write(base + REG_DSI_10nm_PHY_PLL_DSM_DIVIDER, 0x00);
pll_write(base + REG_DSI_10nm_PHY_PLL_FEEDBACK_DIVIDER, 0x4e);
pll_write(base + REG_DSI_10nm_PHY_PLL_CALIBRATION_SETTINGS, 0x40);
pll_write(base + REG_DSI_10nm_PHY_PLL_BAND_SEL_CAL_SETTINGS_THREE,
0xba);
pll_write(base + REG_DSI_10nm_PHY_PLL_FREQ_DETECT_SETTINGS_ONE, 0x0c);
pll_write(base + REG_DSI_10nm_PHY_PLL_OUTDIV, 0x00);
pll_write(base + REG_DSI_10nm_PHY_PLL_CORE_OVERRIDE, 0x00);
pll_write(base + REG_DSI_10nm_PHY_PLL_PLL_DIGITAL_TIMERS_TWO, 0x08);
pll_write(base + REG_DSI_10nm_PHY_PLL_PLL_PROP_GAIN_RATE_1, 0x08);
pll_write(base + REG_DSI_10nm_PHY_PLL_PLL_BAND_SET_RATE_1, 0xc0);
pll_write(base + REG_DSI_10nm_PHY_PLL_PLL_INT_GAIN_IFILT_BAND_1, 0xfa);
pll_write(base + REG_DSI_10nm_PHY_PLL_PLL_FL_INT_GAIN_PFILT_BAND_1,
0x4c);
pll_write(base + REG_DSI_10nm_PHY_PLL_PLL_LOCK_OVERRIDE, 0x80);
pll_write(base + REG_DSI_10nm_PHY_PLL_PFILT, 0x29);
pll_write(base + REG_DSI_10nm_PHY_PLL_IFILT, 0x3f);
}
static void dsi_pll_commit(struct dsi_pll_10nm *pll)
{
void __iomem *base = pll->mmio;
struct dsi_pll_regs *reg = &pll->reg_setup;
pll_write(base + REG_DSI_10nm_PHY_PLL_CORE_INPUT_OVERRIDE, 0x12);
pll_write(base + REG_DSI_10nm_PHY_PLL_DECIMAL_DIV_START_1,
reg->decimal_div_start);
pll_write(base + REG_DSI_10nm_PHY_PLL_FRAC_DIV_START_LOW_1,
reg->frac_div_start_low);
pll_write(base + REG_DSI_10nm_PHY_PLL_FRAC_DIV_START_MID_1,
reg->frac_div_start_mid);
pll_write(base + REG_DSI_10nm_PHY_PLL_FRAC_DIV_START_HIGH_1,
reg->frac_div_start_high);
pll_write(base + REG_DSI_10nm_PHY_PLL_PLL_LOCKDET_RATE_1,
reg->pll_lockdet_rate);
pll_write(base + REG_DSI_10nm_PHY_PLL_PLL_LOCK_DELAY, 0x06);
pll_write(base + REG_DSI_10nm_PHY_PLL_CMODE, 0x10);
pll_write(base + REG_DSI_10nm_PHY_PLL_CLOCK_INVERTERS,
reg->pll_clock_inverters);
}
static int dsi_pll_10nm_vco_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_10nm *pll_10nm = to_pll_10nm(pll);
DBG("DSI PLL%d rate=%lu, parent's=%lu", pll_10nm->id, rate,
parent_rate);
pll_10nm->vco_current_rate = rate;
pll_10nm->vco_ref_clk_rate = VCO_REF_CLK_RATE;
dsi_pll_setup_config(pll_10nm);
dsi_pll_calc_dec_frac(pll_10nm);
dsi_pll_calc_ssc(pll_10nm);
dsi_pll_commit(pll_10nm);
dsi_pll_config_hzindep_reg(pll_10nm);
dsi_pll_ssc_commit(pll_10nm);
/* flush, ensure all register writes are done*/
wmb();
return 0;
}
static int dsi_pll_10nm_lock_status(struct dsi_pll_10nm *pll)
{
struct device *dev = &pll->pdev->dev;
int rc;
u32 status = 0;
u32 const delay_us = 100;
u32 const timeout_us = 5000;
rc = readl_poll_timeout_atomic(pll->mmio +
REG_DSI_10nm_PHY_PLL_COMMON_STATUS_ONE,
status,
((status & BIT(0)) > 0),
delay_us,
timeout_us);
if (rc)
DRM_DEV_ERROR(dev, "DSI PLL(%d) lock failed, status=0x%08x\n",
pll->id, status);
return rc;
}
static void dsi_pll_disable_pll_bias(struct dsi_pll_10nm *pll)
{
u32 data = pll_read(pll->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_CTRL_0);
pll_write(pll->mmio + REG_DSI_10nm_PHY_PLL_SYSTEM_MUXES, 0);
pll_write(pll->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_CTRL_0,
data & ~BIT(5));
ndelay(250);
}
static void dsi_pll_enable_pll_bias(struct dsi_pll_10nm *pll)
{
u32 data = pll_read(pll->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_CTRL_0);
pll_write(pll->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_CTRL_0,
data | BIT(5));
pll_write(pll->mmio + REG_DSI_10nm_PHY_PLL_SYSTEM_MUXES, 0xc0);
ndelay(250);
}
static void dsi_pll_disable_global_clk(struct dsi_pll_10nm *pll)
{
u32 data;
data = pll_read(pll->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_CLK_CFG1);
pll_write(pll->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_CLK_CFG1,
data & ~BIT(5));
}
static void dsi_pll_enable_global_clk(struct dsi_pll_10nm *pll)
{
u32 data;
data = pll_read(pll->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_CLK_CFG1);
pll_write(pll->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_CLK_CFG1,
data | BIT(5));
}
static int dsi_pll_10nm_vco_prepare(struct clk_hw *hw)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_10nm *pll_10nm = to_pll_10nm(pll);
struct device *dev = &pll_10nm->pdev->dev;
int rc;
dsi_pll_enable_pll_bias(pll_10nm);
if (pll_10nm->slave)
dsi_pll_enable_pll_bias(pll_10nm->slave);
rc = dsi_pll_10nm_vco_set_rate(hw,pll_10nm->vco_current_rate, 0);
if (rc) {
DRM_DEV_ERROR(dev, "vco_set_rate failed, rc=%d\n", rc);
return rc;
}
/* Start PLL */
pll_write(pll_10nm->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_PLL_CNTRL,
0x01);
/*
* ensure all PLL configurations are written prior to checking
* for PLL lock.
*/
wmb();
/* Check for PLL lock */
rc = dsi_pll_10nm_lock_status(pll_10nm);
if (rc) {
DRM_DEV_ERROR(dev, "PLL(%d) lock failed\n", pll_10nm->id);
goto error;
}
pll->pll_on = true;
dsi_pll_enable_global_clk(pll_10nm);
if (pll_10nm->slave)
dsi_pll_enable_global_clk(pll_10nm->slave);
pll_write(pll_10nm->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_RBUF_CTRL,
0x01);
if (pll_10nm->slave)
pll_write(pll_10nm->slave->phy_cmn_mmio +
REG_DSI_10nm_PHY_CMN_RBUF_CTRL, 0x01);
error:
return rc;
}
static void dsi_pll_disable_sub(struct dsi_pll_10nm *pll)
{
pll_write(pll->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_RBUF_CTRL, 0);
dsi_pll_disable_pll_bias(pll);
}
static void dsi_pll_10nm_vco_unprepare(struct clk_hw *hw)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_10nm *pll_10nm = to_pll_10nm(pll);
/*
* To avoid any stray glitches while abruptly powering down the PLL
* make sure to gate the clock using the clock enable bit before
* powering down the PLL
*/
dsi_pll_disable_global_clk(pll_10nm);
pll_write(pll_10nm->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_PLL_CNTRL, 0);
dsi_pll_disable_sub(pll_10nm);
if (pll_10nm->slave) {
dsi_pll_disable_global_clk(pll_10nm->slave);
dsi_pll_disable_sub(pll_10nm->slave);
}
/* flush, ensure all register writes are done */
wmb();
pll->pll_on = false;
}
static unsigned long dsi_pll_10nm_vco_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_10nm *pll_10nm = to_pll_10nm(pll);
struct dsi_pll_config *config = &pll_10nm->pll_configuration;
void __iomem *base = pll_10nm->mmio;
u64 ref_clk = pll_10nm->vco_ref_clk_rate;
u64 vco_rate = 0x0;
u64 multiplier;
u32 frac;
u32 dec;
u64 pll_freq, tmp64;
dec = pll_read(base + REG_DSI_10nm_PHY_PLL_DECIMAL_DIV_START_1);
dec &= 0xff;
frac = pll_read(base + REG_DSI_10nm_PHY_PLL_FRAC_DIV_START_LOW_1);
frac |= ((pll_read(base + REG_DSI_10nm_PHY_PLL_FRAC_DIV_START_MID_1) &
0xff) << 8);
frac |= ((pll_read(base + REG_DSI_10nm_PHY_PLL_FRAC_DIV_START_HIGH_1) &
0x3) << 16);
/*
* TODO:
* 1. Assumes prescaler is disabled
*/
multiplier = 1 << config->frac_bits;
pll_freq = dec * (ref_clk * 2);
tmp64 = (ref_clk * 2 * frac);
pll_freq += div_u64(tmp64, multiplier);
vco_rate = pll_freq;
DBG("DSI PLL%d returning vco rate = %lu, dec = %x, frac = %x",
pll_10nm->id, (unsigned long)vco_rate, dec, frac);
return (unsigned long)vco_rate;
}
static const struct clk_ops clk_ops_dsi_pll_10nm_vco = {
.round_rate = msm_dsi_pll_helper_clk_round_rate,
.set_rate = dsi_pll_10nm_vco_set_rate,
.recalc_rate = dsi_pll_10nm_vco_recalc_rate,
.prepare = dsi_pll_10nm_vco_prepare,
.unprepare = dsi_pll_10nm_vco_unprepare,
};
/*
* PLL Callbacks
*/
static void dsi_pll_10nm_save_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_10nm *pll_10nm = to_pll_10nm(pll);
struct pll_10nm_cached_state *cached = &pll_10nm->cached_state;
void __iomem *phy_base = pll_10nm->phy_cmn_mmio;
u32 cmn_clk_cfg0, cmn_clk_cfg1;
cached->pll_out_div = pll_read(pll_10nm->mmio +
REG_DSI_10nm_PHY_PLL_PLL_OUTDIV_RATE);
cached->pll_out_div &= 0x3;
cmn_clk_cfg0 = pll_read(phy_base + REG_DSI_10nm_PHY_CMN_CLK_CFG0);
cached->bit_clk_div = cmn_clk_cfg0 & 0xf;
cached->pix_clk_div = (cmn_clk_cfg0 & 0xf0) >> 4;
cmn_clk_cfg1 = pll_read(phy_base + REG_DSI_10nm_PHY_CMN_CLK_CFG1);
cached->pll_mux = cmn_clk_cfg1 & 0x3;
DBG("DSI PLL%d outdiv %x bit_clk_div %x pix_clk_div %x pll_mux %x",
pll_10nm->id, cached->pll_out_div, cached->bit_clk_div,
cached->pix_clk_div, cached->pll_mux);
}
static int dsi_pll_10nm_restore_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_10nm *pll_10nm = to_pll_10nm(pll);
struct pll_10nm_cached_state *cached = &pll_10nm->cached_state;
void __iomem *phy_base = pll_10nm->phy_cmn_mmio;
u32 val;
int ret;
val = pll_read(pll_10nm->mmio + REG_DSI_10nm_PHY_PLL_PLL_OUTDIV_RATE);
val &= ~0x3;
val |= cached->pll_out_div;
pll_write(pll_10nm->mmio + REG_DSI_10nm_PHY_PLL_PLL_OUTDIV_RATE, val);
pll_write(phy_base + REG_DSI_10nm_PHY_CMN_CLK_CFG0,
cached->bit_clk_div | (cached->pix_clk_div << 4));
val = pll_read(phy_base + REG_DSI_10nm_PHY_CMN_CLK_CFG1);
val &= ~0x3;
val |= cached->pll_mux;
pll_write(phy_base + REG_DSI_10nm_PHY_CMN_CLK_CFG1, val);
ret = dsi_pll_10nm_vco_set_rate(&pll->clk_hw, pll_10nm->vco_current_rate, pll_10nm->vco_ref_clk_rate);
if (ret) {
DRM_DEV_ERROR(&pll_10nm->pdev->dev,
"restore vco rate failed. ret=%d\n", ret);
return ret;
}
DBG("DSI PLL%d", pll_10nm->id);
return 0;
}
static int dsi_pll_10nm_set_usecase(struct msm_dsi_pll *pll,
enum msm_dsi_phy_usecase uc)
{
struct dsi_pll_10nm *pll_10nm = to_pll_10nm(pll);
void __iomem *base = pll_10nm->phy_cmn_mmio;
u32 data = 0x0; /* internal PLL */
DBG("DSI PLL%d", pll_10nm->id);
switch (uc) {
case MSM_DSI_PHY_STANDALONE:
break;
case MSM_DSI_PHY_MASTER:
pll_10nm->slave = pll_10nm_list[(pll_10nm->id + 1) % DSI_MAX];
break;
case MSM_DSI_PHY_SLAVE:
data = 0x1; /* external PLL */
break;
default:
return -EINVAL;
}
/* set PLL src */
pll_write(base + REG_DSI_10nm_PHY_CMN_CLK_CFG1, (data << 2));
pll_10nm->uc = uc;
return 0;
}
static int dsi_pll_10nm_get_provider(struct msm_dsi_pll *pll,
struct clk **byte_clk_provider,
struct clk **pixel_clk_provider)
{
struct dsi_pll_10nm *pll_10nm = to_pll_10nm(pll);
struct clk_hw_onecell_data *hw_data = pll_10nm->hw_data;
DBG("DSI PLL%d", pll_10nm->id);
if (byte_clk_provider)
*byte_clk_provider = hw_data->hws[DSI_BYTE_PLL_CLK]->clk;
if (pixel_clk_provider)
*pixel_clk_provider = hw_data->hws[DSI_PIXEL_PLL_CLK]->clk;
return 0;
}
static void dsi_pll_10nm_destroy(struct msm_dsi_pll *pll)
{
struct dsi_pll_10nm *pll_10nm = to_pll_10nm(pll);
struct device *dev = &pll_10nm->pdev->dev;
DBG("DSI PLL%d", pll_10nm->id);
of_clk_del_provider(dev->of_node);
clk_hw_unregister_divider(pll_10nm->out_dsiclk_hw);
clk_hw_unregister_mux(pll_10nm->pclk_mux_hw);
clk_hw_unregister_fixed_factor(pll_10nm->post_out_div_clk_hw);
clk_hw_unregister_fixed_factor(pll_10nm->by_2_bit_clk_hw);
clk_hw_unregister_fixed_factor(pll_10nm->byte_clk_hw);
clk_hw_unregister_divider(pll_10nm->bit_clk_hw);
clk_hw_unregister_divider(pll_10nm->out_div_clk_hw);
clk_hw_unregister(&pll_10nm->base.clk_hw);
}
/*
* The post dividers and mux clocks are created using the standard divider and
* mux API. Unlike the 14nm PHY, the slave PLL doesn't need its dividers/mux
* state to follow the master PLL's divider/mux state. Therefore, we don't
* require special clock ops that also configure the slave PLL registers
*/
static int pll_10nm_register(struct dsi_pll_10nm *pll_10nm)
{
char clk_name[32], parent[32], vco_name[32];
char parent2[32], parent3[32], parent4[32];
struct clk_init_data vco_init = {
.parent_names = (const char *[]){ "xo" },
.num_parents = 1,
.name = vco_name,
.flags = CLK_IGNORE_UNUSED,
.ops = &clk_ops_dsi_pll_10nm_vco,
};
struct device *dev = &pll_10nm->pdev->dev;
struct clk_hw_onecell_data *hw_data;
struct clk_hw *hw;
int ret;
DBG("DSI%d", pll_10nm->id);
hw_data = devm_kzalloc(dev, sizeof(*hw_data) +
NUM_PROVIDED_CLKS * sizeof(struct clk_hw *),
GFP_KERNEL);
if (!hw_data)
return -ENOMEM;
snprintf(vco_name, 32, "dsi%dvco_clk", pll_10nm->id);
pll_10nm->base.clk_hw.init = &vco_init;
ret = clk_hw_register(dev, &pll_10nm->base.clk_hw);
if (ret)
return ret;
snprintf(clk_name, 32, "dsi%d_pll_out_div_clk", pll_10nm->id);
snprintf(parent, 32, "dsi%dvco_clk", pll_10nm->id);
hw = clk_hw_register_divider(dev, clk_name,
parent, CLK_SET_RATE_PARENT,
pll_10nm->mmio +
REG_DSI_10nm_PHY_PLL_PLL_OUTDIV_RATE,
0, 2, CLK_DIVIDER_POWER_OF_TWO, NULL);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_base_clk_hw;
}
pll_10nm->out_div_clk_hw = hw;
snprintf(clk_name, 32, "dsi%d_pll_bit_clk", pll_10nm->id);
snprintf(parent, 32, "dsi%d_pll_out_div_clk", pll_10nm->id);
/* BIT CLK: DIV_CTRL_3_0 */
hw = clk_hw_register_divider(dev, clk_name, parent,
CLK_SET_RATE_PARENT,
pll_10nm->phy_cmn_mmio +
REG_DSI_10nm_PHY_CMN_CLK_CFG0,
0, 4, CLK_DIVIDER_ONE_BASED,
&pll_10nm->postdiv_lock);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_out_div_clk_hw;
}
pll_10nm->bit_clk_hw = hw;
snprintf(clk_name, 32, "dsi%d_phy_pll_out_byteclk", pll_10nm->id);
snprintf(parent, 32, "dsi%d_pll_bit_clk", pll_10nm->id);
/* DSI Byte clock = VCO_CLK / OUT_DIV / BIT_DIV / 8 */
hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
CLK_SET_RATE_PARENT, 1, 8);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_bit_clk_hw;
}
pll_10nm->byte_clk_hw = hw;
hw_data->hws[DSI_BYTE_PLL_CLK] = hw;
snprintf(clk_name, 32, "dsi%d_pll_by_2_bit_clk", pll_10nm->id);
snprintf(parent, 32, "dsi%d_pll_bit_clk", pll_10nm->id);
hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
0, 1, 2);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_byte_clk_hw;
}
pll_10nm->by_2_bit_clk_hw = hw;
snprintf(clk_name, 32, "dsi%d_pll_post_out_div_clk", pll_10nm->id);
snprintf(parent, 32, "dsi%d_pll_out_div_clk", pll_10nm->id);
hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
0, 1, 4);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_by_2_bit_clk_hw;
}
pll_10nm->post_out_div_clk_hw = hw;
snprintf(clk_name, 32, "dsi%d_pclk_mux", pll_10nm->id);
snprintf(parent, 32, "dsi%d_pll_bit_clk", pll_10nm->id);
snprintf(parent2, 32, "dsi%d_pll_by_2_bit_clk", pll_10nm->id);
snprintf(parent3, 32, "dsi%d_pll_out_div_clk", pll_10nm->id);
snprintf(parent4, 32, "dsi%d_pll_post_out_div_clk", pll_10nm->id);
hw = clk_hw_register_mux(dev, clk_name,
((const char *[]){
parent, parent2, parent3, parent4
}), 4, 0, pll_10nm->phy_cmn_mmio +
REG_DSI_10nm_PHY_CMN_CLK_CFG1,
0, 2, 0, NULL);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_post_out_div_clk_hw;
}
pll_10nm->pclk_mux_hw = hw;
snprintf(clk_name, 32, "dsi%d_phy_pll_out_dsiclk", pll_10nm->id);
snprintf(parent, 32, "dsi%d_pclk_mux", pll_10nm->id);
/* PIX CLK DIV : DIV_CTRL_7_4*/
hw = clk_hw_register_divider(dev, clk_name, parent,
0, pll_10nm->phy_cmn_mmio +
REG_DSI_10nm_PHY_CMN_CLK_CFG0,
4, 4, CLK_DIVIDER_ONE_BASED,
&pll_10nm->postdiv_lock);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_pclk_mux_hw;
}
pll_10nm->out_dsiclk_hw = hw;
hw_data->hws[DSI_PIXEL_PLL_CLK] = hw;
hw_data->num = NUM_PROVIDED_CLKS;
pll_10nm->hw_data = hw_data;
ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
pll_10nm->hw_data);
if (ret) {
DRM_DEV_ERROR(dev, "failed to register clk provider: %d\n", ret);
goto err_dsiclk_hw;
}
return 0;
err_dsiclk_hw:
clk_hw_unregister_divider(pll_10nm->out_dsiclk_hw);
err_pclk_mux_hw:
clk_hw_unregister_mux(pll_10nm->pclk_mux_hw);
err_post_out_div_clk_hw:
clk_hw_unregister_fixed_factor(pll_10nm->post_out_div_clk_hw);
err_by_2_bit_clk_hw:
clk_hw_unregister_fixed_factor(pll_10nm->by_2_bit_clk_hw);
err_byte_clk_hw:
clk_hw_unregister_fixed_factor(pll_10nm->byte_clk_hw);
err_bit_clk_hw:
clk_hw_unregister_divider(pll_10nm->bit_clk_hw);
err_out_div_clk_hw:
clk_hw_unregister_divider(pll_10nm->out_div_clk_hw);
err_base_clk_hw:
clk_hw_unregister(&pll_10nm->base.clk_hw);
return ret;
}
struct msm_dsi_pll *msm_dsi_pll_10nm_init(struct platform_device *pdev, int id)
{
struct dsi_pll_10nm *pll_10nm;
struct msm_dsi_pll *pll;
int ret;
pll_10nm = devm_kzalloc(&pdev->dev, sizeof(*pll_10nm), GFP_KERNEL);
if (!pll_10nm)
return ERR_PTR(-ENOMEM);
DBG("DSI PLL%d", id);
pll_10nm->pdev = pdev;
pll_10nm->id = id;
pll_10nm_list[id] = pll_10nm;
pll_10nm->phy_cmn_mmio = msm_ioremap(pdev, "dsi_phy", "DSI_PHY");
if (IS_ERR_OR_NULL(pll_10nm->phy_cmn_mmio)) {
DRM_DEV_ERROR(&pdev->dev, "failed to map CMN PHY base\n");
return ERR_PTR(-ENOMEM);
}
pll_10nm->mmio = msm_ioremap(pdev, "dsi_pll", "DSI_PLL");
if (IS_ERR_OR_NULL(pll_10nm->mmio)) {
DRM_DEV_ERROR(&pdev->dev, "failed to map PLL base\n");
return ERR_PTR(-ENOMEM);
}
spin_lock_init(&pll_10nm->postdiv_lock);
pll = &pll_10nm->base;
pll->min_rate = 1000000000UL;
pll->max_rate = 3500000000UL;
pll->get_provider = dsi_pll_10nm_get_provider;
pll->destroy = dsi_pll_10nm_destroy;
pll->save_state = dsi_pll_10nm_save_state;
pll->restore_state = dsi_pll_10nm_restore_state;
pll->set_usecase = dsi_pll_10nm_set_usecase;
pll_10nm->vco_delay = 1;
ret = pll_10nm_register(pll_10nm);
if (ret) {
DRM_DEV_ERROR(&pdev->dev, "failed to register PLL: %d\n", ret);
return ERR_PTR(ret);
}
/* TODO: Remove this when we have proper display handover support */
msm_dsi_pll_save_state(pll);
return pll;
}
static int dsi_phy_hw_v3_0_is_pll_on(struct msm_dsi_phy *phy) static int dsi_phy_hw_v3_0_is_pll_on(struct msm_dsi_phy *phy)
{ {
void __iomem *base = phy->base; void __iomem *base = phy->base;
......
...@@ -3,13 +3,1102 @@ ...@@ -3,13 +3,1102 @@
* Copyright (c) 2016, The Linux Foundation. All rights reserved. * Copyright (c) 2016, The Linux Foundation. All rights reserved.
*/ */
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h> #include <linux/delay.h>
#include "dsi_phy.h" #include "dsi_phy.h"
#include "dsi_pll.h"
#include "dsi.xml.h" #include "dsi.xml.h"
#define PHY_14NM_CKLN_IDX 4 #define PHY_14NM_CKLN_IDX 4
/*
* DSI PLL 14nm - clock diagram (eg: DSI0):
*
* dsi0n1_postdiv_clk
* |
* |
* +----+ | +----+
* dsi0vco_clk ---| n1 |--o--| /8 |-- dsi0pllbyte
* +----+ | +----+
* | dsi0n1_postdivby2_clk
* | +----+ |
* o---| /2 |--o--|\
* | +----+ | \ +----+
* | | |--| n2 |-- dsi0pll
* o--------------| / +----+
* |/
*/
#define POLL_MAX_READS 15
#define POLL_TIMEOUT_US 1000
#define NUM_PROVIDED_CLKS 2
#define VCO_REF_CLK_RATE 19200000
#define VCO_MIN_RATE 1300000000UL
#define VCO_MAX_RATE 2600000000UL
#define DSI_BYTE_PLL_CLK 0
#define DSI_PIXEL_PLL_CLK 1
#define DSI_PLL_DEFAULT_VCO_POSTDIV 1
struct dsi_pll_input {
u32 fref; /* reference clk */
u32 fdata; /* bit clock rate */
u32 dsiclk_sel; /* Mux configuration (see diagram) */
u32 ssc_en; /* SSC enable/disable */
u32 ldo_en;
/* fixed params */
u32 refclk_dbler_en;
u32 vco_measure_time;
u32 kvco_measure_time;
u32 bandgap_timer;
u32 pll_wakeup_timer;
u32 plllock_cnt;
u32 plllock_rng;
u32 ssc_center;
u32 ssc_adj_period;
u32 ssc_spread;
u32 ssc_freq;
u32 pll_ie_trim;
u32 pll_ip_trim;
u32 pll_iptat_trim;
u32 pll_cpcset_cur;
u32 pll_cpmset_cur;
u32 pll_icpmset;
u32 pll_icpcset;
u32 pll_icpmset_p;
u32 pll_icpmset_m;
u32 pll_icpcset_p;
u32 pll_icpcset_m;
u32 pll_lpf_res1;
u32 pll_lpf_cap1;
u32 pll_lpf_cap2;
u32 pll_c3ctrl;
u32 pll_r3ctrl;
};
struct dsi_pll_output {
u32 pll_txclk_en;
u32 dec_start;
u32 div_frac_start;
u32 ssc_period;
u32 ssc_step_size;
u32 plllock_cmp;
u32 pll_vco_div_ref;
u32 pll_vco_count;
u32 pll_kvco_div_ref;
u32 pll_kvco_count;
u32 pll_misc1;
u32 pll_lpf2_postdiv;
u32 pll_resetsm_cntrl;
u32 pll_resetsm_cntrl2;
u32 pll_resetsm_cntrl5;
u32 pll_kvco_code;
u32 cmn_clk_cfg0;
u32 cmn_clk_cfg1;
u32 cmn_ldo_cntrl;
u32 pll_postdiv;
u32 fcvo;
};
struct pll_14nm_cached_state {
unsigned long vco_rate;
u8 n2postdiv;
u8 n1postdiv;
};
struct dsi_pll_14nm {
struct msm_dsi_pll base;
int id;
struct platform_device *pdev;
void __iomem *phy_cmn_mmio;
void __iomem *mmio;
int vco_delay;
struct dsi_pll_input in;
struct dsi_pll_output out;
/* protects REG_DSI_14nm_PHY_CMN_CLK_CFG0 register */
spinlock_t postdiv_lock;
u64 vco_current_rate;
u64 vco_ref_clk_rate;
/* private clocks: */
struct clk_hw *hws[NUM_DSI_CLOCKS_MAX];
u32 num_hws;
/* clock-provider: */
struct clk_hw_onecell_data *hw_data;
struct pll_14nm_cached_state cached_state;
enum msm_dsi_phy_usecase uc;
struct dsi_pll_14nm *slave;
};
#define to_pll_14nm(x) container_of(x, struct dsi_pll_14nm, base)
/*
* Private struct for N1/N2 post-divider clocks. These clocks are similar to
* the generic clk_divider class of clocks. The only difference is that it
* also sets the slave DSI PLL's post-dividers if in Dual DSI mode
*/
struct dsi_pll_14nm_postdiv {
struct clk_hw hw;
/* divider params */
u8 shift;
u8 width;
u8 flags; /* same flags as used by clk_divider struct */
struct dsi_pll_14nm *pll;
};
#define to_pll_14nm_postdiv(_hw) container_of(_hw, struct dsi_pll_14nm_postdiv, hw)
/*
* Global list of private DSI PLL struct pointers. We need this for Dual DSI
* mode, where the master PLL's clk_ops needs access the slave's private data
*/
static struct dsi_pll_14nm *pll_14nm_list[DSI_MAX];
static bool pll_14nm_poll_for_ready(struct dsi_pll_14nm *pll_14nm,
u32 nb_tries, u32 timeout_us)
{
bool pll_locked = false;
void __iomem *base = pll_14nm->mmio;
u32 tries, val;
tries = nb_tries;
while (tries--) {
val = pll_read(base +
REG_DSI_14nm_PHY_PLL_RESET_SM_READY_STATUS);
pll_locked = !!(val & BIT(5));
if (pll_locked)
break;
udelay(timeout_us);
}
if (!pll_locked) {
tries = nb_tries;
while (tries--) {
val = pll_read(base +
REG_DSI_14nm_PHY_PLL_RESET_SM_READY_STATUS);
pll_locked = !!(val & BIT(0));
if (pll_locked)
break;
udelay(timeout_us);
}
}
DBG("DSI PLL is %slocked", pll_locked ? "" : "*not* ");
return pll_locked;
}
static void dsi_pll_14nm_input_init(struct dsi_pll_14nm *pll)
{
pll->in.fref = pll->vco_ref_clk_rate;
pll->in.fdata = 0;
pll->in.dsiclk_sel = 1; /* Use the /2 path in Mux */
pll->in.ldo_en = 0; /* disabled for now */
/* fixed input */
pll->in.refclk_dbler_en = 0;
pll->in.vco_measure_time = 5;
pll->in.kvco_measure_time = 5;
pll->in.bandgap_timer = 4;
pll->in.pll_wakeup_timer = 5;
pll->in.plllock_cnt = 1;
pll->in.plllock_rng = 0;
/*
* SSC is enabled by default. We might need DT props for configuring
* some SSC params like PPM and center/down spread etc.
*/
pll->in.ssc_en = 1;
pll->in.ssc_center = 0; /* down spread by default */
pll->in.ssc_spread = 5; /* PPM / 1000 */
pll->in.ssc_freq = 31500; /* default recommended */
pll->in.ssc_adj_period = 37;
pll->in.pll_ie_trim = 4;
pll->in.pll_ip_trim = 4;
pll->in.pll_cpcset_cur = 1;
pll->in.pll_cpmset_cur = 1;
pll->in.pll_icpmset = 4;
pll->in.pll_icpcset = 4;
pll->in.pll_icpmset_p = 0;
pll->in.pll_icpmset_m = 0;
pll->in.pll_icpcset_p = 0;
pll->in.pll_icpcset_m = 0;
pll->in.pll_lpf_res1 = 3;
pll->in.pll_lpf_cap1 = 11;
pll->in.pll_lpf_cap2 = 1;
pll->in.pll_iptat_trim = 7;
pll->in.pll_c3ctrl = 2;
pll->in.pll_r3ctrl = 1;
}
#define CEIL(x, y) (((x) + ((y) - 1)) / (y))
static void pll_14nm_ssc_calc(struct dsi_pll_14nm *pll)
{
u32 period, ssc_period;
u32 ref, rem;
u64 step_size;
DBG("vco=%lld ref=%lld", pll->vco_current_rate, pll->vco_ref_clk_rate);
ssc_period = pll->in.ssc_freq / 500;
period = (u32)pll->vco_ref_clk_rate / 1000;
ssc_period = CEIL(period, ssc_period);
ssc_period -= 1;
pll->out.ssc_period = ssc_period;
DBG("ssc freq=%d spread=%d period=%d", pll->in.ssc_freq,
pll->in.ssc_spread, pll->out.ssc_period);
step_size = (u32)pll->vco_current_rate;
ref = pll->vco_ref_clk_rate;
ref /= 1000;
step_size = div_u64(step_size, ref);
step_size <<= 20;
step_size = div_u64(step_size, 1000);
step_size *= pll->in.ssc_spread;
step_size = div_u64(step_size, 1000);
step_size *= (pll->in.ssc_adj_period + 1);
rem = 0;
step_size = div_u64_rem(step_size, ssc_period + 1, &rem);
if (rem)
step_size++;
DBG("step_size=%lld", step_size);
step_size &= 0x0ffff; /* take lower 16 bits */
pll->out.ssc_step_size = step_size;
}
static void pll_14nm_dec_frac_calc(struct dsi_pll_14nm *pll)
{
struct dsi_pll_input *pin = &pll->in;
struct dsi_pll_output *pout = &pll->out;
u64 multiplier = BIT(20);
u64 dec_start_multiple, dec_start, pll_comp_val;
u32 duration, div_frac_start;
u64 vco_clk_rate = pll->vco_current_rate;
u64 fref = pll->vco_ref_clk_rate;
DBG("vco_clk_rate=%lld ref_clk_rate=%lld", vco_clk_rate, fref);
dec_start_multiple = div_u64(vco_clk_rate * multiplier, fref);
div_u64_rem(dec_start_multiple, multiplier, &div_frac_start);
dec_start = div_u64(dec_start_multiple, multiplier);
pout->dec_start = (u32)dec_start;
pout->div_frac_start = div_frac_start;
if (pin->plllock_cnt == 0)
duration = 1024;
else if (pin->plllock_cnt == 1)
duration = 256;
else if (pin->plllock_cnt == 2)
duration = 128;
else
duration = 32;
pll_comp_val = duration * dec_start_multiple;
pll_comp_val = div_u64(pll_comp_val, multiplier);
do_div(pll_comp_val, 10);
pout->plllock_cmp = (u32)pll_comp_val;
pout->pll_txclk_en = 1;
pout->cmn_ldo_cntrl = 0x3c;
}
static u32 pll_14nm_kvco_slop(u32 vrate)
{
u32 slop = 0;
if (vrate > VCO_MIN_RATE && vrate <= 1800000000UL)
slop = 600;
else if (vrate > 1800000000UL && vrate < 2300000000UL)
slop = 400;
else if (vrate > 2300000000UL && vrate < VCO_MAX_RATE)
slop = 280;
return slop;
}
static void pll_14nm_calc_vco_count(struct dsi_pll_14nm *pll)
{
struct dsi_pll_input *pin = &pll->in;
struct dsi_pll_output *pout = &pll->out;
u64 vco_clk_rate = pll->vco_current_rate;
u64 fref = pll->vco_ref_clk_rate;
u64 data;
u32 cnt;
data = fref * pin->vco_measure_time;
do_div(data, 1000000);
data &= 0x03ff; /* 10 bits */
data -= 2;
pout->pll_vco_div_ref = data;
data = div_u64(vco_clk_rate, 1000000); /* unit is Mhz */
data *= pin->vco_measure_time;
do_div(data, 10);
pout->pll_vco_count = data;
data = fref * pin->kvco_measure_time;
do_div(data, 1000000);
data &= 0x03ff; /* 10 bits */
data -= 1;
pout->pll_kvco_div_ref = data;
cnt = pll_14nm_kvco_slop(vco_clk_rate);
cnt *= 2;
cnt /= 100;
cnt *= pin->kvco_measure_time;
pout->pll_kvco_count = cnt;
pout->pll_misc1 = 16;
pout->pll_resetsm_cntrl = 48;
pout->pll_resetsm_cntrl2 = pin->bandgap_timer << 3;
pout->pll_resetsm_cntrl5 = pin->pll_wakeup_timer;
pout->pll_kvco_code = 0;
}
static void pll_db_commit_ssc(struct dsi_pll_14nm *pll)
{
void __iomem *base = pll->mmio;
struct dsi_pll_input *pin = &pll->in;
struct dsi_pll_output *pout = &pll->out;
u8 data;
data = pin->ssc_adj_period;
data &= 0x0ff;
pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_ADJ_PER1, data);
data = (pin->ssc_adj_period >> 8);
data &= 0x03;
pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_ADJ_PER2, data);
data = pout->ssc_period;
data &= 0x0ff;
pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_PER1, data);
data = (pout->ssc_period >> 8);
data &= 0x0ff;
pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_PER2, data);
data = pout->ssc_step_size;
data &= 0x0ff;
pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_STEP_SIZE1, data);
data = (pout->ssc_step_size >> 8);
data &= 0x0ff;
pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_STEP_SIZE2, data);
data = (pin->ssc_center & 0x01);
data <<= 1;
data |= 0x01; /* enable */
pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_EN_CENTER, data);
wmb(); /* make sure register committed */
}
static void pll_db_commit_common(struct dsi_pll_14nm *pll,
struct dsi_pll_input *pin,
struct dsi_pll_output *pout)
{
void __iomem *base = pll->mmio;
u8 data;
/* confgiure the non frequency dependent pll registers */
data = 0;
pll_write(base + REG_DSI_14nm_PHY_PLL_SYSCLK_EN_RESET, data);
data = pout->pll_txclk_en;
pll_write(base + REG_DSI_14nm_PHY_PLL_TXCLK_EN, data);
data = pout->pll_resetsm_cntrl;
pll_write(base + REG_DSI_14nm_PHY_PLL_RESETSM_CNTRL, data);
data = pout->pll_resetsm_cntrl2;
pll_write(base + REG_DSI_14nm_PHY_PLL_RESETSM_CNTRL2, data);
data = pout->pll_resetsm_cntrl5;
pll_write(base + REG_DSI_14nm_PHY_PLL_RESETSM_CNTRL5, data);
data = pout->pll_vco_div_ref & 0xff;
pll_write(base + REG_DSI_14nm_PHY_PLL_VCO_DIV_REF1, data);
data = (pout->pll_vco_div_ref >> 8) & 0x3;
pll_write(base + REG_DSI_14nm_PHY_PLL_VCO_DIV_REF2, data);
data = pout->pll_kvco_div_ref & 0xff;
pll_write(base + REG_DSI_14nm_PHY_PLL_KVCO_DIV_REF1, data);
data = (pout->pll_kvco_div_ref >> 8) & 0x3;
pll_write(base + REG_DSI_14nm_PHY_PLL_KVCO_DIV_REF2, data);
data = pout->pll_misc1;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_MISC1, data);
data = pin->pll_ie_trim;
pll_write(base + REG_DSI_14nm_PHY_PLL_IE_TRIM, data);
data = pin->pll_ip_trim;
pll_write(base + REG_DSI_14nm_PHY_PLL_IP_TRIM, data);
data = pin->pll_cpmset_cur << 3 | pin->pll_cpcset_cur;
pll_write(base + REG_DSI_14nm_PHY_PLL_CP_SET_CUR, data);
data = pin->pll_icpcset_p << 3 | pin->pll_icpcset_m;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_ICPCSET, data);
data = pin->pll_icpmset_p << 3 | pin->pll_icpcset_m;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_ICPMSET, data);
data = pin->pll_icpmset << 3 | pin->pll_icpcset;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_ICP_SET, data);
data = pin->pll_lpf_cap2 << 4 | pin->pll_lpf_cap1;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_LPF1, data);
data = pin->pll_iptat_trim;
pll_write(base + REG_DSI_14nm_PHY_PLL_IPTAT_TRIM, data);
data = pin->pll_c3ctrl | pin->pll_r3ctrl << 4;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_CRCTRL, data);
}
static void pll_14nm_software_reset(struct dsi_pll_14nm *pll_14nm)
{
void __iomem *cmn_base = pll_14nm->phy_cmn_mmio;
/* de assert pll start and apply pll sw reset */
/* stop pll */
pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_PLL_CNTRL, 0);
/* pll sw reset */
pll_write_udelay(cmn_base + REG_DSI_14nm_PHY_CMN_CTRL_1, 0x20, 10);
wmb(); /* make sure register committed */
pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_CTRL_1, 0);
wmb(); /* make sure register committed */
}
static void pll_db_commit_14nm(struct dsi_pll_14nm *pll,
struct dsi_pll_input *pin,
struct dsi_pll_output *pout)
{
void __iomem *base = pll->mmio;
void __iomem *cmn_base = pll->phy_cmn_mmio;
u8 data;
DBG("DSI%d PLL", pll->id);
data = pout->cmn_ldo_cntrl;
pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_LDO_CNTRL, data);
pll_db_commit_common(pll, pin, pout);
pll_14nm_software_reset(pll);
data = pin->dsiclk_sel; /* set dsiclk_sel = 1 */
pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_CLK_CFG1, data);
data = 0xff; /* data, clk, pll normal operation */
pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_CTRL_0, data);
/* configure the frequency dependent pll registers */
data = pout->dec_start;
pll_write(base + REG_DSI_14nm_PHY_PLL_DEC_START, data);
data = pout->div_frac_start & 0xff;
pll_write(base + REG_DSI_14nm_PHY_PLL_DIV_FRAC_START1, data);
data = (pout->div_frac_start >> 8) & 0xff;
pll_write(base + REG_DSI_14nm_PHY_PLL_DIV_FRAC_START2, data);
data = (pout->div_frac_start >> 16) & 0xf;
pll_write(base + REG_DSI_14nm_PHY_PLL_DIV_FRAC_START3, data);
data = pout->plllock_cmp & 0xff;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLLLOCK_CMP1, data);
data = (pout->plllock_cmp >> 8) & 0xff;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLLLOCK_CMP2, data);
data = (pout->plllock_cmp >> 16) & 0x3;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLLLOCK_CMP3, data);
data = pin->plllock_cnt << 1 | pin->plllock_rng << 3;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLLLOCK_CMP_EN, data);
data = pout->pll_vco_count & 0xff;
pll_write(base + REG_DSI_14nm_PHY_PLL_VCO_COUNT1, data);
data = (pout->pll_vco_count >> 8) & 0xff;
pll_write(base + REG_DSI_14nm_PHY_PLL_VCO_COUNT2, data);
data = pout->pll_kvco_count & 0xff;
pll_write(base + REG_DSI_14nm_PHY_PLL_KVCO_COUNT1, data);
data = (pout->pll_kvco_count >> 8) & 0x3;
pll_write(base + REG_DSI_14nm_PHY_PLL_KVCO_COUNT2, data);
data = (pout->pll_postdiv - 1) << 4 | pin->pll_lpf_res1;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_LPF2_POSTDIV, data);
if (pin->ssc_en)
pll_db_commit_ssc(pll);
wmb(); /* make sure register committed */
}
/*
* VCO clock Callbacks
*/
static int dsi_pll_14nm_vco_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll);
struct dsi_pll_input *pin = &pll_14nm->in;
struct dsi_pll_output *pout = &pll_14nm->out;
DBG("DSI PLL%d rate=%lu, parent's=%lu", pll_14nm->id, rate,
parent_rate);
pll_14nm->vco_current_rate = rate;
pll_14nm->vco_ref_clk_rate = VCO_REF_CLK_RATE;
dsi_pll_14nm_input_init(pll_14nm);
/*
* This configures the post divider internal to the VCO. It's
* fixed to divide by 1 for now.
*
* tx_band = pll_postdiv.
* 0: divided by 1
* 1: divided by 2
* 2: divided by 4
* 3: divided by 8
*/
pout->pll_postdiv = DSI_PLL_DEFAULT_VCO_POSTDIV;
pll_14nm_dec_frac_calc(pll_14nm);
if (pin->ssc_en)
pll_14nm_ssc_calc(pll_14nm);
pll_14nm_calc_vco_count(pll_14nm);
/* commit the slave DSI PLL registers if we're master. Note that we
* don't lock the slave PLL. We just ensure that the PLL/PHY registers
* of the master and slave are identical
*/
if (pll_14nm->uc == MSM_DSI_PHY_MASTER) {
struct dsi_pll_14nm *pll_14nm_slave = pll_14nm->slave;
pll_db_commit_14nm(pll_14nm_slave, pin, pout);
}
pll_db_commit_14nm(pll_14nm, pin, pout);
return 0;
}
static unsigned long dsi_pll_14nm_vco_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll);
void __iomem *base = pll_14nm->mmio;
u64 vco_rate, multiplier = BIT(20);
u32 div_frac_start;
u32 dec_start;
u64 ref_clk = parent_rate;
dec_start = pll_read(base + REG_DSI_14nm_PHY_PLL_DEC_START);
dec_start &= 0x0ff;
DBG("dec_start = %x", dec_start);
div_frac_start = (pll_read(base + REG_DSI_14nm_PHY_PLL_DIV_FRAC_START3)
& 0xf) << 16;
div_frac_start |= (pll_read(base + REG_DSI_14nm_PHY_PLL_DIV_FRAC_START2)
& 0xff) << 8;
div_frac_start |= pll_read(base + REG_DSI_14nm_PHY_PLL_DIV_FRAC_START1)
& 0xff;
DBG("div_frac_start = %x", div_frac_start);
vco_rate = ref_clk * dec_start;
vco_rate += ((ref_clk * div_frac_start) / multiplier);
/*
* Recalculating the rate from dec_start and frac_start doesn't end up
* the rate we originally set. Convert the freq to KHz, round it up and
* convert it back to MHz.
*/
vco_rate = DIV_ROUND_UP_ULL(vco_rate, 1000) * 1000;
DBG("returning vco rate = %lu", (unsigned long)vco_rate);
return (unsigned long)vco_rate;
}
static const struct clk_ops clk_ops_dsi_pll_14nm_vco = {
.round_rate = msm_dsi_pll_helper_clk_round_rate,
.set_rate = dsi_pll_14nm_vco_set_rate,
.recalc_rate = dsi_pll_14nm_vco_recalc_rate,
.prepare = msm_dsi_pll_helper_clk_prepare,
.unprepare = msm_dsi_pll_helper_clk_unprepare,
};
/*
* N1 and N2 post-divider clock callbacks
*/
#define div_mask(width) ((1 << (width)) - 1)
static unsigned long dsi_pll_14nm_postdiv_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct dsi_pll_14nm_postdiv *postdiv = to_pll_14nm_postdiv(hw);
struct dsi_pll_14nm *pll_14nm = postdiv->pll;
void __iomem *base = pll_14nm->phy_cmn_mmio;
u8 shift = postdiv->shift;
u8 width = postdiv->width;
u32 val;
DBG("DSI%d PLL parent rate=%lu", pll_14nm->id, parent_rate);
val = pll_read(base + REG_DSI_14nm_PHY_CMN_CLK_CFG0) >> shift;
val &= div_mask(width);
return divider_recalc_rate(hw, parent_rate, val, NULL,
postdiv->flags, width);
}
static long dsi_pll_14nm_postdiv_round_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long *prate)
{
struct dsi_pll_14nm_postdiv *postdiv = to_pll_14nm_postdiv(hw);
struct dsi_pll_14nm *pll_14nm = postdiv->pll;
DBG("DSI%d PLL parent rate=%lu", pll_14nm->id, rate);
return divider_round_rate(hw, rate, prate, NULL,
postdiv->width,
postdiv->flags);
}
static int dsi_pll_14nm_postdiv_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct dsi_pll_14nm_postdiv *postdiv = to_pll_14nm_postdiv(hw);
struct dsi_pll_14nm *pll_14nm = postdiv->pll;
void __iomem *base = pll_14nm->phy_cmn_mmio;
spinlock_t *lock = &pll_14nm->postdiv_lock;
u8 shift = postdiv->shift;
u8 width = postdiv->width;
unsigned int value;
unsigned long flags = 0;
u32 val;
DBG("DSI%d PLL parent rate=%lu parent rate %lu", pll_14nm->id, rate,
parent_rate);
value = divider_get_val(rate, parent_rate, NULL, postdiv->width,
postdiv->flags);
spin_lock_irqsave(lock, flags);
val = pll_read(base + REG_DSI_14nm_PHY_CMN_CLK_CFG0);
val &= ~(div_mask(width) << shift);
val |= value << shift;
pll_write(base + REG_DSI_14nm_PHY_CMN_CLK_CFG0, val);
/* If we're master in dual DSI mode, then the slave PLL's post-dividers
* follow the master's post dividers
*/
if (pll_14nm->uc == MSM_DSI_PHY_MASTER) {
struct dsi_pll_14nm *pll_14nm_slave = pll_14nm->slave;
void __iomem *slave_base = pll_14nm_slave->phy_cmn_mmio;
pll_write(slave_base + REG_DSI_14nm_PHY_CMN_CLK_CFG0, val);
}
spin_unlock_irqrestore(lock, flags);
return 0;
}
static const struct clk_ops clk_ops_dsi_pll_14nm_postdiv = {
.recalc_rate = dsi_pll_14nm_postdiv_recalc_rate,
.round_rate = dsi_pll_14nm_postdiv_round_rate,
.set_rate = dsi_pll_14nm_postdiv_set_rate,
};
/*
* PLL Callbacks
*/
static int dsi_pll_14nm_enable_seq(struct msm_dsi_pll *pll)
{
struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll);
void __iomem *base = pll_14nm->mmio;
void __iomem *cmn_base = pll_14nm->phy_cmn_mmio;
bool locked;
DBG("");
pll_write(base + REG_DSI_14nm_PHY_PLL_VREF_CFG1, 0x10);
pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_PLL_CNTRL, 1);
locked = pll_14nm_poll_for_ready(pll_14nm, POLL_MAX_READS,
POLL_TIMEOUT_US);
if (unlikely(!locked))
DRM_DEV_ERROR(&pll_14nm->pdev->dev, "DSI PLL lock failed\n");
else
DBG("DSI PLL lock success");
return locked ? 0 : -EINVAL;
}
static void dsi_pll_14nm_disable_seq(struct msm_dsi_pll *pll)
{
struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll);
void __iomem *cmn_base = pll_14nm->phy_cmn_mmio;
DBG("");
pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_PLL_CNTRL, 0);
}
static void dsi_pll_14nm_save_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll);
struct pll_14nm_cached_state *cached_state = &pll_14nm->cached_state;
void __iomem *cmn_base = pll_14nm->phy_cmn_mmio;
u32 data;
data = pll_read(cmn_base + REG_DSI_14nm_PHY_CMN_CLK_CFG0);
cached_state->n1postdiv = data & 0xf;
cached_state->n2postdiv = (data >> 4) & 0xf;
DBG("DSI%d PLL save state %x %x", pll_14nm->id,
cached_state->n1postdiv, cached_state->n2postdiv);
cached_state->vco_rate = clk_hw_get_rate(&pll->clk_hw);
}
static int dsi_pll_14nm_restore_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll);
struct pll_14nm_cached_state *cached_state = &pll_14nm->cached_state;
void __iomem *cmn_base = pll_14nm->phy_cmn_mmio;
u32 data;
int ret;
ret = dsi_pll_14nm_vco_set_rate(&pll->clk_hw,
cached_state->vco_rate, 0);
if (ret) {
DRM_DEV_ERROR(&pll_14nm->pdev->dev,
"restore vco rate failed. ret=%d\n", ret);
return ret;
}
data = cached_state->n1postdiv | (cached_state->n2postdiv << 4);
DBG("DSI%d PLL restore state %x %x", pll_14nm->id,
cached_state->n1postdiv, cached_state->n2postdiv);
pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_CLK_CFG0, data);
/* also restore post-dividers for slave DSI PLL */
if (pll_14nm->uc == MSM_DSI_PHY_MASTER) {
struct dsi_pll_14nm *pll_14nm_slave = pll_14nm->slave;
void __iomem *slave_base = pll_14nm_slave->phy_cmn_mmio;
pll_write(slave_base + REG_DSI_14nm_PHY_CMN_CLK_CFG0, data);
}
return 0;
}
static int dsi_pll_14nm_set_usecase(struct msm_dsi_pll *pll,
enum msm_dsi_phy_usecase uc)
{
struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll);
void __iomem *base = pll_14nm->mmio;
u32 clkbuflr_en, bandgap = 0;
switch (uc) {
case MSM_DSI_PHY_STANDALONE:
clkbuflr_en = 0x1;
break;
case MSM_DSI_PHY_MASTER:
clkbuflr_en = 0x3;
pll_14nm->slave = pll_14nm_list[(pll_14nm->id + 1) % DSI_MAX];
break;
case MSM_DSI_PHY_SLAVE:
clkbuflr_en = 0x0;
bandgap = 0x3;
break;
default:
return -EINVAL;
}
pll_write(base + REG_DSI_14nm_PHY_PLL_CLKBUFLR_EN, clkbuflr_en);
if (bandgap)
pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_BANDGAP, bandgap);
pll_14nm->uc = uc;
return 0;
}
static int dsi_pll_14nm_get_provider(struct msm_dsi_pll *pll,
struct clk **byte_clk_provider,
struct clk **pixel_clk_provider)
{
struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll);
struct clk_hw_onecell_data *hw_data = pll_14nm->hw_data;
if (byte_clk_provider)
*byte_clk_provider = hw_data->hws[DSI_BYTE_PLL_CLK]->clk;
if (pixel_clk_provider)
*pixel_clk_provider = hw_data->hws[DSI_PIXEL_PLL_CLK]->clk;
return 0;
}
static void dsi_pll_14nm_destroy(struct msm_dsi_pll *pll)
{
struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll);
struct platform_device *pdev = pll_14nm->pdev;
int num_hws = pll_14nm->num_hws;
of_clk_del_provider(pdev->dev.of_node);
while (num_hws--)
clk_hw_unregister(pll_14nm->hws[num_hws]);
}
static struct clk_hw *pll_14nm_postdiv_register(struct dsi_pll_14nm *pll_14nm,
const char *name,
const char *parent_name,
unsigned long flags,
u8 shift)
{
struct dsi_pll_14nm_postdiv *pll_postdiv;
struct device *dev = &pll_14nm->pdev->dev;
struct clk_init_data postdiv_init = {
.parent_names = (const char *[]) { parent_name },
.num_parents = 1,
.name = name,
.flags = flags,
.ops = &clk_ops_dsi_pll_14nm_postdiv,
};
int ret;
pll_postdiv = devm_kzalloc(dev, sizeof(*pll_postdiv), GFP_KERNEL);
if (!pll_postdiv)
return ERR_PTR(-ENOMEM);
pll_postdiv->pll = pll_14nm;
pll_postdiv->shift = shift;
/* both N1 and N2 postdividers are 4 bits wide */
pll_postdiv->width = 4;
/* range of each divider is from 1 to 15 */
pll_postdiv->flags = CLK_DIVIDER_ONE_BASED;
pll_postdiv->hw.init = &postdiv_init;
ret = clk_hw_register(dev, &pll_postdiv->hw);
if (ret)
return ERR_PTR(ret);
return &pll_postdiv->hw;
}
static int pll_14nm_register(struct dsi_pll_14nm *pll_14nm)
{
char clk_name[32], parent[32], vco_name[32];
struct clk_init_data vco_init = {
.parent_names = (const char *[]){ "xo" },
.num_parents = 1,
.name = vco_name,
.flags = CLK_IGNORE_UNUSED,
.ops = &clk_ops_dsi_pll_14nm_vco,
};
struct device *dev = &pll_14nm->pdev->dev;
struct clk_hw **hws = pll_14nm->hws;
struct clk_hw_onecell_data *hw_data;
struct clk_hw *hw;
int num = 0;
int ret;
DBG("DSI%d", pll_14nm->id);
hw_data = devm_kzalloc(dev, sizeof(*hw_data) +
NUM_PROVIDED_CLKS * sizeof(struct clk_hw *),
GFP_KERNEL);
if (!hw_data)
return -ENOMEM;
snprintf(vco_name, 32, "dsi%dvco_clk", pll_14nm->id);
pll_14nm->base.clk_hw.init = &vco_init;
ret = clk_hw_register(dev, &pll_14nm->base.clk_hw);
if (ret)
return ret;
hws[num++] = &pll_14nm->base.clk_hw;
snprintf(clk_name, 32, "dsi%dn1_postdiv_clk", pll_14nm->id);
snprintf(parent, 32, "dsi%dvco_clk", pll_14nm->id);
/* N1 postdiv, bits 0-3 in REG_DSI_14nm_PHY_CMN_CLK_CFG0 */
hw = pll_14nm_postdiv_register(pll_14nm, clk_name, parent,
CLK_SET_RATE_PARENT, 0);
if (IS_ERR(hw))
return PTR_ERR(hw);
hws[num++] = hw;
snprintf(clk_name, 32, "dsi%dpllbyte", pll_14nm->id);
snprintf(parent, 32, "dsi%dn1_postdiv_clk", pll_14nm->id);
/* DSI Byte clock = VCO_CLK / N1 / 8 */
hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
CLK_SET_RATE_PARENT, 1, 8);
if (IS_ERR(hw))
return PTR_ERR(hw);
hws[num++] = hw;
hw_data->hws[DSI_BYTE_PLL_CLK] = hw;
snprintf(clk_name, 32, "dsi%dn1_postdivby2_clk", pll_14nm->id);
snprintf(parent, 32, "dsi%dn1_postdiv_clk", pll_14nm->id);
/*
* Skip the mux for now, force DSICLK_SEL to 1, Add a /2 divider
* on the way. Don't let it set parent.
*/
hw = clk_hw_register_fixed_factor(dev, clk_name, parent, 0, 1, 2);
if (IS_ERR(hw))
return PTR_ERR(hw);
hws[num++] = hw;
snprintf(clk_name, 32, "dsi%dpll", pll_14nm->id);
snprintf(parent, 32, "dsi%dn1_postdivby2_clk", pll_14nm->id);
/* DSI pixel clock = VCO_CLK / N1 / 2 / N2
* This is the output of N2 post-divider, bits 4-7 in
* REG_DSI_14nm_PHY_CMN_CLK_CFG0. Don't let it set parent.
*/
hw = pll_14nm_postdiv_register(pll_14nm, clk_name, parent, 0, 4);
if (IS_ERR(hw))
return PTR_ERR(hw);
hws[num++] = hw;
hw_data->hws[DSI_PIXEL_PLL_CLK] = hw;
pll_14nm->num_hws = num;
hw_data->num = NUM_PROVIDED_CLKS;
pll_14nm->hw_data = hw_data;
ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
pll_14nm->hw_data);
if (ret) {
DRM_DEV_ERROR(dev, "failed to register clk provider: %d\n", ret);
return ret;
}
return 0;
}
struct msm_dsi_pll *msm_dsi_pll_14nm_init(struct platform_device *pdev, int id)
{
struct dsi_pll_14nm *pll_14nm;
struct msm_dsi_pll *pll;
int ret;
if (!pdev)
return ERR_PTR(-ENODEV);
pll_14nm = devm_kzalloc(&pdev->dev, sizeof(*pll_14nm), GFP_KERNEL);
if (!pll_14nm)
return ERR_PTR(-ENOMEM);
DBG("PLL%d", id);
pll_14nm->pdev = pdev;
pll_14nm->id = id;
pll_14nm_list[id] = pll_14nm;
pll_14nm->phy_cmn_mmio = msm_ioremap(pdev, "dsi_phy", "DSI_PHY");
if (IS_ERR_OR_NULL(pll_14nm->phy_cmn_mmio)) {
DRM_DEV_ERROR(&pdev->dev, "failed to map CMN PHY base\n");
return ERR_PTR(-ENOMEM);
}
pll_14nm->mmio = msm_ioremap(pdev, "dsi_pll", "DSI_PLL");
if (IS_ERR_OR_NULL(pll_14nm->mmio)) {
DRM_DEV_ERROR(&pdev->dev, "failed to map PLL base\n");
return ERR_PTR(-ENOMEM);
}
spin_lock_init(&pll_14nm->postdiv_lock);
pll = &pll_14nm->base;
pll->min_rate = VCO_MIN_RATE;
pll->max_rate = VCO_MAX_RATE;
pll->get_provider = dsi_pll_14nm_get_provider;
pll->destroy = dsi_pll_14nm_destroy;
pll->disable_seq = dsi_pll_14nm_disable_seq;
pll->save_state = dsi_pll_14nm_save_state;
pll->restore_state = dsi_pll_14nm_restore_state;
pll->set_usecase = dsi_pll_14nm_set_usecase;
pll_14nm->vco_delay = 1;
pll->en_seq_cnt = 1;
pll->enable_seqs[0] = dsi_pll_14nm_enable_seq;
ret = pll_14nm_register(pll_14nm);
if (ret) {
DRM_DEV_ERROR(&pdev->dev, "failed to register PLL: %d\n", ret);
return ERR_PTR(ret);
}
return pll;
}
static void dsi_14nm_dphy_set_timing(struct msm_dsi_phy *phy, static void dsi_14nm_dphy_set_timing(struct msm_dsi_phy *phy,
struct msm_dsi_dphy_timing *timing, struct msm_dsi_dphy_timing *timing,
int lane_idx) int lane_idx)
......
...@@ -3,9 +3,646 @@ ...@@ -3,9 +3,646 @@
* Copyright (c) 2015, The Linux Foundation. All rights reserved. * Copyright (c) 2015, The Linux Foundation. All rights reserved.
*/ */
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include "dsi_phy.h" #include "dsi_phy.h"
#include "dsi_pll.h"
#include "dsi.xml.h" #include "dsi.xml.h"
/*
* DSI PLL 28nm - clock diagram (eg: DSI0):
*
* dsi0analog_postdiv_clk
* | dsi0indirect_path_div2_clk
* | |
* +------+ | +----+ | |\ dsi0byte_mux
* dsi0vco_clk --o--| DIV1 |--o--| /2 |--o--| \ |
* | +------+ +----+ | m| | +----+
* | | u|--o--| /4 |-- dsi0pllbyte
* | | x| +----+
* o--------------------------| /
* | |/
* | +------+
* o----------| DIV3 |------------------------- dsi0pll
* +------+
*/
#define POLL_MAX_READS 10
#define POLL_TIMEOUT_US 50
#define NUM_PROVIDED_CLKS 2
#define VCO_REF_CLK_RATE 19200000
#define VCO_MIN_RATE 350000000
#define VCO_MAX_RATE 750000000
#define DSI_BYTE_PLL_CLK 0
#define DSI_PIXEL_PLL_CLK 1
#define LPFR_LUT_SIZE 10
struct lpfr_cfg {
unsigned long vco_rate;
u32 resistance;
};
/* Loop filter resistance: */
static const struct lpfr_cfg lpfr_lut[LPFR_LUT_SIZE] = {
{ 479500000, 8 },
{ 480000000, 11 },
{ 575500000, 8 },
{ 576000000, 12 },
{ 610500000, 8 },
{ 659500000, 9 },
{ 671500000, 10 },
{ 672000000, 14 },
{ 708500000, 10 },
{ 750000000, 11 },
};
struct pll_28nm_cached_state {
unsigned long vco_rate;
u8 postdiv3;
u8 postdiv1;
u8 byte_mux;
};
struct dsi_pll_28nm {
struct msm_dsi_pll base;
int id;
struct platform_device *pdev;
void __iomem *mmio;
int vco_delay;
/* private clocks: */
struct clk *clks[NUM_DSI_CLOCKS_MAX];
u32 num_clks;
/* clock-provider: */
struct clk *provided_clks[NUM_PROVIDED_CLKS];
struct clk_onecell_data clk_data;
struct pll_28nm_cached_state cached_state;
};
#define to_pll_28nm(x) container_of(x, struct dsi_pll_28nm, base)
static bool pll_28nm_poll_for_ready(struct dsi_pll_28nm *pll_28nm,
u32 nb_tries, u32 timeout_us)
{
bool pll_locked = false;
u32 val;
while (nb_tries--) {
val = pll_read(pll_28nm->mmio + REG_DSI_28nm_PHY_PLL_STATUS);
pll_locked = !!(val & DSI_28nm_PHY_PLL_STATUS_PLL_RDY);
if (pll_locked)
break;
udelay(timeout_us);
}
DBG("DSI PLL is %slocked", pll_locked ? "" : "*not* ");
return pll_locked;
}
static void pll_28nm_software_reset(struct dsi_pll_28nm *pll_28nm)
{
void __iomem *base = pll_28nm->mmio;
/*
* Add HW recommended delays after toggling the software
* reset bit off and back on.
*/
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_TEST_CFG,
DSI_28nm_PHY_PLL_TEST_CFG_PLL_SW_RESET, 1);
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_TEST_CFG, 0x00, 1);
}
/*
* Clock Callbacks
*/
static int dsi_pll_28nm_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
struct device *dev = &pll_28nm->pdev->dev;
void __iomem *base = pll_28nm->mmio;
unsigned long div_fbx1000, gen_vco_clk;
u32 refclk_cfg, frac_n_mode, frac_n_value;
u32 sdm_cfg0, sdm_cfg1, sdm_cfg2, sdm_cfg3;
u32 cal_cfg10, cal_cfg11;
u32 rem;
int i;
VERB("rate=%lu, parent's=%lu", rate, parent_rate);
/* Force postdiv2 to be div-4 */
pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV2_CFG, 3);
/* Configure the Loop filter resistance */
for (i = 0; i < LPFR_LUT_SIZE; i++)
if (rate <= lpfr_lut[i].vco_rate)
break;
if (i == LPFR_LUT_SIZE) {
DRM_DEV_ERROR(dev, "unable to get loop filter resistance. vco=%lu\n",
rate);
return -EINVAL;
}
pll_write(base + REG_DSI_28nm_PHY_PLL_LPFR_CFG, lpfr_lut[i].resistance);
/* Loop filter capacitance values : c1 and c2 */
pll_write(base + REG_DSI_28nm_PHY_PLL_LPFC1_CFG, 0x70);
pll_write(base + REG_DSI_28nm_PHY_PLL_LPFC2_CFG, 0x15);
rem = rate % VCO_REF_CLK_RATE;
if (rem) {
refclk_cfg = DSI_28nm_PHY_PLL_REFCLK_CFG_DBLR;
frac_n_mode = 1;
div_fbx1000 = rate / (VCO_REF_CLK_RATE / 500);
gen_vco_clk = div_fbx1000 * (VCO_REF_CLK_RATE / 500);
} else {
refclk_cfg = 0x0;
frac_n_mode = 0;
div_fbx1000 = rate / (VCO_REF_CLK_RATE / 1000);
gen_vco_clk = div_fbx1000 * (VCO_REF_CLK_RATE / 1000);
}
DBG("refclk_cfg = %d", refclk_cfg);
rem = div_fbx1000 % 1000;
frac_n_value = (rem << 16) / 1000;
DBG("div_fb = %lu", div_fbx1000);
DBG("frac_n_value = %d", frac_n_value);
DBG("Generated VCO Clock: %lu", gen_vco_clk);
rem = 0;
sdm_cfg1 = pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG1);
sdm_cfg1 &= ~DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET__MASK;
if (frac_n_mode) {
sdm_cfg0 = 0x0;
sdm_cfg0 |= DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV(0);
sdm_cfg1 |= DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET(
(u32)(((div_fbx1000 / 1000) & 0x3f) - 1));
sdm_cfg3 = frac_n_value >> 8;
sdm_cfg2 = frac_n_value & 0xff;
} else {
sdm_cfg0 = DSI_28nm_PHY_PLL_SDM_CFG0_BYP;
sdm_cfg0 |= DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV(
(u32)(((div_fbx1000 / 1000) & 0x3f) - 1));
sdm_cfg1 |= DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET(0);
sdm_cfg2 = 0;
sdm_cfg3 = 0;
}
DBG("sdm_cfg0=%d", sdm_cfg0);
DBG("sdm_cfg1=%d", sdm_cfg1);
DBG("sdm_cfg2=%d", sdm_cfg2);
DBG("sdm_cfg3=%d", sdm_cfg3);
cal_cfg11 = (u32)(gen_vco_clk / (256 * 1000000));
cal_cfg10 = (u32)((gen_vco_clk % (256 * 1000000)) / 1000000);
DBG("cal_cfg10=%d, cal_cfg11=%d", cal_cfg10, cal_cfg11);
pll_write(base + REG_DSI_28nm_PHY_PLL_CHGPUMP_CFG, 0x02);
pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG3, 0x2b);
pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG4, 0x06);
pll_write(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x0d);
pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG1, sdm_cfg1);
pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG2,
DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0(sdm_cfg2));
pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG3,
DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8(sdm_cfg3));
pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG4, 0x00);
/* Add hardware recommended delay for correct PLL configuration */
if (pll_28nm->vco_delay)
udelay(pll_28nm->vco_delay);
pll_write(base + REG_DSI_28nm_PHY_PLL_REFCLK_CFG, refclk_cfg);
pll_write(base + REG_DSI_28nm_PHY_PLL_PWRGEN_CFG, 0x00);
pll_write(base + REG_DSI_28nm_PHY_PLL_VCOLPF_CFG, 0x31);
pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG0, sdm_cfg0);
pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG0, 0x12);
pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG6, 0x30);
pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG7, 0x00);
pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG8, 0x60);
pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG9, 0x00);
pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG10, cal_cfg10 & 0xff);
pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG11, cal_cfg11 & 0xff);
pll_write(base + REG_DSI_28nm_PHY_PLL_EFUSE_CFG, 0x20);
return 0;
}
static int dsi_pll_28nm_clk_is_enabled(struct clk_hw *hw)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
return pll_28nm_poll_for_ready(pll_28nm, POLL_MAX_READS,
POLL_TIMEOUT_US);
}
static unsigned long dsi_pll_28nm_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
void __iomem *base = pll_28nm->mmio;
u32 sdm0, doubler, sdm_byp_div;
u32 sdm_dc_off, sdm_freq_seed, sdm2, sdm3;
u32 ref_clk = VCO_REF_CLK_RATE;
unsigned long vco_rate;
VERB("parent_rate=%lu", parent_rate);
/* Check to see if the ref clk doubler is enabled */
doubler = pll_read(base + REG_DSI_28nm_PHY_PLL_REFCLK_CFG) &
DSI_28nm_PHY_PLL_REFCLK_CFG_DBLR;
ref_clk += (doubler * VCO_REF_CLK_RATE);
/* see if it is integer mode or sdm mode */
sdm0 = pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG0);
if (sdm0 & DSI_28nm_PHY_PLL_SDM_CFG0_BYP) {
/* integer mode */
sdm_byp_div = FIELD(
pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG0),
DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV) + 1;
vco_rate = ref_clk * sdm_byp_div;
} else {
/* sdm mode */
sdm_dc_off = FIELD(
pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG1),
DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET);
DBG("sdm_dc_off = %d", sdm_dc_off);
sdm2 = FIELD(pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG2),
DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0);
sdm3 = FIELD(pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG3),
DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8);
sdm_freq_seed = (sdm3 << 8) | sdm2;
DBG("sdm_freq_seed = %d", sdm_freq_seed);
vco_rate = (ref_clk * (sdm_dc_off + 1)) +
mult_frac(ref_clk, sdm_freq_seed, BIT(16));
DBG("vco rate = %lu", vco_rate);
}
DBG("returning vco rate = %lu", vco_rate);
return vco_rate;
}
static const struct clk_ops clk_ops_dsi_pll_28nm_vco = {
.round_rate = msm_dsi_pll_helper_clk_round_rate,
.set_rate = dsi_pll_28nm_clk_set_rate,
.recalc_rate = dsi_pll_28nm_clk_recalc_rate,
.prepare = msm_dsi_pll_helper_clk_prepare,
.unprepare = msm_dsi_pll_helper_clk_unprepare,
.is_enabled = dsi_pll_28nm_clk_is_enabled,
};
/*
* PLL Callbacks
*/
static int dsi_pll_28nm_enable_seq_hpm(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
struct device *dev = &pll_28nm->pdev->dev;
void __iomem *base = pll_28nm->mmio;
u32 max_reads = 5, timeout_us = 100;
bool locked;
u32 val;
int i;
DBG("id=%d", pll_28nm->id);
pll_28nm_software_reset(pll_28nm);
/*
* PLL power up sequence.
* Add necessary delays recommended by hardware.
*/
val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 1);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 200);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 600);
for (i = 0; i < 2; i++) {
/* DSI Uniphy lock detect setting */
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2,
0x0c, 100);
pll_write(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x0d);
/* poll for PLL ready status */
locked = pll_28nm_poll_for_ready(pll_28nm,
max_reads, timeout_us);
if (locked)
break;
pll_28nm_software_reset(pll_28nm);
/*
* PLL power up sequence.
* Add necessary delays recommended by hardware.
*/
val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 1);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 200);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 250);
val &= ~DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 200);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 600);
}
if (unlikely(!locked))
DRM_DEV_ERROR(dev, "DSI PLL lock failed\n");
else
DBG("DSI PLL Lock success");
return locked ? 0 : -EINVAL;
}
static int dsi_pll_28nm_enable_seq_lp(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
struct device *dev = &pll_28nm->pdev->dev;
void __iomem *base = pll_28nm->mmio;
bool locked;
u32 max_reads = 10, timeout_us = 50;
u32 val;
DBG("id=%d", pll_28nm->id);
pll_28nm_software_reset(pll_28nm);
/*
* PLL power up sequence.
* Add necessary delays recommended by hardware.
*/
pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_CAL_CFG1, 0x34, 500);
val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B;
pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B;
pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B |
DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE;
pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500);
/* DSI PLL toggle lock detect setting */
pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x04, 500);
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x05, 512);
locked = pll_28nm_poll_for_ready(pll_28nm, max_reads, timeout_us);
if (unlikely(!locked))
DRM_DEV_ERROR(dev, "DSI PLL lock failed\n");
else
DBG("DSI PLL lock success");
return locked ? 0 : -EINVAL;
}
static void dsi_pll_28nm_disable_seq(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
DBG("id=%d", pll_28nm->id);
pll_write(pll_28nm->mmio + REG_DSI_28nm_PHY_PLL_GLB_CFG, 0x00);
}
static void dsi_pll_28nm_save_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state;
void __iomem *base = pll_28nm->mmio;
cached_state->postdiv3 =
pll_read(base + REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG);
cached_state->postdiv1 =
pll_read(base + REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG);
cached_state->byte_mux = pll_read(base + REG_DSI_28nm_PHY_PLL_VREG_CFG);
if (dsi_pll_28nm_clk_is_enabled(&pll->clk_hw))
cached_state->vco_rate = clk_hw_get_rate(&pll->clk_hw);
else
cached_state->vco_rate = 0;
}
static int dsi_pll_28nm_restore_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state;
void __iomem *base = pll_28nm->mmio;
int ret;
ret = dsi_pll_28nm_clk_set_rate(&pll->clk_hw,
cached_state->vco_rate, 0);
if (ret) {
DRM_DEV_ERROR(&pll_28nm->pdev->dev,
"restore vco rate failed. ret=%d\n", ret);
return ret;
}
pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG,
cached_state->postdiv3);
pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG,
cached_state->postdiv1);
pll_write(base + REG_DSI_28nm_PHY_PLL_VREG_CFG,
cached_state->byte_mux);
return 0;
}
static int dsi_pll_28nm_get_provider(struct msm_dsi_pll *pll,
struct clk **byte_clk_provider,
struct clk **pixel_clk_provider)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
if (byte_clk_provider)
*byte_clk_provider = pll_28nm->provided_clks[DSI_BYTE_PLL_CLK];
if (pixel_clk_provider)
*pixel_clk_provider =
pll_28nm->provided_clks[DSI_PIXEL_PLL_CLK];
return 0;
}
static void dsi_pll_28nm_destroy(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
int i;
msm_dsi_pll_helper_unregister_clks(pll_28nm->pdev,
pll_28nm->clks, pll_28nm->num_clks);
for (i = 0; i < NUM_PROVIDED_CLKS; i++)
pll_28nm->provided_clks[i] = NULL;
pll_28nm->num_clks = 0;
pll_28nm->clk_data.clks = NULL;
pll_28nm->clk_data.clk_num = 0;
}
static int pll_28nm_register(struct dsi_pll_28nm *pll_28nm)
{
char clk_name[32], parent1[32], parent2[32], vco_name[32];
struct clk_init_data vco_init = {
.parent_names = (const char *[]){ "xo" },
.num_parents = 1,
.name = vco_name,
.flags = CLK_IGNORE_UNUSED,
.ops = &clk_ops_dsi_pll_28nm_vco,
};
struct device *dev = &pll_28nm->pdev->dev;
struct clk **clks = pll_28nm->clks;
struct clk **provided_clks = pll_28nm->provided_clks;
int num = 0;
int ret;
DBG("%d", pll_28nm->id);
snprintf(vco_name, 32, "dsi%dvco_clk", pll_28nm->id);
pll_28nm->base.clk_hw.init = &vco_init;
clks[num++] = clk_register(dev, &pll_28nm->base.clk_hw);
snprintf(clk_name, 32, "dsi%danalog_postdiv_clk", pll_28nm->id);
snprintf(parent1, 32, "dsi%dvco_clk", pll_28nm->id);
clks[num++] = clk_register_divider(dev, clk_name,
parent1, CLK_SET_RATE_PARENT,
pll_28nm->mmio +
REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG,
0, 4, 0, NULL);
snprintf(clk_name, 32, "dsi%dindirect_path_div2_clk", pll_28nm->id);
snprintf(parent1, 32, "dsi%danalog_postdiv_clk", pll_28nm->id);
clks[num++] = clk_register_fixed_factor(dev, clk_name,
parent1, CLK_SET_RATE_PARENT,
1, 2);
snprintf(clk_name, 32, "dsi%dpll", pll_28nm->id);
snprintf(parent1, 32, "dsi%dvco_clk", pll_28nm->id);
clks[num++] = provided_clks[DSI_PIXEL_PLL_CLK] =
clk_register_divider(dev, clk_name,
parent1, 0, pll_28nm->mmio +
REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG,
0, 8, 0, NULL);
snprintf(clk_name, 32, "dsi%dbyte_mux", pll_28nm->id);
snprintf(parent1, 32, "dsi%dvco_clk", pll_28nm->id);
snprintf(parent2, 32, "dsi%dindirect_path_div2_clk", pll_28nm->id);
clks[num++] = clk_register_mux(dev, clk_name,
((const char *[]){
parent1, parent2
}), 2, CLK_SET_RATE_PARENT, pll_28nm->mmio +
REG_DSI_28nm_PHY_PLL_VREG_CFG, 1, 1, 0, NULL);
snprintf(clk_name, 32, "dsi%dpllbyte", pll_28nm->id);
snprintf(parent1, 32, "dsi%dbyte_mux", pll_28nm->id);
clks[num++] = provided_clks[DSI_BYTE_PLL_CLK] =
clk_register_fixed_factor(dev, clk_name,
parent1, CLK_SET_RATE_PARENT, 1, 4);
pll_28nm->num_clks = num;
pll_28nm->clk_data.clk_num = NUM_PROVIDED_CLKS;
pll_28nm->clk_data.clks = provided_clks;
ret = of_clk_add_provider(dev->of_node,
of_clk_src_onecell_get, &pll_28nm->clk_data);
if (ret) {
DRM_DEV_ERROR(dev, "failed to register clk provider: %d\n", ret);
return ret;
}
return 0;
}
struct msm_dsi_pll *msm_dsi_pll_28nm_init(struct platform_device *pdev,
enum msm_dsi_phy_type type, int id)
{
struct dsi_pll_28nm *pll_28nm;
struct msm_dsi_pll *pll;
int ret;
if (!pdev)
return ERR_PTR(-ENODEV);
pll_28nm = devm_kzalloc(&pdev->dev, sizeof(*pll_28nm), GFP_KERNEL);
if (!pll_28nm)
return ERR_PTR(-ENOMEM);
pll_28nm->pdev = pdev;
pll_28nm->id = id;
pll_28nm->mmio = msm_ioremap(pdev, "dsi_pll", "DSI_PLL");
if (IS_ERR_OR_NULL(pll_28nm->mmio)) {
DRM_DEV_ERROR(&pdev->dev, "%s: failed to map pll base\n", __func__);
return ERR_PTR(-ENOMEM);
}
pll = &pll_28nm->base;
pll->min_rate = VCO_MIN_RATE;
pll->max_rate = VCO_MAX_RATE;
pll->get_provider = dsi_pll_28nm_get_provider;
pll->destroy = dsi_pll_28nm_destroy;
pll->disable_seq = dsi_pll_28nm_disable_seq;
pll->save_state = dsi_pll_28nm_save_state;
pll->restore_state = dsi_pll_28nm_restore_state;
if (type == MSM_DSI_PHY_28NM_HPM) {
pll_28nm->vco_delay = 1;
pll->en_seq_cnt = 3;
pll->enable_seqs[0] = dsi_pll_28nm_enable_seq_hpm;
pll->enable_seqs[1] = dsi_pll_28nm_enable_seq_hpm;
pll->enable_seqs[2] = dsi_pll_28nm_enable_seq_hpm;
} else if (type == MSM_DSI_PHY_28NM_LP) {
pll_28nm->vco_delay = 1000;
pll->en_seq_cnt = 1;
pll->enable_seqs[0] = dsi_pll_28nm_enable_seq_lp;
} else {
DRM_DEV_ERROR(&pdev->dev, "phy type (%d) is not 28nm\n", type);
return ERR_PTR(-EINVAL);
}
ret = pll_28nm_register(pll_28nm);
if (ret) {
DRM_DEV_ERROR(&pdev->dev, "failed to register PLL: %d\n", ret);
return ERR_PTR(ret);
}
return pll;
}
static void dsi_28nm_dphy_set_timing(struct msm_dsi_phy *phy, static void dsi_28nm_dphy_set_timing(struct msm_dsi_phy *phy,
struct msm_dsi_dphy_timing *timing) struct msm_dsi_dphy_timing *timing)
{ {
......
...@@ -3,11 +3,530 @@ ...@@ -3,11 +3,530 @@
* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
*/ */
#include <linux/clk-provider.h>
#include <linux/delay.h> #include <linux/delay.h>
#include "dsi_phy.h" #include "dsi_phy.h"
#include "dsi_pll.h"
#include "dsi.xml.h" #include "dsi.xml.h"
/*
* DSI PLL 28nm (8960/A family) - clock diagram (eg: DSI1):
*
*
* +------+
* dsi1vco_clk ----o-----| DIV1 |---dsi1pllbit (not exposed as clock)
* F * byte_clk | +------+
* | bit clock divider (F / 8)
* |
* | +------+
* o-----| DIV2 |---dsi0pllbyte---o---> To byte RCG
* | +------+ | (sets parent rate)
* | byte clock divider (F) |
* | |
* | o---> To esc RCG
* | (doesn't set parent rate)
* |
* | +------+
* o-----| DIV3 |----dsi0pll------o---> To dsi RCG
* +------+ | (sets parent rate)
* dsi clock divider (F * magic) |
* |
* o---> To pixel rcg
* (doesn't set parent rate)
*/
#define POLL_MAX_READS 8000
#define POLL_TIMEOUT_US 1
#define NUM_PROVIDED_CLKS 2
#define VCO_REF_CLK_RATE 27000000
#define VCO_MIN_RATE 600000000
#define VCO_MAX_RATE 1200000000
#define DSI_BYTE_PLL_CLK 0
#define DSI_PIXEL_PLL_CLK 1
#define VCO_PREF_DIV_RATIO 27
struct pll_28nm_cached_state {
unsigned long vco_rate;
u8 postdiv3;
u8 postdiv2;
u8 postdiv1;
};
struct clk_bytediv {
struct clk_hw hw;
void __iomem *reg;
};
struct dsi_pll_28nm {
struct msm_dsi_pll base;
int id;
struct platform_device *pdev;
void __iomem *mmio;
/* custom byte clock divider */
struct clk_bytediv *bytediv;
/* private clocks: */
struct clk *clks[NUM_DSI_CLOCKS_MAX];
u32 num_clks;
/* clock-provider: */
struct clk *provided_clks[NUM_PROVIDED_CLKS];
struct clk_onecell_data clk_data;
struct pll_28nm_cached_state cached_state;
};
#define to_pll_28nm(x) container_of(x, struct dsi_pll_28nm, base)
static bool pll_28nm_poll_for_ready(struct dsi_pll_28nm *pll_28nm,
int nb_tries, int timeout_us)
{
bool pll_locked = false;
u32 val;
while (nb_tries--) {
val = pll_read(pll_28nm->mmio + REG_DSI_28nm_8960_PHY_PLL_RDY);
pll_locked = !!(val & DSI_28nm_8960_PHY_PLL_RDY_PLL_RDY);
if (pll_locked)
break;
udelay(timeout_us);
}
DBG("DSI PLL is %slocked", pll_locked ? "" : "*not* ");
return pll_locked;
}
/*
* Clock Callbacks
*/
static int dsi_pll_28nm_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
void __iomem *base = pll_28nm->mmio;
u32 val, temp, fb_divider;
DBG("rate=%lu, parent's=%lu", rate, parent_rate);
temp = rate / 10;
val = VCO_REF_CLK_RATE / 10;
fb_divider = (temp * VCO_PREF_DIV_RATIO) / val;
fb_divider = fb_divider / 2 - 1;
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_1,
fb_divider & 0xff);
val = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_2);
val |= (fb_divider >> 8) & 0x07;
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_2,
val);
val = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_3);
val |= (VCO_PREF_DIV_RATIO - 1) & 0x3f;
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_3,
val);
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_6,
0xf);
val = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8);
val |= 0x7 << 4;
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8,
val);
return 0;
}
static int dsi_pll_28nm_clk_is_enabled(struct clk_hw *hw)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
return pll_28nm_poll_for_ready(pll_28nm, POLL_MAX_READS,
POLL_TIMEOUT_US);
}
static unsigned long dsi_pll_28nm_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
void __iomem *base = pll_28nm->mmio;
unsigned long vco_rate;
u32 status, fb_divider, temp, ref_divider;
VERB("parent_rate=%lu", parent_rate);
status = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_0);
if (status & DSI_28nm_8960_PHY_PLL_CTRL_0_ENABLE) {
fb_divider = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_1);
fb_divider &= 0xff;
temp = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_2) & 0x07;
fb_divider = (temp << 8) | fb_divider;
fb_divider += 1;
ref_divider = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_3);
ref_divider &= 0x3f;
ref_divider += 1;
/* multiply by 2 */
vco_rate = (parent_rate / ref_divider) * fb_divider * 2;
} else {
vco_rate = 0;
}
DBG("returning vco rate = %lu", vco_rate);
return vco_rate;
}
static const struct clk_ops clk_ops_dsi_pll_28nm_vco = {
.round_rate = msm_dsi_pll_helper_clk_round_rate,
.set_rate = dsi_pll_28nm_clk_set_rate,
.recalc_rate = dsi_pll_28nm_clk_recalc_rate,
.prepare = msm_dsi_pll_helper_clk_prepare,
.unprepare = msm_dsi_pll_helper_clk_unprepare,
.is_enabled = dsi_pll_28nm_clk_is_enabled,
};
/*
* Custom byte clock divier clk_ops
*
* This clock is the entry point to configuring the PLL. The user (dsi host)
* will set this clock's rate to the desired byte clock rate. The VCO lock
* frequency is a multiple of the byte clock rate. The multiplication factor
* (shown as F in the diagram above) is a function of the byte clock rate.
*
* This custom divider clock ensures that its parent (VCO) is set to the
* desired rate, and that the byte clock postdivider (POSTDIV2) is configured
* accordingly
*/
#define to_clk_bytediv(_hw) container_of(_hw, struct clk_bytediv, hw)
static unsigned long clk_bytediv_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_bytediv *bytediv = to_clk_bytediv(hw);
unsigned int div;
div = pll_read(bytediv->reg) & 0xff;
return parent_rate / (div + 1);
}
/* find multiplication factor(wrt byte clock) at which the VCO should be set */
static unsigned int get_vco_mul_factor(unsigned long byte_clk_rate)
{
unsigned long bit_mhz;
/* convert to bit clock in Mhz */
bit_mhz = (byte_clk_rate * 8) / 1000000;
if (bit_mhz < 125)
return 64;
else if (bit_mhz < 250)
return 32;
else if (bit_mhz < 600)
return 16;
else
return 8;
}
static long clk_bytediv_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
unsigned long best_parent;
unsigned int factor;
factor = get_vco_mul_factor(rate);
best_parent = rate * factor;
*prate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent);
return *prate / factor;
}
static int clk_bytediv_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_bytediv *bytediv = to_clk_bytediv(hw);
u32 val;
unsigned int factor;
factor = get_vco_mul_factor(rate);
val = pll_read(bytediv->reg);
val |= (factor - 1) & 0xff;
pll_write(bytediv->reg, val);
return 0;
}
/* Our special byte clock divider ops */
static const struct clk_ops clk_bytediv_ops = {
.round_rate = clk_bytediv_round_rate,
.set_rate = clk_bytediv_set_rate,
.recalc_rate = clk_bytediv_recalc_rate,
};
/*
* PLL Callbacks
*/
static int dsi_pll_28nm_enable_seq(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
struct device *dev = &pll_28nm->pdev->dev;
void __iomem *base = pll_28nm->mmio;
bool locked;
unsigned int bit_div, byte_div;
int max_reads = 1000, timeout_us = 100;
u32 val;
DBG("id=%d", pll_28nm->id);
/*
* before enabling the PLL, configure the bit clock divider since we
* don't expose it as a clock to the outside world
* 1: read back the byte clock divider that should already be set
* 2: divide by 8 to get bit clock divider
* 3: write it to POSTDIV1
*/
val = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_9);
byte_div = val + 1;
bit_div = byte_div / 8;
val = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8);
val &= ~0xf;
val |= (bit_div - 1);
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8, val);
/* enable the PLL */
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_0,
DSI_28nm_8960_PHY_PLL_CTRL_0_ENABLE);
locked = pll_28nm_poll_for_ready(pll_28nm, max_reads, timeout_us);
if (unlikely(!locked))
DRM_DEV_ERROR(dev, "DSI PLL lock failed\n");
else
DBG("DSI PLL lock success");
return locked ? 0 : -EINVAL;
}
static void dsi_pll_28nm_disable_seq(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
DBG("id=%d", pll_28nm->id);
pll_write(pll_28nm->mmio + REG_DSI_28nm_8960_PHY_PLL_CTRL_0, 0x00);
}
static void dsi_pll_28nm_save_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state;
void __iomem *base = pll_28nm->mmio;
cached_state->postdiv3 =
pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_10);
cached_state->postdiv2 =
pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_9);
cached_state->postdiv1 =
pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8);
cached_state->vco_rate = clk_hw_get_rate(&pll->clk_hw);
}
static int dsi_pll_28nm_restore_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state;
void __iomem *base = pll_28nm->mmio;
int ret;
ret = dsi_pll_28nm_clk_set_rate(&pll->clk_hw,
cached_state->vco_rate, 0);
if (ret) {
DRM_DEV_ERROR(&pll_28nm->pdev->dev,
"restore vco rate failed. ret=%d\n", ret);
return ret;
}
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_10,
cached_state->postdiv3);
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_9,
cached_state->postdiv2);
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8,
cached_state->postdiv1);
return 0;
}
static int dsi_pll_28nm_get_provider(struct msm_dsi_pll *pll,
struct clk **byte_clk_provider,
struct clk **pixel_clk_provider)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
if (byte_clk_provider)
*byte_clk_provider = pll_28nm->provided_clks[DSI_BYTE_PLL_CLK];
if (pixel_clk_provider)
*pixel_clk_provider =
pll_28nm->provided_clks[DSI_PIXEL_PLL_CLK];
return 0;
}
static void dsi_pll_28nm_destroy(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
msm_dsi_pll_helper_unregister_clks(pll_28nm->pdev,
pll_28nm->clks, pll_28nm->num_clks);
}
static int pll_28nm_register(struct dsi_pll_28nm *pll_28nm)
{
char *clk_name, *parent_name, *vco_name;
struct clk_init_data vco_init = {
.parent_names = (const char *[]){ "pxo" },
.num_parents = 1,
.flags = CLK_IGNORE_UNUSED,
.ops = &clk_ops_dsi_pll_28nm_vco,
};
struct device *dev = &pll_28nm->pdev->dev;
struct clk **clks = pll_28nm->clks;
struct clk **provided_clks = pll_28nm->provided_clks;
struct clk_bytediv *bytediv;
struct clk_init_data bytediv_init = { };
int ret, num = 0;
DBG("%d", pll_28nm->id);
bytediv = devm_kzalloc(dev, sizeof(*bytediv), GFP_KERNEL);
if (!bytediv)
return -ENOMEM;
vco_name = devm_kzalloc(dev, 32, GFP_KERNEL);
if (!vco_name)
return -ENOMEM;
parent_name = devm_kzalloc(dev, 32, GFP_KERNEL);
if (!parent_name)
return -ENOMEM;
clk_name = devm_kzalloc(dev, 32, GFP_KERNEL);
if (!clk_name)
return -ENOMEM;
pll_28nm->bytediv = bytediv;
snprintf(vco_name, 32, "dsi%dvco_clk", pll_28nm->id);
vco_init.name = vco_name;
pll_28nm->base.clk_hw.init = &vco_init;
clks[num++] = clk_register(dev, &pll_28nm->base.clk_hw);
/* prepare and register bytediv */
bytediv->hw.init = &bytediv_init;
bytediv->reg = pll_28nm->mmio + REG_DSI_28nm_8960_PHY_PLL_CTRL_9;
snprintf(parent_name, 32, "dsi%dvco_clk", pll_28nm->id);
snprintf(clk_name, 32, "dsi%dpllbyte", pll_28nm->id);
bytediv_init.name = clk_name;
bytediv_init.ops = &clk_bytediv_ops;
bytediv_init.flags = CLK_SET_RATE_PARENT;
bytediv_init.parent_names = (const char * const *) &parent_name;
bytediv_init.num_parents = 1;
/* DIV2 */
clks[num++] = provided_clks[DSI_BYTE_PLL_CLK] =
clk_register(dev, &bytediv->hw);
snprintf(clk_name, 32, "dsi%dpll", pll_28nm->id);
/* DIV3 */
clks[num++] = provided_clks[DSI_PIXEL_PLL_CLK] =
clk_register_divider(dev, clk_name,
parent_name, 0, pll_28nm->mmio +
REG_DSI_28nm_8960_PHY_PLL_CTRL_10,
0, 8, 0, NULL);
pll_28nm->num_clks = num;
pll_28nm->clk_data.clk_num = NUM_PROVIDED_CLKS;
pll_28nm->clk_data.clks = provided_clks;
ret = of_clk_add_provider(dev->of_node,
of_clk_src_onecell_get, &pll_28nm->clk_data);
if (ret) {
DRM_DEV_ERROR(dev, "failed to register clk provider: %d\n", ret);
return ret;
}
return 0;
}
struct msm_dsi_pll *msm_dsi_pll_28nm_8960_init(struct platform_device *pdev,
int id)
{
struct dsi_pll_28nm *pll_28nm;
struct msm_dsi_pll *pll;
int ret;
if (!pdev)
return ERR_PTR(-ENODEV);
pll_28nm = devm_kzalloc(&pdev->dev, sizeof(*pll_28nm), GFP_KERNEL);
if (!pll_28nm)
return ERR_PTR(-ENOMEM);
pll_28nm->pdev = pdev;
pll_28nm->id = id + 1;
pll_28nm->mmio = msm_ioremap(pdev, "dsi_pll", "DSI_PLL");
if (IS_ERR_OR_NULL(pll_28nm->mmio)) {
DRM_DEV_ERROR(&pdev->dev, "%s: failed to map pll base\n", __func__);
return ERR_PTR(-ENOMEM);
}
pll = &pll_28nm->base;
pll->min_rate = VCO_MIN_RATE;
pll->max_rate = VCO_MAX_RATE;
pll->get_provider = dsi_pll_28nm_get_provider;
pll->destroy = dsi_pll_28nm_destroy;
pll->disable_seq = dsi_pll_28nm_disable_seq;
pll->save_state = dsi_pll_28nm_save_state;
pll->restore_state = dsi_pll_28nm_restore_state;
pll->en_seq_cnt = 1;
pll->enable_seqs[0] = dsi_pll_28nm_enable_seq;
ret = pll_28nm_register(pll_28nm);
if (ret) {
DRM_DEV_ERROR(&pdev->dev, "failed to register PLL: %d\n", ret);
return ERR_PTR(ret);
}
return pll;
}
static void dsi_28nm_dphy_set_timing(struct msm_dsi_phy *phy, static void dsi_28nm_dphy_set_timing(struct msm_dsi_phy *phy,
struct msm_dsi_dphy_timing *timing) struct msm_dsi_dphy_timing *timing)
{ {
......
...@@ -3,11 +3,916 @@ ...@@ -3,11 +3,916 @@
* Copyright (c) 2018, The Linux Foundation * Copyright (c) 2018, The Linux Foundation
*/ */
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include "dsi_pll.h"
#include "dsi_phy.h" #include "dsi_phy.h"
#include "dsi.xml.h" #include "dsi.xml.h"
/*
* DSI PLL 7nm - clock diagram (eg: DSI0): TODO: updated CPHY diagram
*
* dsi0_pll_out_div_clk dsi0_pll_bit_clk
* | |
* | |
* +---------+ | +----------+ | +----+
* dsi0vco_clk ---| out_div |--o--| divl_3_0 |--o--| /8 |-- dsi0_phy_pll_out_byteclk
* +---------+ | +----------+ | +----+
* | |
* | | dsi0_pll_by_2_bit_clk
* | | |
* | | +----+ | |\ dsi0_pclk_mux
* | |--| /2 |--o--| \ |
* | | +----+ | \ | +---------+
* | --------------| |--o--| div_7_4 |-- dsi0_phy_pll_out_dsiclk
* |------------------------------| / +---------+
* | +-----+ | /
* -----------| /4? |--o----------|/
* +-----+ | |
* | |dsiclk_sel
* |
* dsi0_pll_post_out_div_clk
*/
#define DSI_BYTE_PLL_CLK 0
#define DSI_PIXEL_PLL_CLK 1
#define NUM_PROVIDED_CLKS 2
#define VCO_REF_CLK_RATE 19200000
struct dsi_pll_regs {
u32 pll_prop_gain_rate;
u32 pll_lockdet_rate;
u32 decimal_div_start;
u32 frac_div_start_low;
u32 frac_div_start_mid;
u32 frac_div_start_high;
u32 pll_clock_inverters;
u32 ssc_stepsize_low;
u32 ssc_stepsize_high;
u32 ssc_div_per_low;
u32 ssc_div_per_high;
u32 ssc_adjper_low;
u32 ssc_adjper_high;
u32 ssc_control;
};
struct dsi_pll_config {
u32 ref_freq;
bool div_override;
u32 output_div;
bool ignore_frac;
bool disable_prescaler;
bool enable_ssc;
bool ssc_center;
u32 dec_bits;
u32 frac_bits;
u32 lock_timer;
u32 ssc_freq;
u32 ssc_offset;
u32 ssc_adj_per;
u32 thresh_cycles;
u32 refclk_cycles;
};
struct pll_7nm_cached_state {
unsigned long vco_rate;
u8 bit_clk_div;
u8 pix_clk_div;
u8 pll_out_div;
u8 pll_mux;
};
struct dsi_pll_7nm {
struct msm_dsi_pll base;
int id;
struct platform_device *pdev;
void __iomem *phy_cmn_mmio;
void __iomem *mmio;
u64 vco_ref_clk_rate;
u64 vco_current_rate;
/* protects REG_DSI_7nm_PHY_CMN_CLK_CFG0 register */
spinlock_t postdiv_lock;
int vco_delay;
struct dsi_pll_config pll_configuration;
struct dsi_pll_regs reg_setup;
/* private clocks: */
struct clk_hw *out_div_clk_hw;
struct clk_hw *bit_clk_hw;
struct clk_hw *byte_clk_hw;
struct clk_hw *by_2_bit_clk_hw;
struct clk_hw *post_out_div_clk_hw;
struct clk_hw *pclk_mux_hw;
struct clk_hw *out_dsiclk_hw;
/* clock-provider: */
struct clk_hw_onecell_data *hw_data;
struct pll_7nm_cached_state cached_state;
enum msm_dsi_phy_usecase uc;
struct dsi_pll_7nm *slave;
};
#define to_pll_7nm(x) container_of(x, struct dsi_pll_7nm, base)
/*
* Global list of private DSI PLL struct pointers. We need this for Dual DSI
* mode, where the master PLL's clk_ops needs access the slave's private data
*/
static struct dsi_pll_7nm *pll_7nm_list[DSI_MAX];
static void dsi_pll_setup_config(struct dsi_pll_7nm *pll)
{
struct dsi_pll_config *config = &pll->pll_configuration;
config->ref_freq = pll->vco_ref_clk_rate;
config->output_div = 1;
config->dec_bits = 8;
config->frac_bits = 18;
config->lock_timer = 64;
config->ssc_freq = 31500;
config->ssc_offset = 4800;
config->ssc_adj_per = 2;
config->thresh_cycles = 32;
config->refclk_cycles = 256;
config->div_override = false;
config->ignore_frac = false;
config->disable_prescaler = false;
/* TODO: ssc enable */
config->enable_ssc = false;
config->ssc_center = 0;
}
static void dsi_pll_calc_dec_frac(struct dsi_pll_7nm *pll)
{
struct dsi_pll_config *config = &pll->pll_configuration;
struct dsi_pll_regs *regs = &pll->reg_setup;
u64 fref = pll->vco_ref_clk_rate;
u64 pll_freq;
u64 divider;
u64 dec, dec_multiple;
u32 frac;
u64 multiplier;
pll_freq = pll->vco_current_rate;
if (config->disable_prescaler)
divider = fref;
else
divider = fref * 2;
multiplier = 1 << config->frac_bits;
dec_multiple = div_u64(pll_freq * multiplier, divider);
div_u64_rem(dec_multiple, multiplier, &frac);
dec = div_u64(dec_multiple, multiplier);
if (pll->base.type != MSM_DSI_PHY_7NM_V4_1)
regs->pll_clock_inverters = 0x28;
else if (pll_freq <= 1000000000ULL)
regs->pll_clock_inverters = 0xa0;
else if (pll_freq <= 2500000000ULL)
regs->pll_clock_inverters = 0x20;
else if (pll_freq <= 3020000000ULL)
regs->pll_clock_inverters = 0x00;
else
regs->pll_clock_inverters = 0x40;
regs->pll_lockdet_rate = config->lock_timer;
regs->decimal_div_start = dec;
regs->frac_div_start_low = (frac & 0xff);
regs->frac_div_start_mid = (frac & 0xff00) >> 8;
regs->frac_div_start_high = (frac & 0x30000) >> 16;
}
#define SSC_CENTER BIT(0)
#define SSC_EN BIT(1)
static void dsi_pll_calc_ssc(struct dsi_pll_7nm *pll)
{
struct dsi_pll_config *config = &pll->pll_configuration;
struct dsi_pll_regs *regs = &pll->reg_setup;
u32 ssc_per;
u32 ssc_mod;
u64 ssc_step_size;
u64 frac;
if (!config->enable_ssc) {
DBG("SSC not enabled\n");
return;
}
ssc_per = DIV_ROUND_CLOSEST(config->ref_freq, config->ssc_freq) / 2 - 1;
ssc_mod = (ssc_per + 1) % (config->ssc_adj_per + 1);
ssc_per -= ssc_mod;
frac = regs->frac_div_start_low |
(regs->frac_div_start_mid << 8) |
(regs->frac_div_start_high << 16);
ssc_step_size = regs->decimal_div_start;
ssc_step_size *= (1 << config->frac_bits);
ssc_step_size += frac;
ssc_step_size *= config->ssc_offset;
ssc_step_size *= (config->ssc_adj_per + 1);
ssc_step_size = div_u64(ssc_step_size, (ssc_per + 1));
ssc_step_size = DIV_ROUND_CLOSEST_ULL(ssc_step_size, 1000000);
regs->ssc_div_per_low = ssc_per & 0xFF;
regs->ssc_div_per_high = (ssc_per & 0xFF00) >> 8;
regs->ssc_stepsize_low = (u32)(ssc_step_size & 0xFF);
regs->ssc_stepsize_high = (u32)((ssc_step_size & 0xFF00) >> 8);
regs->ssc_adjper_low = config->ssc_adj_per & 0xFF;
regs->ssc_adjper_high = (config->ssc_adj_per & 0xFF00) >> 8;
regs->ssc_control = config->ssc_center ? SSC_CENTER : 0;
pr_debug("SCC: Dec:%d, frac:%llu, frac_bits:%d\n",
regs->decimal_div_start, frac, config->frac_bits);
pr_debug("SSC: div_per:0x%X, stepsize:0x%X, adjper:0x%X\n",
ssc_per, (u32)ssc_step_size, config->ssc_adj_per);
}
static void dsi_pll_ssc_commit(struct dsi_pll_7nm *pll)
{
void __iomem *base = pll->mmio;
struct dsi_pll_regs *regs = &pll->reg_setup;
if (pll->pll_configuration.enable_ssc) {
pr_debug("SSC is enabled\n");
pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_STEPSIZE_LOW_1,
regs->ssc_stepsize_low);
pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_STEPSIZE_HIGH_1,
regs->ssc_stepsize_high);
pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_DIV_PER_LOW_1,
regs->ssc_div_per_low);
pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_DIV_PER_HIGH_1,
regs->ssc_div_per_high);
pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_ADJPER_LOW_1,
regs->ssc_adjper_low);
pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_ADJPER_HIGH_1,
regs->ssc_adjper_high);
pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_CONTROL,
SSC_EN | regs->ssc_control);
}
}
static void dsi_pll_config_hzindep_reg(struct dsi_pll_7nm *pll)
{
void __iomem *base = pll->mmio;
u8 analog_controls_five_1 = 0x01, vco_config_1 = 0x00;
if (pll->base.type == MSM_DSI_PHY_7NM_V4_1) {
if (pll->vco_current_rate >= 3100000000ULL)
analog_controls_five_1 = 0x03;
if (pll->vco_current_rate < 1520000000ULL)
vco_config_1 = 0x08;
else if (pll->vco_current_rate < 2990000000ULL)
vco_config_1 = 0x01;
}
pll_write(base + REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_FIVE_1,
analog_controls_five_1);
pll_write(base + REG_DSI_7nm_PHY_PLL_VCO_CONFIG_1, vco_config_1);
pll_write(base + REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_FIVE, 0x01);
pll_write(base + REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_TWO, 0x03);
pll_write(base + REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_THREE, 0x00);
pll_write(base + REG_DSI_7nm_PHY_PLL_DSM_DIVIDER, 0x00);
pll_write(base + REG_DSI_7nm_PHY_PLL_FEEDBACK_DIVIDER, 0x4e);
pll_write(base + REG_DSI_7nm_PHY_PLL_CALIBRATION_SETTINGS, 0x40);
pll_write(base + REG_DSI_7nm_PHY_PLL_BAND_SEL_CAL_SETTINGS_THREE, 0xba);
pll_write(base + REG_DSI_7nm_PHY_PLL_FREQ_DETECT_SETTINGS_ONE, 0x0c);
pll_write(base + REG_DSI_7nm_PHY_PLL_OUTDIV, 0x00);
pll_write(base + REG_DSI_7nm_PHY_PLL_CORE_OVERRIDE, 0x00);
pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_DIGITAL_TIMERS_TWO, 0x08);
pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_PROP_GAIN_RATE_1, 0x0a);
pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_BAND_SEL_RATE_1, 0xc0);
pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_INT_GAIN_IFILT_BAND_1, 0x84);
pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_INT_GAIN_IFILT_BAND_1, 0x82);
pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_FL_INT_GAIN_PFILT_BAND_1, 0x4c);
pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_LOCK_OVERRIDE, 0x80);
pll_write(base + REG_DSI_7nm_PHY_PLL_PFILT, 0x29);
pll_write(base + REG_DSI_7nm_PHY_PLL_PFILT, 0x2f);
pll_write(base + REG_DSI_7nm_PHY_PLL_IFILT, 0x2a);
pll_write(base + REG_DSI_7nm_PHY_PLL_IFILT,
pll->base.type == MSM_DSI_PHY_7NM_V4_1 ? 0x3f : 0x22);
if (pll->base.type == MSM_DSI_PHY_7NM_V4_1) {
pll_write(base + REG_DSI_7nm_PHY_PLL_PERF_OPTIMIZE, 0x22);
if (pll->slave)
pll_write(pll->slave->mmio + REG_DSI_7nm_PHY_PLL_PERF_OPTIMIZE, 0x22);
}
}
static void dsi_pll_commit(struct dsi_pll_7nm *pll)
{
void __iomem *base = pll->mmio;
struct dsi_pll_regs *reg = &pll->reg_setup;
pll_write(base + REG_DSI_7nm_PHY_PLL_CORE_INPUT_OVERRIDE, 0x12);
pll_write(base + REG_DSI_7nm_PHY_PLL_DECIMAL_DIV_START_1, reg->decimal_div_start);
pll_write(base + REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_LOW_1, reg->frac_div_start_low);
pll_write(base + REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_MID_1, reg->frac_div_start_mid);
pll_write(base + REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_HIGH_1, reg->frac_div_start_high);
pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_LOCKDET_RATE_1, reg->pll_lockdet_rate);
pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_LOCK_DELAY, 0x06);
pll_write(base + REG_DSI_7nm_PHY_PLL_CMODE_1, 0x10); /* TODO: 0x00 for CPHY */
pll_write(base + REG_DSI_7nm_PHY_PLL_CLOCK_INVERTERS, reg->pll_clock_inverters);
}
static int dsi_pll_7nm_vco_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll);
DBG("DSI PLL%d rate=%lu, parent's=%lu", pll_7nm->id, rate,
parent_rate);
pll_7nm->vco_current_rate = rate;
pll_7nm->vco_ref_clk_rate = VCO_REF_CLK_RATE;
dsi_pll_setup_config(pll_7nm);
dsi_pll_calc_dec_frac(pll_7nm);
dsi_pll_calc_ssc(pll_7nm);
dsi_pll_commit(pll_7nm);
dsi_pll_config_hzindep_reg(pll_7nm);
dsi_pll_ssc_commit(pll_7nm);
/* flush, ensure all register writes are done*/
wmb();
return 0;
}
static int dsi_pll_7nm_lock_status(struct dsi_pll_7nm *pll)
{
int rc;
u32 status = 0;
u32 const delay_us = 100;
u32 const timeout_us = 5000;
rc = readl_poll_timeout_atomic(pll->mmio +
REG_DSI_7nm_PHY_PLL_COMMON_STATUS_ONE,
status,
((status & BIT(0)) > 0),
delay_us,
timeout_us);
if (rc)
pr_err("DSI PLL(%d) lock failed, status=0x%08x\n",
pll->id, status);
return rc;
}
static void dsi_pll_disable_pll_bias(struct dsi_pll_7nm *pll)
{
u32 data = pll_read(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CTRL_0);
pll_write(pll->mmio + REG_DSI_7nm_PHY_PLL_SYSTEM_MUXES, 0);
pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CTRL_0, data & ~BIT(5));
ndelay(250);
}
static void dsi_pll_enable_pll_bias(struct dsi_pll_7nm *pll)
{
u32 data = pll_read(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CTRL_0);
pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CTRL_0, data | BIT(5));
pll_write(pll->mmio + REG_DSI_7nm_PHY_PLL_SYSTEM_MUXES, 0xc0);
ndelay(250);
}
static void dsi_pll_disable_global_clk(struct dsi_pll_7nm *pll)
{
u32 data;
data = pll_read(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CLK_CFG1);
pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CLK_CFG1, data & ~BIT(5));
}
static void dsi_pll_enable_global_clk(struct dsi_pll_7nm *pll)
{
u32 data;
pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CTRL_3, 0x04);
data = pll_read(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CLK_CFG1);
pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CLK_CFG1,
data | BIT(5) | BIT(4));
}
static void dsi_pll_phy_dig_reset(struct dsi_pll_7nm *pll)
{
/*
* Reset the PHY digital domain. This would be needed when
* coming out of a CX or analog rail power collapse while
* ensuring that the pads maintain LP00 or LP11 state
*/
pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_GLBL_DIGTOP_SPARE4, BIT(0));
wmb(); /* Ensure that the reset is deasserted */
pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_GLBL_DIGTOP_SPARE4, 0x0);
wmb(); /* Ensure that the reset is deasserted */
}
static int dsi_pll_7nm_vco_prepare(struct clk_hw *hw)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll);
int rc;
dsi_pll_enable_pll_bias(pll_7nm);
if (pll_7nm->slave)
dsi_pll_enable_pll_bias(pll_7nm->slave);
/* Start PLL */
pll_write(pll_7nm->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_PLL_CNTRL, 0x01);
/*
* ensure all PLL configurations are written prior to checking
* for PLL lock.
*/
wmb();
/* Check for PLL lock */
rc = dsi_pll_7nm_lock_status(pll_7nm);
if (rc) {
pr_err("PLL(%d) lock failed\n", pll_7nm->id);
goto error;
}
pll->pll_on = true;
/*
* assert power on reset for PHY digital in case the PLL is
* enabled after CX of analog domain power collapse. This needs
* to be done before enabling the global clk.
*/
dsi_pll_phy_dig_reset(pll_7nm);
if (pll_7nm->slave)
dsi_pll_phy_dig_reset(pll_7nm->slave);
dsi_pll_enable_global_clk(pll_7nm);
if (pll_7nm->slave)
dsi_pll_enable_global_clk(pll_7nm->slave);
error:
return rc;
}
static void dsi_pll_disable_sub(struct dsi_pll_7nm *pll)
{
pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_RBUF_CTRL, 0);
dsi_pll_disable_pll_bias(pll);
}
static void dsi_pll_7nm_vco_unprepare(struct clk_hw *hw)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll);
/*
* To avoid any stray glitches while abruptly powering down the PLL
* make sure to gate the clock using the clock enable bit before
* powering down the PLL
*/
dsi_pll_disable_global_clk(pll_7nm);
pll_write(pll_7nm->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_PLL_CNTRL, 0);
dsi_pll_disable_sub(pll_7nm);
if (pll_7nm->slave) {
dsi_pll_disable_global_clk(pll_7nm->slave);
dsi_pll_disable_sub(pll_7nm->slave);
}
/* flush, ensure all register writes are done */
wmb();
pll->pll_on = false;
}
static unsigned long dsi_pll_7nm_vco_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll);
struct dsi_pll_config *config = &pll_7nm->pll_configuration;
void __iomem *base = pll_7nm->mmio;
u64 ref_clk = pll_7nm->vco_ref_clk_rate;
u64 vco_rate = 0x0;
u64 multiplier;
u32 frac;
u32 dec;
u64 pll_freq, tmp64;
dec = pll_read(base + REG_DSI_7nm_PHY_PLL_DECIMAL_DIV_START_1);
dec &= 0xff;
frac = pll_read(base + REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_LOW_1);
frac |= ((pll_read(base + REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_MID_1) &
0xff) << 8);
frac |= ((pll_read(base + REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_HIGH_1) &
0x3) << 16);
/*
* TODO:
* 1. Assumes prescaler is disabled
*/
multiplier = 1 << config->frac_bits;
pll_freq = dec * (ref_clk * 2);
tmp64 = (ref_clk * 2 * frac);
pll_freq += div_u64(tmp64, multiplier);
vco_rate = pll_freq;
DBG("DSI PLL%d returning vco rate = %lu, dec = %x, frac = %x",
pll_7nm->id, (unsigned long)vco_rate, dec, frac);
return (unsigned long)vco_rate;
}
static const struct clk_ops clk_ops_dsi_pll_7nm_vco = {
.round_rate = msm_dsi_pll_helper_clk_round_rate,
.set_rate = dsi_pll_7nm_vco_set_rate,
.recalc_rate = dsi_pll_7nm_vco_recalc_rate,
.prepare = dsi_pll_7nm_vco_prepare,
.unprepare = dsi_pll_7nm_vco_unprepare,
};
/*
* PLL Callbacks
*/
static void dsi_pll_7nm_save_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll);
struct pll_7nm_cached_state *cached = &pll_7nm->cached_state;
void __iomem *phy_base = pll_7nm->phy_cmn_mmio;
u32 cmn_clk_cfg0, cmn_clk_cfg1;
cached->pll_out_div = pll_read(pll_7nm->mmio +
REG_DSI_7nm_PHY_PLL_PLL_OUTDIV_RATE);
cached->pll_out_div &= 0x3;
cmn_clk_cfg0 = pll_read(phy_base + REG_DSI_7nm_PHY_CMN_CLK_CFG0);
cached->bit_clk_div = cmn_clk_cfg0 & 0xf;
cached->pix_clk_div = (cmn_clk_cfg0 & 0xf0) >> 4;
cmn_clk_cfg1 = pll_read(phy_base + REG_DSI_7nm_PHY_CMN_CLK_CFG1);
cached->pll_mux = cmn_clk_cfg1 & 0x3;
DBG("DSI PLL%d outdiv %x bit_clk_div %x pix_clk_div %x pll_mux %x",
pll_7nm->id, cached->pll_out_div, cached->bit_clk_div,
cached->pix_clk_div, cached->pll_mux);
}
static int dsi_pll_7nm_restore_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll);
struct pll_7nm_cached_state *cached = &pll_7nm->cached_state;
void __iomem *phy_base = pll_7nm->phy_cmn_mmio;
u32 val;
int ret;
val = pll_read(pll_7nm->mmio + REG_DSI_7nm_PHY_PLL_PLL_OUTDIV_RATE);
val &= ~0x3;
val |= cached->pll_out_div;
pll_write(pll_7nm->mmio + REG_DSI_7nm_PHY_PLL_PLL_OUTDIV_RATE, val);
pll_write(phy_base + REG_DSI_7nm_PHY_CMN_CLK_CFG0,
cached->bit_clk_div | (cached->pix_clk_div << 4));
val = pll_read(phy_base + REG_DSI_7nm_PHY_CMN_CLK_CFG1);
val &= ~0x3;
val |= cached->pll_mux;
pll_write(phy_base + REG_DSI_7nm_PHY_CMN_CLK_CFG1, val);
ret = dsi_pll_7nm_vco_set_rate(&pll->clk_hw, pll_7nm->vco_current_rate, pll_7nm->vco_ref_clk_rate);
if (ret) {
DRM_DEV_ERROR(&pll_7nm->pdev->dev,
"restore vco rate failed. ret=%d\n", ret);
return ret;
}
DBG("DSI PLL%d", pll_7nm->id);
return 0;
}
static int dsi_pll_7nm_set_usecase(struct msm_dsi_pll *pll,
enum msm_dsi_phy_usecase uc)
{
struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll);
void __iomem *base = pll_7nm->phy_cmn_mmio;
u32 data = 0x0; /* internal PLL */
DBG("DSI PLL%d", pll_7nm->id);
switch (uc) {
case MSM_DSI_PHY_STANDALONE:
break;
case MSM_DSI_PHY_MASTER:
pll_7nm->slave = pll_7nm_list[(pll_7nm->id + 1) % DSI_MAX];
break;
case MSM_DSI_PHY_SLAVE:
data = 0x1; /* external PLL */
break;
default:
return -EINVAL;
}
/* set PLL src */
pll_write(base + REG_DSI_7nm_PHY_CMN_CLK_CFG1, (data << 2));
pll_7nm->uc = uc;
return 0;
}
static int dsi_pll_7nm_get_provider(struct msm_dsi_pll *pll,
struct clk **byte_clk_provider,
struct clk **pixel_clk_provider)
{
struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll);
struct clk_hw_onecell_data *hw_data = pll_7nm->hw_data;
DBG("DSI PLL%d", pll_7nm->id);
if (byte_clk_provider)
*byte_clk_provider = hw_data->hws[DSI_BYTE_PLL_CLK]->clk;
if (pixel_clk_provider)
*pixel_clk_provider = hw_data->hws[DSI_PIXEL_PLL_CLK]->clk;
return 0;
}
static void dsi_pll_7nm_destroy(struct msm_dsi_pll *pll)
{
struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll);
struct device *dev = &pll_7nm->pdev->dev;
DBG("DSI PLL%d", pll_7nm->id);
of_clk_del_provider(dev->of_node);
clk_hw_unregister_divider(pll_7nm->out_dsiclk_hw);
clk_hw_unregister_mux(pll_7nm->pclk_mux_hw);
clk_hw_unregister_fixed_factor(pll_7nm->post_out_div_clk_hw);
clk_hw_unregister_fixed_factor(pll_7nm->by_2_bit_clk_hw);
clk_hw_unregister_fixed_factor(pll_7nm->byte_clk_hw);
clk_hw_unregister_divider(pll_7nm->bit_clk_hw);
clk_hw_unregister_divider(pll_7nm->out_div_clk_hw);
clk_hw_unregister(&pll_7nm->base.clk_hw);
}
/*
* The post dividers and mux clocks are created using the standard divider and
* mux API. Unlike the 14nm PHY, the slave PLL doesn't need its dividers/mux
* state to follow the master PLL's divider/mux state. Therefore, we don't
* require special clock ops that also configure the slave PLL registers
*/
static int pll_7nm_register(struct dsi_pll_7nm *pll_7nm)
{
char clk_name[32], parent[32], vco_name[32];
char parent2[32], parent3[32], parent4[32];
struct clk_init_data vco_init = {
.parent_names = (const char *[]){ "bi_tcxo" },
.num_parents = 1,
.name = vco_name,
.flags = CLK_IGNORE_UNUSED,
.ops = &clk_ops_dsi_pll_7nm_vco,
};
struct device *dev = &pll_7nm->pdev->dev;
struct clk_hw_onecell_data *hw_data;
struct clk_hw *hw;
int ret;
DBG("DSI%d", pll_7nm->id);
hw_data = devm_kzalloc(dev, sizeof(*hw_data) +
NUM_PROVIDED_CLKS * sizeof(struct clk_hw *),
GFP_KERNEL);
if (!hw_data)
return -ENOMEM;
snprintf(vco_name, 32, "dsi%dvco_clk", pll_7nm->id);
pll_7nm->base.clk_hw.init = &vco_init;
ret = clk_hw_register(dev, &pll_7nm->base.clk_hw);
if (ret)
return ret;
snprintf(clk_name, 32, "dsi%d_pll_out_div_clk", pll_7nm->id);
snprintf(parent, 32, "dsi%dvco_clk", pll_7nm->id);
hw = clk_hw_register_divider(dev, clk_name,
parent, CLK_SET_RATE_PARENT,
pll_7nm->mmio +
REG_DSI_7nm_PHY_PLL_PLL_OUTDIV_RATE,
0, 2, CLK_DIVIDER_POWER_OF_TWO, NULL);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_base_clk_hw;
}
pll_7nm->out_div_clk_hw = hw;
snprintf(clk_name, 32, "dsi%d_pll_bit_clk", pll_7nm->id);
snprintf(parent, 32, "dsi%d_pll_out_div_clk", pll_7nm->id);
/* BIT CLK: DIV_CTRL_3_0 */
hw = clk_hw_register_divider(dev, clk_name, parent,
CLK_SET_RATE_PARENT,
pll_7nm->phy_cmn_mmio +
REG_DSI_7nm_PHY_CMN_CLK_CFG0,
0, 4, CLK_DIVIDER_ONE_BASED,
&pll_7nm->postdiv_lock);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_out_div_clk_hw;
}
pll_7nm->bit_clk_hw = hw;
snprintf(clk_name, 32, "dsi%d_phy_pll_out_byteclk", pll_7nm->id);
snprintf(parent, 32, "dsi%d_pll_bit_clk", pll_7nm->id);
/* DSI Byte clock = VCO_CLK / OUT_DIV / BIT_DIV / 8 */
hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
CLK_SET_RATE_PARENT, 1, 8);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_bit_clk_hw;
}
pll_7nm->byte_clk_hw = hw;
hw_data->hws[DSI_BYTE_PLL_CLK] = hw;
snprintf(clk_name, 32, "dsi%d_pll_by_2_bit_clk", pll_7nm->id);
snprintf(parent, 32, "dsi%d_pll_bit_clk", pll_7nm->id);
hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
0, 1, 2);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_byte_clk_hw;
}
pll_7nm->by_2_bit_clk_hw = hw;
snprintf(clk_name, 32, "dsi%d_pll_post_out_div_clk", pll_7nm->id);
snprintf(parent, 32, "dsi%d_pll_out_div_clk", pll_7nm->id);
hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
0, 1, 4);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_by_2_bit_clk_hw;
}
pll_7nm->post_out_div_clk_hw = hw;
snprintf(clk_name, 32, "dsi%d_pclk_mux", pll_7nm->id);
snprintf(parent, 32, "dsi%d_pll_bit_clk", pll_7nm->id);
snprintf(parent2, 32, "dsi%d_pll_by_2_bit_clk", pll_7nm->id);
snprintf(parent3, 32, "dsi%d_pll_out_div_clk", pll_7nm->id);
snprintf(parent4, 32, "dsi%d_pll_post_out_div_clk", pll_7nm->id);
hw = clk_hw_register_mux(dev, clk_name,
((const char *[]){
parent, parent2, parent3, parent4
}), 4, 0, pll_7nm->phy_cmn_mmio +
REG_DSI_7nm_PHY_CMN_CLK_CFG1,
0, 2, 0, NULL);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_post_out_div_clk_hw;
}
pll_7nm->pclk_mux_hw = hw;
snprintf(clk_name, 32, "dsi%d_phy_pll_out_dsiclk", pll_7nm->id);
snprintf(parent, 32, "dsi%d_pclk_mux", pll_7nm->id);
/* PIX CLK DIV : DIV_CTRL_7_4*/
hw = clk_hw_register_divider(dev, clk_name, parent,
0, pll_7nm->phy_cmn_mmio +
REG_DSI_7nm_PHY_CMN_CLK_CFG0,
4, 4, CLK_DIVIDER_ONE_BASED,
&pll_7nm->postdiv_lock);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_pclk_mux_hw;
}
pll_7nm->out_dsiclk_hw = hw;
hw_data->hws[DSI_PIXEL_PLL_CLK] = hw;
hw_data->num = NUM_PROVIDED_CLKS;
pll_7nm->hw_data = hw_data;
ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
pll_7nm->hw_data);
if (ret) {
DRM_DEV_ERROR(dev, "failed to register clk provider: %d\n", ret);
goto err_dsiclk_hw;
}
return 0;
err_dsiclk_hw:
clk_hw_unregister_divider(pll_7nm->out_dsiclk_hw);
err_pclk_mux_hw:
clk_hw_unregister_mux(pll_7nm->pclk_mux_hw);
err_post_out_div_clk_hw:
clk_hw_unregister_fixed_factor(pll_7nm->post_out_div_clk_hw);
err_by_2_bit_clk_hw:
clk_hw_unregister_fixed_factor(pll_7nm->by_2_bit_clk_hw);
err_byte_clk_hw:
clk_hw_unregister_fixed_factor(pll_7nm->byte_clk_hw);
err_bit_clk_hw:
clk_hw_unregister_divider(pll_7nm->bit_clk_hw);
err_out_div_clk_hw:
clk_hw_unregister_divider(pll_7nm->out_div_clk_hw);
err_base_clk_hw:
clk_hw_unregister(&pll_7nm->base.clk_hw);
return ret;
}
struct msm_dsi_pll *msm_dsi_pll_7nm_init(struct platform_device *pdev,
enum msm_dsi_phy_type type, int id)
{
struct dsi_pll_7nm *pll_7nm;
struct msm_dsi_pll *pll;
int ret;
pll_7nm = devm_kzalloc(&pdev->dev, sizeof(*pll_7nm), GFP_KERNEL);
if (!pll_7nm)
return ERR_PTR(-ENOMEM);
DBG("DSI PLL%d", id);
pll_7nm->pdev = pdev;
pll_7nm->id = id;
pll_7nm_list[id] = pll_7nm;
pll_7nm->phy_cmn_mmio = msm_ioremap(pdev, "dsi_phy", "DSI_PHY");
if (IS_ERR_OR_NULL(pll_7nm->phy_cmn_mmio)) {
DRM_DEV_ERROR(&pdev->dev, "failed to map CMN PHY base\n");
return ERR_PTR(-ENOMEM);
}
pll_7nm->mmio = msm_ioremap(pdev, "dsi_pll", "DSI_PLL");
if (IS_ERR_OR_NULL(pll_7nm->mmio)) {
DRM_DEV_ERROR(&pdev->dev, "failed to map PLL base\n");
return ERR_PTR(-ENOMEM);
}
spin_lock_init(&pll_7nm->postdiv_lock);
pll = &pll_7nm->base;
pll->min_rate = 1000000000UL;
pll->max_rate = 3500000000UL;
if (type == MSM_DSI_PHY_7NM_V4_1) {
pll->min_rate = 600000000UL;
pll->max_rate = (unsigned long)5000000000ULL;
/* workaround for max rate overflowing on 32-bit builds: */
pll->max_rate = max(pll->max_rate, 0xffffffffUL);
}
pll->get_provider = dsi_pll_7nm_get_provider;
pll->destroy = dsi_pll_7nm_destroy;
pll->save_state = dsi_pll_7nm_save_state;
pll->restore_state = dsi_pll_7nm_restore_state;
pll->set_usecase = dsi_pll_7nm_set_usecase;
pll_7nm->vco_delay = 1;
ret = pll_7nm_register(pll_7nm);
if (ret) {
DRM_DEV_ERROR(&pdev->dev, "failed to register PLL: %d\n", ret);
return ERR_PTR(ret);
}
/* TODO: Remove this when we have proper display handover support */
msm_dsi_pll_save_state(pll);
return pll;
}
static int dsi_phy_hw_v4_0_is_pll_on(struct msm_dsi_phy *phy) static int dsi_phy_hw_v4_0_is_pll_on(struct msm_dsi_phy *phy)
{ {
void __iomem *base = phy->base; void __iomem *base = phy->base;
......
/*
* SPDX-License-Identifier: GPL-2.0
* Copyright (c) 2018, The Linux Foundation
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/iopoll.h>
#include "dsi_pll.h"
#include "dsi.xml.h"
/*
* DSI PLL 10nm - clock diagram (eg: DSI0):
*
* dsi0_pll_out_div_clk dsi0_pll_bit_clk
* | |
* | |
* +---------+ | +----------+ | +----+
* dsi0vco_clk ---| out_div |--o--| divl_3_0 |--o--| /8 |-- dsi0_phy_pll_out_byteclk
* +---------+ | +----------+ | +----+
* | |
* | | dsi0_pll_by_2_bit_clk
* | | |
* | | +----+ | |\ dsi0_pclk_mux
* | |--| /2 |--o--| \ |
* | | +----+ | \ | +---------+
* | --------------| |--o--| div_7_4 |-- dsi0_phy_pll_out_dsiclk
* |------------------------------| / +---------+
* | +-----+ | /
* -----------| /4? |--o----------|/
* +-----+ | |
* | |dsiclk_sel
* |
* dsi0_pll_post_out_div_clk
*/
#define DSI_BYTE_PLL_CLK 0
#define DSI_PIXEL_PLL_CLK 1
#define NUM_PROVIDED_CLKS 2
#define VCO_REF_CLK_RATE 19200000
struct dsi_pll_regs {
u32 pll_prop_gain_rate;
u32 pll_lockdet_rate;
u32 decimal_div_start;
u32 frac_div_start_low;
u32 frac_div_start_mid;
u32 frac_div_start_high;
u32 pll_clock_inverters;
u32 ssc_stepsize_low;
u32 ssc_stepsize_high;
u32 ssc_div_per_low;
u32 ssc_div_per_high;
u32 ssc_adjper_low;
u32 ssc_adjper_high;
u32 ssc_control;
};
struct dsi_pll_config {
u32 ref_freq;
bool div_override;
u32 output_div;
bool ignore_frac;
bool disable_prescaler;
bool enable_ssc;
bool ssc_center;
u32 dec_bits;
u32 frac_bits;
u32 lock_timer;
u32 ssc_freq;
u32 ssc_offset;
u32 ssc_adj_per;
u32 thresh_cycles;
u32 refclk_cycles;
};
struct pll_10nm_cached_state {
unsigned long vco_rate;
u8 bit_clk_div;
u8 pix_clk_div;
u8 pll_out_div;
u8 pll_mux;
};
struct dsi_pll_10nm {
struct msm_dsi_pll base;
int id;
struct platform_device *pdev;
void __iomem *phy_cmn_mmio;
void __iomem *mmio;
u64 vco_ref_clk_rate;
u64 vco_current_rate;
/* protects REG_DSI_10nm_PHY_CMN_CLK_CFG0 register */
spinlock_t postdiv_lock;
int vco_delay;
struct dsi_pll_config pll_configuration;
struct dsi_pll_regs reg_setup;
/* private clocks: */
struct clk_hw *out_div_clk_hw;
struct clk_hw *bit_clk_hw;
struct clk_hw *byte_clk_hw;
struct clk_hw *by_2_bit_clk_hw;
struct clk_hw *post_out_div_clk_hw;
struct clk_hw *pclk_mux_hw;
struct clk_hw *out_dsiclk_hw;
/* clock-provider: */
struct clk_hw_onecell_data *hw_data;
struct pll_10nm_cached_state cached_state;
enum msm_dsi_phy_usecase uc;
struct dsi_pll_10nm *slave;
};
#define to_pll_10nm(x) container_of(x, struct dsi_pll_10nm, base)
/*
* Global list of private DSI PLL struct pointers. We need this for Dual DSI
* mode, where the master PLL's clk_ops needs access the slave's private data
*/
static struct dsi_pll_10nm *pll_10nm_list[DSI_MAX];
static void dsi_pll_setup_config(struct dsi_pll_10nm *pll)
{
struct dsi_pll_config *config = &pll->pll_configuration;
config->ref_freq = pll->vco_ref_clk_rate;
config->output_div = 1;
config->dec_bits = 8;
config->frac_bits = 18;
config->lock_timer = 64;
config->ssc_freq = 31500;
config->ssc_offset = 5000;
config->ssc_adj_per = 2;
config->thresh_cycles = 32;
config->refclk_cycles = 256;
config->div_override = false;
config->ignore_frac = false;
config->disable_prescaler = false;
config->enable_ssc = false;
config->ssc_center = 0;
}
static void dsi_pll_calc_dec_frac(struct dsi_pll_10nm *pll)
{
struct dsi_pll_config *config = &pll->pll_configuration;
struct dsi_pll_regs *regs = &pll->reg_setup;
u64 fref = pll->vco_ref_clk_rate;
u64 pll_freq;
u64 divider;
u64 dec, dec_multiple;
u32 frac;
u64 multiplier;
pll_freq = pll->vco_current_rate;
if (config->disable_prescaler)
divider = fref;
else
divider = fref * 2;
multiplier = 1 << config->frac_bits;
dec_multiple = div_u64(pll_freq * multiplier, divider);
dec = div_u64_rem(dec_multiple, multiplier, &frac);
if (pll_freq <= 1900000000UL)
regs->pll_prop_gain_rate = 8;
else if (pll_freq <= 3000000000UL)
regs->pll_prop_gain_rate = 10;
else
regs->pll_prop_gain_rate = 12;
if (pll_freq < 1100000000UL)
regs->pll_clock_inverters = 8;
else
regs->pll_clock_inverters = 0;
regs->pll_lockdet_rate = config->lock_timer;
regs->decimal_div_start = dec;
regs->frac_div_start_low = (frac & 0xff);
regs->frac_div_start_mid = (frac & 0xff00) >> 8;
regs->frac_div_start_high = (frac & 0x30000) >> 16;
}
#define SSC_CENTER BIT(0)
#define SSC_EN BIT(1)
static void dsi_pll_calc_ssc(struct dsi_pll_10nm *pll)
{
struct dsi_pll_config *config = &pll->pll_configuration;
struct dsi_pll_regs *regs = &pll->reg_setup;
u32 ssc_per;
u32 ssc_mod;
u64 ssc_step_size;
u64 frac;
if (!config->enable_ssc) {
DBG("SSC not enabled\n");
return;
}
ssc_per = DIV_ROUND_CLOSEST(config->ref_freq, config->ssc_freq) / 2 - 1;
ssc_mod = (ssc_per + 1) % (config->ssc_adj_per + 1);
ssc_per -= ssc_mod;
frac = regs->frac_div_start_low |
(regs->frac_div_start_mid << 8) |
(regs->frac_div_start_high << 16);
ssc_step_size = regs->decimal_div_start;
ssc_step_size *= (1 << config->frac_bits);
ssc_step_size += frac;
ssc_step_size *= config->ssc_offset;
ssc_step_size *= (config->ssc_adj_per + 1);
ssc_step_size = div_u64(ssc_step_size, (ssc_per + 1));
ssc_step_size = DIV_ROUND_CLOSEST_ULL(ssc_step_size, 1000000);
regs->ssc_div_per_low = ssc_per & 0xFF;
regs->ssc_div_per_high = (ssc_per & 0xFF00) >> 8;
regs->ssc_stepsize_low = (u32)(ssc_step_size & 0xFF);
regs->ssc_stepsize_high = (u32)((ssc_step_size & 0xFF00) >> 8);
regs->ssc_adjper_low = config->ssc_adj_per & 0xFF;
regs->ssc_adjper_high = (config->ssc_adj_per & 0xFF00) >> 8;
regs->ssc_control = config->ssc_center ? SSC_CENTER : 0;
pr_debug("SCC: Dec:%d, frac:%llu, frac_bits:%d\n",
regs->decimal_div_start, frac, config->frac_bits);
pr_debug("SSC: div_per:0x%X, stepsize:0x%X, adjper:0x%X\n",
ssc_per, (u32)ssc_step_size, config->ssc_adj_per);
}
static void dsi_pll_ssc_commit(struct dsi_pll_10nm *pll)
{
void __iomem *base = pll->mmio;
struct dsi_pll_regs *regs = &pll->reg_setup;
if (pll->pll_configuration.enable_ssc) {
pr_debug("SSC is enabled\n");
pll_write(base + REG_DSI_10nm_PHY_PLL_SSC_STEPSIZE_LOW_1,
regs->ssc_stepsize_low);
pll_write(base + REG_DSI_10nm_PHY_PLL_SSC_STEPSIZE_HIGH_1,
regs->ssc_stepsize_high);
pll_write(base + REG_DSI_10nm_PHY_PLL_SSC_DIV_PER_LOW_1,
regs->ssc_div_per_low);
pll_write(base + REG_DSI_10nm_PHY_PLL_SSC_DIV_PER_HIGH_1,
regs->ssc_div_per_high);
pll_write(base + REG_DSI_10nm_PHY_PLL_SSC_DIV_ADJPER_LOW_1,
regs->ssc_adjper_low);
pll_write(base + REG_DSI_10nm_PHY_PLL_SSC_DIV_ADJPER_HIGH_1,
regs->ssc_adjper_high);
pll_write(base + REG_DSI_10nm_PHY_PLL_SSC_CONTROL,
SSC_EN | regs->ssc_control);
}
}
static void dsi_pll_config_hzindep_reg(struct dsi_pll_10nm *pll)
{
void __iomem *base = pll->mmio;
pll_write(base + REG_DSI_10nm_PHY_PLL_ANALOG_CONTROLS_ONE, 0x80);
pll_write(base + REG_DSI_10nm_PHY_PLL_ANALOG_CONTROLS_TWO, 0x03);
pll_write(base + REG_DSI_10nm_PHY_PLL_ANALOG_CONTROLS_THREE, 0x00);
pll_write(base + REG_DSI_10nm_PHY_PLL_DSM_DIVIDER, 0x00);
pll_write(base + REG_DSI_10nm_PHY_PLL_FEEDBACK_DIVIDER, 0x4e);
pll_write(base + REG_DSI_10nm_PHY_PLL_CALIBRATION_SETTINGS, 0x40);
pll_write(base + REG_DSI_10nm_PHY_PLL_BAND_SEL_CAL_SETTINGS_THREE,
0xba);
pll_write(base + REG_DSI_10nm_PHY_PLL_FREQ_DETECT_SETTINGS_ONE, 0x0c);
pll_write(base + REG_DSI_10nm_PHY_PLL_OUTDIV, 0x00);
pll_write(base + REG_DSI_10nm_PHY_PLL_CORE_OVERRIDE, 0x00);
pll_write(base + REG_DSI_10nm_PHY_PLL_PLL_DIGITAL_TIMERS_TWO, 0x08);
pll_write(base + REG_DSI_10nm_PHY_PLL_PLL_PROP_GAIN_RATE_1, 0x08);
pll_write(base + REG_DSI_10nm_PHY_PLL_PLL_BAND_SET_RATE_1, 0xc0);
pll_write(base + REG_DSI_10nm_PHY_PLL_PLL_INT_GAIN_IFILT_BAND_1, 0xfa);
pll_write(base + REG_DSI_10nm_PHY_PLL_PLL_FL_INT_GAIN_PFILT_BAND_1,
0x4c);
pll_write(base + REG_DSI_10nm_PHY_PLL_PLL_LOCK_OVERRIDE, 0x80);
pll_write(base + REG_DSI_10nm_PHY_PLL_PFILT, 0x29);
pll_write(base + REG_DSI_10nm_PHY_PLL_IFILT, 0x3f);
}
static void dsi_pll_commit(struct dsi_pll_10nm *pll)
{
void __iomem *base = pll->mmio;
struct dsi_pll_regs *reg = &pll->reg_setup;
pll_write(base + REG_DSI_10nm_PHY_PLL_CORE_INPUT_OVERRIDE, 0x12);
pll_write(base + REG_DSI_10nm_PHY_PLL_DECIMAL_DIV_START_1,
reg->decimal_div_start);
pll_write(base + REG_DSI_10nm_PHY_PLL_FRAC_DIV_START_LOW_1,
reg->frac_div_start_low);
pll_write(base + REG_DSI_10nm_PHY_PLL_FRAC_DIV_START_MID_1,
reg->frac_div_start_mid);
pll_write(base + REG_DSI_10nm_PHY_PLL_FRAC_DIV_START_HIGH_1,
reg->frac_div_start_high);
pll_write(base + REG_DSI_10nm_PHY_PLL_PLL_LOCKDET_RATE_1,
reg->pll_lockdet_rate);
pll_write(base + REG_DSI_10nm_PHY_PLL_PLL_LOCK_DELAY, 0x06);
pll_write(base + REG_DSI_10nm_PHY_PLL_CMODE, 0x10);
pll_write(base + REG_DSI_10nm_PHY_PLL_CLOCK_INVERTERS,
reg->pll_clock_inverters);
}
static int dsi_pll_10nm_vco_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_10nm *pll_10nm = to_pll_10nm(pll);
DBG("DSI PLL%d rate=%lu, parent's=%lu", pll_10nm->id, rate,
parent_rate);
pll_10nm->vco_current_rate = rate;
pll_10nm->vco_ref_clk_rate = VCO_REF_CLK_RATE;
dsi_pll_setup_config(pll_10nm);
dsi_pll_calc_dec_frac(pll_10nm);
dsi_pll_calc_ssc(pll_10nm);
dsi_pll_commit(pll_10nm);
dsi_pll_config_hzindep_reg(pll_10nm);
dsi_pll_ssc_commit(pll_10nm);
/* flush, ensure all register writes are done*/
wmb();
return 0;
}
static int dsi_pll_10nm_lock_status(struct dsi_pll_10nm *pll)
{
struct device *dev = &pll->pdev->dev;
int rc;
u32 status = 0;
u32 const delay_us = 100;
u32 const timeout_us = 5000;
rc = readl_poll_timeout_atomic(pll->mmio +
REG_DSI_10nm_PHY_PLL_COMMON_STATUS_ONE,
status,
((status & BIT(0)) > 0),
delay_us,
timeout_us);
if (rc)
DRM_DEV_ERROR(dev, "DSI PLL(%d) lock failed, status=0x%08x\n",
pll->id, status);
return rc;
}
static void dsi_pll_disable_pll_bias(struct dsi_pll_10nm *pll)
{
u32 data = pll_read(pll->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_CTRL_0);
pll_write(pll->mmio + REG_DSI_10nm_PHY_PLL_SYSTEM_MUXES, 0);
pll_write(pll->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_CTRL_0,
data & ~BIT(5));
ndelay(250);
}
static void dsi_pll_enable_pll_bias(struct dsi_pll_10nm *pll)
{
u32 data = pll_read(pll->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_CTRL_0);
pll_write(pll->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_CTRL_0,
data | BIT(5));
pll_write(pll->mmio + REG_DSI_10nm_PHY_PLL_SYSTEM_MUXES, 0xc0);
ndelay(250);
}
static void dsi_pll_disable_global_clk(struct dsi_pll_10nm *pll)
{
u32 data;
data = pll_read(pll->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_CLK_CFG1);
pll_write(pll->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_CLK_CFG1,
data & ~BIT(5));
}
static void dsi_pll_enable_global_clk(struct dsi_pll_10nm *pll)
{
u32 data;
data = pll_read(pll->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_CLK_CFG1);
pll_write(pll->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_CLK_CFG1,
data | BIT(5));
}
static int dsi_pll_10nm_vco_prepare(struct clk_hw *hw)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_10nm *pll_10nm = to_pll_10nm(pll);
struct device *dev = &pll_10nm->pdev->dev;
int rc;
dsi_pll_enable_pll_bias(pll_10nm);
if (pll_10nm->slave)
dsi_pll_enable_pll_bias(pll_10nm->slave);
rc = dsi_pll_10nm_vco_set_rate(hw,pll_10nm->vco_current_rate, 0);
if (rc) {
DRM_DEV_ERROR(dev, "vco_set_rate failed, rc=%d\n", rc);
return rc;
}
/* Start PLL */
pll_write(pll_10nm->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_PLL_CNTRL,
0x01);
/*
* ensure all PLL configurations are written prior to checking
* for PLL lock.
*/
wmb();
/* Check for PLL lock */
rc = dsi_pll_10nm_lock_status(pll_10nm);
if (rc) {
DRM_DEV_ERROR(dev, "PLL(%d) lock failed\n", pll_10nm->id);
goto error;
}
pll->pll_on = true;
dsi_pll_enable_global_clk(pll_10nm);
if (pll_10nm->slave)
dsi_pll_enable_global_clk(pll_10nm->slave);
pll_write(pll_10nm->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_RBUF_CTRL,
0x01);
if (pll_10nm->slave)
pll_write(pll_10nm->slave->phy_cmn_mmio +
REG_DSI_10nm_PHY_CMN_RBUF_CTRL, 0x01);
error:
return rc;
}
static void dsi_pll_disable_sub(struct dsi_pll_10nm *pll)
{
pll_write(pll->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_RBUF_CTRL, 0);
dsi_pll_disable_pll_bias(pll);
}
static void dsi_pll_10nm_vco_unprepare(struct clk_hw *hw)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_10nm *pll_10nm = to_pll_10nm(pll);
/*
* To avoid any stray glitches while abruptly powering down the PLL
* make sure to gate the clock using the clock enable bit before
* powering down the PLL
*/
dsi_pll_disable_global_clk(pll_10nm);
pll_write(pll_10nm->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_PLL_CNTRL, 0);
dsi_pll_disable_sub(pll_10nm);
if (pll_10nm->slave) {
dsi_pll_disable_global_clk(pll_10nm->slave);
dsi_pll_disable_sub(pll_10nm->slave);
}
/* flush, ensure all register writes are done */
wmb();
pll->pll_on = false;
}
static unsigned long dsi_pll_10nm_vco_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_10nm *pll_10nm = to_pll_10nm(pll);
struct dsi_pll_config *config = &pll_10nm->pll_configuration;
void __iomem *base = pll_10nm->mmio;
u64 ref_clk = pll_10nm->vco_ref_clk_rate;
u64 vco_rate = 0x0;
u64 multiplier;
u32 frac;
u32 dec;
u64 pll_freq, tmp64;
dec = pll_read(base + REG_DSI_10nm_PHY_PLL_DECIMAL_DIV_START_1);
dec &= 0xff;
frac = pll_read(base + REG_DSI_10nm_PHY_PLL_FRAC_DIV_START_LOW_1);
frac |= ((pll_read(base + REG_DSI_10nm_PHY_PLL_FRAC_DIV_START_MID_1) &
0xff) << 8);
frac |= ((pll_read(base + REG_DSI_10nm_PHY_PLL_FRAC_DIV_START_HIGH_1) &
0x3) << 16);
/*
* TODO:
* 1. Assumes prescaler is disabled
*/
multiplier = 1 << config->frac_bits;
pll_freq = dec * (ref_clk * 2);
tmp64 = (ref_clk * 2 * frac);
pll_freq += div_u64(tmp64, multiplier);
vco_rate = pll_freq;
DBG("DSI PLL%d returning vco rate = %lu, dec = %x, frac = %x",
pll_10nm->id, (unsigned long)vco_rate, dec, frac);
return (unsigned long)vco_rate;
}
static const struct clk_ops clk_ops_dsi_pll_10nm_vco = {
.round_rate = msm_dsi_pll_helper_clk_round_rate,
.set_rate = dsi_pll_10nm_vco_set_rate,
.recalc_rate = dsi_pll_10nm_vco_recalc_rate,
.prepare = dsi_pll_10nm_vco_prepare,
.unprepare = dsi_pll_10nm_vco_unprepare,
};
/*
* PLL Callbacks
*/
static void dsi_pll_10nm_save_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_10nm *pll_10nm = to_pll_10nm(pll);
struct pll_10nm_cached_state *cached = &pll_10nm->cached_state;
void __iomem *phy_base = pll_10nm->phy_cmn_mmio;
u32 cmn_clk_cfg0, cmn_clk_cfg1;
cached->pll_out_div = pll_read(pll_10nm->mmio +
REG_DSI_10nm_PHY_PLL_PLL_OUTDIV_RATE);
cached->pll_out_div &= 0x3;
cmn_clk_cfg0 = pll_read(phy_base + REG_DSI_10nm_PHY_CMN_CLK_CFG0);
cached->bit_clk_div = cmn_clk_cfg0 & 0xf;
cached->pix_clk_div = (cmn_clk_cfg0 & 0xf0) >> 4;
cmn_clk_cfg1 = pll_read(phy_base + REG_DSI_10nm_PHY_CMN_CLK_CFG1);
cached->pll_mux = cmn_clk_cfg1 & 0x3;
DBG("DSI PLL%d outdiv %x bit_clk_div %x pix_clk_div %x pll_mux %x",
pll_10nm->id, cached->pll_out_div, cached->bit_clk_div,
cached->pix_clk_div, cached->pll_mux);
}
static int dsi_pll_10nm_restore_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_10nm *pll_10nm = to_pll_10nm(pll);
struct pll_10nm_cached_state *cached = &pll_10nm->cached_state;
void __iomem *phy_base = pll_10nm->phy_cmn_mmio;
u32 val;
int ret;
val = pll_read(pll_10nm->mmio + REG_DSI_10nm_PHY_PLL_PLL_OUTDIV_RATE);
val &= ~0x3;
val |= cached->pll_out_div;
pll_write(pll_10nm->mmio + REG_DSI_10nm_PHY_PLL_PLL_OUTDIV_RATE, val);
pll_write(phy_base + REG_DSI_10nm_PHY_CMN_CLK_CFG0,
cached->bit_clk_div | (cached->pix_clk_div << 4));
val = pll_read(phy_base + REG_DSI_10nm_PHY_CMN_CLK_CFG1);
val &= ~0x3;
val |= cached->pll_mux;
pll_write(phy_base + REG_DSI_10nm_PHY_CMN_CLK_CFG1, val);
ret = dsi_pll_10nm_vco_set_rate(&pll->clk_hw, pll_10nm->vco_current_rate, pll_10nm->vco_ref_clk_rate);
if (ret) {
DRM_DEV_ERROR(&pll_10nm->pdev->dev,
"restore vco rate failed. ret=%d\n", ret);
return ret;
}
DBG("DSI PLL%d", pll_10nm->id);
return 0;
}
static int dsi_pll_10nm_set_usecase(struct msm_dsi_pll *pll,
enum msm_dsi_phy_usecase uc)
{
struct dsi_pll_10nm *pll_10nm = to_pll_10nm(pll);
void __iomem *base = pll_10nm->phy_cmn_mmio;
u32 data = 0x0; /* internal PLL */
DBG("DSI PLL%d", pll_10nm->id);
switch (uc) {
case MSM_DSI_PHY_STANDALONE:
break;
case MSM_DSI_PHY_MASTER:
pll_10nm->slave = pll_10nm_list[(pll_10nm->id + 1) % DSI_MAX];
break;
case MSM_DSI_PHY_SLAVE:
data = 0x1; /* external PLL */
break;
default:
return -EINVAL;
}
/* set PLL src */
pll_write(base + REG_DSI_10nm_PHY_CMN_CLK_CFG1, (data << 2));
pll_10nm->uc = uc;
return 0;
}
static int dsi_pll_10nm_get_provider(struct msm_dsi_pll *pll,
struct clk **byte_clk_provider,
struct clk **pixel_clk_provider)
{
struct dsi_pll_10nm *pll_10nm = to_pll_10nm(pll);
struct clk_hw_onecell_data *hw_data = pll_10nm->hw_data;
DBG("DSI PLL%d", pll_10nm->id);
if (byte_clk_provider)
*byte_clk_provider = hw_data->hws[DSI_BYTE_PLL_CLK]->clk;
if (pixel_clk_provider)
*pixel_clk_provider = hw_data->hws[DSI_PIXEL_PLL_CLK]->clk;
return 0;
}
static void dsi_pll_10nm_destroy(struct msm_dsi_pll *pll)
{
struct dsi_pll_10nm *pll_10nm = to_pll_10nm(pll);
struct device *dev = &pll_10nm->pdev->dev;
DBG("DSI PLL%d", pll_10nm->id);
of_clk_del_provider(dev->of_node);
clk_hw_unregister_divider(pll_10nm->out_dsiclk_hw);
clk_hw_unregister_mux(pll_10nm->pclk_mux_hw);
clk_hw_unregister_fixed_factor(pll_10nm->post_out_div_clk_hw);
clk_hw_unregister_fixed_factor(pll_10nm->by_2_bit_clk_hw);
clk_hw_unregister_fixed_factor(pll_10nm->byte_clk_hw);
clk_hw_unregister_divider(pll_10nm->bit_clk_hw);
clk_hw_unregister_divider(pll_10nm->out_div_clk_hw);
clk_hw_unregister(&pll_10nm->base.clk_hw);
}
/*
* The post dividers and mux clocks are created using the standard divider and
* mux API. Unlike the 14nm PHY, the slave PLL doesn't need its dividers/mux
* state to follow the master PLL's divider/mux state. Therefore, we don't
* require special clock ops that also configure the slave PLL registers
*/
static int pll_10nm_register(struct dsi_pll_10nm *pll_10nm)
{
char clk_name[32], parent[32], vco_name[32];
char parent2[32], parent3[32], parent4[32];
struct clk_init_data vco_init = {
.parent_names = (const char *[]){ "xo" },
.num_parents = 1,
.name = vco_name,
.flags = CLK_IGNORE_UNUSED,
.ops = &clk_ops_dsi_pll_10nm_vco,
};
struct device *dev = &pll_10nm->pdev->dev;
struct clk_hw_onecell_data *hw_data;
struct clk_hw *hw;
int ret;
DBG("DSI%d", pll_10nm->id);
hw_data = devm_kzalloc(dev, sizeof(*hw_data) +
NUM_PROVIDED_CLKS * sizeof(struct clk_hw *),
GFP_KERNEL);
if (!hw_data)
return -ENOMEM;
snprintf(vco_name, 32, "dsi%dvco_clk", pll_10nm->id);
pll_10nm->base.clk_hw.init = &vco_init;
ret = clk_hw_register(dev, &pll_10nm->base.clk_hw);
if (ret)
return ret;
snprintf(clk_name, 32, "dsi%d_pll_out_div_clk", pll_10nm->id);
snprintf(parent, 32, "dsi%dvco_clk", pll_10nm->id);
hw = clk_hw_register_divider(dev, clk_name,
parent, CLK_SET_RATE_PARENT,
pll_10nm->mmio +
REG_DSI_10nm_PHY_PLL_PLL_OUTDIV_RATE,
0, 2, CLK_DIVIDER_POWER_OF_TWO, NULL);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_base_clk_hw;
}
pll_10nm->out_div_clk_hw = hw;
snprintf(clk_name, 32, "dsi%d_pll_bit_clk", pll_10nm->id);
snprintf(parent, 32, "dsi%d_pll_out_div_clk", pll_10nm->id);
/* BIT CLK: DIV_CTRL_3_0 */
hw = clk_hw_register_divider(dev, clk_name, parent,
CLK_SET_RATE_PARENT,
pll_10nm->phy_cmn_mmio +
REG_DSI_10nm_PHY_CMN_CLK_CFG0,
0, 4, CLK_DIVIDER_ONE_BASED,
&pll_10nm->postdiv_lock);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_out_div_clk_hw;
}
pll_10nm->bit_clk_hw = hw;
snprintf(clk_name, 32, "dsi%d_phy_pll_out_byteclk", pll_10nm->id);
snprintf(parent, 32, "dsi%d_pll_bit_clk", pll_10nm->id);
/* DSI Byte clock = VCO_CLK / OUT_DIV / BIT_DIV / 8 */
hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
CLK_SET_RATE_PARENT, 1, 8);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_bit_clk_hw;
}
pll_10nm->byte_clk_hw = hw;
hw_data->hws[DSI_BYTE_PLL_CLK] = hw;
snprintf(clk_name, 32, "dsi%d_pll_by_2_bit_clk", pll_10nm->id);
snprintf(parent, 32, "dsi%d_pll_bit_clk", pll_10nm->id);
hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
0, 1, 2);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_byte_clk_hw;
}
pll_10nm->by_2_bit_clk_hw = hw;
snprintf(clk_name, 32, "dsi%d_pll_post_out_div_clk", pll_10nm->id);
snprintf(parent, 32, "dsi%d_pll_out_div_clk", pll_10nm->id);
hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
0, 1, 4);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_by_2_bit_clk_hw;
}
pll_10nm->post_out_div_clk_hw = hw;
snprintf(clk_name, 32, "dsi%d_pclk_mux", pll_10nm->id);
snprintf(parent, 32, "dsi%d_pll_bit_clk", pll_10nm->id);
snprintf(parent2, 32, "dsi%d_pll_by_2_bit_clk", pll_10nm->id);
snprintf(parent3, 32, "dsi%d_pll_out_div_clk", pll_10nm->id);
snprintf(parent4, 32, "dsi%d_pll_post_out_div_clk", pll_10nm->id);
hw = clk_hw_register_mux(dev, clk_name,
((const char *[]){
parent, parent2, parent3, parent4
}), 4, 0, pll_10nm->phy_cmn_mmio +
REG_DSI_10nm_PHY_CMN_CLK_CFG1,
0, 2, 0, NULL);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_post_out_div_clk_hw;
}
pll_10nm->pclk_mux_hw = hw;
snprintf(clk_name, 32, "dsi%d_phy_pll_out_dsiclk", pll_10nm->id);
snprintf(parent, 32, "dsi%d_pclk_mux", pll_10nm->id);
/* PIX CLK DIV : DIV_CTRL_7_4*/
hw = clk_hw_register_divider(dev, clk_name, parent,
0, pll_10nm->phy_cmn_mmio +
REG_DSI_10nm_PHY_CMN_CLK_CFG0,
4, 4, CLK_DIVIDER_ONE_BASED,
&pll_10nm->postdiv_lock);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_pclk_mux_hw;
}
pll_10nm->out_dsiclk_hw = hw;
hw_data->hws[DSI_PIXEL_PLL_CLK] = hw;
hw_data->num = NUM_PROVIDED_CLKS;
pll_10nm->hw_data = hw_data;
ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
pll_10nm->hw_data);
if (ret) {
DRM_DEV_ERROR(dev, "failed to register clk provider: %d\n", ret);
goto err_dsiclk_hw;
}
return 0;
err_dsiclk_hw:
clk_hw_unregister_divider(pll_10nm->out_dsiclk_hw);
err_pclk_mux_hw:
clk_hw_unregister_mux(pll_10nm->pclk_mux_hw);
err_post_out_div_clk_hw:
clk_hw_unregister_fixed_factor(pll_10nm->post_out_div_clk_hw);
err_by_2_bit_clk_hw:
clk_hw_unregister_fixed_factor(pll_10nm->by_2_bit_clk_hw);
err_byte_clk_hw:
clk_hw_unregister_fixed_factor(pll_10nm->byte_clk_hw);
err_bit_clk_hw:
clk_hw_unregister_divider(pll_10nm->bit_clk_hw);
err_out_div_clk_hw:
clk_hw_unregister_divider(pll_10nm->out_div_clk_hw);
err_base_clk_hw:
clk_hw_unregister(&pll_10nm->base.clk_hw);
return ret;
}
struct msm_dsi_pll *msm_dsi_pll_10nm_init(struct platform_device *pdev, int id)
{
struct dsi_pll_10nm *pll_10nm;
struct msm_dsi_pll *pll;
int ret;
pll_10nm = devm_kzalloc(&pdev->dev, sizeof(*pll_10nm), GFP_KERNEL);
if (!pll_10nm)
return ERR_PTR(-ENOMEM);
DBG("DSI PLL%d", id);
pll_10nm->pdev = pdev;
pll_10nm->id = id;
pll_10nm_list[id] = pll_10nm;
pll_10nm->phy_cmn_mmio = msm_ioremap(pdev, "dsi_phy", "DSI_PHY");
if (IS_ERR_OR_NULL(pll_10nm->phy_cmn_mmio)) {
DRM_DEV_ERROR(&pdev->dev, "failed to map CMN PHY base\n");
return ERR_PTR(-ENOMEM);
}
pll_10nm->mmio = msm_ioremap(pdev, "dsi_pll", "DSI_PLL");
if (IS_ERR_OR_NULL(pll_10nm->mmio)) {
DRM_DEV_ERROR(&pdev->dev, "failed to map PLL base\n");
return ERR_PTR(-ENOMEM);
}
spin_lock_init(&pll_10nm->postdiv_lock);
pll = &pll_10nm->base;
pll->min_rate = 1000000000UL;
pll->max_rate = 3500000000UL;
pll->get_provider = dsi_pll_10nm_get_provider;
pll->destroy = dsi_pll_10nm_destroy;
pll->save_state = dsi_pll_10nm_save_state;
pll->restore_state = dsi_pll_10nm_restore_state;
pll->set_usecase = dsi_pll_10nm_set_usecase;
pll_10nm->vco_delay = 1;
ret = pll_10nm_register(pll_10nm);
if (ret) {
DRM_DEV_ERROR(&pdev->dev, "failed to register PLL: %d\n", ret);
return ERR_PTR(ret);
}
/* TODO: Remove this when we have proper display handover support */
msm_dsi_pll_save_state(pll);
return pll;
}
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016, The Linux Foundation. All rights reserved.
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include "dsi_pll.h"
#include "dsi.xml.h"
/*
* DSI PLL 14nm - clock diagram (eg: DSI0):
*
* dsi0n1_postdiv_clk
* |
* |
* +----+ | +----+
* dsi0vco_clk ---| n1 |--o--| /8 |-- dsi0pllbyte
* +----+ | +----+
* | dsi0n1_postdivby2_clk
* | +----+ |
* o---| /2 |--o--|\
* | +----+ | \ +----+
* | | |--| n2 |-- dsi0pll
* o--------------| / +----+
* |/
*/
#define POLL_MAX_READS 15
#define POLL_TIMEOUT_US 1000
#define NUM_PROVIDED_CLKS 2
#define VCO_REF_CLK_RATE 19200000
#define VCO_MIN_RATE 1300000000UL
#define VCO_MAX_RATE 2600000000UL
#define DSI_BYTE_PLL_CLK 0
#define DSI_PIXEL_PLL_CLK 1
#define DSI_PLL_DEFAULT_VCO_POSTDIV 1
struct dsi_pll_input {
u32 fref; /* reference clk */
u32 fdata; /* bit clock rate */
u32 dsiclk_sel; /* Mux configuration (see diagram) */
u32 ssc_en; /* SSC enable/disable */
u32 ldo_en;
/* fixed params */
u32 refclk_dbler_en;
u32 vco_measure_time;
u32 kvco_measure_time;
u32 bandgap_timer;
u32 pll_wakeup_timer;
u32 plllock_cnt;
u32 plllock_rng;
u32 ssc_center;
u32 ssc_adj_period;
u32 ssc_spread;
u32 ssc_freq;
u32 pll_ie_trim;
u32 pll_ip_trim;
u32 pll_iptat_trim;
u32 pll_cpcset_cur;
u32 pll_cpmset_cur;
u32 pll_icpmset;
u32 pll_icpcset;
u32 pll_icpmset_p;
u32 pll_icpmset_m;
u32 pll_icpcset_p;
u32 pll_icpcset_m;
u32 pll_lpf_res1;
u32 pll_lpf_cap1;
u32 pll_lpf_cap2;
u32 pll_c3ctrl;
u32 pll_r3ctrl;
};
struct dsi_pll_output {
u32 pll_txclk_en;
u32 dec_start;
u32 div_frac_start;
u32 ssc_period;
u32 ssc_step_size;
u32 plllock_cmp;
u32 pll_vco_div_ref;
u32 pll_vco_count;
u32 pll_kvco_div_ref;
u32 pll_kvco_count;
u32 pll_misc1;
u32 pll_lpf2_postdiv;
u32 pll_resetsm_cntrl;
u32 pll_resetsm_cntrl2;
u32 pll_resetsm_cntrl5;
u32 pll_kvco_code;
u32 cmn_clk_cfg0;
u32 cmn_clk_cfg1;
u32 cmn_ldo_cntrl;
u32 pll_postdiv;
u32 fcvo;
};
struct pll_14nm_cached_state {
unsigned long vco_rate;
u8 n2postdiv;
u8 n1postdiv;
};
struct dsi_pll_14nm {
struct msm_dsi_pll base;
int id;
struct platform_device *pdev;
void __iomem *phy_cmn_mmio;
void __iomem *mmio;
int vco_delay;
struct dsi_pll_input in;
struct dsi_pll_output out;
/* protects REG_DSI_14nm_PHY_CMN_CLK_CFG0 register */
spinlock_t postdiv_lock;
u64 vco_current_rate;
u64 vco_ref_clk_rate;
/* private clocks: */
struct clk_hw *hws[NUM_DSI_CLOCKS_MAX];
u32 num_hws;
/* clock-provider: */
struct clk_hw_onecell_data *hw_data;
struct pll_14nm_cached_state cached_state;
enum msm_dsi_phy_usecase uc;
struct dsi_pll_14nm *slave;
};
#define to_pll_14nm(x) container_of(x, struct dsi_pll_14nm, base)
/*
* Private struct for N1/N2 post-divider clocks. These clocks are similar to
* the generic clk_divider class of clocks. The only difference is that it
* also sets the slave DSI PLL's post-dividers if in Dual DSI mode
*/
struct dsi_pll_14nm_postdiv {
struct clk_hw hw;
/* divider params */
u8 shift;
u8 width;
u8 flags; /* same flags as used by clk_divider struct */
struct dsi_pll_14nm *pll;
};
#define to_pll_14nm_postdiv(_hw) container_of(_hw, struct dsi_pll_14nm_postdiv, hw)
/*
* Global list of private DSI PLL struct pointers. We need this for Dual DSI
* mode, where the master PLL's clk_ops needs access the slave's private data
*/
static struct dsi_pll_14nm *pll_14nm_list[DSI_MAX];
static bool pll_14nm_poll_for_ready(struct dsi_pll_14nm *pll_14nm,
u32 nb_tries, u32 timeout_us)
{
bool pll_locked = false;
void __iomem *base = pll_14nm->mmio;
u32 tries, val;
tries = nb_tries;
while (tries--) {
val = pll_read(base +
REG_DSI_14nm_PHY_PLL_RESET_SM_READY_STATUS);
pll_locked = !!(val & BIT(5));
if (pll_locked)
break;
udelay(timeout_us);
}
if (!pll_locked) {
tries = nb_tries;
while (tries--) {
val = pll_read(base +
REG_DSI_14nm_PHY_PLL_RESET_SM_READY_STATUS);
pll_locked = !!(val & BIT(0));
if (pll_locked)
break;
udelay(timeout_us);
}
}
DBG("DSI PLL is %slocked", pll_locked ? "" : "*not* ");
return pll_locked;
}
static void dsi_pll_14nm_input_init(struct dsi_pll_14nm *pll)
{
pll->in.fref = pll->vco_ref_clk_rate;
pll->in.fdata = 0;
pll->in.dsiclk_sel = 1; /* Use the /2 path in Mux */
pll->in.ldo_en = 0; /* disabled for now */
/* fixed input */
pll->in.refclk_dbler_en = 0;
pll->in.vco_measure_time = 5;
pll->in.kvco_measure_time = 5;
pll->in.bandgap_timer = 4;
pll->in.pll_wakeup_timer = 5;
pll->in.plllock_cnt = 1;
pll->in.plllock_rng = 0;
/*
* SSC is enabled by default. We might need DT props for configuring
* some SSC params like PPM and center/down spread etc.
*/
pll->in.ssc_en = 1;
pll->in.ssc_center = 0; /* down spread by default */
pll->in.ssc_spread = 5; /* PPM / 1000 */
pll->in.ssc_freq = 31500; /* default recommended */
pll->in.ssc_adj_period = 37;
pll->in.pll_ie_trim = 4;
pll->in.pll_ip_trim = 4;
pll->in.pll_cpcset_cur = 1;
pll->in.pll_cpmset_cur = 1;
pll->in.pll_icpmset = 4;
pll->in.pll_icpcset = 4;
pll->in.pll_icpmset_p = 0;
pll->in.pll_icpmset_m = 0;
pll->in.pll_icpcset_p = 0;
pll->in.pll_icpcset_m = 0;
pll->in.pll_lpf_res1 = 3;
pll->in.pll_lpf_cap1 = 11;
pll->in.pll_lpf_cap2 = 1;
pll->in.pll_iptat_trim = 7;
pll->in.pll_c3ctrl = 2;
pll->in.pll_r3ctrl = 1;
}
#define CEIL(x, y) (((x) + ((y) - 1)) / (y))
static void pll_14nm_ssc_calc(struct dsi_pll_14nm *pll)
{
u32 period, ssc_period;
u32 ref, rem;
u64 step_size;
DBG("vco=%lld ref=%lld", pll->vco_current_rate, pll->vco_ref_clk_rate);
ssc_period = pll->in.ssc_freq / 500;
period = (u32)pll->vco_ref_clk_rate / 1000;
ssc_period = CEIL(period, ssc_period);
ssc_period -= 1;
pll->out.ssc_period = ssc_period;
DBG("ssc freq=%d spread=%d period=%d", pll->in.ssc_freq,
pll->in.ssc_spread, pll->out.ssc_period);
step_size = (u32)pll->vco_current_rate;
ref = pll->vco_ref_clk_rate;
ref /= 1000;
step_size = div_u64(step_size, ref);
step_size <<= 20;
step_size = div_u64(step_size, 1000);
step_size *= pll->in.ssc_spread;
step_size = div_u64(step_size, 1000);
step_size *= (pll->in.ssc_adj_period + 1);
rem = 0;
step_size = div_u64_rem(step_size, ssc_period + 1, &rem);
if (rem)
step_size++;
DBG("step_size=%lld", step_size);
step_size &= 0x0ffff; /* take lower 16 bits */
pll->out.ssc_step_size = step_size;
}
static void pll_14nm_dec_frac_calc(struct dsi_pll_14nm *pll)
{
struct dsi_pll_input *pin = &pll->in;
struct dsi_pll_output *pout = &pll->out;
u64 multiplier = BIT(20);
u64 dec_start_multiple, dec_start, pll_comp_val;
u32 duration, div_frac_start;
u64 vco_clk_rate = pll->vco_current_rate;
u64 fref = pll->vco_ref_clk_rate;
DBG("vco_clk_rate=%lld ref_clk_rate=%lld", vco_clk_rate, fref);
dec_start_multiple = div_u64(vco_clk_rate * multiplier, fref);
div_u64_rem(dec_start_multiple, multiplier, &div_frac_start);
dec_start = div_u64(dec_start_multiple, multiplier);
pout->dec_start = (u32)dec_start;
pout->div_frac_start = div_frac_start;
if (pin->plllock_cnt == 0)
duration = 1024;
else if (pin->plllock_cnt == 1)
duration = 256;
else if (pin->plllock_cnt == 2)
duration = 128;
else
duration = 32;
pll_comp_val = duration * dec_start_multiple;
pll_comp_val = div_u64(pll_comp_val, multiplier);
do_div(pll_comp_val, 10);
pout->plllock_cmp = (u32)pll_comp_val;
pout->pll_txclk_en = 1;
pout->cmn_ldo_cntrl = 0x3c;
}
static u32 pll_14nm_kvco_slop(u32 vrate)
{
u32 slop = 0;
if (vrate > VCO_MIN_RATE && vrate <= 1800000000UL)
slop = 600;
else if (vrate > 1800000000UL && vrate < 2300000000UL)
slop = 400;
else if (vrate > 2300000000UL && vrate < VCO_MAX_RATE)
slop = 280;
return slop;
}
static void pll_14nm_calc_vco_count(struct dsi_pll_14nm *pll)
{
struct dsi_pll_input *pin = &pll->in;
struct dsi_pll_output *pout = &pll->out;
u64 vco_clk_rate = pll->vco_current_rate;
u64 fref = pll->vco_ref_clk_rate;
u64 data;
u32 cnt;
data = fref * pin->vco_measure_time;
do_div(data, 1000000);
data &= 0x03ff; /* 10 bits */
data -= 2;
pout->pll_vco_div_ref = data;
data = div_u64(vco_clk_rate, 1000000); /* unit is Mhz */
data *= pin->vco_measure_time;
do_div(data, 10);
pout->pll_vco_count = data;
data = fref * pin->kvco_measure_time;
do_div(data, 1000000);
data &= 0x03ff; /* 10 bits */
data -= 1;
pout->pll_kvco_div_ref = data;
cnt = pll_14nm_kvco_slop(vco_clk_rate);
cnt *= 2;
cnt /= 100;
cnt *= pin->kvco_measure_time;
pout->pll_kvco_count = cnt;
pout->pll_misc1 = 16;
pout->pll_resetsm_cntrl = 48;
pout->pll_resetsm_cntrl2 = pin->bandgap_timer << 3;
pout->pll_resetsm_cntrl5 = pin->pll_wakeup_timer;
pout->pll_kvco_code = 0;
}
static void pll_db_commit_ssc(struct dsi_pll_14nm *pll)
{
void __iomem *base = pll->mmio;
struct dsi_pll_input *pin = &pll->in;
struct dsi_pll_output *pout = &pll->out;
u8 data;
data = pin->ssc_adj_period;
data &= 0x0ff;
pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_ADJ_PER1, data);
data = (pin->ssc_adj_period >> 8);
data &= 0x03;
pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_ADJ_PER2, data);
data = pout->ssc_period;
data &= 0x0ff;
pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_PER1, data);
data = (pout->ssc_period >> 8);
data &= 0x0ff;
pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_PER2, data);
data = pout->ssc_step_size;
data &= 0x0ff;
pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_STEP_SIZE1, data);
data = (pout->ssc_step_size >> 8);
data &= 0x0ff;
pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_STEP_SIZE2, data);
data = (pin->ssc_center & 0x01);
data <<= 1;
data |= 0x01; /* enable */
pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_EN_CENTER, data);
wmb(); /* make sure register committed */
}
static void pll_db_commit_common(struct dsi_pll_14nm *pll,
struct dsi_pll_input *pin,
struct dsi_pll_output *pout)
{
void __iomem *base = pll->mmio;
u8 data;
/* confgiure the non frequency dependent pll registers */
data = 0;
pll_write(base + REG_DSI_14nm_PHY_PLL_SYSCLK_EN_RESET, data);
data = pout->pll_txclk_en;
pll_write(base + REG_DSI_14nm_PHY_PLL_TXCLK_EN, data);
data = pout->pll_resetsm_cntrl;
pll_write(base + REG_DSI_14nm_PHY_PLL_RESETSM_CNTRL, data);
data = pout->pll_resetsm_cntrl2;
pll_write(base + REG_DSI_14nm_PHY_PLL_RESETSM_CNTRL2, data);
data = pout->pll_resetsm_cntrl5;
pll_write(base + REG_DSI_14nm_PHY_PLL_RESETSM_CNTRL5, data);
data = pout->pll_vco_div_ref & 0xff;
pll_write(base + REG_DSI_14nm_PHY_PLL_VCO_DIV_REF1, data);
data = (pout->pll_vco_div_ref >> 8) & 0x3;
pll_write(base + REG_DSI_14nm_PHY_PLL_VCO_DIV_REF2, data);
data = pout->pll_kvco_div_ref & 0xff;
pll_write(base + REG_DSI_14nm_PHY_PLL_KVCO_DIV_REF1, data);
data = (pout->pll_kvco_div_ref >> 8) & 0x3;
pll_write(base + REG_DSI_14nm_PHY_PLL_KVCO_DIV_REF2, data);
data = pout->pll_misc1;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_MISC1, data);
data = pin->pll_ie_trim;
pll_write(base + REG_DSI_14nm_PHY_PLL_IE_TRIM, data);
data = pin->pll_ip_trim;
pll_write(base + REG_DSI_14nm_PHY_PLL_IP_TRIM, data);
data = pin->pll_cpmset_cur << 3 | pin->pll_cpcset_cur;
pll_write(base + REG_DSI_14nm_PHY_PLL_CP_SET_CUR, data);
data = pin->pll_icpcset_p << 3 | pin->pll_icpcset_m;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_ICPCSET, data);
data = pin->pll_icpmset_p << 3 | pin->pll_icpcset_m;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_ICPMSET, data);
data = pin->pll_icpmset << 3 | pin->pll_icpcset;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_ICP_SET, data);
data = pin->pll_lpf_cap2 << 4 | pin->pll_lpf_cap1;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_LPF1, data);
data = pin->pll_iptat_trim;
pll_write(base + REG_DSI_14nm_PHY_PLL_IPTAT_TRIM, data);
data = pin->pll_c3ctrl | pin->pll_r3ctrl << 4;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_CRCTRL, data);
}
static void pll_14nm_software_reset(struct dsi_pll_14nm *pll_14nm)
{
void __iomem *cmn_base = pll_14nm->phy_cmn_mmio;
/* de assert pll start and apply pll sw reset */
/* stop pll */
pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_PLL_CNTRL, 0);
/* pll sw reset */
pll_write_udelay(cmn_base + REG_DSI_14nm_PHY_CMN_CTRL_1, 0x20, 10);
wmb(); /* make sure register committed */
pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_CTRL_1, 0);
wmb(); /* make sure register committed */
}
static void pll_db_commit_14nm(struct dsi_pll_14nm *pll,
struct dsi_pll_input *pin,
struct dsi_pll_output *pout)
{
void __iomem *base = pll->mmio;
void __iomem *cmn_base = pll->phy_cmn_mmio;
u8 data;
DBG("DSI%d PLL", pll->id);
data = pout->cmn_ldo_cntrl;
pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_LDO_CNTRL, data);
pll_db_commit_common(pll, pin, pout);
pll_14nm_software_reset(pll);
data = pin->dsiclk_sel; /* set dsiclk_sel = 1 */
pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_CLK_CFG1, data);
data = 0xff; /* data, clk, pll normal operation */
pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_CTRL_0, data);
/* configure the frequency dependent pll registers */
data = pout->dec_start;
pll_write(base + REG_DSI_14nm_PHY_PLL_DEC_START, data);
data = pout->div_frac_start & 0xff;
pll_write(base + REG_DSI_14nm_PHY_PLL_DIV_FRAC_START1, data);
data = (pout->div_frac_start >> 8) & 0xff;
pll_write(base + REG_DSI_14nm_PHY_PLL_DIV_FRAC_START2, data);
data = (pout->div_frac_start >> 16) & 0xf;
pll_write(base + REG_DSI_14nm_PHY_PLL_DIV_FRAC_START3, data);
data = pout->plllock_cmp & 0xff;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLLLOCK_CMP1, data);
data = (pout->plllock_cmp >> 8) & 0xff;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLLLOCK_CMP2, data);
data = (pout->plllock_cmp >> 16) & 0x3;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLLLOCK_CMP3, data);
data = pin->plllock_cnt << 1 | pin->plllock_rng << 3;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLLLOCK_CMP_EN, data);
data = pout->pll_vco_count & 0xff;
pll_write(base + REG_DSI_14nm_PHY_PLL_VCO_COUNT1, data);
data = (pout->pll_vco_count >> 8) & 0xff;
pll_write(base + REG_DSI_14nm_PHY_PLL_VCO_COUNT2, data);
data = pout->pll_kvco_count & 0xff;
pll_write(base + REG_DSI_14nm_PHY_PLL_KVCO_COUNT1, data);
data = (pout->pll_kvco_count >> 8) & 0x3;
pll_write(base + REG_DSI_14nm_PHY_PLL_KVCO_COUNT2, data);
data = (pout->pll_postdiv - 1) << 4 | pin->pll_lpf_res1;
pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_LPF2_POSTDIV, data);
if (pin->ssc_en)
pll_db_commit_ssc(pll);
wmb(); /* make sure register committed */
}
/*
* VCO clock Callbacks
*/
static int dsi_pll_14nm_vco_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll);
struct dsi_pll_input *pin = &pll_14nm->in;
struct dsi_pll_output *pout = &pll_14nm->out;
DBG("DSI PLL%d rate=%lu, parent's=%lu", pll_14nm->id, rate,
parent_rate);
pll_14nm->vco_current_rate = rate;
pll_14nm->vco_ref_clk_rate = VCO_REF_CLK_RATE;
dsi_pll_14nm_input_init(pll_14nm);
/*
* This configures the post divider internal to the VCO. It's
* fixed to divide by 1 for now.
*
* tx_band = pll_postdiv.
* 0: divided by 1
* 1: divided by 2
* 2: divided by 4
* 3: divided by 8
*/
pout->pll_postdiv = DSI_PLL_DEFAULT_VCO_POSTDIV;
pll_14nm_dec_frac_calc(pll_14nm);
if (pin->ssc_en)
pll_14nm_ssc_calc(pll_14nm);
pll_14nm_calc_vco_count(pll_14nm);
/* commit the slave DSI PLL registers if we're master. Note that we
* don't lock the slave PLL. We just ensure that the PLL/PHY registers
* of the master and slave are identical
*/
if (pll_14nm->uc == MSM_DSI_PHY_MASTER) {
struct dsi_pll_14nm *pll_14nm_slave = pll_14nm->slave;
pll_db_commit_14nm(pll_14nm_slave, pin, pout);
}
pll_db_commit_14nm(pll_14nm, pin, pout);
return 0;
}
static unsigned long dsi_pll_14nm_vco_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll);
void __iomem *base = pll_14nm->mmio;
u64 vco_rate, multiplier = BIT(20);
u32 div_frac_start;
u32 dec_start;
u64 ref_clk = parent_rate;
dec_start = pll_read(base + REG_DSI_14nm_PHY_PLL_DEC_START);
dec_start &= 0x0ff;
DBG("dec_start = %x", dec_start);
div_frac_start = (pll_read(base + REG_DSI_14nm_PHY_PLL_DIV_FRAC_START3)
& 0xf) << 16;
div_frac_start |= (pll_read(base + REG_DSI_14nm_PHY_PLL_DIV_FRAC_START2)
& 0xff) << 8;
div_frac_start |= pll_read(base + REG_DSI_14nm_PHY_PLL_DIV_FRAC_START1)
& 0xff;
DBG("div_frac_start = %x", div_frac_start);
vco_rate = ref_clk * dec_start;
vco_rate += ((ref_clk * div_frac_start) / multiplier);
/*
* Recalculating the rate from dec_start and frac_start doesn't end up
* the rate we originally set. Convert the freq to KHz, round it up and
* convert it back to MHz.
*/
vco_rate = DIV_ROUND_UP_ULL(vco_rate, 1000) * 1000;
DBG("returning vco rate = %lu", (unsigned long)vco_rate);
return (unsigned long)vco_rate;
}
static const struct clk_ops clk_ops_dsi_pll_14nm_vco = {
.round_rate = msm_dsi_pll_helper_clk_round_rate,
.set_rate = dsi_pll_14nm_vco_set_rate,
.recalc_rate = dsi_pll_14nm_vco_recalc_rate,
.prepare = msm_dsi_pll_helper_clk_prepare,
.unprepare = msm_dsi_pll_helper_clk_unprepare,
};
/*
* N1 and N2 post-divider clock callbacks
*/
#define div_mask(width) ((1 << (width)) - 1)
static unsigned long dsi_pll_14nm_postdiv_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct dsi_pll_14nm_postdiv *postdiv = to_pll_14nm_postdiv(hw);
struct dsi_pll_14nm *pll_14nm = postdiv->pll;
void __iomem *base = pll_14nm->phy_cmn_mmio;
u8 shift = postdiv->shift;
u8 width = postdiv->width;
u32 val;
DBG("DSI%d PLL parent rate=%lu", pll_14nm->id, parent_rate);
val = pll_read(base + REG_DSI_14nm_PHY_CMN_CLK_CFG0) >> shift;
val &= div_mask(width);
return divider_recalc_rate(hw, parent_rate, val, NULL,
postdiv->flags, width);
}
static long dsi_pll_14nm_postdiv_round_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long *prate)
{
struct dsi_pll_14nm_postdiv *postdiv = to_pll_14nm_postdiv(hw);
struct dsi_pll_14nm *pll_14nm = postdiv->pll;
DBG("DSI%d PLL parent rate=%lu", pll_14nm->id, rate);
return divider_round_rate(hw, rate, prate, NULL,
postdiv->width,
postdiv->flags);
}
static int dsi_pll_14nm_postdiv_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct dsi_pll_14nm_postdiv *postdiv = to_pll_14nm_postdiv(hw);
struct dsi_pll_14nm *pll_14nm = postdiv->pll;
void __iomem *base = pll_14nm->phy_cmn_mmio;
spinlock_t *lock = &pll_14nm->postdiv_lock;
u8 shift = postdiv->shift;
u8 width = postdiv->width;
unsigned int value;
unsigned long flags = 0;
u32 val;
DBG("DSI%d PLL parent rate=%lu parent rate %lu", pll_14nm->id, rate,
parent_rate);
value = divider_get_val(rate, parent_rate, NULL, postdiv->width,
postdiv->flags);
spin_lock_irqsave(lock, flags);
val = pll_read(base + REG_DSI_14nm_PHY_CMN_CLK_CFG0);
val &= ~(div_mask(width) << shift);
val |= value << shift;
pll_write(base + REG_DSI_14nm_PHY_CMN_CLK_CFG0, val);
/* If we're master in dual DSI mode, then the slave PLL's post-dividers
* follow the master's post dividers
*/
if (pll_14nm->uc == MSM_DSI_PHY_MASTER) {
struct dsi_pll_14nm *pll_14nm_slave = pll_14nm->slave;
void __iomem *slave_base = pll_14nm_slave->phy_cmn_mmio;
pll_write(slave_base + REG_DSI_14nm_PHY_CMN_CLK_CFG0, val);
}
spin_unlock_irqrestore(lock, flags);
return 0;
}
static const struct clk_ops clk_ops_dsi_pll_14nm_postdiv = {
.recalc_rate = dsi_pll_14nm_postdiv_recalc_rate,
.round_rate = dsi_pll_14nm_postdiv_round_rate,
.set_rate = dsi_pll_14nm_postdiv_set_rate,
};
/*
* PLL Callbacks
*/
static int dsi_pll_14nm_enable_seq(struct msm_dsi_pll *pll)
{
struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll);
void __iomem *base = pll_14nm->mmio;
void __iomem *cmn_base = pll_14nm->phy_cmn_mmio;
bool locked;
DBG("");
pll_write(base + REG_DSI_14nm_PHY_PLL_VREF_CFG1, 0x10);
pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_PLL_CNTRL, 1);
locked = pll_14nm_poll_for_ready(pll_14nm, POLL_MAX_READS,
POLL_TIMEOUT_US);
if (unlikely(!locked))
DRM_DEV_ERROR(&pll_14nm->pdev->dev, "DSI PLL lock failed\n");
else
DBG("DSI PLL lock success");
return locked ? 0 : -EINVAL;
}
static void dsi_pll_14nm_disable_seq(struct msm_dsi_pll *pll)
{
struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll);
void __iomem *cmn_base = pll_14nm->phy_cmn_mmio;
DBG("");
pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_PLL_CNTRL, 0);
}
static void dsi_pll_14nm_save_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll);
struct pll_14nm_cached_state *cached_state = &pll_14nm->cached_state;
void __iomem *cmn_base = pll_14nm->phy_cmn_mmio;
u32 data;
data = pll_read(cmn_base + REG_DSI_14nm_PHY_CMN_CLK_CFG0);
cached_state->n1postdiv = data & 0xf;
cached_state->n2postdiv = (data >> 4) & 0xf;
DBG("DSI%d PLL save state %x %x", pll_14nm->id,
cached_state->n1postdiv, cached_state->n2postdiv);
cached_state->vco_rate = clk_hw_get_rate(&pll->clk_hw);
}
static int dsi_pll_14nm_restore_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll);
struct pll_14nm_cached_state *cached_state = &pll_14nm->cached_state;
void __iomem *cmn_base = pll_14nm->phy_cmn_mmio;
u32 data;
int ret;
ret = dsi_pll_14nm_vco_set_rate(&pll->clk_hw,
cached_state->vco_rate, 0);
if (ret) {
DRM_DEV_ERROR(&pll_14nm->pdev->dev,
"restore vco rate failed. ret=%d\n", ret);
return ret;
}
data = cached_state->n1postdiv | (cached_state->n2postdiv << 4);
DBG("DSI%d PLL restore state %x %x", pll_14nm->id,
cached_state->n1postdiv, cached_state->n2postdiv);
pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_CLK_CFG0, data);
/* also restore post-dividers for slave DSI PLL */
if (pll_14nm->uc == MSM_DSI_PHY_MASTER) {
struct dsi_pll_14nm *pll_14nm_slave = pll_14nm->slave;
void __iomem *slave_base = pll_14nm_slave->phy_cmn_mmio;
pll_write(slave_base + REG_DSI_14nm_PHY_CMN_CLK_CFG0, data);
}
return 0;
}
static int dsi_pll_14nm_set_usecase(struct msm_dsi_pll *pll,
enum msm_dsi_phy_usecase uc)
{
struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll);
void __iomem *base = pll_14nm->mmio;
u32 clkbuflr_en, bandgap = 0;
switch (uc) {
case MSM_DSI_PHY_STANDALONE:
clkbuflr_en = 0x1;
break;
case MSM_DSI_PHY_MASTER:
clkbuflr_en = 0x3;
pll_14nm->slave = pll_14nm_list[(pll_14nm->id + 1) % DSI_MAX];
break;
case MSM_DSI_PHY_SLAVE:
clkbuflr_en = 0x0;
bandgap = 0x3;
break;
default:
return -EINVAL;
}
pll_write(base + REG_DSI_14nm_PHY_PLL_CLKBUFLR_EN, clkbuflr_en);
if (bandgap)
pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_BANDGAP, bandgap);
pll_14nm->uc = uc;
return 0;
}
static int dsi_pll_14nm_get_provider(struct msm_dsi_pll *pll,
struct clk **byte_clk_provider,
struct clk **pixel_clk_provider)
{
struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll);
struct clk_hw_onecell_data *hw_data = pll_14nm->hw_data;
if (byte_clk_provider)
*byte_clk_provider = hw_data->hws[DSI_BYTE_PLL_CLK]->clk;
if (pixel_clk_provider)
*pixel_clk_provider = hw_data->hws[DSI_PIXEL_PLL_CLK]->clk;
return 0;
}
static void dsi_pll_14nm_destroy(struct msm_dsi_pll *pll)
{
struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll);
struct platform_device *pdev = pll_14nm->pdev;
int num_hws = pll_14nm->num_hws;
of_clk_del_provider(pdev->dev.of_node);
while (num_hws--)
clk_hw_unregister(pll_14nm->hws[num_hws]);
}
static struct clk_hw *pll_14nm_postdiv_register(struct dsi_pll_14nm *pll_14nm,
const char *name,
const char *parent_name,
unsigned long flags,
u8 shift)
{
struct dsi_pll_14nm_postdiv *pll_postdiv;
struct device *dev = &pll_14nm->pdev->dev;
struct clk_init_data postdiv_init = {
.parent_names = (const char *[]) { parent_name },
.num_parents = 1,
.name = name,
.flags = flags,
.ops = &clk_ops_dsi_pll_14nm_postdiv,
};
int ret;
pll_postdiv = devm_kzalloc(dev, sizeof(*pll_postdiv), GFP_KERNEL);
if (!pll_postdiv)
return ERR_PTR(-ENOMEM);
pll_postdiv->pll = pll_14nm;
pll_postdiv->shift = shift;
/* both N1 and N2 postdividers are 4 bits wide */
pll_postdiv->width = 4;
/* range of each divider is from 1 to 15 */
pll_postdiv->flags = CLK_DIVIDER_ONE_BASED;
pll_postdiv->hw.init = &postdiv_init;
ret = clk_hw_register(dev, &pll_postdiv->hw);
if (ret)
return ERR_PTR(ret);
return &pll_postdiv->hw;
}
static int pll_14nm_register(struct dsi_pll_14nm *pll_14nm)
{
char clk_name[32], parent[32], vco_name[32];
struct clk_init_data vco_init = {
.parent_names = (const char *[]){ "xo" },
.num_parents = 1,
.name = vco_name,
.flags = CLK_IGNORE_UNUSED,
.ops = &clk_ops_dsi_pll_14nm_vco,
};
struct device *dev = &pll_14nm->pdev->dev;
struct clk_hw **hws = pll_14nm->hws;
struct clk_hw_onecell_data *hw_data;
struct clk_hw *hw;
int num = 0;
int ret;
DBG("DSI%d", pll_14nm->id);
hw_data = devm_kzalloc(dev, sizeof(*hw_data) +
NUM_PROVIDED_CLKS * sizeof(struct clk_hw *),
GFP_KERNEL);
if (!hw_data)
return -ENOMEM;
snprintf(vco_name, 32, "dsi%dvco_clk", pll_14nm->id);
pll_14nm->base.clk_hw.init = &vco_init;
ret = clk_hw_register(dev, &pll_14nm->base.clk_hw);
if (ret)
return ret;
hws[num++] = &pll_14nm->base.clk_hw;
snprintf(clk_name, 32, "dsi%dn1_postdiv_clk", pll_14nm->id);
snprintf(parent, 32, "dsi%dvco_clk", pll_14nm->id);
/* N1 postdiv, bits 0-3 in REG_DSI_14nm_PHY_CMN_CLK_CFG0 */
hw = pll_14nm_postdiv_register(pll_14nm, clk_name, parent,
CLK_SET_RATE_PARENT, 0);
if (IS_ERR(hw))
return PTR_ERR(hw);
hws[num++] = hw;
snprintf(clk_name, 32, "dsi%dpllbyte", pll_14nm->id);
snprintf(parent, 32, "dsi%dn1_postdiv_clk", pll_14nm->id);
/* DSI Byte clock = VCO_CLK / N1 / 8 */
hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
CLK_SET_RATE_PARENT, 1, 8);
if (IS_ERR(hw))
return PTR_ERR(hw);
hws[num++] = hw;
hw_data->hws[DSI_BYTE_PLL_CLK] = hw;
snprintf(clk_name, 32, "dsi%dn1_postdivby2_clk", pll_14nm->id);
snprintf(parent, 32, "dsi%dn1_postdiv_clk", pll_14nm->id);
/*
* Skip the mux for now, force DSICLK_SEL to 1, Add a /2 divider
* on the way. Don't let it set parent.
*/
hw = clk_hw_register_fixed_factor(dev, clk_name, parent, 0, 1, 2);
if (IS_ERR(hw))
return PTR_ERR(hw);
hws[num++] = hw;
snprintf(clk_name, 32, "dsi%dpll", pll_14nm->id);
snprintf(parent, 32, "dsi%dn1_postdivby2_clk", pll_14nm->id);
/* DSI pixel clock = VCO_CLK / N1 / 2 / N2
* This is the output of N2 post-divider, bits 4-7 in
* REG_DSI_14nm_PHY_CMN_CLK_CFG0. Don't let it set parent.
*/
hw = pll_14nm_postdiv_register(pll_14nm, clk_name, parent, 0, 4);
if (IS_ERR(hw))
return PTR_ERR(hw);
hws[num++] = hw;
hw_data->hws[DSI_PIXEL_PLL_CLK] = hw;
pll_14nm->num_hws = num;
hw_data->num = NUM_PROVIDED_CLKS;
pll_14nm->hw_data = hw_data;
ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
pll_14nm->hw_data);
if (ret) {
DRM_DEV_ERROR(dev, "failed to register clk provider: %d\n", ret);
return ret;
}
return 0;
}
struct msm_dsi_pll *msm_dsi_pll_14nm_init(struct platform_device *pdev, int id)
{
struct dsi_pll_14nm *pll_14nm;
struct msm_dsi_pll *pll;
int ret;
if (!pdev)
return ERR_PTR(-ENODEV);
pll_14nm = devm_kzalloc(&pdev->dev, sizeof(*pll_14nm), GFP_KERNEL);
if (!pll_14nm)
return ERR_PTR(-ENOMEM);
DBG("PLL%d", id);
pll_14nm->pdev = pdev;
pll_14nm->id = id;
pll_14nm_list[id] = pll_14nm;
pll_14nm->phy_cmn_mmio = msm_ioremap(pdev, "dsi_phy", "DSI_PHY");
if (IS_ERR_OR_NULL(pll_14nm->phy_cmn_mmio)) {
DRM_DEV_ERROR(&pdev->dev, "failed to map CMN PHY base\n");
return ERR_PTR(-ENOMEM);
}
pll_14nm->mmio = msm_ioremap(pdev, "dsi_pll", "DSI_PLL");
if (IS_ERR_OR_NULL(pll_14nm->mmio)) {
DRM_DEV_ERROR(&pdev->dev, "failed to map PLL base\n");
return ERR_PTR(-ENOMEM);
}
spin_lock_init(&pll_14nm->postdiv_lock);
pll = &pll_14nm->base;
pll->min_rate = VCO_MIN_RATE;
pll->max_rate = VCO_MAX_RATE;
pll->get_provider = dsi_pll_14nm_get_provider;
pll->destroy = dsi_pll_14nm_destroy;
pll->disable_seq = dsi_pll_14nm_disable_seq;
pll->save_state = dsi_pll_14nm_save_state;
pll->restore_state = dsi_pll_14nm_restore_state;
pll->set_usecase = dsi_pll_14nm_set_usecase;
pll_14nm->vco_delay = 1;
pll->en_seq_cnt = 1;
pll->enable_seqs[0] = dsi_pll_14nm_enable_seq;
ret = pll_14nm_register(pll_14nm);
if (ret) {
DRM_DEV_ERROR(&pdev->dev, "failed to register PLL: %d\n", ret);
return ERR_PTR(ret);
}
return pll;
}
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include "dsi_pll.h"
#include "dsi.xml.h"
/*
* DSI PLL 28nm - clock diagram (eg: DSI0):
*
* dsi0analog_postdiv_clk
* | dsi0indirect_path_div2_clk
* | |
* +------+ | +----+ | |\ dsi0byte_mux
* dsi0vco_clk --o--| DIV1 |--o--| /2 |--o--| \ |
* | +------+ +----+ | m| | +----+
* | | u|--o--| /4 |-- dsi0pllbyte
* | | x| +----+
* o--------------------------| /
* | |/
* | +------+
* o----------| DIV3 |------------------------- dsi0pll
* +------+
*/
#define POLL_MAX_READS 10
#define POLL_TIMEOUT_US 50
#define NUM_PROVIDED_CLKS 2
#define VCO_REF_CLK_RATE 19200000
#define VCO_MIN_RATE 350000000
#define VCO_MAX_RATE 750000000
#define DSI_BYTE_PLL_CLK 0
#define DSI_PIXEL_PLL_CLK 1
#define LPFR_LUT_SIZE 10
struct lpfr_cfg {
unsigned long vco_rate;
u32 resistance;
};
/* Loop filter resistance: */
static const struct lpfr_cfg lpfr_lut[LPFR_LUT_SIZE] = {
{ 479500000, 8 },
{ 480000000, 11 },
{ 575500000, 8 },
{ 576000000, 12 },
{ 610500000, 8 },
{ 659500000, 9 },
{ 671500000, 10 },
{ 672000000, 14 },
{ 708500000, 10 },
{ 750000000, 11 },
};
struct pll_28nm_cached_state {
unsigned long vco_rate;
u8 postdiv3;
u8 postdiv1;
u8 byte_mux;
};
struct dsi_pll_28nm {
struct msm_dsi_pll base;
int id;
struct platform_device *pdev;
void __iomem *mmio;
int vco_delay;
/* private clocks: */
struct clk *clks[NUM_DSI_CLOCKS_MAX];
u32 num_clks;
/* clock-provider: */
struct clk *provided_clks[NUM_PROVIDED_CLKS];
struct clk_onecell_data clk_data;
struct pll_28nm_cached_state cached_state;
};
#define to_pll_28nm(x) container_of(x, struct dsi_pll_28nm, base)
static bool pll_28nm_poll_for_ready(struct dsi_pll_28nm *pll_28nm,
u32 nb_tries, u32 timeout_us)
{
bool pll_locked = false;
u32 val;
while (nb_tries--) {
val = pll_read(pll_28nm->mmio + REG_DSI_28nm_PHY_PLL_STATUS);
pll_locked = !!(val & DSI_28nm_PHY_PLL_STATUS_PLL_RDY);
if (pll_locked)
break;
udelay(timeout_us);
}
DBG("DSI PLL is %slocked", pll_locked ? "" : "*not* ");
return pll_locked;
}
static void pll_28nm_software_reset(struct dsi_pll_28nm *pll_28nm)
{
void __iomem *base = pll_28nm->mmio;
/*
* Add HW recommended delays after toggling the software
* reset bit off and back on.
*/
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_TEST_CFG,
DSI_28nm_PHY_PLL_TEST_CFG_PLL_SW_RESET, 1);
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_TEST_CFG, 0x00, 1);
}
/*
* Clock Callbacks
*/
static int dsi_pll_28nm_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
struct device *dev = &pll_28nm->pdev->dev;
void __iomem *base = pll_28nm->mmio;
unsigned long div_fbx1000, gen_vco_clk;
u32 refclk_cfg, frac_n_mode, frac_n_value;
u32 sdm_cfg0, sdm_cfg1, sdm_cfg2, sdm_cfg3;
u32 cal_cfg10, cal_cfg11;
u32 rem;
int i;
VERB("rate=%lu, parent's=%lu", rate, parent_rate);
/* Force postdiv2 to be div-4 */
pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV2_CFG, 3);
/* Configure the Loop filter resistance */
for (i = 0; i < LPFR_LUT_SIZE; i++)
if (rate <= lpfr_lut[i].vco_rate)
break;
if (i == LPFR_LUT_SIZE) {
DRM_DEV_ERROR(dev, "unable to get loop filter resistance. vco=%lu\n",
rate);
return -EINVAL;
}
pll_write(base + REG_DSI_28nm_PHY_PLL_LPFR_CFG, lpfr_lut[i].resistance);
/* Loop filter capacitance values : c1 and c2 */
pll_write(base + REG_DSI_28nm_PHY_PLL_LPFC1_CFG, 0x70);
pll_write(base + REG_DSI_28nm_PHY_PLL_LPFC2_CFG, 0x15);
rem = rate % VCO_REF_CLK_RATE;
if (rem) {
refclk_cfg = DSI_28nm_PHY_PLL_REFCLK_CFG_DBLR;
frac_n_mode = 1;
div_fbx1000 = rate / (VCO_REF_CLK_RATE / 500);
gen_vco_clk = div_fbx1000 * (VCO_REF_CLK_RATE / 500);
} else {
refclk_cfg = 0x0;
frac_n_mode = 0;
div_fbx1000 = rate / (VCO_REF_CLK_RATE / 1000);
gen_vco_clk = div_fbx1000 * (VCO_REF_CLK_RATE / 1000);
}
DBG("refclk_cfg = %d", refclk_cfg);
rem = div_fbx1000 % 1000;
frac_n_value = (rem << 16) / 1000;
DBG("div_fb = %lu", div_fbx1000);
DBG("frac_n_value = %d", frac_n_value);
DBG("Generated VCO Clock: %lu", gen_vco_clk);
rem = 0;
sdm_cfg1 = pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG1);
sdm_cfg1 &= ~DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET__MASK;
if (frac_n_mode) {
sdm_cfg0 = 0x0;
sdm_cfg0 |= DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV(0);
sdm_cfg1 |= DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET(
(u32)(((div_fbx1000 / 1000) & 0x3f) - 1));
sdm_cfg3 = frac_n_value >> 8;
sdm_cfg2 = frac_n_value & 0xff;
} else {
sdm_cfg0 = DSI_28nm_PHY_PLL_SDM_CFG0_BYP;
sdm_cfg0 |= DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV(
(u32)(((div_fbx1000 / 1000) & 0x3f) - 1));
sdm_cfg1 |= DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET(0);
sdm_cfg2 = 0;
sdm_cfg3 = 0;
}
DBG("sdm_cfg0=%d", sdm_cfg0);
DBG("sdm_cfg1=%d", sdm_cfg1);
DBG("sdm_cfg2=%d", sdm_cfg2);
DBG("sdm_cfg3=%d", sdm_cfg3);
cal_cfg11 = (u32)(gen_vco_clk / (256 * 1000000));
cal_cfg10 = (u32)((gen_vco_clk % (256 * 1000000)) / 1000000);
DBG("cal_cfg10=%d, cal_cfg11=%d", cal_cfg10, cal_cfg11);
pll_write(base + REG_DSI_28nm_PHY_PLL_CHGPUMP_CFG, 0x02);
pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG3, 0x2b);
pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG4, 0x06);
pll_write(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x0d);
pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG1, sdm_cfg1);
pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG2,
DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0(sdm_cfg2));
pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG3,
DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8(sdm_cfg3));
pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG4, 0x00);
/* Add hardware recommended delay for correct PLL configuration */
if (pll_28nm->vco_delay)
udelay(pll_28nm->vco_delay);
pll_write(base + REG_DSI_28nm_PHY_PLL_REFCLK_CFG, refclk_cfg);
pll_write(base + REG_DSI_28nm_PHY_PLL_PWRGEN_CFG, 0x00);
pll_write(base + REG_DSI_28nm_PHY_PLL_VCOLPF_CFG, 0x31);
pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG0, sdm_cfg0);
pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG0, 0x12);
pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG6, 0x30);
pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG7, 0x00);
pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG8, 0x60);
pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG9, 0x00);
pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG10, cal_cfg10 & 0xff);
pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG11, cal_cfg11 & 0xff);
pll_write(base + REG_DSI_28nm_PHY_PLL_EFUSE_CFG, 0x20);
return 0;
}
static int dsi_pll_28nm_clk_is_enabled(struct clk_hw *hw)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
return pll_28nm_poll_for_ready(pll_28nm, POLL_MAX_READS,
POLL_TIMEOUT_US);
}
static unsigned long dsi_pll_28nm_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
void __iomem *base = pll_28nm->mmio;
u32 sdm0, doubler, sdm_byp_div;
u32 sdm_dc_off, sdm_freq_seed, sdm2, sdm3;
u32 ref_clk = VCO_REF_CLK_RATE;
unsigned long vco_rate;
VERB("parent_rate=%lu", parent_rate);
/* Check to see if the ref clk doubler is enabled */
doubler = pll_read(base + REG_DSI_28nm_PHY_PLL_REFCLK_CFG) &
DSI_28nm_PHY_PLL_REFCLK_CFG_DBLR;
ref_clk += (doubler * VCO_REF_CLK_RATE);
/* see if it is integer mode or sdm mode */
sdm0 = pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG0);
if (sdm0 & DSI_28nm_PHY_PLL_SDM_CFG0_BYP) {
/* integer mode */
sdm_byp_div = FIELD(
pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG0),
DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV) + 1;
vco_rate = ref_clk * sdm_byp_div;
} else {
/* sdm mode */
sdm_dc_off = FIELD(
pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG1),
DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET);
DBG("sdm_dc_off = %d", sdm_dc_off);
sdm2 = FIELD(pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG2),
DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0);
sdm3 = FIELD(pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG3),
DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8);
sdm_freq_seed = (sdm3 << 8) | sdm2;
DBG("sdm_freq_seed = %d", sdm_freq_seed);
vco_rate = (ref_clk * (sdm_dc_off + 1)) +
mult_frac(ref_clk, sdm_freq_seed, BIT(16));
DBG("vco rate = %lu", vco_rate);
}
DBG("returning vco rate = %lu", vco_rate);
return vco_rate;
}
static const struct clk_ops clk_ops_dsi_pll_28nm_vco = {
.round_rate = msm_dsi_pll_helper_clk_round_rate,
.set_rate = dsi_pll_28nm_clk_set_rate,
.recalc_rate = dsi_pll_28nm_clk_recalc_rate,
.prepare = msm_dsi_pll_helper_clk_prepare,
.unprepare = msm_dsi_pll_helper_clk_unprepare,
.is_enabled = dsi_pll_28nm_clk_is_enabled,
};
/*
* PLL Callbacks
*/
static int dsi_pll_28nm_enable_seq_hpm(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
struct device *dev = &pll_28nm->pdev->dev;
void __iomem *base = pll_28nm->mmio;
u32 max_reads = 5, timeout_us = 100;
bool locked;
u32 val;
int i;
DBG("id=%d", pll_28nm->id);
pll_28nm_software_reset(pll_28nm);
/*
* PLL power up sequence.
* Add necessary delays recommended by hardware.
*/
val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 1);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 200);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 600);
for (i = 0; i < 2; i++) {
/* DSI Uniphy lock detect setting */
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2,
0x0c, 100);
pll_write(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x0d);
/* poll for PLL ready status */
locked = pll_28nm_poll_for_ready(pll_28nm,
max_reads, timeout_us);
if (locked)
break;
pll_28nm_software_reset(pll_28nm);
/*
* PLL power up sequence.
* Add necessary delays recommended by hardware.
*/
val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 1);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 200);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 250);
val &= ~DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 200);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE;
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 600);
}
if (unlikely(!locked))
DRM_DEV_ERROR(dev, "DSI PLL lock failed\n");
else
DBG("DSI PLL Lock success");
return locked ? 0 : -EINVAL;
}
static int dsi_pll_28nm_enable_seq_lp(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
struct device *dev = &pll_28nm->pdev->dev;
void __iomem *base = pll_28nm->mmio;
bool locked;
u32 max_reads = 10, timeout_us = 50;
u32 val;
DBG("id=%d", pll_28nm->id);
pll_28nm_software_reset(pll_28nm);
/*
* PLL power up sequence.
* Add necessary delays recommended by hardware.
*/
pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_CAL_CFG1, 0x34, 500);
val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B;
pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B;
pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B |
DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE;
pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500);
/* DSI PLL toggle lock detect setting */
pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x04, 500);
pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x05, 512);
locked = pll_28nm_poll_for_ready(pll_28nm, max_reads, timeout_us);
if (unlikely(!locked))
DRM_DEV_ERROR(dev, "DSI PLL lock failed\n");
else
DBG("DSI PLL lock success");
return locked ? 0 : -EINVAL;
}
static void dsi_pll_28nm_disable_seq(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
DBG("id=%d", pll_28nm->id);
pll_write(pll_28nm->mmio + REG_DSI_28nm_PHY_PLL_GLB_CFG, 0x00);
}
static void dsi_pll_28nm_save_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state;
void __iomem *base = pll_28nm->mmio;
cached_state->postdiv3 =
pll_read(base + REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG);
cached_state->postdiv1 =
pll_read(base + REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG);
cached_state->byte_mux = pll_read(base + REG_DSI_28nm_PHY_PLL_VREG_CFG);
if (dsi_pll_28nm_clk_is_enabled(&pll->clk_hw))
cached_state->vco_rate = clk_hw_get_rate(&pll->clk_hw);
else
cached_state->vco_rate = 0;
}
static int dsi_pll_28nm_restore_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state;
void __iomem *base = pll_28nm->mmio;
int ret;
ret = dsi_pll_28nm_clk_set_rate(&pll->clk_hw,
cached_state->vco_rate, 0);
if (ret) {
DRM_DEV_ERROR(&pll_28nm->pdev->dev,
"restore vco rate failed. ret=%d\n", ret);
return ret;
}
pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG,
cached_state->postdiv3);
pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG,
cached_state->postdiv1);
pll_write(base + REG_DSI_28nm_PHY_PLL_VREG_CFG,
cached_state->byte_mux);
return 0;
}
static int dsi_pll_28nm_get_provider(struct msm_dsi_pll *pll,
struct clk **byte_clk_provider,
struct clk **pixel_clk_provider)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
if (byte_clk_provider)
*byte_clk_provider = pll_28nm->provided_clks[DSI_BYTE_PLL_CLK];
if (pixel_clk_provider)
*pixel_clk_provider =
pll_28nm->provided_clks[DSI_PIXEL_PLL_CLK];
return 0;
}
static void dsi_pll_28nm_destroy(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
int i;
msm_dsi_pll_helper_unregister_clks(pll_28nm->pdev,
pll_28nm->clks, pll_28nm->num_clks);
for (i = 0; i < NUM_PROVIDED_CLKS; i++)
pll_28nm->provided_clks[i] = NULL;
pll_28nm->num_clks = 0;
pll_28nm->clk_data.clks = NULL;
pll_28nm->clk_data.clk_num = 0;
}
static int pll_28nm_register(struct dsi_pll_28nm *pll_28nm)
{
char clk_name[32], parent1[32], parent2[32], vco_name[32];
struct clk_init_data vco_init = {
.parent_names = (const char *[]){ "xo" },
.num_parents = 1,
.name = vco_name,
.flags = CLK_IGNORE_UNUSED,
.ops = &clk_ops_dsi_pll_28nm_vco,
};
struct device *dev = &pll_28nm->pdev->dev;
struct clk **clks = pll_28nm->clks;
struct clk **provided_clks = pll_28nm->provided_clks;
int num = 0;
int ret;
DBG("%d", pll_28nm->id);
snprintf(vco_name, 32, "dsi%dvco_clk", pll_28nm->id);
pll_28nm->base.clk_hw.init = &vco_init;
clks[num++] = clk_register(dev, &pll_28nm->base.clk_hw);
snprintf(clk_name, 32, "dsi%danalog_postdiv_clk", pll_28nm->id);
snprintf(parent1, 32, "dsi%dvco_clk", pll_28nm->id);
clks[num++] = clk_register_divider(dev, clk_name,
parent1, CLK_SET_RATE_PARENT,
pll_28nm->mmio +
REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG,
0, 4, 0, NULL);
snprintf(clk_name, 32, "dsi%dindirect_path_div2_clk", pll_28nm->id);
snprintf(parent1, 32, "dsi%danalog_postdiv_clk", pll_28nm->id);
clks[num++] = clk_register_fixed_factor(dev, clk_name,
parent1, CLK_SET_RATE_PARENT,
1, 2);
snprintf(clk_name, 32, "dsi%dpll", pll_28nm->id);
snprintf(parent1, 32, "dsi%dvco_clk", pll_28nm->id);
clks[num++] = provided_clks[DSI_PIXEL_PLL_CLK] =
clk_register_divider(dev, clk_name,
parent1, 0, pll_28nm->mmio +
REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG,
0, 8, 0, NULL);
snprintf(clk_name, 32, "dsi%dbyte_mux", pll_28nm->id);
snprintf(parent1, 32, "dsi%dvco_clk", pll_28nm->id);
snprintf(parent2, 32, "dsi%dindirect_path_div2_clk", pll_28nm->id);
clks[num++] = clk_register_mux(dev, clk_name,
((const char *[]){
parent1, parent2
}), 2, CLK_SET_RATE_PARENT, pll_28nm->mmio +
REG_DSI_28nm_PHY_PLL_VREG_CFG, 1, 1, 0, NULL);
snprintf(clk_name, 32, "dsi%dpllbyte", pll_28nm->id);
snprintf(parent1, 32, "dsi%dbyte_mux", pll_28nm->id);
clks[num++] = provided_clks[DSI_BYTE_PLL_CLK] =
clk_register_fixed_factor(dev, clk_name,
parent1, CLK_SET_RATE_PARENT, 1, 4);
pll_28nm->num_clks = num;
pll_28nm->clk_data.clk_num = NUM_PROVIDED_CLKS;
pll_28nm->clk_data.clks = provided_clks;
ret = of_clk_add_provider(dev->of_node,
of_clk_src_onecell_get, &pll_28nm->clk_data);
if (ret) {
DRM_DEV_ERROR(dev, "failed to register clk provider: %d\n", ret);
return ret;
}
return 0;
}
struct msm_dsi_pll *msm_dsi_pll_28nm_init(struct platform_device *pdev,
enum msm_dsi_phy_type type, int id)
{
struct dsi_pll_28nm *pll_28nm;
struct msm_dsi_pll *pll;
int ret;
if (!pdev)
return ERR_PTR(-ENODEV);
pll_28nm = devm_kzalloc(&pdev->dev, sizeof(*pll_28nm), GFP_KERNEL);
if (!pll_28nm)
return ERR_PTR(-ENOMEM);
pll_28nm->pdev = pdev;
pll_28nm->id = id;
pll_28nm->mmio = msm_ioremap(pdev, "dsi_pll", "DSI_PLL");
if (IS_ERR_OR_NULL(pll_28nm->mmio)) {
DRM_DEV_ERROR(&pdev->dev, "%s: failed to map pll base\n", __func__);
return ERR_PTR(-ENOMEM);
}
pll = &pll_28nm->base;
pll->min_rate = VCO_MIN_RATE;
pll->max_rate = VCO_MAX_RATE;
pll->get_provider = dsi_pll_28nm_get_provider;
pll->destroy = dsi_pll_28nm_destroy;
pll->disable_seq = dsi_pll_28nm_disable_seq;
pll->save_state = dsi_pll_28nm_save_state;
pll->restore_state = dsi_pll_28nm_restore_state;
if (type == MSM_DSI_PHY_28NM_HPM) {
pll_28nm->vco_delay = 1;
pll->en_seq_cnt = 3;
pll->enable_seqs[0] = dsi_pll_28nm_enable_seq_hpm;
pll->enable_seqs[1] = dsi_pll_28nm_enable_seq_hpm;
pll->enable_seqs[2] = dsi_pll_28nm_enable_seq_hpm;
} else if (type == MSM_DSI_PHY_28NM_LP) {
pll_28nm->vco_delay = 1000;
pll->en_seq_cnt = 1;
pll->enable_seqs[0] = dsi_pll_28nm_enable_seq_lp;
} else {
DRM_DEV_ERROR(&pdev->dev, "phy type (%d) is not 28nm\n", type);
return ERR_PTR(-EINVAL);
}
ret = pll_28nm_register(pll_28nm);
if (ret) {
DRM_DEV_ERROR(&pdev->dev, "failed to register PLL: %d\n", ret);
return ERR_PTR(ret);
}
return pll;
}
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
*/
#include <linux/clk-provider.h>
#include "dsi_pll.h"
#include "dsi.xml.h"
/*
* DSI PLL 28nm (8960/A family) - clock diagram (eg: DSI1):
*
*
* +------+
* dsi1vco_clk ----o-----| DIV1 |---dsi1pllbit (not exposed as clock)
* F * byte_clk | +------+
* | bit clock divider (F / 8)
* |
* | +------+
* o-----| DIV2 |---dsi0pllbyte---o---> To byte RCG
* | +------+ | (sets parent rate)
* | byte clock divider (F) |
* | |
* | o---> To esc RCG
* | (doesn't set parent rate)
* |
* | +------+
* o-----| DIV3 |----dsi0pll------o---> To dsi RCG
* +------+ | (sets parent rate)
* dsi clock divider (F * magic) |
* |
* o---> To pixel rcg
* (doesn't set parent rate)
*/
#define POLL_MAX_READS 8000
#define POLL_TIMEOUT_US 1
#define NUM_PROVIDED_CLKS 2
#define VCO_REF_CLK_RATE 27000000
#define VCO_MIN_RATE 600000000
#define VCO_MAX_RATE 1200000000
#define DSI_BYTE_PLL_CLK 0
#define DSI_PIXEL_PLL_CLK 1
#define VCO_PREF_DIV_RATIO 27
struct pll_28nm_cached_state {
unsigned long vco_rate;
u8 postdiv3;
u8 postdiv2;
u8 postdiv1;
};
struct clk_bytediv {
struct clk_hw hw;
void __iomem *reg;
};
struct dsi_pll_28nm {
struct msm_dsi_pll base;
int id;
struct platform_device *pdev;
void __iomem *mmio;
/* custom byte clock divider */
struct clk_bytediv *bytediv;
/* private clocks: */
struct clk *clks[NUM_DSI_CLOCKS_MAX];
u32 num_clks;
/* clock-provider: */
struct clk *provided_clks[NUM_PROVIDED_CLKS];
struct clk_onecell_data clk_data;
struct pll_28nm_cached_state cached_state;
};
#define to_pll_28nm(x) container_of(x, struct dsi_pll_28nm, base)
static bool pll_28nm_poll_for_ready(struct dsi_pll_28nm *pll_28nm,
int nb_tries, int timeout_us)
{
bool pll_locked = false;
u32 val;
while (nb_tries--) {
val = pll_read(pll_28nm->mmio + REG_DSI_28nm_8960_PHY_PLL_RDY);
pll_locked = !!(val & DSI_28nm_8960_PHY_PLL_RDY_PLL_RDY);
if (pll_locked)
break;
udelay(timeout_us);
}
DBG("DSI PLL is %slocked", pll_locked ? "" : "*not* ");
return pll_locked;
}
/*
* Clock Callbacks
*/
static int dsi_pll_28nm_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
void __iomem *base = pll_28nm->mmio;
u32 val, temp, fb_divider;
DBG("rate=%lu, parent's=%lu", rate, parent_rate);
temp = rate / 10;
val = VCO_REF_CLK_RATE / 10;
fb_divider = (temp * VCO_PREF_DIV_RATIO) / val;
fb_divider = fb_divider / 2 - 1;
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_1,
fb_divider & 0xff);
val = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_2);
val |= (fb_divider >> 8) & 0x07;
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_2,
val);
val = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_3);
val |= (VCO_PREF_DIV_RATIO - 1) & 0x3f;
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_3,
val);
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_6,
0xf);
val = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8);
val |= 0x7 << 4;
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8,
val);
return 0;
}
static int dsi_pll_28nm_clk_is_enabled(struct clk_hw *hw)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
return pll_28nm_poll_for_ready(pll_28nm, POLL_MAX_READS,
POLL_TIMEOUT_US);
}
static unsigned long dsi_pll_28nm_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
void __iomem *base = pll_28nm->mmio;
unsigned long vco_rate;
u32 status, fb_divider, temp, ref_divider;
VERB("parent_rate=%lu", parent_rate);
status = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_0);
if (status & DSI_28nm_8960_PHY_PLL_CTRL_0_ENABLE) {
fb_divider = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_1);
fb_divider &= 0xff;
temp = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_2) & 0x07;
fb_divider = (temp << 8) | fb_divider;
fb_divider += 1;
ref_divider = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_3);
ref_divider &= 0x3f;
ref_divider += 1;
/* multiply by 2 */
vco_rate = (parent_rate / ref_divider) * fb_divider * 2;
} else {
vco_rate = 0;
}
DBG("returning vco rate = %lu", vco_rate);
return vco_rate;
}
static const struct clk_ops clk_ops_dsi_pll_28nm_vco = {
.round_rate = msm_dsi_pll_helper_clk_round_rate,
.set_rate = dsi_pll_28nm_clk_set_rate,
.recalc_rate = dsi_pll_28nm_clk_recalc_rate,
.prepare = msm_dsi_pll_helper_clk_prepare,
.unprepare = msm_dsi_pll_helper_clk_unprepare,
.is_enabled = dsi_pll_28nm_clk_is_enabled,
};
/*
* Custom byte clock divier clk_ops
*
* This clock is the entry point to configuring the PLL. The user (dsi host)
* will set this clock's rate to the desired byte clock rate. The VCO lock
* frequency is a multiple of the byte clock rate. The multiplication factor
* (shown as F in the diagram above) is a function of the byte clock rate.
*
* This custom divider clock ensures that its parent (VCO) is set to the
* desired rate, and that the byte clock postdivider (POSTDIV2) is configured
* accordingly
*/
#define to_clk_bytediv(_hw) container_of(_hw, struct clk_bytediv, hw)
static unsigned long clk_bytediv_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_bytediv *bytediv = to_clk_bytediv(hw);
unsigned int div;
div = pll_read(bytediv->reg) & 0xff;
return parent_rate / (div + 1);
}
/* find multiplication factor(wrt byte clock) at which the VCO should be set */
static unsigned int get_vco_mul_factor(unsigned long byte_clk_rate)
{
unsigned long bit_mhz;
/* convert to bit clock in Mhz */
bit_mhz = (byte_clk_rate * 8) / 1000000;
if (bit_mhz < 125)
return 64;
else if (bit_mhz < 250)
return 32;
else if (bit_mhz < 600)
return 16;
else
return 8;
}
static long clk_bytediv_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
unsigned long best_parent;
unsigned int factor;
factor = get_vco_mul_factor(rate);
best_parent = rate * factor;
*prate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent);
return *prate / factor;
}
static int clk_bytediv_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_bytediv *bytediv = to_clk_bytediv(hw);
u32 val;
unsigned int factor;
factor = get_vco_mul_factor(rate);
val = pll_read(bytediv->reg);
val |= (factor - 1) & 0xff;
pll_write(bytediv->reg, val);
return 0;
}
/* Our special byte clock divider ops */
static const struct clk_ops clk_bytediv_ops = {
.round_rate = clk_bytediv_round_rate,
.set_rate = clk_bytediv_set_rate,
.recalc_rate = clk_bytediv_recalc_rate,
};
/*
* PLL Callbacks
*/
static int dsi_pll_28nm_enable_seq(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
struct device *dev = &pll_28nm->pdev->dev;
void __iomem *base = pll_28nm->mmio;
bool locked;
unsigned int bit_div, byte_div;
int max_reads = 1000, timeout_us = 100;
u32 val;
DBG("id=%d", pll_28nm->id);
/*
* before enabling the PLL, configure the bit clock divider since we
* don't expose it as a clock to the outside world
* 1: read back the byte clock divider that should already be set
* 2: divide by 8 to get bit clock divider
* 3: write it to POSTDIV1
*/
val = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_9);
byte_div = val + 1;
bit_div = byte_div / 8;
val = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8);
val &= ~0xf;
val |= (bit_div - 1);
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8, val);
/* enable the PLL */
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_0,
DSI_28nm_8960_PHY_PLL_CTRL_0_ENABLE);
locked = pll_28nm_poll_for_ready(pll_28nm, max_reads, timeout_us);
if (unlikely(!locked))
DRM_DEV_ERROR(dev, "DSI PLL lock failed\n");
else
DBG("DSI PLL lock success");
return locked ? 0 : -EINVAL;
}
static void dsi_pll_28nm_disable_seq(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
DBG("id=%d", pll_28nm->id);
pll_write(pll_28nm->mmio + REG_DSI_28nm_8960_PHY_PLL_CTRL_0, 0x00);
}
static void dsi_pll_28nm_save_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state;
void __iomem *base = pll_28nm->mmio;
cached_state->postdiv3 =
pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_10);
cached_state->postdiv2 =
pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_9);
cached_state->postdiv1 =
pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8);
cached_state->vco_rate = clk_hw_get_rate(&pll->clk_hw);
}
static int dsi_pll_28nm_restore_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state;
void __iomem *base = pll_28nm->mmio;
int ret;
ret = dsi_pll_28nm_clk_set_rate(&pll->clk_hw,
cached_state->vco_rate, 0);
if (ret) {
DRM_DEV_ERROR(&pll_28nm->pdev->dev,
"restore vco rate failed. ret=%d\n", ret);
return ret;
}
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_10,
cached_state->postdiv3);
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_9,
cached_state->postdiv2);
pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8,
cached_state->postdiv1);
return 0;
}
static int dsi_pll_28nm_get_provider(struct msm_dsi_pll *pll,
struct clk **byte_clk_provider,
struct clk **pixel_clk_provider)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
if (byte_clk_provider)
*byte_clk_provider = pll_28nm->provided_clks[DSI_BYTE_PLL_CLK];
if (pixel_clk_provider)
*pixel_clk_provider =
pll_28nm->provided_clks[DSI_PIXEL_PLL_CLK];
return 0;
}
static void dsi_pll_28nm_destroy(struct msm_dsi_pll *pll)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
msm_dsi_pll_helper_unregister_clks(pll_28nm->pdev,
pll_28nm->clks, pll_28nm->num_clks);
}
static int pll_28nm_register(struct dsi_pll_28nm *pll_28nm)
{
char *clk_name, *parent_name, *vco_name;
struct clk_init_data vco_init = {
.parent_names = (const char *[]){ "pxo" },
.num_parents = 1,
.flags = CLK_IGNORE_UNUSED,
.ops = &clk_ops_dsi_pll_28nm_vco,
};
struct device *dev = &pll_28nm->pdev->dev;
struct clk **clks = pll_28nm->clks;
struct clk **provided_clks = pll_28nm->provided_clks;
struct clk_bytediv *bytediv;
struct clk_init_data bytediv_init = { };
int ret, num = 0;
DBG("%d", pll_28nm->id);
bytediv = devm_kzalloc(dev, sizeof(*bytediv), GFP_KERNEL);
if (!bytediv)
return -ENOMEM;
vco_name = devm_kzalloc(dev, 32, GFP_KERNEL);
if (!vco_name)
return -ENOMEM;
parent_name = devm_kzalloc(dev, 32, GFP_KERNEL);
if (!parent_name)
return -ENOMEM;
clk_name = devm_kzalloc(dev, 32, GFP_KERNEL);
if (!clk_name)
return -ENOMEM;
pll_28nm->bytediv = bytediv;
snprintf(vco_name, 32, "dsi%dvco_clk", pll_28nm->id);
vco_init.name = vco_name;
pll_28nm->base.clk_hw.init = &vco_init;
clks[num++] = clk_register(dev, &pll_28nm->base.clk_hw);
/* prepare and register bytediv */
bytediv->hw.init = &bytediv_init;
bytediv->reg = pll_28nm->mmio + REG_DSI_28nm_8960_PHY_PLL_CTRL_9;
snprintf(parent_name, 32, "dsi%dvco_clk", pll_28nm->id);
snprintf(clk_name, 32, "dsi%dpllbyte", pll_28nm->id);
bytediv_init.name = clk_name;
bytediv_init.ops = &clk_bytediv_ops;
bytediv_init.flags = CLK_SET_RATE_PARENT;
bytediv_init.parent_names = (const char * const *) &parent_name;
bytediv_init.num_parents = 1;
/* DIV2 */
clks[num++] = provided_clks[DSI_BYTE_PLL_CLK] =
clk_register(dev, &bytediv->hw);
snprintf(clk_name, 32, "dsi%dpll", pll_28nm->id);
/* DIV3 */
clks[num++] = provided_clks[DSI_PIXEL_PLL_CLK] =
clk_register_divider(dev, clk_name,
parent_name, 0, pll_28nm->mmio +
REG_DSI_28nm_8960_PHY_PLL_CTRL_10,
0, 8, 0, NULL);
pll_28nm->num_clks = num;
pll_28nm->clk_data.clk_num = NUM_PROVIDED_CLKS;
pll_28nm->clk_data.clks = provided_clks;
ret = of_clk_add_provider(dev->of_node,
of_clk_src_onecell_get, &pll_28nm->clk_data);
if (ret) {
DRM_DEV_ERROR(dev, "failed to register clk provider: %d\n", ret);
return ret;
}
return 0;
}
struct msm_dsi_pll *msm_dsi_pll_28nm_8960_init(struct platform_device *pdev,
int id)
{
struct dsi_pll_28nm *pll_28nm;
struct msm_dsi_pll *pll;
int ret;
if (!pdev)
return ERR_PTR(-ENODEV);
pll_28nm = devm_kzalloc(&pdev->dev, sizeof(*pll_28nm), GFP_KERNEL);
if (!pll_28nm)
return ERR_PTR(-ENOMEM);
pll_28nm->pdev = pdev;
pll_28nm->id = id + 1;
pll_28nm->mmio = msm_ioremap(pdev, "dsi_pll", "DSI_PLL");
if (IS_ERR_OR_NULL(pll_28nm->mmio)) {
DRM_DEV_ERROR(&pdev->dev, "%s: failed to map pll base\n", __func__);
return ERR_PTR(-ENOMEM);
}
pll = &pll_28nm->base;
pll->min_rate = VCO_MIN_RATE;
pll->max_rate = VCO_MAX_RATE;
pll->get_provider = dsi_pll_28nm_get_provider;
pll->destroy = dsi_pll_28nm_destroy;
pll->disable_seq = dsi_pll_28nm_disable_seq;
pll->save_state = dsi_pll_28nm_save_state;
pll->restore_state = dsi_pll_28nm_restore_state;
pll->en_seq_cnt = 1;
pll->enable_seqs[0] = dsi_pll_28nm_enable_seq;
ret = pll_28nm_register(pll_28nm);
if (ret) {
DRM_DEV_ERROR(&pdev->dev, "failed to register PLL: %d\n", ret);
return ERR_PTR(ret);
}
return pll;
}
/*
* SPDX-License-Identifier: GPL-2.0
* Copyright (c) 2018, The Linux Foundation
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/iopoll.h>
#include "dsi_pll.h"
#include "dsi.xml.h"
/*
* DSI PLL 7nm - clock diagram (eg: DSI0): TODO: updated CPHY diagram
*
* dsi0_pll_out_div_clk dsi0_pll_bit_clk
* | |
* | |
* +---------+ | +----------+ | +----+
* dsi0vco_clk ---| out_div |--o--| divl_3_0 |--o--| /8 |-- dsi0_phy_pll_out_byteclk
* +---------+ | +----------+ | +----+
* | |
* | | dsi0_pll_by_2_bit_clk
* | | |
* | | +----+ | |\ dsi0_pclk_mux
* | |--| /2 |--o--| \ |
* | | +----+ | \ | +---------+
* | --------------| |--o--| div_7_4 |-- dsi0_phy_pll_out_dsiclk
* |------------------------------| / +---------+
* | +-----+ | /
* -----------| /4? |--o----------|/
* +-----+ | |
* | |dsiclk_sel
* |
* dsi0_pll_post_out_div_clk
*/
#define DSI_BYTE_PLL_CLK 0
#define DSI_PIXEL_PLL_CLK 1
#define NUM_PROVIDED_CLKS 2
#define VCO_REF_CLK_RATE 19200000
struct dsi_pll_regs {
u32 pll_prop_gain_rate;
u32 pll_lockdet_rate;
u32 decimal_div_start;
u32 frac_div_start_low;
u32 frac_div_start_mid;
u32 frac_div_start_high;
u32 pll_clock_inverters;
u32 ssc_stepsize_low;
u32 ssc_stepsize_high;
u32 ssc_div_per_low;
u32 ssc_div_per_high;
u32 ssc_adjper_low;
u32 ssc_adjper_high;
u32 ssc_control;
};
struct dsi_pll_config {
u32 ref_freq;
bool div_override;
u32 output_div;
bool ignore_frac;
bool disable_prescaler;
bool enable_ssc;
bool ssc_center;
u32 dec_bits;
u32 frac_bits;
u32 lock_timer;
u32 ssc_freq;
u32 ssc_offset;
u32 ssc_adj_per;
u32 thresh_cycles;
u32 refclk_cycles;
};
struct pll_7nm_cached_state {
unsigned long vco_rate;
u8 bit_clk_div;
u8 pix_clk_div;
u8 pll_out_div;
u8 pll_mux;
};
struct dsi_pll_7nm {
struct msm_dsi_pll base;
int id;
struct platform_device *pdev;
void __iomem *phy_cmn_mmio;
void __iomem *mmio;
u64 vco_ref_clk_rate;
u64 vco_current_rate;
/* protects REG_DSI_7nm_PHY_CMN_CLK_CFG0 register */
spinlock_t postdiv_lock;
int vco_delay;
struct dsi_pll_config pll_configuration;
struct dsi_pll_regs reg_setup;
/* private clocks: */
struct clk_hw *out_div_clk_hw;
struct clk_hw *bit_clk_hw;
struct clk_hw *byte_clk_hw;
struct clk_hw *by_2_bit_clk_hw;
struct clk_hw *post_out_div_clk_hw;
struct clk_hw *pclk_mux_hw;
struct clk_hw *out_dsiclk_hw;
/* clock-provider: */
struct clk_hw_onecell_data *hw_data;
struct pll_7nm_cached_state cached_state;
enum msm_dsi_phy_usecase uc;
struct dsi_pll_7nm *slave;
};
#define to_pll_7nm(x) container_of(x, struct dsi_pll_7nm, base)
/*
* Global list of private DSI PLL struct pointers. We need this for Dual DSI
* mode, where the master PLL's clk_ops needs access the slave's private data
*/
static struct dsi_pll_7nm *pll_7nm_list[DSI_MAX];
static void dsi_pll_setup_config(struct dsi_pll_7nm *pll)
{
struct dsi_pll_config *config = &pll->pll_configuration;
config->ref_freq = pll->vco_ref_clk_rate;
config->output_div = 1;
config->dec_bits = 8;
config->frac_bits = 18;
config->lock_timer = 64;
config->ssc_freq = 31500;
config->ssc_offset = 4800;
config->ssc_adj_per = 2;
config->thresh_cycles = 32;
config->refclk_cycles = 256;
config->div_override = false;
config->ignore_frac = false;
config->disable_prescaler = false;
/* TODO: ssc enable */
config->enable_ssc = false;
config->ssc_center = 0;
}
static void dsi_pll_calc_dec_frac(struct dsi_pll_7nm *pll)
{
struct dsi_pll_config *config = &pll->pll_configuration;
struct dsi_pll_regs *regs = &pll->reg_setup;
u64 fref = pll->vco_ref_clk_rate;
u64 pll_freq;
u64 divider;
u64 dec, dec_multiple;
u32 frac;
u64 multiplier;
pll_freq = pll->vco_current_rate;
if (config->disable_prescaler)
divider = fref;
else
divider = fref * 2;
multiplier = 1 << config->frac_bits;
dec_multiple = div_u64(pll_freq * multiplier, divider);
div_u64_rem(dec_multiple, multiplier, &frac);
dec = div_u64(dec_multiple, multiplier);
if (pll->base.type != MSM_DSI_PHY_7NM_V4_1)
regs->pll_clock_inverters = 0x28;
else if (pll_freq <= 1000000000ULL)
regs->pll_clock_inverters = 0xa0;
else if (pll_freq <= 2500000000ULL)
regs->pll_clock_inverters = 0x20;
else if (pll_freq <= 3020000000ULL)
regs->pll_clock_inverters = 0x00;
else
regs->pll_clock_inverters = 0x40;
regs->pll_lockdet_rate = config->lock_timer;
regs->decimal_div_start = dec;
regs->frac_div_start_low = (frac & 0xff);
regs->frac_div_start_mid = (frac & 0xff00) >> 8;
regs->frac_div_start_high = (frac & 0x30000) >> 16;
}
#define SSC_CENTER BIT(0)
#define SSC_EN BIT(1)
static void dsi_pll_calc_ssc(struct dsi_pll_7nm *pll)
{
struct dsi_pll_config *config = &pll->pll_configuration;
struct dsi_pll_regs *regs = &pll->reg_setup;
u32 ssc_per;
u32 ssc_mod;
u64 ssc_step_size;
u64 frac;
if (!config->enable_ssc) {
DBG("SSC not enabled\n");
return;
}
ssc_per = DIV_ROUND_CLOSEST(config->ref_freq, config->ssc_freq) / 2 - 1;
ssc_mod = (ssc_per + 1) % (config->ssc_adj_per + 1);
ssc_per -= ssc_mod;
frac = regs->frac_div_start_low |
(regs->frac_div_start_mid << 8) |
(regs->frac_div_start_high << 16);
ssc_step_size = regs->decimal_div_start;
ssc_step_size *= (1 << config->frac_bits);
ssc_step_size += frac;
ssc_step_size *= config->ssc_offset;
ssc_step_size *= (config->ssc_adj_per + 1);
ssc_step_size = div_u64(ssc_step_size, (ssc_per + 1));
ssc_step_size = DIV_ROUND_CLOSEST_ULL(ssc_step_size, 1000000);
regs->ssc_div_per_low = ssc_per & 0xFF;
regs->ssc_div_per_high = (ssc_per & 0xFF00) >> 8;
regs->ssc_stepsize_low = (u32)(ssc_step_size & 0xFF);
regs->ssc_stepsize_high = (u32)((ssc_step_size & 0xFF00) >> 8);
regs->ssc_adjper_low = config->ssc_adj_per & 0xFF;
regs->ssc_adjper_high = (config->ssc_adj_per & 0xFF00) >> 8;
regs->ssc_control = config->ssc_center ? SSC_CENTER : 0;
pr_debug("SCC: Dec:%d, frac:%llu, frac_bits:%d\n",
regs->decimal_div_start, frac, config->frac_bits);
pr_debug("SSC: div_per:0x%X, stepsize:0x%X, adjper:0x%X\n",
ssc_per, (u32)ssc_step_size, config->ssc_adj_per);
}
static void dsi_pll_ssc_commit(struct dsi_pll_7nm *pll)
{
void __iomem *base = pll->mmio;
struct dsi_pll_regs *regs = &pll->reg_setup;
if (pll->pll_configuration.enable_ssc) {
pr_debug("SSC is enabled\n");
pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_STEPSIZE_LOW_1,
regs->ssc_stepsize_low);
pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_STEPSIZE_HIGH_1,
regs->ssc_stepsize_high);
pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_DIV_PER_LOW_1,
regs->ssc_div_per_low);
pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_DIV_PER_HIGH_1,
regs->ssc_div_per_high);
pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_ADJPER_LOW_1,
regs->ssc_adjper_low);
pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_ADJPER_HIGH_1,
regs->ssc_adjper_high);
pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_CONTROL,
SSC_EN | regs->ssc_control);
}
}
static void dsi_pll_config_hzindep_reg(struct dsi_pll_7nm *pll)
{
void __iomem *base = pll->mmio;
u8 analog_controls_five_1 = 0x01, vco_config_1 = 0x00;
if (pll->base.type == MSM_DSI_PHY_7NM_V4_1) {
if (pll->vco_current_rate >= 3100000000ULL)
analog_controls_five_1 = 0x03;
if (pll->vco_current_rate < 1520000000ULL)
vco_config_1 = 0x08;
else if (pll->vco_current_rate < 2990000000ULL)
vco_config_1 = 0x01;
}
pll_write(base + REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_FIVE_1,
analog_controls_five_1);
pll_write(base + REG_DSI_7nm_PHY_PLL_VCO_CONFIG_1, vco_config_1);
pll_write(base + REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_FIVE, 0x01);
pll_write(base + REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_TWO, 0x03);
pll_write(base + REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_THREE, 0x00);
pll_write(base + REG_DSI_7nm_PHY_PLL_DSM_DIVIDER, 0x00);
pll_write(base + REG_DSI_7nm_PHY_PLL_FEEDBACK_DIVIDER, 0x4e);
pll_write(base + REG_DSI_7nm_PHY_PLL_CALIBRATION_SETTINGS, 0x40);
pll_write(base + REG_DSI_7nm_PHY_PLL_BAND_SEL_CAL_SETTINGS_THREE, 0xba);
pll_write(base + REG_DSI_7nm_PHY_PLL_FREQ_DETECT_SETTINGS_ONE, 0x0c);
pll_write(base + REG_DSI_7nm_PHY_PLL_OUTDIV, 0x00);
pll_write(base + REG_DSI_7nm_PHY_PLL_CORE_OVERRIDE, 0x00);
pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_DIGITAL_TIMERS_TWO, 0x08);
pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_PROP_GAIN_RATE_1, 0x0a);
pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_BAND_SEL_RATE_1, 0xc0);
pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_INT_GAIN_IFILT_BAND_1, 0x84);
pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_INT_GAIN_IFILT_BAND_1, 0x82);
pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_FL_INT_GAIN_PFILT_BAND_1, 0x4c);
pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_LOCK_OVERRIDE, 0x80);
pll_write(base + REG_DSI_7nm_PHY_PLL_PFILT, 0x29);
pll_write(base + REG_DSI_7nm_PHY_PLL_PFILT, 0x2f);
pll_write(base + REG_DSI_7nm_PHY_PLL_IFILT, 0x2a);
pll_write(base + REG_DSI_7nm_PHY_PLL_IFILT,
pll->base.type == MSM_DSI_PHY_7NM_V4_1 ? 0x3f : 0x22);
if (pll->base.type == MSM_DSI_PHY_7NM_V4_1) {
pll_write(base + REG_DSI_7nm_PHY_PLL_PERF_OPTIMIZE, 0x22);
if (pll->slave)
pll_write(pll->slave->mmio + REG_DSI_7nm_PHY_PLL_PERF_OPTIMIZE, 0x22);
}
}
static void dsi_pll_commit(struct dsi_pll_7nm *pll)
{
void __iomem *base = pll->mmio;
struct dsi_pll_regs *reg = &pll->reg_setup;
pll_write(base + REG_DSI_7nm_PHY_PLL_CORE_INPUT_OVERRIDE, 0x12);
pll_write(base + REG_DSI_7nm_PHY_PLL_DECIMAL_DIV_START_1, reg->decimal_div_start);
pll_write(base + REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_LOW_1, reg->frac_div_start_low);
pll_write(base + REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_MID_1, reg->frac_div_start_mid);
pll_write(base + REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_HIGH_1, reg->frac_div_start_high);
pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_LOCKDET_RATE_1, reg->pll_lockdet_rate);
pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_LOCK_DELAY, 0x06);
pll_write(base + REG_DSI_7nm_PHY_PLL_CMODE_1, 0x10); /* TODO: 0x00 for CPHY */
pll_write(base + REG_DSI_7nm_PHY_PLL_CLOCK_INVERTERS, reg->pll_clock_inverters);
}
static int dsi_pll_7nm_vco_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll);
DBG("DSI PLL%d rate=%lu, parent's=%lu", pll_7nm->id, rate,
parent_rate);
pll_7nm->vco_current_rate = rate;
pll_7nm->vco_ref_clk_rate = VCO_REF_CLK_RATE;
dsi_pll_setup_config(pll_7nm);
dsi_pll_calc_dec_frac(pll_7nm);
dsi_pll_calc_ssc(pll_7nm);
dsi_pll_commit(pll_7nm);
dsi_pll_config_hzindep_reg(pll_7nm);
dsi_pll_ssc_commit(pll_7nm);
/* flush, ensure all register writes are done*/
wmb();
return 0;
}
static int dsi_pll_7nm_lock_status(struct dsi_pll_7nm *pll)
{
int rc;
u32 status = 0;
u32 const delay_us = 100;
u32 const timeout_us = 5000;
rc = readl_poll_timeout_atomic(pll->mmio +
REG_DSI_7nm_PHY_PLL_COMMON_STATUS_ONE,
status,
((status & BIT(0)) > 0),
delay_us,
timeout_us);
if (rc)
pr_err("DSI PLL(%d) lock failed, status=0x%08x\n",
pll->id, status);
return rc;
}
static void dsi_pll_disable_pll_bias(struct dsi_pll_7nm *pll)
{
u32 data = pll_read(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CTRL_0);
pll_write(pll->mmio + REG_DSI_7nm_PHY_PLL_SYSTEM_MUXES, 0);
pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CTRL_0, data & ~BIT(5));
ndelay(250);
}
static void dsi_pll_enable_pll_bias(struct dsi_pll_7nm *pll)
{
u32 data = pll_read(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CTRL_0);
pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CTRL_0, data | BIT(5));
pll_write(pll->mmio + REG_DSI_7nm_PHY_PLL_SYSTEM_MUXES, 0xc0);
ndelay(250);
}
static void dsi_pll_disable_global_clk(struct dsi_pll_7nm *pll)
{
u32 data;
data = pll_read(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CLK_CFG1);
pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CLK_CFG1, data & ~BIT(5));
}
static void dsi_pll_enable_global_clk(struct dsi_pll_7nm *pll)
{
u32 data;
pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CTRL_3, 0x04);
data = pll_read(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CLK_CFG1);
pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CLK_CFG1,
data | BIT(5) | BIT(4));
}
static void dsi_pll_phy_dig_reset(struct dsi_pll_7nm *pll)
{
/*
* Reset the PHY digital domain. This would be needed when
* coming out of a CX or analog rail power collapse while
* ensuring that the pads maintain LP00 or LP11 state
*/
pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_GLBL_DIGTOP_SPARE4, BIT(0));
wmb(); /* Ensure that the reset is deasserted */
pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_GLBL_DIGTOP_SPARE4, 0x0);
wmb(); /* Ensure that the reset is deasserted */
}
static int dsi_pll_7nm_vco_prepare(struct clk_hw *hw)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll);
int rc;
dsi_pll_enable_pll_bias(pll_7nm);
if (pll_7nm->slave)
dsi_pll_enable_pll_bias(pll_7nm->slave);
/* Start PLL */
pll_write(pll_7nm->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_PLL_CNTRL, 0x01);
/*
* ensure all PLL configurations are written prior to checking
* for PLL lock.
*/
wmb();
/* Check for PLL lock */
rc = dsi_pll_7nm_lock_status(pll_7nm);
if (rc) {
pr_err("PLL(%d) lock failed\n", pll_7nm->id);
goto error;
}
pll->pll_on = true;
/*
* assert power on reset for PHY digital in case the PLL is
* enabled after CX of analog domain power collapse. This needs
* to be done before enabling the global clk.
*/
dsi_pll_phy_dig_reset(pll_7nm);
if (pll_7nm->slave)
dsi_pll_phy_dig_reset(pll_7nm->slave);
dsi_pll_enable_global_clk(pll_7nm);
if (pll_7nm->slave)
dsi_pll_enable_global_clk(pll_7nm->slave);
error:
return rc;
}
static void dsi_pll_disable_sub(struct dsi_pll_7nm *pll)
{
pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_RBUF_CTRL, 0);
dsi_pll_disable_pll_bias(pll);
}
static void dsi_pll_7nm_vco_unprepare(struct clk_hw *hw)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll);
/*
* To avoid any stray glitches while abruptly powering down the PLL
* make sure to gate the clock using the clock enable bit before
* powering down the PLL
*/
dsi_pll_disable_global_clk(pll_7nm);
pll_write(pll_7nm->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_PLL_CNTRL, 0);
dsi_pll_disable_sub(pll_7nm);
if (pll_7nm->slave) {
dsi_pll_disable_global_clk(pll_7nm->slave);
dsi_pll_disable_sub(pll_7nm->slave);
}
/* flush, ensure all register writes are done */
wmb();
pll->pll_on = false;
}
static unsigned long dsi_pll_7nm_vco_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll);
struct dsi_pll_config *config = &pll_7nm->pll_configuration;
void __iomem *base = pll_7nm->mmio;
u64 ref_clk = pll_7nm->vco_ref_clk_rate;
u64 vco_rate = 0x0;
u64 multiplier;
u32 frac;
u32 dec;
u64 pll_freq, tmp64;
dec = pll_read(base + REG_DSI_7nm_PHY_PLL_DECIMAL_DIV_START_1);
dec &= 0xff;
frac = pll_read(base + REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_LOW_1);
frac |= ((pll_read(base + REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_MID_1) &
0xff) << 8);
frac |= ((pll_read(base + REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_HIGH_1) &
0x3) << 16);
/*
* TODO:
* 1. Assumes prescaler is disabled
*/
multiplier = 1 << config->frac_bits;
pll_freq = dec * (ref_clk * 2);
tmp64 = (ref_clk * 2 * frac);
pll_freq += div_u64(tmp64, multiplier);
vco_rate = pll_freq;
DBG("DSI PLL%d returning vco rate = %lu, dec = %x, frac = %x",
pll_7nm->id, (unsigned long)vco_rate, dec, frac);
return (unsigned long)vco_rate;
}
static const struct clk_ops clk_ops_dsi_pll_7nm_vco = {
.round_rate = msm_dsi_pll_helper_clk_round_rate,
.set_rate = dsi_pll_7nm_vco_set_rate,
.recalc_rate = dsi_pll_7nm_vco_recalc_rate,
.prepare = dsi_pll_7nm_vco_prepare,
.unprepare = dsi_pll_7nm_vco_unprepare,
};
/*
* PLL Callbacks
*/
static void dsi_pll_7nm_save_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll);
struct pll_7nm_cached_state *cached = &pll_7nm->cached_state;
void __iomem *phy_base = pll_7nm->phy_cmn_mmio;
u32 cmn_clk_cfg0, cmn_clk_cfg1;
cached->pll_out_div = pll_read(pll_7nm->mmio +
REG_DSI_7nm_PHY_PLL_PLL_OUTDIV_RATE);
cached->pll_out_div &= 0x3;
cmn_clk_cfg0 = pll_read(phy_base + REG_DSI_7nm_PHY_CMN_CLK_CFG0);
cached->bit_clk_div = cmn_clk_cfg0 & 0xf;
cached->pix_clk_div = (cmn_clk_cfg0 & 0xf0) >> 4;
cmn_clk_cfg1 = pll_read(phy_base + REG_DSI_7nm_PHY_CMN_CLK_CFG1);
cached->pll_mux = cmn_clk_cfg1 & 0x3;
DBG("DSI PLL%d outdiv %x bit_clk_div %x pix_clk_div %x pll_mux %x",
pll_7nm->id, cached->pll_out_div, cached->bit_clk_div,
cached->pix_clk_div, cached->pll_mux);
}
static int dsi_pll_7nm_restore_state(struct msm_dsi_pll *pll)
{
struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll);
struct pll_7nm_cached_state *cached = &pll_7nm->cached_state;
void __iomem *phy_base = pll_7nm->phy_cmn_mmio;
u32 val;
int ret;
val = pll_read(pll_7nm->mmio + REG_DSI_7nm_PHY_PLL_PLL_OUTDIV_RATE);
val &= ~0x3;
val |= cached->pll_out_div;
pll_write(pll_7nm->mmio + REG_DSI_7nm_PHY_PLL_PLL_OUTDIV_RATE, val);
pll_write(phy_base + REG_DSI_7nm_PHY_CMN_CLK_CFG0,
cached->bit_clk_div | (cached->pix_clk_div << 4));
val = pll_read(phy_base + REG_DSI_7nm_PHY_CMN_CLK_CFG1);
val &= ~0x3;
val |= cached->pll_mux;
pll_write(phy_base + REG_DSI_7nm_PHY_CMN_CLK_CFG1, val);
ret = dsi_pll_7nm_vco_set_rate(&pll->clk_hw, pll_7nm->vco_current_rate, pll_7nm->vco_ref_clk_rate);
if (ret) {
DRM_DEV_ERROR(&pll_7nm->pdev->dev,
"restore vco rate failed. ret=%d\n", ret);
return ret;
}
DBG("DSI PLL%d", pll_7nm->id);
return 0;
}
static int dsi_pll_7nm_set_usecase(struct msm_dsi_pll *pll,
enum msm_dsi_phy_usecase uc)
{
struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll);
void __iomem *base = pll_7nm->phy_cmn_mmio;
u32 data = 0x0; /* internal PLL */
DBG("DSI PLL%d", pll_7nm->id);
switch (uc) {
case MSM_DSI_PHY_STANDALONE:
break;
case MSM_DSI_PHY_MASTER:
pll_7nm->slave = pll_7nm_list[(pll_7nm->id + 1) % DSI_MAX];
break;
case MSM_DSI_PHY_SLAVE:
data = 0x1; /* external PLL */
break;
default:
return -EINVAL;
}
/* set PLL src */
pll_write(base + REG_DSI_7nm_PHY_CMN_CLK_CFG1, (data << 2));
pll_7nm->uc = uc;
return 0;
}
static int dsi_pll_7nm_get_provider(struct msm_dsi_pll *pll,
struct clk **byte_clk_provider,
struct clk **pixel_clk_provider)
{
struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll);
struct clk_hw_onecell_data *hw_data = pll_7nm->hw_data;
DBG("DSI PLL%d", pll_7nm->id);
if (byte_clk_provider)
*byte_clk_provider = hw_data->hws[DSI_BYTE_PLL_CLK]->clk;
if (pixel_clk_provider)
*pixel_clk_provider = hw_data->hws[DSI_PIXEL_PLL_CLK]->clk;
return 0;
}
static void dsi_pll_7nm_destroy(struct msm_dsi_pll *pll)
{
struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll);
struct device *dev = &pll_7nm->pdev->dev;
DBG("DSI PLL%d", pll_7nm->id);
of_clk_del_provider(dev->of_node);
clk_hw_unregister_divider(pll_7nm->out_dsiclk_hw);
clk_hw_unregister_mux(pll_7nm->pclk_mux_hw);
clk_hw_unregister_fixed_factor(pll_7nm->post_out_div_clk_hw);
clk_hw_unregister_fixed_factor(pll_7nm->by_2_bit_clk_hw);
clk_hw_unregister_fixed_factor(pll_7nm->byte_clk_hw);
clk_hw_unregister_divider(pll_7nm->bit_clk_hw);
clk_hw_unregister_divider(pll_7nm->out_div_clk_hw);
clk_hw_unregister(&pll_7nm->base.clk_hw);
}
/*
* The post dividers and mux clocks are created using the standard divider and
* mux API. Unlike the 14nm PHY, the slave PLL doesn't need its dividers/mux
* state to follow the master PLL's divider/mux state. Therefore, we don't
* require special clock ops that also configure the slave PLL registers
*/
static int pll_7nm_register(struct dsi_pll_7nm *pll_7nm)
{
char clk_name[32], parent[32], vco_name[32];
char parent2[32], parent3[32], parent4[32];
struct clk_init_data vco_init = {
.parent_names = (const char *[]){ "bi_tcxo" },
.num_parents = 1,
.name = vco_name,
.flags = CLK_IGNORE_UNUSED,
.ops = &clk_ops_dsi_pll_7nm_vco,
};
struct device *dev = &pll_7nm->pdev->dev;
struct clk_hw_onecell_data *hw_data;
struct clk_hw *hw;
int ret;
DBG("DSI%d", pll_7nm->id);
hw_data = devm_kzalloc(dev, sizeof(*hw_data) +
NUM_PROVIDED_CLKS * sizeof(struct clk_hw *),
GFP_KERNEL);
if (!hw_data)
return -ENOMEM;
snprintf(vco_name, 32, "dsi%dvco_clk", pll_7nm->id);
pll_7nm->base.clk_hw.init = &vco_init;
ret = clk_hw_register(dev, &pll_7nm->base.clk_hw);
if (ret)
return ret;
snprintf(clk_name, 32, "dsi%d_pll_out_div_clk", pll_7nm->id);
snprintf(parent, 32, "dsi%dvco_clk", pll_7nm->id);
hw = clk_hw_register_divider(dev, clk_name,
parent, CLK_SET_RATE_PARENT,
pll_7nm->mmio +
REG_DSI_7nm_PHY_PLL_PLL_OUTDIV_RATE,
0, 2, CLK_DIVIDER_POWER_OF_TWO, NULL);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_base_clk_hw;
}
pll_7nm->out_div_clk_hw = hw;
snprintf(clk_name, 32, "dsi%d_pll_bit_clk", pll_7nm->id);
snprintf(parent, 32, "dsi%d_pll_out_div_clk", pll_7nm->id);
/* BIT CLK: DIV_CTRL_3_0 */
hw = clk_hw_register_divider(dev, clk_name, parent,
CLK_SET_RATE_PARENT,
pll_7nm->phy_cmn_mmio +
REG_DSI_7nm_PHY_CMN_CLK_CFG0,
0, 4, CLK_DIVIDER_ONE_BASED,
&pll_7nm->postdiv_lock);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_out_div_clk_hw;
}
pll_7nm->bit_clk_hw = hw;
snprintf(clk_name, 32, "dsi%d_phy_pll_out_byteclk", pll_7nm->id);
snprintf(parent, 32, "dsi%d_pll_bit_clk", pll_7nm->id);
/* DSI Byte clock = VCO_CLK / OUT_DIV / BIT_DIV / 8 */
hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
CLK_SET_RATE_PARENT, 1, 8);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_bit_clk_hw;
}
pll_7nm->byte_clk_hw = hw;
hw_data->hws[DSI_BYTE_PLL_CLK] = hw;
snprintf(clk_name, 32, "dsi%d_pll_by_2_bit_clk", pll_7nm->id);
snprintf(parent, 32, "dsi%d_pll_bit_clk", pll_7nm->id);
hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
0, 1, 2);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_byte_clk_hw;
}
pll_7nm->by_2_bit_clk_hw = hw;
snprintf(clk_name, 32, "dsi%d_pll_post_out_div_clk", pll_7nm->id);
snprintf(parent, 32, "dsi%d_pll_out_div_clk", pll_7nm->id);
hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
0, 1, 4);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_by_2_bit_clk_hw;
}
pll_7nm->post_out_div_clk_hw = hw;
snprintf(clk_name, 32, "dsi%d_pclk_mux", pll_7nm->id);
snprintf(parent, 32, "dsi%d_pll_bit_clk", pll_7nm->id);
snprintf(parent2, 32, "dsi%d_pll_by_2_bit_clk", pll_7nm->id);
snprintf(parent3, 32, "dsi%d_pll_out_div_clk", pll_7nm->id);
snprintf(parent4, 32, "dsi%d_pll_post_out_div_clk", pll_7nm->id);
hw = clk_hw_register_mux(dev, clk_name,
((const char *[]){
parent, parent2, parent3, parent4
}), 4, 0, pll_7nm->phy_cmn_mmio +
REG_DSI_7nm_PHY_CMN_CLK_CFG1,
0, 2, 0, NULL);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_post_out_div_clk_hw;
}
pll_7nm->pclk_mux_hw = hw;
snprintf(clk_name, 32, "dsi%d_phy_pll_out_dsiclk", pll_7nm->id);
snprintf(parent, 32, "dsi%d_pclk_mux", pll_7nm->id);
/* PIX CLK DIV : DIV_CTRL_7_4*/
hw = clk_hw_register_divider(dev, clk_name, parent,
0, pll_7nm->phy_cmn_mmio +
REG_DSI_7nm_PHY_CMN_CLK_CFG0,
4, 4, CLK_DIVIDER_ONE_BASED,
&pll_7nm->postdiv_lock);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_pclk_mux_hw;
}
pll_7nm->out_dsiclk_hw = hw;
hw_data->hws[DSI_PIXEL_PLL_CLK] = hw;
hw_data->num = NUM_PROVIDED_CLKS;
pll_7nm->hw_data = hw_data;
ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
pll_7nm->hw_data);
if (ret) {
DRM_DEV_ERROR(dev, "failed to register clk provider: %d\n", ret);
goto err_dsiclk_hw;
}
return 0;
err_dsiclk_hw:
clk_hw_unregister_divider(pll_7nm->out_dsiclk_hw);
err_pclk_mux_hw:
clk_hw_unregister_mux(pll_7nm->pclk_mux_hw);
err_post_out_div_clk_hw:
clk_hw_unregister_fixed_factor(pll_7nm->post_out_div_clk_hw);
err_by_2_bit_clk_hw:
clk_hw_unregister_fixed_factor(pll_7nm->by_2_bit_clk_hw);
err_byte_clk_hw:
clk_hw_unregister_fixed_factor(pll_7nm->byte_clk_hw);
err_bit_clk_hw:
clk_hw_unregister_divider(pll_7nm->bit_clk_hw);
err_out_div_clk_hw:
clk_hw_unregister_divider(pll_7nm->out_div_clk_hw);
err_base_clk_hw:
clk_hw_unregister(&pll_7nm->base.clk_hw);
return ret;
}
struct msm_dsi_pll *msm_dsi_pll_7nm_init(struct platform_device *pdev,
enum msm_dsi_phy_type type, int id)
{
struct dsi_pll_7nm *pll_7nm;
struct msm_dsi_pll *pll;
int ret;
pll_7nm = devm_kzalloc(&pdev->dev, sizeof(*pll_7nm), GFP_KERNEL);
if (!pll_7nm)
return ERR_PTR(-ENOMEM);
DBG("DSI PLL%d", id);
pll_7nm->pdev = pdev;
pll_7nm->id = id;
pll_7nm_list[id] = pll_7nm;
pll_7nm->phy_cmn_mmio = msm_ioremap(pdev, "dsi_phy", "DSI_PHY");
if (IS_ERR_OR_NULL(pll_7nm->phy_cmn_mmio)) {
DRM_DEV_ERROR(&pdev->dev, "failed to map CMN PHY base\n");
return ERR_PTR(-ENOMEM);
}
pll_7nm->mmio = msm_ioremap(pdev, "dsi_pll", "DSI_PLL");
if (IS_ERR_OR_NULL(pll_7nm->mmio)) {
DRM_DEV_ERROR(&pdev->dev, "failed to map PLL base\n");
return ERR_PTR(-ENOMEM);
}
spin_lock_init(&pll_7nm->postdiv_lock);
pll = &pll_7nm->base;
pll->min_rate = 1000000000UL;
pll->max_rate = 3500000000UL;
if (type == MSM_DSI_PHY_7NM_V4_1) {
pll->min_rate = 600000000UL;
pll->max_rate = (unsigned long)5000000000ULL;
/* workaround for max rate overflowing on 32-bit builds: */
pll->max_rate = max(pll->max_rate, 0xffffffffUL);
}
pll->get_provider = dsi_pll_7nm_get_provider;
pll->destroy = dsi_pll_7nm_destroy;
pll->save_state = dsi_pll_7nm_save_state;
pll->restore_state = dsi_pll_7nm_restore_state;
pll->set_usecase = dsi_pll_7nm_set_usecase;
pll_7nm->vco_delay = 1;
ret = pll_7nm_register(pll_7nm);
if (ret) {
DRM_DEV_ERROR(&pdev->dev, "failed to register PLL: %d\n", ret);
return ERR_PTR(ret);
}
/* TODO: Remove this when we have proper display handover support */
msm_dsi_pll_save_state(pll);
return pll;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment