Populate the PHY ops with the downstream driver as reference. There are a couple of TODOs which need to be resolved: - The PHY timings are all hardcoded for now. This needs to be replaced with automatic calculations once we get/understand them. - There are some lane configuration registers which use a new representation between physical and logical lane mappings. For now, we've hardcoced them to follow the default mapping (i.e logical 0 -> phy 0, logical 1 -> phy 1 etc). Signed-off-by: Archit Taneja <architt@xxxxxxxxxxxxxx> --- drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c | 199 +++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c index b7545fb63bf5..0af951aaeea1 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c @@ -8,9 +8,208 @@ #include "dsi_phy.h" #include "dsi.xml.h" +static int dsi_phy_hw_v3_0_is_pll_on(struct msm_dsi_phy *phy) +{ + void __iomem *base = phy->base; + u32 data = 0; + + data = dsi_phy_read(base + REG_DSI_10nm_PHY_CMN_PLL_CNTRL); + mb(); /* make sure read happened */ + + return (data & BIT(0)); +} + +static void dsi_phy_hw_v3_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_10nm_PHY_LN_LPRX_CTRL(phy_lane_0), 0x3); + else + dsi_phy_write(lane_base + + REG_DSI_10nm_PHY_LN_LPRX_CTRL(phy_lane_0), 0); +} + +static void dsi_phy_hw_v3_0_lane_settings(struct msm_dsi_phy *phy) +{ + int i; + u8 tx_dctrl[] = { 0x00, 0x00, 0x00, 0x04, 0x01 }; + void __iomem *lane_base = phy->lane_base; + + /* Strength ctrl settings */ + for (i = 0; i < 5; i++) { + dsi_phy_write(lane_base + REG_DSI_10nm_PHY_LN_LPTX_STR_CTRL(i), + 0x55); + /* + * 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_10nm_PHY_LN_LPRX_CTRL(i), 0); + dsi_phy_write(lane_base + REG_DSI_10nm_PHY_LN_PIN_SWAP(i), 0x0); + dsi_phy_write(lane_base + REG_DSI_10nm_PHY_LN_HSTX_STR_CTRL(i), + 0x88); + } + + dsi_phy_hw_v3_0_config_lpcdrx(phy, true); + + /* other settings */ + for (i = 0; i < 5; i++) { + dsi_phy_write(lane_base + REG_DSI_10nm_PHY_LN_CFG0(i), 0x0); + dsi_phy_write(lane_base + REG_DSI_10nm_PHY_LN_CFG1(i), 0x0); + dsi_phy_write(lane_base + REG_DSI_10nm_PHY_LN_CFG2(i), 0x0); + dsi_phy_write(lane_base + REG_DSI_10nm_PHY_LN_CFG3(i), + i == 4 ? 0x80 : 0x0); + dsi_phy_write(lane_base + + REG_DSI_10nm_PHY_LN_OFFSET_TOP_CTRL(i), 0x0); + dsi_phy_write(lane_base + + REG_DSI_10nm_PHY_LN_OFFSET_BOT_CTRL(i), 0x0); + dsi_phy_write(lane_base + REG_DSI_10nm_PHY_LN_TX_DCTRL(i), + tx_dctrl[i]); + } + + /* Toggle BIT 0 to release freeze I/0 */ + dsi_phy_write(lane_base + REG_DSI_10nm_PHY_LN_TX_DCTRL(3), 0x05); + dsi_phy_write(lane_base + REG_DSI_10nm_PHY_LN_TX_DCTRL(3), 0x04); +} + +static int msm_dsi_dphy_timing_calc_v3(struct msm_dsi_dphy_timing *timing, + struct msm_dsi_phy_clk_request *clk_req) +{ + /* + * TODO: These params need to be computed, they're currently hardcoded + * for a 1440x2560@60Hz panel with a byteclk of 100.618 Mhz, and a + * default escape clock of 19.2 Mhz. + */ + + timing->hs_halfbyte_en = 0; + timing->clk_zero = 0x1c; + timing->clk_prepare = 0x07; + timing->clk_trail = 0x07; + timing->hs_exit = 0x23; + timing->hs_zero = 0x21; + timing->hs_prepare = 0x07; + timing->hs_trail = 0x07; + timing->hs_rqst = 0x05; + timing->ta_sure = 0x00; + timing->ta_go = 0x03; + timing->ta_get = 0x04; + + timing->shared_timings.clk_pre = 0x2d; + timing->shared_timings.clk_post = 0x0d; + + return 0; +} + static int dsi_10nm_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; + u32 data; + + DBG(""); + + if (msm_dsi_dphy_timing_calc_v3(timing, clk_req)) { + dev_err(&phy->pdev->dev, + "%s: D-PHY timing calculation failed\n", __func__); + return -EINVAL; + } + + if (dsi_phy_hw_v3_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_10nm_PHY_CMN_PHY_STATUS, + status, (status & BIT(0)), + delay_us, timeout_us); + if (ret) { + pr_err("Ref gen not ready. Aborting\n"); + return -EINVAL; + } + + /* de-assert digital and pll power down */ + data = BIT(6) | BIT(5); + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_CTRL_0, data); + + /* Assert PLL core reset */ + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_PLL_CNTRL, 0x00); + + /* turn off resync FIFO */ + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_RBUF_CTRL, 0x00); + + /* Select MS1 byte-clk */ + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_GLBL_CTRL, 0x10); + + /* Enable LDO */ + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_VREG_CTRL, 0x59); + + /* Configure PHY lane swap (TODO: we need to calculate this) */ + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_LANE_CFG0, 0x21); + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_LANE_CFG1, 0x84); + + /* DSI PHY timings */ + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_TIMING_CTRL_0, + timing->hs_halfbyte_en); + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_TIMING_CTRL_1, + timing->clk_zero); + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_TIMING_CTRL_2, + timing->clk_prepare); + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_TIMING_CTRL_3, + timing->clk_trail); + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_TIMING_CTRL_4, + timing->hs_exit); + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_TIMING_CTRL_5, + timing->hs_zero); + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_TIMING_CTRL_6, + timing->hs_prepare); + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_TIMING_CTRL_7, + timing->hs_trail); + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_TIMING_CTRL_8, + timing->hs_rqst); + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_TIMING_CTRL_9, + timing->ta_go | (timing->ta_sure << 3)); + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_TIMING_CTRL_10, + timing->ta_get); + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_TIMING_CTRL_11, + 0x00); + + /* Remove power down from all blocks */ + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_CTRL_0, 0x7f); + + /* power up lanes */ + data = dsi_phy_read(base + REG_DSI_10nm_PHY_CMN_CTRL_0); + + /* TODO: only power up lanes that are used */ + data |= 0x1F; + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_CTRL_0, data); + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_LANE_CTRL0, 0x1F); + + /* Select full-rate mode */ + dsi_phy_write(base + REG_DSI_10nm_PHY_CMN_CTRL_2, 0x40); + + ret = msm_dsi_pll_set_usecase(phy->pll, phy->usecase); + if (ret) { + dev_err(&phy->pdev->dev, "%s: set pll usecase failed, %d\n", + __func__, ret); + return ret; + } + + /* DSI lane settings */ + dsi_phy_hw_v3_0_lane_settings(phy); + + DBG("DSI%d PHY enabled", phy->id); + return 0; } -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, hosted by The Linux Foundation -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html