disp: msm: dsi: move dsi pll as subnode to dsi PHY

DSI PLL is tightly coupled with DSI PHY. This change removes
separate DSI pll driver and makes DSI pll as a subnode to DSI
PHY which is an accurate way of representation. In addition, this
change adds support for 5nm DSI ctrl and PHY revisions and adds
DSI pll support for 5nm. Remove support for older DSI pll revisions
such as 7nm, 10nm, 14nm, 20nm, 28nm.

Change-Id: Ic8b886a9fe24b906e4ec5130720600efa1e59b68
Signed-off-by: Satya Rama Aditya Pinapala <psraditya30@codeaurora.org>
This commit is contained in:
Satya Rama Aditya Pinapala
2019-11-18 15:35:41 -08:00
parent beb705e598
commit 5694bc2eee
48 changed files with 681 additions and 21684 deletions

View File

@@ -27,4 +27,3 @@ endif
obj-$(CONFIG_DRM_MSM) += msm/
obj-$(CONFIG_MSM_SDE_ROTATOR) += rotator/
obj-$(CONFIG_QCOM_MDSS_PLL) += pll/

View File

@@ -2,6 +2,7 @@
ccflags-y := -I$(srctree)/include/drm -I$(srctree)/techpack/display/msm -I$(srctree)/techpack/display/msm/dsi -I$(srctree)/techpack/display/msm/dp
ccflags-y += -I$(srctree)/techpack/display/msm/sde
ccflags-y += -I$(srctree)/techpack/display/rotator
ccflags-y += -I$(srctree)/drivers/clk/qcom/
msm_drm-$(CONFIG_DRM_MSM_DP) += dp/dp_usbpd.o \
dp/dp_parser.o \
@@ -88,6 +89,8 @@ msm_drm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi_phy.o \
dsi/dsi_phy_timing_v2_0.o \
dsi/dsi_phy_timing_v3_0.o \
dsi/dsi_phy_timing_v4_0.o \
dsi/dsi_pll.o \
dsi/dsi_pll_5nm.o \
dsi/dsi_ctrl_hw_cmn.o \
dsi/dsi_ctrl_hw_1_4.o \
dsi/dsi_ctrl_hw_2_0.o \

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/errno.h>
@@ -100,6 +100,7 @@ static void dsi_catalog_cmn_init(struct dsi_ctrl_hw *ctrl,
case DSI_CTRL_VERSION_2_2:
case DSI_CTRL_VERSION_2_3:
case DSI_CTRL_VERSION_2_4:
case DSI_CTRL_VERSION_2_5:
ctrl->ops.phy_reset_config = dsi_ctrl_hw_22_phy_reset_config;
ctrl->ops.config_clk_gating = dsi_ctrl_hw_22_config_clk_gating;
ctrl->ops.setup_lane_map = dsi_ctrl_hw_20_setup_lane_map;
@@ -163,6 +164,7 @@ int dsi_catalog_ctrl_setup(struct dsi_ctrl_hw *ctrl,
case DSI_CTRL_VERSION_2_2:
case DSI_CTRL_VERSION_2_3:
case DSI_CTRL_VERSION_2_4:
case DSI_CTRL_VERSION_2_5:
ctrl->phy_isolation_enabled = phy_isolation_enabled;
dsi_catalog_cmn_init(ctrl, version);
break;
@@ -302,6 +304,7 @@ int dsi_catalog_phy_setup(struct dsi_phy_hw *phy,
break;
case DSI_PHY_VERSION_4_0:
case DSI_PHY_VERSION_4_1:
case DSI_PHY_VERSION_4_2:
dsi_catalog_phy_4_0_init(phy);
break;
case DSI_PHY_VERSION_0_0_HPM:

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/of_device.h>
@@ -53,6 +53,7 @@ static const enum dsi_ctrl_version dsi_ctrl_v2_0 = DSI_CTRL_VERSION_2_0;
static const enum dsi_ctrl_version dsi_ctrl_v2_2 = DSI_CTRL_VERSION_2_2;
static const enum dsi_ctrl_version dsi_ctrl_v2_3 = DSI_CTRL_VERSION_2_3;
static const enum dsi_ctrl_version dsi_ctrl_v2_4 = DSI_CTRL_VERSION_2_4;
static const enum dsi_ctrl_version dsi_ctrl_v2_5 = DSI_CTRL_VERSION_2_5;
static const struct of_device_id msm_dsi_of_match[] = {
{
@@ -75,6 +76,10 @@ static const struct of_device_id msm_dsi_of_match[] = {
.compatible = "qcom,dsi-ctrl-hw-v2.4",
.data = &dsi_ctrl_v2_4,
},
{
.compatible = "qcom,dsi-ctrl-hw-v2.5",
.data = &dsi_ctrl_v2_5,
},
{}
};
@@ -559,6 +564,7 @@ static int dsi_ctrl_init_regmap(struct platform_device *pdev,
case DSI_CTRL_VERSION_2_2:
case DSI_CTRL_VERSION_2_3:
case DSI_CTRL_VERSION_2_4:
case DSI_CTRL_VERSION_2_5:
ptr = msm_ioremap(pdev, "disp_cc_base", ctrl->name);
if (IS_ERR(ptr)) {
DSI_CTRL_ERR(ctrl, "disp_cc base address not found for\n");
@@ -1867,18 +1873,18 @@ static int dsi_ctrl_dev_probe(struct platform_device *pdev)
goto fail;
}
rc = dsi_ctrl_clocks_init(pdev, dsi_ctrl);
if (rc) {
DSI_CTRL_ERR(dsi_ctrl, "Failed to parse clock information, rc = %d\n",
rc);
goto fail;
}
rc = dsi_ctrl_supplies_init(pdev, dsi_ctrl);
if (rc) {
DSI_CTRL_ERR(dsi_ctrl, "Failed to parse voltage supplies, rc = %d\n",
rc);
goto fail_clks;
goto fail;
}
rc = dsi_ctrl_clocks_init(pdev, dsi_ctrl);
if (rc) {
DSI_CTRL_ERR(dsi_ctrl, "Failed to parse clock information, rc = %d\n",
rc);
goto fail_supplies;
}
rc = dsi_catalog_ctrl_setup(&dsi_ctrl->hw, dsi_ctrl->version,
@@ -1887,7 +1893,7 @@ static int dsi_ctrl_dev_probe(struct platform_device *pdev)
if (rc) {
DSI_CTRL_ERR(dsi_ctrl, "Catalog does not support version (%d)\n",
dsi_ctrl->version);
goto fail_supplies;
goto fail_clks;
}
item->ctrl = dsi_ctrl;
@@ -1905,10 +1911,10 @@ static int dsi_ctrl_dev_probe(struct platform_device *pdev)
return 0;
fail_supplies:
(void)dsi_ctrl_supplies_deinit(dsi_ctrl);
fail_clks:
(void)dsi_ctrl_clocks_deinit(dsi_ctrl);
fail_supplies:
(void)dsi_ctrl_supplies_deinit(dsi_ctrl);
fail:
return rc;
}

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DSI_CTRL_HW_H_
@@ -36,6 +36,7 @@
* @DSI_CTRL_VERSION_2_2: DSI host v2.2 controller
* @DSI_CTRL_VERSION_2_3: DSI host v2.3 controller
* @DSI_CTRL_VERSION_2_4: DSI host v2.4 controller
* @DSI_CTRL_VERSION_2_5: DSI host v2.5 controller
* @DSI_CTRL_VERSION_MAX: max version
*/
enum dsi_ctrl_version {
@@ -45,6 +46,7 @@ enum dsi_ctrl_version {
DSI_CTRL_VERSION_2_2,
DSI_CTRL_VERSION_2_3,
DSI_CTRL_VERSION_2_4,
DSI_CTRL_VERSION_2_5,
DSI_CTRL_VERSION_MAX
};

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/of_device.h>
@@ -83,6 +83,14 @@ static const struct dsi_ver_spec_info dsi_phy_v4_1 = {
.timing_cfg_count = 14,
};
static const struct dsi_ver_spec_info dsi_phy_v4_2 = {
.version = DSI_PHY_VERSION_4_2,
.lane_cfg_count = 4,
.strength_cfg_count = 2,
.regulator_cfg_count = 0,
.timing_cfg_count = 14,
};
static const struct of_device_id msm_dsi_phy_of_match[] = {
{ .compatible = "qcom,dsi-phy-v0.0-hpm",
.data = &dsi_phy_v0_0_hpm,},
@@ -98,6 +106,8 @@ static const struct of_device_id msm_dsi_phy_of_match[] = {
.data = &dsi_phy_v4_0,},
{ .compatible = "qcom,dsi-phy-v4.1",
.data = &dsi_phy_v4_1,},
{ .compatible = "qcom,dsi-phy-v4.2",
.data = &dsi_phy_v4_2,},
{}
};
@@ -422,6 +432,12 @@ static int dsi_phy_driver_probe(struct platform_device *pdev)
goto fail_supplies;
}
rc = dsi_pll_init(pdev, &dsi_phy->pll);
if (rc) {
DSI_PHY_ERR(dsi_phy, "Failed to initialize DSI PLL, rc=%d\n", rc);
goto fail_settings;
}
item->phy = dsi_phy;
mutex_lock(&dsi_phy_list_lock);
@@ -435,6 +451,8 @@ static int dsi_phy_driver_probe(struct platform_device *pdev)
DSI_PHY_INFO(dsi_phy, "Probe successful\n");
return 0;
fail_settings:
(void)dsi_phy_settings_deinit(dsi_phy);
fail_supplies:
(void)dsi_phy_supplies_deinit(dsi_phy);
fail_regmap:

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DSI_PHY_H_
@@ -10,6 +10,7 @@
#include "dsi_clk.h"
#include "dsi_pwr.h"
#include "dsi_phy_hw.h"
#include "dsi_pll.h"
struct dsi_ver_spec_info {
enum dsi_phy_version version;
@@ -70,6 +71,7 @@ enum phy_ulps_return_type {
* @mode: Current mode.
* @data_lanes: Number of data lanes used.
* @dst_format: Destination format.
* @pll: Pointer to PLL resource.
* @allow_phy_power_off: True if PHY is allowed to power off when idle
* @regulator_min_datarate_bps: Minimum per lane data rate to turn on regulator
* @regulator_required: True if phy regulator is required
@@ -95,6 +97,8 @@ struct msm_dsi_phy {
enum dsi_data_lanes data_lanes;
enum dsi_pixel_format dst_format;
struct dsi_pll_resource *pll;
bool allow_phy_power_off;
u32 regulator_min_datarate_bps;
bool regulator_required;

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DSI_PHY_HW_H_
@@ -31,6 +31,7 @@
* @DSI_PHY_VERSION_3_0: 10nm
* @DSI_PHY_VERSION_4_0: 7nm
* @DSI_PHY_VERSION_4_1: 7nm
* @DSI_PHY_VERSION_4_2: 5nm
* @DSI_PHY_VERSION_MAX:
*/
enum dsi_phy_version {
@@ -42,6 +43,7 @@ enum dsi_phy_version {
DSI_PHY_VERSION_3_0, /* 10nm */
DSI_PHY_VERSION_4_0, /* 7nm */
DSI_PHY_VERSION_4_1, /* 7nm */
DSI_PHY_VERSION_4_2, /* 5nm */
DSI_PHY_VERSION_MAX
};

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/math64.h>
@@ -156,7 +156,8 @@ static void dsi_phy_hw_v4_0_lane_settings(struct dsi_phy_hw *phy,
u8 tx_dctrl_v4_1[] = {0x40, 0x40, 0x40, 0x46, 0x41};
u8 *tx_dctrl;
if (phy->version == DSI_PHY_VERSION_4_1)
if ((phy->version == DSI_PHY_VERSION_4_1) ||
(phy->version == DSI_PHY_VERSION_4_2))
tx_dctrl = &tx_dctrl_v4_1[0];
else
tx_dctrl = &tx_dctrl_v4[0];
@@ -241,7 +242,13 @@ void dsi_phy_hw_v4_0_enable(struct dsi_phy_hw *phy,
if (cfg->bit_clk_rate_hz <= 1500000000)
less_than_1500_mhz = true;
if (phy->version == DSI_PHY_VERSION_4_1) {
if (phy->version == DSI_PHY_VERSION_4_2) {
vreg_ctrl_0 = 0x58;
glbl_rescode_top_ctrl = 0x03;
glbl_rescode_bot_ctrl = 0x3c;
glbl_str_swi_cal_sel_ctrl = 0x00;
glbl_hstx_str_ctrl_0 = 0x88;
} else if (phy->version == DSI_PHY_VERSION_4_1) {
vreg_ctrl_0 = less_than_1500_mhz ? 0x53 : 0x52;
glbl_rescode_top_ctrl = less_than_1500_mhz ? 0x3d : 0x00;
glbl_rescode_bot_ctrl = less_than_1500_mhz ? 0x39 : 0x3c;

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#include "dsi_phy_timing_calc.h"
@@ -788,6 +788,7 @@ int dsi_phy_timing_calc_init(struct dsi_phy_hw *phy,
break;
case DSI_PHY_VERSION_4_0:
case DSI_PHY_VERSION_4_1:
case DSI_PHY_VERSION_4_2:
ops->get_default_phy_params =
dsi_phy_hw_v4_0_get_default_phy_params;
ops->calc_clk_zero =

148
msm/dsi/dsi_pll.c Normal file
View File

@@ -0,0 +1,148 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include "dsi_pll.h"
static int dsi_pll_clock_register(struct platform_device *pdev,
struct dsi_pll_resource *pll_res)
{
int rc;
switch (pll_res->pll_revision) {
case DSI_PLL_5NM:
rc = dsi_pll_clock_register_5nm(pdev, pll_res);
break;
default:
rc = -EINVAL;
break;
}
if (rc)
DSI_PLL_ERR(pll_res, "clock register failed rc=%d\n", rc);
return rc;
}
static inline int dsi_pll_get_ioresources(struct platform_device *pdev,
void __iomem **regmap, char *resource_name)
{
int rc = 0;
struct resource *rsc = platform_get_resource_byname(pdev,
IORESOURCE_MEM, resource_name);
if (rsc) {
if (!regmap)
return -ENOMEM;
*regmap = devm_ioremap(&pdev->dev,
rsc->start, resource_size(rsc));
if (!*regmap)
return -ENOMEM;
}
return rc;
}
int dsi_pll_init(struct platform_device *pdev, struct dsi_pll_resource **pll)
{
int rc = 0;
const char *label;
struct dsi_pll_resource *pll_res = NULL;
if (!pdev->dev.of_node) {
pr_err("Invalid DSI PHY node\n");
return -ENOTSUPP;
}
pll_res = devm_kzalloc(&pdev->dev, sizeof(struct dsi_pll_resource),
GFP_KERNEL);
if (!pll_res)
return -ENOMEM;
*pll = pll_res;
label = of_get_property(pdev->dev.of_node, "pll-label", NULL);
if (!label)
DSI_PLL_INFO(pll_res, "DSI pll label not specified\n");
else
DSI_PLL_INFO(pll_res, "DSI pll label = %s\n", label);
/**
* Currently, Only supports 5nm PLL version. Will add
* support for other versions as needed.
*/
if (!strcmp(label, "dsi_pll_5nm"))
pll_res->pll_revision = DSI_PLL_5NM;
else
return -ENOTSUPP;
rc = of_property_read_u32(pdev->dev.of_node, "cell-index",
&pll_res->index);
if (rc) {
DSI_PLL_ERR(pll_res, "Unable to get the cell-index rc=%d\n", rc);
pll_res->index = 0;
}
pll_res->ssc_en = of_property_read_bool(pdev->dev.of_node,
"qcom,dsi-pll-ssc-en");
if (pll_res->ssc_en) {
DSI_PLL_INFO(pll_res, "PLL SSC enabled\n");
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,ssc-frequency-hz", &pll_res->ssc_freq);
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,ssc-ppm", &pll_res->ssc_ppm);
pll_res->ssc_center = false;
label = of_get_property(pdev->dev.of_node,
"qcom,dsi-pll-ssc-mode", NULL);
if (label && !strcmp(label, "center-spread"))
pll_res->ssc_center = true;
}
if (dsi_pll_get_ioresources(pdev, &pll_res->pll_base, "pll_base")) {
DSI_PLL_ERR(pll_res, "Unable to remap pll base resources\n");
return -ENOMEM;
}
pr_info("PLL base=%p\n", pll_res->pll_base);
if (dsi_pll_get_ioresources(pdev, &pll_res->phy_base, "dsi_phy")) {
DSI_PLL_ERR(pll_res, "Unable to remap pll phy base resources\n");
return -ENOMEM;
}
if (dsi_pll_get_ioresources(pdev, &pll_res->dyn_pll_base,
"dyn_refresh_base")) {
DSI_PLL_ERR(pll_res, "Unable to remap dynamic pll base resources\n");
return -ENOMEM;
}
if (dsi_pll_get_ioresources(pdev, &pll_res->gdsc_base, "gdsc_base")) {
DSI_PLL_ERR(pll_res, "Unable to remap gdsc base resources\n");
return -ENOMEM;
}
rc = dsi_pll_clock_register(pdev, pll_res);
if (rc) {
DSI_PLL_ERR(pll_res, "clock register failed rc=%d\n", rc);
return -EINVAL;
}
return rc;
}

View File

@@ -1,10 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*/
#ifndef __MDSS_PLL_H
#define __MDSS_PLL_H
#ifndef __DSI_PLL_H
#define __DSI_PLL_H
#include <linux/clk-provider.h>
#include <linux/io.h>
@@ -14,47 +14,48 @@
#include "clk-regmap.h"
#include "clk-regmap-divider.h"
#include "clk-regmap-mux.h"
#include "dsi_defs.h"
#if defined(CONFIG_DRM)
#include <linux/sde_io_util.h>
#else
#include <linux/mdss_io_util.h>
#endif
#define DSI_PLL_DBG(p, fmt, ...) DRM_DEV_DEBUG(NULL, "[msm-dsi-debug]: DSI_PLL_%d: "\
fmt, p ? p->index : -1, ##__VA_ARGS__)
#define DSI_PLL_ERR(p, fmt, ...) DRM_DEV_ERROR(NULL, "[msm-dsi-error]: DSI_PLL_%d: "\
fmt, p ? p->index : -1, ##__VA_ARGS__)
#define DSI_PLL_INFO(p, fmt, ...) DRM_DEV_INFO(NULL, "[msm-dsi-info]: DSI_PLL_%d: "\
fmt, p ? p->index : -1, ##__VA_ARGS__)
#define DSI_PLL_WARN(p, fmt, ...) DRM_WARN("[msm-dsi-warn]: DSI_PLL_%d: "\
fmt, p ? p->index : -1, ##__VA_ARGS__)
#define MDSS_PLL_REG_W(base, offset, data) \
#define DSI_PLL_REG_W(base, offset, data) \
writel_relaxed((data), (base) + (offset))
#define MDSS_PLL_REG_R(base, offset) readl_relaxed((base) + (offset))
#define DSI_PLL_REG_R(base, offset) readl_relaxed((base) + (offset))
#define PLL_CALC_DATA(addr0, addr1, data0, data1) \
(((data1) << 24) | ((((addr1) / 4) & 0xFF) << 16) | \
((data0) << 8) | (((addr0) / 4) & 0xFF))
#define MDSS_DYN_PLL_REG_W(base, offset, addr0, addr1, data0, data1) \
#define DSI_DYN_PLL_REG_W(base, offset, addr0, addr1, data0, data1) \
writel_relaxed(PLL_CALC_DATA(addr0, addr1, data0, data1), \
(base) + (offset))
#define upper_8_bit(x) ((((x) >> 2) & 0x100) >> 8)
enum {
MDSS_DSI_PLL_10NM,
MDSS_DP_PLL_10NM,
MDSS_DSI_PLL_7NM,
MDSS_DSI_PLL_7NM_V2,
MDSS_DSI_PLL_7NM_V4_1,
MDSS_DP_PLL_7NM,
MDSS_DP_PLL_7NM_V2,
MDSS_DSI_PLL_28LPM,
MDSS_DSI_PLL_14NM,
MDSS_DP_PLL_14NM,
MDSS_HDMI_PLL_28LPM,
MDSS_UNKNOWN_PLL,
};
enum {
MDSS_PLL_TARGET_8996,
};
#define DFPS_MAX_NUM_OF_FRAME_RATES 16
#define MAX_DSI_PLL_EN_SEQS 10
/* Register offsets for 5nm PHY PLL */
#define MMSS_DSI_PHY_PLL_PLL_CNTRL (0x0014)
#define MMSS_DSI_PHY_PLL_PLL_BKG_KVCO_CAL_EN (0x002C)
#define MMSS_DSI_PHY_PLL_PLLLOCK_CMP_EN (0x009C)
struct lpfr_cfg {
unsigned long vco_rate;
u32 r;
};
enum {
DSI_PLL_5NM,
DSI_UNKNOWN_PLL,
};
struct dfps_pll_codes {
uint32_t pll_codes_1;
@@ -73,27 +74,17 @@ struct dfps_info {
struct dfps_codes_info codes_dfps[DFPS_MAX_NUM_OF_FRAME_RATES];
};
struct mdss_pll_resources {
/* Pll specific resources like GPIO, power supply, clocks, etc*/
struct dss_module_power mp;
struct dsi_pll_resource {
/*
* dsi/edp/hmdi plls' base register, phy, gdsc and dynamic refresh
* dsi base register, phy, gdsc and dynamic refresh
* register mapping
*/
void __iomem *pll_base;
void __iomem *phy_base;
void __iomem *ln_tx0_base;
void __iomem *ln_tx0_tran_base;
void __iomem *ln_tx0_vmode_base;
void __iomem *ln_tx1_base;
void __iomem *ln_tx1_tran_base;
void __iomem *ln_tx1_vmode_base;
void __iomem *gdsc_base;
void __iomem *dyn_pll_base;
bool is_init_locked;
s64 vco_current_rate;
s64 vco_locking_rate;
s64 vco_ref_clk_rate;
@@ -109,28 +100,13 @@ struct mdss_pll_resources {
u32 cached_postdiv1;
u32 cached_postdiv3;
u32 cached_vreg_cfg;
/* dsi/edp/hmdi pll interface type */
u32 pll_interface_type;
u32 pll_revision;
/*
* Target ID. Used in pll_register API for valid target check before
* registering the PLL clocks.
*/
u32 target_id;
/* HW recommended delay during configuration of vco clock rate */
u32 vco_delay;
/* Ref-count of the PLL resources */
u32 resource_ref_cnt;
/*
* Keep track to resource status to avoid updating same status for the
* pll from different paths
*/
bool resource_enable;
/*
* Certain plls' do not allow vco rate update if it is on. Keep track of
@@ -156,15 +132,6 @@ struct mdss_pll_resources {
*/
bool reg_upd;
/*
* Notifier callback for MDSS gdsc regulator events
*/
struct notifier_block gdsc_cb;
/*
* Worker function to call PLL off event
*/
struct work_struct pll_off;
/*
* PLL index if multiple index are available. Eg. in case of
@@ -177,7 +144,7 @@ struct mdss_pll_resources {
u32 ssc_freq;
u32 ssc_ppm;
struct mdss_pll_resources *slave;
struct dsi_pll_resource *slave;
/*
* target pll revision information
@@ -198,7 +165,21 @@ struct mdss_pll_resources {
bool dfps_trigger;
};
struct mdss_pll_vco_calc {
struct dsi_pll_vco_clk {
struct clk_hw hw;
unsigned long ref_clk_rate;
u64 min_rate;
u64 max_rate;
u32 pll_en_seq_cnt;
struct lpfr_cfg *lpfr_lut;
u32 lpfr_lut_size;
void *priv;
int (*pll_enable_seqs[MAX_DSI_PLL_EN_SEQS])
(struct dsi_pll_resource *pll_res);
};
struct dsi_pll_vco_calc {
s32 div_frac_start1;
s32 div_frac_start2;
s32 div_frac_start3;
@@ -209,7 +190,7 @@ struct mdss_pll_vco_calc {
s64 pll_plllock_cmp3;
};
static inline bool is_gdsc_disabled(struct mdss_pll_resources *pll_res)
static inline bool is_gdsc_disabled(struct dsi_pll_resource *pll_res)
{
if (!pll_res->gdsc_base) {
WARN(1, "gdsc_base register is not defined\n");
@@ -218,7 +199,7 @@ static inline bool is_gdsc_disabled(struct mdss_pll_resources *pll_res)
return readl_relaxed(pll_res->gdsc_base) & BIT(31) ? false : true;
}
static inline int mdss_pll_div_prepare(struct clk_hw *hw)
static inline int dsi_pll_div_prepare(struct clk_hw *hw)
{
struct clk_hw *parent_hw = clk_hw_get_parent(hw);
/* Restore the divider's value */
@@ -226,24 +207,27 @@ static inline int mdss_pll_div_prepare(struct clk_hw *hw)
clk_hw_get_rate(parent_hw));
}
static inline int mdss_set_mux_sel(void *context, unsigned int reg,
static inline int dsi_set_mux_sel(void *context, unsigned int reg,
unsigned int val)
{
return 0;
}
static inline int mdss_get_mux_sel(void *context, unsigned int reg,
static inline int dsi_get_mux_sel(void *context, unsigned int reg,
unsigned int *val)
{
*val = 0;
return 0;
}
int mdss_pll_resource_enable(struct mdss_pll_resources *pll_res, bool enable);
int mdss_pll_util_resource_enable(struct mdss_pll_resources *pll_res,
bool enable);
int mdss_pll_util_resource_parse(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
struct dss_vreg *mdss_pll_get_mp_by_reg_name(struct mdss_pll_resources *pll_res
, char *name);
static inline struct dsi_pll_vco_clk *to_vco_clk_hw(struct clk_hw *hw)
{
return container_of(hw, struct dsi_pll_vco_clk, hw);
}
int dsi_pll_clock_register_5nm(struct platform_device *pdev,
struct dsi_pll_resource *pll_res);
int dsi_pll_init(struct platform_device *pdev,
struct dsi_pll_resource **pll_res);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
ccflags-y := -I$(srctree)/drivers/clk/qcom/
obj-$(CONFIG_QCOM_MDSS_PLL) += pll_util.o
obj-$(CONFIG_QCOM_MDSS_PLL) += pll_drv.o
obj-$(CONFIG_QCOM_MDSS_PLL) += dsi_pll_10nm.o
obj-$(CONFIG_QCOM_MDSS_PLL) += dsi_pll_7nm.o
obj-$(CONFIG_QCOM_MDSS_PLL) += dsi_pll_28lpm.o
obj-$(CONFIG_QCOM_MDSS_PLL) += dsi_pll_28nm_util.o
obj-$(CONFIG_QCOM_MDSS_PLL) += dsi_pll_14nm.o
obj-$(CONFIG_QCOM_MDSS_PLL) += dsi_pll_14nm_util.o
obj-$(CONFIG_QCOM_MDSS_PLL) += hdmi_pll_28lpm.o
obj-$(CONFIG_QCOM_MDSS_DP_PLL) += dp_pll_7nm.o \
dp_pll_7nm_util.o \
dp_pll_10nm.o \
dp_pll_10nm_util.o \
dp_pll_14nm.o \

View File

@@ -1,50 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
*/
#ifndef __MDSS_DP_PLL_H
#define __MDSS_DP_PLL_H
struct dp_pll_vco_clk {
struct clk_hw hw;
unsigned long rate; /* current vco rate */
u64 min_rate; /* min vco rate */
u64 max_rate; /* max vco rate */
void *priv;
};
static inline struct dp_pll_vco_clk *to_dp_vco_hw(struct clk_hw *hw)
{
return container_of(hw, struct dp_pll_vco_clk, hw);
}
#ifdef CONFIG_QCOM_MDSS_DP_PLL
int dp_pll_clock_register_14nm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
int dp_pll_clock_register_10nm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
int dp_pll_clock_register_7nm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
#else
static inline int dp_pll_clock_register_14nm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
return 0;
}
static inline int dp_pll_clock_register_10nm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
return 0;
}
static inline int dp_pll_clock_register_7nm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
return 0;
}
#endif
#endif /* __MDSS_DP_PLL_H */

View File

@@ -1,287 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
*/
/*
* Display Port PLL driver block diagram for branch clocks
*
* +------------------------------+
* | DP_VCO_CLK |
* | |
* | +-------------------+ |
* | | (DP PLL/VCO) | |
* | +---------+---------+ |
* | v |
* | +----------+-----------+ |
* | | hsclk_divsel_clk_src | |
* | +----------+-----------+ |
* +------------------------------+
* |
* +------------<---------v------------>----------+
* | |
* +-----v------------+ |
* | dp_link_clk_src | |
* | divsel_ten | |
* +---------+--------+ |
* | |
* | |
* v v
* Input to DISPCC block |
* for link clk, crypto clk |
* and interface clock |
* |
* |
* +--------<------------+-----------------+---<---+
* | | |
* +-------v------+ +--------v-----+ +--------v------+
* | vco_divided | | vco_divided | | vco_divided |
* | _clk_src | | _clk_src | | _clk_src |
* | | | | | |
* |divsel_six | | divsel_two | | divsel_four |
* +-------+------+ +-----+--------+ +--------+------+
* | | |
* v------->----------v-------------<------v
* |
* +----------+---------+
* | vco_divided_clk |
* | _src_mux |
* +---------+----------+
* |
* v
* Input to DISPCC block
* for DP pixel clock
*
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <dt-bindings/clock/mdss-10nm-pll-clk.h>
#include "pll_drv.h"
#include "dp_pll.h"
#include "dp_pll_10nm.h"
static struct dp_pll_db dp_pdb;
static struct clk_ops mux_clk_ops;
static struct regmap_config dp_pll_10nm_cfg = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = 0x910,
};
static struct regmap_bus dp_pixel_mux_regmap_ops = {
.reg_write = dp_mux_set_parent_10nm,
.reg_read = dp_mux_get_parent_10nm,
};
/* Op structures */
static const struct clk_ops dp_10nm_vco_clk_ops = {
.recalc_rate = dp_vco_recalc_rate_10nm,
.set_rate = dp_vco_set_rate_10nm,
.round_rate = dp_vco_round_rate_10nm,
.prepare = dp_vco_prepare_10nm,
.unprepare = dp_vco_unprepare_10nm,
};
static struct dp_pll_vco_clk dp_vco_clk = {
.min_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000,
.max_rate = DP_VCO_HSCLK_RATE_8100MHZDIV1000,
.hw.init = &(struct clk_init_data){
.name = "dp_vco_clk",
.parent_names = (const char *[]){ "xo_board" },
.num_parents = 1,
.ops = &dp_10nm_vco_clk_ops,
},
};
static struct clk_fixed_factor dp_link_clk_divsel_ten = {
.div = 10,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dp_link_clk_divsel_ten",
.parent_names =
(const char *[]){ "dp_vco_clk" },
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor dp_vco_divsel_two_clk_src = {
.div = 2,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dp_vco_divsel_two_clk_src",
.parent_names =
(const char *[]){ "dp_vco_clk" },
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor dp_vco_divsel_four_clk_src = {
.div = 4,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dp_vco_divsel_four_clk_src",
.parent_names =
(const char *[]){ "dp_vco_clk" },
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor dp_vco_divsel_six_clk_src = {
.div = 6,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dp_vco_divsel_six_clk_src",
.parent_names =
(const char *[]){ "dp_vco_clk" },
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE),
.ops = &clk_fixed_factor_ops,
},
};
static int clk_mux_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
int ret = 0;
ret = __clk_mux_determine_rate_closest(hw, req);
if (ret)
return ret;
/* Set the new parent of mux if there is a new valid parent */
if (hw->clk && req->best_parent_hw->clk)
clk_set_parent(hw->clk, req->best_parent_hw->clk);
return 0;
}
static unsigned long mux_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk *div_clk = NULL, *vco_clk = NULL;
struct dp_pll_vco_clk *vco = NULL;
div_clk = clk_get_parent(hw->clk);
if (!div_clk)
return 0;
vco_clk = clk_get_parent(div_clk);
if (!vco_clk)
return 0;
vco = to_dp_vco_hw(__clk_get_hw(vco_clk));
if (!vco)
return 0;
if (vco->rate == DP_VCO_HSCLK_RATE_8100MHZDIV1000)
return (vco->rate / 6);
else if (vco->rate == DP_VCO_HSCLK_RATE_5400MHZDIV1000)
return (vco->rate / 4);
else
return (vco->rate / 2);
}
static struct clk_regmap_mux dp_vco_divided_clk_src_mux = {
.reg = 0x64,
.shift = 0,
.width = 2,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dp_vco_divided_clk_src_mux",
.parent_names =
(const char *[]){"dp_vco_divsel_two_clk_src",
"dp_vco_divsel_four_clk_src",
"dp_vco_divsel_six_clk_src"},
.num_parents = 3,
.ops = &mux_clk_ops,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
},
},
};
static struct clk_hw *mdss_dp_pllcc_10nm[] = {
[DP_VCO_CLK] = &dp_vco_clk.hw,
[DP_LINK_CLK_DIVSEL_TEN] = &dp_link_clk_divsel_ten.hw,
[DP_VCO_DIVIDED_TWO_CLK_SRC] = &dp_vco_divsel_two_clk_src.hw,
[DP_VCO_DIVIDED_FOUR_CLK_SRC] = &dp_vco_divsel_four_clk_src.hw,
[DP_VCO_DIVIDED_SIX_CLK_SRC] = &dp_vco_divsel_six_clk_src.hw,
[DP_VCO_DIVIDED_CLK_SRC_MUX] = &dp_vco_divided_clk_src_mux.clkr.hw,
};
int dp_pll_clock_register_10nm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc = -ENOTSUPP, i = 0;
struct clk_onecell_data *clk_data;
struct clk *clk;
struct regmap *regmap;
int num_clks = ARRAY_SIZE(mdss_dp_pllcc_10nm);
clk_data = devm_kzalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
clk_data->clks = devm_kcalloc(&pdev->dev, num_clks,
sizeof(struct clk *), GFP_KERNEL);
if (!clk_data->clks)
return -ENOMEM;
clk_data->clk_num = num_clks;
pll_res->priv = &dp_pdb;
dp_pdb.pll = pll_res;
/* Set client data for vco, mux and div clocks */
regmap = devm_regmap_init(&pdev->dev, &dp_pixel_mux_regmap_ops,
pll_res, &dp_pll_10nm_cfg);
dp_vco_divided_clk_src_mux.clkr.regmap = regmap;
mux_clk_ops = clk_regmap_mux_closest_ops;
mux_clk_ops.determine_rate = clk_mux_determine_rate;
mux_clk_ops.recalc_rate = mux_recalc_rate;
dp_vco_clk.priv = pll_res;
for (i = DP_VCO_CLK; i <= DP_VCO_DIVIDED_CLK_SRC_MUX; i++) {
pr_debug("reg clk: %d index: %d\n", i, pll_res->index);
clk = devm_clk_register(&pdev->dev,
mdss_dp_pllcc_10nm[i]);
if (IS_ERR(clk)) {
pr_err("clk registration failed for DP: %d\n",
pll_res->index);
rc = -EINVAL;
goto clk_reg_fail;
}
clk_data->clks[i] = clk;
}
rc = of_clk_add_provider(pdev->dev.of_node,
of_clk_src_onecell_get, clk_data);
if (rc) {
pr_err("%s: Clock register failed rc=%d\n", __func__, rc);
rc = -EPROBE_DEFER;
} else {
pr_debug("%s SUCCESS\n", __func__);
}
return 0;
clk_reg_fail:
return rc;
}

View File

@@ -1,51 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
*/
#ifndef __MDSS_DP_PLL_10NM_H
#define __MDSS_DP_PLL_10NM_H
#define DP_VCO_HSCLK_RATE_1620MHZDIV1000 1620000UL
#define DP_VCO_HSCLK_RATE_2700MHZDIV1000 2700000UL
#define DP_VCO_HSCLK_RATE_5400MHZDIV1000 5400000UL
#define DP_VCO_HSCLK_RATE_8100MHZDIV1000 8100000UL
struct dp_pll_db {
struct mdss_pll_resources *pll;
/* lane and orientation settings */
u8 lane_cnt;
u8 orientation;
/* COM PHY settings */
u32 hsclk_sel;
u32 dec_start_mode0;
u32 div_frac_start1_mode0;
u32 div_frac_start2_mode0;
u32 div_frac_start3_mode0;
u32 integloop_gain0_mode0;
u32 integloop_gain1_mode0;
u32 vco_tune_map;
u32 lock_cmp1_mode0;
u32 lock_cmp2_mode0;
u32 lock_cmp3_mode0;
u32 lock_cmp_en;
/* PHY vco divider */
u32 phy_vco_div;
};
int dp_vco_set_rate_10nm(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate);
unsigned long dp_vco_recalc_rate_10nm(struct clk_hw *hw,
unsigned long parent_rate);
long dp_vco_round_rate_10nm(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate);
int dp_vco_prepare_10nm(struct clk_hw *hw);
void dp_vco_unprepare_10nm(struct clk_hw *hw);
int dp_mux_set_parent_10nm(void *context,
unsigned int reg, unsigned int val);
int dp_mux_get_parent_10nm(void *context,
unsigned int reg, unsigned int *val);
#endif /* __MDSS_DP_PLL_10NM_H */

View File

@@ -1,757 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/iopoll.h>
#include <linux/delay.h>
#include <linux/usb/usbpd.h>
#include "pll_drv.h"
#include "dp_pll.h"
#include "dp_pll_10nm.h"
#define DP_PHY_REVISION_ID0 0x0000
#define DP_PHY_REVISION_ID1 0x0004
#define DP_PHY_REVISION_ID2 0x0008
#define DP_PHY_REVISION_ID3 0x000C
#define DP_PHY_CFG 0x0010
#define DP_PHY_PD_CTL 0x0018
#define DP_PHY_MODE 0x001C
#define DP_PHY_AUX_CFG0 0x0020
#define DP_PHY_AUX_CFG1 0x0024
#define DP_PHY_AUX_CFG2 0x0028
#define DP_PHY_AUX_CFG3 0x002C
#define DP_PHY_AUX_CFG4 0x0030
#define DP_PHY_AUX_CFG5 0x0034
#define DP_PHY_AUX_CFG6 0x0038
#define DP_PHY_AUX_CFG7 0x003C
#define DP_PHY_AUX_CFG8 0x0040
#define DP_PHY_AUX_CFG9 0x0044
#define DP_PHY_AUX_INTERRUPT_MASK 0x0048
#define DP_PHY_AUX_INTERRUPT_CLEAR 0x004C
#define DP_PHY_AUX_BIST_CFG 0x0050
#define DP_PHY_VCO_DIV 0x0064
#define DP_PHY_TX0_TX1_LANE_CTL 0x006C
#define DP_PHY_TX2_TX3_LANE_CTL 0x0088
#define DP_PHY_SPARE0 0x00AC
#define DP_PHY_STATUS 0x00C0
/* Tx registers */
#define TXn_BIST_MODE_LANENO 0x0000
#define TXn_CLKBUF_ENABLE 0x0008
#define TXn_TX_EMP_POST1_LVL 0x000C
#define TXn_TX_DRV_LVL 0x001C
#define TXn_RESET_TSYNC_EN 0x0024
#define TXn_PRE_STALL_LDO_BOOST_EN 0x0028
#define TXn_TX_BAND 0x002C
#define TXn_SLEW_CNTL 0x0030
#define TXn_INTERFACE_SELECT 0x0034
#define TXn_RES_CODE_LANE_TX 0x003C
#define TXn_RES_CODE_LANE_RX 0x0040
#define TXn_RES_CODE_LANE_OFFSET_TX 0x0044
#define TXn_RES_CODE_LANE_OFFSET_RX 0x0048
#define TXn_DEBUG_BUS_SEL 0x0058
#define TXn_TRANSCEIVER_BIAS_EN 0x005C
#define TXn_HIGHZ_DRVR_EN 0x0060
#define TXn_TX_POL_INV 0x0064
#define TXn_PARRATE_REC_DETECT_IDLE_EN 0x0068
#define TXn_LANE_MODE_1 0x008C
#define TXn_TRAN_DRVR_EMP_EN 0x00C0
#define TXn_TX_INTERFACE_MODE 0x00C4
#define TXn_VMODE_CTRL1 0x00F0
/* PLL register offset */
#define QSERDES_COM_ATB_SEL1 0x0000
#define QSERDES_COM_ATB_SEL2 0x0004
#define QSERDES_COM_FREQ_UPDATE 0x0008
#define QSERDES_COM_BG_TIMER 0x000C
#define QSERDES_COM_SSC_EN_CENTER 0x0010
#define QSERDES_COM_SSC_ADJ_PER1 0x0014
#define QSERDES_COM_SSC_ADJ_PER2 0x0018
#define QSERDES_COM_SSC_PER1 0x001C
#define QSERDES_COM_SSC_PER2 0x0020
#define QSERDES_COM_SSC_STEP_SIZE1 0x0024
#define QSERDES_COM_SSC_STEP_SIZE2 0x0028
#define QSERDES_COM_POST_DIV 0x002C
#define QSERDES_COM_POST_DIV_MUX 0x0030
#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x0034
#define QSERDES_COM_CLK_ENABLE1 0x0038
#define QSERDES_COM_SYS_CLK_CTRL 0x003C
#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x0040
#define QSERDES_COM_PLL_EN 0x0044
#define QSERDES_COM_PLL_IVCO 0x0048
#define QSERDES_COM_CMN_IETRIM 0x004C
#define QSERDES_COM_CMN_IPTRIM 0x0050
#define QSERDES_COM_CP_CTRL_MODE0 0x0060
#define QSERDES_COM_CP_CTRL_MODE1 0x0064
#define QSERDES_COM_PLL_RCTRL_MODE0 0x0068
#define QSERDES_COM_PLL_RCTRL_MODE1 0x006C
#define QSERDES_COM_PLL_CCTRL_MODE0 0x0070
#define QSERDES_COM_PLL_CCTRL_MODE1 0x0074
#define QSERDES_COM_PLL_CNTRL 0x0078
#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM 0x007C
#define QSERDES_COM_SYSCLK_EN_SEL 0x0080
#define QSERDES_COM_CML_SYSCLK_SEL 0x0084
#define QSERDES_COM_RESETSM_CNTRL 0x0088
#define QSERDES_COM_RESETSM_CNTRL2 0x008C
#define QSERDES_COM_LOCK_CMP_EN 0x0090
#define QSERDES_COM_LOCK_CMP_CFG 0x0094
#define QSERDES_COM_LOCK_CMP1_MODE0 0x0098
#define QSERDES_COM_LOCK_CMP2_MODE0 0x009C
#define QSERDES_COM_LOCK_CMP3_MODE0 0x00A0
#define QSERDES_COM_DEC_START_MODE0 0x00B0
#define QSERDES_COM_DEC_START_MODE1 0x00B4
#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x00B8
#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x00BC
#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x00C0
#define QSERDES_COM_DIV_FRAC_START1_MODE1 0x00C4
#define QSERDES_COM_DIV_FRAC_START2_MODE1 0x00C8
#define QSERDES_COM_DIV_FRAC_START3_MODE1 0x00CC
#define QSERDES_COM_INTEGLOOP_INITVAL 0x00D0
#define QSERDES_COM_INTEGLOOP_EN 0x00D4
#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x00D8
#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x00DC
#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x00E0
#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 0x00E4
#define QSERDES_COM_VCOCAL_DEADMAN_CTRL 0x00E8
#define QSERDES_COM_VCO_TUNE_CTRL 0x00EC
#define QSERDES_COM_VCO_TUNE_MAP 0x00F0
#define QSERDES_COM_CMN_STATUS 0x0124
#define QSERDES_COM_RESET_SM_STATUS 0x0128
#define QSERDES_COM_CLK_SEL 0x0138
#define QSERDES_COM_HSCLK_SEL 0x013C
#define QSERDES_COM_CORECLK_DIV_MODE0 0x0148
#define QSERDES_COM_SW_RESET 0x0150
#define QSERDES_COM_CORE_CLK_EN 0x0154
#define QSERDES_COM_C_READY_STATUS 0x0158
#define QSERDES_COM_CMN_CONFIG 0x015C
#define QSERDES_COM_SVS_MODE_CLK_SEL 0x0164
#define DP_PHY_PLL_POLL_SLEEP_US 500
#define DP_PHY_PLL_POLL_TIMEOUT_US 10000
#define DP_VCO_RATE_8100MHZDIV1000 8100000UL
#define DP_VCO_RATE_9720MHZDIV1000 9720000UL
#define DP_VCO_RATE_10800MHZDIV1000 10800000UL
int dp_mux_set_parent_10nm(void *context, unsigned int reg, unsigned int val)
{
struct mdss_pll_resources *dp_res = context;
int rc;
u32 auxclk_div;
rc = mdss_pll_resource_enable(dp_res, true);
if (rc) {
pr_err("Failed to enable mdss DP PLL resources\n");
return rc;
}
auxclk_div = MDSS_PLL_REG_R(dp_res->phy_base, DP_PHY_VCO_DIV);
auxclk_div &= ~0x03; /* bits 0 to 1 */
if (val == 0) /* mux parent index = 0 */
auxclk_div |= 1;
else if (val == 1) /* mux parent index = 1 */
auxclk_div |= 2;
else if (val == 2) /* mux parent index = 2 */
auxclk_div |= 0;
MDSS_PLL_REG_W(dp_res->phy_base,
DP_PHY_VCO_DIV, auxclk_div);
/* Make sure the PHY registers writes are done */
wmb();
pr_debug("%s: mux=%d auxclk_div=%x\n", __func__, val, auxclk_div);
mdss_pll_resource_enable(dp_res, false);
return 0;
}
int dp_mux_get_parent_10nm(void *context, unsigned int reg, unsigned int *val)
{
int rc;
u32 auxclk_div = 0;
struct mdss_pll_resources *dp_res = context;
rc = mdss_pll_resource_enable(dp_res, true);
if (rc) {
pr_err("Failed to enable dp_res resources\n");
return rc;
}
auxclk_div = MDSS_PLL_REG_R(dp_res->phy_base, DP_PHY_VCO_DIV);
auxclk_div &= 0x03;
if (auxclk_div == 1) /* Default divider */
*val = 0;
else if (auxclk_div == 2)
*val = 1;
else if (auxclk_div == 0)
*val = 2;
mdss_pll_resource_enable(dp_res, false);
pr_debug("%s: auxclk_div=%d, val=%d\n", __func__, auxclk_div, *val);
return 0;
}
static int dp_vco_pll_init_db_10nm(struct dp_pll_db *pdb,
unsigned long rate)
{
struct mdss_pll_resources *dp_res = pdb->pll;
u32 spare_value = 0;
spare_value = MDSS_PLL_REG_R(dp_res->phy_base, DP_PHY_SPARE0);
pdb->lane_cnt = spare_value & 0x0F;
pdb->orientation = (spare_value & 0xF0) >> 4;
pr_debug("%s: spare_value=0x%x, ln_cnt=0x%x, orientation=0x%x\n",
__func__, spare_value, pdb->lane_cnt, pdb->orientation);
switch (rate) {
case DP_VCO_HSCLK_RATE_1620MHZDIV1000:
pr_debug("%s: VCO rate: %ld\n", __func__,
DP_VCO_RATE_9720MHZDIV1000);
pdb->hsclk_sel = 0x0c;
pdb->dec_start_mode0 = 0x69;
pdb->div_frac_start1_mode0 = 0x00;
pdb->div_frac_start2_mode0 = 0x80;
pdb->div_frac_start3_mode0 = 0x07;
pdb->integloop_gain0_mode0 = 0x3f;
pdb->integloop_gain1_mode0 = 0x00;
pdb->vco_tune_map = 0x00;
pdb->lock_cmp1_mode0 = 0x6f;
pdb->lock_cmp2_mode0 = 0x08;
pdb->lock_cmp3_mode0 = 0x00;
pdb->phy_vco_div = 0x1;
pdb->lock_cmp_en = 0x00;
break;
case DP_VCO_HSCLK_RATE_2700MHZDIV1000:
pr_debug("%s: VCO rate: %ld\n", __func__,
DP_VCO_RATE_10800MHZDIV1000);
pdb->hsclk_sel = 0x04;
pdb->dec_start_mode0 = 0x69;
pdb->div_frac_start1_mode0 = 0x00;
pdb->div_frac_start2_mode0 = 0x80;
pdb->div_frac_start3_mode0 = 0x07;
pdb->integloop_gain0_mode0 = 0x3f;
pdb->integloop_gain1_mode0 = 0x00;
pdb->vco_tune_map = 0x00;
pdb->lock_cmp1_mode0 = 0x0f;
pdb->lock_cmp2_mode0 = 0x0e;
pdb->lock_cmp3_mode0 = 0x00;
pdb->phy_vco_div = 0x1;
pdb->lock_cmp_en = 0x00;
break;
case DP_VCO_HSCLK_RATE_5400MHZDIV1000:
pr_debug("%s: VCO rate: %ld\n", __func__,
DP_VCO_RATE_10800MHZDIV1000);
pdb->hsclk_sel = 0x00;
pdb->dec_start_mode0 = 0x8c;
pdb->div_frac_start1_mode0 = 0x00;
pdb->div_frac_start2_mode0 = 0x00;
pdb->div_frac_start3_mode0 = 0x0a;
pdb->integloop_gain0_mode0 = 0x3f;
pdb->integloop_gain1_mode0 = 0x00;
pdb->vco_tune_map = 0x00;
pdb->lock_cmp1_mode0 = 0x1f;
pdb->lock_cmp2_mode0 = 0x1c;
pdb->lock_cmp3_mode0 = 0x00;
pdb->phy_vco_div = 0x2;
pdb->lock_cmp_en = 0x00;
break;
case DP_VCO_HSCLK_RATE_8100MHZDIV1000:
pr_debug("%s: VCO rate: %ld\n", __func__,
DP_VCO_RATE_8100MHZDIV1000);
pdb->hsclk_sel = 0x03;
pdb->dec_start_mode0 = 0x69;
pdb->div_frac_start1_mode0 = 0x00;
pdb->div_frac_start2_mode0 = 0x80;
pdb->div_frac_start3_mode0 = 0x07;
pdb->integloop_gain0_mode0 = 0x3f;
pdb->integloop_gain1_mode0 = 0x00;
pdb->vco_tune_map = 0x00;
pdb->lock_cmp1_mode0 = 0x2f;
pdb->lock_cmp2_mode0 = 0x2a;
pdb->lock_cmp3_mode0 = 0x00;
pdb->phy_vco_div = 0x0;
pdb->lock_cmp_en = 0x08;
break;
default:
return -EINVAL;
}
return 0;
}
static int dp_config_vco_rate_10nm(struct dp_pll_vco_clk *vco,
unsigned long rate)
{
u32 res = 0;
struct mdss_pll_resources *dp_res = vco->priv;
struct dp_pll_db *pdb = (struct dp_pll_db *)dp_res->priv;
res = dp_vco_pll_init_db_10nm(pdb, rate);
if (res) {
pr_err("VCO Init DB failed\n");
return res;
}
if (pdb->lane_cnt != 4) {
if (pdb->orientation == ORIENTATION_CC2)
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_PD_CTL, 0x6d);
else
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_PD_CTL, 0x75);
} else {
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_PD_CTL, 0x7d);
}
/* Make sure the PHY register writes are done */
wmb();
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_SVS_MODE_CLK_SEL, 0x01);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYSCLK_EN_SEL, 0x37);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYS_CLK_CTRL, 0x02);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_CLK_ENABLE1, 0x0e);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x06);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_CLK_SEL, 0x30);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_CMN_CONFIG, 0x02);
/* Different for each clock rates */
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_HSCLK_SEL, pdb->hsclk_sel);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_DEC_START_MODE0, pdb->dec_start_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_DIV_FRAC_START1_MODE0, pdb->div_frac_start1_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_DIV_FRAC_START2_MODE0, pdb->div_frac_start2_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_DIV_FRAC_START3_MODE0, pdb->div_frac_start3_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_INTEGLOOP_GAIN0_MODE0, pdb->integloop_gain0_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_INTEGLOOP_GAIN1_MODE0, pdb->integloop_gain1_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_VCO_TUNE_MAP, pdb->vco_tune_map);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_LOCK_CMP1_MODE0, pdb->lock_cmp1_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_LOCK_CMP2_MODE0, pdb->lock_cmp2_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_LOCK_CMP3_MODE0, pdb->lock_cmp3_mode0);
/* Make sure the PLL register writes are done */
wmb();
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_BG_TIMER, 0x0a);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_CORECLK_DIV_MODE0, 0x0a);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_VCO_TUNE_CTRL, 0x00);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3f);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_CORE_CLK_EN, 0x1f);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_IVCO, 0x07);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_LOCK_CMP_EN, pdb->lock_cmp_en);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_CCTRL_MODE0, 0x36);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_RCTRL_MODE0, 0x16);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_CP_CTRL_MODE0, 0x06);
/* Make sure the PHY register writes are done */
wmb();
if (pdb->orientation == ORIENTATION_CC2)
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_MODE, 0x4c);
else
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_MODE, 0x5c);
/* Make sure the PLL register writes are done */
wmb();
/* TX Lane configuration */
MDSS_PLL_REG_W(dp_res->phy_base,
DP_PHY_TX0_TX1_LANE_CTL, 0x05);
MDSS_PLL_REG_W(dp_res->phy_base,
DP_PHY_TX2_TX3_LANE_CTL, 0x05);
/* TX-0 register configuration */
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_TRANSCEIVER_BIAS_EN, 0x1a);
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_VMODE_CTRL1, 0x40);
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_INTERFACE_SELECT, 0x3d);
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_CLKBUF_ENABLE, 0x0f);
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_RESET_TSYNC_EN, 0x03);
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_TRAN_DRVR_EMP_EN, 0x03);
MDSS_PLL_REG_W(dp_res->ln_tx0_base,
TXn_PARRATE_REC_DETECT_IDLE_EN, 0x00);
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_TX_INTERFACE_MODE, 0x00);
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_TX_BAND, 0x4);
/* TX-1 register configuration */
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_TRANSCEIVER_BIAS_EN, 0x1a);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_VMODE_CTRL1, 0x40);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_INTERFACE_SELECT, 0x3d);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_CLKBUF_ENABLE, 0x0f);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_RESET_TSYNC_EN, 0x03);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_TRAN_DRVR_EMP_EN, 0x03);
MDSS_PLL_REG_W(dp_res->ln_tx1_base,
TXn_PARRATE_REC_DETECT_IDLE_EN, 0x00);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_TX_INTERFACE_MODE, 0x00);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_TX_BAND, 0x4);
/* Make sure the PHY register writes are done */
wmb();
/* dependent on the vco frequency */
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_VCO_DIV, pdb->phy_vco_div);
return res;
}
static bool dp_10nm_pll_lock_status(struct mdss_pll_resources *dp_res)
{
u32 status;
bool pll_locked;
/* poll for PLL lock status */
if (readl_poll_timeout_atomic((dp_res->pll_base +
QSERDES_COM_C_READY_STATUS),
status,
((status & BIT(0)) > 0),
DP_PHY_PLL_POLL_SLEEP_US,
DP_PHY_PLL_POLL_TIMEOUT_US)) {
pr_err("%s: C_READY status is not high. Status=%x\n",
__func__, status);
pll_locked = false;
} else {
pll_locked = true;
}
return pll_locked;
}
static bool dp_10nm_phy_rdy_status(struct mdss_pll_resources *dp_res)
{
u32 status;
bool phy_ready = true;
/* poll for PHY ready status */
if (readl_poll_timeout_atomic((dp_res->phy_base +
DP_PHY_STATUS),
status,
((status & (BIT(1))) > 0),
DP_PHY_PLL_POLL_SLEEP_US,
DP_PHY_PLL_POLL_TIMEOUT_US)) {
pr_err("%s: Phy_ready is not high. Status=%x\n",
__func__, status);
phy_ready = false;
}
return phy_ready;
}
static int dp_pll_enable_10nm(struct clk_hw *hw)
{
int rc = 0;
struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
struct mdss_pll_resources *dp_res = vco->priv;
struct dp_pll_db *pdb = (struct dp_pll_db *)dp_res->priv;
u32 bias_en, drvr_en;
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_AUX_CFG2, 0x04);
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x01);
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x05);
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x01);
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x09);
wmb(); /* Make sure the PHY register writes are done */
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_RESETSM_CNTRL, 0x20);
wmb(); /* Make sure the PLL register writes are done */
if (!dp_10nm_pll_lock_status(dp_res)) {
rc = -EINVAL;
goto lock_err;
}
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x19);
/* Make sure the PHY register writes are done */
wmb();
/* poll for PHY ready status */
if (!dp_10nm_phy_rdy_status(dp_res)) {
rc = -EINVAL;
goto lock_err;
}
pr_debug("%s: PLL is locked\n", __func__);
if (pdb->lane_cnt == 1) {
bias_en = 0x3e;
drvr_en = 0x13;
} else {
bias_en = 0x3f;
drvr_en = 0x10;
}
if (pdb->lane_cnt != 4) {
if (pdb->orientation == ORIENTATION_CC1) {
MDSS_PLL_REG_W(dp_res->ln_tx1_base,
TXn_HIGHZ_DRVR_EN, drvr_en);
MDSS_PLL_REG_W(dp_res->ln_tx1_base,
TXn_TRANSCEIVER_BIAS_EN, bias_en);
} else {
MDSS_PLL_REG_W(dp_res->ln_tx0_base,
TXn_HIGHZ_DRVR_EN, drvr_en);
MDSS_PLL_REG_W(dp_res->ln_tx0_base,
TXn_TRANSCEIVER_BIAS_EN, bias_en);
}
} else {
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_HIGHZ_DRVR_EN, drvr_en);
MDSS_PLL_REG_W(dp_res->ln_tx0_base,
TXn_TRANSCEIVER_BIAS_EN, bias_en);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_HIGHZ_DRVR_EN, drvr_en);
MDSS_PLL_REG_W(dp_res->ln_tx1_base,
TXn_TRANSCEIVER_BIAS_EN, bias_en);
}
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_TX_POL_INV, 0x0a);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_TX_POL_INV, 0x0a);
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x18);
udelay(2000);
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x19);
/*
* Make sure all the register writes are completed before
* doing any other operation
*/
wmb();
/* poll for PHY ready status */
if (!dp_10nm_phy_rdy_status(dp_res)) {
rc = -EINVAL;
goto lock_err;
}
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_TX_DRV_LVL, 0x38);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_TX_DRV_LVL, 0x38);
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_TX_EMP_POST1_LVL, 0x20);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_TX_EMP_POST1_LVL, 0x20);
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_RES_CODE_LANE_OFFSET_TX, 0x06);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_RES_CODE_LANE_OFFSET_TX, 0x06);
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_RES_CODE_LANE_OFFSET_RX, 0x07);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_RES_CODE_LANE_OFFSET_RX, 0x07);
/* Make sure the PHY register writes are done */
wmb();
lock_err:
return rc;
}
static int dp_pll_disable_10nm(struct clk_hw *hw)
{
struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
struct mdss_pll_resources *dp_res = vco->priv;
/* Assert DP PHY power down */
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_PD_CTL, 0x2);
/*
* Make sure all the register writes to disable PLL are
* completed before doing any other operation
*/
wmb();
return 0;
}
int dp_vco_prepare_10nm(struct clk_hw *hw)
{
int rc = 0;
struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
struct mdss_pll_resources *dp_res = vco->priv;
pr_debug("rate=%ld\n", vco->rate);
rc = mdss_pll_resource_enable(dp_res, true);
if (rc) {
pr_err("Failed to enable mdss DP pll resources\n");
goto error;
}
if ((dp_res->vco_cached_rate != 0)
&& (dp_res->vco_cached_rate == vco->rate)) {
rc = vco->hw.init->ops->set_rate(hw,
dp_res->vco_cached_rate, dp_res->vco_cached_rate);
if (rc) {
pr_err("index=%d vco_set_rate failed. rc=%d\n",
rc, dp_res->index);
mdss_pll_resource_enable(dp_res, false);
goto error;
}
}
rc = dp_pll_enable_10nm(hw);
if (rc) {
mdss_pll_resource_enable(dp_res, false);
pr_err("ndx=%d failed to enable dp pll\n",
dp_res->index);
goto error;
}
mdss_pll_resource_enable(dp_res, false);
error:
return rc;
}
void dp_vco_unprepare_10nm(struct clk_hw *hw)
{
struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
struct mdss_pll_resources *dp_res = vco->priv;
if (!dp_res) {
pr_err("Invalid input parameter\n");
return;
}
if (!dp_res->pll_on &&
mdss_pll_resource_enable(dp_res, true)) {
pr_err("pll resource can't be enabled\n");
return;
}
dp_res->vco_cached_rate = vco->rate;
dp_pll_disable_10nm(hw);
dp_res->handoff_resources = false;
mdss_pll_resource_enable(dp_res, false);
dp_res->pll_on = false;
}
int dp_vco_set_rate_10nm(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
struct mdss_pll_resources *dp_res = vco->priv;
int rc;
rc = mdss_pll_resource_enable(dp_res, true);
if (rc) {
pr_err("pll resource can't be enabled\n");
return rc;
}
pr_debug("DP lane CLK rate=%ld\n", rate);
rc = dp_config_vco_rate_10nm(vco, rate);
if (rc)
pr_err("%s: Failed to set clk rate\n", __func__);
mdss_pll_resource_enable(dp_res, false);
vco->rate = rate;
return 0;
}
unsigned long dp_vco_recalc_rate_10nm(struct clk_hw *hw,
unsigned long parent_rate)
{
struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
int rc;
u32 div, hsclk_div, link_clk_div = 0;
u64 vco_rate;
struct mdss_pll_resources *dp_res = vco->priv;
rc = mdss_pll_resource_enable(dp_res, true);
if (rc) {
pr_err("Failed to enable mdss DP pll=%d\n", dp_res->index);
return rc;
}
div = MDSS_PLL_REG_R(dp_res->pll_base, QSERDES_COM_HSCLK_SEL);
div &= 0x0f;
if (div == 12)
hsclk_div = 6; /* Default */
else if (div == 4)
hsclk_div = 4;
else if (div == 0)
hsclk_div = 2;
else if (div == 3)
hsclk_div = 1;
else {
pr_debug("unknown divider. forcing to default\n");
hsclk_div = 5;
}
div = MDSS_PLL_REG_R(dp_res->phy_base, DP_PHY_AUX_CFG2);
div >>= 2;
if ((div & 0x3) == 0)
link_clk_div = 5;
else if ((div & 0x3) == 1)
link_clk_div = 10;
else if ((div & 0x3) == 2)
link_clk_div = 20;
else
pr_err("%s: unsupported div. Phy_mode: %d\n", __func__, div);
if (link_clk_div == 20) {
vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
} else {
if (hsclk_div == 6)
vco_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000;
else if (hsclk_div == 4)
vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
else if (hsclk_div == 2)
vco_rate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
else
vco_rate = DP_VCO_HSCLK_RATE_8100MHZDIV1000;
}
pr_debug("returning vco rate = %lu\n", (unsigned long)vco_rate);
mdss_pll_resource_enable(dp_res, false);
dp_res->vco_cached_rate = vco->rate = vco_rate;
return (unsigned long)vco_rate;
}
long dp_vco_round_rate_10nm(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
unsigned long rrate = rate;
struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
if (rate <= vco->min_rate)
rrate = vco->min_rate;
else if (rate <= DP_VCO_HSCLK_RATE_2700MHZDIV1000)
rrate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
else if (rate <= DP_VCO_HSCLK_RATE_5400MHZDIV1000)
rrate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
else
rrate = vco->max_rate;
pr_debug("%s: rrate=%ld\n", __func__, rrate);
*parent_rate = rrate;
return rrate;
}

View File

@@ -1,820 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
*/
/*
***************************************************************************
******** Display Port PLL driver block diagram for branch clocks **********
***************************************************************************
+--------------------------+
| DP_VCO_CLK |
| |
| +-------------------+ |
| | (DP PLL/VCO) | |
| +---------+---------+ |
| v |
| +----------+-----------+ |
| | hsclk_divsel_clk_src | |
| +----------+-----------+ |
+--------------------------+
|
v
+------------<------------|------------>-------------+
| | |
+----------v----------+ +----------v----------+ +----------v----------+
| dp_link_2x_clk | | vco_divided_clk_src | | vco_divided_clk_src |
| divsel_five | | | | |
v----------+----------v | divsel_two | | divsel_four |
| +----------+----------+ +----------+----------+
| | |
v v v
| +---------------------+ |
Input to MMSSCC block | | (aux_clk_ops) | |
for link clk, crypto clk +--> vco_divided_clk <-+
and interface clock | _src_mux |
+----------+----------+
|
v
Input to MMSSCC block
for DP pixel clock
******************************************************************************
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/iopoll.h>
#include <linux/delay.h>
#include <linux/usb/usbpd.h>
#include <dt-bindings/clock/mdss-14nm-pll-clk.h>
#include "pll_drv.h"
#include "dp_pll.h"
#include "dp_pll_14nm.h"
static struct dp_pll_db dp_pdb;
static struct clk_ops mux_clk_ops;
static struct regmap_config dp_pll_14nm_cfg = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = 0x910,
};
static struct regmap_bus dp_pixel_mux_regmap_ops = {
.reg_write = dp_mux_set_parent_14nm,
.reg_read = dp_mux_get_parent_14nm,
};
/* Op structures */
static const struct clk_ops dp_14nm_vco_clk_ops = {
.recalc_rate = dp_vco_recalc_rate_14nm,
.set_rate = dp_vco_set_rate_14nm,
.round_rate = dp_vco_round_rate_14nm,
.prepare = dp_vco_prepare_14nm,
.unprepare = dp_vco_unprepare_14nm,
};
static struct dp_pll_vco_clk dp_vco_clk = {
.min_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000,
.max_rate = DP_VCO_HSCLK_RATE_5400MHZDIV1000,
.hw.init = &(struct clk_init_data){
.name = "dp_vco_clk",
.parent_names = (const char *[]){ "xo_board" },
.num_parents = 1,
.ops = &dp_14nm_vco_clk_ops,
},
};
static struct clk_fixed_factor dp_phy_pll_link_clk = {
.div = 10,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dp_phy_pll_link_clk",
.parent_names =
(const char *[]){ "dp_vco_clk" },
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor dp_vco_divsel_two_clk_src = {
.div = 2,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dp_vco_divsel_two_clk_src",
.parent_names =
(const char *[]){ "dp_vco_clk" },
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor dp_vco_divsel_four_clk_src = {
.div = 4,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dp_vco_divsel_four_clk_src",
.parent_names =
(const char *[]){ "dp_vco_clk" },
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE),
.ops = &clk_fixed_factor_ops,
},
};
static int clk_mux_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
int ret = 0;
ret = __clk_mux_determine_rate_closest(hw, req);
if (ret)
return ret;
/* Set the new parent of mux if there is a new valid parent */
if (hw->clk && req->best_parent_hw->clk)
clk_set_parent(hw->clk, req->best_parent_hw->clk);
return 0;
}
static unsigned long mux_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk *div_clk = NULL, *vco_clk = NULL;
struct dp_pll_vco_clk *vco = NULL;
div_clk = clk_get_parent(hw->clk);
if (!div_clk)
return 0;
vco_clk = clk_get_parent(div_clk);
if (!vco_clk)
return 0;
vco = to_dp_vco_hw(__clk_get_hw(vco_clk));
if (!vco)
return 0;
if (vco->rate == DP_VCO_HSCLK_RATE_5400MHZDIV1000)
return (vco->rate / 4);
else
return (vco->rate / 2);
}
static struct clk_regmap_mux dp_phy_pll_vco_div_clk = {
.reg = 0x64,
.shift = 0,
.width = 1,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dp_phy_pll_vco_div_clk",
.parent_names =
(const char *[]){"dp_vco_divsel_two_clk_src",
"dp_vco_divsel_four_clk_src"},
.num_parents = 2,
.ops = &mux_clk_ops,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
},
},
};
static struct clk_hw *mdss_dp_pllcc_14nm[] = {
[DP_VCO_CLK] = &dp_vco_clk.hw,
[DP_PHY_PLL_LINK_CLK] = &dp_phy_pll_link_clk.hw,
[DP_VCO_DIVSEL_FOUR_CLK_SRC] = &dp_vco_divsel_four_clk_src.hw,
[DP_VCO_DIVSEL_TWO_CLK_SRC] = &dp_vco_divsel_two_clk_src.hw,
[DP_PHY_PLL_VCO_DIV_CLK] = &dp_phy_pll_vco_div_clk.clkr.hw,
};
int dp_mux_set_parent_14nm(void *context, unsigned int reg, unsigned int val)
{
struct mdss_pll_resources *dp_res = context;
int rc;
u32 auxclk_div;
rc = mdss_pll_resource_enable(dp_res, true);
if (rc) {
pr_err("Failed to enable mdss DP PLL resources\n");
return rc;
}
auxclk_div = MDSS_PLL_REG_R(dp_res->phy_base, DP_PHY_VCO_DIV);
auxclk_div &= ~0x03; /* bits 0 to 1 */
if (val == 0) /* mux parent index = 0 */
auxclk_div |= 1;
else if (val == 1) /* mux parent index = 1 */
auxclk_div |= 2;
MDSS_PLL_REG_W(dp_res->phy_base,
DP_PHY_VCO_DIV, auxclk_div);
/* Make sure the PHY registers writes are done */
wmb();
pr_debug("mux=%d auxclk_div=%x\n", val, auxclk_div);
mdss_pll_resource_enable(dp_res, false);
return 0;
}
int dp_mux_get_parent_14nm(void *context, unsigned int reg, unsigned int *val)
{
int rc;
u32 auxclk_div = 0;
struct mdss_pll_resources *dp_res = context;
rc = mdss_pll_resource_enable(dp_res, true);
if (rc) {
pr_err("Failed to enable dp_res resources\n");
return rc;
}
auxclk_div = MDSS_PLL_REG_R(dp_res->phy_base, DP_PHY_VCO_DIV);
auxclk_div &= 0x03;
if (auxclk_div == 1) /* Default divider */
*val = 0;
else if (auxclk_div == 2)
*val = 1;
mdss_pll_resource_enable(dp_res, false);
pr_debug("auxclk_div=%d, val=%d\n", auxclk_div, *val);
return 0;
}
static int dp_vco_pll_init_db_14nm(struct dp_pll_db *pdb,
unsigned long rate)
{
struct mdss_pll_resources *dp_res = pdb->pll;
u32 spare_value = 0;
spare_value = MDSS_PLL_REG_R(dp_res->phy_base, DP_PHY_SPARE0);
pdb->lane_cnt = spare_value & 0x0F;
pdb->orientation = (spare_value & 0xF0) >> 4;
pr_debug("spare_value=0x%x, ln_cnt=0x%x, orientation=0x%x\n",
spare_value, pdb->lane_cnt, pdb->orientation);
switch (rate) {
case DP_VCO_HSCLK_RATE_1620MHZDIV1000:
pdb->hsclk_sel = 0x2c;
pdb->dec_start_mode0 = 0x69;
pdb->div_frac_start1_mode0 = 0x00;
pdb->div_frac_start2_mode0 = 0x80;
pdb->div_frac_start3_mode0 = 0x07;
pdb->lock_cmp1_mode0 = 0xbf;
pdb->lock_cmp2_mode0 = 0x21;
pdb->lock_cmp3_mode0 = 0x00;
pdb->phy_vco_div = 0x1;
pdb->lane_mode_1 = 0xc6;
break;
case DP_VCO_HSCLK_RATE_2700MHZDIV1000:
pdb->hsclk_sel = 0x24;
pdb->dec_start_mode0 = 0x69;
pdb->div_frac_start1_mode0 = 0x00;
pdb->div_frac_start2_mode0 = 0x80;
pdb->div_frac_start3_mode0 = 0x07;
pdb->lock_cmp1_mode0 = 0x3f;
pdb->lock_cmp2_mode0 = 0x38;
pdb->lock_cmp3_mode0 = 0x00;
pdb->phy_vco_div = 0x1;
pdb->lane_mode_1 = 0xc4;
break;
case DP_VCO_HSCLK_RATE_5400MHZDIV1000:
pdb->hsclk_sel = 0x20;
pdb->dec_start_mode0 = 0x8c;
pdb->div_frac_start1_mode0 = 0x00;
pdb->div_frac_start2_mode0 = 0x00;
pdb->div_frac_start3_mode0 = 0x0a;
pdb->lock_cmp1_mode0 = 0x7f;
pdb->lock_cmp2_mode0 = 0x70;
pdb->lock_cmp3_mode0 = 0x00;
pdb->phy_vco_div = 0x2;
pdb->lane_mode_1 = 0xc4;
break;
default:
return -EINVAL;
}
return 0;
}
int dp_config_vco_rate_14nm(struct dp_pll_vco_clk *vco,
unsigned long rate)
{
u32 res = 0;
struct mdss_pll_resources *dp_res = vco->priv;
struct dp_pll_db *pdb = (struct dp_pll_db *)dp_res->priv;
res = dp_vco_pll_init_db_14nm(pdb, rate);
if (res) {
pr_err("VCO Init DB failed\n");
return res;
}
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_PD_CTL, 0x3d);
/* Make sure the PHY register writes are done */
wmb();
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_SVS_MODE_CLK_SEL, 0x01);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_SYSCLK_EN_SEL, 0x37);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_CLK_SELECT, 0x00);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_SYS_CLK_CTRL, 0x06);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3f);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_CLK_ENABLE1, 0x0e);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_BG_CTRL, 0x0f);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_SYSCLK_BUF_ENABLE, 0x06);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_CLK_SELECT, 0x30);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_PLL_IVCO, 0x0f);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_PLL_CCTRL_MODE0, 0x28);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_PLL_RCTRL_MODE0, 0x16);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_CP_CTRL_MODE0, 0x0b);
/* Parameters dependent on vco clock frequency */
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_HSCLK_SEL, pdb->hsclk_sel);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_DEC_START_MODE0, pdb->dec_start_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_DIV_FRAC_START1_MODE0, pdb->div_frac_start1_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_DIV_FRAC_START2_MODE0, pdb->div_frac_start2_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_DIV_FRAC_START3_MODE0, pdb->div_frac_start3_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_LOCK_CMP1_MODE0, pdb->lock_cmp1_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_LOCK_CMP2_MODE0, pdb->lock_cmp2_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_LOCK_CMP3_MODE0, pdb->lock_cmp3_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x40);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_VCO_TUNE_MAP, 0x00);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_BG_TIMER, 0x08);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_CORECLK_DIV, 0x05);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_VCO_TUNE_CTRL, 0x00);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_VCO_TUNE1_MODE0, 0x00);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_VCO_TUNE2_MODE0, 0x00);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_VCO_TUNE_CTRL, 0x00);
wmb(); /* make sure write happens */
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_CORE_CLK_EN, 0x0f);
wmb(); /* make sure write happens */
if (pdb->orientation == ORIENTATION_CC2)
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_MODE, 0xc9);
else
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_MODE, 0xd9);
wmb(); /* make sure write happens */
/* TX Lane configuration */
MDSS_PLL_REG_W(dp_res->phy_base,
DP_PHY_TX0_TX1_LANE_CTL, 0x05);
MDSS_PLL_REG_W(dp_res->phy_base,
DP_PHY_TX2_TX3_LANE_CTL, 0x05);
/* TX-0 register configuration */
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_TRANSCEIVER_BIAS_EN, 0x1a);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_VMODE_CTRL1, 0x40);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_INTERFACE_SELECT, 0x3d);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_CLKBUF_ENABLE, 0x0f);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_RESET_TSYNC_EN, 0x03);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_TRAN_DRVR_EMP_EN, 0x03);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_PARRATE_REC_DETECT_IDLE_EN, 0x00);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_TX_INTERFACE_MODE, 0x00);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_TX_EMP_POST1_LVL, 0x2b);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_TX_DRV_LVL, 0x2f);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_TX_BAND, 0x4);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_RES_CODE_LANE_OFFSET_TX, 0x12);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_RES_CODE_LANE_OFFSET_RX, 0x12);
/* TX-1 register configuration */
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_TRANSCEIVER_BIAS_EN, 0x1a);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_VMODE_CTRL1, 0x40);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_INTERFACE_SELECT, 0x3d);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_CLKBUF_ENABLE, 0x0f);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_RESET_TSYNC_EN, 0x03);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_TRAN_DRVR_EMP_EN, 0x03);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_PARRATE_REC_DETECT_IDLE_EN, 0x00);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_TX_INTERFACE_MODE, 0x00);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_TX_EMP_POST1_LVL, 0x2b);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_TX_DRV_LVL, 0x2f);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_TX_BAND, 0x4);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_RES_CODE_LANE_OFFSET_TX, 0x12);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_RES_CODE_LANE_OFFSET_RX, 0x12);
wmb(); /* make sure write happens */
/* PHY VCO divider programming */
MDSS_PLL_REG_W(dp_res->phy_base,
DP_PHY_VCO_DIV, pdb->phy_vco_div);
wmb(); /* make sure write happens */
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_CMN_CONFIG, 0x02);
wmb(); /* make sure write happens */
return res;
}
static bool dp_14nm_pll_lock_status(struct mdss_pll_resources *dp_res)
{
u32 status;
bool pll_locked;
/* poll for PLL lock status */
if (readl_poll_timeout_atomic((dp_res->pll_base +
QSERDES_COM_C_READY_STATUS),
status,
((status & BIT(0)) > 0),
DP_PLL_POLL_SLEEP_US,
DP_PLL_POLL_TIMEOUT_US)) {
pr_err("C_READY status is not high. Status=%x\n", status);
pll_locked = false;
} else {
pll_locked = true;
}
return pll_locked;
}
static bool dp_14nm_phy_rdy_status(struct mdss_pll_resources *dp_res)
{
u32 status;
bool phy_ready = true;
/* poll for PHY ready status */
if (readl_poll_timeout_atomic((dp_res->phy_base +
DP_PHY_STATUS),
status,
((status & (BIT(1) | BIT(0))) > 0),
DP_PHY_POLL_SLEEP_US,
DP_PHY_POLL_TIMEOUT_US)) {
pr_err("Phy_ready is not high. Status=%x\n", status);
phy_ready = false;
}
return phy_ready;
}
static int dp_pll_enable_14nm(struct clk_hw *hw)
{
int rc = 0;
struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
struct mdss_pll_resources *dp_res = vco->priv;
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x01);
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x05);
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x01);
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x09);
wmb(); /* Make sure the PHY register writes are done */
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_RESETSM_CNTRL, 0x20);
wmb(); /* Make sure the PLL register writes are done */
udelay(900); /* hw recommended delay for full PU */
if (!dp_14nm_pll_lock_status(dp_res)) {
rc = -EINVAL;
goto lock_err;
}
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x19);
wmb(); /* Make sure the PHY register writes are done */
udelay(10); /* hw recommended delay */
if (!dp_14nm_phy_rdy_status(dp_res)) {
rc = -EINVAL;
goto lock_err;
}
pr_debug("PLL is locked\n");
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_TRANSCEIVER_BIAS_EN, 0x3f);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_HIGHZ_DRVR_EN, 0x10);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_TRANSCEIVER_BIAS_EN, 0x3f);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_HIGHZ_DRVR_EN, 0x10);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_TX_POL_INV, 0x0a);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_TX_POL_INV, 0x0a);
/*
* Switch DP Mainlink clock (cc_dpphy_link_clk) from DP
* controller side with final frequency
*/
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x18);
wmb(); /* Make sure the PHY register writes are done */
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x19);
wmb(); /* Make sure the PHY register writes are done */
lock_err:
return rc;
}
static int dp_pll_disable_14nm(struct clk_hw *hw)
{
struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
struct mdss_pll_resources *dp_res = vco->priv;
/* Assert DP PHY power down */
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_PD_CTL, 0x2);
/*
* Make sure all the register writes to disable PLL are
* completed before doing any other operation
*/
wmb();
return 0;
}
int dp_vco_prepare_14nm(struct clk_hw *hw)
{
int rc = 0;
struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
struct mdss_pll_resources *dp_res = vco->priv;
pr_debug("rate=%ld\n", vco->rate);
rc = mdss_pll_resource_enable(dp_res, true);
if (rc) {
pr_err("Failed to enable mdss DP pll resources\n");
goto error;
}
if ((dp_res->vco_cached_rate != 0)
&& (dp_res->vco_cached_rate == vco->rate)) {
rc = vco->hw.init->ops->set_rate(hw,
dp_res->vco_cached_rate, dp_res->vco_cached_rate);
if (rc) {
pr_err("index=%d vco_set_rate failed. rc=%d\n",
rc, dp_res->index);
mdss_pll_resource_enable(dp_res, false);
goto error;
}
}
rc = dp_pll_enable_14nm(hw);
if (rc) {
mdss_pll_resource_enable(dp_res, false);
pr_err("ndx=%d failed to enable dp pll\n",
dp_res->index);
goto error;
}
mdss_pll_resource_enable(dp_res, false);
error:
return rc;
}
void dp_vco_unprepare_14nm(struct clk_hw *hw)
{
struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
struct mdss_pll_resources *dp_res = vco->priv;
if (!dp_res) {
pr_err("Invalid input parameter\n");
return;
}
if (!dp_res->pll_on &&
mdss_pll_resource_enable(dp_res, true)) {
pr_err("pll resource can't be enabled\n");
return;
}
dp_res->vco_cached_rate = vco->rate;
dp_pll_disable_14nm(hw);
dp_res->handoff_resources = false;
mdss_pll_resource_enable(dp_res, false);
dp_res->pll_on = false;
}
int dp_vco_set_rate_14nm(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
struct mdss_pll_resources *dp_res = vco->priv;
int rc;
rc = mdss_pll_resource_enable(dp_res, true);
if (rc) {
pr_err("pll resource can't be enabled\n");
return rc;
}
pr_debug("DP lane CLK rate=%ld\n", rate);
rc = dp_config_vco_rate_14nm(vco, rate);
if (rc)
pr_err("Failed to set clk rate\n");
mdss_pll_resource_enable(dp_res, false);
vco->rate = rate;
return 0;
}
unsigned long dp_vco_recalc_rate_14nm(struct clk_hw *hw,
unsigned long parent_rate)
{
struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
int rc;
u32 div, hsclk_div;
u64 vco_rate;
struct mdss_pll_resources *dp_res = vco->priv;
if (is_gdsc_disabled(dp_res))
return 0;
rc = mdss_pll_resource_enable(dp_res, true);
if (rc) {
pr_err("Failed to enable mdss DP pll=%d\n", dp_res->index);
return rc;
}
div = MDSS_PLL_REG_R(dp_res->pll_base, QSERDES_COM_HSCLK_SEL);
div &= 0x0f;
if (div == 12)
hsclk_div = 5; /* Default */
else if (div == 4)
hsclk_div = 3;
else if (div == 0)
hsclk_div = 2;
else {
pr_debug("unknown divider. forcing to default\n");
hsclk_div = 5;
}
if (hsclk_div == 5)
vco_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000;
else if (hsclk_div == 3)
vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
else
vco_rate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
pr_debug("returning vco rate = %lu\n", (unsigned long)vco_rate);
mdss_pll_resource_enable(dp_res, false);
dp_res->vco_cached_rate = vco->rate = vco_rate;
return (unsigned long)vco_rate;
}
long dp_vco_round_rate_14nm(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
unsigned long rrate = rate;
struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
if (rate <= vco->min_rate)
rrate = vco->min_rate;
else if (rate <= DP_VCO_HSCLK_RATE_2700MHZDIV1000)
rrate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
else
rrate = vco->max_rate;
pr_debug("rrate=%ld\n", rrate);
*parent_rate = rrate;
return rrate;
}
int dp_pll_clock_register_14nm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc = -ENOTSUPP, i = 0;
struct clk_onecell_data *clk_data;
struct clk *clk;
struct regmap *regmap;
int num_clks = ARRAY_SIZE(mdss_dp_pllcc_14nm);
clk_data = devm_kzalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
clk_data->clks = devm_kcalloc(&pdev->dev, num_clks,
sizeof(struct clk *), GFP_KERNEL);
if (!clk_data->clks)
return -ENOMEM;
clk_data->clk_num = num_clks;
pll_res->priv = &dp_pdb;
dp_pdb.pll = pll_res;
/* Set client data for vco, mux and div clocks */
regmap = devm_regmap_init(&pdev->dev, &dp_pixel_mux_regmap_ops,
pll_res, &dp_pll_14nm_cfg);
dp_phy_pll_vco_div_clk.clkr.regmap = regmap;
mux_clk_ops = clk_regmap_mux_closest_ops;
mux_clk_ops.determine_rate = clk_mux_determine_rate;
mux_clk_ops.recalc_rate = mux_recalc_rate;
dp_vco_clk.priv = pll_res;
for (i = DP_VCO_CLK; i <= DP_PHY_PLL_VCO_DIV_CLK; i++) {
pr_debug("reg clk: %d index: %d\n", i, pll_res->index);
clk = devm_clk_register(&pdev->dev,
mdss_dp_pllcc_14nm[i]);
if (IS_ERR(clk)) {
pr_err("clk registration failed for DP: %d\n",
pll_res->index);
rc = -EINVAL;
goto clk_reg_fail;
}
clk_data->clks[i] = clk;
}
rc = of_clk_add_provider(pdev->dev.of_node,
of_clk_src_onecell_get, clk_data);
if (rc) {
pr_err("Clock register failed rc=%d\n", rc);
rc = -EPROBE_DEFER;
} else {
pr_debug("SUCCESS\n");
}
return 0;
clk_reg_fail:
return rc;
}

View File

@@ -1,188 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
*/
#ifndef __MDSS_DP_PLL_14NM_H
#define __MDSS_DP_PLL_14NM_H
#define DP_PHY_REVISION_ID0 0x0000
#define DP_PHY_REVISION_ID1 0x0004
#define DP_PHY_REVISION_ID2 0x0008
#define DP_PHY_REVISION_ID3 0x000C
#define DP_PHY_CFG 0x0010
#define DP_PHY_CFG_1 0x0014
#define DP_PHY_PD_CTL 0x0018
#define DP_PHY_MODE 0x001C
#define DP_PHY_AUX_CFG0 0x0020
#define DP_PHY_AUX_CFG1 0x0024
#define DP_PHY_AUX_CFG2 0x0028
#define DP_PHY_AUX_CFG3 0x002C
#define DP_PHY_AUX_CFG4 0x0030
#define DP_PHY_AUX_CFG5 0x0034
#define DP_PHY_AUX_CFG6 0x0038
#define DP_PHY_AUX_CFG7 0x003C
#define DP_PHY_AUX_CFG8 0x0040
#define DP_PHY_AUX_CFG9 0x0044
#define DP_PHY_AUX_INTERRUPT_MASK 0x0048
#define DP_PHY_AUX_INTERRUPT_CLEAR 0x004C
#define DP_PHY_AUX_BIST_CFG 0x0050
#define DP_PHY_VCO_DIV 0x0068
#define DP_PHY_TX0_TX1_LANE_CTL 0x006C
#define DP_PHY_TX2_TX3_LANE_CTL 0x0088
#define DP_PHY_SPARE0 0x00AC
#define DP_PHY_STATUS 0x00C0
/* Tx registers */
#define QSERDES_TX0_OFFSET 0x0400
#define QSERDES_TX1_OFFSET 0x0800
#define TXn_BIST_MODE_LANENO 0x0000
#define TXn_CLKBUF_ENABLE 0x0008
#define TXn_TX_EMP_POST1_LVL 0x000C
#define TXn_TX_DRV_LVL 0x001C
#define TXn_RESET_TSYNC_EN 0x0024
#define TXn_PRE_STALL_LDO_BOOST_EN 0x0028
#define TXn_TX_BAND 0x002C
#define TXn_SLEW_CNTL 0x0030
#define TXn_INTERFACE_SELECT 0x0034
#define TXn_RES_CODE_LANE_TX 0x003C
#define TXn_RES_CODE_LANE_RX 0x0040
#define TXn_RES_CODE_LANE_OFFSET_TX 0x0044
#define TXn_RES_CODE_LANE_OFFSET_RX 0x0048
#define TXn_DEBUG_BUS_SEL 0x0058
#define TXn_TRANSCEIVER_BIAS_EN 0x005C
#define TXn_HIGHZ_DRVR_EN 0x0060
#define TXn_TX_POL_INV 0x0064
#define TXn_PARRATE_REC_DETECT_IDLE_EN 0x0068
#define TXn_LANE_MODE_1 0x008C
#define TXn_TRAN_DRVR_EMP_EN 0x00C0
#define TXn_TX_INTERFACE_MODE 0x00C4
#define TXn_VMODE_CTRL1 0x00F0
/* PLL register offset */
#define QSERDES_COM_ATB_SEL1 0x0000
#define QSERDES_COM_ATB_SEL2 0x0004
#define QSERDES_COM_FREQ_UPDATE 0x0008
#define QSERDES_COM_BG_TIMER 0x000C
#define QSERDES_COM_SSC_EN_CENTER 0x0010
#define QSERDES_COM_SSC_ADJ_PER1 0x0014
#define QSERDES_COM_SSC_ADJ_PER2 0x0018
#define QSERDES_COM_SSC_PER1 0x001C
#define QSERDES_COM_SSC_PER2 0x0020
#define QSERDES_COM_SSC_STEP_SIZE1 0x0024
#define QSERDES_COM_SSC_STEP_SIZE2 0x0028
#define QSERDES_COM_POST_DIV 0x002C
#define QSERDES_COM_POST_DIV_MUX 0x0030
#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x0034
#define QSERDES_COM_CLK_ENABLE1 0x0038
#define QSERDES_COM_SYS_CLK_CTRL 0x003C
#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x0040
#define QSERDES_COM_PLL_EN 0x0044
#define QSERDES_COM_PLL_IVCO 0x0048
#define QSERDES_COM_LOCK_CMP1_MODE0 0x004C
#define QSERDES_COM_LOCK_CMP2_MODE0 0x0050
#define QSERDES_COM_LOCK_CMP3_MODE0 0x0054
#define QSERDES_COM_CP_CTRL_MODE0 0x0078
#define QSERDES_COM_CP_CTRL_MODE1 0x007C
#define QSERDES_COM_PLL_RCTRL_MODE0 0x0084
#define QSERDES_COM_PLL_CCTRL_MODE0 0x0090
#define QSERDES_COM_PLL_CNTRL 0x009C
#define QSERDES_COM_SYSCLK_EN_SEL 0x00AC
#define QSERDES_COM_CML_SYSCLK_SEL 0x00B0
#define QSERDES_COM_RESETSM_CNTRL 0x00B4
#define QSERDES_COM_RESETSM_CNTRL2 0x00B8
#define QSERDES_COM_LOCK_CMP_EN 0x00C8
#define QSERDES_COM_LOCK_CMP_CFG 0x00CC
#define QSERDES_COM_DEC_START_MODE0 0x00D0
#define QSERDES_COM_DEC_START_MODE1 0x00D4
#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x00DC
#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x00E0
#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x00E4
#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x0108
#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x010C
#define QSERDES_COM_VCO_TUNE_CTRL 0x0124
#define QSERDES_COM_VCO_TUNE_MAP 0x0128
#define QSERDES_COM_VCO_TUNE1_MODE0 0x012C
#define QSERDES_COM_VCO_TUNE2_MODE0 0x0130
#define QSERDES_COM_CMN_STATUS 0x015C
#define QSERDES_COM_RESET_SM_STATUS 0x0160
#define QSERDES_COM_BG_CTRL 0x0170
#define QSERDES_COM_CLK_SELECT 0x0174
#define QSERDES_COM_HSCLK_SEL 0x0178
#define QSERDES_COM_CORECLK_DIV 0x0184
#define QSERDES_COM_SW_RESET 0x0188
#define QSERDES_COM_CORE_CLK_EN 0x018C
#define QSERDES_COM_C_READY_STATUS 0x0190
#define QSERDES_COM_CMN_CONFIG 0x0194
#define QSERDES_COM_SVS_MODE_CLK_SEL 0x019C
#define DP_PLL_POLL_SLEEP_US 500
#define DP_PLL_POLL_TIMEOUT_US 10000
#define DP_PHY_POLL_SLEEP_US 500
#define DP_PHY_POLL_TIMEOUT_US 10000
#define DP_VCO_RATE_8100MHZDIV1000 8100000UL
#define DP_VCO_RATE_10800MHZDIV1000 10800000UL
#define DP_VCO_HSCLK_RATE_1620MHZDIV1000 1620000UL
#define DP_VCO_HSCLK_RATE_2700MHZDIV1000 2700000UL
#define DP_VCO_HSCLK_RATE_5400MHZDIV1000 5400000UL
struct dp_pll_db {
struct mdss_pll_resources *pll;
/* lane and orientation settings */
u8 lane_cnt;
u8 orientation;
/* COM PHY settings */
u32 hsclk_sel;
u32 dec_start_mode0;
u32 div_frac_start1_mode0;
u32 div_frac_start2_mode0;
u32 div_frac_start3_mode0;
u32 lock_cmp1_mode0;
u32 lock_cmp2_mode0;
u32 lock_cmp3_mode0;
/* PHY vco divider */
u32 phy_vco_div;
/* TX settings */
u32 lane_mode_1;
};
int dp_vco_set_rate_14nm(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate);
unsigned long dp_vco_recalc_rate_14nm(struct clk_hw *hw,
unsigned long parent_rate);
long dp_vco_round_rate_14nm(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate);
int dp_vco_prepare_14nm(struct clk_hw *hw);
void dp_vco_unprepare_14nm(struct clk_hw *hw);
int dp_mux_set_parent_14nm(void *context,
unsigned int reg, unsigned int val);
int dp_mux_get_parent_14nm(void *context,
unsigned int reg, unsigned int *val);
#endif /* __MDSS_DP_PLL_14NM_H */

View File

@@ -1,341 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
*/
/*
* Display Port PLL driver block diagram for branch clocks
*
* +------------------------------+
* | DP_VCO_CLK |
* | |
* | +-------------------+ |
* | | (DP PLL/VCO) | |
* | +---------+---------+ |
* | v |
* | +----------+-----------+ |
* | | hsclk_divsel_clk_src | |
* | +----------+-----------+ |
* +------------------------------+
* |
* +------------<---------v------------>----------+
* | |
* +-----v------------+ |
* | dp_link_clk_src | |
* | divsel_ten | |
* +---------+--------+ |
* | |
* | |
* v v
* Input to DISPCC block |
* for link clk, crypto clk |
* and interface clock |
* |
* |
* +--------<------------+-----------------+---<---+
* | | |
* +-------v------+ +--------v-----+ +--------v------+
* | vco_divided | | vco_divided | | vco_divided |
* | _clk_src | | _clk_src | | _clk_src |
* | | | | | |
* |divsel_six | | divsel_two | | divsel_four |
* +-------+------+ +-----+--------+ +--------+------+
* | | |
* v------->----------v-------------<------v
* |
* +----------+---------+
* | vco_divided_clk |
* | _src_mux |
* +---------+----------+
* |
* v
* Input to DISPCC block
* for DP pixel clock
*
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <dt-bindings/clock/mdss-10nm-pll-clk.h>
#include "pll_drv.h"
#include "dp_pll.h"
#include "dp_pll_7nm.h"
static struct dp_pll_db_7nm dp_pdb_7nm;
static struct clk_ops mux_clk_ops;
static struct regmap_config dp_pll_7nm_cfg = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = 0x910,
};
static struct regmap_bus dp_pixel_mux_regmap_ops = {
.reg_write = dp_mux_set_parent_7nm,
.reg_read = dp_mux_get_parent_7nm,
};
/* Op structures */
static const struct clk_ops dp_7nm_vco_clk_ops = {
.recalc_rate = dp_vco_recalc_rate_7nm,
.set_rate = dp_vco_set_rate_7nm,
.round_rate = dp_vco_round_rate_7nm,
.prepare = dp_vco_prepare_7nm,
.unprepare = dp_vco_unprepare_7nm,
};
static struct dp_pll_vco_clk dp_vco_clk = {
.min_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000,
.max_rate = DP_VCO_HSCLK_RATE_8100MHZDIV1000,
.hw.init = &(struct clk_init_data){
.name = "dp_vco_clk",
.parent_names = (const char *[]){ "xo_board" },
.num_parents = 1,
.ops = &dp_7nm_vco_clk_ops,
},
};
static struct clk_fixed_factor dp_phy_pll_link_clk = {
.div = 10,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dp_phy_pll_link_clk",
.parent_names =
(const char *[]){ "dp_vco_clk" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor dp_link_clk_divsel_ten = {
.div = 10,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dp_link_clk_divsel_ten",
.parent_names =
(const char *[]){ "dp_vco_clk" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor dp_vco_divsel_two_clk_src = {
.div = 2,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dp_vco_divsel_two_clk_src",
.parent_names =
(const char *[]){ "dp_vco_clk" },
.num_parents = 1,
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor dp_vco_divsel_four_clk_src = {
.div = 4,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dp_vco_divsel_four_clk_src",
.parent_names =
(const char *[]){ "dp_vco_clk" },
.num_parents = 1,
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor dp_vco_divsel_six_clk_src = {
.div = 6,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dp_vco_divsel_six_clk_src",
.parent_names =
(const char *[]){ "dp_vco_clk" },
.num_parents = 1,
.ops = &clk_fixed_factor_ops,
},
};
static int clk_mux_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
int ret = 0;
if (!hw || !req) {
pr_err("Invalid input parameters\n");
return -EINVAL;
}
ret = __clk_mux_determine_rate_closest(hw, req);
if (ret)
return ret;
/* Set the new parent of mux if there is a new valid parent */
if (hw->clk && req->best_parent_hw->clk)
clk_set_parent(hw->clk, req->best_parent_hw->clk);
return 0;
}
static unsigned long mux_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk *div_clk = NULL, *vco_clk = NULL;
struct dp_pll_vco_clk *vco = NULL;
if (!hw) {
pr_err("Invalid input parameter\n");
return 0;
}
div_clk = clk_get_parent(hw->clk);
if (!div_clk)
return 0;
vco_clk = clk_get_parent(div_clk);
if (!vco_clk)
return 0;
vco = to_dp_vco_hw(__clk_get_hw(vco_clk));
if (!vco)
return 0;
if (vco->rate == DP_VCO_HSCLK_RATE_8100MHZDIV1000)
return (vco->rate / 6);
else if (vco->rate == DP_VCO_HSCLK_RATE_5400MHZDIV1000)
return (vco->rate / 4);
else
return (vco->rate / 2);
}
static struct clk_regmap_mux dp_phy_pll_vco_div_clk = {
.reg = 0x64,
.shift = 0,
.width = 2,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dp_phy_pll_vco_div_clk",
.parent_names =
(const char *[]){"dp_vco_divsel_two_clk_src",
"dp_vco_divsel_four_clk_src",
"dp_vco_divsel_six_clk_src"},
.num_parents = 3,
.ops = &mux_clk_ops,
.flags = CLK_SET_RATE_PARENT,
},
},
};
static struct clk_regmap_mux dp_vco_divided_clk_src_mux = {
.reg = 0x64,
.shift = 0,
.width = 2,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dp_vco_divided_clk_src_mux",
.parent_names =
(const char *[]){"dp_vco_divsel_two_clk_src",
"dp_vco_divsel_four_clk_src",
"dp_vco_divsel_six_clk_src"},
.num_parents = 3,
.ops = &mux_clk_ops,
.flags = CLK_SET_RATE_PARENT,
},
},
};
static struct clk_hw *mdss_dp_pllcc_7nm[] = {
[DP_VCO_CLK] = &dp_vco_clk.hw,
[DP_LINK_CLK_DIVSEL_TEN] = &dp_link_clk_divsel_ten.hw,
[DP_VCO_DIVIDED_TWO_CLK_SRC] = &dp_vco_divsel_two_clk_src.hw,
[DP_VCO_DIVIDED_FOUR_CLK_SRC] = &dp_vco_divsel_four_clk_src.hw,
[DP_VCO_DIVIDED_SIX_CLK_SRC] = &dp_vco_divsel_six_clk_src.hw,
[DP_VCO_DIVIDED_CLK_SRC_MUX] = &dp_vco_divided_clk_src_mux.clkr.hw,
};
int dp_pll_clock_register_7nm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc = -ENOTSUPP, i = 0;
struct clk_onecell_data *clk_data;
struct clk *clk;
struct regmap *regmap;
int num_clks = ARRAY_SIZE(mdss_dp_pllcc_7nm);
clk_data = devm_kzalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
clk_data->clks = devm_kcalloc(&pdev->dev, num_clks,
sizeof(struct clk *), GFP_KERNEL);
if (!clk_data->clks)
return -ENOMEM;
clk_data->clk_num = num_clks;
pll_res->priv = &dp_pdb_7nm;
dp_pdb_7nm.pll = pll_res;
/* Set client data for vco, mux and div clocks */
regmap = devm_regmap_init(&pdev->dev, &dp_pixel_mux_regmap_ops,
pll_res, &dp_pll_7nm_cfg);
mux_clk_ops = clk_regmap_mux_closest_ops;
mux_clk_ops.determine_rate = clk_mux_determine_rate;
mux_clk_ops.recalc_rate = mux_recalc_rate;
dp_vco_clk.priv = pll_res;
/*
* Consumer for the pll clock expects, the DP_LINK_CLK_DIVSEL_TEN and
* DP_VCO_DIVIDED_CLK_SRC_MUX clock names to be "dp_phy_pll_link_clk"
* and "dp_phy_pll_vco_div_clk" respectively for a V2 pll interface
* target.
*/
if (pll_res->pll_interface_type == MDSS_DP_PLL_7NM_V2) {
mdss_dp_pllcc_7nm[DP_LINK_CLK_DIVSEL_TEN] =
&dp_phy_pll_link_clk.hw;
mdss_dp_pllcc_7nm[DP_VCO_DIVIDED_CLK_SRC_MUX] =
&dp_phy_pll_vco_div_clk.clkr.hw;
dp_phy_pll_vco_div_clk.clkr.regmap = regmap;
} else
dp_vco_divided_clk_src_mux.clkr.regmap = regmap;
for (i = DP_VCO_CLK; i <= DP_VCO_DIVIDED_CLK_SRC_MUX; i++) {
pr_debug("reg clk: %d index: %d\n", i, pll_res->index);
clk = devm_clk_register(&pdev->dev, mdss_dp_pllcc_7nm[i]);
if (IS_ERR(clk)) {
pr_err("clk registration failed for DP: %d\n",
pll_res->index);
rc = -EINVAL;
goto clk_reg_fail;
}
clk_data->clks[i] = clk;
}
rc = of_clk_add_provider(pdev->dev.of_node,
of_clk_src_onecell_get, clk_data);
if (rc) {
pr_err("Clock register failed rc=%d\n", rc);
rc = -EPROBE_DEFER;
goto clk_reg_fail;
} else {
pr_debug("SUCCESS\n");
}
return rc;
clk_reg_fail:
return rc;
}

View File

@@ -1,52 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
*/
#ifndef __MDSS_DP_PLL_7NM_H
#define __MDSS_DP_PLL_7NM_H
#define DP_VCO_HSCLK_RATE_1620MHZDIV1000 1620000UL
#define DP_VCO_HSCLK_RATE_2700MHZDIV1000 2700000UL
#define DP_VCO_HSCLK_RATE_5400MHZDIV1000 5400000UL
#define DP_VCO_HSCLK_RATE_8100MHZDIV1000 8100000UL
struct dp_pll_db_7nm {
struct mdss_pll_resources *pll;
/* lane and orientation settings */
u8 lane_cnt;
u8 orientation;
/* COM PHY settings */
u32 hsclk_sel;
u32 dec_start_mode0;
u32 div_frac_start1_mode0;
u32 div_frac_start2_mode0;
u32 div_frac_start3_mode0;
u32 integloop_gain0_mode0;
u32 integloop_gain1_mode0;
u32 vco_tune_map;
u32 lock_cmp1_mode0;
u32 lock_cmp2_mode0;
u32 lock_cmp_en;
u32 cmn_config;
u32 txn_tran_drv_emp_en;
/* PHY vco divider */
u32 phy_vco_div;
};
int dp_vco_set_rate_7nm(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate);
unsigned long dp_vco_recalc_rate_7nm(struct clk_hw *hw,
unsigned long parent_rate);
long dp_vco_round_rate_7nm(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate);
int dp_vco_prepare_7nm(struct clk_hw *hw);
void dp_vco_unprepare_7nm(struct clk_hw *hw);
int dp_mux_set_parent_7nm(void *context,
unsigned int reg, unsigned int val);
int dp_mux_get_parent_7nm(void *context,
unsigned int reg, unsigned int *val);
#endif /* __MDSS_DP_PLL_7NM_H */

View File

@@ -1,739 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "[dp-pll] %s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/iopoll.h>
#include <linux/delay.h>
#include <linux/usb/usbpd.h>
#include "pll_drv.h"
#include "dp_pll.h"
#include "dp_pll_7nm.h"
#define DP_PHY_CFG 0x0010
#define DP_PHY_CFG_1 0x0014
#define DP_PHY_PD_CTL 0x0018
#define DP_PHY_MODE 0x001C
#define DP_PHY_AUX_CFG1 0x0024
#define DP_PHY_AUX_CFG2 0x0028
#define DP_PHY_VCO_DIV 0x0070
#define DP_PHY_TX0_TX1_LANE_CTL 0x0078
#define DP_PHY_TX2_TX3_LANE_CTL 0x009C
#define DP_PHY_SPARE0 0x00C8
#define DP_PHY_STATUS 0x00DC
/* Tx registers */
#define TXn_CLKBUF_ENABLE 0x0008
#define TXn_TX_EMP_POST1_LVL 0x000C
#define TXn_TX_DRV_LVL 0x0014
#define TXn_RESET_TSYNC_EN 0x001C
#define TXn_PRE_STALL_LDO_BOOST_EN 0x0020
#define TXn_TX_BAND 0x0024
#define TXn_INTERFACE_SELECT 0x002C
#define TXn_RES_CODE_LANE_OFFSET_TX 0x003C
#define TXn_RES_CODE_LANE_OFFSET_RX 0x0040
#define TXn_TRANSCEIVER_BIAS_EN 0x0054
#define TXn_HIGHZ_DRVR_EN 0x0058
#define TXn_TX_POL_INV 0x005C
#define TXn_PARRATE_REC_DETECT_IDLE_EN 0x0060
/* PLL register offset */
#define QSERDES_COM_BG_TIMER 0x000C
#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x0044
#define QSERDES_COM_CLK_ENABLE1 0x0048
#define QSERDES_COM_SYS_CLK_CTRL 0x004C
#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x0050
#define QSERDES_COM_PLL_IVCO 0x0058
#define QSERDES_COM_CP_CTRL_MODE0 0x0074
#define QSERDES_COM_PLL_RCTRL_MODE0 0x007C
#define QSERDES_COM_PLL_CCTRL_MODE0 0x0084
#define QSERDES_COM_SYSCLK_EN_SEL 0x0094
#define QSERDES_COM_RESETSM_CNTRL 0x009C
#define QSERDES_COM_LOCK_CMP_EN 0x00A4
#define QSERDES_COM_LOCK_CMP1_MODE0 0x00AC
#define QSERDES_COM_LOCK_CMP2_MODE0 0x00B0
#define QSERDES_COM_DEC_START_MODE0 0x00BC
#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x00CC
#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x00D0
#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x00D4
#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x00EC
#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x00F0
#define QSERDES_COM_VCO_TUNE_CTRL 0x0108
#define QSERDES_COM_VCO_TUNE_MAP 0x010C
#define QSERDES_COM_CMN_STATUS 0x0140
#define QSERDES_COM_CLK_SEL 0x0154
#define QSERDES_COM_HSCLK_SEL 0x0158
#define QSERDES_COM_CORECLK_DIV_MODE0 0x0168
#define QSERDES_COM_CORE_CLK_EN 0x0174
#define QSERDES_COM_C_READY_STATUS 0x0178
#define QSERDES_COM_CMN_CONFIG 0x017C
#define QSERDES_COM_SVS_MODE_CLK_SEL 0x0184
/* Tx tran offsets */
#define DP_TRAN_DRVR_EMP_EN 0x0000
#define DP_TX_INTERFACE_MODE 0x0004
/* Tx VMODE offsets */
#define DP_VMODE_CTRL1 0x0000
#define DP_PHY_PLL_POLL_SLEEP_US 500
#define DP_PHY_PLL_POLL_TIMEOUT_US 10000
#define DP_VCO_RATE_8100MHZDIV1000 8100000UL
#define DP_VCO_RATE_9720MHZDIV1000 9720000UL
#define DP_VCO_RATE_10800MHZDIV1000 10800000UL
#define DP_7NM_C_READY BIT(0)
#define DP_7NM_FREQ_DONE BIT(0)
#define DP_7NM_PLL_LOCKED BIT(1)
#define DP_7NM_PHY_READY BIT(1)
#define DP_7NM_TSYNC_DONE BIT(0)
int dp_mux_set_parent_7nm(void *context, unsigned int reg, unsigned int val)
{
struct mdss_pll_resources *dp_res = context;
int rc;
u32 auxclk_div;
if (!context) {
pr_err("invalid input parameters\n");
return -EINVAL;
}
rc = mdss_pll_resource_enable(dp_res, true);
if (rc) {
pr_err("Failed to enable mdss DP PLL resources\n");
return rc;
}
auxclk_div = MDSS_PLL_REG_R(dp_res->phy_base, DP_PHY_VCO_DIV);
auxclk_div &= ~0x03;
if (val == 0)
auxclk_div |= 1;
else if (val == 1)
auxclk_div |= 2;
else if (val == 2)
auxclk_div |= 0;
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_VCO_DIV, auxclk_div);
/* Make sure the PHY registers writes are done */
wmb();
pr_debug("mux=%d auxclk_div=%x\n", val, auxclk_div);
mdss_pll_resource_enable(dp_res, false);
return 0;
}
int dp_mux_get_parent_7nm(void *context, unsigned int reg, unsigned int *val)
{
int rc;
u32 auxclk_div = 0;
struct mdss_pll_resources *dp_res = context;
if (!context || !val) {
pr_err("invalid input parameters\n");
return -EINVAL;
}
if (is_gdsc_disabled(dp_res))
return 0;
rc = mdss_pll_resource_enable(dp_res, true);
if (rc) {
pr_err("Failed to enable dp_res resources\n");
return rc;
}
auxclk_div = MDSS_PLL_REG_R(dp_res->phy_base, DP_PHY_VCO_DIV);
auxclk_div &= 0x03;
if (auxclk_div == 1) /* Default divider */
*val = 0;
else if (auxclk_div == 2)
*val = 1;
else if (auxclk_div == 0)
*val = 2;
mdss_pll_resource_enable(dp_res, false);
pr_debug("auxclk_div=%d, val=%d\n", auxclk_div, *val);
return 0;
}
static int dp_vco_pll_init_db_7nm(struct dp_pll_db_7nm *pdb,
unsigned long rate)
{
struct mdss_pll_resources *dp_res = pdb->pll;
u32 spare_value = 0;
spare_value = MDSS_PLL_REG_R(dp_res->phy_base, DP_PHY_SPARE0);
pdb->lane_cnt = spare_value & 0x0F;
pdb->orientation = (spare_value & 0xF0) >> 4;
pr_debug("spare_value=0x%x, ln_cnt=0x%x, orientation=0x%x\n",
spare_value, pdb->lane_cnt, pdb->orientation);
pdb->div_frac_start1_mode0 = 0x00;
pdb->integloop_gain0_mode0 = 0x3f;
pdb->integloop_gain1_mode0 = 0x00;
pdb->vco_tune_map = 0x00;
pdb->cmn_config = 0x02;
switch (rate) {
case DP_VCO_HSCLK_RATE_1620MHZDIV1000:
pr_debug("VCO rate: %ld\n", DP_VCO_RATE_9720MHZDIV1000);
pdb->hsclk_sel = 0x05;
pdb->dec_start_mode0 = 0x69;
pdb->div_frac_start2_mode0 = 0x80;
pdb->div_frac_start3_mode0 = 0x07;
pdb->lock_cmp1_mode0 = 0x6f;
pdb->lock_cmp2_mode0 = 0x08;
pdb->phy_vco_div = 0x1;
pdb->lock_cmp_en = 0x04;
break;
case DP_VCO_HSCLK_RATE_2700MHZDIV1000:
pr_debug("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000);
pdb->hsclk_sel = 0x03;
pdb->dec_start_mode0 = 0x69;
pdb->div_frac_start2_mode0 = 0x80;
pdb->div_frac_start3_mode0 = 0x07;
pdb->lock_cmp1_mode0 = 0x0f;
pdb->lock_cmp2_mode0 = 0x0e;
pdb->phy_vco_div = 0x1;
pdb->lock_cmp_en = 0x08;
break;
case DP_VCO_HSCLK_RATE_5400MHZDIV1000:
pr_debug("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000);
pdb->hsclk_sel = 0x01;
pdb->dec_start_mode0 = 0x8c;
pdb->div_frac_start2_mode0 = 0x00;
pdb->div_frac_start3_mode0 = 0x0a;
pdb->lock_cmp1_mode0 = 0x1f;
pdb->lock_cmp2_mode0 = 0x1c;
pdb->phy_vco_div = 0x2;
pdb->lock_cmp_en = 0x08;
break;
case DP_VCO_HSCLK_RATE_8100MHZDIV1000:
pr_debug("VCO rate: %ld\n", DP_VCO_RATE_8100MHZDIV1000);
pdb->hsclk_sel = 0x00;
pdb->dec_start_mode0 = 0x69;
pdb->div_frac_start2_mode0 = 0x80;
pdb->div_frac_start3_mode0 = 0x07;
pdb->lock_cmp1_mode0 = 0x2f;
pdb->lock_cmp2_mode0 = 0x2a;
pdb->phy_vco_div = 0x0;
pdb->lock_cmp_en = 0x08;
break;
default:
pr_err("unsupported rate %ld\n", rate);
return -EINVAL;
}
return 0;
}
static int dp_config_vco_rate_7nm(struct dp_pll_vco_clk *vco,
unsigned long rate)
{
u32 res = 0;
struct mdss_pll_resources *dp_res = vco->priv;
struct dp_pll_db_7nm *pdb = (struct dp_pll_db_7nm *)dp_res->priv;
res = dp_vco_pll_init_db_7nm(pdb, rate);
if (res) {
pr_err("VCO Init DB failed\n");
return res;
}
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG_1, 0x0F);
if (pdb->lane_cnt != 4) {
if (pdb->orientation == ORIENTATION_CC2)
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_PD_CTL, 0x6d);
else
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_PD_CTL, 0x75);
} else {
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_PD_CTL, 0x7d);
}
/* Make sure the PHY register writes are done */
wmb();
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_SVS_MODE_CLK_SEL, 0x05);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYSCLK_EN_SEL, 0x3b);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYS_CLK_CTRL, 0x02);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_CLK_ENABLE1, 0x0c);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x06);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_CLK_SEL, 0x30);
/* Make sure the PHY register writes are done */
wmb();
/* PLL Optimization */
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_IVCO, 0x0f);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_CCTRL_MODE0, 0x36);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_RCTRL_MODE0, 0x16);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_CP_CTRL_MODE0, 0x06);
/* Make sure the PHY register writes are done */
wmb();
/* link rate dependent params */
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_HSCLK_SEL, pdb->hsclk_sel);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_DEC_START_MODE0, pdb->dec_start_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_DIV_FRAC_START1_MODE0, pdb->div_frac_start1_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_DIV_FRAC_START2_MODE0, pdb->div_frac_start2_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_DIV_FRAC_START3_MODE0, pdb->div_frac_start3_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_LOCK_CMP1_MODE0, pdb->lock_cmp1_mode0);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_LOCK_CMP2_MODE0, pdb->lock_cmp2_mode0);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_LOCK_CMP_EN,
pdb->lock_cmp_en);
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_VCO_DIV, pdb->phy_vco_div);
/* Make sure the PLL register writes are done */
wmb();
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_CMN_CONFIG, 0x02);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x3f);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_VCO_TUNE_MAP, 0x00);
/* Make sure the PHY register writes are done */
wmb();
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_BG_TIMER, 0x0a);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_CORECLK_DIV_MODE0, 0x0a);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_VCO_TUNE_CTRL, 0x00);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x17);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_CORE_CLK_EN, 0x1f);
/* Make sure the PHY register writes are done */
wmb();
if (pdb->orientation == ORIENTATION_CC2)
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_MODE, 0x4c);
else
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_MODE, 0x5c);
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_AUX_CFG1, 0x13);
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_AUX_CFG2, 0xA4);
/* Make sure the PLL register writes are done */
wmb();
/* TX-0 register configuration */
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_TX0_TX1_LANE_CTL, 0x05);
MDSS_PLL_REG_W(dp_res->ln_tx0_vmode_base, DP_VMODE_CTRL1, 0x40);
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_INTERFACE_SELECT, 0x3b);
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_CLKBUF_ENABLE, 0x0f);
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_RESET_TSYNC_EN, 0x03);
MDSS_PLL_REG_W(dp_res->ln_tx0_tran_base, DP_TRAN_DRVR_EMP_EN, 0xf);
MDSS_PLL_REG_W(dp_res->ln_tx0_base,
TXn_PARRATE_REC_DETECT_IDLE_EN, 0x00);
MDSS_PLL_REG_W(dp_res->ln_tx0_tran_base, DP_TX_INTERFACE_MODE, 0x00);
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_RES_CODE_LANE_OFFSET_TX, 0x11);
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_RES_CODE_LANE_OFFSET_RX, 0x11);
MDSS_PLL_REG_W(dp_res->ln_tx0_base, TXn_TX_BAND, 0x04);
/* TX-1 register configuration */
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_TX2_TX3_LANE_CTL, 0x05);
MDSS_PLL_REG_W(dp_res->ln_tx1_vmode_base, DP_VMODE_CTRL1, 0x40);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_INTERFACE_SELECT, 0x3b);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_CLKBUF_ENABLE, 0x0f);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_RESET_TSYNC_EN, 0x03);
MDSS_PLL_REG_W(dp_res->ln_tx1_tran_base, DP_TRAN_DRVR_EMP_EN, 0xf);
MDSS_PLL_REG_W(dp_res->ln_tx1_base,
TXn_PARRATE_REC_DETECT_IDLE_EN, 0x00);
MDSS_PLL_REG_W(dp_res->ln_tx1_tran_base, DP_TX_INTERFACE_MODE, 0x00);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_RES_CODE_LANE_OFFSET_TX, 0x11);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_RES_CODE_LANE_OFFSET_RX, 0x11);
MDSS_PLL_REG_W(dp_res->ln_tx1_base, TXn_TX_BAND, 0x04);
/* Make sure the PHY register writes are done */
wmb();
return res;
}
enum dp_7nm_pll_status {
C_READY,
FREQ_DONE,
PLL_LOCKED,
PHY_READY,
TSYNC_DONE,
};
char *dp_7nm_pll_get_status_name(enum dp_7nm_pll_status status)
{
switch (status) {
case C_READY:
return "C_READY";
case FREQ_DONE:
return "FREQ_DONE";
case PLL_LOCKED:
return "PLL_LOCKED";
case PHY_READY:
return "PHY_READY";
case TSYNC_DONE:
return "TSYNC_DONE";
default:
return "unknown";
}
}
static bool dp_7nm_pll_get_status(struct mdss_pll_resources *dp_res,
enum dp_7nm_pll_status status)
{
u32 reg, state, bit;
void __iomem *base;
bool success = true;
switch (status) {
case C_READY:
base = dp_res->pll_base;
reg = QSERDES_COM_C_READY_STATUS;
bit = DP_7NM_C_READY;
break;
case FREQ_DONE:
base = dp_res->pll_base;
reg = QSERDES_COM_CMN_STATUS;
bit = DP_7NM_FREQ_DONE;
break;
case PLL_LOCKED:
base = dp_res->pll_base;
reg = QSERDES_COM_CMN_STATUS;
bit = DP_7NM_PLL_LOCKED;
break;
case PHY_READY:
base = dp_res->phy_base;
reg = DP_PHY_STATUS;
bit = DP_7NM_PHY_READY;
break;
case TSYNC_DONE:
base = dp_res->phy_base;
reg = DP_PHY_STATUS;
bit = DP_7NM_TSYNC_DONE;
break;
default:
return false;
}
if (readl_poll_timeout_atomic((base + reg), state,
((state & bit) > 0),
DP_PHY_PLL_POLL_SLEEP_US,
DP_PHY_PLL_POLL_TIMEOUT_US)) {
pr_err("%s failed, status=%x\n",
dp_7nm_pll_get_status_name(status), state);
success = false;
}
return success;
}
static int dp_pll_enable_7nm(struct clk_hw *hw)
{
int rc = 0;
struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
struct mdss_pll_resources *dp_res = vco->priv;
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x01);
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x05);
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x01);
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x09);
MDSS_PLL_REG_W(dp_res->pll_base, QSERDES_COM_RESETSM_CNTRL, 0x20);
wmb(); /* Make sure the PLL register writes are done */
if (!dp_7nm_pll_get_status(dp_res, C_READY)) {
rc = -EINVAL;
goto lock_err;
}
if (!dp_7nm_pll_get_status(dp_res, FREQ_DONE)) {
rc = -EINVAL;
goto lock_err;
}
if (!dp_7nm_pll_get_status(dp_res, PLL_LOCKED)) {
rc = -EINVAL;
goto lock_err;
}
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x19);
/* Make sure the PHY register writes are done */
wmb();
if (!dp_7nm_pll_get_status(dp_res, TSYNC_DONE)) {
rc = -EINVAL;
goto lock_err;
}
if (!dp_7nm_pll_get_status(dp_res, PHY_READY)) {
rc = -EINVAL;
goto lock_err;
}
pr_debug("PLL is locked\n");
lock_err:
return rc;
}
static int dp_pll_disable_7nm(struct clk_hw *hw)
{
struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
struct mdss_pll_resources *dp_res = vco->priv;
/* Assert DP PHY power down */
MDSS_PLL_REG_W(dp_res->phy_base, DP_PHY_PD_CTL, 0x2);
/*
* Make sure all the register writes to disable PLL are
* completed before doing any other operation
*/
wmb();
return 0;
}
int dp_vco_prepare_7nm(struct clk_hw *hw)
{
int rc = 0;
struct dp_pll_vco_clk *vco;
struct mdss_pll_resources *dp_res;
if (!hw) {
pr_err("invalid input parameters\n");
return -EINVAL;
}
vco = to_dp_vco_hw(hw);
dp_res = vco->priv;
pr_debug("rate=%ld\n", vco->rate);
rc = mdss_pll_resource_enable(dp_res, true);
if (rc) {
pr_err("Failed to enable mdss DP pll resources\n");
goto error;
}
if ((dp_res->vco_cached_rate != 0)
&& (dp_res->vco_cached_rate == vco->rate)) {
rc = vco->hw.init->ops->set_rate(hw,
dp_res->vco_cached_rate, dp_res->vco_cached_rate);
if (rc) {
pr_err("index=%d vco_set_rate failed. rc=%d\n",
rc, dp_res->index);
mdss_pll_resource_enable(dp_res, false);
goto error;
}
}
rc = dp_pll_enable_7nm(hw);
if (rc) {
mdss_pll_resource_enable(dp_res, false);
pr_err("ndx=%d failed to enable dp pll\n", dp_res->index);
goto error;
}
mdss_pll_resource_enable(dp_res, false);
error:
return rc;
}
void dp_vco_unprepare_7nm(struct clk_hw *hw)
{
struct dp_pll_vco_clk *vco;
struct mdss_pll_resources *dp_res;
if (!hw) {
pr_err("invalid input parameters\n");
return;
}
vco = to_dp_vco_hw(hw);
dp_res = vco->priv;
if (!dp_res) {
pr_err("invalid input parameter\n");
return;
}
if (!dp_res->pll_on &&
mdss_pll_resource_enable(dp_res, true)) {
pr_err("pll resource can't be enabled\n");
return;
}
dp_res->vco_cached_rate = vco->rate;
dp_pll_disable_7nm(hw);
dp_res->handoff_resources = false;
mdss_pll_resource_enable(dp_res, false);
dp_res->pll_on = false;
}
int dp_vco_set_rate_7nm(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct dp_pll_vco_clk *vco;
struct mdss_pll_resources *dp_res;
int rc;
if (!hw) {
pr_err("invalid input parameters\n");
return -EINVAL;
}
vco = to_dp_vco_hw(hw);
dp_res = vco->priv;
rc = mdss_pll_resource_enable(dp_res, true);
if (rc) {
pr_err("pll resource can't be enabled\n");
return rc;
}
pr_debug("DP lane CLK rate=%ld\n", rate);
rc = dp_config_vco_rate_7nm(vco, rate);
if (rc)
pr_err("Failed to set clk rate\n");
mdss_pll_resource_enable(dp_res, false);
vco->rate = rate;
return 0;
}
unsigned long dp_vco_recalc_rate_7nm(struct clk_hw *hw,
unsigned long parent_rate)
{
struct dp_pll_vco_clk *vco;
int rc;
u32 hsclk_sel, link_clk_divsel, hsclk_div, link_clk_div = 0;
unsigned long vco_rate;
struct mdss_pll_resources *dp_res;
if (!hw) {
pr_err("invalid input parameters\n");
return 0;
}
vco = to_dp_vco_hw(hw);
dp_res = vco->priv;
if (is_gdsc_disabled(dp_res))
return 0;
rc = mdss_pll_resource_enable(dp_res, true);
if (rc) {
pr_err("Failed to enable mdss DP pll=%d\n", dp_res->index);
return 0;
}
pr_debug("input rates: parent=%lu, vco=%lu\n", parent_rate, vco->rate);
hsclk_sel = MDSS_PLL_REG_R(dp_res->pll_base, QSERDES_COM_HSCLK_SEL);
hsclk_sel &= 0x0f;
if (hsclk_sel == 5)
hsclk_div = 5;
else if (hsclk_sel == 3)
hsclk_div = 3;
else if (hsclk_sel == 1)
hsclk_div = 2;
else if (hsclk_sel == 0)
hsclk_div = 1;
else {
pr_debug("unknown divider. forcing to default\n");
hsclk_div = 5;
}
link_clk_divsel = MDSS_PLL_REG_R(dp_res->phy_base, DP_PHY_AUX_CFG2);
link_clk_divsel >>= 2;
link_clk_divsel &= 0x3;
if (link_clk_divsel == 0)
link_clk_div = 5;
else if (link_clk_divsel == 1)
link_clk_div = 10;
else if (link_clk_divsel == 2)
link_clk_div = 20;
else
pr_err("unsupported div. Phy_mode: %d\n", link_clk_divsel);
if (link_clk_div == 20) {
vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
} else {
if (hsclk_div == 5)
vco_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000;
else if (hsclk_div == 3)
vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
else if (hsclk_div == 2)
vco_rate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
else
vco_rate = DP_VCO_HSCLK_RATE_8100MHZDIV1000;
}
pr_debug("hsclk: sel=0x%x, div=0x%x; lclk: sel=%u, div=%u, rate=%lu\n",
hsclk_sel, hsclk_div, link_clk_divsel, link_clk_div, vco_rate);
mdss_pll_resource_enable(dp_res, false);
dp_res->vco_cached_rate = vco->rate = vco_rate;
return vco_rate;
}
long dp_vco_round_rate_7nm(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
unsigned long rrate = rate;
struct dp_pll_vco_clk *vco;
if (!hw) {
pr_err("invalid input parameters\n");
return 0;
}
vco = to_dp_vco_hw(hw);
if (rate <= vco->min_rate)
rrate = vco->min_rate;
else if (rate <= DP_VCO_HSCLK_RATE_2700MHZDIV1000)
rrate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
else if (rate <= DP_VCO_HSCLK_RATE_5400MHZDIV1000)
rrate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
else
rrate = vco->max_rate;
pr_debug("rrate=%ld\n", rrate);
if (parent_rate)
*parent_rate = rrate;
return rrate;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,52 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*/
#ifndef __MDSS_DSI_PLL_H
#define __MDSS_DSI_PLL_H
#include <linux/clk-provider.h>
#include "pll_drv.h"
#define MAX_DSI_PLL_EN_SEQS 10
/* Register offsets for 20nm PHY PLL */
#define MMSS_DSI_PHY_PLL_PLL_CNTRL (0x0014)
#define MMSS_DSI_PHY_PLL_PLL_BKG_KVCO_CAL_EN (0x002C)
#define MMSS_DSI_PHY_PLL_PLLLOCK_CMP_EN (0x009C)
struct lpfr_cfg {
unsigned long vco_rate;
u32 r;
};
struct dsi_pll_vco_clk {
struct clk_hw hw;
unsigned long ref_clk_rate;
u64 min_rate;
u64 max_rate;
u32 pll_en_seq_cnt;
struct lpfr_cfg *lpfr_lut;
u32 lpfr_lut_size;
void *priv;
int (*pll_enable_seqs[MAX_DSI_PLL_EN_SEQS])
(struct mdss_pll_resources *dsi_pll_Res);
};
int dsi_pll_clock_register_10nm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
int dsi_pll_clock_register_7nm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
int dsi_pll_clock_register_28lpm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
static inline struct dsi_pll_vco_clk *to_vco_clk_hw(struct clk_hw *hw)
{
return container_of(hw, struct dsi_pll_vco_clk, hw);
}
int dsi_pll_clock_register_14nm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,592 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include "pll_drv.h"
#include "dsi_pll.h"
#include "dsi_pll_14nm.h"
#include <dt-bindings/clock/mdss-14nm-pll-clk.h>
#define VCO_DELAY_USEC 1
static struct dsi_pll_db pll_db[DSI_PLL_NUM];
static struct regmap_config dsi_pll_14nm_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = 0x588,
};
static struct regmap_bus post_n1_div_regmap_bus = {
.reg_write = post_n1_div_set_div,
.reg_read = post_n1_div_get_div,
};
static struct regmap_bus n2_div_regmap_bus = {
.reg_write = n2_div_set_div,
.reg_read = n2_div_get_div,
};
static struct regmap_bus shadow_n2_div_regmap_bus = {
.reg_write = shadow_n2_div_set_div,
.reg_read = n2_div_get_div,
};
static struct regmap_bus dsi_mux_regmap_bus = {
.reg_write = dsi_mux_set_parent_14nm,
.reg_read = dsi_mux_get_parent_14nm,
};
/* Op structures */
static const struct clk_ops clk_ops_dsi_vco = {
.recalc_rate = pll_vco_recalc_rate_14nm,
.set_rate = pll_vco_set_rate_14nm,
.round_rate = pll_vco_round_rate_14nm,
.prepare = pll_vco_prepare_14nm,
.unprepare = pll_vco_unprepare_14nm,
};
/* Shadow ops for dynamic refresh */
static const struct clk_ops clk_ops_shadow_dsi_vco = {
.recalc_rate = pll_vco_recalc_rate_14nm,
.set_rate = shadow_pll_vco_set_rate_14nm,
.round_rate = pll_vco_round_rate_14nm,
};
static struct dsi_pll_vco_clk dsi0pll_vco_clk = {
.ref_clk_rate = 19200000UL,
.min_rate = 1300000000UL,
.max_rate = 2600000000UL,
.pll_en_seq_cnt = 1,
.pll_enable_seqs[0] = dsi_pll_enable_seq_14nm,
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_vco_clk_14nm",
.parent_names = (const char *[]){ "bi_tcxo" },
.num_parents = 1,
.ops = &clk_ops_dsi_vco,
},
};
static struct dsi_pll_vco_clk dsi0pll_shadow_vco_clk = {
.ref_clk_rate = 19200000u,
.min_rate = 1300000000u,
.max_rate = 2600000000u,
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_shadow_vco_clk_14nm",
.parent_names = (const char *[]){ "bi_tcxo" },
.num_parents = 1,
.ops = &clk_ops_shadow_dsi_vco,
},
};
static struct dsi_pll_vco_clk dsi1pll_vco_clk = {
.ref_clk_rate = 19200000UL,
.min_rate = 1300000000UL,
.max_rate = 2600000000UL,
.pll_en_seq_cnt = 1,
.pll_enable_seqs[0] = dsi_pll_enable_seq_14nm,
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_vco_clk_14nm",
.parent_names = (const char *[]){ "bi_tcxo" },
.num_parents = 1,
.ops = &clk_ops_dsi_vco,
},
};
static struct dsi_pll_vco_clk dsi1pll_shadow_vco_clk = {
.ref_clk_rate = 19200000u,
.min_rate = 1300000000u,
.max_rate = 2600000000u,
.pll_en_seq_cnt = 1,
.pll_enable_seqs[0] = dsi_pll_enable_seq_14nm,
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_shadow_vco_clk_14nm",
.parent_names = (const char *[]){ "bi_tcxo" },
.num_parents = 1,
.ops = &clk_ops_shadow_dsi_vco,
},
};
static struct clk_regmap_div dsi0pll_post_n1_div_clk = {
.reg = 0x48,
.shift = 0,
.width = 4,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_post_n1_div_clk",
.parent_names =
(const char *[]){ "dsi0pll_vco_clk_14nm" },
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_regmap_div_ops,
},
},
};
static struct clk_regmap_div dsi0pll_shadow_post_n1_div_clk = {
.reg = 0x48,
.shift = 0,
.width = 4,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_shadow_post_n1_div_clk",
.parent_names =
(const char *[]){"dsi0pll_shadow_vco_clk_14nm"},
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_regmap_div_ops,
},
},
};
static struct clk_regmap_div dsi1pll_post_n1_div_clk = {
.reg = 0x48,
.shift = 0,
.width = 4,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_post_n1_div_clk",
.parent_names =
(const char *[]){ "dsi1pll_vco_clk_14nm" },
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_regmap_div_ops,
},
},
};
static struct clk_regmap_div dsi1pll_shadow_post_n1_div_clk = {
.reg = 0x48,
.shift = 0,
.width = 4,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_shadow_post_n1_div_clk",
.parent_names =
(const char *[]){"dsi1pll_shadow_vco_clk_14nm"},
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_regmap_div_ops,
},
},
};
static struct clk_regmap_div dsi0pll_n2_div_clk = {
.reg = 0x48,
.shift = 0,
.width = 4,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_n2_div_clk",
.parent_names =
(const char *[]){ "dsi0pll_post_n1_div_clk" },
.num_parents = 1,
.flags = CLK_GET_RATE_NOCACHE,
.ops = &clk_regmap_div_ops,
},
},
};
static struct clk_regmap_div dsi0pll_shadow_n2_div_clk = {
.reg = 0x48,
.shift = 0,
.width = 4,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_shadow_n2_div_clk",
.parent_names =
(const char *[]){ "dsi0pll_shadow_post_n1_div_clk" },
.num_parents = 1,
.flags = CLK_GET_RATE_NOCACHE,
.ops = &clk_regmap_div_ops,
},
},
};
static struct clk_regmap_div dsi1pll_n2_div_clk = {
.reg = 0x48,
.shift = 0,
.width = 4,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_n2_div_clk",
.parent_names =
(const char *[]){ "dsi1pll_post_n1_div_clk" },
.num_parents = 1,
.flags = CLK_GET_RATE_NOCACHE,
.ops = &clk_regmap_div_ops,
},
},
};
static struct clk_regmap_div dsi1pll_shadow_n2_div_clk = {
.reg = 0x48,
.shift = 0,
.width = 4,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_shadow_n2_div_clk",
.parent_names =
(const char *[]){ "dsi1pll_shadow_post_n1_div_clk" },
.num_parents = 1,
.flags = CLK_GET_RATE_NOCACHE,
.ops = &clk_regmap_div_ops,
},
},
};
static struct clk_fixed_factor dsi0pll_pixel_clk_src = {
.div = 2,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_pixel_clk_src",
.parent_names = (const char *[]){ "dsi0pll_n2_div_clk" },
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor dsi0pll_shadow_pixel_clk_src = {
.div = 2,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_shadow_pixel_clk_src",
.parent_names = (const char *[]){ "dsi0pll_shadow_n2_div_clk" },
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor dsi1pll_pixel_clk_src = {
.div = 2,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_pixel_clk_src",
.parent_names = (const char *[]){ "dsi1pll_n2_div_clk" },
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor dsi1pll_shadow_pixel_clk_src = {
.div = 2,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_shadow_pixel_clk_src",
.parent_names = (const char *[]){ "dsi1pll_shadow_n2_div_clk" },
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_regmap_mux dsi0pll_pixel_clk_mux = {
.reg = 0x48,
.shift = 0,
.width = 1,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi0_phy_pll_out_dsiclk",
.parent_names =
(const char *[]){ "dsi0pll_pixel_clk_src",
"dsi0pll_shadow_pixel_clk_src"},
.num_parents = 2,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_regmap_mux_closest_ops,
},
},
};
static struct clk_regmap_mux dsi1pll_pixel_clk_mux = {
.reg = 0x48,
.shift = 0,
.width = 1,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_pixel_clk_mux",
.parent_names =
(const char *[]){ "dsi1pll_pixel_clk_src",
"dsi1pll_shadow_pixel_clk_src"},
.num_parents = 2,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_regmap_mux_closest_ops,
},
},
};
static struct clk_fixed_factor dsi0pll_byte_clk_src = {
.div = 8,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_byte_clk_src",
.parent_names = (const char *[]){ "dsi0pll_post_n1_div_clk" },
.num_parents = 1,
.flags = (CLK_SET_RATE_PARENT),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor dsi0pll_shadow_byte_clk_src = {
.div = 8,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_shadow_byte_clk_src",
.parent_names =
(const char *[]){ "dsi0pll_shadow_post_n1_div_clk" },
.num_parents = 1,
.flags = (CLK_SET_RATE_PARENT),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor dsi1pll_byte_clk_src = {
.div = 8,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_byte_clk_src",
.parent_names = (const char *[]){ "dsi1pll_post_n1_div_clk" },
.num_parents = 1,
.flags = (CLK_SET_RATE_PARENT),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor dsi1pll_shadow_byte_clk_src = {
.div = 8,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_shadow_byte_clk_src",
.parent_names =
(const char *[]){ "dsi1pll_shadow_post_n1_div_clk" },
.num_parents = 1,
.flags = (CLK_SET_RATE_PARENT),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_regmap_mux dsi0pll_byte_clk_mux = {
.reg = 0x48,
.shift = 0,
.width = 1,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi0_phy_pll_out_byteclk",
.parent_names =
(const char *[]){"dsi0pll_byte_clk_src",
"dsi0pll_shadow_byte_clk_src"},
.num_parents = 2,
.ops = &clk_regmap_mux_closest_ops,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
},
},
};
static struct clk_regmap_mux dsi1pll_byte_clk_mux = {
.reg = 0x48,
.shift = 0,
.width = 1,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_byte_clk_mux",
.parent_names =
(const char *[]){"dsi1pll_byte_clk_src",
"dsi1pll_shadow_byte_clk_src"},
.num_parents = 2,
.ops = &clk_regmap_mux_closest_ops,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
},
},
};
static struct clk_hw *mdss_dsi_pllcc_14nm[] = {
[BYTE0_MUX_CLK] = &dsi0pll_byte_clk_mux.clkr.hw,
[BYTE0_SRC_CLK] = &dsi0pll_byte_clk_src.hw,
[PIX0_MUX_CLK] = &dsi0pll_pixel_clk_mux.clkr.hw,
[PIX0_SRC_CLK] = &dsi0pll_pixel_clk_src.hw,
[N2_DIV_0_CLK] = &dsi0pll_n2_div_clk.clkr.hw,
[POST_N1_DIV_0_CLK] = &dsi0pll_post_n1_div_clk.clkr.hw,
[VCO_CLK_0_CLK] = &dsi0pll_vco_clk.hw,
[SHADOW_BYTE0_SRC_CLK] = &dsi0pll_shadow_byte_clk_src.hw,
[SHADOW_PIX0_SRC_CLK] = &dsi0pll_shadow_pixel_clk_src.hw,
[SHADOW_N2_DIV_0_CLK] = &dsi0pll_shadow_n2_div_clk.clkr.hw,
[SHADOW_POST_N1_DIV_0_CLK] = &dsi0pll_shadow_post_n1_div_clk.clkr.hw,
[SHADOW_VCO_CLK_0_CLK] = &dsi0pll_shadow_vco_clk.hw,
[BYTE1_MUX_CLK] = &dsi1pll_byte_clk_mux.clkr.hw,
[BYTE1_SRC_CLK] = &dsi1pll_byte_clk_src.hw,
[PIX1_MUX_CLK] = &dsi1pll_pixel_clk_mux.clkr.hw,
[PIX1_SRC_CLK] = &dsi1pll_pixel_clk_src.hw,
[N2_DIV_1_CLK] = &dsi1pll_n2_div_clk.clkr.hw,
[POST_N1_DIV_1_CLK] = &dsi1pll_post_n1_div_clk.clkr.hw,
[VCO_CLK_1_CLK] = &dsi1pll_vco_clk.hw,
[SHADOW_BYTE1_SRC_CLK] = &dsi1pll_shadow_byte_clk_src.hw,
[SHADOW_PIX1_SRC_CLK] = &dsi1pll_shadow_pixel_clk_src.hw,
[SHADOW_N2_DIV_1_CLK] = &dsi1pll_shadow_n2_div_clk.clkr.hw,
[SHADOW_POST_N1_DIV_1_CLK] = &dsi1pll_shadow_post_n1_div_clk.clkr.hw,
[SHADOW_VCO_CLK_1_CLK] = &dsi1pll_shadow_vco_clk.hw,
};
int dsi_pll_clock_register_14nm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc = 0, ndx, i;
int const ssc_freq_default = 31500; /* default h/w recommended value */
int const ssc_ppm_default = 5000; /* default h/w recommended value */
struct dsi_pll_db *pdb;
struct clk_onecell_data *clk_data;
struct clk *clk;
struct regmap *regmap;
int num_clks = ARRAY_SIZE(mdss_dsi_pllcc_14nm);
if (pll_res->index >= DSI_PLL_NUM) {
pr_err("pll ndx=%d is NOT supported\n", pll_res->index);
return -EINVAL;
}
ndx = pll_res->index;
pdb = &pll_db[ndx];
pll_res->priv = pdb;
pdb->pll = pll_res;
ndx++;
ndx %= DSI_PLL_NUM;
pdb->next = &pll_db[ndx];
if (pll_res->ssc_en) {
if (!pll_res->ssc_freq)
pll_res->ssc_freq = ssc_freq_default;
if (!pll_res->ssc_ppm)
pll_res->ssc_ppm = ssc_ppm_default;
}
clk_data = devm_kzalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
clk_data->clks = devm_kcalloc(&pdev->dev, num_clks,
sizeof(struct clk *), GFP_KERNEL);
if (!clk_data->clks)
return -ENOMEM;
clk_data->clk_num = num_clks;
/* Set client data to mux, div and vco clocks. */
if (pll_res->index == DSI_PLL_1) {
regmap = devm_regmap_init(&pdev->dev, &post_n1_div_regmap_bus,
pll_res, &dsi_pll_14nm_config);
dsi1pll_post_n1_div_clk.clkr.regmap = regmap;
dsi1pll_shadow_post_n1_div_clk.clkr.regmap = regmap;
regmap = devm_regmap_init(&pdev->dev, &n2_div_regmap_bus,
pll_res, &dsi_pll_14nm_config);
dsi1pll_n2_div_clk.clkr.regmap = regmap;
regmap = devm_regmap_init(&pdev->dev, &shadow_n2_div_regmap_bus,
pll_res, &dsi_pll_14nm_config);
dsi1pll_shadow_n2_div_clk.clkr.regmap = regmap;
regmap = devm_regmap_init(&pdev->dev, &dsi_mux_regmap_bus,
pll_res, &dsi_pll_14nm_config);
dsi1pll_byte_clk_mux.clkr.regmap = regmap;
dsi1pll_pixel_clk_mux.clkr.regmap = regmap;
dsi1pll_vco_clk.priv = pll_res;
dsi1pll_shadow_vco_clk.priv = pll_res;
pll_res->vco_delay = VCO_DELAY_USEC;
for (i = BYTE1_MUX_CLK; i <= SHADOW_VCO_CLK_1_CLK; i++) {
pr_debug("register clk: %d index: %d\n",
i, pll_res->index);
clk = devm_clk_register(&pdev->dev,
mdss_dsi_pllcc_14nm[i]);
if (IS_ERR(clk)) {
pr_err("clk registration failed for DSI: %d\n",
pll_res->index);
rc = -EINVAL;
goto clk_reg_fail;
}
clk_data->clks[i] = clk;
}
rc = of_clk_add_provider(pdev->dev.of_node,
of_clk_src_onecell_get, clk_data);
} else {
regmap = devm_regmap_init(&pdev->dev, &post_n1_div_regmap_bus,
pll_res, &dsi_pll_14nm_config);
dsi0pll_post_n1_div_clk.clkr.regmap = regmap;
dsi0pll_shadow_post_n1_div_clk.clkr.regmap = regmap;
regmap = devm_regmap_init(&pdev->dev, &n2_div_regmap_bus,
pll_res, &dsi_pll_14nm_config);
dsi0pll_n2_div_clk.clkr.regmap = regmap;
regmap = devm_regmap_init(&pdev->dev, &shadow_n2_div_regmap_bus,
pll_res, &dsi_pll_14nm_config);
dsi0pll_shadow_n2_div_clk.clkr.regmap = regmap;
regmap = devm_regmap_init(&pdev->dev, &dsi_mux_regmap_bus,
pll_res, &dsi_pll_14nm_config);
dsi0pll_byte_clk_mux.clkr.regmap = regmap;
dsi0pll_pixel_clk_mux.clkr.regmap = regmap;
dsi0pll_vco_clk.priv = pll_res;
dsi0pll_shadow_vco_clk.priv = pll_res;
pll_res->vco_delay = VCO_DELAY_USEC;
for (i = BYTE0_MUX_CLK; i <= SHADOW_VCO_CLK_0_CLK; i++) {
pr_debug("reg clk: %d index: %d\n", i, pll_res->index);
clk = devm_clk_register(&pdev->dev,
mdss_dsi_pllcc_14nm[i]);
if (IS_ERR(clk)) {
pr_err("clk registration failed for DSI: %d\n",
pll_res->index);
rc = -EINVAL;
goto clk_reg_fail;
}
clk_data->clks[i] = clk;
}
rc = of_clk_add_provider(pdev->dev.of_node,
of_clk_src_onecell_get, clk_data);
}
if (!rc) {
pr_info("Registered DSI PLL ndx=%d clocks successfully\n",
pll_res->index);
return rc;
}
clk_reg_fail:
return rc;
}

View File

@@ -1,220 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
*/
#ifndef MDSS_DSI_PLL_14NM_H
#define MDSS_DSI_PLL_14NM_H
#define DSIPHY_CMN_CLK_CFG0 0x0010
#define DSIPHY_CMN_CLK_CFG1 0x0014
#define DSIPHY_CMN_GLBL_TEST_CTRL 0x0018
#define DSIPHY_CMN_PLL_CNTRL 0x0048
#define DSIPHY_CMN_CTRL_0 0x001c
#define DSIPHY_CMN_CTRL_1 0x0020
#define DSIPHY_CMN_LDO_CNTRL 0x004c
#define DSIPHY_PLL_IE_TRIM 0x0400
#define DSIPHY_PLL_IP_TRIM 0x0404
#define DSIPHY_PLL_IPTAT_TRIM 0x0410
#define DSIPHY_PLL_CLKBUFLR_EN 0x041c
#define DSIPHY_PLL_SYSCLK_EN_RESET 0x0428
#define DSIPHY_PLL_RESETSM_CNTRL 0x042c
#define DSIPHY_PLL_RESETSM_CNTRL2 0x0430
#define DSIPHY_PLL_RESETSM_CNTRL3 0x0434
#define DSIPHY_PLL_RESETSM_CNTRL4 0x0438
#define DSIPHY_PLL_RESETSM_CNTRL5 0x043c
#define DSIPHY_PLL_KVCO_DIV_REF1 0x0440
#define DSIPHY_PLL_KVCO_DIV_REF2 0x0444
#define DSIPHY_PLL_KVCO_COUNT1 0x0448
#define DSIPHY_PLL_KVCO_COUNT2 0x044c
#define DSIPHY_PLL_VREF_CFG1 0x045c
#define DSIPHY_PLL_KVCO_CODE 0x0458
#define DSIPHY_PLL_VCO_DIV_REF1 0x046c
#define DSIPHY_PLL_VCO_DIV_REF2 0x0470
#define DSIPHY_PLL_VCO_COUNT1 0x0474
#define DSIPHY_PLL_VCO_COUNT2 0x0478
#define DSIPHY_PLL_PLLLOCK_CMP1 0x047c
#define DSIPHY_PLL_PLLLOCK_CMP2 0x0480
#define DSIPHY_PLL_PLLLOCK_CMP3 0x0484
#define DSIPHY_PLL_PLLLOCK_CMP_EN 0x0488
#define DSIPHY_PLL_PLL_VCO_TUNE 0x048C
#define DSIPHY_PLL_DEC_START 0x0490
#define DSIPHY_PLL_SSC_EN_CENTER 0x0494
#define DSIPHY_PLL_SSC_ADJ_PER1 0x0498
#define DSIPHY_PLL_SSC_ADJ_PER2 0x049c
#define DSIPHY_PLL_SSC_PER1 0x04a0
#define DSIPHY_PLL_SSC_PER2 0x04a4
#define DSIPHY_PLL_SSC_STEP_SIZE1 0x04a8
#define DSIPHY_PLL_SSC_STEP_SIZE2 0x04ac
#define DSIPHY_PLL_DIV_FRAC_START1 0x04b4
#define DSIPHY_PLL_DIV_FRAC_START2 0x04b8
#define DSIPHY_PLL_DIV_FRAC_START3 0x04bc
#define DSIPHY_PLL_TXCLK_EN 0x04c0
#define DSIPHY_PLL_PLL_CRCTRL 0x04c4
#define DSIPHY_PLL_RESET_SM_READY_STATUS 0x04cc
#define DSIPHY_PLL_PLL_MISC1 0x04e8
#define DSIPHY_PLL_CP_SET_CUR 0x04f0
#define DSIPHY_PLL_PLL_ICPMSET 0x04f4
#define DSIPHY_PLL_PLL_ICPCSET 0x04f8
#define DSIPHY_PLL_PLL_ICP_SET 0x04fc
#define DSIPHY_PLL_PLL_LPF1 0x0500
#define DSIPHY_PLL_PLL_LPF2_POSTDIV 0x0504
#define DSIPHY_PLL_PLL_BANDGAP 0x0508
#define DSI_DYNAMIC_REFRESH_PLL_CTRL15 0x050
#define DSI_DYNAMIC_REFRESH_PLL_CTRL19 0x060
#define DSI_DYNAMIC_REFRESH_PLL_CTRL20 0x064
#define DSI_DYNAMIC_REFRESH_PLL_CTRL21 0x068
#define DSI_DYNAMIC_REFRESH_PLL_CTRL22 0x06C
#define DSI_DYNAMIC_REFRESH_PLL_CTRL23 0x070
#define DSI_DYNAMIC_REFRESH_PLL_CTRL24 0x074
#define DSI_DYNAMIC_REFRESH_PLL_CTRL25 0x078
#define DSI_DYNAMIC_REFRESH_PLL_CTRL26 0x07C
#define DSI_DYNAMIC_REFRESH_PLL_CTRL27 0x080
#define DSI_DYNAMIC_REFRESH_PLL_CTRL28 0x084
#define DSI_DYNAMIC_REFRESH_PLL_CTRL29 0x088
#define DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR 0x094
#define DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR2 0x098
struct dsi_pll_input {
u32 fref; /* 19.2 Mhz, reference clk */
u32 fdata; /* bit clock rate */
u32 dsiclk_sel; /* 1, reg: 0x0014 */
u32 n2div; /* 1, reg: 0x0010, bit 4-7 */
u32 ssc_en; /* 1, reg: 0x0494, bit 0 */
u32 ldo_en; /* 0, reg: 0x004c, bit 0 */
/* fixed */
u32 refclk_dbler_en; /* 0, reg: 0x04c0, bit 1 */
u32 vco_measure_time; /* 5, unknown */
u32 kvco_measure_time; /* 5, unknown */
u32 bandgap_timer; /* 4, reg: 0x0430, bit 3 - 5 */
u32 pll_wakeup_timer; /* 5, reg: 0x043c, bit 0 - 2 */
u32 plllock_cnt; /* 1, reg: 0x0488, bit 1 - 2 */
u32 plllock_rng; /* 1, reg: 0x0488, bit 3 - 4 */
u32 ssc_center; /* 0, reg: 0x0494, bit 1 */
u32 ssc_adj_period; /* 37, reg: 0x498, bit 0 - 9 */
u32 ssc_spread; /* 0.005 */
u32 ssc_freq; /* unknown */
u32 pll_ie_trim; /* 4, reg: 0x0400 */
u32 pll_ip_trim; /* 4, reg: 0x0404 */
u32 pll_iptat_trim; /* reg: 0x0410 */
u32 pll_cpcset_cur; /* 1, reg: 0x04f0, bit 0 - 2 */
u32 pll_cpmset_cur; /* 1, reg: 0x04f0, bit 3 - 5 */
u32 pll_icpmset; /* 4, reg: 0x04fc, bit 3 - 5 */
u32 pll_icpcset; /* 4, reg: 0x04fc, bit 0 - 2 */
u32 pll_icpmset_p; /* 0, reg: 0x04f4, bit 0 - 2 */
u32 pll_icpmset_m; /* 0, reg: 0x04f4, bit 3 - 5 */
u32 pll_icpcset_p; /* 0, reg: 0x04f8, bit 0 - 2 */
u32 pll_icpcset_m; /* 0, reg: 0x04f8, bit 3 - 5 */
u32 pll_lpf_res1; /* 3, reg: 0x0504, bit 0 - 3 */
u32 pll_lpf_cap1; /* 11, reg: 0x0500, bit 0 - 3 */
u32 pll_lpf_cap2; /* 1, reg: 0x0500, bit 4 - 7 */
u32 pll_c3ctrl; /* 2, reg: 0x04c4 */
u32 pll_r3ctrl; /* 1, reg: 0x04c4 */
};
struct dsi_pll_output {
u32 pll_txclk_en; /* reg: 0x04c0 */
u32 dec_start; /* reg: 0x0490 */
u32 div_frac_start; /* reg: 0x04b4, 0x4b8, 0x04bc */
u32 ssc_period; /* reg: 0x04a0, 0x04a4 */
u32 ssc_step_size; /* reg: 0x04a8, 0x04ac */
u32 plllock_cmp; /* reg: 0x047c, 0x0480, 0x0484 */
u32 pll_vco_div_ref; /* reg: 0x046c, 0x0470 */
u32 pll_vco_count; /* reg: 0x0474, 0x0478 */
u32 pll_kvco_div_ref; /* reg: 0x0440, 0x0444 */
u32 pll_kvco_count; /* reg: 0x0448, 0x044c */
u32 pll_misc1; /* reg: 0x04e8 */
u32 pll_lpf2_postdiv; /* reg: 0x0504 */
u32 pll_resetsm_cntrl; /* reg: 0x042c */
u32 pll_resetsm_cntrl2; /* reg: 0x0430 */
u32 pll_resetsm_cntrl5; /* reg: 0x043c */
u32 pll_kvco_code; /* reg: 0x0458 */
u32 cmn_clk_cfg0; /* reg: 0x0010 */
u32 cmn_clk_cfg1; /* reg: 0x0014 */
u32 cmn_ldo_cntrl; /* reg: 0x004c */
u32 pll_postdiv; /* vco */
u32 pll_n1div; /* vco */
u32 pll_n2div; /* hr_oclk3, pixel */
u32 fcvo;
};
enum {
DSI_PLL_0,
DSI_PLL_1,
DSI_PLL_NUM
};
struct dsi_pll_db {
struct dsi_pll_db *next;
struct mdss_pll_resources *pll;
struct dsi_pll_input in;
struct dsi_pll_output out;
int source_setup_done;
};
enum {
PLL_OUTPUT_NONE,
PLL_OUTPUT_RIGHT,
PLL_OUTPUT_LEFT,
PLL_OUTPUT_BOTH
};
enum {
PLL_SOURCE_FROM_LEFT,
PLL_SOURCE_FROM_RIGHT
};
enum {
PLL_UNKNOWN,
PLL_STANDALONE,
PLL_SLAVE,
PLL_MASTER
};
int pll_vco_set_rate_14nm(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate);
int shadow_pll_vco_set_rate_14nm(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate);
long pll_vco_round_rate_14nm(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate);
unsigned long pll_vco_recalc_rate_14nm(struct clk_hw *hw,
unsigned long parent_rate);
int pll_vco_prepare_14nm(struct clk_hw *hw);
void pll_vco_unprepare_14nm(struct clk_hw *hw);
int shadow_post_n1_div_set_div(void *context,
unsigned int reg, unsigned int div);
int shadow_post_n1_div_get_div(void *context,
unsigned int reg, unsigned int *div);
int shadow_n2_div_set_div(void *context, unsigned int reg, unsigned int div);
int shadow_n2_div_get_div(void *context, unsigned int reg, unsigned int *div);
int post_n1_div_set_div(void *context, unsigned int reg, unsigned int div);
int post_n1_div_get_div(void *context, unsigned int reg, unsigned int *div);
int n2_div_set_div(void *context, unsigned int reg, unsigned int div);
int n2_div_get_div(void *context, unsigned int reg, unsigned int *div);
int dsi_pll_enable_seq_14nm(struct mdss_pll_resources *pll);
int dsi_mux_set_parent_14nm(void *context, unsigned int reg, unsigned int val);
int dsi_mux_get_parent_14nm(void *context, unsigned int reg, unsigned int *val);
#endif /* MDSS_DSI_PLL_14NM_H */

File diff suppressed because it is too large Load Diff

View File

@@ -1,590 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014-2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/clk/msm-clk-provider.h>
#include <linux/clk/msm-clk.h>
#include <linux/workqueue.h>
#include <linux/clk/msm-clock-generic.h>
#include <dt-bindings/clock/msm-clocks-8994.h>
#include "pll_drv.h"
#include "dsi_pll.h"
#define VCO_DELAY_USEC 1
static const struct clk_ops bypass_lp_div_mux_clk_ops;
static const struct clk_ops pixel_clk_src_ops;
static const struct clk_ops byte_clk_src_ops;
static const struct clk_ops ndiv_clk_ops;
static const struct clk_ops shadow_pixel_clk_src_ops;
static const struct clk_ops shadow_byte_clk_src_ops;
static const struct clk_ops clk_ops_gen_mux_dsi;
static int vco_set_rate_20nm(struct clk *c, unsigned long rate)
{
int rc;
struct dsi_pll_vco_clk *vco = to_vco_clk(c);
struct mdss_pll_resources *dsi_pll_res = vco->priv;
rc = mdss_pll_resource_enable(dsi_pll_res, true);
if (rc) {
pr_err("Failed to enable mdss dsi pll resources\n");
return rc;
}
pr_debug("Cancel pending pll off work\n");
cancel_work_sync(&dsi_pll_res->pll_off);
rc = pll_20nm_vco_set_rate(vco, rate);
mdss_pll_resource_enable(dsi_pll_res, false);
return rc;
}
static int pll1_vco_set_rate_20nm(struct clk *c, unsigned long rate)
{
struct dsi_pll_vco_clk *vco = to_vco_clk(c);
struct mdss_pll_resources *pll_res = vco->priv;
mdss_pll_resource_enable(pll_res, true);
__dsi_pll_disable(pll_res->pll_base);
mdss_pll_resource_enable(pll_res, false);
pr_debug("Configuring PLL1 registers.\n");
return 0;
}
static int shadow_vco_set_rate_20nm(struct clk *c, unsigned long rate)
{
int rc;
struct dsi_pll_vco_clk *vco = to_vco_clk(c);
struct mdss_pll_resources *dsi_pll_res = vco->priv;
if (!dsi_pll_res->resource_enable) {
pr_err("PLL resources disabled. Dynamic fps invalid\n");
return -EINVAL;
}
rc = shadow_pll_20nm_vco_set_rate(vco, rate);
return rc;
}
/* Op structures */
static const struct clk_ops pll1_clk_ops_dsi_vco = {
.set_rate = pll1_vco_set_rate_20nm,
};
static const struct clk_ops clk_ops_dsi_vco = {
.set_rate = vco_set_rate_20nm,
.round_rate = pll_20nm_vco_round_rate,
.handoff = pll_20nm_vco_handoff,
.prepare = pll_20nm_vco_prepare,
.unprepare = pll_20nm_vco_unprepare,
};
static struct clk_div_ops fixed_hr_oclk2_div_ops = {
.set_div = fixed_hr_oclk2_set_div,
.get_div = fixed_hr_oclk2_get_div,
};
static struct clk_div_ops ndiv_ops = {
.set_div = ndiv_set_div,
.get_div = ndiv_get_div,
};
static struct clk_div_ops hr_oclk3_div_ops = {
.set_div = hr_oclk3_set_div,
.get_div = hr_oclk3_get_div,
};
static struct clk_mux_ops bypass_lp_div_mux_ops = {
.set_mux_sel = set_bypass_lp_div_mux_sel,
.get_mux_sel = get_bypass_lp_div_mux_sel,
};
static const struct clk_ops shadow_clk_ops_dsi_vco = {
.set_rate = shadow_vco_set_rate_20nm,
.round_rate = pll_20nm_vco_round_rate,
.handoff = pll_20nm_vco_handoff,
};
static struct clk_div_ops shadow_fixed_hr_oclk2_div_ops = {
.set_div = shadow_fixed_hr_oclk2_set_div,
.get_div = fixed_hr_oclk2_get_div,
};
static struct clk_div_ops shadow_ndiv_ops = {
.set_div = shadow_ndiv_set_div,
.get_div = ndiv_get_div,
};
static struct clk_div_ops shadow_hr_oclk3_div_ops = {
.set_div = shadow_hr_oclk3_set_div,
.get_div = hr_oclk3_get_div,
};
static struct clk_mux_ops shadow_bypass_lp_div_mux_ops = {
.set_mux_sel = set_shadow_bypass_lp_div_mux_sel,
.get_mux_sel = get_bypass_lp_div_mux_sel,
};
static struct clk_mux_ops mdss_byte_mux_ops = {
.set_mux_sel = set_mdss_byte_mux_sel,
.get_mux_sel = get_mdss_byte_mux_sel,
};
static struct clk_mux_ops mdss_pixel_mux_ops = {
.set_mux_sel = set_mdss_pixel_mux_sel,
.get_mux_sel = get_mdss_pixel_mux_sel,
};
static struct dsi_pll_vco_clk mdss_dsi1_vco_clk_src = {
.c = {
.dbg_name = "mdss_dsi1_vco_clk_src",
.ops = &pll1_clk_ops_dsi_vco,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(mdss_dsi1_vco_clk_src.c),
},
};
static struct dsi_pll_vco_clk dsi_vco_clk_8994 = {
.ref_clk_rate = 19200000,
.min_rate = 300000000,
.max_rate = 1500000000,
.pll_en_seq_cnt = 1,
.pll_enable_seqs[0] = pll_20nm_vco_enable_seq,
.c = {
.dbg_name = "dsi_vco_clk_8994",
.ops = &clk_ops_dsi_vco,
CLK_INIT(dsi_vco_clk_8994.c),
},
};
static struct dsi_pll_vco_clk shadow_dsi_vco_clk_8994 = {
.ref_clk_rate = 19200000,
.min_rate = 300000000,
.max_rate = 1500000000,
.c = {
.dbg_name = "shadow_dsi_vco_clk_8994",
.ops = &shadow_clk_ops_dsi_vco,
CLK_INIT(shadow_dsi_vco_clk_8994.c),
},
};
static struct div_clk ndiv_clk_8994 = {
.data = {
.max_div = 15,
.min_div = 1,
},
.ops = &ndiv_ops,
.c = {
.parent = &dsi_vco_clk_8994.c,
.dbg_name = "ndiv_clk_8994",
.ops = &ndiv_clk_ops,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(ndiv_clk_8994.c),
},
};
static struct div_clk shadow_ndiv_clk_8994 = {
.data = {
.max_div = 15,
.min_div = 1,
},
.ops = &shadow_ndiv_ops,
.c = {
.parent = &shadow_dsi_vco_clk_8994.c,
.dbg_name = "shadow_ndiv_clk_8994",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(shadow_ndiv_clk_8994.c),
},
};
static struct div_clk indirect_path_div2_clk_8994 = {
.data = {
.div = 2,
.min_div = 2,
.max_div = 2,
},
.c = {
.parent = &ndiv_clk_8994.c,
.dbg_name = "indirect_path_div2_clk_8994",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(indirect_path_div2_clk_8994.c),
},
};
static struct div_clk shadow_indirect_path_div2_clk_8994 = {
.data = {
.div = 2,
.min_div = 2,
.max_div = 2,
},
.c = {
.parent = &shadow_ndiv_clk_8994.c,
.dbg_name = "shadow_indirect_path_div2_clk_8994",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(shadow_indirect_path_div2_clk_8994.c),
},
};
static struct div_clk hr_oclk3_div_clk_8994 = {
.data = {
.max_div = 255,
.min_div = 1,
},
.ops = &hr_oclk3_div_ops,
.c = {
.parent = &dsi_vco_clk_8994.c,
.dbg_name = "hr_oclk3_div_clk_8994",
.ops = &pixel_clk_src_ops,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(hr_oclk3_div_clk_8994.c),
},
};
static struct div_clk shadow_hr_oclk3_div_clk_8994 = {
.data = {
.max_div = 255,
.min_div = 1,
},
.ops = &shadow_hr_oclk3_div_ops,
.c = {
.parent = &shadow_dsi_vco_clk_8994.c,
.dbg_name = "shadow_hr_oclk3_div_clk_8994",
.ops = &shadow_pixel_clk_src_ops,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(shadow_hr_oclk3_div_clk_8994.c),
},
};
static struct div_clk pixel_clk_src = {
.data = {
.div = 2,
.min_div = 2,
.max_div = 2,
},
.c = {
.parent = &hr_oclk3_div_clk_8994.c,
.dbg_name = "pixel_clk_src",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(pixel_clk_src.c),
},
};
static struct div_clk shadow_pixel_clk_src = {
.data = {
.div = 2,
.min_div = 2,
.max_div = 2,
},
.c = {
.parent = &shadow_hr_oclk3_div_clk_8994.c,
.dbg_name = "shadow_pixel_clk_src",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(shadow_pixel_clk_src.c),
},
};
static struct mux_clk bypass_lp_div_mux_8994 = {
.num_parents = 2,
.parents = (struct clk_src[]){
{&dsi_vco_clk_8994.c, 0},
{&indirect_path_div2_clk_8994.c, 1},
},
.ops = &bypass_lp_div_mux_ops,
.c = {
.parent = &dsi_vco_clk_8994.c,
.dbg_name = "bypass_lp_div_mux_8994",
.ops = &bypass_lp_div_mux_clk_ops,
CLK_INIT(bypass_lp_div_mux_8994.c),
},
};
static struct mux_clk shadow_bypass_lp_div_mux_8994 = {
.num_parents = 2,
.parents = (struct clk_src[]){
{&shadow_dsi_vco_clk_8994.c, 0},
{&shadow_indirect_path_div2_clk_8994.c, 1},
},
.ops = &shadow_bypass_lp_div_mux_ops,
.c = {
.parent = &shadow_dsi_vco_clk_8994.c,
.dbg_name = "shadow_bypass_lp_div_mux_8994",
.ops = &clk_ops_gen_mux,
CLK_INIT(shadow_bypass_lp_div_mux_8994.c),
},
};
static struct div_clk fixed_hr_oclk2_div_clk_8994 = {
.ops = &fixed_hr_oclk2_div_ops,
.data = {
.min_div = 4,
.max_div = 4,
},
.c = {
.parent = &bypass_lp_div_mux_8994.c,
.dbg_name = "fixed_hr_oclk2_div_clk_8994",
.ops = &byte_clk_src_ops,
CLK_INIT(fixed_hr_oclk2_div_clk_8994.c),
},
};
static struct div_clk shadow_fixed_hr_oclk2_div_clk_8994 = {
.ops = &shadow_fixed_hr_oclk2_div_ops,
.data = {
.min_div = 4,
.max_div = 4,
},
.c = {
.parent = &shadow_bypass_lp_div_mux_8994.c,
.dbg_name = "shadow_fixed_hr_oclk2_div_clk_8994",
.ops = &shadow_byte_clk_src_ops,
CLK_INIT(shadow_fixed_hr_oclk2_div_clk_8994.c),
},
};
static struct div_clk byte_clk_src = {
.data = {
.div = 2,
.min_div = 2,
.max_div = 2,
},
.c = {
.parent = &fixed_hr_oclk2_div_clk_8994.c,
.dbg_name = "byte_clk_src",
.ops = &clk_ops_div,
CLK_INIT(byte_clk_src.c),
},
};
static struct div_clk shadow_byte_clk_src = {
.data = {
.div = 2,
.min_div = 2,
.max_div = 2,
},
.c = {
.parent = &shadow_fixed_hr_oclk2_div_clk_8994.c,
.dbg_name = "shadow_byte_clk_src",
.ops = &clk_ops_div,
CLK_INIT(shadow_byte_clk_src.c),
},
};
static struct mux_clk mdss_pixel_clk_mux = {
.num_parents = 2,
.parents = (struct clk_src[]) {
{&pixel_clk_src.c, 0},
{&shadow_pixel_clk_src.c, 1},
},
.ops = &mdss_pixel_mux_ops,
.c = {
.parent = &pixel_clk_src.c,
.dbg_name = "mdss_pixel_clk_mux",
.ops = &clk_ops_gen_mux,
CLK_INIT(mdss_pixel_clk_mux.c),
}
};
static struct mux_clk mdss_byte_clk_mux = {
.num_parents = 2,
.parents = (struct clk_src[]) {
{&byte_clk_src.c, 0},
{&shadow_byte_clk_src.c, 1},
},
.ops = &mdss_byte_mux_ops,
.c = {
.parent = &byte_clk_src.c,
.dbg_name = "mdss_byte_clk_mux",
.ops = &clk_ops_gen_mux_dsi,
CLK_INIT(mdss_byte_clk_mux.c),
}
};
static struct clk_lookup mdss_dsi_pll_1_cc_8994[] = {
CLK_LIST(mdss_dsi1_vco_clk_src),
};
static struct clk_lookup mdss_dsi_pllcc_8994[] = {
CLK_LIST(mdss_pixel_clk_mux),
CLK_LIST(mdss_byte_clk_mux),
CLK_LIST(pixel_clk_src),
CLK_LIST(byte_clk_src),
CLK_LIST(fixed_hr_oclk2_div_clk_8994),
CLK_LIST(bypass_lp_div_mux_8994),
CLK_LIST(hr_oclk3_div_clk_8994),
CLK_LIST(indirect_path_div2_clk_8994),
CLK_LIST(ndiv_clk_8994),
CLK_LIST(dsi_vco_clk_8994),
CLK_LIST(shadow_pixel_clk_src),
CLK_LIST(shadow_byte_clk_src),
CLK_LIST(shadow_fixed_hr_oclk2_div_clk_8994),
CLK_LIST(shadow_bypass_lp_div_mux_8994),
CLK_LIST(shadow_hr_oclk3_div_clk_8994),
CLK_LIST(shadow_indirect_path_div2_clk_8994),
CLK_LIST(shadow_ndiv_clk_8994),
CLK_LIST(shadow_dsi_vco_clk_8994),
};
static void dsi_pll_off_work(struct work_struct *work)
{
struct mdss_pll_resources *pll_res;
if (!work) {
pr_err("pll_resource is invalid\n");
return;
}
pr_debug("Starting PLL off Worker%s\n", __func__);
pll_res = container_of(work, struct
mdss_pll_resources, pll_off);
mdss_pll_resource_enable(pll_res, true);
__dsi_pll_disable(pll_res->pll_base);
if (pll_res->pll_1_base)
__dsi_pll_disable(pll_res->pll_1_base);
mdss_pll_resource_enable(pll_res, false);
}
static int dsi_pll_regulator_notifier_call(struct notifier_block *self,
unsigned long event, void *data)
{
struct mdss_pll_resources *pll_res;
if (!self) {
pr_err("pll_resource is invalid\n");
goto error;
}
pll_res = container_of(self, struct
mdss_pll_resources, gdsc_cb);
if (event & REGULATOR_EVENT_ENABLE) {
pr_debug("Regulator ON event. Scheduling pll off worker\n");
schedule_work(&pll_res->pll_off);
}
if (event & REGULATOR_EVENT_DISABLE)
pr_debug("Regulator OFF event.\n");
error:
return NOTIFY_OK;
}
int dsi_pll_clock_register_20nm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc;
struct dss_vreg *pll_reg;
/*
* Set client data to mux, div and vco clocks.
* This needs to be done only for PLL0 since, that is the one in
* use.
**/
if (!pll_res->index) {
byte_clk_src.priv = pll_res;
pixel_clk_src.priv = pll_res;
bypass_lp_div_mux_8994.priv = pll_res;
indirect_path_div2_clk_8994.priv = pll_res;
ndiv_clk_8994.priv = pll_res;
fixed_hr_oclk2_div_clk_8994.priv = pll_res;
hr_oclk3_div_clk_8994.priv = pll_res;
dsi_vco_clk_8994.priv = pll_res;
shadow_byte_clk_src.priv = pll_res;
shadow_pixel_clk_src.priv = pll_res;
shadow_bypass_lp_div_mux_8994.priv = pll_res;
shadow_indirect_path_div2_clk_8994.priv = pll_res;
shadow_ndiv_clk_8994.priv = pll_res;
shadow_fixed_hr_oclk2_div_clk_8994.priv = pll_res;
shadow_hr_oclk3_div_clk_8994.priv = pll_res;
shadow_dsi_vco_clk_8994.priv = pll_res;
pll_res->vco_delay = VCO_DELAY_USEC;
/* Set clock source operations */
pixel_clk_src_ops = clk_ops_slave_div;
pixel_clk_src_ops.prepare = dsi_pll_div_prepare;
ndiv_clk_ops = clk_ops_div;
ndiv_clk_ops.prepare = dsi_pll_div_prepare;
byte_clk_src_ops = clk_ops_div;
byte_clk_src_ops.prepare = dsi_pll_div_prepare;
bypass_lp_div_mux_clk_ops = clk_ops_gen_mux;
bypass_lp_div_mux_clk_ops.prepare = dsi_pll_mux_prepare;
clk_ops_gen_mux_dsi = clk_ops_gen_mux;
clk_ops_gen_mux_dsi.round_rate = parent_round_rate;
clk_ops_gen_mux_dsi.set_rate = parent_set_rate;
shadow_pixel_clk_src_ops = clk_ops_slave_div;
shadow_pixel_clk_src_ops.prepare = dsi_pll_div_prepare;
shadow_byte_clk_src_ops = clk_ops_div;
shadow_byte_clk_src_ops.prepare = dsi_pll_div_prepare;
} else {
mdss_dsi1_vco_clk_src.priv = pll_res;
}
if ((pll_res->target_id == MDSS_PLL_TARGET_8994) ||
(pll_res->target_id == MDSS_PLL_TARGET_8992)) {
if (pll_res->index) {
rc = of_msm_clock_register(pdev->dev.of_node,
mdss_dsi_pll_1_cc_8994,
ARRAY_SIZE(mdss_dsi_pll_1_cc_8994));
if (rc) {
pr_err("Clock register failed\n");
rc = -EPROBE_DEFER;
}
} else {
rc = of_msm_clock_register(pdev->dev.of_node,
mdss_dsi_pllcc_8994,
ARRAY_SIZE(mdss_dsi_pllcc_8994));
if (rc) {
pr_err("Clock register failed\n");
rc = -EPROBE_DEFER;
}
pll_res->gdsc_cb.notifier_call =
dsi_pll_regulator_notifier_call;
INIT_WORK(&pll_res->pll_off, dsi_pll_off_work);
pll_reg = mdss_pll_get_mp_by_reg_name(pll_res, "gdsc");
if (pll_reg) {
pr_debug("Registering for gdsc regulator events\n");
if (regulator_register_notifier(pll_reg->vreg,
&(pll_res->gdsc_cb)))
pr_err("Regulator notification registration failed!\n");
}
}
} else {
pr_err("Invalid target ID\n");
rc = -EINVAL;
}
if (!rc)
pr_info("Registered DSI PLL clocks successfully\n");
return rc;
}

View File

@@ -1,317 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/clk/msm-clk-provider.h>
#include <linux/clk/msm-clk.h>
#include <linux/clk/msm-clock-generic.h>
#include <dt-bindings/clock/msm-clocks-8974.h>
#include "pll_drv.h"
#include "dsi_pll.h"
#define VCO_DELAY_USEC 1
static struct clk_div_ops fixed_2div_ops;
static const struct clk_ops byte_mux_clk_ops;
static const struct clk_ops pixel_clk_src_ops;
static const struct clk_ops byte_clk_src_ops;
static const struct clk_ops analog_postdiv_clk_ops;
static struct lpfr_cfg lpfr_lut_struct[] = {
{479500000, 8},
{480000000, 11},
{575500000, 8},
{576000000, 12},
{610500000, 8},
{659500000, 9},
{671500000, 10},
{672000000, 14},
{708500000, 10},
{750000000, 11},
};
static void dsi_pll_software_reset(struct mdss_pll_resources *dsi_pll_res)
{
/*
* Add HW recommended delays after toggling the software
* reset bit off and back on.
*/
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x01);
udelay(1);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x00);
udelay(1);
}
static int vco_set_rate_hpm(struct clk *c, unsigned long rate)
{
int rc;
struct dsi_pll_vco_clk *vco = to_vco_clk(c);
struct mdss_pll_resources *dsi_pll_res = vco->priv;
rc = mdss_pll_resource_enable(dsi_pll_res, true);
if (rc) {
pr_err("Failed to enable mdss dsi pll resources\n");
return rc;
}
rc = vco_set_rate(vco, rate);
mdss_pll_resource_enable(dsi_pll_res, false);
return rc;
}
static int dsi_pll_enable_seq_8974(struct mdss_pll_resources *dsi_pll_res)
{
int i, rc = 0;
int pll_locked;
dsi_pll_software_reset(dsi_pll_res);
/*
* PLL power up sequence.
* Add necessary delays recommeded by hardware.
*/
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
udelay(1);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
udelay(200);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07);
udelay(500);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
udelay(500);
for (i = 0; i < 2; i++) {
udelay(100);
/* DSI Uniphy lock detect setting */
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x0c);
udelay(100);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x0d);
pll_locked = dsi_pll_lock_status(dsi_pll_res);
if (pll_locked)
break;
dsi_pll_software_reset(dsi_pll_res);
/*
* PLL power up sequence.
* Add necessary delays recommeded by hardware.
*/
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x1);
udelay(1);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x5);
udelay(200);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x7);
udelay(250);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x5);
udelay(200);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x7);
udelay(500);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0xf);
udelay(500);
}
if (!pll_locked) {
pr_err("DSI PLL lock failed\n");
rc = -EINVAL;
} else {
pr_debug("DSI PLL Lock success\n");
}
return rc;
}
/* Op structures */
static const struct clk_ops clk_ops_dsi_vco = {
.set_rate = vco_set_rate_hpm,
.round_rate = vco_round_rate,
.handoff = vco_handoff,
.prepare = vco_prepare,
.unprepare = vco_unprepare,
};
static struct clk_div_ops fixed_4div_ops = {
.set_div = fixed_4div_set_div,
.get_div = fixed_4div_get_div,
};
static struct clk_div_ops analog_postdiv_ops = {
.set_div = analog_set_div,
.get_div = analog_get_div,
};
static struct clk_div_ops digital_postdiv_ops = {
.set_div = digital_set_div,
.get_div = digital_get_div,
};
static struct clk_mux_ops byte_mux_ops = {
.set_mux_sel = set_byte_mux_sel,
.get_mux_sel = get_byte_mux_sel,
};
static struct dsi_pll_vco_clk dsi_vco_clk_8974 = {
.ref_clk_rate = 19200000,
.min_rate = 350000000,
.max_rate = 750000000,
.pll_en_seq_cnt = 3,
.pll_enable_seqs[0] = dsi_pll_enable_seq_8974,
.pll_enable_seqs[1] = dsi_pll_enable_seq_8974,
.pll_enable_seqs[2] = dsi_pll_enable_seq_8974,
.lpfr_lut_size = 10,
.lpfr_lut = lpfr_lut_struct,
.c = {
.dbg_name = "dsi_vco_clk_8974",
.ops = &clk_ops_dsi_vco,
CLK_INIT(dsi_vco_clk_8974.c),
},
};
static struct div_clk analog_postdiv_clk_8974 = {
.data = {
.max_div = 255,
.min_div = 1,
},
.ops = &analog_postdiv_ops,
.c = {
.parent = &dsi_vco_clk_8974.c,
.dbg_name = "analog_postdiv_clk",
.ops = &analog_postdiv_clk_ops,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(analog_postdiv_clk_8974.c),
},
};
static struct div_clk indirect_path_div2_clk_8974 = {
.ops = &fixed_2div_ops,
.data = {
.div = 2,
.min_div = 2,
.max_div = 2,
},
.c = {
.parent = &analog_postdiv_clk_8974.c,
.dbg_name = "indirect_path_div2_clk",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(indirect_path_div2_clk_8974.c),
},
};
static struct div_clk pixel_clk_src_8974 = {
.data = {
.max_div = 255,
.min_div = 1,
},
.ops = &digital_postdiv_ops,
.c = {
.parent = &dsi_vco_clk_8974.c,
.dbg_name = "pixel_clk_src_8974",
.ops = &pixel_clk_src_ops,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(pixel_clk_src_8974.c),
},
};
static struct mux_clk byte_mux_8974 = {
.num_parents = 2,
.parents = (struct clk_src[]){
{&dsi_vco_clk_8974.c, 0},
{&indirect_path_div2_clk_8974.c, 1},
},
.ops = &byte_mux_ops,
.c = {
.parent = &dsi_vco_clk_8974.c,
.dbg_name = "byte_mux_8974",
.ops = &byte_mux_clk_ops,
CLK_INIT(byte_mux_8974.c),
},
};
static struct div_clk byte_clk_src_8974 = {
.ops = &fixed_4div_ops,
.data = {
.min_div = 4,
.max_div = 4,
},
.c = {
.parent = &byte_mux_8974.c,
.dbg_name = "byte_clk_src_8974",
.ops = &byte_clk_src_ops,
CLK_INIT(byte_clk_src_8974.c),
},
};
static struct clk_lookup mdss_dsi_pllcc_8974[] = {
CLK_LOOKUP_OF("pixel_src", pixel_clk_src_8974,
"fd8c0000.qcom,mmsscc-mdss"),
CLK_LOOKUP_OF("byte_src", byte_clk_src_8974,
"fd8c0000.qcom,mmsscc-mdss"),
};
int dsi_pll_clock_register_hpm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc;
/* Set client data to mux, div and vco clocks */
byte_clk_src_8974.priv = pll_res;
pixel_clk_src_8974.priv = pll_res;
byte_mux_8974.priv = pll_res;
indirect_path_div2_clk_8974.priv = pll_res;
analog_postdiv_clk_8974.priv = pll_res;
dsi_vco_clk_8974.priv = pll_res;
pll_res->vco_delay = VCO_DELAY_USEC;
/* Set clock source operations */
pixel_clk_src_ops = clk_ops_slave_div;
pixel_clk_src_ops.prepare = dsi_pll_div_prepare;
analog_postdiv_clk_ops = clk_ops_div;
analog_postdiv_clk_ops.prepare = dsi_pll_div_prepare;
byte_clk_src_ops = clk_ops_div;
byte_clk_src_ops.prepare = dsi_pll_div_prepare;
byte_mux_clk_ops = clk_ops_gen_mux;
byte_mux_clk_ops.prepare = dsi_pll_mux_prepare;
if (pll_res->target_id == MDSS_PLL_TARGET_8974) {
rc = of_msm_clock_register(pdev->dev.of_node,
mdss_dsi_pllcc_8974, ARRAY_SIZE(mdss_dsi_pllcc_8974));
if (rc) {
pr_err("Clock register failed\n");
rc = -EPROBE_DEFER;
}
} else {
pr_err("Invalid target ID\n");
rc = -EINVAL;
}
if (!rc)
pr_info("Registered DSI PLL clocks successfully\n");
return rc;
}

View File

@@ -1,545 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <dt-bindings/clock/mdss-28nm-pll-clk.h>
#include "pll_drv.h"
#include "dsi_pll.h"
#include "dsi_pll_28nm.h"
#define VCO_DELAY_USEC 1000
enum {
DSI_PLL_0,
DSI_PLL_1,
DSI_PLL_MAX
};
static struct lpfr_cfg lpfr_lut_struct[] = {
{479500000, 8},
{480000000, 11},
{575500000, 8},
{576000000, 12},
{610500000, 8},
{659500000, 9},
{671500000, 10},
{672000000, 14},
{708500000, 10},
{750000000, 11},
};
static void dsi_pll_sw_reset(struct mdss_pll_resources *rsc)
{
/*
* DSI PLL software reset. Add HW recommended delays after toggling
* the software reset bit off and back on.
*/
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x01);
ndelay(500);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x00);
}
static void dsi_pll_toggle_lock_detect(
struct mdss_pll_resources *rsc)
{
/* DSI PLL toggle lock detect setting */
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x04);
ndelay(500);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x05);
udelay(512);
}
static int dsi_pll_check_lock_status(
struct mdss_pll_resources *rsc)
{
int rc = 0;
rc = dsi_pll_lock_status(rsc);
if (rc)
pr_debug("PLL Locked\n");
else
pr_err("PLL failed to lock\n");
return rc;
}
static int dsi_pll_enable_seq_gf2(struct mdss_pll_resources *rsc)
{
int pll_locked = 0;
dsi_pll_sw_reset(rsc);
/*
* GF PART 2 PLL power up sequence.
* Add necessary delays recommended by hardware.
*/
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x04);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
udelay(3);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
udelay(500);
dsi_pll_toggle_lock_detect(rsc);
pll_locked = dsi_pll_check_lock_status(rsc);
return pll_locked ? 0 : -EINVAL;
}
static int dsi_pll_enable_seq_gf1(struct mdss_pll_resources *rsc)
{
int pll_locked = 0;
dsi_pll_sw_reset(rsc);
/*
* GF PART 1 PLL power up sequence.
* Add necessary delays recommended by hardware.
*/
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x14);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
udelay(3);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
udelay(500);
dsi_pll_toggle_lock_detect(rsc);
pll_locked = dsi_pll_check_lock_status(rsc);
return pll_locked ? 0 : -EINVAL;
}
static int dsi_pll_enable_seq_tsmc(struct mdss_pll_resources *rsc)
{
int pll_locked = 0;
dsi_pll_sw_reset(rsc);
/*
* TSMC PLL power up sequence.
* Add necessary delays recommended by hardware.
*/
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x34);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
udelay(500);
dsi_pll_toggle_lock_detect(rsc);
pll_locked = dsi_pll_check_lock_status(rsc);
return pll_locked ? 0 : -EINVAL;
}
static struct regmap_config dsi_pll_28lpm_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = 0xF4,
};
static struct regmap_bus analog_postdiv_regmap_bus = {
.reg_write = analog_postdiv_reg_write,
.reg_read = analog_postdiv_reg_read,
};
static struct regmap_bus byteclk_src_mux_regmap_bus = {
.reg_write = byteclk_mux_write_sel,
.reg_read = byteclk_mux_read_sel,
};
static struct regmap_bus pclk_src_regmap_bus = {
.reg_write = pixel_clk_set_div,
.reg_read = pixel_clk_get_div,
};
static const struct clk_ops clk_ops_vco_28lpm = {
.recalc_rate = vco_28nm_recalc_rate,
.set_rate = vco_28nm_set_rate,
.round_rate = vco_28nm_round_rate,
.prepare = vco_28nm_prepare,
.unprepare = vco_28nm_unprepare,
};
static struct dsi_pll_vco_clk dsi0pll_vco_clk = {
.ref_clk_rate = 19200000UL,
.min_rate = 350000000UL,
.max_rate = 750000000UL,
.pll_en_seq_cnt = 9,
.pll_enable_seqs[0] = dsi_pll_enable_seq_tsmc,
.pll_enable_seqs[1] = dsi_pll_enable_seq_tsmc,
.pll_enable_seqs[2] = dsi_pll_enable_seq_tsmc,
.pll_enable_seqs[3] = dsi_pll_enable_seq_gf1,
.pll_enable_seqs[4] = dsi_pll_enable_seq_gf1,
.pll_enable_seqs[5] = dsi_pll_enable_seq_gf1,
.pll_enable_seqs[6] = dsi_pll_enable_seq_gf2,
.pll_enable_seqs[7] = dsi_pll_enable_seq_gf2,
.pll_enable_seqs[8] = dsi_pll_enable_seq_gf2,
.lpfr_lut_size = 10,
.lpfr_lut = lpfr_lut_struct,
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_vco_clk",
.parent_names = (const char *[]){"cxo"},
.num_parents = 1,
.ops = &clk_ops_vco_28lpm,
.flags = CLK_GET_RATE_NOCACHE,
},
};
static struct dsi_pll_vco_clk dsi1pll_vco_clk = {
.ref_clk_rate = 19200000UL,
.min_rate = 350000000UL,
.max_rate = 750000000UL,
.pll_en_seq_cnt = 9,
.pll_enable_seqs[0] = dsi_pll_enable_seq_tsmc,
.pll_enable_seqs[1] = dsi_pll_enable_seq_tsmc,
.pll_enable_seqs[2] = dsi_pll_enable_seq_tsmc,
.pll_enable_seqs[3] = dsi_pll_enable_seq_gf1,
.pll_enable_seqs[4] = dsi_pll_enable_seq_gf1,
.pll_enable_seqs[5] = dsi_pll_enable_seq_gf1,
.pll_enable_seqs[6] = dsi_pll_enable_seq_gf2,
.pll_enable_seqs[7] = dsi_pll_enable_seq_gf2,
.pll_enable_seqs[8] = dsi_pll_enable_seq_gf2,
.lpfr_lut_size = 10,
.lpfr_lut = lpfr_lut_struct,
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_vco_clk",
.parent_names = (const char *[]){"cxo"},
.num_parents = 1,
.ops = &clk_ops_vco_28lpm,
.flags = CLK_GET_RATE_NOCACHE,
},
};
static struct clk_regmap_div dsi0pll_analog_postdiv = {
.reg = DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG,
.shift = 0,
.width = 4,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_analog_postdiv",
.parent_names = (const char *[]){"dsi0pll_vco_clk"},
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_regmap_div_ops,
},
},
};
static struct clk_regmap_div dsi1pll_analog_postdiv = {
.reg = DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG,
.shift = 0,
.width = 4,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_analog_postdiv",
.parent_names = (const char *[]){"dsi1pll_vco_clk"},
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_regmap_div_ops,
},
},
};
static struct clk_fixed_factor dsi0pll_indirect_path_src = {
.div = 2,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_indirect_path_src",
.parent_names = (const char *[]){"dsi0pll_analog_postdiv"},
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor dsi1pll_indirect_path_src = {
.div = 2,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_indirect_path_src",
.parent_names = (const char *[]){"dsi1pll_analog_postdiv"},
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_regmap_mux dsi0pll_byteclk_src_mux = {
.reg = DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG,
.shift = 1,
.width = 1,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_byteclk_src_mux",
.parent_names = (const char *[]){
"dsi0pll_vco_clk",
"dsi0pll_indirect_path_src"},
.num_parents = 2,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_regmap_mux_closest_ops,
},
},
};
static struct clk_regmap_mux dsi1pll_byteclk_src_mux = {
.reg = DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG,
.shift = 1,
.width = 1,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_byteclk_src_mux",
.parent_names = (const char *[]){
"dsi1pll_vco_clk",
"dsi1pll_indirect_path_src"},
.num_parents = 2,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_regmap_mux_closest_ops,
},
},
};
static struct clk_fixed_factor dsi0pll_byteclk_src = {
.div = 4,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_byteclk_src",
.parent_names = (const char *[]){
"dsi0pll_byteclk_src_mux"},
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor dsi1pll_byteclk_src = {
.div = 4,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_byteclk_src",
.parent_names = (const char *[]){
"dsi1pll_byteclk_src_mux"},
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_regmap_div dsi0pll_pclk_src = {
.reg = DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG,
.shift = 0,
.width = 8,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_pclk_src",
.parent_names = (const char *[]){"dsi0pll_vco_clk"},
.num_parents = 1,
.flags = CLK_GET_RATE_NOCACHE,
.ops = &clk_regmap_div_ops,
},
},
};
static struct clk_regmap_div dsi1pll_pclk_src = {
.reg = DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG,
.shift = 0,
.width = 8,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_pclk_src",
.parent_names = (const char *[]){"dsi1pll_vco_clk"},
.num_parents = 1,
.flags = CLK_GET_RATE_NOCACHE,
.ops = &clk_regmap_div_ops,
},
},
};
static struct clk_hw *mdss_dsi_pllcc_28lpm[] = {
[VCO_CLK_0] = &dsi0pll_vco_clk.hw,
[ANALOG_POSTDIV_0_CLK] = &dsi0pll_analog_postdiv.clkr.hw,
[INDIRECT_PATH_SRC_0_CLK] = &dsi0pll_indirect_path_src.hw,
[BYTECLK_SRC_MUX_0_CLK] = &dsi0pll_byteclk_src_mux.clkr.hw,
[BYTECLK_SRC_0_CLK] = &dsi0pll_byteclk_src.hw,
[PCLK_SRC_0_CLK] = &dsi0pll_pclk_src.clkr.hw,
[VCO_CLK_1] = &dsi1pll_vco_clk.hw,
[ANALOG_POSTDIV_1_CLK] = &dsi1pll_analog_postdiv.clkr.hw,
[INDIRECT_PATH_SRC_1_CLK] = &dsi1pll_indirect_path_src.hw,
[BYTECLK_SRC_MUX_1_CLK] = &dsi1pll_byteclk_src_mux.clkr.hw,
[BYTECLK_SRC_1_CLK] = &dsi1pll_byteclk_src.hw,
[PCLK_SRC_1_CLK] = &dsi1pll_pclk_src.clkr.hw,
};
int dsi_pll_clock_register_28lpm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc = 0, ndx, i;
struct clk *clk;
struct clk_onecell_data *clk_data;
int num_clks = ARRAY_SIZE(mdss_dsi_pllcc_28lpm);
struct regmap *rmap;
int const ssc_freq_min = 30000; /* min. recommended freq. value */
int const ssc_freq_max = 33000; /* max. recommended freq. value */
int const ssc_ppm_max = 5000; /* max. recommended ppm */
ndx = pll_res->index;
if (ndx >= DSI_PLL_MAX) {
pr_err("pll index(%d) NOT supported\n", ndx);
return -EINVAL;
}
pll_res->vco_delay = VCO_DELAY_USEC;
if (pll_res->ssc_en) {
if (!pll_res->ssc_freq || (pll_res->ssc_freq < ssc_freq_min) ||
(pll_res->ssc_freq > ssc_freq_max)) {
pll_res->ssc_freq = ssc_freq_min;
pr_debug("SSC frequency out of recommended range. Set to default=%d\n",
pll_res->ssc_freq);
}
if (!pll_res->ssc_ppm || (pll_res->ssc_ppm > ssc_ppm_max)) {
pll_res->ssc_ppm = ssc_ppm_max;
pr_debug("SSC PPM out of recommended range. Set to default=%d\n",
pll_res->ssc_ppm);
}
}
clk_data = devm_kzalloc(&pdev->dev, sizeof(*clk_data),
GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
clk_data->clks = devm_kcalloc(&pdev->dev, num_clks,
sizeof(struct clk *), GFP_KERNEL);
if (!clk_data->clks)
return -ENOMEM;
clk_data->clk_num = num_clks;
/* Establish client data */
if (ndx == 0) {
rmap = devm_regmap_init(&pdev->dev, &byteclk_src_mux_regmap_bus,
pll_res, &dsi_pll_28lpm_config);
if (IS_ERR(rmap)) {
pr_err("regmap init failed for DSI clock:%d\n",
pll_res->index);
return -EINVAL;
}
dsi0pll_byteclk_src_mux.clkr.regmap = rmap;
rmap = devm_regmap_init(&pdev->dev, &analog_postdiv_regmap_bus,
pll_res, &dsi_pll_28lpm_config);
if (IS_ERR(rmap)) {
pr_err("regmap init failed for DSI clock:%d\n",
pll_res->index);
return -EINVAL;
}
dsi0pll_analog_postdiv.clkr.regmap = rmap;
rmap = devm_regmap_init(&pdev->dev, &pclk_src_regmap_bus,
pll_res, &dsi_pll_28lpm_config);
if (IS_ERR(rmap)) {
pr_err("regmap init failed for DSI clock:%d\n",
pll_res->index);
return -EINVAL;
}
dsi0pll_pclk_src.clkr.regmap = rmap;
dsi0pll_vco_clk.priv = pll_res;
for (i = VCO_CLK_0; i <= PCLK_SRC_0_CLK; i++) {
clk = devm_clk_register(&pdev->dev,
mdss_dsi_pllcc_28lpm[i]);
if (IS_ERR(clk)) {
pr_err("clk registration failed for DSI clock:%d\n",
pll_res->index);
rc = -EINVAL;
goto clk_register_fail;
}
clk_data->clks[i] = clk;
}
rc = of_clk_add_provider(pdev->dev.of_node,
of_clk_src_onecell_get, clk_data);
} else {
rmap = devm_regmap_init(&pdev->dev, &byteclk_src_mux_regmap_bus,
pll_res, &dsi_pll_28lpm_config);
if (IS_ERR(rmap)) {
pr_err("regmap init failed for DSI clock:%d\n",
pll_res->index);
return -EINVAL;
}
dsi1pll_byteclk_src_mux.clkr.regmap = rmap;
rmap = devm_regmap_init(&pdev->dev, &analog_postdiv_regmap_bus,
pll_res, &dsi_pll_28lpm_config);
if (IS_ERR(rmap)) {
pr_err("regmap init failed for DSI clock:%d\n",
pll_res->index);
return -EINVAL;
}
dsi1pll_analog_postdiv.clkr.regmap = rmap;
rmap = devm_regmap_init(&pdev->dev, &pclk_src_regmap_bus,
pll_res, &dsi_pll_28lpm_config);
if (IS_ERR(rmap)) {
pr_err("regmap init failed for DSI clock:%d\n",
pll_res->index);
return -EINVAL;
}
dsi1pll_pclk_src.clkr.regmap = rmap;
dsi1pll_vco_clk.priv = pll_res;
for (i = VCO_CLK_1; i <= PCLK_SRC_1_CLK; i++) {
clk = devm_clk_register(&pdev->dev,
mdss_dsi_pllcc_28lpm[i]);
if (IS_ERR(clk)) {
pr_err("clk registration failed for DSI clock:%d\n",
pll_res->index);
rc = -EINVAL;
goto clk_register_fail;
}
clk_data->clks[i] = clk;
}
rc = of_clk_add_provider(pdev->dev.of_node,
of_clk_src_onecell_get, clk_data);
}
if (!rc) {
pr_info("Registered DSI PLL ndx=%d, clocks successfully\n",
ndx);
return rc;
}
clk_register_fail:
return rc;
}

View File

@@ -1,64 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*/
#ifndef __MDSS_DSI_PLL_28NM_H
#define __MDSS_DSI_PLL_28NM_H
#define DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG (0x0020)
#define DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2 (0x0064)
#define DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG (0x0068)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1 (0x0070)
#define DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG (0x0004)
#define DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG (0x0028)
#define DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG (0x0010)
struct ssc_params {
s32 kdiv;
s64 triang_inc_7_0;
s64 triang_inc_9_8;
s64 triang_steps;
s64 dc_offset;
s64 freq_seed_7_0;
s64 freq_seed_15_8;
};
struct mdss_dsi_vco_calc {
s64 sdm_cfg0;
s64 sdm_cfg1;
s64 sdm_cfg2;
s64 sdm_cfg3;
s64 cal_cfg10;
s64 cal_cfg11;
s64 refclk_cfg;
s64 gen_vco_clk;
u32 lpfr_lut_res;
struct ssc_params ssc;
};
unsigned long vco_28nm_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate);
int vco_28nm_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate);
long vco_28nm_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate);
int vco_28nm_prepare(struct clk_hw *hw);
void vco_28nm_unprepare(struct clk_hw *hw);
int analog_postdiv_reg_write(void *context,
unsigned int reg, unsigned int div);
int analog_postdiv_reg_read(void *context,
unsigned int reg, unsigned int *div);
int byteclk_mux_write_sel(void *context,
unsigned int reg, unsigned int val);
int byteclk_mux_read_sel(void *context,
unsigned int reg, unsigned int *val);
int pixel_clk_set_div(void *context,
unsigned int reg, unsigned int div);
int pixel_clk_get_div(void *context,
unsigned int reg, unsigned int *div);
int dsi_pll_lock_status(struct mdss_pll_resources *rsc);
#endif /* __MDSS_DSI_PLL_28NM_H */

View File

@@ -1,652 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/iopoll.h>
#include <linux/delay.h>
#include "pll_drv.h"
#include "dsi_pll.h"
#include "dsi_pll_28nm.h"
#define DSI_PHY_PLL_UNIPHY_PLL_REFCLK_CFG (0x0)
#define DSI_PHY_PLL_UNIPHY_PLL_CHGPUMP_CFG (0x0008)
#define DSI_PHY_PLL_UNIPHY_PLL_VCOLPF_CFG (0x000C)
#define DSI_PHY_PLL_UNIPHY_PLL_PWRGEN_CFG (0x0014)
#define DSI_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG (0x0024)
#define DSI_PHY_PLL_UNIPHY_PLL_LPFR_CFG (0x002C)
#define DSI_PHY_PLL_UNIPHY_PLL_LPFC1_CFG (0x0030)
#define DSI_PHY_PLL_UNIPHY_PLL_LPFC2_CFG (0x0034)
#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0 (0x0038)
#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1 (0x003C)
#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2 (0x0040)
#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3 (0x0044)
#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG4 (0x0048)
#define DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG0 (0x004C)
#define DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG1 (0x0050)
#define DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG2 (0x0054)
#define DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG3 (0x0058)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG0 (0x006C)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG2 (0x0074)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG3 (0x0078)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG4 (0x007C)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG5 (0x0080)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG6 (0x0084)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG7 (0x0088)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG8 (0x008C)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG9 (0x0090)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG10 (0x0094)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG11 (0x0098)
#define DSI_PHY_PLL_UNIPHY_PLL_EFUSE_CFG (0x009C)
#define DSI_PHY_PLL_UNIPHY_PLL_STATUS (0x00C0)
#define DSI_PLL_POLL_DELAY_US 50
#define DSI_PLL_POLL_TIMEOUT_US 500
int analog_postdiv_reg_read(void *context, unsigned int reg,
unsigned int *div)
{
int rc = 0;
struct mdss_pll_resources *rsc = context;
rc = mdss_pll_resource_enable(rsc, true);
if (rc) {
pr_err("Failed to enable dsi pll resources, rc=%d\n", rc);
return rc;
}
*div = MDSS_PLL_REG_R(rsc->pll_base, reg);
pr_debug("analog_postdiv div = %d\n", *div);
(void)mdss_pll_resource_enable(rsc, false);
return rc;
}
int analog_postdiv_reg_write(void *context, unsigned int reg,
unsigned int div)
{
int rc = 0;
struct mdss_pll_resources *rsc = context;
rc = mdss_pll_resource_enable(rsc, true);
if (rc) {
pr_err("Failed to enable dsi pll resources, rc=%d\n", rc);
return rc;
}
pr_debug("analog_postdiv div = %d\n", div);
MDSS_PLL_REG_W(rsc->pll_base, reg, div);
(void)mdss_pll_resource_enable(rsc, false);
return rc;
}
int byteclk_mux_read_sel(void *context, unsigned int reg,
unsigned int *val)
{
int rc = 0;
struct mdss_pll_resources *rsc = context;
rc = mdss_pll_resource_enable(rsc, true);
if (rc) {
pr_err("Failed to enable dsi pll resources, rc=%d\n", rc);
return rc;
}
*val = (MDSS_PLL_REG_R(rsc->pll_base, reg) & BIT(1));
pr_debug("byteclk mux mode = %s\n", *val ? "indirect" : "direct");
(void)mdss_pll_resource_enable(rsc, false);
return rc;
}
int byteclk_mux_write_sel(void *context, unsigned int reg,
unsigned int val)
{
int rc = 0;
u32 reg_val = 0;
struct mdss_pll_resources *rsc = context;
rc = mdss_pll_resource_enable(rsc, true);
if (rc) {
pr_err("Failed to enable dsi pll resources, rc=%d\n", rc);
return rc;
}
pr_debug("byteclk mux set to %s mode\n", val ? "indirect" : "direct");
reg_val = MDSS_PLL_REG_R(rsc->pll_base, reg);
reg_val &= ~0x02;
reg_val |= val;
MDSS_PLL_REG_W(rsc->pll_base, reg, reg_val);
(void)mdss_pll_resource_enable(rsc, false);
return rc;
}
int pixel_clk_get_div(void *context, unsigned int reg,
unsigned int *div)
{
int rc = 0;
struct mdss_pll_resources *rsc = context;
rc = mdss_pll_resource_enable(rsc, true);
if (rc) {
pr_err("Failed to enable dsi pll resources, rc=%d\n", rc);
return rc;
}
*div = MDSS_PLL_REG_R(rsc->pll_base, reg);
pr_debug("pclk_src div = %d\n", *div);
(void)mdss_pll_resource_enable(rsc, false);
return rc;
}
int pixel_clk_set_div(void *context, unsigned int reg,
unsigned int div)
{
int rc = 0;
struct mdss_pll_resources *rsc = context;
rc = mdss_pll_resource_enable(rsc, true);
if (rc) {
pr_err("Failed to enable dsi pll resources, rc=%d\n", rc);
return rc;
}
pr_debug("pclk_src div = %d\n", div);
MDSS_PLL_REG_W(rsc->pll_base, reg, div);
(void)mdss_pll_resource_enable(rsc, false);
return rc;
}
int dsi_pll_lock_status(struct mdss_pll_resources *rsc)
{
u32 status;
int pll_locked;
/* poll for PLL ready status */
if (readl_poll_timeout_atomic((rsc->pll_base +
DSI_PHY_PLL_UNIPHY_PLL_STATUS),
status,
((status & BIT(0)) == 1),
DSI_PLL_POLL_DELAY_US,
DSI_PLL_POLL_TIMEOUT_US)) {
pr_debug("DSI PLL status=%x failed to Lock\n", status);
pll_locked = 0;
} else {
pll_locked = 1;
}
return pll_locked;
}
static int pll_28nm_vco_rate_calc(struct dsi_pll_vco_clk *vco,
struct mdss_dsi_vco_calc *vco_calc, unsigned long vco_clk_rate)
{
s32 rem;
s64 frac_n_mode, ref_doubler_en_b;
s64 ref_clk_to_pll, div_fb, frac_n_value;
int i;
/* Configure the Loop filter resistance */
for (i = 0; i < vco->lpfr_lut_size; i++)
if (vco_clk_rate <= vco->lpfr_lut[i].vco_rate)
break;
if (i == vco->lpfr_lut_size) {
pr_err("unable to get loop filter resistance. vco=%ld\n",
vco_clk_rate);
return -EINVAL;
}
vco_calc->lpfr_lut_res = vco->lpfr_lut[i].r;
div_s64_rem(vco_clk_rate, vco->ref_clk_rate, &rem);
if (rem) {
vco_calc->refclk_cfg = 0x1;
frac_n_mode = 1;
ref_doubler_en_b = 0;
} else {
vco_calc->refclk_cfg = 0x0;
frac_n_mode = 0;
ref_doubler_en_b = 1;
}
pr_debug("refclk_cfg = %lld\n", vco_calc->refclk_cfg);
ref_clk_to_pll = ((vco->ref_clk_rate * 2 * (vco_calc->refclk_cfg))
+ (ref_doubler_en_b * vco->ref_clk_rate));
div_fb = div_s64_rem(vco_clk_rate, ref_clk_to_pll, &rem);
frac_n_value = div_s64(((s64)rem * (1 << 16)), ref_clk_to_pll);
vco_calc->gen_vco_clk = vco_clk_rate;
pr_debug("ref_clk_to_pll = %lld\n", ref_clk_to_pll);
pr_debug("div_fb = %lld\n", div_fb);
pr_debug("frac_n_value = %lld\n", frac_n_value);
pr_debug("Generated VCO Clock: %lld\n", vco_calc->gen_vco_clk);
rem = 0;
if (frac_n_mode) {
vco_calc->sdm_cfg0 = 0;
vco_calc->sdm_cfg1 = (div_fb & 0x3f) - 1;
vco_calc->sdm_cfg3 = div_s64_rem(frac_n_value, 256, &rem);
vco_calc->sdm_cfg2 = rem;
} else {
vco_calc->sdm_cfg0 = (0x1 << 5);
vco_calc->sdm_cfg0 |= (div_fb & 0x3f) - 1;
vco_calc->sdm_cfg1 = 0;
vco_calc->sdm_cfg2 = 0;
vco_calc->sdm_cfg3 = 0;
}
pr_debug("sdm_cfg0=%lld\n", vco_calc->sdm_cfg0);
pr_debug("sdm_cfg1=%lld\n", vco_calc->sdm_cfg1);
pr_debug("sdm_cfg2=%lld\n", vco_calc->sdm_cfg2);
pr_debug("sdm_cfg3=%lld\n", vco_calc->sdm_cfg3);
vco_calc->cal_cfg11 = div_s64_rem(vco_calc->gen_vco_clk,
256 * 1000000, &rem);
vco_calc->cal_cfg10 = rem / 1000000;
pr_debug("cal_cfg10=%lld, cal_cfg11=%lld\n",
vco_calc->cal_cfg10, vco_calc->cal_cfg11);
return 0;
}
static void pll_28nm_ssc_param_calc(struct dsi_pll_vco_clk *vco,
struct mdss_dsi_vco_calc *vco_calc)
{
struct mdss_pll_resources *rsc = vco->priv;
s64 ppm_freq, incr, spread_freq, div_rf, frac_n_value;
s32 rem;
if (!rsc->ssc_en) {
pr_debug("DSI PLL SSC not enabled\n");
return;
}
vco_calc->ssc.kdiv = DIV_ROUND_CLOSEST(vco->ref_clk_rate,
1000000) - 1;
vco_calc->ssc.triang_steps = DIV_ROUND_CLOSEST(vco->ref_clk_rate,
rsc->ssc_freq * (vco_calc->ssc.kdiv + 1));
ppm_freq = div_s64(vco_calc->gen_vco_clk * rsc->ssc_ppm,
1000000);
incr = div64_s64(ppm_freq * 65536, vco->ref_clk_rate * 2 *
vco_calc->ssc.triang_steps);
vco_calc->ssc.triang_inc_7_0 = incr & 0xff;
vco_calc->ssc.triang_inc_9_8 = (incr >> 8) & 0x3;
if (!rsc->ssc_center)
spread_freq = vco_calc->gen_vco_clk - ppm_freq;
else
spread_freq = vco_calc->gen_vco_clk - (ppm_freq / 2);
div_rf = div_s64(spread_freq, 2 * vco->ref_clk_rate);
vco_calc->ssc.dc_offset = (div_rf - 1);
div_s64_rem(spread_freq, 2 * vco->ref_clk_rate, &rem);
frac_n_value = div_s64((s64)rem * 65536, 2 * vco->ref_clk_rate);
vco_calc->ssc.freq_seed_7_0 = frac_n_value & 0xff;
vco_calc->ssc.freq_seed_15_8 = (frac_n_value >> 8) & 0xff;
}
static void pll_28nm_vco_config(struct dsi_pll_vco_clk *vco,
struct mdss_dsi_vco_calc *vco_calc)
{
struct mdss_pll_resources *rsc = vco->priv;
void __iomem *pll_base = rsc->pll_base;
u32 vco_delay_us = rsc->vco_delay;
bool ssc_en = rsc->ssc_en;
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_LPFR_CFG,
vco_calc->lpfr_lut_res);
/* Loop filter capacitance values : c1 and c2 */
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_LPFC1_CFG, 0x70);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_LPFC2_CFG, 0x15);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CHGPUMP_CFG, 0x02);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG3, 0x2b);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG4, 0x66);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x0d);
if (!ssc_en) {
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1,
(u32)(vco_calc->sdm_cfg1 & 0xff));
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2,
(u32)(vco_calc->sdm_cfg2 & 0xff));
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3,
(u32)(vco_calc->sdm_cfg3 & 0xff));
} else {
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1,
(u32)vco_calc->ssc.dc_offset);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2,
(u32)vco_calc->ssc.freq_seed_7_0);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3,
(u32)vco_calc->ssc.freq_seed_15_8);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG0,
(u32)vco_calc->ssc.kdiv);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG1,
(u32)vco_calc->ssc.triang_inc_7_0);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG2,
(u32)vco_calc->ssc.triang_inc_9_8);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG3,
(u32)vco_calc->ssc.triang_steps);
}
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG4, 0x00);
/* Add hardware recommended delay for correct PLL configuration */
if (vco_delay_us)
udelay(vco_delay_us);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_REFCLK_CFG,
(u32)vco_calc->refclk_cfg);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_PWRGEN_CFG, 0x00);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_VCOLPF_CFG, 0x71);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0,
(u32)vco_calc->sdm_cfg0);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG0, 0x12);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG6, 0x30);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG7, 0x00);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG8, 0x60);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG9, 0x00);
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG10,
(u32)(vco_calc->cal_cfg10 & 0xff));
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG11,
(u32)(vco_calc->cal_cfg11 & 0xff));
MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_EFUSE_CFG, 0x20);
MDSS_PLL_REG_W(pll_base,
DSI_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG, 0x3); /* Fixed div-4 */
}
static int vco_set_rate(struct dsi_pll_vco_clk *vco, unsigned long rate)
{
struct mdss_dsi_vco_calc vco_calc = {0};
int rc = 0;
rc = pll_28nm_vco_rate_calc(vco, &vco_calc, rate);
if (rc) {
pr_err("vco rate calculation failed\n");
return rc;
}
pll_28nm_ssc_param_calc(vco, &vco_calc);
pll_28nm_vco_config(vco, &vco_calc);
return 0;
}
static unsigned long vco_get_rate(struct dsi_pll_vco_clk *vco)
{
struct mdss_pll_resources *rsc = vco->priv;
int rc;
u32 sdm0, doubler, sdm_byp_div;
u64 vco_rate;
u32 sdm_dc_off, sdm_freq_seed, sdm2, sdm3;
u64 ref_clk = vco->ref_clk_rate;
rc = mdss_pll_resource_enable(rsc, true);
if (rc) {
pr_err("Failed to enable mdss dsi pll resources\n");
return rc;
}
/* Check to see if the ref clk doubler is enabled */
doubler = MDSS_PLL_REG_R(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_REFCLK_CFG) & BIT(0);
ref_clk += (doubler * vco->ref_clk_rate);
/* see if it is integer mode or sdm mode */
sdm0 = MDSS_PLL_REG_R(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0);
if (sdm0 & BIT(6)) {
/* integer mode */
sdm_byp_div = (MDSS_PLL_REG_R(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0) & 0x3f) + 1;
vco_rate = ref_clk * sdm_byp_div;
} else {
/* sdm mode */
sdm_dc_off = MDSS_PLL_REG_R(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1) & 0xFF;
pr_debug("sdm_dc_off = %d\n", sdm_dc_off);
sdm2 = MDSS_PLL_REG_R(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2) & 0xFF;
sdm3 = MDSS_PLL_REG_R(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3) & 0xFF;
sdm_freq_seed = (sdm3 << 8) | sdm2;
pr_debug("sdm_freq_seed = %d\n", sdm_freq_seed);
vco_rate = (ref_clk * (sdm_dc_off + 1)) +
mult_frac(ref_clk, sdm_freq_seed, BIT(16));
pr_debug("vco rate = %lld\n", vco_rate);
}
pr_debug("returning vco rate = %lu\n", (unsigned long)vco_rate);
mdss_pll_resource_enable(rsc, false);
return (unsigned long)vco_rate;
}
static int dsi_pll_enable(struct dsi_pll_vco_clk *vco)
{
int i, rc;
struct mdss_pll_resources *rsc = vco->priv;
rc = mdss_pll_resource_enable(rsc, true);
if (rc) {
pr_err("failed to enable dsi pll(%d) resources\n",
rsc->index);
return rc;
}
/* Try all enable sequences until one succeeds */
for (i = 0; i < vco->pll_en_seq_cnt; i++) {
rc = vco->pll_enable_seqs[i](rsc);
pr_debug("DSI PLL %s after sequence #%d\n",
rc ? "unlocked" : "locked", i + 1);
if (!rc)
break;
}
if (rc) {
mdss_pll_resource_enable(rsc, false);
pr_err("DSI PLL failed to lock\n");
}
rsc->pll_on = true;
return rc;
}
static void dsi_pll_disable(struct dsi_pll_vco_clk *vco)
{
struct mdss_pll_resources *rsc = vco->priv;
if (!rsc->pll_on &&
mdss_pll_resource_enable(rsc, true)) {
pr_err("failed to enable dsi pll(%d) resources\n",
rsc->index);
return;
}
rsc->handoff_resources = false;
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x00);
mdss_pll_resource_enable(rsc, false);
rsc->pll_on = false;
pr_debug("DSI PLL Disabled\n");
}
int vco_28nm_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct dsi_pll_vco_clk *vco = to_vco_clk_hw(hw);
struct mdss_pll_resources *rsc = vco->priv;
int rc;
if (!rsc) {
pr_err("pll resource not found\n");
return -EINVAL;
}
if (rsc->pll_on)
return 0;
pr_debug("ndx=%d, rate=%lu\n", rsc->index, rate);
rc = mdss_pll_resource_enable(rsc, true);
if (rc) {
pr_err("failed to enable mdss dsi pll(%d), rc=%d\n",
rsc->index, rc);
return rc;
}
/*
* DSI PLL software reset. Add HW recommended delays after toggling
* the software reset bit off and back on.
*/
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x01);
udelay(1000);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x00);
udelay(1000);
rc = vco_set_rate(vco, rate);
rsc->vco_current_rate = rate;
mdss_pll_resource_enable(rsc, false);
return 0;
}
long vco_28nm_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
unsigned long rrate = rate;
struct dsi_pll_vco_clk *vco = to_vco_clk_hw(hw);
if (rate < vco->min_rate)
rrate = vco->min_rate;
if (rate > vco->max_rate)
rrate = vco->max_rate;
*parent_rate = rrate;
return rrate;
}
unsigned long vco_28nm_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct dsi_pll_vco_clk *vco = to_vco_clk_hw(hw);
struct mdss_pll_resources *rsc = vco->priv;
int rc;
u64 vco_rate = 0;
if (!rsc) {
pr_err("dsi pll resources not available\n");
return 0;
}
if (rsc->vco_current_rate)
return (unsigned long)rsc->vco_current_rate;
if (is_gdsc_disabled(rsc))
return 0;
rc = mdss_pll_resource_enable(rsc, true);
if (rc) {
pr_err("failed to enable dsi pll(%d) resources\n",
rsc->index);
return 0;
}
if (dsi_pll_lock_status(rsc)) {
rsc->handoff_resources = true;
rsc->pll_on = true;
vco_rate = vco_get_rate(vco);
} else {
mdss_pll_resource_enable(rsc, false);
}
return (unsigned long)vco_rate;
}
int vco_28nm_prepare(struct clk_hw *hw)
{
int rc = 0;
struct dsi_pll_vco_clk *vco = to_vco_clk_hw(hw);
struct mdss_pll_resources *rsc = vco->priv;
if (!rsc) {
pr_err("dsi pll resources not available\n");
return -EINVAL;
}
if ((rsc->vco_cached_rate != 0)
&& (rsc->vco_cached_rate == clk_hw_get_rate(hw))) {
rc = hw->init->ops->set_rate(hw, rsc->vco_cached_rate,
rsc->vco_cached_rate);
if (rc) {
pr_err("pll(%d ) set_rate failed. rc=%d\n",
rsc->index, rc);
goto error;
}
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG,
rsc->cached_postdiv1);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG,
rsc->cached_postdiv3);
MDSS_PLL_REG_W(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG,
rsc->cached_vreg_cfg);
}
rc = dsi_pll_enable(vco);
error:
return rc;
}
void vco_28nm_unprepare(struct clk_hw *hw)
{
struct dsi_pll_vco_clk *vco = to_vco_clk_hw(hw);
struct mdss_pll_resources *rsc = vco->priv;
if (!rsc) {
pr_err("dsi pll resources not available\n");
return;
}
rsc->cached_postdiv1 = MDSS_PLL_REG_R(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG);
rsc->cached_postdiv3 = MDSS_PLL_REG_R(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG);
rsc->cached_vreg_cfg = MDSS_PLL_REG_R(rsc->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG);
rsc->vco_cached_rate = clk_hw_get_rate(hw);
dsi_pll_disable(vco);
}

View File

@@ -1,548 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/clk/msm-clk-provider.h>
#include <linux/clk/msm-clk.h>
#include <linux/workqueue.h>
#include <linux/clk/msm-clock-generic.h>
#include <dt-bindings/clock/msm-clocks-8996.h>
#include "pll_drv.h"
#include "dsi_pll.h"
#include "dsi_pll_8996.h"
#define VCO_DELAY_USEC 1
static struct dsi_pll_db pll_db[DSI_PLL_NUM];
static const struct clk_ops n2_clk_src_ops;
static const struct clk_ops shadow_n2_clk_src_ops;
static const struct clk_ops byte_clk_src_ops;
static const struct clk_ops post_n1_div_clk_src_ops;
static const struct clk_ops shadow_post_n1_div_clk_src_ops;
static const struct clk_ops clk_ops_gen_mux_dsi;
/* Op structures */
static const struct clk_ops clk_ops_dsi_vco = {
.set_rate = pll_vco_set_rate_8996,
.round_rate = pll_vco_round_rate_8996,
.handoff = pll_vco_handoff_8996,
.prepare = pll_vco_prepare_8996,
.unprepare = pll_vco_unprepare_8996,
};
static struct clk_div_ops post_n1_div_ops = {
.set_div = post_n1_div_set_div,
.get_div = post_n1_div_get_div,
};
static struct clk_div_ops n2_div_ops = { /* hr_oclk3 */
.set_div = n2_div_set_div,
.get_div = n2_div_get_div,
};
static struct clk_mux_ops mdss_byte_mux_ops = {
.set_mux_sel = set_mdss_byte_mux_sel_8996,
.get_mux_sel = get_mdss_byte_mux_sel_8996,
};
static struct clk_mux_ops mdss_pixel_mux_ops = {
.set_mux_sel = set_mdss_pixel_mux_sel_8996,
.get_mux_sel = get_mdss_pixel_mux_sel_8996,
};
/* Shadow ops for dynamic refresh */
static const struct clk_ops clk_ops_shadow_dsi_vco = {
.set_rate = shadow_pll_vco_set_rate_8996,
.round_rate = pll_vco_round_rate_8996,
.handoff = shadow_pll_vco_handoff_8996,
};
static struct clk_div_ops shadow_post_n1_div_ops = {
.set_div = post_n1_div_set_div,
};
static struct clk_div_ops shadow_n2_div_ops = {
.set_div = shadow_n2_div_set_div,
};
static struct dsi_pll_vco_clk dsi0pll_vco_clk = {
.ref_clk_rate = 19200000UL,
.min_rate = 1300000000UL,
.max_rate = 2600000000UL,
.pll_en_seq_cnt = 1,
.pll_enable_seqs[0] = dsi_pll_enable_seq_8996,
.c = {
.dbg_name = "dsi0pll_vco_clk_8996",
.ops = &clk_ops_dsi_vco,
CLK_INIT(dsi0pll_vco_clk.c),
},
};
static struct dsi_pll_vco_clk dsi0pll_shadow_vco_clk = {
.ref_clk_rate = 19200000u,
.min_rate = 1300000000u,
.max_rate = 2600000000u,
.c = {
.dbg_name = "dsi0pll_shadow_vco_clk",
.ops = &clk_ops_shadow_dsi_vco,
CLK_INIT(dsi0pll_shadow_vco_clk.c),
},
};
static struct dsi_pll_vco_clk dsi1pll_vco_clk = {
.ref_clk_rate = 19200000UL,
.min_rate = 1300000000UL,
.max_rate = 2600000000UL,
.pll_en_seq_cnt = 1,
.pll_enable_seqs[0] = dsi_pll_enable_seq_8996,
.c = {
.dbg_name = "dsi1pll_vco_clk_8996",
.ops = &clk_ops_dsi_vco,
CLK_INIT(dsi1pll_vco_clk.c),
},
};
static struct dsi_pll_vco_clk dsi1pll_shadow_vco_clk = {
.ref_clk_rate = 19200000u,
.min_rate = 1300000000u,
.max_rate = 2600000000u,
.pll_en_seq_cnt = 1,
.pll_enable_seqs[0] = dsi_pll_enable_seq_8996,
.c = {
.dbg_name = "dsi1pll_shadow_vco_clk",
.ops = &clk_ops_shadow_dsi_vco,
CLK_INIT(dsi1pll_shadow_vco_clk.c),
},
};
static struct div_clk dsi0pll_post_n1_div_clk = {
.data = {
.max_div = 15,
.min_div = 1,
},
.ops = &post_n1_div_ops,
.c = {
.parent = &dsi0pll_vco_clk.c,
.dbg_name = "dsi0pll_post_n1_div_clk",
.ops = &post_n1_div_clk_src_ops,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_post_n1_div_clk.c),
},
};
static struct div_clk dsi0pll_shadow_post_n1_div_clk = {
.data = {
.max_div = 15,
.min_div = 1,
},
.ops = &shadow_post_n1_div_ops,
.c = {
.parent = &dsi0pll_shadow_vco_clk.c,
.dbg_name = "dsi0pll_shadow_post_n1_div_clk",
.ops = &shadow_post_n1_div_clk_src_ops,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_shadow_post_n1_div_clk.c),
},
};
static struct div_clk dsi1pll_post_n1_div_clk = {
.data = {
.max_div = 15,
.min_div = 1,
},
.ops = &post_n1_div_ops,
.c = {
.parent = &dsi1pll_vco_clk.c,
.dbg_name = "dsi1pll_post_n1_div_clk",
.ops = &post_n1_div_clk_src_ops,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_post_n1_div_clk.c),
},
};
static struct div_clk dsi1pll_shadow_post_n1_div_clk = {
.data = {
.max_div = 15,
.min_div = 1,
},
.ops = &shadow_post_n1_div_ops,
.c = {
.parent = &dsi1pll_shadow_vco_clk.c,
.dbg_name = "dsi1pll_shadow_post_n1_div_clk",
.ops = &shadow_post_n1_div_clk_src_ops,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_shadow_post_n1_div_clk.c),
},
};
static struct div_clk dsi0pll_n2_div_clk = {
.data = {
.max_div = 15,
.min_div = 1,
},
.ops = &n2_div_ops,
.c = {
.parent = &dsi0pll_post_n1_div_clk.c,
.dbg_name = "dsi0pll_n2_div_clk",
.ops = &n2_clk_src_ops,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_n2_div_clk.c),
},
};
static struct div_clk dsi0pll_shadow_n2_div_clk = {
.data = {
.max_div = 15,
.min_div = 1,
},
.ops = &shadow_n2_div_ops,
.c = {
.parent = &dsi0pll_shadow_post_n1_div_clk.c,
.dbg_name = "dsi0pll_shadow_n2_div_clk",
.ops = &shadow_n2_clk_src_ops,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_shadow_n2_div_clk.c),
},
};
static struct div_clk dsi1pll_n2_div_clk = {
.data = {
.max_div = 15,
.min_div = 1,
},
.ops = &n2_div_ops,
.c = {
.parent = &dsi1pll_post_n1_div_clk.c,
.dbg_name = "dsi1pll_n2_div_clk",
.ops = &n2_clk_src_ops,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_n2_div_clk.c),
},
};
static struct div_clk dsi1pll_shadow_n2_div_clk = {
.data = {
.max_div = 15,
.min_div = 1,
},
.ops = &shadow_n2_div_ops,
.c = {
.parent = &dsi1pll_shadow_post_n1_div_clk.c,
.dbg_name = "dsi1pll_shadow_n2_div_clk",
.ops = &shadow_n2_clk_src_ops,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_shadow_n2_div_clk.c),
},
};
static struct div_clk dsi0pll_pixel_clk_src = {
.data = {
.div = 2,
.min_div = 2,
.max_div = 2,
},
.c = {
.parent = &dsi0pll_n2_div_clk.c,
.dbg_name = "dsi0pll_pixel_clk_src",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_pixel_clk_src.c),
},
};
static struct div_clk dsi0pll_shadow_pixel_clk_src = {
.data = {
.div = 2,
.min_div = 2,
.max_div = 2,
},
.c = {
.parent = &dsi0pll_shadow_n2_div_clk.c,
.dbg_name = "dsi0pll_shadow_pixel_clk_src",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_shadow_pixel_clk_src.c),
},
};
static struct div_clk dsi1pll_pixel_clk_src = {
.data = {
.div = 2,
.min_div = 2,
.max_div = 2,
},
.c = {
.parent = &dsi1pll_n2_div_clk.c,
.dbg_name = "dsi1pll_pixel_clk_src",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_pixel_clk_src.c),
},
};
static struct div_clk dsi1pll_shadow_pixel_clk_src = {
.data = {
.div = 2,
.min_div = 2,
.max_div = 2,
},
.c = {
.parent = &dsi1pll_shadow_n2_div_clk.c,
.dbg_name = "dsi1pll_shadow_pixel_clk_src",
.ops = &clk_ops_div,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_shadow_pixel_clk_src.c),
},
};
static struct mux_clk dsi0pll_pixel_clk_mux = {
.num_parents = 2,
.parents = (struct clk_src[]) {
{&dsi0pll_pixel_clk_src.c, 0},
{&dsi0pll_shadow_pixel_clk_src.c, 1},
},
.ops = &mdss_pixel_mux_ops,
.c = {
.parent = &dsi0pll_pixel_clk_src.c,
.dbg_name = "dsi0pll_pixel_clk_mux",
.ops = &clk_ops_gen_mux_dsi,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_pixel_clk_mux.c),
}
};
static struct mux_clk dsi1pll_pixel_clk_mux = {
.num_parents = 2,
.parents = (struct clk_src[]) {
{&dsi1pll_pixel_clk_src.c, 0},
{&dsi1pll_shadow_pixel_clk_src.c, 1},
},
.ops = &mdss_pixel_mux_ops,
.c = {
.parent = &dsi1pll_pixel_clk_src.c,
.dbg_name = "dsi1pll_pixel_clk_mux",
.ops = &clk_ops_gen_mux_dsi,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_pixel_clk_mux.c),
}
};
static struct div_clk dsi0pll_byte_clk_src = {
.data = {
.div = 8,
.min_div = 8,
.max_div = 8,
},
.c = {
.parent = &dsi0pll_post_n1_div_clk.c,
.dbg_name = "dsi0pll_byte_clk_src",
.ops = &clk_ops_div,
CLK_INIT(dsi0pll_byte_clk_src.c),
},
};
static struct div_clk dsi0pll_shadow_byte_clk_src = {
.data = {
.div = 8,
.min_div = 8,
.max_div = 8,
},
.c = {
.parent = &dsi0pll_shadow_post_n1_div_clk.c,
.dbg_name = "dsi0pll_shadow_byte_clk_src",
.ops = &clk_ops_div,
CLK_INIT(dsi0pll_shadow_byte_clk_src.c),
},
};
static struct div_clk dsi1pll_byte_clk_src = {
.data = {
.div = 8,
.min_div = 8,
.max_div = 8,
},
.c = {
.parent = &dsi1pll_post_n1_div_clk.c,
.dbg_name = "dsi1pll_byte_clk_src",
.ops = &clk_ops_div,
CLK_INIT(dsi1pll_byte_clk_src.c),
},
};
static struct div_clk dsi1pll_shadow_byte_clk_src = {
.data = {
.div = 8,
.min_div = 8,
.max_div = 8,
},
.c = {
.parent = &dsi1pll_shadow_post_n1_div_clk.c,
.dbg_name = "dsi1pll_shadow_byte_clk_src",
.ops = &clk_ops_div,
CLK_INIT(dsi1pll_shadow_byte_clk_src.c),
},
};
static struct mux_clk dsi0pll_byte_clk_mux = {
.num_parents = 2,
.parents = (struct clk_src[]) {
{&dsi0pll_byte_clk_src.c, 0},
{&dsi0pll_shadow_byte_clk_src.c, 1},
},
.ops = &mdss_byte_mux_ops,
.c = {
.parent = &dsi0pll_byte_clk_src.c,
.dbg_name = "dsi0pll_byte_clk_mux",
.ops = &clk_ops_gen_mux_dsi,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi0pll_byte_clk_mux.c),
}
};
static struct mux_clk dsi1pll_byte_clk_mux = {
.num_parents = 2,
.parents = (struct clk_src[]) {
{&dsi1pll_byte_clk_src.c, 0},
{&dsi1pll_shadow_byte_clk_src.c, 1},
},
.ops = &mdss_byte_mux_ops,
.c = {
.parent = &dsi1pll_byte_clk_src.c,
.dbg_name = "dsi1pll_byte_clk_mux",
.ops = &clk_ops_gen_mux_dsi,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(dsi1pll_byte_clk_mux.c),
}
};
static struct clk_lookup mdss_dsi_pllcc_8996[] = {
CLK_LIST(dsi0pll_byte_clk_mux),
CLK_LIST(dsi0pll_byte_clk_src),
CLK_LIST(dsi0pll_pixel_clk_mux),
CLK_LIST(dsi0pll_pixel_clk_src),
CLK_LIST(dsi0pll_n2_div_clk),
CLK_LIST(dsi0pll_post_n1_div_clk),
CLK_LIST(dsi0pll_vco_clk),
CLK_LIST(dsi0pll_shadow_byte_clk_src),
CLK_LIST(dsi0pll_shadow_pixel_clk_src),
CLK_LIST(dsi0pll_shadow_n2_div_clk),
CLK_LIST(dsi0pll_shadow_post_n1_div_clk),
CLK_LIST(dsi0pll_shadow_vco_clk),
};
static struct clk_lookup mdss_dsi_pllcc_8996_1[] = {
CLK_LIST(dsi1pll_byte_clk_mux),
CLK_LIST(dsi1pll_byte_clk_src),
CLK_LIST(dsi1pll_pixel_clk_mux),
CLK_LIST(dsi1pll_pixel_clk_src),
CLK_LIST(dsi1pll_n2_div_clk),
CLK_LIST(dsi1pll_post_n1_div_clk),
CLK_LIST(dsi1pll_vco_clk),
CLK_LIST(dsi1pll_shadow_byte_clk_src),
CLK_LIST(dsi1pll_shadow_pixel_clk_src),
CLK_LIST(dsi1pll_shadow_n2_div_clk),
CLK_LIST(dsi1pll_shadow_post_n1_div_clk),
CLK_LIST(dsi1pll_shadow_vco_clk),
};
int dsi_pll_clock_register_8996(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc = 0, ndx;
int const ssc_freq_default = 31500; /* default h/w recommended value */
int const ssc_ppm_default = 5000; /* default h/w recommended value */
struct dsi_pll_db *pdb;
if (pll_res->index >= DSI_PLL_NUM) {
pr_err("pll ndx=%d is NOT supported\n", pll_res->index);
return -EINVAL;
}
ndx = pll_res->index;
pdb = &pll_db[ndx];
pll_res->priv = pdb;
pdb->pll = pll_res;
ndx++;
ndx %= DSI_PLL_NUM;
pdb->next = &pll_db[ndx];
/* Set clock source operations */
/* hr_oclk3, pixel */
n2_clk_src_ops = clk_ops_slave_div;
n2_clk_src_ops.prepare = mdss_pll_div_prepare;
shadow_n2_clk_src_ops = clk_ops_slave_div;
/* hr_ockl2, byte, vco pll */
post_n1_div_clk_src_ops = clk_ops_div;
post_n1_div_clk_src_ops.prepare = mdss_pll_div_prepare;
shadow_post_n1_div_clk_src_ops = clk_ops_div;
byte_clk_src_ops = clk_ops_div;
byte_clk_src_ops.prepare = mdss_pll_div_prepare;
clk_ops_gen_mux_dsi = clk_ops_gen_mux;
clk_ops_gen_mux_dsi.round_rate = parent_round_rate;
clk_ops_gen_mux_dsi.set_rate = parent_set_rate;
if (pll_res->ssc_en) {
if (!pll_res->ssc_freq)
pll_res->ssc_freq = ssc_freq_default;
if (!pll_res->ssc_ppm)
pll_res->ssc_ppm = ssc_ppm_default;
}
/* Set client data to mux, div and vco clocks. */
if (pll_res->index == DSI_PLL_1) {
dsi1pll_byte_clk_src.priv = pll_res;
dsi1pll_pixel_clk_src.priv = pll_res;
dsi1pll_post_n1_div_clk.priv = pll_res;
dsi1pll_n2_div_clk.priv = pll_res;
dsi1pll_vco_clk.priv = pll_res;
dsi1pll_shadow_byte_clk_src.priv = pll_res;
dsi1pll_shadow_pixel_clk_src.priv = pll_res;
dsi1pll_shadow_post_n1_div_clk.priv = pll_res;
dsi1pll_shadow_n2_div_clk.priv = pll_res;
dsi1pll_shadow_vco_clk.priv = pll_res;
pll_res->vco_delay = VCO_DELAY_USEC;
rc = of_msm_clock_register(pdev->dev.of_node,
mdss_dsi_pllcc_8996_1,
ARRAY_SIZE(mdss_dsi_pllcc_8996_1));
} else {
dsi0pll_byte_clk_src.priv = pll_res;
dsi0pll_pixel_clk_src.priv = pll_res;
dsi0pll_post_n1_div_clk.priv = pll_res;
dsi0pll_n2_div_clk.priv = pll_res;
dsi0pll_vco_clk.priv = pll_res;
dsi0pll_shadow_byte_clk_src.priv = pll_res;
dsi0pll_shadow_pixel_clk_src.priv = pll_res;
dsi0pll_shadow_post_n1_div_clk.priv = pll_res;
dsi0pll_shadow_n2_div_clk.priv = pll_res;
dsi0pll_shadow_vco_clk.priv = pll_res;
pll_res->vco_delay = VCO_DELAY_USEC;
rc = of_msm_clock_register(pdev->dev.of_node,
mdss_dsi_pllcc_8996,
ARRAY_SIZE(mdss_dsi_pllcc_8996));
}
if (!rc) {
pr_info("Registered DSI PLL ndx=%d clocks successfully\n",
pll_res->index);
}
return rc;
}

View File

@@ -1,214 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
*/
#ifndef MDSS_DSI_PLL_8996_H
#define MDSS_DSI_PLL_8996_H
#define DSIPHY_CMN_CLK_CFG0 0x0010
#define DSIPHY_CMN_CLK_CFG1 0x0014
#define DSIPHY_CMN_GLBL_TEST_CTRL 0x0018
#define DSIPHY_CMN_PLL_CNTRL 0x0048
#define DSIPHY_CMN_CTRL_0 0x001c
#define DSIPHY_CMN_CTRL_1 0x0020
#define DSIPHY_CMN_LDO_CNTRL 0x004c
#define DSIPHY_PLL_IE_TRIM 0x0400
#define DSIPHY_PLL_IP_TRIM 0x0404
#define DSIPHY_PLL_IPTAT_TRIM 0x0410
#define DSIPHY_PLL_CLKBUFLR_EN 0x041c
#define DSIPHY_PLL_SYSCLK_EN_RESET 0x0428
#define DSIPHY_PLL_RESETSM_CNTRL 0x042c
#define DSIPHY_PLL_RESETSM_CNTRL2 0x0430
#define DSIPHY_PLL_RESETSM_CNTRL3 0x0434
#define DSIPHY_PLL_RESETSM_CNTRL4 0x0438
#define DSIPHY_PLL_RESETSM_CNTRL5 0x043c
#define DSIPHY_PLL_KVCO_DIV_REF1 0x0440
#define DSIPHY_PLL_KVCO_DIV_REF2 0x0444
#define DSIPHY_PLL_KVCO_COUNT1 0x0448
#define DSIPHY_PLL_KVCO_COUNT2 0x044c
#define DSIPHY_PLL_VREF_CFG1 0x045c
#define DSIPHY_PLL_KVCO_CODE 0x0458
#define DSIPHY_PLL_VCO_DIV_REF1 0x046c
#define DSIPHY_PLL_VCO_DIV_REF2 0x0470
#define DSIPHY_PLL_VCO_COUNT1 0x0474
#define DSIPHY_PLL_VCO_COUNT2 0x0478
#define DSIPHY_PLL_PLLLOCK_CMP1 0x047c
#define DSIPHY_PLL_PLLLOCK_CMP2 0x0480
#define DSIPHY_PLL_PLLLOCK_CMP3 0x0484
#define DSIPHY_PLL_PLLLOCK_CMP_EN 0x0488
#define DSIPHY_PLL_PLL_VCO_TUNE 0x048C
#define DSIPHY_PLL_DEC_START 0x0490
#define DSIPHY_PLL_SSC_EN_CENTER 0x0494
#define DSIPHY_PLL_SSC_ADJ_PER1 0x0498
#define DSIPHY_PLL_SSC_ADJ_PER2 0x049c
#define DSIPHY_PLL_SSC_PER1 0x04a0
#define DSIPHY_PLL_SSC_PER2 0x04a4
#define DSIPHY_PLL_SSC_STEP_SIZE1 0x04a8
#define DSIPHY_PLL_SSC_STEP_SIZE2 0x04ac
#define DSIPHY_PLL_DIV_FRAC_START1 0x04b4
#define DSIPHY_PLL_DIV_FRAC_START2 0x04b8
#define DSIPHY_PLL_DIV_FRAC_START3 0x04bc
#define DSIPHY_PLL_TXCLK_EN 0x04c0
#define DSIPHY_PLL_PLL_CRCTRL 0x04c4
#define DSIPHY_PLL_RESET_SM_READY_STATUS 0x04cc
#define DSIPHY_PLL_PLL_MISC1 0x04e8
#define DSIPHY_PLL_CP_SET_CUR 0x04f0
#define DSIPHY_PLL_PLL_ICPMSET 0x04f4
#define DSIPHY_PLL_PLL_ICPCSET 0x04f8
#define DSIPHY_PLL_PLL_ICP_SET 0x04fc
#define DSIPHY_PLL_PLL_LPF1 0x0500
#define DSIPHY_PLL_PLL_LPF2_POSTDIV 0x0504
#define DSIPHY_PLL_PLL_BANDGAP 0x0508
#define DSI_DYNAMIC_REFRESH_PLL_CTRL15 0x050
#define DSI_DYNAMIC_REFRESH_PLL_CTRL19 0x060
#define DSI_DYNAMIC_REFRESH_PLL_CTRL20 0x064
#define DSI_DYNAMIC_REFRESH_PLL_CTRL21 0x068
#define DSI_DYNAMIC_REFRESH_PLL_CTRL22 0x06C
#define DSI_DYNAMIC_REFRESH_PLL_CTRL23 0x070
#define DSI_DYNAMIC_REFRESH_PLL_CTRL24 0x074
#define DSI_DYNAMIC_REFRESH_PLL_CTRL25 0x078
#define DSI_DYNAMIC_REFRESH_PLL_CTRL26 0x07C
#define DSI_DYNAMIC_REFRESH_PLL_CTRL27 0x080
#define DSI_DYNAMIC_REFRESH_PLL_CTRL28 0x084
#define DSI_DYNAMIC_REFRESH_PLL_CTRL29 0x088
#define DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR 0x094
#define DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR2 0x098
struct dsi_pll_input {
u32 fref; /* 19.2 Mhz, reference clk */
u32 fdata; /* bit clock rate */
u32 dsiclk_sel; /* 1, reg: 0x0014 */
u32 n2div; /* 1, reg: 0x0010, bit 4-7 */
u32 ssc_en; /* 1, reg: 0x0494, bit 0 */
u32 ldo_en; /* 0, reg: 0x004c, bit 0 */
/* fixed */
u32 refclk_dbler_en; /* 0, reg: 0x04c0, bit 1 */
u32 vco_measure_time; /* 5, unknown */
u32 kvco_measure_time; /* 5, unknown */
u32 bandgap_timer; /* 4, reg: 0x0430, bit 3 - 5 */
u32 pll_wakeup_timer; /* 5, reg: 0x043c, bit 0 - 2 */
u32 plllock_cnt; /* 1, reg: 0x0488, bit 1 - 2 */
u32 plllock_rng; /* 1, reg: 0x0488, bit 3 - 4 */
u32 ssc_center; /* 0, reg: 0x0494, bit 1 */
u32 ssc_adj_period; /* 37, reg: 0x498, bit 0 - 9 */
u32 ssc_spread; /* 0.005 */
u32 ssc_freq; /* unknown */
u32 pll_ie_trim; /* 4, reg: 0x0400 */
u32 pll_ip_trim; /* 4, reg: 0x0404 */
u32 pll_iptat_trim; /* reg: 0x0410 */
u32 pll_cpcset_cur; /* 1, reg: 0x04f0, bit 0 - 2 */
u32 pll_cpmset_cur; /* 1, reg: 0x04f0, bit 3 - 5 */
u32 pll_icpmset; /* 4, reg: 0x04fc, bit 3 - 5 */
u32 pll_icpcset; /* 4, reg: 0x04fc, bit 0 - 2 */
u32 pll_icpmset_p; /* 0, reg: 0x04f4, bit 0 - 2 */
u32 pll_icpmset_m; /* 0, reg: 0x04f4, bit 3 - 5 */
u32 pll_icpcset_p; /* 0, reg: 0x04f8, bit 0 - 2 */
u32 pll_icpcset_m; /* 0, reg: 0x04f8, bit 3 - 5 */
u32 pll_lpf_res1; /* 3, reg: 0x0504, bit 0 - 3 */
u32 pll_lpf_cap1; /* 11, reg: 0x0500, bit 0 - 3 */
u32 pll_lpf_cap2; /* 1, reg: 0x0500, bit 4 - 7 */
u32 pll_c3ctrl; /* 2, reg: 0x04c4 */
u32 pll_r3ctrl; /* 1, reg: 0x04c4 */
};
struct dsi_pll_output {
u32 pll_txclk_en; /* reg: 0x04c0 */
u32 dec_start; /* reg: 0x0490 */
u32 div_frac_start; /* reg: 0x04b4, 0x4b8, 0x04bc */
u32 ssc_period; /* reg: 0x04a0, 0x04a4 */
u32 ssc_step_size; /* reg: 0x04a8, 0x04ac */
u32 plllock_cmp; /* reg: 0x047c, 0x0480, 0x0484 */
u32 pll_vco_div_ref; /* reg: 0x046c, 0x0470 */
u32 pll_vco_count; /* reg: 0x0474, 0x0478 */
u32 pll_kvco_div_ref; /* reg: 0x0440, 0x0444 */
u32 pll_kvco_count; /* reg: 0x0448, 0x044c */
u32 pll_misc1; /* reg: 0x04e8 */
u32 pll_lpf2_postdiv; /* reg: 0x0504 */
u32 pll_resetsm_cntrl; /* reg: 0x042c */
u32 pll_resetsm_cntrl2; /* reg: 0x0430 */
u32 pll_resetsm_cntrl5; /* reg: 0x043c */
u32 pll_kvco_code; /* reg: 0x0458 */
u32 cmn_clk_cfg0; /* reg: 0x0010 */
u32 cmn_clk_cfg1; /* reg: 0x0014 */
u32 cmn_ldo_cntrl; /* reg: 0x004c */
u32 pll_postdiv; /* vco */
u32 pll_n1div; /* vco */
u32 pll_n2div; /* hr_oclk3, pixel */
u32 fcvo;
};
enum {
DSI_PLL_0,
DSI_PLL_1,
DSI_PLL_NUM
};
struct dsi_pll_db {
struct dsi_pll_db *next;
struct mdss_pll_resources *pll;
struct dsi_pll_input in;
struct dsi_pll_output out;
int source_setup_done;
};
enum {
PLL_OUTPUT_NONE,
PLL_OUTPUT_RIGHT,
PLL_OUTPUT_LEFT,
PLL_OUTPUT_BOTH
};
enum {
PLL_SOURCE_FROM_LEFT,
PLL_SOURCE_FROM_RIGHT
};
enum {
PLL_UNKNOWN,
PLL_STANDALONE,
PLL_SLAVE,
PLL_MASTER
};
int pll_vco_set_rate_8996(struct clk *c, unsigned long rate);
long pll_vco_round_rate_8996(struct clk *c, unsigned long rate);
enum handoff pll_vco_handoff_8996(struct clk *c);
enum handoff shadow_pll_vco_handoff_8996(struct clk *c);
int shadow_post_n1_div_set_div(struct div_clk *clk, int div);
int shadow_post_n1_div_get_div(struct div_clk *clk);
int shadow_n2_div_set_div(struct div_clk *clk, int div);
int shadow_n2_div_get_div(struct div_clk *clk);
int shadow_pll_vco_set_rate_8996(struct clk *c, unsigned long rate);
int pll_vco_prepare_8996(struct clk *c);
void pll_vco_unprepare_8996(struct clk *c);
int set_mdss_byte_mux_sel_8996(struct mux_clk *clk, int sel);
int get_mdss_byte_mux_sel_8996(struct mux_clk *clk);
int set_mdss_pixel_mux_sel_8996(struct mux_clk *clk, int sel);
int get_mdss_pixel_mux_sel_8996(struct mux_clk *clk);
int post_n1_div_set_div(struct div_clk *clk, int div);
int post_n1_div_get_div(struct div_clk *clk);
int n2_div_set_div(struct div_clk *clk, int div);
int n2_div_get_div(struct div_clk *clk);
int dsi_pll_enable_seq_8996(struct mdss_pll_resources *pll);
#endif /* MDSS_DSI_PLL_8996_H */

File diff suppressed because it is too large Load Diff

View File

@@ -1,580 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/iopoll.h>
#include <linux/delay.h>
#include <linux/clk/msm-clock-generic.h>
#include "pll_drv.h"
#include "dsi_pll.h"
#define DSI_PHY_PLL_UNIPHY_PLL_REFCLK_CFG (0x0)
#define DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG (0x0004)
#define DSI_PHY_PLL_UNIPHY_PLL_CHGPUMP_CFG (0x0008)
#define DSI_PHY_PLL_UNIPHY_PLL_VCOLPF_CFG (0x000C)
#define DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG (0x0010)
#define DSI_PHY_PLL_UNIPHY_PLL_PWRGEN_CFG (0x0014)
#define DSI_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG (0x0024)
#define DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG (0x0028)
#define DSI_PHY_PLL_UNIPHY_PLL_LPFR_CFG (0x002C)
#define DSI_PHY_PLL_UNIPHY_PLL_LPFC1_CFG (0x0030)
#define DSI_PHY_PLL_UNIPHY_PLL_LPFC2_CFG (0x0034)
#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0 (0x0038)
#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1 (0x003C)
#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2 (0x0040)
#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3 (0x0044)
#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG4 (0x0048)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG0 (0x006C)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG2 (0x0074)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG3 (0x0078)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG4 (0x007C)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG5 (0x0080)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG6 (0x0084)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG7 (0x0088)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG8 (0x008C)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG9 (0x0090)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG10 (0x0094)
#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG11 (0x0098)
#define DSI_PHY_PLL_UNIPHY_PLL_EFUSE_CFG (0x009C)
#define DSI_PHY_PLL_UNIPHY_PLL_STATUS (0x00C0)
#define DSI_PLL_POLL_DELAY_US 50
#define DSI_PLL_POLL_TIMEOUT_US 500
int set_byte_mux_sel(struct mux_clk *clk, int sel)
{
struct mdss_pll_resources *dsi_pll_res = clk->priv;
pr_debug("byte mux set to %s mode\n", sel ? "indirect" : "direct");
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG, (sel << 1));
return 0;
}
int get_byte_mux_sel(struct mux_clk *clk)
{
int mux_mode, rc;
struct mdss_pll_resources *dsi_pll_res = clk->priv;
if (is_gdsc_disabled(dsi_pll_res))
return 0;
rc = mdss_pll_resource_enable(dsi_pll_res, true);
if (rc) {
pr_err("Failed to enable mdss dsi pll resources\n");
return rc;
}
mux_mode = MDSS_PLL_REG_R(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG) & BIT(1);
pr_debug("byte mux mode = %s\n", mux_mode ? "indirect" : "direct");
mdss_pll_resource_enable(dsi_pll_res, false);
return !!mux_mode;
}
int dsi_pll_div_prepare(struct clk *c)
{
struct div_clk *div = to_div_clk(c);
/* Restore the divider's value */
return div->ops->set_div(div, div->data.div);
}
int dsi_pll_mux_prepare(struct clk *c)
{
struct mux_clk *mux = to_mux_clk(c);
int i, rc, sel = 0;
struct mdss_pll_resources *dsi_pll_res = mux->priv;
rc = mdss_pll_resource_enable(dsi_pll_res, true);
if (rc) {
pr_err("Failed to enable mdss dsi pll resources\n");
return rc;
}
for (i = 0; i < mux->num_parents; i++)
if (mux->parents[i].src == c->parent) {
sel = mux->parents[i].sel;
break;
}
if (i == mux->num_parents) {
pr_err("Failed to select the parent clock\n");
rc = -EINVAL;
goto error;
}
/* Restore the mux source select value */
rc = mux->ops->set_mux_sel(mux, sel);
error:
mdss_pll_resource_enable(dsi_pll_res, false);
return rc;
}
int fixed_4div_set_div(struct div_clk *clk, int div)
{
int rc;
struct mdss_pll_resources *dsi_pll_res = clk->priv;
rc = mdss_pll_resource_enable(dsi_pll_res, true);
if (rc) {
pr_err("Failed to enable mdss dsi pll resources\n");
return rc;
}
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG, (div - 1));
mdss_pll_resource_enable(dsi_pll_res, false);
return rc;
}
int fixed_4div_get_div(struct div_clk *clk)
{
int div = 0, rc;
struct mdss_pll_resources *dsi_pll_res = clk->priv;
if (is_gdsc_disabled(dsi_pll_res))
return 0;
rc = mdss_pll_resource_enable(dsi_pll_res, true);
if (rc) {
pr_err("Failed to enable mdss dsi pll resources\n");
return rc;
}
div = MDSS_PLL_REG_R(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG);
mdss_pll_resource_enable(dsi_pll_res, false);
return div + 1;
}
int digital_set_div(struct div_clk *clk, int div)
{
int rc;
struct mdss_pll_resources *dsi_pll_res = clk->priv;
rc = mdss_pll_resource_enable(dsi_pll_res, true);
if (rc) {
pr_err("Failed to enable mdss dsi pll resources\n");
return rc;
}
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG, (div - 1));
mdss_pll_resource_enable(dsi_pll_res, false);
return rc;
}
int digital_get_div(struct div_clk *clk)
{
int div = 0, rc;
struct mdss_pll_resources *dsi_pll_res = clk->priv;
if (is_gdsc_disabled(dsi_pll_res))
return 0;
rc = mdss_pll_resource_enable(dsi_pll_res, true);
if (rc) {
pr_err("Failed to enable mdss dsi pll resources\n");
return rc;
}
div = MDSS_PLL_REG_R(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG);
mdss_pll_resource_enable(dsi_pll_res, false);
return div + 1;
}
int analog_set_div(struct div_clk *clk, int div)
{
int rc;
struct mdss_pll_resources *dsi_pll_res = clk->priv;
rc = mdss_pll_resource_enable(dsi_pll_res, true);
if (rc) {
pr_err("Failed to enable mdss dsi pll resources\n");
return rc;
}
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG, div - 1);
mdss_pll_resource_enable(dsi_pll_res, false);
return rc;
}
int analog_get_div(struct div_clk *clk)
{
int div = 0, rc;
struct mdss_pll_resources *dsi_pll_res = clk->priv;
if (is_gdsc_disabled(dsi_pll_res))
return 0;
rc = mdss_pll_resource_enable(clk->priv, true);
if (rc) {
pr_err("Failed to enable mdss dsi pll resources\n");
return rc;
}
div = MDSS_PLL_REG_R(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG) + 1;
mdss_pll_resource_enable(dsi_pll_res, false);
return div;
}
int dsi_pll_lock_status(struct mdss_pll_resources *dsi_pll_res)
{
u32 status;
int pll_locked;
/* poll for PLL ready status */
if (readl_poll_timeout_atomic((dsi_pll_res->pll_base +
DSI_PHY_PLL_UNIPHY_PLL_STATUS),
status,
((status & BIT(0)) == 1),
DSI_PLL_POLL_DELAY_US,
DSI_PLL_POLL_TIMEOUT_US)) {
pr_debug("DSI PLL status=%x failed to Lock\n", status);
pll_locked = 0;
} else {
pll_locked = 1;
}
return pll_locked;
}
int vco_set_rate(struct dsi_pll_vco_clk *vco, unsigned long rate)
{
s64 vco_clk_rate = rate;
s32 rem;
s64 refclk_cfg, frac_n_mode, ref_doubler_en_b;
s64 ref_clk_to_pll, div_fbx1000, frac_n_value;
s64 sdm_cfg0, sdm_cfg1, sdm_cfg2, sdm_cfg3;
s64 gen_vco_clk, cal_cfg10, cal_cfg11;
u32 res;
int i;
struct mdss_pll_resources *dsi_pll_res = vco->priv;
/* Configure the Loop filter resistance */
for (i = 0; i < vco->lpfr_lut_size; i++)
if (vco_clk_rate <= vco->lpfr_lut[i].vco_rate)
break;
if (i == vco->lpfr_lut_size) {
pr_err("unable to get loop filter resistance. vco=%ld\n", rate);
return -EINVAL;
}
res = vco->lpfr_lut[i].r;
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_LPFR_CFG, res);
/* Loop filter capacitance values : c1 and c2 */
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_LPFC1_CFG, 0x70);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_LPFC2_CFG, 0x15);
div_s64_rem(vco_clk_rate, vco->ref_clk_rate, &rem);
if (rem) {
refclk_cfg = 0x1;
frac_n_mode = 1;
ref_doubler_en_b = 0;
} else {
refclk_cfg = 0x0;
frac_n_mode = 0;
ref_doubler_en_b = 1;
}
pr_debug("refclk_cfg = %lld\n", refclk_cfg);
ref_clk_to_pll = ((vco->ref_clk_rate * 2 * (refclk_cfg))
+ (ref_doubler_en_b * vco->ref_clk_rate));
div_fbx1000 = div_s64((vco_clk_rate * 1000), ref_clk_to_pll);
div_s64_rem(div_fbx1000, 1000, &rem);
frac_n_value = div_s64((rem * (1 << 16)), 1000);
gen_vco_clk = div_s64(div_fbx1000 * ref_clk_to_pll, 1000);
pr_debug("ref_clk_to_pll = %lld\n", ref_clk_to_pll);
pr_debug("div_fb = %lld\n", div_fbx1000);
pr_debug("frac_n_value = %lld\n", frac_n_value);
pr_debug("Generated VCO Clock: %lld\n", gen_vco_clk);
rem = 0;
if (frac_n_mode) {
sdm_cfg0 = (0x0 << 5);
sdm_cfg0 |= (0x0 & 0x3f);
sdm_cfg1 = (div_s64(div_fbx1000, 1000) & 0x3f) - 1;
sdm_cfg3 = div_s64_rem(frac_n_value, 256, &rem);
sdm_cfg2 = rem;
} else {
sdm_cfg0 = (0x1 << 5);
sdm_cfg0 |= (div_s64(div_fbx1000, 1000) & 0x3f) - 1;
sdm_cfg1 = (0x0 & 0x3f);
sdm_cfg2 = 0;
sdm_cfg3 = 0;
}
pr_debug("sdm_cfg0=%lld\n", sdm_cfg0);
pr_debug("sdm_cfg1=%lld\n", sdm_cfg1);
pr_debug("sdm_cfg2=%lld\n", sdm_cfg2);
pr_debug("sdm_cfg3=%lld\n", sdm_cfg3);
cal_cfg11 = div_s64_rem(gen_vco_clk, 256 * 1000000, &rem);
cal_cfg10 = rem / 1000000;
pr_debug("cal_cfg10=%lld, cal_cfg11=%lld\n", cal_cfg10, cal_cfg11);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_CHGPUMP_CFG, 0x02);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG3, 0x2b);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG4, 0x66);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x0d);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1, (u32)(sdm_cfg1 & 0xff));
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2, (u32)(sdm_cfg2 & 0xff));
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3, (u32)(sdm_cfg3 & 0xff));
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG4, 0x00);
/* Add hardware recommended delay for correct PLL configuration */
if (dsi_pll_res->vco_delay)
udelay(dsi_pll_res->vco_delay);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_REFCLK_CFG, (u32)refclk_cfg);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_PWRGEN_CFG, 0x00);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_VCOLPF_CFG, 0x71);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0, (u32)sdm_cfg0);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG0, 0x12);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG6, 0x30);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG7, 0x00);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG8, 0x60);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG9, 0x00);
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG10, (u32)(cal_cfg10 & 0xff));
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG11, (u32)(cal_cfg11 & 0xff));
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_EFUSE_CFG, 0x20);
return 0;
}
unsigned long vco_get_rate(struct clk *c)
{
u32 sdm0, doubler, sdm_byp_div;
u64 vco_rate;
u32 sdm_dc_off, sdm_freq_seed, sdm2, sdm3;
struct dsi_pll_vco_clk *vco = to_vco_clk(c);
u64 ref_clk = vco->ref_clk_rate;
int rc;
struct mdss_pll_resources *dsi_pll_res = vco->priv;
if (is_gdsc_disabled(dsi_pll_res))
return 0;
rc = mdss_pll_resource_enable(dsi_pll_res, true);
if (rc) {
pr_err("Failed to enable mdss dsi pll resources\n");
return rc;
}
/* Check to see if the ref clk doubler is enabled */
doubler = MDSS_PLL_REG_R(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_REFCLK_CFG) & BIT(0);
ref_clk += (doubler * vco->ref_clk_rate);
/* see if it is integer mode or sdm mode */
sdm0 = MDSS_PLL_REG_R(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0);
if (sdm0 & BIT(6)) {
/* integer mode */
sdm_byp_div = (MDSS_PLL_REG_R(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0) & 0x3f) + 1;
vco_rate = ref_clk * sdm_byp_div;
} else {
/* sdm mode */
sdm_dc_off = MDSS_PLL_REG_R(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1) & 0xFF;
pr_debug("sdm_dc_off = %d\n", sdm_dc_off);
sdm2 = MDSS_PLL_REG_R(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2) & 0xFF;
sdm3 = MDSS_PLL_REG_R(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3) & 0xFF;
sdm_freq_seed = (sdm3 << 8) | sdm2;
pr_debug("sdm_freq_seed = %d\n", sdm_freq_seed);
vco_rate = (ref_clk * (sdm_dc_off + 1)) +
mult_frac(ref_clk, sdm_freq_seed, BIT(16));
pr_debug("vco rate = %lld\n", vco_rate);
}
pr_debug("returning vco rate = %lu\n", (unsigned long)vco_rate);
mdss_pll_resource_enable(dsi_pll_res, false);
return (unsigned long)vco_rate;
}
static int dsi_pll_enable(struct clk *c)
{
int i, rc;
struct dsi_pll_vco_clk *vco = to_vco_clk(c);
struct mdss_pll_resources *dsi_pll_res = vco->priv;
rc = mdss_pll_resource_enable(dsi_pll_res, true);
if (rc) {
pr_err("Failed to enable mdss dsi pll resources\n");
return rc;
}
/* Try all enable sequences until one succeeds */
for (i = 0; i < vco->pll_en_seq_cnt; i++) {
rc = vco->pll_enable_seqs[i](dsi_pll_res);
pr_debug("DSI PLL %s after sequence #%d\n",
rc ? "unlocked" : "locked", i + 1);
if (!rc)
break;
}
if (rc) {
mdss_pll_resource_enable(dsi_pll_res, false);
pr_err("DSI PLL failed to lock\n");
}
dsi_pll_res->pll_on = true;
return rc;
}
static void dsi_pll_disable(struct clk *c)
{
struct dsi_pll_vco_clk *vco = to_vco_clk(c);
struct mdss_pll_resources *dsi_pll_res = vco->priv;
if (!dsi_pll_res->pll_on &&
mdss_pll_resource_enable(dsi_pll_res, true)) {
pr_err("Failed to enable mdss dsi pll resources\n");
return;
}
dsi_pll_res->handoff_resources = false;
MDSS_PLL_REG_W(dsi_pll_res->pll_base,
DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x00);
mdss_pll_resource_enable(dsi_pll_res, false);
dsi_pll_res->pll_on = false;
pr_debug("DSI PLL Disabled\n");
}
long vco_round_rate(struct clk *c, unsigned long rate)
{
unsigned long rrate = rate;
struct dsi_pll_vco_clk *vco = to_vco_clk(c);
if (rate < vco->min_rate)
rrate = vco->min_rate;
if (rate > vco->max_rate)
rrate = vco->max_rate;
return rrate;
}
enum handoff vco_handoff(struct clk *c)
{
int rc;
enum handoff ret = HANDOFF_DISABLED_CLK;
struct dsi_pll_vco_clk *vco = to_vco_clk(c);
struct mdss_pll_resources *dsi_pll_res = vco->priv;
if (is_gdsc_disabled(dsi_pll_res))
return HANDOFF_DISABLED_CLK;
rc = mdss_pll_resource_enable(dsi_pll_res, true);
if (rc) {
pr_err("Failed to enable mdss dsi pll resources\n");
return ret;
}
if (dsi_pll_lock_status(dsi_pll_res)) {
dsi_pll_res->handoff_resources = true;
dsi_pll_res->pll_on = true;
c->rate = vco_get_rate(c);
ret = HANDOFF_ENABLED_CLK;
} else {
mdss_pll_resource_enable(dsi_pll_res, false);
}
return ret;
}
int vco_prepare(struct clk *c)
{
int rc = 0;
struct dsi_pll_vco_clk *vco = to_vco_clk(c);
struct mdss_pll_resources *dsi_pll_res = vco->priv;
if (!dsi_pll_res) {
pr_err("Dsi pll resources are not available\n");
return -EINVAL;
}
if ((dsi_pll_res->vco_cached_rate != 0)
&& (dsi_pll_res->vco_cached_rate == c->rate)) {
rc = c->ops->set_rate(c, dsi_pll_res->vco_cached_rate);
if (rc) {
pr_err("vco_set_rate failed. rc=%d\n", rc);
goto error;
}
}
rc = dsi_pll_enable(c);
error:
return rc;
}
void vco_unprepare(struct clk *c)
{
struct dsi_pll_vco_clk *vco = to_vco_clk(c);
struct mdss_pll_resources *dsi_pll_res = vco->priv;
if (!dsi_pll_res) {
pr_err("Dsi pll resources are not available\n");
return;
}
dsi_pll_res->vco_cached_rate = c->rate;
dsi_pll_disable(c);
}

View File

@@ -1,580 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/iopoll.h>
#include <linux/clk/msm-clk-provider.h>
#include <linux/clk/msm-clk.h>
#include <linux/clk/msm-clock-generic.h>
#include <dt-bindings/clock/msm-clocks-8974.h>
#include "pll_drv.h"
#include "edp_pll.h"
#define EDP_PHY_PLL_UNIPHY_PLL_REFCLK_CFG (0x0)
#define EDP_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG (0x0004)
#define EDP_PHY_PLL_UNIPHY_PLL_VCOLPF_CFG (0x000C)
#define EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG (0x0020)
#define EDP_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG (0x0024)
#define EDP_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG (0x0028)
#define EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG0 (0x0038)
#define EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG1 (0x003C)
#define EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG2 (0x0040)
#define EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG3 (0x0044)
#define EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG4 (0x0048)
#define EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG0 (0x004C)
#define EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG1 (0x0050)
#define EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG2 (0x0054)
#define EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG3 (0x0058)
#define EDP_PHY_PLL_UNIPHY_PLL_LKDET_CFG2 (0x0064)
#define EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG0 (0x006C)
#define EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG2 (0x0074)
#define EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG6 (0x0084)
#define EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG7 (0x0088)
#define EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG8 (0x008C)
#define EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG9 (0x0090)
#define EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG10 (0x0094)
#define EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG11 (0x0098)
#define EDP_PHY_PLL_UNIPHY_PLL_LKDET_CFG0 (0x005C)
#define EDP_PHY_PLL_UNIPHY_PLL_LKDET_CFG1 (0x0060)
#define EDP_PLL_POLL_DELAY_US 50
#define EDP_PLL_POLL_TIMEOUT_US 500
static const struct clk_ops edp_mainlink_clk_src_ops;
static struct clk_div_ops fixed_5div_ops; /* null ops */
static const struct clk_ops edp_pixel_clk_ops;
static inline struct edp_pll_vco_clk *to_edp_vco_clk(struct clk *clk)
{
return container_of(clk, struct edp_pll_vco_clk, c);
}
int edp_div_prepare(struct clk *c)
{
struct div_clk *div = to_div_clk(c);
/* Restore the divider's value */
return div->ops->set_div(div, div->data.div);
}
static int edp_vco_set_rate(struct clk *c, unsigned long vco_rate)
{
struct edp_pll_vco_clk *vco = to_edp_vco_clk(c);
struct mdss_pll_resources *edp_pll_res = vco->priv;
int rc;
pr_debug("vco_rate=%d\n", (int)vco_rate);
rc = mdss_pll_resource_enable(edp_pll_res, true);
if (rc) {
pr_err("failed to enable edp pll res rc=%d\n", rc);
rc = -EINVAL;
}
if (vco_rate == 810000000) {
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_VCOLPF_CFG, 0x18);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x0d);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_REFCLK_CFG, 0x00);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG0, 0x36);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG1, 0x69);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG2, 0xff);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG3, 0x2f);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG4, 0x00);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG0, 0x80);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG1, 0x00);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG2, 0x00);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG3, 0x00);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG0, 0x12);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG2, 0x01);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG6, 0x5a);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG7, 0x0);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG8, 0x60);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG9, 0x0);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG10, 0x2a);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG11, 0x3);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_LKDET_CFG0, 0x10);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_LKDET_CFG1, 0x1a);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG, 0x00);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG, 0x00);
} else if (vco_rate == 1350000000) {
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x0d);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_REFCLK_CFG, 0x01);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG0, 0x36);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG1, 0x62);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG2, 0x00);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG3, 0x28);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_SDM_CFG4, 0x00);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG0, 0x80);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG1, 0x00);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG2, 0x00);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_SSC_CFG3, 0x00);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG0, 0x12);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG2, 0x01);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG6, 0x5a);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG7, 0x0);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG8, 0x60);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG9, 0x0);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG10, 0x46);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_CAL_CFG11, 0x5);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_LKDET_CFG0, 0x10);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_LKDET_CFG1, 0x1a);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG, 0x00);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG, 0x00);
} else {
pr_err("rate=%d is NOT supported\n", (int)vco_rate);
vco_rate = 0;
rc = -EINVAL;
}
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
udelay(100);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
udelay(100);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07);
udelay(100);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
udelay(100);
mdss_pll_resource_enable(edp_pll_res, false);
vco->rate = vco_rate;
return rc;
}
static int edp_pll_ready_poll(struct mdss_pll_resources *edp_pll_res)
{
int cnt;
u32 status;
cnt = 100;
while (cnt--) {
udelay(100);
status = MDSS_PLL_REG_R(edp_pll_res->pll_base, 0xc0);
status &= 0x01;
if (status)
break;
}
pr_debug("cnt=%d status=%d\n", cnt, (int)status);
if (status)
return 1;
return 0;
}
static int edp_vco_enable(struct clk *c)
{
int i, ready;
int rc;
struct edp_pll_vco_clk *vco = to_edp_vco_clk(c);
struct mdss_pll_resources *edp_pll_res = vco->priv;
rc = mdss_pll_resource_enable(edp_pll_res, true);
if (rc) {
pr_err("edp pll resources not available\n");
return rc;
}
for (i = 0; i < 3; i++) {
ready = edp_pll_ready_poll(edp_pll_res);
if (ready)
break;
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
udelay(100);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
udelay(100);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07);
udelay(100);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
udelay(100);
}
if (ready) {
pr_debug("EDP PLL lock success\n");
edp_pll_res->pll_on = true;
rc = 0;
} else {
pr_err("EDP PLL failed to lock\n");
mdss_pll_resource_enable(edp_pll_res, false);
rc = -EINVAL;
}
return rc;
}
static void edp_vco_disable(struct clk *c)
{
struct edp_pll_vco_clk *vco = to_edp_vco_clk(c);
struct mdss_pll_resources *edp_pll_res = vco->priv;
if (!edp_pll_res) {
pr_err("Invalid input parameter\n");
return;
}
if (!edp_pll_res->pll_on &&
mdss_pll_resource_enable(edp_pll_res, true)) {
pr_err("edp pll resources not available\n");
return;
}
MDSS_PLL_REG_W(edp_pll_res->pll_base, 0x20, 0x00);
edp_pll_res->handoff_resources = false;
edp_pll_res->pll_on = false;
mdss_pll_resource_enable(edp_pll_res, false);
pr_debug("EDP PLL Disabled\n");
}
static unsigned long edp_vco_get_rate(struct clk *c)
{
struct edp_pll_vco_clk *vco = to_edp_vco_clk(c);
struct mdss_pll_resources *edp_pll_res = vco->priv;
u32 pll_status, div2;
int rc;
if (is_gdsc_disabled(edp_pll_res))
return 0;
rc = mdss_pll_resource_enable(edp_pll_res, true);
if (rc) {
pr_err("edp pll resources not available\n");
return rc;
}
if (vco->rate == 0) {
pll_status = MDSS_PLL_REG_R(edp_pll_res->pll_base, 0xc0);
if (pll_status & 0x01) {
div2 = MDSS_PLL_REG_R(edp_pll_res->pll_base, 0x24);
if (div2 & 0x01)
vco->rate = 1350000000;
else
vco->rate = 810000000;
}
}
mdss_pll_resource_enable(edp_pll_res, false);
pr_debug("rate=%d\n", (int)vco->rate);
return vco->rate;
}
static long edp_vco_round_rate(struct clk *c, unsigned long rate)
{
struct edp_pll_vco_clk *vco = to_edp_vco_clk(c);
unsigned long rrate = -ENOENT;
unsigned long *lp;
lp = vco->rate_list;
while (*lp) {
rrate = *lp;
if (rate <= rrate)
break;
lp++;
}
pr_debug("rrate=%d\n", (int)rrate);
return rrate;
}
static int edp_vco_prepare(struct clk *c)
{
struct edp_pll_vco_clk *vco = to_edp_vco_clk(c);
pr_debug("rate=%d\n", (int)vco->rate);
return edp_vco_set_rate(c, vco->rate);
}
static void edp_vco_unprepare(struct clk *c)
{
struct edp_pll_vco_clk *vco = to_edp_vco_clk(c);
pr_debug("rate=%d\n", (int)vco->rate);
edp_vco_disable(c);
}
static int edp_pll_lock_status(struct mdss_pll_resources *edp_pll_res)
{
u32 status;
int pll_locked = 0;
int rc;
rc = mdss_pll_resource_enable(edp_pll_res, true);
if (rc) {
pr_err("edp pll resources not available\n");
return rc;
}
/* poll for PLL ready status */
if (readl_poll_timeout_atomic((edp_pll_res->pll_base + 0xc0),
status, ((status & BIT(0)) == 1),
EDP_PLL_POLL_DELAY_US,
EDP_PLL_POLL_TIMEOUT_US)) {
pr_debug("EDP PLL status=%x failed to Lock\n", status);
pll_locked = 0;
} else {
pll_locked = 1;
}
mdss_pll_resource_enable(edp_pll_res, false);
return pll_locked;
}
static enum handoff edp_vco_handoff(struct clk *c)
{
enum handoff ret = HANDOFF_DISABLED_CLK;
struct edp_pll_vco_clk *vco = to_edp_vco_clk(c);
struct mdss_pll_resources *edp_pll_res = vco->priv;
if (is_gdsc_disabled(edp_pll_res))
return HANDOFF_DISABLED_CLK;
if (mdss_pll_resource_enable(edp_pll_res, true)) {
pr_err("edp pll resources not available\n");
return ret;
}
edp_pll_res->handoff_resources = true;
if (edp_pll_lock_status(edp_pll_res)) {
c->rate = edp_vco_get_rate(c);
edp_pll_res->pll_on = true;
ret = HANDOFF_ENABLED_CLK;
} else {
edp_pll_res->handoff_resources = false;
mdss_pll_resource_enable(edp_pll_res, false);
}
pr_debug("done, ret=%d\n", ret);
return ret;
}
static unsigned long edp_vco_rate_list[] = {
810000000, 1350000000, 0};
struct const clk_ops edp_vco_clk_ops = {
.enable = edp_vco_enable,
.set_rate = edp_vco_set_rate,
.get_rate = edp_vco_get_rate,
.round_rate = edp_vco_round_rate,
.prepare = edp_vco_prepare,
.unprepare = edp_vco_unprepare,
.handoff = edp_vco_handoff,
};
struct edp_pll_vco_clk edp_vco_clk = {
.ref_clk_rate = 19200000,
.rate = 0,
.rate_list = edp_vco_rate_list,
.c = {
.dbg_name = "edp_vco_clk",
.ops = &edp_vco_clk_ops,
CLK_INIT(edp_vco_clk.c),
},
};
static unsigned long edp_mainlink_get_rate(struct clk *c)
{
struct div_clk *mclk = to_div_clk(c);
struct clk *pclk;
unsigned long rate = 0;
pclk = clk_get_parent(c);
if (pclk && pclk->ops->get_rate) {
rate = pclk->ops->get_rate(pclk);
rate /= mclk->data.div;
}
pr_debug("rate=%d div=%d\n", (int)rate, mclk->data.div);
return rate;
}
struct div_clk edp_mainlink_clk_src = {
.ops = &fixed_5div_ops,
.data = {
.div = 5,
.min_div = 5,
.max_div = 5,
},
.c = {
.parent = &edp_vco_clk.c,
.dbg_name = "edp_mainlink_clk_src",
.ops = &edp_mainlink_clk_src_ops,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(edp_mainlink_clk_src.c),
}
};
/*
* this rate is from pll to clock controller
* output from pll to CC has two possibilities
* 1: if mainlink rate is 270M, then 675M
* 2: if mainlink rate is 162M, then 810M
*/
static int edp_pixel_set_div(struct div_clk *clk, int div)
{
int rc;
struct mdss_pll_resources *edp_pll_res = clk->priv;
rc = mdss_pll_resource_enable(edp_pll_res, true);
if (rc) {
pr_err("edp pll resources not available\n");
return rc;
}
pr_debug("div=%d\n", div);
MDSS_PLL_REG_W(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG, (div - 1));
mdss_pll_resource_enable(edp_pll_res, false);
return 0;
}
static int edp_pixel_get_div(struct div_clk *clk)
{
int div = 0;
int rc;
struct mdss_pll_resources *edp_pll_res = clk->priv;
if (is_gdsc_disabled(edp_pll_res))
return 0;
rc = mdss_pll_resource_enable(edp_pll_res, true);
if (rc) {
pr_err("edp pll resources not available\n");
return rc;
}
div = MDSS_PLL_REG_R(edp_pll_res->pll_base,
EDP_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG);
mdss_pll_resource_enable(edp_pll_res, false);
div &= 0x01;
pr_debug("div=%d\n", div);
return div + 1;
}
static struct clk_div_ops edp_pixel_ops = {
.set_div = edp_pixel_set_div,
.get_div = edp_pixel_get_div,
};
struct div_clk edp_pixel_clk_src = {
.data = {
.max_div = 2,
.min_div = 1,
},
.ops = &edp_pixel_ops,
.c = {
.parent = &edp_vco_clk.c,
.dbg_name = "edp_pixel_clk_src",
.ops = &edp_pixel_clk_ops,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(edp_pixel_clk_src.c),
},
};
static struct clk_lookup mdss_edp_pllcc_8974[] = {
CLK_LOOKUP("edp_pixel_src", edp_pixel_clk_src.c,
"fd8c0000.qcom,mmsscc-mdss"),
CLK_LOOKUP("edp_mainlink_src", edp_mainlink_clk_src.c,
"fd8c0000.qcom,mmsscc-mdss"),
};
int edp_pll_clock_register(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc = -ENOTSUPP;
/* Set client data to div and vco clocks */
edp_pixel_clk_src.priv = pll_res;
edp_mainlink_clk_src.priv = pll_res;
edp_vco_clk.priv = pll_res;
/* Set clock operation for mainlink and pixel clock */
edp_mainlink_clk_src_ops = clk_ops_div;
edp_mainlink_clk_src_ops.get_parent = clk_get_parent;
edp_mainlink_clk_src_ops.get_rate = edp_mainlink_get_rate;
edp_pixel_clk_ops = clk_ops_slave_div;
edp_pixel_clk_ops.prepare = edp_div_prepare;
rc = of_msm_clock_register(pdev->dev.of_node, mdss_edp_pllcc_8974,
ARRAY_SIZE(mdss_edp_pllcc_8974));
if (rc) {
pr_err("Clock register failed rc=%d\n", rc);
rc = -EPROBE_DEFER;
}
return rc;
}

View File

@@ -1,57 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*/
#ifndef __MDSS_HDMI_PLL_H
#define __MDSS_HDMI_PLL_H
struct hdmi_pll_cfg {
unsigned long vco_rate;
u32 reg;
};
struct hdmi_pll_vco_clk {
struct clk_hw hw;
unsigned long rate; /* current vco rate */
unsigned long min_rate; /* min vco rate */
unsigned long max_rate; /* max vco rate */
bool rate_set;
struct hdmi_pll_cfg *ip_seti;
struct hdmi_pll_cfg *cp_seti;
struct hdmi_pll_cfg *ip_setp;
struct hdmi_pll_cfg *cp_setp;
struct hdmi_pll_cfg *crctrl;
void *priv;
};
static inline struct hdmi_pll_vco_clk *to_hdmi_vco_clk_hw(struct clk_hw *hw)
{
return container_of(hw, struct hdmi_pll_vco_clk, hw);
}
int hdmi_pll_clock_register_28lpm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
int hdmi_pll_clock_register(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
int hdmi_20nm_pll_clock_register(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
int hdmi_8996_v1_pll_clock_register(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
int hdmi_8996_v2_pll_clock_register(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
int hdmi_8996_v3_pll_clock_register(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
int hdmi_8996_v3_1p8_pll_clock_register(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
int hdmi_8998_pll_clock_register(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
#endif

View File

@@ -1,970 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/clk/msm-clk-provider.h>
#include <linux/clk/msm-clk.h>
#include <linux/clk/msm-clock-generic.h>
#include <dt-bindings/clock/msm-clocks-8994.h>
#include "pll_drv.h"
#include "hdmi_pll.h"
/* hdmi phy registers */
#define HDMI_PHY_CMD_SIZE 68
#define HDMI_PHY_CLK_SIZE 97
/* Set to 1 for auto KVCO cal; set to 0 for fixed value */
#define HDMI_PHY_AUTO_KVCO_CAL 1
/* PLL REGISTERS */
#define QSERDES_COM_SYS_CLK_CTRL (0x000)
#define QSERDES_COM_PLL_VCOTAIL_EN (0x004)
#define QSERDES_COM_CMN_MODE (0x008)
#define QSERDES_COM_IE_TRIM (0x00C)
#define QSERDES_COM_IP_TRIM (0x010)
#define QSERDES_COM_PLL_CNTRL (0x014)
#define QSERDES_COM_PLL_PHSEL_CONTROL (0x018)
#define QSERDES_COM_IPTAT_TRIM_VCCA_TX_SEL (0x01C)
#define QSERDES_COM_PLL_PHSEL_DC (0x020)
#define QSERDES_COM_PLL_IP_SETI (0x024)
#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL (0x028)
#define QSERDES_COM_PLL_BKG_KVCO_CAL_EN (0x02C)
#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN (0x030)
#define QSERDES_COM_PLL_CP_SETI (0x034)
#define QSERDES_COM_PLL_IP_SETP (0x038)
#define QSERDES_COM_PLL_CP_SETP (0x03C)
#define QSERDES_COM_ATB_SEL1 (0x040)
#define QSERDES_COM_ATB_SEL2 (0x044)
#define QSERDES_COM_SYSCLK_EN_SEL_TXBAND (0x048)
#define QSERDES_COM_RESETSM_CNTRL (0x04C)
#define QSERDES_COM_RESETSM_CNTRL2 (0x050)
#define QSERDES_COM_RESETSM_CNTRL3 (0x054)
#define QSERDES_COM_RESETSM_PLL_CAL_COUNT1 (0x058)
#define QSERDES_COM_RESETSM_PLL_CAL_COUNT2 (0x05C)
#define QSERDES_COM_DIV_REF1 (0x060)
#define QSERDES_COM_DIV_REF2 (0x064)
#define QSERDES_COM_KVCO_COUNT1 (0x068)
#define QSERDES_COM_KVCO_COUNT2 (0x06C)
#define QSERDES_COM_KVCO_CAL_CNTRL (0x070)
#define QSERDES_COM_KVCO_CODE (0x074)
#define QSERDES_COM_VREF_CFG1 (0x078)
#define QSERDES_COM_VREF_CFG2 (0x07C)
#define QSERDES_COM_VREF_CFG3 (0x080)
#define QSERDES_COM_VREF_CFG4 (0x084)
#define QSERDES_COM_VREF_CFG5 (0x088)
#define QSERDES_COM_VREF_CFG6 (0x08C)
#define QSERDES_COM_PLLLOCK_CMP1 (0x090)
#define QSERDES_COM_PLLLOCK_CMP2 (0x094)
#define QSERDES_COM_PLLLOCK_CMP3 (0x098)
#define QSERDES_COM_PLLLOCK_CMP_EN (0x09C)
#define QSERDES_COM_BGTC (0x0A0)
#define QSERDES_COM_PLL_TEST_UPDN (0x0A4)
#define QSERDES_COM_PLL_VCO_TUNE (0x0A8)
#define QSERDES_COM_DEC_START1 (0x0AC)
#define QSERDES_COM_PLL_AMP_OS (0x0B0)
#define QSERDES_COM_SSC_EN_CENTER (0x0B4)
#define QSERDES_COM_SSC_ADJ_PER1 (0x0B8)
#define QSERDES_COM_SSC_ADJ_PER2 (0x0BC)
#define QSERDES_COM_SSC_PER1 (0x0C0)
#define QSERDES_COM_SSC_PER2 (0x0C4)
#define QSERDES_COM_SSC_STEP_SIZE1 (0x0C8)
#define QSERDES_COM_SSC_STEP_SIZE2 (0x0CC)
#define QSERDES_COM_RES_CODE_UP (0x0D0)
#define QSERDES_COM_RES_CODE_DN (0x0D4)
#define QSERDES_COM_RES_CODE_UP_OFFSET (0x0D8)
#define QSERDES_COM_RES_CODE_DN_OFFSET (0x0DC)
#define QSERDES_COM_RES_CODE_START_SEG1 (0x0E0)
#define QSERDES_COM_RES_CODE_START_SEG2 (0x0E4)
#define QSERDES_COM_RES_CODE_CAL_CSR (0x0E8)
#define QSERDES_COM_RES_CODE (0x0EC)
#define QSERDES_COM_RES_TRIM_CONTROL (0x0F0)
#define QSERDES_COM_RES_TRIM_CONTROL2 (0x0F4)
#define QSERDES_COM_RES_TRIM_EN_VCOCALDONE (0x0F8)
#define QSERDES_COM_FAUX_EN (0x0FC)
#define QSERDES_COM_DIV_FRAC_START1 (0x100)
#define QSERDES_COM_DIV_FRAC_START2 (0x104)
#define QSERDES_COM_DIV_FRAC_START3 (0x108)
#define QSERDES_COM_DEC_START2 (0x10C)
#define QSERDES_COM_PLL_RXTXEPCLK_EN (0x110)
#define QSERDES_COM_PLL_CRCTRL (0x114)
#define QSERDES_COM_PLL_CLKEPDIV (0x118)
#define QSERDES_COM_PLL_FREQUPDATE (0x11C)
#define QSERDES_COM_PLL_BKGCAL_TRIM_UP (0x120)
#define QSERDES_COM_PLL_BKGCAL_TRIM_DN (0x124)
#define QSERDES_COM_PLL_BKGCAL_TRIM_MUX (0x128)
#define QSERDES_COM_PLL_BKGCAL_VREF_CFG (0x12C)
#define QSERDES_COM_PLL_BKGCAL_DIV_REF1 (0x130)
#define QSERDES_COM_PLL_BKGCAL_DIV_REF2 (0x134)
#define QSERDES_COM_MUXADDR (0x138)
#define QSERDES_COM_LOW_POWER_RO_CONTROL (0x13C)
#define QSERDES_COM_POST_DIVIDER_CONTROL (0x140)
#define QSERDES_COM_HR_OCLK2_DIVIDER (0x144)
#define QSERDES_COM_HR_OCLK3_DIVIDER (0x148)
#define QSERDES_COM_PLL_VCO_HIGH (0x14C)
#define QSERDES_COM_RESET_SM (0x150)
#define QSERDES_COM_MUXVAL (0x154)
#define QSERDES_COM_CORE_RES_CODE_DN (0x158)
#define QSERDES_COM_CORE_RES_CODE_UP (0x15C)
#define QSERDES_COM_CORE_VCO_TUNE (0x160)
#define QSERDES_COM_CORE_VCO_TAIL (0x164)
#define QSERDES_COM_CORE_KVCO_CODE (0x168)
/* Tx Channel 0 REGISTERS */
#define QSERDES_TX_L0_BIST_MODE_LANENO (0x00)
#define QSERDES_TX_L0_CLKBUF_ENABLE (0x04)
#define QSERDES_TX_L0_TX_EMP_POST1_LVL (0x08)
#define QSERDES_TX_L0_TX_DRV_LVL (0x0C)
#define QSERDES_TX_L0_RESET_TSYNC_EN (0x10)
#define QSERDES_TX_L0_LPB_EN (0x14)
#define QSERDES_TX_L0_RES_CODE_UP (0x18)
#define QSERDES_TX_L0_RES_CODE_DN (0x1C)
#define QSERDES_TX_L0_PERL_LENGTH1 (0x20)
#define QSERDES_TX_L0_PERL_LENGTH2 (0x24)
#define QSERDES_TX_L0_SERDES_BYP_EN_OUT (0x28)
#define QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN (0x2C)
#define QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN (0x30)
#define QSERDES_TX_L0_BIST_PATTERN1 (0x34)
#define QSERDES_TX_L0_BIST_PATTERN2 (0x38)
#define QSERDES_TX_L0_BIST_PATTERN3 (0x3C)
#define QSERDES_TX_L0_BIST_PATTERN4 (0x40)
#define QSERDES_TX_L0_BIST_PATTERN5 (0x44)
#define QSERDES_TX_L0_BIST_PATTERN6 (0x48)
#define QSERDES_TX_L0_BIST_PATTERN7 (0x4C)
#define QSERDES_TX_L0_BIST_PATTERN8 (0x50)
#define QSERDES_TX_L0_LANE_MODE (0x54)
#define QSERDES_TX_L0_IDAC_CAL_LANE_MODE (0x58)
#define QSERDES_TX_L0_IDAC_CAL_LANE_MODE_CONFIGURATION (0x5C)
#define QSERDES_TX_L0_ATB_SEL1 (0x60)
#define QSERDES_TX_L0_ATB_SEL2 (0x64)
#define QSERDES_TX_L0_RCV_DETECT_LVL (0x68)
#define QSERDES_TX_L0_PRBS_SEED1 (0x6C)
#define QSERDES_TX_L0_PRBS_SEED2 (0x70)
#define QSERDES_TX_L0_PRBS_SEED3 (0x74)
#define QSERDES_TX_L0_PRBS_SEED4 (0x78)
#define QSERDES_TX_L0_RESET_GEN (0x7C)
#define QSERDES_TX_L0_TRAN_DRVR_EMP_EN (0x80)
#define QSERDES_TX_L0_TX_INTERFACE_MODE (0x84)
#define QSERDES_TX_L0_PWM_CTRL (0x88)
#define QSERDES_TX_L0_PWM_DATA (0x8C)
#define QSERDES_TX_L0_PWM_ENC_DIV_CTRL (0x90)
#define QSERDES_TX_L0_VMODE_CTRL1 (0x94)
#define QSERDES_TX_L0_VMODE_CTRL2 (0x98)
#define QSERDES_TX_L0_VMODE_CTRL3 (0x9C)
#define QSERDES_TX_L0_VMODE_CTRL4 (0xA0)
#define QSERDES_TX_L0_VMODE_CTRL5 (0xA4)
#define QSERDES_TX_L0_VMODE_CTRL6 (0xA8)
#define QSERDES_TX_L0_VMODE_CTRL7 (0xAC)
#define QSERDES_TX_L0_TX_ALOG_INTF_OBSV_CNTL (0xB0)
#define QSERDES_TX_L0_BIST_STATUS (0xB4)
#define QSERDES_TX_L0_BIST_ERROR_COUNT1 (0xB8)
#define QSERDES_TX_L0_BIST_ERROR_COUNT2 (0xBC)
#define QSERDES_TX_L0_TX_ALOG_INTF_OBSV (0xC0)
#define QSERDES_TX_L0_PWM_DEC_STATUS (0xC4)
/* Tx Channel 1 REGISTERS */
#define QSERDES_TX_L1_BIST_MODE_LANENO (0x00)
#define QSERDES_TX_L1_CLKBUF_ENABLE (0x04)
#define QSERDES_TX_L1_TX_EMP_POST1_LVL (0x08)
#define QSERDES_TX_L1_TX_DRV_LVL (0x0C)
#define QSERDES_TX_L1_RESET_TSYNC_EN (0x10)
#define QSERDES_TX_L1_LPB_EN (0x14)
#define QSERDES_TX_L1_RES_CODE_UP (0x18)
#define QSERDES_TX_L1_RES_CODE_DN (0x1C)
#define QSERDES_TX_L1_PERL_LENGTH1 (0x20)
#define QSERDES_TX_L1_PERL_LENGTH2 (0x24)
#define QSERDES_TX_L1_SERDES_BYP_EN_OUT (0x28)
#define QSERDES_TX_L1_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN (0x2C)
#define QSERDES_TX_L1_PARRATE_REC_DETECT_IDLE_EN (0x30)
#define QSERDES_TX_L1_BIST_PATTERN1 (0x34)
#define QSERDES_TX_L1_BIST_PATTERN2 (0x38)
#define QSERDES_TX_L1_BIST_PATTERN3 (0x3C)
#define QSERDES_TX_L1_BIST_PATTERN4 (0x40)
#define QSERDES_TX_L1_BIST_PATTERN5 (0x44)
#define QSERDES_TX_L1_BIST_PATTERN6 (0x48)
#define QSERDES_TX_L1_BIST_PATTERN7 (0x4C)
#define QSERDES_TX_L1_BIST_PATTERN8 (0x50)
#define QSERDES_TX_L1_LANE_MODE (0x54)
#define QSERDES_TX_L1_IDAC_CAL_LANE_MODE (0x58)
#define QSERDES_TX_L1_IDAC_CAL_LANE_MODE_CONFIGURATION (0x5C)
#define QSERDES_TX_L1_ATB_SEL1 (0x60)
#define QSERDES_TX_L1_ATB_SEL2 (0x64)
#define QSERDES_TX_L1_RCV_DETECT_LVL (0x68)
#define QSERDES_TX_L1_PRBS_SEED1 (0x6C)
#define QSERDES_TX_L1_PRBS_SEED2 (0x70)
#define QSERDES_TX_L1_PRBS_SEED3 (0x74)
#define QSERDES_TX_L1_PRBS_SEED4 (0x78)
#define QSERDES_TX_L1_RESET_GEN (0x7C)
#define QSERDES_TX_L1_TRAN_DRVR_EMP_EN (0x80)
#define QSERDES_TX_L1_TX_INTERFACE_MODE (0x84)
#define QSERDES_TX_L1_PWM_CTRL (0x88)
#define QSERDES_TX_L1_PWM_DATA (0x8C)
#define QSERDES_TX_L1_PWM_ENC_DIV_CTRL (0x90)
#define QSERDES_TX_L1_VMODE_CTRL1 (0x94)
#define QSERDES_TX_L1_VMODE_CTRL2 (0x98)
#define QSERDES_TX_L1_VMODE_CTRL3 (0x9C)
#define QSERDES_TX_L1_VMODE_CTRL4 (0xA0)
#define QSERDES_TX_L1_VMODE_CTRL5 (0xA4)
#define QSERDES_TX_L1_VMODE_CTRL6 (0xA8)
#define QSERDES_TX_L1_VMODE_CTRL7 (0xAC)
#define QSERDES_TX_L1_TX_ALOG_INTF_OBSV_CNTL (0xB0)
#define QSERDES_TX_L1_BIST_STATUS (0xB4)
#define QSERDES_TX_L1_BIST_ERROR_COUNT1 (0xB8)
#define QSERDES_TX_L1_BIST_ERROR_COUNT2 (0xBC)
#define QSERDES_TX_L1_TX_ALOG_INTF_OBSV (0xC0)
#define QSERDES_TX_L1_PWM_DEC_STATUS (0xC4)
/* Tx Channel 2 REGISERS */
#define QSERDES_TX_L2_BIST_MODE_LANENO (0x00)
#define QSERDES_TX_L2_CLKBUF_ENABLE (0x04)
#define QSERDES_TX_L2_TX_EMP_POST1_LVL (0x08)
#define QSERDES_TX_L2_TX_DRV_LVL (0x0C)
#define QSERDES_TX_L2_RESET_TSYNC_EN (0x10)
#define QSERDES_TX_L2_LPB_EN (0x14)
#define QSERDES_TX_L2_RES_CODE_UP (0x18)
#define QSERDES_TX_L2_RES_CODE_DN (0x1C)
#define QSERDES_TX_L2_PERL_LENGTH1 (0x20)
#define QSERDES_TX_L2_PERL_LENGTH2 (0x24)
#define QSERDES_TX_L2_SERDES_BYP_EN_OUT (0x28)
#define QSERDES_TX_L2_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN (0x2C)
#define QSERDES_TX_L2_PARRATE_REC_DETECT_IDLE_EN (0x30)
#define QSERDES_TX_L2_BIST_PATTERN1 (0x34)
#define QSERDES_TX_L2_BIST_PATTERN2 (0x38)
#define QSERDES_TX_L2_BIST_PATTERN3 (0x3C)
#define QSERDES_TX_L2_BIST_PATTERN4 (0x40)
#define QSERDES_TX_L2_BIST_PATTERN5 (0x44)
#define QSERDES_TX_L2_BIST_PATTERN6 (0x48)
#define QSERDES_TX_L2_BIST_PATTERN7 (0x4C)
#define QSERDES_TX_L2_BIST_PATTERN8 (0x50)
#define QSERDES_TX_L2_LANE_MODE (0x54)
#define QSERDES_TX_L2_IDAC_CAL_LANE_MODE (0x58)
#define QSERDES_TX_L2_IDAC_CAL_LANE_MODE_CONFIGURATION (0x5C)
#define QSERDES_TX_L2_ATB_SEL1 (0x60)
#define QSERDES_TX_L2_ATB_SEL2 (0x64)
#define QSERDES_TX_L2_RCV_DETECT_LVL (0x68)
#define QSERDES_TX_L2_PRBS_SEED1 (0x6C)
#define QSERDES_TX_L2_PRBS_SEED2 (0x70)
#define QSERDES_TX_L2_PRBS_SEED3 (0x74)
#define QSERDES_TX_L2_PRBS_SEED4 (0x78)
#define QSERDES_TX_L2_RESET_GEN (0x7C)
#define QSERDES_TX_L2_TRAN_DRVR_EMP_EN (0x80)
#define QSERDES_TX_L2_TX_INTERFACE_MODE (0x84)
#define QSERDES_TX_L2_PWM_CTRL (0x88)
#define QSERDES_TX_L2_PWM_DATA (0x8C)
#define QSERDES_TX_L2_PWM_ENC_DIV_CTRL (0x90)
#define QSERDES_TX_L2_VMODE_CTRL1 (0x94)
#define QSERDES_TX_L2_VMODE_CTRL2 (0x98)
#define QSERDES_TX_L2_VMODE_CTRL3 (0x9C)
#define QSERDES_TX_L2_VMODE_CTRL4 (0xA0)
#define QSERDES_TX_L2_VMODE_CTRL5 (0xA4)
#define QSERDES_TX_L2_VMODE_CTRL6 (0xA8)
#define QSERDES_TX_L2_VMODE_CTRL7 (0xAC)
#define QSERDES_TX_L2_TX_ALOG_INTF_OBSV_CNTL (0xB0)
#define QSERDES_TX_L2_BIST_STATUS (0xB4)
#define QSERDES_TX_L2_BIST_ERROR_COUNT1 (0xB8)
#define QSERDES_TX_L2_BIST_ERROR_COUNT2 (0xBC)
#define QSERDES_TX_L2_TX_ALOG_INTF_OBSV (0xC0)
#define QSERDES_TX_L2_PWM_DEC_STATUS (0xC4)
/* Tx Channel 3 REGISERS */
#define QSERDES_TX_L3_BIST_MODE_LANENO (0x00)
#define QSERDES_TX_L3_CLKBUF_ENABLE (0x04)
#define QSERDES_TX_L3_TX_EMP_POST1_LVL (0x08)
#define QSERDES_TX_L3_TX_DRV_LVL (0x0C)
#define QSERDES_TX_L3_RESET_TSYNC_EN (0x10)
#define QSERDES_TX_L3_LPB_EN (0x14)
#define QSERDES_TX_L3_RES_CODE_UP (0x18)
#define QSERDES_TX_L3_RES_CODE_DN (0x1C)
#define QSERDES_TX_L3_PERL_LENGTH1 (0x20)
#define QSERDES_TX_L3_PERL_LENGTH2 (0x24)
#define QSERDES_TX_L3_SERDES_BYP_EN_OUT (0x28)
#define QSERDES_TX_L3_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN (0x2C)
#define QSERDES_TX_L3_PARRATE_REC_DETECT_IDLE_EN (0x30)
#define QSERDES_TX_L3_BIST_PATTERN1 (0x34)
#define QSERDES_TX_L3_BIST_PATTERN2 (0x38)
#define QSERDES_TX_L3_BIST_PATTERN3 (0x3C)
#define QSERDES_TX_L3_BIST_PATTERN4 (0x40)
#define QSERDES_TX_L3_BIST_PATTERN5 (0x44)
#define QSERDES_TX_L3_BIST_PATTERN6 (0x48)
#define QSERDES_TX_L3_BIST_PATTERN7 (0x4C)
#define QSERDES_TX_L3_BIST_PATTERN8 (0x50)
#define QSERDES_TX_L3_LANE_MODE (0x54)
#define QSERDES_TX_L3_IDAC_CAL_LANE_MODE (0x58)
#define QSERDES_TX_L3_IDAC_CAL_LANE_MODE_CONFIGURATION (0x5C)
#define QSERDES_TX_L3_ATB_SEL1 (0x60)
#define QSERDES_TX_L3_ATB_SEL2 (0x64)
#define QSERDES_TX_L3_RCV_DETECT_LVL (0x68)
#define QSERDES_TX_L3_PRBS_SEED1 (0x6C)
#define QSERDES_TX_L3_PRBS_SEED2 (0x70)
#define QSERDES_TX_L3_PRBS_SEED3 (0x74)
#define QSERDES_TX_L3_PRBS_SEED4 (0x78)
#define QSERDES_TX_L3_RESET_GEN (0x7C)
#define QSERDES_TX_L3_TRAN_DRVR_EMP_EN (0x80)
#define QSERDES_TX_L3_TX_INTERFACE_MODE (0x84)
#define QSERDES_TX_L3_PWM_CTRL (0x88)
#define QSERDES_TX_L3_PWM_DATA (0x8C)
#define QSERDES_TX_L3_PWM_ENC_DIV_CTRL (0x90)
#define QSERDES_TX_L3_VMODE_CTRL1 (0x94)
#define QSERDES_TX_L3_VMODE_CTRL2 (0x98)
#define QSERDES_TX_L3_VMODE_CTRL3 (0x9C)
#define QSERDES_TX_L3_VMODE_CTRL4 (0xA0)
#define QSERDES_TX_L3_VMODE_CTRL5 (0xA4)
#define QSERDES_TX_L3_VMODE_CTRL6 (0xA8)
#define QSERDES_TX_L3_VMODE_CTRL7 (0xAC)
#define QSERDES_TX_L3_TX_ALOG_INTF_OBSV_CNTL (0xB0)
#define QSERDES_TX_L3_BIST_STATUS (0xB4)
#define QSERDES_TX_L3_BIST_ERROR_COUNT1 (0xB8)
#define QSERDES_TX_L3_BIST_ERROR_COUNT2 (0xBC)
#define QSERDES_TX_L3_TX_ALOG_INTF_OBSV (0xC0)
#define QSERDES_TX_L3_PWM_DEC_STATUS (0xC4)
/* HDMI PHY REGISTERS */
#define HDMI_PHY_CFG (0x00)
#define HDMI_PHY_PD_CTL (0x04)
#define HDMI_PHY_MODE (0x08)
#define HDMI_PHY_MISR_CLEAR (0x0C)
#define HDMI_PHY_TX0_TX1_BIST_CFG0 (0x10)
#define HDMI_PHY_TX0_TX1_BIST_CFG1 (0x14)
#define HDMI_PHY_TX0_TX1_PRBS_SEED_BYTE0 (0x18)
#define HDMI_PHY_TX0_TX1_PRBS_SEED_BYTE1 (0x1C)
#define HDMI_PHY_TX0_TX1_PRBS_SEED_BYTE2 (0x20)
#define HDMI_PHY_TX0_TX1_PRBS_SEED_BYTE3 (0x24)
#define HDMI_PHY_TX0_TX1_PRBS_POLY_BYTE0 (0x28)
#define HDMI_PHY_TX0_TX1_PRBS_POLY_BYTE1 (0x2C)
#define HDMI_PHY_TX0_TX1_PRBS_POLY_BYTE2 (0x30)
#define HDMI_PHY_TX0_TX1_PRBS_POLY_BYTE3 (0x34)
#define HDMI_PHY_TX2_TX3_BIST_CFG0 (0x38)
#define HDMI_PHY_TX2_TX3_BIST_CFG1 (0x3C)
#define HDMI_PHY_TX2_TX3_PRBS_SEED_BYTE0 (0x40)
#define HDMI_PHY_TX2_TX3_PRBS_SEED_BYTE1 (0x44)
#define HDMI_PHY_TX2_TX3_PRBS_SEED_BYTE2 (0x48)
#define HDMI_PHY_TX2_TX3_PRBS_SEED_BYTE3 (0x4C)
#define HDMI_PHY_TX2_TX3_PRBS_POLY_BYTE0 (0x50)
#define HDMI_PHY_TX2_TX3_PRBS_POLY_BYTE1 (0x54)
#define HDMI_PHY_TX2_TX3_PRBS_POLY_BYTE2 (0x58)
#define HDMI_PHY_TX2_TX3_PRBS_POLY_BYTE3 (0x5C)
#define HDMI_PHY_DEBUG_BUS_SEL (0x60)
#define HDMI_PHY_TXCAL_CFG0 (0x64)
#define HDMI_PHY_TXCAL_CFG1 (0x68)
#define HDMI_PHY_TX0_TX1_BIST_STATUS0 (0x6C)
#define HDMI_PHY_TX0_TX1_BIST_STATUS1 (0x70)
#define HDMI_PHY_TX0_TX1_BIST_STATUS2 (0x74)
#define HDMI_PHY_TX2_TX3_BIST_STATUS0 (0x78)
#define HDMI_PHY_TX2_TX3_BIST_STATUS1 (0x7C)
#define HDMI_PHY_TX2_TX3_BIST_STATUS2 (0x80)
#define HDMI_PHY_PRE_MISR_STATUS0 (0x84)
#define HDMI_PHY_PRE_MISR_STATUS1 (0x88)
#define HDMI_PHY_PRE_MISR_STATUS2 (0x8C)
#define HDMI_PHY_PRE_MISR_STATUS3 (0x90)
#define HDMI_PHY_POST_MISR_STATUS0 (0x94)
#define HDMI_PHY_POST_MISR_STATUS1 (0x98)
#define HDMI_PHY_POST_MISR_STATUS2 (0x9C)
#define HDMI_PHY_POST_MISR_STATUS3 (0xA0)
#define HDMI_PHY_STATUS (0xA4)
#define HDMI_PHY_MISC3_STATUS (0xA8)
#define HDMI_PHY_DEBUG_BUS0 (0xAC)
#define HDMI_PHY_DEBUG_BUS1 (0xB0)
#define HDMI_PHY_DEBUG_BUS2 (0xB4)
#define HDMI_PHY_DEBUG_BUS3 (0xB8)
#define HDMI_PHY_REVISION_ID0 (0xBC)
#define HDMI_PHY_REVISION_ID1 (0xC0)
#define HDMI_PHY_REVISION_ID2 (0xC4)
#define HDMI_PHY_REVISION_ID3 (0xC8)
#define HDMI_PLL_POLL_DELAY_US 50
#define HDMI_PLL_POLL_TIMEOUT_US 125000
#define HDMI_PLL_REF_CLK_RATE 192ULL
#define HDMI_PLL_DIVISOR 10000000000ULL
#define HDMI_PLL_DIVISOR_32 100000U
#define HDMI_PLL_MIN_VCO_CLK 160000000ULL
#define HDMI_PLL_TMDS_MAX 800000000U
static int hdmi_20nm_pll_lock_status(struct mdss_pll_resources *io)
{
u32 status;
int pll_locked = 0;
int phy_ready = 0;
int rc;
rc = mdss_pll_resource_enable(io, true);
if (rc) {
pr_err("pll resource can't be enabled\n");
return rc;
}
/* Poll for C_READY and PHY READY */
pr_debug("%s: Waiting for PHY Ready\n", __func__);
/* poll for PLL ready status */
if (!readl_poll_timeout_atomic(
(io->pll_base + QSERDES_COM_RESET_SM),
status, ((status & BIT(6)) == 1),
HDMI_PLL_POLL_DELAY_US,
HDMI_PLL_POLL_TIMEOUT_US)) {
pr_debug("%s: C READY\n", __func__);
pll_locked = 1;
} else {
pr_debug("%s: C READY TIMEOUT\n", __func__);
pll_locked = 0;
}
/* poll for PHY ready status */
if (pll_locked && !readl_poll_timeout_atomic(
(io->phy_base + HDMI_PHY_STATUS),
status, ((status & BIT(0)) == 1),
HDMI_PLL_POLL_DELAY_US,
HDMI_PLL_POLL_TIMEOUT_US)) {
pr_debug("%s: PHY READY\n", __func__);
phy_ready = 1;
} else {
pr_debug("%s: PHY READY TIMEOUT\n", __func__);
phy_ready = 0;
}
mdss_pll_resource_enable(io, false);
return phy_ready;
}
static inline struct hdmi_pll_vco_clk *to_hdmi_20nm_vco_clk(struct clk *clk)
{
return container_of(clk, struct hdmi_pll_vco_clk, c);
}
static inline u32 hdmi_20nm_phy_pll_vco_reg_val(struct hdmi_pll_cfg *pll_cfg,
u32 tmds_clk)
{
u32 index = 0;
while (pll_cfg[index].vco_rate < HDMI_PLL_TMDS_MAX &&
pll_cfg[index].vco_rate < tmds_clk)
index++;
return pll_cfg[index].reg;
}
static void hdmi_20nm_phy_pll_calc_settings(struct mdss_pll_resources *io,
struct hdmi_pll_vco_clk *vco, u32 vco_clk, u32 tmds_clk)
{
u32 val = 0;
u64 dec_start_val, frac_start_val, pll_lock_cmp;
/* Calculate decimal and fractional values */
dec_start_val = 1000000UL * vco_clk;
do_div(dec_start_val, HDMI_PLL_REF_CLK_RATE);
do_div(dec_start_val, 2U);
frac_start_val = dec_start_val;
do_div(frac_start_val, HDMI_PLL_DIVISOR_32);
do_div(frac_start_val, HDMI_PLL_DIVISOR_32);
frac_start_val *= HDMI_PLL_DIVISOR;
frac_start_val = dec_start_val - frac_start_val;
frac_start_val *= (u64)(2 << 19);
do_div(frac_start_val, HDMI_PLL_DIVISOR_32);
do_div(frac_start_val, HDMI_PLL_DIVISOR_32);
pll_lock_cmp = dec_start_val;
do_div(pll_lock_cmp, 10U);
pll_lock_cmp *= 0x800;
do_div(pll_lock_cmp, HDMI_PLL_DIVISOR_32);
do_div(pll_lock_cmp, HDMI_PLL_DIVISOR_32);
pll_lock_cmp -= 1U;
do_div(dec_start_val, HDMI_PLL_DIVISOR_32);
do_div(dec_start_val, HDMI_PLL_DIVISOR_32);
/* PLL loop bandwidth */
val = hdmi_20nm_phy_pll_vco_reg_val(vco->ip_seti, tmds_clk);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_IP_SETI, val);
val = hdmi_20nm_phy_pll_vco_reg_val(vco->cp_seti, tmds_clk);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_CP_SETI, val);
val = hdmi_20nm_phy_pll_vco_reg_val(vco->cp_setp, tmds_clk);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_CP_SETP, val);
val = hdmi_20nm_phy_pll_vco_reg_val(vco->ip_setp, tmds_clk);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_IP_SETP, val);
val = hdmi_20nm_phy_pll_vco_reg_val(vco->crctrl, tmds_clk);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_CRCTRL, val);
/* PLL calibration */
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_FRAC_START1,
0x80 | (frac_start_val & 0x7F));
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_FRAC_START2,
0x80 | ((frac_start_val >> 7) & 0x7F));
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_FRAC_START3,
0x40 | ((frac_start_val >> 14) & 0x3F));
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DEC_START1,
0x80 | (dec_start_val & 0x7F));
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DEC_START2,
0x02 | (0x01 & (dec_start_val >> 7)));
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLLLOCK_CMP1,
pll_lock_cmp & 0xFF);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLLLOCK_CMP2,
(pll_lock_cmp >> 8) & 0xFF);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLLLOCK_CMP3,
(pll_lock_cmp >> 16) & 0xFF);
}
static u32 hdmi_20nm_phy_pll_set_clk_rate(struct clk *c, u32 tmds_clk)
{
u32 tx_band = 0;
struct hdmi_pll_vco_clk *vco = to_hdmi_20nm_vco_clk(c);
struct mdss_pll_resources *io = vco->priv;
u64 vco_clk = tmds_clk;
while (vco_clk > 0 && vco_clk < HDMI_PLL_MIN_VCO_CLK) {
tx_band++;
vco_clk *= 2;
}
/* Initially shut down PHY */
pr_debug("%s: Disabling PHY\n", __func__);
MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_PD_CTL, 0x0);
udelay(1000);
/* memory barrier */
mb();
/* power-up and recommended common block settings */
MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_PD_CTL, 0x1F);
MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x01);
udelay(1000);
/* memory barrier */
mb();
MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x07);
udelay(1000);
/* memory barrier */
mb();
MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x05);
udelay(1000);
/* memory barrier */
mb();
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SYS_CLK_CTRL, 0x42);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_VCOTAIL_EN, 0x03);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MODE, 0x00);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_IE_TRIM, 0x00);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_IP_TRIM, 0x00);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_CNTRL, 0x07);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_PHSEL_CONTROL, 0x04);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_IPTAT_TRIM_VCCA_TX_SEL, 0xA0);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_PHSEL_DC, 0x00);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CORE_CLK_IN_SYNC_SEL, 0x00);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_BKG_KVCO_CAL_EN, 0x00);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x0F);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL1, 0x01);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL2, 0x01);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SYSCLK_EN_SEL_TXBAND,
0x4A + (0x10 * tx_band));
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VREF_CFG1, 0x00);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VREF_CFG2, 0x00);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BGTC, 0xFF);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_TEST_UPDN, 0x00);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_VCO_TUNE, 0x00);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_AMP_OS, 0x00);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_EN_CENTER, 0x00);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RES_CODE_UP, 0x00);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RES_CODE_DN, 0x00);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_KVCO_CODE,
tmds_clk > 300000000 ? 0x3F : 0x00);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_KVCO_COUNT1,
tmds_clk > 300000000 ? 0x00 : 0x8A);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_REF1, 0x00);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_REF2,
tmds_clk > 300000000 ? 0x00 : 0x01);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_KVCO_CAL_CNTRL,
tmds_clk > 300000000 ? 0x00 : 0x1F);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VREF_CFG3,
tmds_clk > 300000000 ? 0x00 : 0x40);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VREF_CFG4, 0x00);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VREF_CFG5, 0x10);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RESETSM_CNTRL,
tmds_clk > 300000000 ? 0x80 : 0x00);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RES_CODE_CAL_CSR, 0x77);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RES_TRIM_EN_VCOCALDONE, 0x00);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_RXTXEPCLK_EN, 0x0C);
hdmi_20nm_phy_pll_calc_settings(io, vco, vco_clk, tmds_clk);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLLLOCK_CMP_EN, 0x11);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_CNTRL, 0x07);
/* Resistor calibration linear search */
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RES_CODE_START_SEG1, 0x60);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RES_CODE_START_SEG2, 0x60);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RES_TRIM_CONTROL, 0x01);
MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RESETSM_CNTRL2, 0x07);
udelay(1000);
/* memory barrier */
mb();
MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_MODE, tx_band);
/* TX lanes (transceivers) power-up sequence */
MDSS_PLL_REG_W(io->pll_base + 0x400, QSERDES_TX_L0_CLKBUF_ENABLE, 0x03);
MDSS_PLL_REG_W(io->pll_base + 0x600, QSERDES_TX_L1_CLKBUF_ENABLE, 0x03);
MDSS_PLL_REG_W(io->pll_base + 0x800, QSERDES_TX_L2_CLKBUF_ENABLE, 0x03);
MDSS_PLL_REG_W(io->pll_base + 0xA00, QSERDES_TX_L3_CLKBUF_ENABLE, 0x03);
MDSS_PLL_REG_W(io->pll_base + 0x400,
QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03);
MDSS_PLL_REG_W(io->pll_base + 0x600,
QSERDES_TX_L1_TRAN_DRVR_EMP_EN, 0x03);
MDSS_PLL_REG_W(io->pll_base + 0x800,
QSERDES_TX_L2_TRAN_DRVR_EMP_EN, 0x03);
MDSS_PLL_REG_W(io->pll_base + 0xA00,
QSERDES_TX_L3_TRAN_DRVR_EMP_EN, 0x03);
MDSS_PLL_REG_W(io->pll_base + 0x400,
QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x6F);
MDSS_PLL_REG_W(io->pll_base + 0x600,
QSERDES_TX_L1_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x6F);
MDSS_PLL_REG_W(io->pll_base + 0x800,
QSERDES_TX_L2_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x6F);
MDSS_PLL_REG_W(io->pll_base + 0xA00,
QSERDES_TX_L3_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x6F);
MDSS_PLL_REG_W(io->pll_base + 0x400,
QSERDES_TX_L0_TX_EMP_POST1_LVL, 0x0000002F);
MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TXCAL_CFG0, 0x000000AF);
MDSS_PLL_REG_W(io->pll_base + 0x400, QSERDES_TX_L0_VMODE_CTRL1, 0x08);
MDSS_PLL_REG_W(io->pll_base + 0x800, QSERDES_TX_L2_VMODE_CTRL1, 0x09);
MDSS_PLL_REG_W(io->pll_base + 0x400, QSERDES_TX_L0_VMODE_CTRL5, 0xA0);
MDSS_PLL_REG_W(io->pll_base + 0x400, QSERDES_TX_L0_VMODE_CTRL6, 0x01);
MDSS_PLL_REG_W(io->pll_base + 0x800, QSERDES_TX_L2_VMODE_CTRL5, 0xA0);
MDSS_PLL_REG_W(io->pll_base + 0x800, QSERDES_TX_L2_VMODE_CTRL6, 0x01);
MDSS_PLL_REG_W(io->pll_base + 0x400,
QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40);
MDSS_PLL_REG_W(io->pll_base + 0x400,
QSERDES_TX_L0_TX_INTERFACE_MODE, 0x00);
MDSS_PLL_REG_W(io->pll_base + 0x600,
QSERDES_TX_L1_PARRATE_REC_DETECT_IDLE_EN, 0x40);
MDSS_PLL_REG_W(io->pll_base + 0x600,
QSERDES_TX_L1_TX_INTERFACE_MODE, 0x00);
MDSS_PLL_REG_W(io->pll_base + 0x800,
QSERDES_TX_L2_PARRATE_REC_DETECT_IDLE_EN, 0x40);
MDSS_PLL_REG_W(io->pll_base + 0x800,
QSERDES_TX_L2_TX_INTERFACE_MODE, 0x00);
MDSS_PLL_REG_W(io->pll_base + 0xA00,
QSERDES_TX_L3_PARRATE_REC_DETECT_IDLE_EN, 0x40);
MDSS_PLL_REG_W(io->pll_base + 0xA00,
QSERDES_TX_L3_TX_INTERFACE_MODE, 0x00);
return 0;
}
static int hdmi_20nm_vco_enable(struct clk *c)
{
u32 ready_poll;
u32 time_out_loop;
/* Hardware recommended timeout iterator */
u32 time_out_max = 50000;
struct hdmi_pll_vco_clk *vco = to_hdmi_20nm_vco_clk(c);
struct mdss_pll_resources *io = vco->priv;
MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x00000000);
udelay(100);
/* memory barrier */
mb();
MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x00000003);
udelay(100);
/* memory barrier */
mb();
MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x00000009);
udelay(100);
/* memory barrier */
mb();
/* Poll for C_READY and PHY READY */
pr_debug("%s: Waiting for PHY Ready\n", __func__);
time_out_loop = 0;
do {
ready_poll = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_RESET_SM);
time_out_loop++;
udelay(10);
} while (((ready_poll & (1 << 6)) == 0) &&
(time_out_loop < time_out_max));
if (time_out_loop >= time_out_max)
pr_err("%s: ERROR: TIMED OUT BEFORE C READY\n", __func__);
else
pr_debug("%s: C READY\n", __func__);
/* Poll for PHY READY */
pr_debug("%s: Waiting for PHY Ready\n", __func__);
time_out_loop = 0;
do {
ready_poll = MDSS_PLL_REG_R(io->phy_base, HDMI_PHY_STATUS);
time_out_loop++;
udelay(1);
} while (((ready_poll & 0x1) == 0) && (time_out_loop < time_out_max));
if (time_out_loop >= time_out_max)
pr_err("%s: TIMED OUT BEFORE PHY READY\n", __func__);
else
pr_debug("%s: HDMI PHY READY\n", __func__);
io->pll_on = true;
return 0;
}
static int hdmi_20nm_vco_set_rate(struct clk *c, unsigned long rate)
{
struct hdmi_pll_vco_clk *vco = to_hdmi_20nm_vco_clk(c);
struct mdss_pll_resources *io = vco->priv;
void __iomem *pll_base;
void __iomem *phy_base;
unsigned int set_power_dwn = 0;
int rc;
rc = mdss_pll_resource_enable(io, true);
if (rc) {
pr_err("pll resource can't be enabled\n");
return rc;
}
if (io->pll_on)
set_power_dwn = 1;
pll_base = io->pll_base;
phy_base = io->phy_base;
pr_debug("rate=%ld\n", rate);
hdmi_20nm_phy_pll_set_clk_rate(c, rate);
mdss_pll_resource_enable(io, false);
if (set_power_dwn)
hdmi_20nm_vco_enable(c);
vco->rate = rate;
vco->rate_set = true;
return 0;
}
static unsigned long hdmi_20nm_vco_get_rate(struct clk *c)
{
unsigned long freq = 0;
int rc;
struct hdmi_pll_vco_clk *vco = to_hdmi_20nm_vco_clk(c);
struct mdss_pll_resources *io = vco->priv;
if (is_gdsc_disabled(io))
return 0;
rc = mdss_pll_resource_enable(io, true);
if (rc) {
pr_err("pll resource can't be enabled\n");
return rc;
}
mdss_pll_resource_enable(io, false);
return freq;
}
static long hdmi_20nm_vco_round_rate(struct clk *c, unsigned long rate)
{
unsigned long rrate = rate;
pr_debug("rrate=%ld\n", rrate);
return rrate;
}
static int hdmi_20nm_vco_prepare(struct clk *c)
{
struct hdmi_pll_vco_clk *vco = to_hdmi_20nm_vco_clk(c);
struct mdss_pll_resources *io = vco->priv;
int ret = 0;
pr_debug("rate=%ld\n", vco->rate);
if (!vco->rate_set && vco->rate)
ret = hdmi_20nm_vco_set_rate(c, vco->rate);
if (!ret) {
ret = mdss_pll_resource_enable(io, true);
if (ret)
pr_err("pll resource can't be enabled\n");
}
return ret;
}
static void hdmi_20nm_vco_unprepare(struct clk *c)
{
struct hdmi_pll_vco_clk *vco = to_hdmi_20nm_vco_clk(c);
struct mdss_pll_resources *io = vco->priv;
vco->rate_set = false;
if (!io) {
pr_err("Invalid input parameter\n");
return;
}
if (!io->pll_on &&
mdss_pll_resource_enable(io, true)) {
pr_err("pll resource can't be enabled\n");
return;
}
io->handoff_resources = false;
mdss_pll_resource_enable(io, false);
io->pll_on = false;
}
static enum handoff hdmi_20nm_vco_handoff(struct clk *c)
{
enum handoff ret = HANDOFF_DISABLED_CLK;
struct hdmi_pll_vco_clk *vco = to_hdmi_20nm_vco_clk(c);
struct mdss_pll_resources *io = vco->priv;
if (is_gdsc_disabled(io))
return HANDOFF_DISABLED_CLK;
if (mdss_pll_resource_enable(io, true)) {
pr_err("pll resource can't be enabled\n");
return ret;
}
io->handoff_resources = true;
if (hdmi_20nm_pll_lock_status(io)) {
io->pll_on = true;
c->rate = hdmi_20nm_vco_get_rate(c);
ret = HANDOFF_ENABLED_CLK;
} else {
io->handoff_resources = false;
mdss_pll_resource_enable(io, false);
}
pr_debug("done, ret=%d\n", ret);
return ret;
}
static const struct clk_ops hdmi_20nm_vco_clk_ops = {
.enable = hdmi_20nm_vco_enable,
.set_rate = hdmi_20nm_vco_set_rate,
.get_rate = hdmi_20nm_vco_get_rate,
.round_rate = hdmi_20nm_vco_round_rate,
.prepare = hdmi_20nm_vco_prepare,
.unprepare = hdmi_20nm_vco_unprepare,
.handoff = hdmi_20nm_vco_handoff,
};
static struct hdmi_pll_vco_clk hdmi_20nm_vco_clk = {
.ip_seti = (struct hdmi_pll_cfg[]){
{550890000, 0x03},
{589240000, 0x07},
{689290000, 0x03},
{727600000, 0x07},
{HDMI_PLL_TMDS_MAX, 0x03},
},
.cp_seti = (struct hdmi_pll_cfg[]){
{34440000, 0x3F},
{36830000, 0x2F},
{68870000, 0x3F},
{73660000, 0x2F},
{137730000, 0x3F},
{147310000, 0x2F},
{275450000, 0x3F},
{294620000, 0x2F},
{344650000, 0x3F},
{363800000, 0x2F},
{477960000, 0x3F},
{512530000, 0x2F},
{550890000, 0x1F},
{589240000, 0x2F},
{630900000, 0x3F},
{650590000, 0x2F},
{689290000, 0x1F},
{727600000, 0x2F},
{HDMI_PLL_TMDS_MAX, 0x3F},
},
.ip_setp = (struct hdmi_pll_cfg[]){
{497340000, 0x03},
{512530000, 0x07},
{535680000, 0x03},
{550890000, 0x07},
{574060000, 0x03},
{727600000, 0x07},
{HDMI_PLL_TMDS_MAX, 0x03},
},
.cp_setp = (struct hdmi_pll_cfg[]){
{36830000, 0x1F},
{40010000, 0x17},
{73660000, 0x1F},
{80000000, 0x17},
{147310000, 0x1F},
{160010000, 0x17},
{294620000, 0x1F},
{363800000, 0x17},
{497340000, 0x0F},
{512530000, 0x1F},
{535680000, 0x0F},
{550890000, 0x1F},
{574060000, 0x0F},
{589240000, 0x1F},
{727600000, 0x17},
{HDMI_PLL_TMDS_MAX, 0x07},
},
.crctrl = (struct hdmi_pll_cfg[]){
{40010000, 0xBB},
{40030000, 0x77},
{80000000, 0xBB},
{80060000, 0x77},
{160010000, 0xBB},
{160120000, 0x77},
{772930000, 0xBB},
{HDMI_PLL_TMDS_MAX, 0xFF},
},
.c = {
.dbg_name = "hdmi_20nm_vco_clk",
.ops = &hdmi_20nm_vco_clk_ops,
CLK_INIT(hdmi_20nm_vco_clk.c),
},
};
static struct clk_lookup hdmipllcc_8994[] = {
CLK_LIST(hdmi_20nm_vco_clk),
};
int hdmi_20nm_pll_clock_register(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc = -ENOTSUPP;
/* Set client data for vco, mux and div clocks */
hdmi_20nm_vco_clk.priv = pll_res;
rc = of_msm_clock_register(pdev->dev.of_node, hdmipllcc_8994,
ARRAY_SIZE(hdmipllcc_8994));
if (rc) {
pr_err("Clock register failed rc=%d\n", rc);
rc = -EPROBE_DEFER;
} else {
pr_debug("%s: SUCCESS\n", __func__);
}
return rc;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,790 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <dt-bindings/clock/mdss-28nm-pll-clk.h>
#include "pll_drv.h"
#include "hdmi_pll.h"
/* HDMI PLL macros */
#define HDMI_PHY_PLL_REFCLK_CFG (0x0400)
#define HDMI_PHY_PLL_CHRG_PUMP_CFG (0x0404)
#define HDMI_PHY_PLL_LOOP_FLT_CFG0 (0x0408)
#define HDMI_PHY_PLL_LOOP_FLT_CFG1 (0x040c)
#define HDMI_PHY_PLL_IDAC_ADJ_CFG (0x0410)
#define HDMI_PHY_PLL_I_VI_KVCO_CFG (0x0414)
#define HDMI_PHY_PLL_PWRDN_B (0x0418)
#define HDMI_PHY_PLL_SDM_CFG0 (0x041c)
#define HDMI_PHY_PLL_SDM_CFG1 (0x0420)
#define HDMI_PHY_PLL_SDM_CFG2 (0x0424)
#define HDMI_PHY_PLL_SDM_CFG3 (0x0428)
#define HDMI_PHY_PLL_SDM_CFG4 (0x042c)
#define HDMI_PHY_PLL_SSC_CFG0 (0x0430)
#define HDMI_PHY_PLL_SSC_CFG1 (0x0434)
#define HDMI_PHY_PLL_SSC_CFG2 (0x0438)
#define HDMI_PHY_PLL_SSC_CFG3 (0x043c)
#define HDMI_PHY_PLL_LOCKDET_CFG0 (0x0440)
#define HDMI_PHY_PLL_LOCKDET_CFG1 (0x0444)
#define HDMI_PHY_PLL_LOCKDET_CFG2 (0x0448)
#define HDMI_PHY_PLL_VCOCAL_CFG0 (0x044c)
#define HDMI_PHY_PLL_VCOCAL_CFG1 (0x0450)
#define HDMI_PHY_PLL_VCOCAL_CFG2 (0x0454)
#define HDMI_PHY_PLL_VCOCAL_CFG3 (0x0458)
#define HDMI_PHY_PLL_VCOCAL_CFG4 (0x045c)
#define HDMI_PHY_PLL_VCOCAL_CFG5 (0x0460)
#define HDMI_PHY_PLL_VCOCAL_CFG6 (0x0464)
#define HDMI_PHY_PLL_VCOCAL_CFG7 (0x0468)
#define HDMI_PHY_PLL_DEBUG_SEL (0x046c)
#define HDMI_PHY_PLL_MISC0 (0x0470)
#define HDMI_PHY_PLL_MISC1 (0x0474)
#define HDMI_PHY_PLL_MISC2 (0x0478)
#define HDMI_PHY_PLL_MISC3 (0x047c)
#define HDMI_PHY_PLL_MISC4 (0x0480)
#define HDMI_PHY_PLL_MISC5 (0x0484)
#define HDMI_PHY_PLL_MISC6 (0x0488)
#define HDMI_PHY_PLL_DEBUG_BUS0 (0x048c)
#define HDMI_PHY_PLL_DEBUG_BUS1 (0x0490)
#define HDMI_PHY_PLL_DEBUG_BUS2 (0x0494)
#define HDMI_PHY_PLL_STATUS0 (0x0498)
#define HDMI_PHY_PLL_STATUS1 (0x049c)
#define HDMI_PHY_REG_0 (0x0000)
#define HDMI_PHY_REG_1 (0x0004)
#define HDMI_PHY_REG_2 (0x0008)
#define HDMI_PHY_REG_3 (0x000c)
#define HDMI_PHY_REG_4 (0x0010)
#define HDMI_PHY_REG_5 (0x0014)
#define HDMI_PHY_REG_6 (0x0018)
#define HDMI_PHY_REG_7 (0x001c)
#define HDMI_PHY_REG_8 (0x0020)
#define HDMI_PHY_REG_9 (0x0024)
#define HDMI_PHY_REG_10 (0x0028)
#define HDMI_PHY_REG_11 (0x002c)
#define HDMI_PHY_REG_12 (0x0030)
#define HDMI_PHY_REG_BIST_CFG (0x0034)
#define HDMI_PHY_DEBUG_BUS_SEL (0x0038)
#define HDMI_PHY_REG_MISC0 (0x003c)
#define HDMI_PHY_REG_13 (0x0040)
#define HDMI_PHY_REG_14 (0x0044)
#define HDMI_PHY_REG_15 (0x0048)
/* HDMI PHY/PLL bit field macros */
#define SW_RESET BIT(2)
#define SW_RESET_PLL BIT(0)
#define PWRDN_B BIT(7)
#define PLL_PWRDN_B BIT(3)
#define REG_VTEST_EN BIT(2)
#define PD_PLL BIT(1)
#define PD_PLL_REG BIT(0)
#define HDMI_PLL_POLL_DELAY_US 50
#define HDMI_PLL_POLL_TIMEOUT_US 500
static int hdmi_pll_lock_status(struct mdss_pll_resources *hdmi_pll_res)
{
u32 status;
int pll_locked = 0;
int rc;
rc = mdss_pll_resource_enable(hdmi_pll_res, true);
if (rc) {
pr_err("pll resource can't be enabled\n");
return rc;
}
/* poll for PLL ready status */
if (readl_poll_timeout_atomic(
(hdmi_pll_res->pll_base + HDMI_PHY_PLL_STATUS0),
status, ((status & BIT(0)) == 1),
HDMI_PLL_POLL_DELAY_US,
HDMI_PLL_POLL_TIMEOUT_US)) {
pr_debug("HDMI PLL status=%x failed to Lock\n", status);
pll_locked = 0;
} else {
pr_debug("HDMI PLL locked\n");
pll_locked = 1;
}
mdss_pll_resource_enable(hdmi_pll_res, false);
return pll_locked;
}
static void hdmi_pll_disable_28lpm(struct clk_hw *hw)
{
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk_hw(hw);
struct mdss_pll_resources *hdmi_pll_res = vco->priv;
u32 val;
if (!hdmi_pll_res) {
pr_err("Invalid input parameter\n");
return;
}
val = MDSS_PLL_REG_R(hdmi_pll_res->pll_base, HDMI_PHY_REG_12);
val &= (~PWRDN_B);
MDSS_PLL_REG_W(hdmi_pll_res->pll_base, HDMI_PHY_REG_12, val);
val = MDSS_PLL_REG_R(hdmi_pll_res->pll_base, HDMI_PHY_PLL_PWRDN_B);
val |= PD_PLL;
val &= (~PLL_PWRDN_B);
MDSS_PLL_REG_W(hdmi_pll_res->pll_base, HDMI_PHY_PLL_PWRDN_B, val);
/* Make sure HDMI PHY/PLL are powered down */
wmb();
} /* hdmi_pll_disable_28lpm */
static int hdmi_pll_enable_28lpm(struct clk_hw *hw)
{
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk_hw(hw);
struct mdss_pll_resources *hdmi_pll_res = vco->priv;
void __iomem *pll_base;
u32 val;
int pll_lock_retry = 10;
pll_base = hdmi_pll_res->pll_base;
/* Assert PLL S/W reset */
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_LOCKDET_CFG2, 0x8d);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_LOCKDET_CFG0, 0x10);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_LOCKDET_CFG1, 0x1a);
udelay(10);
/* De-assert PLL S/W reset */
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_LOCKDET_CFG2, 0x0d);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_REG_1, 0xf2);
udelay(10);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_REG_2, 0x1f);
val = MDSS_PLL_REG_R(pll_base, HDMI_PHY_REG_12);
val |= BIT(5);
/* Assert PHY S/W reset */
MDSS_PLL_REG_W(pll_base, HDMI_PHY_REG_12, val);
val &= ~BIT(5);
udelay(10);
/* De-assert PHY S/W reset */
MDSS_PLL_REG_W(pll_base, HDMI_PHY_REG_12, val);
val = MDSS_PLL_REG_R(pll_base, HDMI_PHY_REG_12);
val |= PWRDN_B;
MDSS_PLL_REG_W(pll_base, HDMI_PHY_REG_12, val);
/* Wait 10 us for enabling global power for PHY */
wmb();
udelay(10);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_REG_3, 0x20);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_REG_4, 0x10);
val = MDSS_PLL_REG_R(pll_base, HDMI_PHY_PLL_PWRDN_B);
val |= PLL_PWRDN_B;
val |= REG_VTEST_EN;
val &= ~PD_PLL;
val |= PD_PLL_REG;
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_PWRDN_B, val);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_REG_2, 0x81);
do {
if (!hdmi_pll_lock_status(hdmi_pll_res)) {
/* PLL has still not locked.
* Do a software reset and try again
* Assert PLL S/W reset first
*/
MDSS_PLL_REG_W(pll_base,
HDMI_PHY_PLL_LOCKDET_CFG2, 0x8d);
/* Wait for a short time before de-asserting
* to allow the hardware to complete its job.
* This much of delay should be fine for hardware
* to assert and de-assert.
*/
udelay(10);
MDSS_PLL_REG_W(pll_base,
HDMI_PHY_PLL_LOCKDET_CFG2, 0xd);
/* Wait for a short duration for the PLL calibration
* before checking if the PLL gets locked
*/
udelay(350);
} else {
pr_debug("HDMI PLL locked\n");
break;
}
} while (--pll_lock_retry);
if (!pll_lock_retry) {
pr_err("HDMI PLL not locked\n");
hdmi_pll_disable_28lpm(hw);
return -EAGAIN;
}
return 0;
} /* hdmi_pll_enable_28lpm */
static void hdmi_phy_pll_calculator_28lpm(unsigned long vco_rate,
struct mdss_pll_resources *hdmi_pll_res)
{
u32 ref_clk = 19200000;
u32 integer_mode = 0;
u32 ref_clk_multiplier = integer_mode == 0 ? 2 : 1;
u32 int_ref_clk_freq = ref_clk * ref_clk_multiplier;
u32 refclk_cfg = 0;
u32 ten_power_six = 1000000;
u64 multiplier_q = 0;
u64 multiplier_r = 0;
u32 lf_cfg0 = 0;
u32 lf_cfg1 = 0;
u64 vco_cfg0 = 0;
u64 vco_cfg4 = 0;
u64 sdm_cfg0 = 0;
u64 sdm_cfg1 = 0;
u64 sdm_cfg2 = 0;
u32 val1 = 0;
u32 val2 = 0;
u32 val3 = 0;
void __iomem *pll_base = hdmi_pll_res->pll_base;
multiplier_q = vco_rate;
multiplier_r = do_div(multiplier_q, int_ref_clk_freq);
lf_cfg0 = multiplier_q > 30 ? 0 : (multiplier_q > 16 ? 16 : 32);
lf_cfg0 += integer_mode;
lf_cfg1 = multiplier_q > 30 ? 0xc3 : (multiplier_q > 16 ? 0xbb : 0xf9);
vco_cfg0 = vco_rate / ten_power_six;
vco_cfg4 = ((ref_clk * 5) / ten_power_six) - 1;
sdm_cfg0 = (integer_mode * 64) + multiplier_q - 1;
sdm_cfg1 = 64 + multiplier_q - 1;
sdm_cfg2 = (multiplier_r) * 65536;
do_div(sdm_cfg2, int_ref_clk_freq);
pr_debug("lf_cfg0 = 0x%x lf_cfg1 = 0x%x\n", lf_cfg0, lf_cfg1);
pr_debug("vco_cfg0 = 0x%llx vco_cfg4 = 0x%llx\n", vco_cfg0, vco_cfg4);
pr_debug("sdm_cfg0 = 0x%llx sdm_cfg1 = 0x%llx sdm_cfg2 = 0x%llx\n",
sdm_cfg0, sdm_cfg1, sdm_cfg2);
refclk_cfg = MDSS_PLL_REG_R(pll_base, HDMI_PHY_PLL_REFCLK_CFG);
refclk_cfg &= ~0xf;
refclk_cfg |= (ref_clk_multiplier == 2) ? 0x8
: (ref_clk_multiplier == 1) ? 0 : 0x2;
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_REFCLK_CFG, refclk_cfg);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_CHRG_PUMP_CFG, 0x02);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_LOOP_FLT_CFG0, lf_cfg0);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_LOOP_FLT_CFG1, lf_cfg1);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_IDAC_ADJ_CFG, 0x2c);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_I_VI_KVCO_CFG, 0x06);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_PWRDN_B, 0x0a);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_SDM_CFG0, sdm_cfg0);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_SDM_CFG1, sdm_cfg1);
val1 = sdm_cfg2 & 0xff;
val2 = (sdm_cfg2 >> 8) & 0xff;
val3 = (sdm_cfg2 >> 16) & 0xff;
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_SDM_CFG2, val1);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_SDM_CFG3, val2);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_SDM_CFG4, val3);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_SSC_CFG0, 0x9a);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_SSC_CFG1, 0x00);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_SSC_CFG2, 0x00);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_SSC_CFG3, 0x00);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_LOCKDET_CFG0, 0x10);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_LOCKDET_CFG1, 0x1a);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_LOCKDET_CFG2, 0x0d);
val1 = vco_cfg0 & 0xff;
val2 = (vco_cfg0 >> 8) & 0xff;
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_VCOCAL_CFG0, val1);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_VCOCAL_CFG1, val2);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_VCOCAL_CFG2, 0x3b);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_VCOCAL_CFG3, 0x00);
val1 = vco_cfg4 & 0xff;
val2 = (vco_cfg4 >> 8) & 0xff;
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_VCOCAL_CFG4, val1);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_VCOCAL_CFG5, val2);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_VCOCAL_CFG6, 0x33);
MDSS_PLL_REG_W(pll_base, HDMI_PHY_PLL_VCOCAL_CFG7, 0x03);
}
int hdmi_vco_set_rate_28lpm(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk_hw(hw);
struct mdss_pll_resources *hdmi_pll_res = vco->priv;
void __iomem *pll_base;
int rc;
rc = mdss_pll_resource_enable(hdmi_pll_res, true);
if (rc) {
pr_err("pll resource can't be enabled\n");
return rc;
}
if (hdmi_pll_res->pll_on)
return 0;
pll_base = hdmi_pll_res->pll_base;
pr_debug("rate=%ld\n", rate);
hdmi_phy_pll_calculator_28lpm(rate, hdmi_pll_res);
/* Make sure writes complete before disabling iface clock */
wmb();
vco->rate = rate;
hdmi_pll_res->vco_current_rate = rate;
mdss_pll_resource_enable(hdmi_pll_res, false);
return 0;
} /* hdmi_pll_set_rate */
static unsigned long hdmi_vco_get_rate(struct hdmi_pll_vco_clk *vco)
{
unsigned long freq = 0;
int rc = 0;
struct mdss_pll_resources *hdmi_pll_res = vco->priv;
rc = mdss_pll_resource_enable(hdmi_pll_res, true);
if (rc) {
pr_err("Failed to enable hdmi pll resources\n");
return 0;
}
freq = MDSS_PLL_REG_R(hdmi_pll_res->pll_base,
HDMI_PHY_PLL_VCOCAL_CFG1) << 8 |
MDSS_PLL_REG_R(hdmi_pll_res->pll_base,
HDMI_PHY_PLL_VCOCAL_CFG0);
switch (freq) {
case 742:
freq = 742500000;
break;
case 810:
if (MDSS_PLL_REG_R(hdmi_pll_res->pll_base,
HDMI_PHY_PLL_SDM_CFG3) == 0x18)
freq = 810000000;
else
freq = 810900000;
break;
case 1342:
freq = 1342500000;
break;
default:
freq *= 1000000;
}
mdss_pll_resource_enable(hdmi_pll_res, false);
return freq;
}
long hdmi_vco_round_rate_28lpm(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
unsigned long rrate = rate;
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk_hw(hw);
if (rate < vco->min_rate)
rrate = vco->min_rate;
if (rate > vco->max_rate)
rrate = vco->max_rate;
*parent_rate = rrate;
pr_debug("rrate=%ld\n", rrate);
return rrate;
}
int hdmi_vco_prepare_28lpm(struct clk_hw *hw)
{
int rc = 0;
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk_hw(hw);
struct mdss_pll_resources *hdmi_res = vco->priv;
pr_debug("rate=%ld\n", clk_hw_get_rate(hw));
rc = mdss_pll_resource_enable(hdmi_res, true);
if (rc) {
pr_err("Failed to enable mdss HDMI pll resources\n");
goto error;
}
if ((hdmi_res->vco_cached_rate != 0)
&& (hdmi_res->vco_cached_rate == clk_hw_get_rate(hw))) {
rc = vco->hw.init->ops->set_rate(hw,
hdmi_res->vco_cached_rate, hdmi_res->vco_cached_rate);
if (rc) {
pr_err("index=%d vco_set_rate failed. rc=%d\n",
rc, hdmi_res->index);
mdss_pll_resource_enable(hdmi_res, false);
goto error;
}
}
rc = hdmi_pll_enable_28lpm(hw);
if (rc) {
mdss_pll_resource_enable(hdmi_res, false);
pr_err("ndx=%d failed to enable hdmi pll\n",
hdmi_res->index);
goto error;
}
mdss_pll_resource_enable(hdmi_res, false);
pr_debug("HDMI PLL enabled\n");
error:
return rc;
}
void hdmi_vco_unprepare_28lpm(struct clk_hw *hw)
{
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk_hw(hw);
struct mdss_pll_resources *hdmi_res = vco->priv;
if (!hdmi_res) {
pr_err("Invalid input parameter\n");
return;
}
if (!hdmi_res->pll_on &&
mdss_pll_resource_enable(hdmi_res, true)) {
pr_err("pll resource can't be enabled\n");
return;
}
hdmi_res->vco_cached_rate = clk_hw_get_rate(hw);
hdmi_pll_disable_28lpm(hw);
hdmi_res->handoff_resources = false;
mdss_pll_resource_enable(hdmi_res, false);
hdmi_res->pll_on = false;
pr_debug("HDMI PLL disabled\n");
}
unsigned long hdmi_vco_recalc_rate_28lpm(struct clk_hw *hw,
unsigned long parent_rate)
{
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk_hw(hw);
struct mdss_pll_resources *hdmi_pll_res = vco->priv;
u64 vco_rate = 0;
if (!hdmi_pll_res) {
pr_err("dsi pll resources not available\n");
return 0;
}
if (hdmi_pll_res->vco_current_rate) {
vco_rate = (unsigned long)hdmi_pll_res->vco_current_rate;
pr_debug("vco_rate=%lld\n", vco_rate);
return vco_rate;
}
if (is_gdsc_disabled(hdmi_pll_res))
return 0;
if (mdss_pll_resource_enable(hdmi_pll_res, true)) {
pr_err("Failed to enable hdmi pll resources\n");
return 0;
}
if (hdmi_pll_lock_status(hdmi_pll_res)) {
hdmi_pll_res->handoff_resources = true;
hdmi_pll_res->pll_on = true;
vco_rate = hdmi_vco_get_rate(vco);
} else {
hdmi_pll_res->handoff_resources = false;
mdss_pll_resource_enable(hdmi_pll_res, false);
}
pr_debug("vco_rate = %lld\n", vco_rate);
return (unsigned long)vco_rate;
}
static int hdmi_mux_set_parent(void *context, unsigned int reg,
unsigned int mux_sel)
{
struct mdss_pll_resources *hdmi_pll_res = context;
int rc = 0;
u32 reg_val = 0;
const u32 div_4 = 0x20;
const u32 div_6 = 0x30;
rc = mdss_pll_resource_enable(hdmi_pll_res, true);
if (rc) {
pr_err("Failed to enable hdmi pll resources\n");
return rc;
}
/*
* divsel_six is preferred over divsel_four to keep
* vco range within goal limits to maintain margin.
* To achieve this, its precedence order is toggled
* at mux level. So reverse toggle the mux_sel value
* here.
*/
switch (mux_sel) {
case 0x20: /* intended divider is divsel_six */
mux_sel = div_6;
break;
case 0x30: /* intended divider is divsel_four */
mux_sel = div_4;
break;
}
pr_debug("mux_sel = %d\n", mux_sel);
reg_val = MDSS_PLL_REG_R(hdmi_pll_res->pll_base,
HDMI_PHY_PLL_REFCLK_CFG);
reg_val &= ~0x70;
reg_val |= (mux_sel & 0x70);
pr_debug("pll_refclk_cfg = 0x%x\n", reg_val);
MDSS_PLL_REG_W(hdmi_pll_res->pll_base,
HDMI_PHY_PLL_REFCLK_CFG, reg_val);
(void)mdss_pll_resource_enable(hdmi_pll_res, false);
return 0;
}
static int hdmi_mux_get_parent(void *context, unsigned int reg,
unsigned int *val)
{
int rc = 0;
int mux_sel = 0;
struct mdss_pll_resources *hdmi_pll_res = context;
rc = mdss_pll_resource_enable(hdmi_pll_res, true);
if (rc) {
*val = 0;
pr_err("Failed to enable hdmi pll resources\n");
} else {
mux_sel = MDSS_PLL_REG_R(hdmi_pll_res->pll_base,
HDMI_PHY_PLL_REFCLK_CFG);
mux_sel &= 0x70;
*val = mux_sel;
pr_debug("mux_sel = %d\n", *val);
}
(void)mdss_pll_resource_enable(hdmi_pll_res, false);
return rc;
}
static struct regmap_config hdmi_pll_28lpm_cfg = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = 0x49c,
};
static struct regmap_bus hdmi_pclk_src_mux_regmap_ops = {
.reg_write = hdmi_mux_set_parent,
.reg_read = hdmi_mux_get_parent,
};
/* Op structures */
static const struct clk_ops hdmi_28lpm_vco_clk_ops = {
.recalc_rate = hdmi_vco_recalc_rate_28lpm,
.set_rate = hdmi_vco_set_rate_28lpm,
.round_rate = hdmi_vco_round_rate_28lpm,
.prepare = hdmi_vco_prepare_28lpm,
.unprepare = hdmi_vco_unprepare_28lpm,
};
static struct hdmi_pll_vco_clk hdmi_vco_clk = {
.min_rate = 540000000,
.max_rate = 1125000000,
.hw.init = &(struct clk_init_data){
.name = "hdmi_vco_clk",
.parent_names = (const char *[]){ "cxo" },
.num_parents = 1,
.ops = &hdmi_28lpm_vco_clk_ops,
},
};
static struct clk_fixed_factor hdmi_vco_divsel_one_clk_src = {
.div = 1,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "hdmi_vco_divsel_one_clk_src",
.parent_names =
(const char *[]){ "hdmi_vco_clk" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor hdmi_vco_divsel_two_clk_src = {
.div = 2,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "hdmi_vco_divsel_two_clk_src",
.parent_names =
(const char *[]){ "hdmi_vco_clk" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor hdmi_vco_divsel_four_clk_src = {
.div = 4,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "hdmi_vco_divsel_four_clk_src",
.parent_names =
(const char *[]){ "hdmi_vco_clk" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_fixed_factor hdmi_vco_divsel_six_clk_src = {
.div = 6,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "hdmi_vco_divsel_six_clk_src",
.parent_names =
(const char *[]){ "hdmi_vco_clk" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_regmap_mux hdmi_pclk_src_mux = {
.reg = HDMI_PHY_PLL_REFCLK_CFG,
.shift = 4,
.width = 2,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "hdmi_pclk_src_mux",
.parent_names =
(const char *[]){"hdmi_vco_divsel_one_clk_src",
"hdmi_vco_divsel_two_clk_src",
"hdmi_vco_divsel_six_clk_src",
"hdmi_vco_divsel_four_clk_src"},
.num_parents = 4,
.ops = &clk_regmap_mux_closest_ops,
.flags = CLK_SET_RATE_PARENT,
},
},
};
static struct clk_fixed_factor hdmi_pclk_src = {
.div = 5,
.mult = 1,
.hw.init = &(struct clk_init_data){
.name = "hdmi_phy_pll_clk",
.parent_names =
(const char *[]){ "hdmi_pclk_src_mux" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
.ops = &clk_fixed_factor_ops,
},
};
static struct clk_hw *mdss_hdmi_pllcc_28lpm[] = {
[HDMI_VCO_CLK] = &hdmi_vco_clk.hw,
[HDMI_VCO_DIVIDED_1_CLK_SRC] = &hdmi_vco_divsel_one_clk_src.hw,
[HDMI_VCO_DIVIDED_TWO_CLK_SRC] = &hdmi_vco_divsel_two_clk_src.hw,
[HDMI_VCO_DIVIDED_FOUR_CLK_SRC] = &hdmi_vco_divsel_four_clk_src.hw,
[HDMI_VCO_DIVIDED_SIX_CLK_SRC] = &hdmi_vco_divsel_six_clk_src.hw,
[HDMI_PCLK_SRC_MUX] = &hdmi_pclk_src_mux.clkr.hw,
[HDMI_PCLK_SRC] = &hdmi_pclk_src.hw,
};
int hdmi_pll_clock_register_28lpm(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc = -ENOTSUPP, i;
struct clk *clk;
struct clk_onecell_data *clk_data;
int num_clks = ARRAY_SIZE(mdss_hdmi_pllcc_28lpm);
struct regmap *regmap;
if (!pdev || !pdev->dev.of_node ||
!pll_res || !pll_res->pll_base) {
pr_err("Invalid input parameters\n");
return -EPROBE_DEFER;
}
clk_data = devm_kzalloc(&pdev->dev, sizeof(struct clk_onecell_data),
GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
clk_data->clks = devm_kzalloc(&pdev->dev, (num_clks *
sizeof(struct clk *)), GFP_KERNEL);
if (!clk_data->clks) {
devm_kfree(&pdev->dev, clk_data);
return -ENOMEM;
}
clk_data->clk_num = num_clks;
/* Set client data for vco, mux and div clocks */
regmap = devm_regmap_init(&pdev->dev, &hdmi_pclk_src_mux_regmap_ops,
pll_res, &hdmi_pll_28lpm_cfg);
hdmi_pclk_src_mux.clkr.regmap = regmap;
hdmi_vco_clk.priv = pll_res;
for (i = HDMI_VCO_CLK; i <= HDMI_PCLK_SRC; i++) {
pr_debug("reg clk: %d index: %d\n", i, pll_res->index);
clk = devm_clk_register(&pdev->dev,
mdss_hdmi_pllcc_28lpm[i]);
if (IS_ERR(clk)) {
pr_err("clk registration failed for HDMI: %d\n",
pll_res->index);
rc = -EINVAL;
goto clk_reg_fail;
}
clk_data->clks[i] = clk;
}
rc = of_clk_add_provider(pdev->dev.of_node,
of_clk_src_onecell_get, clk_data);
if (rc) {
pr_err("%s: Clock register failed rc=%d\n", __func__, rc);
rc = -EPROBE_DEFER;
} else {
pr_debug("%s SUCCESS\n", __func__);
rc = 0;
}
return rc;
clk_reg_fail:
return rc;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,827 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/clk/msm-clk-provider.h>
#include <linux/clk/msm-clk.h>
#include <linux/clk/msm-clock-generic.h>
#include <dt-bindings/clock/msm-clocks-8998.h>
#include "pll_drv.h"
#include "hdmi_pll.h"
#define _W(x, y, z) MDSS_PLL_REG_W(x, y, z)
#define _R(x, y) MDSS_PLL_REG_R(x, y)
/* PLL REGISTERS */
#define BIAS_EN_CLKBUFLR_EN (0x034)
#define CLK_ENABLE1 (0x038)
#define SYS_CLK_CTRL (0x03C)
#define SYSCLK_BUF_ENABLE (0x040)
#define PLL_IVCO (0x048)
#define CP_CTRL_MODE0 (0x060)
#define PLL_RCTRL_MODE0 (0x068)
#define PLL_CCTRL_MODE0 (0x070)
#define SYSCLK_EN_SEL (0x080)
#define RESETSM_CNTRL (0x088)
#define LOCK_CMP_EN (0x090)
#define LOCK_CMP1_MODE0 (0x098)
#define LOCK_CMP2_MODE0 (0x09C)
#define LOCK_CMP3_MODE0 (0x0A0)
#define DEC_START_MODE0 (0x0B0)
#define DIV_FRAC_START1_MODE0 (0x0B8)
#define DIV_FRAC_START2_MODE0 (0x0BC)
#define DIV_FRAC_START3_MODE0 (0x0C0)
#define INTEGLOOP_GAIN0_MODE0 (0x0D8)
#define INTEGLOOP_GAIN1_MODE0 (0x0DC)
#define VCO_TUNE_CTRL (0x0EC)
#define VCO_TUNE_MAP (0x0F0)
#define CLK_SELECT (0x138)
#define HSCLK_SEL (0x13C)
#define CORECLK_DIV_MODE0 (0x148)
#define CORE_CLK_EN (0x154)
#define C_READY_STATUS (0x158)
#define SVS_MODE_CLK_SEL (0x164)
/* Tx Channel PHY registers */
#define PHY_TX_EMP_POST1_LVL(n) ((((n) * 0x200) + 0x400) + 0x000)
#define PHY_TX_INTERFACE_SELECT_TX_BAND(n) ((((n) * 0x200) + 0x400) + 0x008)
#define PHY_TX_CLKBUF_TERM_ENABLE(n) ((((n) * 0x200) + 0x400) + 0x00C)
#define PHY_TX_DRV_LVL_RES_CODE_OFFSET(n) ((((n) * 0x200) + 0x400) + 0x014)
#define PHY_TX_DRV_LVL(n) ((((n) * 0x200) + 0x400) + 0x018)
#define PHY_TX_LANE_CONFIG(n) ((((n) * 0x200) + 0x400) + 0x01C)
#define PHY_TX_PRE_DRIVER_1(n) ((((n) * 0x200) + 0x400) + 0x024)
#define PHY_TX_PRE_DRIVER_2(n) ((((n) * 0x200) + 0x400) + 0x028)
#define PHY_TX_LANE_MODE(n) ((((n) * 0x200) + 0x400) + 0x02C)
/* HDMI PHY registers */
#define PHY_CFG (0x00)
#define PHY_PD_CTL (0x04)
#define PHY_MODE (0x10)
#define PHY_CLOCK (0x5C)
#define PHY_CMN_CTRL (0x68)
#define PHY_STATUS (0xB4)
#define HDMI_BIT_CLK_TO_PIX_CLK_RATIO 10
#define HDMI_MHZ_TO_HZ 1000000
#define HDMI_HZ_TO_MHZ 1000000
#define HDMI_REF_CLOCK_MHZ 19.2
#define HDMI_REF_CLOCK_HZ (HDMI_REF_CLOCK_MHZ * 1000000)
#define HDMI_VCO_MIN_RATE_HZ 25000000
#define HDMI_VCO_MAX_RATE_HZ 600000000
struct 8998_reg_cfg {
u32 tx_band;
u32 svs_mode_clk_sel;
u32 hsclk_sel;
u32 lock_cmp_en;
u32 cctrl_mode0;
u32 rctrl_mode0;
u32 cpctrl_mode0;
u32 dec_start_mode0;
u32 div_frac_start1_mode0;
u32 div_frac_start2_mode0;
u32 div_frac_start3_mode0;
u32 integloop_gain0_mode0;
u32 integloop_gain1_mode0;
u32 lock_cmp1_mode0;
u32 lock_cmp2_mode0;
u32 lock_cmp3_mode0;
u32 ssc_per1;
u32 ssc_per2;
u32 ssc_step_size1;
u32 ssc_step_size2;
u32 core_clk_en;
u32 coreclk_div_mode0;
u32 phy_mode;
u32 vco_freq;
u32 hsclk_divsel;
u32 vco_ratio;
u32 ssc_en_center;
u32 l0_tx_drv_lvl;
u32 l0_tx_emp_post1_lvl;
u32 l1_tx_drv_lvl;
u32 l1_tx_emp_post1_lvl;
u32 l2_tx_drv_lvl;
u32 l2_tx_emp_post1_lvl;
u32 l3_tx_drv_lvl;
u32 l3_tx_emp_post1_lvl;
u32 l0_pre_driver_1;
u32 l0_pre_driver_2;
u32 l1_pre_driver_1;
u32 l1_pre_driver_2;
u32 l2_pre_driver_1;
u32 l2_pre_driver_2;
u32 l3_pre_driver_1;
u32 l3_pre_driver_2;
bool debug;
};
static void hdmi_8998_get_div(struct 8998_reg_cfg * cfg, unsigned long pclk)
{
u32 const ratio_list[] = {1, 2, 3, 4, 5, 6,
9, 10, 12, 15, 25};
u32 const band_list[] = {0, 1, 2, 3};
u32 const sz_ratio = ARRAY_SIZE(ratio_list);
u32 const sz_band = ARRAY_SIZE(band_list);
u32 const min_freq = 8000, max_freq = 12000;
u32 const cmp_cnt = 1024;
u32 const th_min = 500, th_max = 1000;
u64 bit_clk = pclk * HDMI_BIT_CLK_TO_PIX_CLK_RATIO;
u32 half_rate_mode = 0;
u32 freq_optimal, list_elements;
int optimal_index;
u32 i, j, k;
u32 freq_list[sz_ratio * sz_band];
u32 found_hsclk_divsel = 0, found_vco_ratio;
u32 found_tx_band_sel, found_vco_freq;
find_optimal_index:
freq_optimal = max_freq;
optimal_index = -1;
list_elements = 0;
for (i = 0; i < sz_ratio; i++) {
for (j = 0; j < sz_band; j++) {
u64 freq = (bit_clk / (1 << half_rate_mode));
freq *= (ratio_list[i] * (1 << band_list[j]));
do_div(freq, (u64) HDMI_MHZ_TO_HZ);
freq_list[list_elements++] = freq;
}
}
for (k = 0; k < ARRAY_SIZE(freq_list); k++) {
u32 const clks_pll_div = 2, core_clk_div = 5;
u32 const rng1 = 16, rng2 = 8;
u32 core_clk, rvar1;
u32 th1, th2;
core_clk = (((freq_list[k] /
ratio_list[k / sz_band]) /
clks_pll_div) / core_clk_div);
rvar1 = HDMI_REF_CLOCK_HZ / cmp_cnt;
rvar1 *= rng1;
rvar1 /= core_clk;
th1 = rvar1;
rvar1 = HDMI_REF_CLOCK_HZ / cmp_cnt;
rvar1 *= rng2;
rvar1 /= core_clk;
th2 = rvar1;
if (freq_list[k] >= min_freq &&
freq_list[k] <= max_freq) {
if ((th1 >= th_min && th1 <= th_max) ||
(th2 >= th_min && th2 <= th_max)) {
if (freq_list[k] <= freq_optimal) {
freq_optimal = freq_list[k];
optimal_index = k;
}
}
}
}
if (optimal_index == -1) {
if (!half_rate_mode) {
half_rate_mode = 1;
goto find_optimal_index;
} else {
/* set to default values */
found_vco_freq = max_freq;
found_hsclk_divsel = 0;
found_vco_ratio = 2;
found_tx_band_sel = 0;
pr_err("Config error for pclk %ld\n", pclk);
}
} else {
found_vco_ratio = ratio_list[optimal_index / sz_band];
found_tx_band_sel = band_list[optimal_index % sz_band];
found_vco_freq = freq_optimal;
}
switch (found_vco_ratio) {
case 1:
found_hsclk_divsel = 15;
break;
case 2:
found_hsclk_divsel = 0;
break;
case 3:
found_hsclk_divsel = 4;
break;
case 4:
found_hsclk_divsel = 8;
break;
case 5:
found_hsclk_divsel = 12;
break;
case 6:
found_hsclk_divsel = 1;
break;
case 9:
found_hsclk_divsel = 5;
break;
case 10:
found_hsclk_divsel = 2;
break;
case 12:
found_hsclk_divsel = 9;
break;
case 15:
found_hsclk_divsel = 13;
break;
case 25:
found_hsclk_divsel = 14;
break;
}
pr_debug("found_vco_freq=%d\n", found_vco_freq);
pr_debug("found_hsclk_divsel=%d\n", found_hsclk_divsel);
pr_debug("found_vco_ratio=%d\n", found_vco_ratio);
pr_debug("found_tx_band_sel=%d\n", found_tx_band_sel);
pr_debug("half_rate_mode=%d\n", half_rate_mode);
pr_debug("optimal_index=%d\n", optimal_index);
cfg->vco_freq = found_vco_freq;
cfg->hsclk_divsel = found_hsclk_divsel;
cfg->vco_ratio = found_vco_ratio;
cfg->tx_band = found_tx_band_sel;
}
static int hdmi_8998_config_phy(unsigned long rate,
struct 8998_reg_cfg * cfg)
{
u64 const high_freq_bit_clk_threshold = 3400000000UL;
u64 const dig_freq_bit_clk_threshold = 1500000000UL;
u64 const mid_freq_bit_clk_threshold = 750000000;
u64 fdata, tmds_clk;
u64 pll_div = 4 * HDMI_REF_CLOCK_HZ;
u64 bclk;
u64 vco_freq_mhz;
u64 hsclk_sel, dec_start, div_frac_start;
u64 rem;
u64 cpctrl, rctrl, cctrl;
u64 integloop_gain;
u32 digclk_divsel;
u32 tmds_bclk_ratio;
u64 cmp_rng, cmp_cnt = 1024, pll_cmp;
bool gen_ssc = false;
bclk = rate * HDMI_BIT_CLK_TO_PIX_CLK_RATIO;
if (bclk > high_freq_bit_clk_threshold) {
tmds_clk = rate / 4;
tmds_bclk_ratio = 1;
} else {
tmds_clk = rate;
tmds_bclk_ratio = 0;
}
hdmi_8998_get_div(cfg, rate);
vco_freq_mhz = cfg->vco_freq * (u64) HDMI_HZ_TO_MHZ;
fdata = cfg->vco_freq;
do_div(fdata, cfg->vco_ratio);
hsclk_sel = cfg->hsclk_divsel;
dec_start = vco_freq_mhz;
do_div(dec_start, pll_div);
div_frac_start = vco_freq_mhz * (1 << 20);
rem = do_div(div_frac_start, pll_div);
div_frac_start -= (dec_start * (1 << 20));
if (rem > (pll_div >> 1))
div_frac_start++;
if ((div_frac_start != 0) || gen_ssc) {
cpctrl = 0x8;
rctrl = 0x16;
cctrl = 0x34;
} else {
cpctrl = 0x30;
rctrl = 0x18;
cctrl = 0x2;
}
digclk_divsel = (bclk > dig_freq_bit_clk_threshold) ? 0x1 : 0x2;
integloop_gain = ((div_frac_start != 0) ||
gen_ssc) ? 0x3F : 0xC4;
integloop_gain <<= digclk_divsel;
integloop_gain = (integloop_gain <= 2046 ? integloop_gain : 0x7FE);
cmp_rng = gen_ssc ? 0x40 : 0x10;
pll_cmp = cmp_cnt * fdata;
rem = do_div(pll_cmp, (u64)(HDMI_REF_CLOCK_MHZ * 10));
if (rem > ((u64)(HDMI_REF_CLOCK_MHZ * 10) >> 1))
pll_cmp++;
pll_cmp = pll_cmp - 1;
pr_debug("VCO_FREQ = %u\n", cfg->vco_freq);
pr_debug("FDATA = %llu\n", fdata);
pr_debug("DEC_START = %llu\n", dec_start);
pr_debug("DIV_FRAC_START = %llu\n", div_frac_start);
pr_debug("CPCTRL = %llu\n", cpctrl);
pr_debug("RCTRL = %llu\n", rctrl);
pr_debug("CCTRL = %llu\n", cctrl);
pr_debug("DIGCLK_DIVSEL = %u\n", digclk_divsel);
pr_debug("INTEGLOOP_GAIN = %llu\n", integloop_gain);
pr_debug("CMP_RNG = %llu\n", cmp_rng);
pr_debug("PLL_CMP = %llu\n", pll_cmp);
cfg->svs_mode_clk_sel = (digclk_divsel & 0xFF);
cfg->hsclk_sel = (0x20 | hsclk_sel);
cfg->lock_cmp_en = (gen_ssc ? 0x4 : 0x0);
cfg->cctrl_mode0 = (cctrl & 0xFF);
cfg->rctrl_mode0 = (rctrl & 0xFF);
cfg->cpctrl_mode0 = (cpctrl & 0xFF);
cfg->dec_start_mode0 = (dec_start & 0xFF);
cfg->div_frac_start1_mode0 = (div_frac_start & 0xFF);
cfg->div_frac_start2_mode0 = ((div_frac_start & 0xFF00) >> 8);
cfg->div_frac_start3_mode0 = ((div_frac_start & 0xF0000) >> 16);
cfg->integloop_gain0_mode0 = (integloop_gain & 0xFF);
cfg->integloop_gain1_mode0 = (integloop_gain & 0xF00) >> 8;
cfg->lock_cmp1_mode0 = (pll_cmp & 0xFF);
cfg->lock_cmp2_mode0 = ((pll_cmp & 0xFF00) >> 8);
cfg->lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16);
cfg->ssc_per1 = 0;
cfg->ssc_per2 = 0;
cfg->ssc_step_size1 = 0;
cfg->ssc_step_size2 = 0;
cfg->core_clk_en = 0x2C;
cfg->coreclk_div_mode0 = 0x5;
cfg->phy_mode = (tmds_bclk_ratio ? 0x5 : 0x4);
cfg->ssc_en_center = 0x0;
if (bclk > high_freq_bit_clk_threshold) {
cfg->l0_tx_drv_lvl = 0xA;
cfg->l0_tx_emp_post1_lvl = 0x3;
cfg->l1_tx_drv_lvl = 0xA;
cfg->l1_tx_emp_post1_lvl = 0x3;
cfg->l2_tx_drv_lvl = 0xA;
cfg->l2_tx_emp_post1_lvl = 0x3;
cfg->l3_tx_drv_lvl = 0x8;
cfg->l3_tx_emp_post1_lvl = 0x3;
cfg->l0_pre_driver_1 = 0x0;
cfg->l0_pre_driver_2 = 0x1C;
cfg->l1_pre_driver_1 = 0x0;
cfg->l1_pre_driver_2 = 0x1C;
cfg->l2_pre_driver_1 = 0x0;
cfg->l2_pre_driver_2 = 0x1C;
cfg->l3_pre_driver_1 = 0x0;
cfg->l3_pre_driver_2 = 0x0;
} else if (bclk > dig_freq_bit_clk_threshold) {
cfg->l0_tx_drv_lvl = 0x9;
cfg->l0_tx_emp_post1_lvl = 0x3;
cfg->l1_tx_drv_lvl = 0x9;
cfg->l1_tx_emp_post1_lvl = 0x3;
cfg->l2_tx_drv_lvl = 0x9;
cfg->l2_tx_emp_post1_lvl = 0x3;
cfg->l3_tx_drv_lvl = 0x8;
cfg->l3_tx_emp_post1_lvl = 0x3;
cfg->l0_pre_driver_1 = 0x0;
cfg->l0_pre_driver_2 = 0x16;
cfg->l1_pre_driver_1 = 0x0;
cfg->l1_pre_driver_2 = 0x16;
cfg->l2_pre_driver_1 = 0x0;
cfg->l2_pre_driver_2 = 0x16;
cfg->l3_pre_driver_1 = 0x0;
cfg->l3_pre_driver_2 = 0x0;
} else if (bclk > mid_freq_bit_clk_threshold) {
cfg->l0_tx_drv_lvl = 0x9;
cfg->l0_tx_emp_post1_lvl = 0x3;
cfg->l1_tx_drv_lvl = 0x9;
cfg->l1_tx_emp_post1_lvl = 0x3;
cfg->l2_tx_drv_lvl = 0x9;
cfg->l2_tx_emp_post1_lvl = 0x3;
cfg->l3_tx_drv_lvl = 0x8;
cfg->l3_tx_emp_post1_lvl = 0x3;
cfg->l0_pre_driver_1 = 0x0;
cfg->l0_pre_driver_2 = 0x0E;
cfg->l1_pre_driver_1 = 0x0;
cfg->l1_pre_driver_2 = 0x0E;
cfg->l2_pre_driver_1 = 0x0;
cfg->l2_pre_driver_2 = 0x0E;
cfg->l3_pre_driver_1 = 0x0;
cfg->l3_pre_driver_2 = 0x0;
} else {
cfg->l0_tx_drv_lvl = 0x0;
cfg->l0_tx_emp_post1_lvl = 0x0;
cfg->l1_tx_drv_lvl = 0x0;
cfg->l1_tx_emp_post1_lvl = 0x0;
cfg->l2_tx_drv_lvl = 0x0;
cfg->l2_tx_emp_post1_lvl = 0x0;
cfg->l3_tx_drv_lvl = 0x0;
cfg->l3_tx_emp_post1_lvl = 0x0;
cfg->l0_pre_driver_1 = 0x0;
cfg->l0_pre_driver_2 = 0x01;
cfg->l1_pre_driver_1 = 0x0;
cfg->l1_pre_driver_2 = 0x01;
cfg->l2_pre_driver_1 = 0x0;
cfg->l2_pre_driver_2 = 0x01;
cfg->l3_pre_driver_1 = 0x0;
cfg->l3_pre_driver_2 = 0x0;
}
return 0;
}
static int hdmi_8998_pll_set_clk_rate(struct clk *c, unsigned long rate)
{
int rc = 0;
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c);
struct mdss_pll_resources *io = vco->priv;
struct 8998_reg_cfg cfg = {0};
void __iomem *phy = io->phy_base, *pll = io->pll_base;
rc = hdmi_8998_config_phy(rate, &cfg);
if (rc) {
pr_err("rate calculation failed\n, rc=%d\n", rc);
return rc;
}
_W(phy, PHY_PD_CTL, 0x0);
udelay(500);
_W(phy, PHY_PD_CTL, 0x1);
_W(pll, RESETSM_CNTRL, 0x20);
_W(phy, PHY_CMN_CTRL, 0x6);
_W(pll, PHY_TX_INTERFACE_SELECT_TX_BAND(0), cfg.tx_band);
_W(pll, PHY_TX_INTERFACE_SELECT_TX_BAND(1), cfg.tx_band);
_W(pll, PHY_TX_INTERFACE_SELECT_TX_BAND(2), cfg.tx_band);
_W(pll, PHY_TX_INTERFACE_SELECT_TX_BAND(3), cfg.tx_band);
_W(pll, PHY_TX_CLKBUF_TERM_ENABLE(0), 0x1);
_W(pll, PHY_TX_LANE_MODE(0), 0x20);
_W(pll, PHY_TX_LANE_MODE(1), 0x20);
_W(pll, PHY_TX_LANE_MODE(2), 0x20);
_W(pll, PHY_TX_LANE_MODE(3), 0x20);
_W(pll, PHY_TX_CLKBUF_TERM_ENABLE(1), 0x1);
_W(pll, PHY_TX_CLKBUF_TERM_ENABLE(2), 0x1);
_W(pll, PHY_TX_CLKBUF_TERM_ENABLE(3), 0x1);
_W(pll, SYSCLK_BUF_ENABLE, 0x2);
_W(pll, BIAS_EN_CLKBUFLR_EN, 0xB);
_W(pll, SYSCLK_EN_SEL, 0x37);
_W(pll, SYS_CLK_CTRL, 0x2);
_W(pll, CLK_ENABLE1, 0xE);
_W(pll, PLL_IVCO, 0xF);
_W(pll, VCO_TUNE_CTRL, 0x0);
_W(pll, SVS_MODE_CLK_SEL, cfg.svs_mode_clk_sel);
_W(pll, CLK_SELECT, 0x30);
_W(pll, HSCLK_SEL, cfg.hsclk_sel);
_W(pll, LOCK_CMP_EN, cfg.lock_cmp_en);
_W(pll, PLL_CCTRL_MODE0, cfg.cctrl_mode0);
_W(pll, PLL_RCTRL_MODE0, cfg.rctrl_mode0);
_W(pll, CP_CTRL_MODE0, cfg.cpctrl_mode0);
_W(pll, DEC_START_MODE0, cfg.dec_start_mode0);
_W(pll, DIV_FRAC_START1_MODE0, cfg.div_frac_start1_mode0);
_W(pll, DIV_FRAC_START2_MODE0, cfg.div_frac_start2_mode0);
_W(pll, DIV_FRAC_START3_MODE0, cfg.div_frac_start3_mode0);
_W(pll, INTEGLOOP_GAIN0_MODE0, cfg.integloop_gain0_mode0);
_W(pll, INTEGLOOP_GAIN1_MODE0, cfg.integloop_gain1_mode0);
_W(pll, LOCK_CMP1_MODE0, cfg.lock_cmp1_mode0);
_W(pll, LOCK_CMP2_MODE0, cfg.lock_cmp2_mode0);
_W(pll, LOCK_CMP3_MODE0, cfg.lock_cmp3_mode0);
_W(pll, VCO_TUNE_MAP, 0x0);
_W(pll, CORE_CLK_EN, cfg.core_clk_en);
_W(pll, CORECLK_DIV_MODE0, cfg.coreclk_div_mode0);
_W(pll, PHY_TX_DRV_LVL(0), cfg.l0_tx_drv_lvl);
_W(pll, PHY_TX_DRV_LVL(1), cfg.l1_tx_drv_lvl);
_W(pll, PHY_TX_DRV_LVL(2), cfg.l2_tx_drv_lvl);
_W(pll, PHY_TX_DRV_LVL(3), cfg.l3_tx_drv_lvl);
_W(pll, PHY_TX_EMP_POST1_LVL(0), cfg.l0_tx_emp_post1_lvl);
_W(pll, PHY_TX_EMP_POST1_LVL(1), cfg.l1_tx_emp_post1_lvl);
_W(pll, PHY_TX_EMP_POST1_LVL(2), cfg.l2_tx_emp_post1_lvl);
_W(pll, PHY_TX_EMP_POST1_LVL(3), cfg.l3_tx_emp_post1_lvl);
_W(pll, PHY_TX_PRE_DRIVER_1(0), cfg.l0_pre_driver_1);
_W(pll, PHY_TX_PRE_DRIVER_1(1), cfg.l1_pre_driver_1);
_W(pll, PHY_TX_PRE_DRIVER_1(2), cfg.l2_pre_driver_1);
_W(pll, PHY_TX_PRE_DRIVER_1(3), cfg.l3_pre_driver_1);
_W(pll, PHY_TX_PRE_DRIVER_2(0), cfg.l0_pre_driver_2);
_W(pll, PHY_TX_PRE_DRIVER_2(1), cfg.l1_pre_driver_2);
_W(pll, PHY_TX_PRE_DRIVER_2(2), cfg.l2_pre_driver_2);
_W(pll, PHY_TX_PRE_DRIVER_2(3), cfg.l3_pre_driver_2);
_W(pll, PHY_TX_DRV_LVL_RES_CODE_OFFSET(0), 0x0);
_W(pll, PHY_TX_DRV_LVL_RES_CODE_OFFSET(1), 0x0);
_W(pll, PHY_TX_DRV_LVL_RES_CODE_OFFSET(2), 0x0);
_W(pll, PHY_TX_DRV_LVL_RES_CODE_OFFSET(3), 0x0);
_W(phy, PHY_MODE, cfg.phy_mode);
_W(pll, PHY_TX_LANE_CONFIG(0), 0x10);
_W(pll, PHY_TX_LANE_CONFIG(1), 0x10);
_W(pll, PHY_TX_LANE_CONFIG(2), 0x10);
_W(pll, PHY_TX_LANE_CONFIG(3), 0x10);
/* Ensure all registers are flushed to hardware */
wmb();
return 0;
}
static int hdmi_8998_pll_lock_status(struct mdss_pll_resources *io)
{
u32 const delay_us = 100;
u32 const timeout_us = 5000;
u32 status;
int rc = 0;
void __iomem *pll = io->pll_base;
rc = mdss_pll_resource_enable(io, true);
if (rc) {
pr_err("pll resource can't be enabled\n");
return rc;
}
rc = readl_poll_timeout_atomic(pll + C_READY_STATUS,
status,
((status & BIT(0)) > 0),
delay_us,
timeout_us);
if (rc)
pr_err("HDMI PLL(%d) lock failed, status=0x%08x\n",
io->index, status);
else
pr_debug("HDMI PLL(%d) lock passed, status=0x%08x\n",
io->index, status);
mdss_pll_resource_enable(io, false);
return rc;
}
static int hdmi_8998_phy_ready_status(struct mdss_pll_resources *io)
{
u32 const delay_us = 100;
u32 const timeout_us = 5000;
u32 status;
int rc = 0;
void __iomem *phy = io->phy_base;
rc = mdss_pll_resource_enable(io, true);
if (rc) {
pr_err("pll resource can't be enabled\n");
return rc;
}
rc = readl_poll_timeout_atomic(phy + PHY_STATUS,
status,
((status & BIT(0)) > 0),
delay_us,
timeout_us);
if (rc)
pr_err("HDMI PHY(%d) not ready, status=0x%08x\n",
io->index, status);
else
pr_debug("HDMI PHY(%d) ready, status=0x%08x\n",
io->index, status);
mdss_pll_resource_enable(io, false);
return rc;
}
static int hdmi_8998_vco_set_rate(struct clk *c, unsigned long rate)
{
int rc = 0;
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c);
struct mdss_pll_resources *io = vco->priv;
rc = mdss_pll_resource_enable(io, true);
if (rc) {
pr_err("pll resource enable failed, rc=%d\n", rc);
return rc;
}
if (io->pll_on)
goto error;
rc = hdmi_8998_pll_set_clk_rate(c, rate);
if (rc) {
pr_err("failed to set clk rate, rc=%d\n", rc);
goto error;
}
vco->rate = rate;
vco->rate_set = true;
error:
(void)mdss_pll_resource_enable(io, false);
return rc;
}
static long hdmi_8998_vco_round_rate(struct clk *c, unsigned long rate)
{
unsigned long rrate = rate;
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c);
if (rate < vco->min_rate)
rrate = vco->min_rate;
if (rate > vco->max_rate)
rrate = vco->max_rate;
return rrate;
}
static int hdmi_8998_pll_enable(struct clk *c)
{
int rc = 0;
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c);
struct mdss_pll_resources *io = vco->priv;
void __iomem *phy = io->phy_base, *pll = io->pll_base;
_W(phy, PHY_CFG, 0x1);
udelay(100);
_W(phy, PHY_CFG, 0x59);
udelay(100);
_W(phy, PHY_CLOCK, 0x6);
/* Ensure all registers are flushed to hardware */
wmb();
rc = hdmi_8998_pll_lock_status(io);
if (rc) {
pr_err("PLL not locked, rc=%d\n", rc);
return rc;
}
_W(pll, PHY_TX_LANE_CONFIG(0), 0x1F);
_W(pll, PHY_TX_LANE_CONFIG(1), 0x1F);
_W(pll, PHY_TX_LANE_CONFIG(2), 0x1F);
_W(pll, PHY_TX_LANE_CONFIG(3), 0x1F);
/* Ensure all registers are flushed to hardware */
wmb();
rc = hdmi_8998_phy_ready_status(io);
if (rc) {
pr_err("PHY NOT READY, rc=%d\n", rc);
return rc;
}
_W(phy, PHY_CFG, 0x58);
udelay(1);
_W(phy, PHY_CFG, 0x59);
/* Ensure all registers are flushed to hardware */
wmb();
io->pll_on = true;
return rc;
}
static int hdmi_8998_vco_prepare(struct clk *c)
{
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c);
struct mdss_pll_resources *io = vco->priv;
int rc = 0;
if (!io) {
pr_err("hdmi pll resources are not available\n");
return -EINVAL;
}
rc = mdss_pll_resource_enable(io, true);
if (rc) {
pr_err("pll resource enable failed, rc=%d\n", rc);
return rc;
}
if (!vco->rate_set && vco->rate) {
rc = hdmi_8998_pll_set_clk_rate(c, vco->rate);
if (rc) {
pr_err("set rate failed, rc=%d\n", rc);
goto error;
}
}
rc = hdmi_8998_pll_enable(c);
if (rc)
pr_err("pll enabled failed, rc=%d\n", rc);
error:
if (rc)
mdss_pll_resource_enable(io, false);
return rc;
}
static void hdmi_8998_pll_disable(struct hdmi_pll_vco_clk *vco)
{
struct mdss_pll_resources *io = vco->priv;
void __iomem *phy = io->phy_base;
if (!io->pll_on)
return;
_W(phy, PHY_PD_CTL, 0x0);
/* Ensure all registers are flushed to hardware */
wmb();
vco->rate_set = false;
io->handoff_resources = false;
io->pll_on = false;
}
static void hdmi_8998_vco_unprepare(struct clk *c)
{
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c);
struct mdss_pll_resources *io = vco->priv;
if (!io) {
pr_err("HDMI pll resources not available\n");
return;
}
hdmi_8998_pll_disable(vco);
mdss_pll_resource_enable(io, false);
}
static enum handoff hdmi_8998_vco_handoff(struct clk *c)
{
enum handoff ret = HANDOFF_DISABLED_CLK;
struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c);
struct mdss_pll_resources *io = vco->priv;
if (mdss_pll_resource_enable(io, true)) {
pr_err("pll resource can't be enabled\n");
return ret;
}
io->handoff_resources = true;
if (_R(io->pll_base, C_READY_STATUS) & BIT(0) &&
_R(io->phy_base, PHY_STATUS) & BIT(0)) {
io->pll_on = true;
/* TODO: calculate rate based on the phy/pll register values. */
ret = HANDOFF_ENABLED_CLK;
} else {
io->handoff_resources = false;
mdss_pll_resource_enable(io, false);
pr_debug("%s: PHY/PLL not ready\n", __func__);
}
pr_debug("done, ret=%d\n", ret);
return ret;
}
static const struct clk_ops hdmi_8998_vco_clk_ops = {
.set_rate = hdmi_8998_vco_set_rate,
.round_rate = hdmi_8998_vco_round_rate,
.prepare = hdmi_8998_vco_prepare,
.unprepare = hdmi_8998_vco_unprepare,
.handoff = hdmi_8998_vco_handoff,
};
static struct hdmi_pll_vco_clk hdmi_vco_clk = {
.min_rate = HDMI_VCO_MIN_RATE_HZ,
.max_rate = HDMI_VCO_MAX_RATE_HZ,
.c = {
.dbg_name = "hdmi_8998_vco_clk",
.ops = &hdmi_8998_vco_clk_ops,
CLK_INIT(hdmi_vco_clk.c),
},
};
static struct clk_lookup hdmipllcc_8998[] = {
CLK_LIST(hdmi_vco_clk),
};
int hdmi_8998_pll_clock_register(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc = 0;
hdmi_vco_clk.priv = pll_res;
rc = of_msm_clock_register(pdev->dev.of_node, hdmipllcc_8998,
ARRAY_SIZE(hdmipllcc_8998));
if (rc) {
pr_err("clock register failed, rc=%d\n", rc);
return rc;
}
return rc;
}

View File

@@ -1,424 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include "pll_drv.h"
#include "dsi_pll.h"
#include "dp_pll.h"
#include "hdmi_pll.h"
int mdss_pll_resource_enable(struct mdss_pll_resources *pll_res, bool enable)
{
int rc = 0;
int changed = 0;
if (!pll_res) {
pr_err("Invalid input parameters\n");
return -EINVAL;
}
/*
* Don't turn off resources during handoff or add more than
* 1 refcount.
*/
if (pll_res->handoff_resources &&
(!enable || (enable & pll_res->resource_enable))) {
pr_debug("Do not turn on/off pll resources during handoff case\n");
return rc;
}
if (enable) {
if (pll_res->resource_ref_cnt == 0)
changed++;
pll_res->resource_ref_cnt++;
} else {
if (pll_res->resource_ref_cnt) {
pll_res->resource_ref_cnt--;
if (pll_res->resource_ref_cnt == 0)
changed++;
} else {
pr_err("PLL Resources already OFF\n");
}
}
if (changed) {
rc = mdss_pll_util_resource_enable(pll_res, enable);
if (rc)
pr_err("Resource update failed rc=%d\n", rc);
else
pll_res->resource_enable = enable;
}
return rc;
}
static int mdss_pll_resource_init(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc = 0;
struct dss_module_power *mp = &pll_res->mp;
rc = msm_dss_config_vreg(&pdev->dev,
mp->vreg_config, mp->num_vreg, 1);
if (rc) {
pr_err("Vreg config failed rc=%d\n", rc);
goto vreg_err;
}
rc = msm_dss_get_clk(&pdev->dev, mp->clk_config, mp->num_clk);
if (rc) {
pr_err("Clock get failed rc=%d\n", rc);
goto clk_err;
}
return rc;
clk_err:
msm_dss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg, 0);
vreg_err:
return rc;
}
static void mdss_pll_resource_deinit(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
struct dss_module_power *mp = &pll_res->mp;
msm_dss_put_clk(mp->clk_config, mp->num_clk);
msm_dss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg, 0);
}
static void mdss_pll_resource_release(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
struct dss_module_power *mp = &pll_res->mp;
mp->num_vreg = 0;
mp->num_clk = 0;
}
static int mdss_pll_resource_parse(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc = 0;
const char *compatible_stream;
rc = mdss_pll_util_resource_parse(pdev, pll_res);
if (rc) {
pr_err("Failed to parse the resources rc=%d\n", rc);
goto end;
}
compatible_stream = of_get_property(pdev->dev.of_node,
"compatible", NULL);
if (!compatible_stream) {
pr_err("Failed to parse the compatible stream\n");
goto err;
}
if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_10nm"))
pll_res->pll_interface_type = MDSS_DSI_PLL_10NM;
if (!strcmp(compatible_stream, "qcom,mdss_dp_pll_10nm"))
pll_res->pll_interface_type = MDSS_DP_PLL_10NM;
else if (!strcmp(compatible_stream, "qcom,mdss_dp_pll_7nm"))
pll_res->pll_interface_type = MDSS_DP_PLL_7NM;
else if (!strcmp(compatible_stream, "qcom,mdss_dp_pll_7nm_v2"))
pll_res->pll_interface_type = MDSS_DP_PLL_7NM_V2;
else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_7nm"))
pll_res->pll_interface_type = MDSS_DSI_PLL_7NM;
else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_7nm_v2"))
pll_res->pll_interface_type = MDSS_DSI_PLL_7NM_V2;
else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_7nm_v4_1"))
pll_res->pll_interface_type = MDSS_DSI_PLL_7NM_V4_1;
else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_28lpm"))
pll_res->pll_interface_type = MDSS_DSI_PLL_28LPM;
else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_14nm"))
pll_res->pll_interface_type = MDSS_DSI_PLL_14NM;
else if (!strcmp(compatible_stream, "qcom,mdss_dp_pll_14nm"))
pll_res->pll_interface_type = MDSS_DP_PLL_14NM;
else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_28lpm"))
pll_res->pll_interface_type = MDSS_HDMI_PLL_28LPM;
else
goto err;
return rc;
err:
mdss_pll_resource_release(pdev, pll_res);
end:
return rc;
}
static int mdss_pll_clock_register(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc;
switch (pll_res->pll_interface_type) {
case MDSS_DSI_PLL_10NM:
rc = dsi_pll_clock_register_10nm(pdev, pll_res);
break;
case MDSS_DP_PLL_10NM:
rc = dp_pll_clock_register_10nm(pdev, pll_res);
break;
case MDSS_DSI_PLL_7NM:
case MDSS_DSI_PLL_7NM_V2:
case MDSS_DSI_PLL_7NM_V4_1:
rc = dsi_pll_clock_register_7nm(pdev, pll_res);
break;
case MDSS_DP_PLL_7NM:
case MDSS_DP_PLL_7NM_V2:
rc = dp_pll_clock_register_7nm(pdev, pll_res);
break;
case MDSS_DSI_PLL_28LPM:
rc = dsi_pll_clock_register_28lpm(pdev, pll_res);
break;
case MDSS_DSI_PLL_14NM:
rc = dsi_pll_clock_register_14nm(pdev, pll_res);
break;
case MDSS_DP_PLL_14NM:
rc = dp_pll_clock_register_14nm(pdev, pll_res);
break;
case MDSS_HDMI_PLL_28LPM:
rc = hdmi_pll_clock_register_28lpm(pdev, pll_res);
break;
case MDSS_UNKNOWN_PLL:
default:
rc = -EINVAL;
break;
}
if (rc)
pr_err("Pll ndx=%d clock register failed rc=%d\n",
pll_res->index, rc);
return rc;
}
static inline int mdss_pll_get_ioresurces(struct platform_device *pdev,
void __iomem **regmap, char *resource_name)
{
int rc = 0;
struct resource *rsc = platform_get_resource_byname(pdev,
IORESOURCE_MEM, resource_name);
if (rsc) {
if (!regmap)
return -ENOMEM;
*regmap = devm_ioremap(&pdev->dev,
rsc->start, resource_size(rsc));
if (!*regmap)
return -ENOMEM;
}
return rc;
}
static int mdss_pll_probe(struct platform_device *pdev)
{
int rc = 0;
const char *label;
struct mdss_pll_resources *pll_res;
if (!pdev->dev.of_node) {
pr_err("MDSS pll driver only supports device tree probe\n");
return -ENOTSUPP;
}
label = of_get_property(pdev->dev.of_node, "label", NULL);
if (!label)
pr_info("MDSS pll label not specified\n");
else
pr_info("MDSS pll label = %s\n", label);
pll_res = devm_kzalloc(&pdev->dev, sizeof(struct mdss_pll_resources),
GFP_KERNEL);
if (!pll_res)
return -ENOMEM;
platform_set_drvdata(pdev, pll_res);
rc = of_property_read_u32(pdev->dev.of_node, "cell-index",
&pll_res->index);
if (rc) {
pr_err("Unable to get the cell-index rc=%d\n", rc);
pll_res->index = 0;
}
pll_res->ssc_en = of_property_read_bool(pdev->dev.of_node,
"qcom,dsi-pll-ssc-en");
if (pll_res->ssc_en) {
pr_info("%s: label=%s PLL SSC enabled\n", __func__, label);
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,ssc-frequency-hz", &pll_res->ssc_freq);
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,ssc-ppm", &pll_res->ssc_ppm);
pll_res->ssc_center = false;
label = of_get_property(pdev->dev.of_node,
"qcom,dsi-pll-ssc-mode", NULL);
if (label && !strcmp(label, "center-spread"))
pll_res->ssc_center = true;
}
if (mdss_pll_get_ioresurces(pdev, &pll_res->pll_base, "pll_base")) {
pr_err("Unable to remap pll base resources\n");
return -ENOMEM;
}
pr_debug("%s: ndx=%d base=%p\n", __func__,
pll_res->index, pll_res->pll_base);
rc = mdss_pll_resource_parse(pdev, pll_res);
if (rc) {
pr_err("Pll resource parsing from dt failed rc=%d\n", rc);
return rc;
}
if (mdss_pll_get_ioresurces(pdev, &pll_res->phy_base, "phy_base")) {
pr_err("Unable to remap pll phy base resources\n");
return -ENOMEM;
}
if (mdss_pll_get_ioresurces(pdev, &pll_res->dyn_pll_base,
"dynamic_pll_base")) {
pr_err("Unable to remap dynamic pll base resources\n");
return -ENOMEM;
}
if (mdss_pll_get_ioresurces(pdev, &pll_res->ln_tx0_base,
"ln_tx0_base")) {
pr_err("Unable to remap Lane TX0 base resources\n");
return -ENOMEM;
}
if (mdss_pll_get_ioresurces(pdev, &pll_res->ln_tx0_tran_base,
"ln_tx0_tran_base")) {
pr_err("Unable to remap Lane TX0 base resources\n");
return -ENOMEM;
}
if (mdss_pll_get_ioresurces(pdev, &pll_res->ln_tx0_vmode_base,
"ln_tx0_vmode_base")) {
pr_err("Unable to remap Lane TX0 base resources\n");
return -ENOMEM;
}
if (mdss_pll_get_ioresurces(pdev, &pll_res->ln_tx1_base,
"ln_tx1_base")) {
pr_err("Unable to remap Lane TX1 base resources\n");
return -ENOMEM;
}
if (mdss_pll_get_ioresurces(pdev, &pll_res->ln_tx1_tran_base,
"ln_tx1_tran_base")) {
pr_err("Unable to remap Lane TX1 base resources\n");
return -ENOMEM;
}
if (mdss_pll_get_ioresurces(pdev, &pll_res->ln_tx1_vmode_base,
"ln_tx1_vmode_base")) {
pr_err("Unable to remap Lane TX1 base resources\n");
return -ENOMEM;
}
if (mdss_pll_get_ioresurces(pdev, &pll_res->gdsc_base, "gdsc_base")) {
pr_err("Unable to remap gdsc base resources\n");
return -ENOMEM;
}
rc = mdss_pll_resource_init(pdev, pll_res);
if (rc) {
pr_err("Pll ndx=%d resource init failed rc=%d\n",
pll_res->index, rc);
return rc;
}
rc = mdss_pll_clock_register(pdev, pll_res);
if (rc) {
pr_err("Pll ndx=%d clock register failed rc=%d\n",
pll_res->index, rc);
goto clock_register_error;
}
return rc;
clock_register_error:
mdss_pll_resource_deinit(pdev, pll_res);
return rc;
}
static int mdss_pll_remove(struct platform_device *pdev)
{
struct mdss_pll_resources *pll_res;
pll_res = platform_get_drvdata(pdev);
if (!pll_res) {
pr_err("Invalid PLL resource data\n");
return 0;
}
mdss_pll_resource_deinit(pdev, pll_res);
mdss_pll_resource_release(pdev, pll_res);
return 0;
}
static const struct of_device_id mdss_pll_dt_match[] = {
{.compatible = "qcom,mdss_dsi_pll_10nm"},
{.compatible = "qcom,mdss_dp_pll_10nm"},
{.compatible = "qcom,mdss_dsi_pll_7nm"},
{.compatible = "qcom,mdss_dsi_pll_7nm_v2"},
{.compatible = "qcom,mdss_dsi_pll_7nm_v4_1"},
{.compatible = "qcom,mdss_dp_pll_7nm"},
{.compatible = "qcom,mdss_dp_pll_7nm_v2"},
{.compatible = "qcom,mdss_dsi_pll_28lpm"},
{.compatible = "qcom,mdss_dsi_pll_14nm"},
{.compatible = "qcom,mdss_dp_pll_14nm"},
{},
};
MODULE_DEVICE_TABLE(of, mdss_clock_dt_match);
static struct platform_driver mdss_pll_driver = {
.probe = mdss_pll_probe,
.remove = mdss_pll_remove,
.driver = {
.name = "mdss_pll",
.of_match_table = mdss_pll_dt_match,
},
};
static int __init mdss_pll_driver_init(void)
{
int rc;
rc = platform_driver_register(&mdss_pll_driver);
if (rc)
pr_err("mdss_register_pll_driver() failed!\n");
return rc;
}
fs_initcall(mdss_pll_driver_init);
static void __exit mdss_pll_driver_deinit(void)
{
platform_driver_unregister(&mdss_pll_driver);
}
module_exit(mdss_pll_driver_deinit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("mdss pll driver");

View File

@@ -1,109 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
*/
#if !defined(_MDSS_PLL_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
#define _MDSS_PLL_TRACE_H_
#include <linux/stringify.h>
#include <linux/types.h>
#include <linux/tracepoint.h>
#undef TRACE_SYSTEM
#define TRACE_SYSTEM mdss_pll
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE pll_trace
TRACE_EVENT(mdss_pll_lock_start,
TP_PROTO(
u64 vco_cached_rate,
s64 vco_current_rate,
u32 cached_cfg0,
u32 cached_cfg1,
u32 cached_outdiv,
u32 resource_ref_cnt),
TP_ARGS(
vco_cached_rate,
vco_current_rate,
cached_cfg0,
cached_cfg1,
cached_outdiv,
resource_ref_cnt),
TP_STRUCT__entry(
__field(u64, vco_cached_rate)
__field(s64, vco_current_rate)
__field(u32, cached_cfg0)
__field(u32, cached_cfg1)
__field(u32, cached_outdiv)
__field(u32, resource_ref_cnt)
),
TP_fast_assign(
__entry->vco_cached_rate = vco_cached_rate;
__entry->vco_current_rate = vco_current_rate;
__entry->cached_cfg0 = cached_cfg0;
__entry->cached_cfg1 = cached_cfg1;
__entry->cached_outdiv = cached_outdiv;
__entry->resource_ref_cnt = resource_ref_cnt;
),
TP_printk(
"vco_cached_rate=%llu vco_current_rate=%lld cached_cfg0=%d cached_cfg1=%d cached_outdiv=%d resource_ref_cnt=%d",
__entry->vco_cached_rate,
__entry->vco_current_rate,
__entry->cached_cfg0,
__entry->cached_cfg1,
__entry->cached_outdiv,
__entry->resource_ref_cnt)
);
TRACE_EVENT(pll_tracing_mark_write,
TP_PROTO(int pid, const char *name, bool trace_begin),
TP_ARGS(pid, name, trace_begin),
TP_STRUCT__entry(
__field(int, pid)
__string(trace_name, name)
__field(bool, trace_begin)
),
TP_fast_assign(
__entry->pid = pid;
__assign_str(trace_name, name);
__entry->trace_begin = trace_begin;
),
TP_printk("%s|%d|%s", __entry->trace_begin ? "B" : "E",
__entry->pid, __get_str(trace_name))
)
TRACE_EVENT(mdss_pll_trace_counter,
TP_PROTO(int pid, char *name, int value),
TP_ARGS(pid, name, value),
TP_STRUCT__entry(
__field(int, pid)
__string(counter_name, name)
__field(int, value)
),
TP_fast_assign(
__entry->pid = current->tgid;
__assign_str(counter_name, name);
__entry->value = value;
),
TP_printk("%d|%s|%d", __entry->pid,
__get_str(counter_name), __entry->value)
)
#define MDSS_PLL_ATRACE_END(name) trace_pll_tracing_mark_write(current->tgid,\
name, 0)
#define MDSS_PLL_ATRACE_BEGIN(name) trace_pll_tracing_mark_write(current->tgid,\
name, 1)
#define MDSS_PLL_ATRACE_FUNC() MDSS_PLL_ATRACE_BEGIN(__func__)
#define MDSS_PLL_ATRACE_INT(name, value) \
trace_mdss_pll_trace_counter(current->tgid, name, value)
#endif /* _MDSS_PLL_TRACE_H_ */
/* This part must be outside protection */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#include <trace/define_trace.h>

View File

@@ -1,380 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/string.h>
#include <linux/of_address.h>
#include <linux/dma-mapping.h>
#include <linux/vmalloc.h>
#include <linux/memblock.h>
#include "pll_drv.h"
/**
* mdss_pll_get_mp_by_reg_name() -- Find power module by regulator name
*@pll_res: Pointer to the PLL resource
*@name: Regulator name as specified in the pll dtsi
*
* This is a helper function to retrieve the regulator information
* for each pll resource.
*/
struct dss_vreg *mdss_pll_get_mp_by_reg_name(struct mdss_pll_resources *pll_res
, char *name)
{
struct dss_vreg *regulator = NULL;
int i;
if ((pll_res == NULL) || (pll_res->mp.vreg_config == NULL)) {
pr_err("%s Invalid PLL resource\n", __func__);
goto error;
}
regulator = pll_res->mp.vreg_config;
for (i = 0; i < pll_res->mp.num_vreg; i++) {
if (!strcmp(name, regulator->vreg_name)) {
pr_debug("Found regulator match for %s\n", name);
break;
}
regulator++;
}
error:
return regulator;
}
int mdss_pll_util_resource_enable(struct mdss_pll_resources *pll_res,
bool enable)
{
int rc = 0;
struct dss_module_power *mp = &pll_res->mp;
if (enable) {
rc = msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, enable);
if (rc) {
pr_err("Failed to enable vregs rc=%d\n", rc);
goto vreg_err;
}
rc = msm_dss_clk_set_rate(mp->clk_config, mp->num_clk);
if (rc) {
pr_err("Failed to set clock rate rc=%d\n", rc);
goto clk_err;
}
rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable);
if (rc) {
pr_err("clock enable failed rc:%d\n", rc);
goto clk_err;
}
} else {
msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable);
msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, enable);
}
return rc;
clk_err:
msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, 0);
vreg_err:
return rc;
}
static int mdss_pll_util_parse_dt_supply(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int i = 0, rc = 0;
u32 tmp = 0;
struct device_node *of_node = NULL, *supply_root_node = NULL;
struct device_node *supply_node = NULL;
struct dss_module_power *mp = &pll_res->mp;
of_node = pdev->dev.of_node;
mp->num_vreg = 0;
supply_root_node = of_get_child_by_name(of_node,
"qcom,platform-supply-entries");
if (!supply_root_node) {
pr_err("no supply entry present\n");
return rc;
}
for_each_child_of_node(supply_root_node, supply_node) {
mp->num_vreg++;
}
if (mp->num_vreg == 0) {
pr_debug("no vreg\n");
return rc;
}
pr_debug("vreg found. count=%d\n", mp->num_vreg);
mp->vreg_config = devm_kzalloc(&pdev->dev, sizeof(struct dss_vreg) *
mp->num_vreg, GFP_KERNEL);
if (!mp->vreg_config) {
rc = -ENOMEM;
return rc;
}
for_each_child_of_node(supply_root_node, supply_node) {
const char *st = NULL;
rc = of_property_read_string(supply_node,
"qcom,supply-name", &st);
if (rc) {
pr_err(":error reading name. rc=%d\n", rc);
goto error;
}
strlcpy(mp->vreg_config[i].vreg_name, st,
sizeof(mp->vreg_config[i].vreg_name));
rc = of_property_read_u32(supply_node,
"qcom,supply-min-voltage", &tmp);
if (rc) {
pr_err(": error reading min volt. rc=%d\n", rc);
goto error;
}
mp->vreg_config[i].min_voltage = tmp;
rc = of_property_read_u32(supply_node,
"qcom,supply-max-voltage", &tmp);
if (rc) {
pr_err(": error reading max volt. rc=%d\n", rc);
goto error;
}
mp->vreg_config[i].max_voltage = tmp;
rc = of_property_read_u32(supply_node,
"qcom,supply-enable-load", &tmp);
if (rc) {
pr_err(": error reading enable load. rc=%d\n", rc);
goto error;
}
mp->vreg_config[i].enable_load = tmp;
rc = of_property_read_u32(supply_node,
"qcom,supply-disable-load", &tmp);
if (rc) {
pr_err(": error reading disable load. rc=%d\n", rc);
goto error;
}
mp->vreg_config[i].disable_load = tmp;
rc = of_property_read_u32(supply_node,
"qcom,supply-pre-on-sleep", &tmp);
if (rc)
pr_debug("error reading supply pre sleep value. rc=%d\n",
rc);
mp->vreg_config[i].pre_on_sleep = (!rc ? tmp : 0);
rc = of_property_read_u32(supply_node,
"qcom,supply-pre-off-sleep", &tmp);
if (rc)
pr_debug("error reading supply pre sleep value. rc=%d\n",
rc);
mp->vreg_config[i].pre_off_sleep = (!rc ? tmp : 0);
rc = of_property_read_u32(supply_node,
"qcom,supply-post-on-sleep", &tmp);
if (rc)
pr_debug("error reading supply post sleep value. rc=%d\n",
rc);
mp->vreg_config[i].post_on_sleep = (!rc ? tmp : 0);
rc = of_property_read_u32(supply_node,
"qcom,supply-post-off-sleep", &tmp);
if (rc)
pr_debug("error reading supply post sleep value. rc=%d\n",
rc);
mp->vreg_config[i].post_off_sleep = (!rc ? tmp : 0);
pr_debug("%s min=%d, max=%d, enable=%d, disable=%d, preonsleep=%d, postonsleep=%d, preoffsleep=%d, postoffsleep=%d\n",
mp->vreg_config[i].vreg_name,
mp->vreg_config[i].min_voltage,
mp->vreg_config[i].max_voltage,
mp->vreg_config[i].enable_load,
mp->vreg_config[i].disable_load,
mp->vreg_config[i].pre_on_sleep,
mp->vreg_config[i].post_on_sleep,
mp->vreg_config[i].pre_off_sleep,
mp->vreg_config[i].post_off_sleep);
++i;
rc = 0;
}
return rc;
error:
if (mp->vreg_config) {
devm_kfree(&pdev->dev, mp->vreg_config);
mp->vreg_config = NULL;
mp->num_vreg = 0;
}
return rc;
}
static int mdss_pll_util_parse_dt_clock(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
u32 i = 0, rc = 0;
struct dss_module_power *mp = &pll_res->mp;
const char *clock_name;
u32 clock_rate;
mp->num_clk = of_property_count_strings(pdev->dev.of_node,
"clock-names");
if (mp->num_clk <= 0) {
pr_err("clocks are not defined\n");
goto clk_err;
}
mp->clk_config = devm_kzalloc(&pdev->dev,
sizeof(struct dss_clk) * mp->num_clk, GFP_KERNEL);
if (!mp->clk_config) {
rc = -ENOMEM;
mp->num_clk = 0;
goto clk_err;
}
for (i = 0; i < mp->num_clk; i++) {
of_property_read_string_index(pdev->dev.of_node, "clock-names",
i, &clock_name);
strlcpy(mp->clk_config[i].clk_name, clock_name,
sizeof(mp->clk_config[i].clk_name));
of_property_read_u32_index(pdev->dev.of_node, "clock-rate",
i, &clock_rate);
mp->clk_config[i].rate = clock_rate;
if (!clock_rate)
mp->clk_config[i].type = DSS_CLK_AHB;
else
mp->clk_config[i].type = DSS_CLK_PCLK;
}
clk_err:
return rc;
}
static void mdss_pll_free_bootmem(u32 mem_addr, u32 size)
{
unsigned long pfn_start, pfn_end, pfn_idx;
pfn_start = mem_addr >> PAGE_SHIFT;
pfn_end = (mem_addr + size) >> PAGE_SHIFT;
for (pfn_idx = pfn_start; pfn_idx < pfn_end; pfn_idx++)
free_reserved_page(pfn_to_page(pfn_idx));
}
static int mdss_pll_util_parse_dt_dfps(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc = 0;
struct device_node *pnode;
const u32 *addr;
struct vm_struct *area;
u64 size;
u32 offsets[2];
unsigned long virt_add;
pnode = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
if (IS_ERR_OR_NULL(pnode)) {
rc = PTR_ERR(pnode);
goto pnode_err;
}
addr = of_get_address(pnode, 0, &size, NULL);
if (!addr) {
pr_err("failed to parse the dfps memory address\n");
rc = -EINVAL;
goto pnode_err;
}
/* maintain compatibility for 32/64 bit */
offsets[0] = (u32) of_read_ulong(addr, 2);
offsets[1] = (u32) size;
area = get_vm_area(offsets[1], VM_IOREMAP);
if (!area) {
rc = -ENOMEM;
goto dfps_mem_err;
}
virt_add = (unsigned long)area->addr;
rc = ioremap_page_range(virt_add, (virt_add + offsets[1]),
offsets[0], PAGE_KERNEL);
if (rc) {
rc = -ENOMEM;
goto ioremap_err;
}
pll_res->dfps = kzalloc(sizeof(struct dfps_info), GFP_KERNEL);
if (IS_ERR_OR_NULL(pll_res->dfps)) {
rc = PTR_ERR(pll_res->dfps);
pr_err("couldn't allocate dfps kernel memory\n");
goto addr_err;
}
/* memcopy complete dfps structure from kernel virtual memory */
memcpy_fromio(pll_res->dfps, area->addr, sizeof(struct dfps_info));
addr_err:
if (virt_add)
unmap_kernel_range(virt_add, (unsigned long) size);
ioremap_err:
if (area)
vfree(area->addr);
dfps_mem_err:
/* free the dfps memory here */
memblock_free(offsets[0], offsets[1]);
mdss_pll_free_bootmem(offsets[0], offsets[1]);
pnode_err:
if (pnode)
of_node_put(pnode);
return rc;
}
int mdss_pll_util_resource_parse(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc = 0;
struct dss_module_power *mp = &pll_res->mp;
rc = mdss_pll_util_parse_dt_supply(pdev, pll_res);
if (rc) {
pr_err("vreg parsing failed rc=%d\n", rc);
goto end;
}
rc = mdss_pll_util_parse_dt_clock(pdev, pll_res);
if (rc) {
pr_err("clock name parsing failed rc=%d\n", rc);
goto clk_err;
}
if (mdss_pll_util_parse_dt_dfps(pdev, pll_res))
pr_err("dfps not enabled!\n");
return rc;
clk_err:
devm_kfree(&pdev->dev, mp->vreg_config);
mp->num_vreg = 0;
end:
return rc;
}