Commit 1ef7c99d authored by Jonathan Marek's avatar Jonathan Marek Committed by Rob Clark

drm/msm/dsi: add support for 7nm DSI PHY/PLL

This adds support for the 7nm ("V4") DSI PHY/PLL for sm8150 and sm8250.

Implementation is based on 10nm driver, but updated based on the downstream
7nm driver.
Signed-off-by: default avatarJonathan Marek <jonathan@marek.ca>
Tested-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> (SM8250)
Signed-off-by: default avatarRob Clark <robdclark@chromium.org>
parent 11550636
......@@ -90,6 +90,8 @@ Required properties:
* "qcom,dsi-phy-14nm-660"
* "qcom,dsi-phy-10nm"
* "qcom,dsi-phy-10nm-8998"
* "qcom,dsi-phy-7nm"
* "qcom,dsi-phy-7nm-8150"
- reg: Physical base address and length of the registers of PLL, PHY. Some
revisions require the PHY regulator base address, whereas others require the
PHY lane base address. See below for each PHY revision.
......@@ -98,7 +100,7 @@ Required properties:
* "dsi_pll"
* "dsi_phy"
* "dsi_phy_regulator"
For DSI 14nm and 10nm PHYs:
For DSI 14nm, 10nm and 7nm PHYs:
* "dsi_pll"
* "dsi_phy"
* "dsi_phy_lane"
......@@ -116,7 +118,7 @@ Required properties:
- vcca-supply: phandle to vcca regulator device node
For 14nm PHY:
- vcca-supply: phandle to vcca regulator device node
For 10nm PHY:
For 10nm and 7nm PHY:
- vdds-supply: phandle to vdds regulator device node
Optional properties:
......
......@@ -110,3 +110,11 @@ config DRM_MSM_DSI_10NM_PHY
default y
help
Choose this option if DSI PHY on SDM845 is used on the platform.
config DRM_MSM_DSI_7NM_PHY
bool "Enable DSI 7nm PHY driver in MSM DRM (used by SM8150/SM8250)"
depends on DRM_MSM_DSI
default y
help
Choose this option if DSI PHY on SM8150/SM8250 is used on the
platform.
......@@ -119,6 +119,7 @@ msm-$(CONFIG_DRM_MSM_DSI_20NM_PHY) += dsi/phy/dsi_phy_20nm.o
msm-$(CONFIG_DRM_MSM_DSI_28NM_8960_PHY) += dsi/phy/dsi_phy_28nm_8960.o
msm-$(CONFIG_DRM_MSM_DSI_14NM_PHY) += dsi/phy/dsi_phy_14nm.o
msm-$(CONFIG_DRM_MSM_DSI_10NM_PHY) += dsi/phy/dsi_phy_10nm.o
msm-$(CONFIG_DRM_MSM_DSI_7NM_PHY) += dsi/phy/dsi_phy_7nm.o
ifeq ($(CONFIG_DRM_MSM_DSI_PLL),y)
msm-y += dsi/pll/dsi_pll.o
......@@ -126,6 +127,7 @@ msm-$(CONFIG_DRM_MSM_DSI_28NM_PHY) += dsi/pll/dsi_pll_28nm.o
msm-$(CONFIG_DRM_MSM_DSI_28NM_8960_PHY) += dsi/pll/dsi_pll_28nm_8960.o
msm-$(CONFIG_DRM_MSM_DSI_14NM_PHY) += dsi/pll/dsi_pll_14nm.o
msm-$(CONFIG_DRM_MSM_DSI_10NM_PHY) += dsi/pll/dsi_pll_10nm.o
msm-$(CONFIG_DRM_MSM_DSI_7NM_PHY) += dsi/pll/dsi_pll_7nm.o
endif
obj-$(CONFIG_DRM_MSM) += msm.o
......@@ -30,6 +30,8 @@ enum msm_dsi_phy_type {
MSM_DSI_PHY_28NM_8960,
MSM_DSI_PHY_14NM,
MSM_DSI_PHY_10NM,
MSM_DSI_PHY_7NM,
MSM_DSI_PHY_7NM_V4_1,
MSM_DSI_PHY_MAX
};
......
This diff is collapsed.
......@@ -364,6 +364,102 @@ int msm_dsi_dphy_timing_calc_v3(struct msm_dsi_dphy_timing *timing,
return 0;
}
int msm_dsi_dphy_timing_calc_v4(struct msm_dsi_dphy_timing *timing,
struct msm_dsi_phy_clk_request *clk_req)
{
const unsigned long bit_rate = clk_req->bitclk_rate;
const unsigned long esc_rate = clk_req->escclk_rate;
s32 ui, ui_x8;
s32 tmax, tmin;
s32 pcnt_clk_prep = 50;
s32 pcnt_clk_zero = 2;
s32 pcnt_clk_trail = 30;
s32 pcnt_hs_prep = 50;
s32 pcnt_hs_zero = 10;
s32 pcnt_hs_trail = 30;
s32 pcnt_hs_exit = 10;
s32 coeff = 1000; /* Precision, should avoid overflow */
s32 hb_en;
s32 temp;
if (!bit_rate || !esc_rate)
return -EINVAL;
hb_en = 0;
ui = mult_frac(NSEC_PER_MSEC, coeff, bit_rate / 1000);
ui_x8 = ui << 3;
/* TODO: verify these calculations against latest downstream driver
* everything except clk_post/clk_pre uses calculations from v3 based
* on the downstream driver having the same calculations for v3 and v4
*/
temp = S_DIV_ROUND_UP(38 * coeff, ui_x8);
tmin = max_t(s32, temp, 0);
temp = (95 * coeff) / ui_x8;
tmax = max_t(s32, temp, 0);
timing->clk_prepare = linear_inter(tmax, tmin, pcnt_clk_prep, 0, false);
temp = 300 * coeff - (timing->clk_prepare << 3) * ui;
tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1;
tmax = (tmin > 255) ? 511 : 255;
timing->clk_zero = linear_inter(tmax, tmin, pcnt_clk_zero, 0, false);
tmin = DIV_ROUND_UP(60 * coeff + 3 * ui, ui_x8);
temp = 105 * coeff + 12 * ui - 20 * coeff;
tmax = (temp + 3 * ui) / ui_x8;
timing->clk_trail = linear_inter(tmax, tmin, pcnt_clk_trail, 0, false);
temp = S_DIV_ROUND_UP(40 * coeff + 4 * ui, ui_x8);
tmin = max_t(s32, temp, 0);
temp = (85 * coeff + 6 * ui) / ui_x8;
tmax = max_t(s32, temp, 0);
timing->hs_prepare = linear_inter(tmax, tmin, pcnt_hs_prep, 0, false);
temp = 145 * coeff + 10 * ui - (timing->hs_prepare << 3) * ui;
tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1;
tmax = 255;
timing->hs_zero = linear_inter(tmax, tmin, pcnt_hs_zero, 0, false);
tmin = DIV_ROUND_UP(60 * coeff + 4 * ui, ui_x8) - 1;
temp = 105 * coeff + 12 * ui - 20 * coeff;
tmax = (temp / ui_x8) - 1;
timing->hs_trail = linear_inter(tmax, tmin, pcnt_hs_trail, 0, false);
temp = 50 * coeff + ((hb_en << 2) - 8) * ui;
timing->hs_rqst = S_DIV_ROUND_UP(temp, ui_x8);
tmin = DIV_ROUND_UP(100 * coeff, ui_x8) - 1;
tmax = 255;
timing->hs_exit = linear_inter(tmax, tmin, pcnt_hs_exit, 0, false);
/* recommended min
* = roundup((mipi_min_ns + t_hs_trail_ns)/(16*bit_clk_ns), 0) - 1
*/
temp = 60 * coeff + 52 * ui + + (timing->hs_trail + 1) * ui_x8;
tmin = DIV_ROUND_UP(temp, 16 * ui) - 1;
tmax = 255;
timing->shared_timings.clk_post = linear_inter(tmax, tmin, 5, 0, false);
/* recommended min
* val1 = (tlpx_ns + clk_prepare_ns + clk_zero_ns + hs_rqst_ns)
* val2 = (16 * bit_clk_ns)
* final = roundup(val1/val2, 0) - 1
*/
temp = 52 * coeff + (timing->clk_prepare + timing->clk_zero + 1) * ui_x8 + 54 * coeff;
tmin = DIV_ROUND_UP(temp, 16 * ui) - 1;
tmax = 255;
timing->shared_timings.clk_pre = DIV_ROUND_UP((tmax - tmin) * 125, 10000) + tmin;
DBG("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d",
timing->shared_timings.clk_pre, timing->shared_timings.clk_post,
timing->clk_zero, timing->clk_trail, timing->clk_prepare, timing->hs_exit,
timing->hs_zero, timing->hs_prepare, timing->hs_trail, timing->hs_rqst);
return 0;
}
void msm_dsi_phy_set_src_pll(struct msm_dsi_phy *phy, int pll_id, u32 reg,
u32 bit_mask)
{
......@@ -507,6 +603,12 @@ static const struct of_device_id dsi_phy_dt_match[] = {
.data = &dsi_phy_10nm_cfgs },
{ .compatible = "qcom,dsi-phy-10nm-8998",
.data = &dsi_phy_10nm_8998_cfgs },
#endif
#ifdef CONFIG_DRM_MSM_DSI_7NM_PHY
{ .compatible = "qcom,dsi-phy-7nm",
.data = &dsi_phy_7nm_cfgs },
{ .compatible = "qcom,dsi-phy-7nm-8150",
.data = &dsi_phy_7nm_8150_cfgs },
#endif
{}
};
......
......@@ -48,6 +48,8 @@ extern const struct msm_dsi_phy_cfg dsi_phy_14nm_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_14nm_660_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_10nm_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_10nm_8998_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_7nm_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_7nm_8150_cfgs;
struct msm_dsi_dphy_timing {
u32 clk_zero;
......@@ -100,6 +102,8 @@ int msm_dsi_dphy_timing_calc_v2(struct msm_dsi_dphy_timing *timing,
struct msm_dsi_phy_clk_request *clk_req);
int msm_dsi_dphy_timing_calc_v3(struct msm_dsi_dphy_timing *timing,
struct msm_dsi_phy_clk_request *clk_req);
int msm_dsi_dphy_timing_calc_v4(struct msm_dsi_dphy_timing *timing,
struct msm_dsi_phy_clk_request *clk_req);
void msm_dsi_phy_set_src_pll(struct msm_dsi_phy *phy, int pll_id, u32 reg,
u32 bit_mask);
int msm_dsi_phy_init_common(struct msm_dsi_phy *phy);
......
/*
* SPDX-License-Identifier: GPL-2.0
* Copyright (c) 2018, The Linux Foundation
*/
#include <linux/iopoll.h>
#include "dsi_phy.h"
#include "dsi.xml.h"
static int dsi_phy_hw_v4_0_is_pll_on(struct msm_dsi_phy *phy)
{
void __iomem *base = phy->base;
u32 data = 0;
data = dsi_phy_read(base + REG_DSI_7nm_PHY_CMN_PLL_CNTRL);
mb(); /* make sure read happened */
return (data & BIT(0));
}
static void dsi_phy_hw_v4_0_config_lpcdrx(struct msm_dsi_phy *phy, bool enable)
{
void __iomem *lane_base = phy->lane_base;
int phy_lane_0 = 0; /* TODO: Support all lane swap configs */
/*
* LPRX and CDRX need to enabled only for physical data lane
* corresponding to the logical data lane 0
*/
if (enable)
dsi_phy_write(lane_base +
REG_DSI_7nm_PHY_LN_LPRX_CTRL(phy_lane_0), 0x3);
else
dsi_phy_write(lane_base +
REG_DSI_7nm_PHY_LN_LPRX_CTRL(phy_lane_0), 0);
}
static void dsi_phy_hw_v4_0_lane_settings(struct msm_dsi_phy *phy)
{
int i;
const u8 tx_dctrl_0[] = { 0x00, 0x00, 0x00, 0x04, 0x01 };
const u8 tx_dctrl_1[] = { 0x40, 0x40, 0x40, 0x46, 0x41 };
const u8 *tx_dctrl = tx_dctrl_0;
void __iomem *lane_base = phy->lane_base;
if (phy->cfg->type == MSM_DSI_PHY_7NM_V4_1)
tx_dctrl = tx_dctrl_1;
/* Strength ctrl settings */
for (i = 0; i < 5; i++) {
/*
* Disable LPRX and CDRX for all lanes. And later on, it will
* be only enabled for the physical data lane corresponding
* to the logical data lane 0
*/
dsi_phy_write(lane_base + REG_DSI_7nm_PHY_LN_LPRX_CTRL(i), 0);
dsi_phy_write(lane_base + REG_DSI_7nm_PHY_LN_PIN_SWAP(i), 0x0);
}
dsi_phy_hw_v4_0_config_lpcdrx(phy, true);
/* other settings */
for (i = 0; i < 5; i++) {
dsi_phy_write(lane_base + REG_DSI_7nm_PHY_LN_CFG0(i), 0x0);
dsi_phy_write(lane_base + REG_DSI_7nm_PHY_LN_CFG1(i), 0x0);
dsi_phy_write(lane_base + REG_DSI_7nm_PHY_LN_CFG2(i), i == 4 ? 0x8a : 0xa);
dsi_phy_write(lane_base + REG_DSI_7nm_PHY_LN_TX_DCTRL(i), tx_dctrl[i]);
}
}
static int dsi_7nm_phy_enable(struct msm_dsi_phy *phy, int src_pll_id,
struct msm_dsi_phy_clk_request *clk_req)
{
int ret;
u32 status;
u32 const delay_us = 5;
u32 const timeout_us = 1000;
struct msm_dsi_dphy_timing *timing = &phy->timing;
void __iomem *base = phy->base;
bool less_than_1500_mhz;
u32 vreg_ctrl_0, glbl_str_swi_cal_sel_ctrl, glbl_hstx_str_ctrl_0;
u32 glbl_rescode_top_ctrl, glbl_rescode_bot_ctrl;
u32 data;
DBG("");
if (msm_dsi_dphy_timing_calc_v4(timing, clk_req)) {
DRM_DEV_ERROR(&phy->pdev->dev,
"%s: D-PHY timing calculation failed\n", __func__);
return -EINVAL;
}
if (dsi_phy_hw_v4_0_is_pll_on(phy))
pr_warn("PLL turned on before configuring PHY\n");
/* wait for REFGEN READY */
ret = readl_poll_timeout_atomic(base + REG_DSI_7nm_PHY_CMN_PHY_STATUS,
status, (status & BIT(0)),
delay_us, timeout_us);
if (ret) {
pr_err("Ref gen not ready. Aborting\n");
return -EINVAL;
}
/* TODO: CPHY enable path (this is for DPHY only) */
/* Alter PHY configurations if data rate less than 1.5GHZ*/
less_than_1500_mhz = (clk_req->bitclk_rate <= 1500000000);
if (phy->cfg->type == MSM_DSI_PHY_7NM_V4_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;
glbl_str_swi_cal_sel_ctrl = 0x00;
glbl_hstx_str_ctrl_0 = 0x88;
} else {
vreg_ctrl_0 = less_than_1500_mhz ? 0x5B : 0x59;
glbl_str_swi_cal_sel_ctrl = less_than_1500_mhz ? 0x03 : 0x00;
glbl_hstx_str_ctrl_0 = less_than_1500_mhz ? 0x66 : 0x88;
glbl_rescode_top_ctrl = 0x03;
glbl_rescode_bot_ctrl = 0x3c;
}
/* de-assert digital and pll power down */
data = BIT(6) | BIT(5);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_CTRL_0, data);
/* Assert PLL core reset */
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_PLL_CNTRL, 0x00);
/* turn off resync FIFO */
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_RBUF_CTRL, 0x00);
/* program CMN_CTRL_4 for minor_ver 2 chipsets*/
data = dsi_phy_read(base + REG_DSI_7nm_PHY_CMN_REVISION_ID0);
data = data & (0xf0);
if (data == 0x20)
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_CTRL_4, 0x04);
/* Configure PHY lane swap (TODO: we need to calculate this) */
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_LANE_CFG0, 0x21);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_LANE_CFG1, 0x84);
/* Enable LDO */
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_VREG_CTRL_0, vreg_ctrl_0);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_VREG_CTRL_1, 0x5c);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_CTRL_3, 0x00);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_GLBL_STR_SWI_CAL_SEL_CTRL,
glbl_str_swi_cal_sel_ctrl);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_GLBL_HSTX_STR_CTRL_0,
glbl_hstx_str_ctrl_0);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_GLBL_PEMPH_CTRL_0, 0x00);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_GLBL_RESCODE_OFFSET_TOP_CTRL,
glbl_rescode_top_ctrl);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_GLBL_RESCODE_OFFSET_BOT_CTRL,
glbl_rescode_bot_ctrl);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_GLBL_LPTX_STR_CTRL, 0x55);
/* Remove power down from all blocks */
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_CTRL_0, 0x7f);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_LANE_CTRL0, 0x1f);
/* Select full-rate mode */
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_CTRL_2, 0x40);
ret = msm_dsi_pll_set_usecase(phy->pll, phy->usecase);
if (ret) {
DRM_DEV_ERROR(&phy->pdev->dev, "%s: set pll usecase failed, %d\n",
__func__, ret);
return ret;
}
/* DSI PHY timings */
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_0, 0x00);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_1, timing->clk_zero);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_2, timing->clk_prepare);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_3, timing->clk_trail);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_4, timing->hs_exit);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_5, timing->hs_zero);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_6, timing->hs_prepare);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_7, timing->hs_trail);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_8, timing->hs_rqst);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_9, 0x02);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_10, 0x04);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_11, 0x00);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_12,
timing->shared_timings.clk_pre);
dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_13,
timing->shared_timings.clk_post);
/* DSI lane settings */
dsi_phy_hw_v4_0_lane_settings(phy);
DBG("DSI%d PHY enabled", phy->id);
return 0;
}
static void dsi_7nm_phy_disable(struct msm_dsi_phy *phy)
{
/* TODO */
}
static int dsi_7nm_phy_init(struct msm_dsi_phy *phy)
{
struct platform_device *pdev = phy->pdev;
phy->lane_base = msm_ioremap(pdev, "dsi_phy_lane",
"DSI_PHY_LANE");
if (IS_ERR(phy->lane_base)) {
DRM_DEV_ERROR(&pdev->dev, "%s: failed to map phy lane base\n",
__func__);
return -ENOMEM;
}
return 0;
}
const struct msm_dsi_phy_cfg dsi_phy_7nm_cfgs = {
.type = MSM_DSI_PHY_7NM_V4_1,
.src_pll_truthtable = { {false, false}, {true, false} },
.reg_cfg = {
.num = 1,
.regs = {
{"vdds", 36000, 32},
},
},
.ops = {
.enable = dsi_7nm_phy_enable,
.disable = dsi_7nm_phy_disable,
.init = dsi_7nm_phy_init,
},
.io_start = { 0xae94400, 0xae96400 },
.num_dsi_phy = 2,
};
const struct msm_dsi_phy_cfg dsi_phy_7nm_8150_cfgs = {
.type = MSM_DSI_PHY_7NM,
.src_pll_truthtable = { {false, false}, {true, false} },
.reg_cfg = {
.num = 1,
.regs = {
{"vdds", 36000, 32},
},
},
.ops = {
.enable = dsi_7nm_phy_enable,
.disable = dsi_7nm_phy_disable,
.init = dsi_7nm_phy_init,
},
.io_start = { 0xae94400, 0xae96400 },
.num_dsi_phy = 2,
};
......@@ -161,6 +161,10 @@ struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev,
case MSM_DSI_PHY_10NM:
pll = msm_dsi_pll_10nm_init(pdev, id);
break;
case MSM_DSI_PHY_7NM:
case MSM_DSI_PHY_7NM_V4_1:
pll = msm_dsi_pll_7nm_init(pdev, id);
break;
default:
pll = ERR_PTR(-ENXIO);
break;
......
......@@ -116,5 +116,15 @@ msm_dsi_pll_10nm_init(struct platform_device *pdev, int id)
return ERR_PTR(-ENODEV);
}
#endif
#ifdef CONFIG_DRM_MSM_DSI_7NM_PHY
struct msm_dsi_pll *msm_dsi_pll_7nm_init(struct platform_device *pdev, int id);
#else
static inline struct msm_dsi_pll *
msm_dsi_pll_7nm_init(struct platform_device *pdev, int id)
{
return ERR_PTR(-ENODEV);
}
#endif
#endif /* __DSI_PLL_H__ */
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment