From 9f52ca1ecbdf6558a41255f4c4e3286f1e70173a Mon Sep 17 00:00:00 2001 From: Ashish Garg Date: Sun, 26 Mar 2017 20:19:25 +0530 Subject: [PATCH] msm: mdss: add split link panel support in mdss In split link panels, the four data lanes are split into sublinks and the clock lane is shared among the sublink. Add support for split link panels in mdss. Change-Id: I40965c274a3591b0a00ca546052e7cb46967844d Signed-off-by: Ashish Garg --- .../devicetree/bindings/fb/mdss-dsi-panel.txt | 13 + drivers/video/fbdev/msm/mdss_dsi_host.c | 27 ++ drivers/video/fbdev/msm/mdss_dsi_panel.c | 44 +++ drivers/video/fbdev/msm/mdss_fb.c | 3 +- drivers/video/fbdev/msm/mdss_fb.h | 6 + drivers/video/fbdev/msm/mdss_mdp.h | 2 + drivers/video/fbdev/msm/mdss_mdp_ctl.c | 10 +- drivers/video/fbdev/msm/mdss_mdp_hwio.h | 4 + drivers/video/fbdev/msm/mdss_mdp_intf_video.c | 10 + drivers/video/fbdev/msm/mdss_panel.h | 3 + drivers/video/fbdev/msm/msm_mdss_io_8974.c | 329 ++++++++++++++---- 11 files changed, 381 insertions(+), 70 deletions(-) diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt index 90ccfa7c62e2..ce5ee56ada68 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt @@ -581,6 +581,15 @@ Additional properties added to the second level nodes that represent timings pro commands. "dsi_lp_mode" = DSI low power mode (default) "dsi_hs_mode" = DSI high speed mode +- qcom,sublinks-count: An integer value indicates the number of sublinks in the panel. + Default value is 1. This property is used only if qcom,split-link-enabled + is defined. +- qcom,lanes-per-sublink: An integer value indicates the number of data lanes per sublink in the panel. + Default value is 1. This property is used only if qcom,split-link-enabled + is defined. +- qcom,split-link-enabled: A boolean value to enable/disable the split link feature. If qcom,sublinks-count + or qcom,lanes-per-sublink are not defined, default values are used. + Note, if a given optional qcom,* binding is not present, then the driver will configure the default values specified. @@ -808,6 +817,10 @@ Example: qcom,mdss-dsc-version = <0x11>; qcom,mdss-dsc-scr-version = <0x1>; + qcom,split-link-enabled; + qcom,sublinks-count = <2>; + qcom,lanes-per-sublink = <2>; + dsi_sim_vid_config0: config0 { qcom,lm-split = <360 360>; qcom,mdss-dsc-encoders = <2>; diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c index 9f4b7eb52492..c3b7bda9dc0e 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_host.c +++ b/drivers/video/fbdev/msm/mdss_dsi_host.c @@ -1311,6 +1311,31 @@ void mdss_dsi_set_burst_mode(struct mdss_dsi_ctrl_pdata *ctrl) } +static void mdss_dsi_split_link_setup(struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + u32 data = 0; + struct mdss_panel_info *pinfo; + + if (!ctrl_pdata) + return; + + pinfo = &ctrl_pdata->panel_data.panel_info; + if (!pinfo->split_link_enabled) + return; + + pr_debug("%s: enable split link\n", __func__); + + data = MIPI_INP((ctrl_pdata->ctrl_base) + 0x330); + /* DMA_LINK_SEL */ + data |= 0x3 << 12; + /* MDP0_LINK_SEL */ + data |= 0x5 << 20; + /* EN */ + data |= 0x1; + /* DSI_SPLIT_LINK_CTRL */ + MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x330, data); +} + static void mdss_dsi_mode_setup(struct mdss_panel_data *pdata) { struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; @@ -1429,6 +1454,8 @@ static void mdss_dsi_mode_setup(struct mdss_panel_data *pdata) } mdss_dsi_dsc_config(ctrl_pdata, dsc); + + mdss_dsi_split_link_setup(ctrl_pdata); } void mdss_dsi_ctrl_setup(struct mdss_dsi_ctrl_pdata *ctrl) diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c index bf701e2a4ac5..0ac41896a539 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -1346,6 +1346,44 @@ static int mdss_dsi_parse_hdr_settings(struct device_node *np, return 0; } +static int mdss_dsi_parse_split_link_settings(struct device_node *np, + struct mdss_panel_info *pinfo) +{ + u32 tmp; + int rc = 0; + + if (!np) { + pr_err("%s: device node pointer is NULL\n", __func__); + return -EINVAL; + } + + if (!pinfo) { + pr_err("%s: panel info is NULL\n", __func__); + return -EINVAL; + } + + pinfo->split_link_enabled = of_property_read_bool(np, + "qcom,split-link-enabled"); + + if (pinfo->split_link_enabled) { + rc = of_property_read_u32(np, + "qcom,sublinks-count", &tmp); + /* default num of sublink is 1*/ + pinfo->mipi.num_of_sublinks = (!rc ? tmp : 1); + + rc = of_property_read_u32(np, + "qcom,lanes-per-sublink", &tmp); + /* default num of lanes per sublink is 1 */ + pinfo->mipi.lanes_per_sublink = (!rc ? tmp : 1); + } + + pr_info("%s: enable %d sublinks-count %d lanes per sublink %d\n", + __func__, pinfo->split_link_enabled, + pinfo->mipi.num_of_sublinks, + pinfo->mipi.lanes_per_sublink); + return 0; +} + static int mdss_dsi_parse_dsc_version(struct device_node *np, struct mdss_panel_timing *timing) { @@ -2730,9 +2768,15 @@ static int mdss_panel_parse_dt(struct device_node *np, pinfo->mipi.data_lane3 = of_property_read_bool(np, "qcom,mdss-dsi-lane-3-state"); + /* parse split link properties */ + rc = mdss_dsi_parse_split_link_settings(np, pinfo); + if (rc) + return rc; + rc = mdss_panel_parse_display_timings(np, &ctrl_pdata->panel_data); if (rc) return rc; + rc = mdss_dsi_parse_hdr_settings(np, pinfo); if (rc) return rc; diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index fe79b6fd52b4..f1fa7e56f334 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -373,7 +373,8 @@ static int mdss_fb_get_panel_xres(struct mdss_panel_info *pinfo) xres = pinfo->xres; if (pdata->next && pdata->next->active) xres += mdss_fb_get_panel_xres(&pdata->next->panel_info); - + if (pinfo->split_link_enabled) + xres = xres * pinfo->mipi.num_of_sublinks; return xres; } diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h index 321531c72a08..375df492e9a9 100644 --- a/drivers/video/fbdev/msm/mdss_fb.h +++ b/drivers/video/fbdev/msm/mdss_fb.h @@ -391,6 +391,12 @@ static inline void mdss_fb_update_notify_update(struct msm_fb_data_type *mfd) } } +/* Function returns true for split link */ +static inline bool is_panel_split_link(struct msm_fb_data_type *mfd) +{ + return mfd && mfd->panel_info && mfd->panel_info->split_link_enabled; +} + /* Function returns true for either any kind of dual display */ static inline bool is_panel_split(struct msm_fb_data_type *mfd) { diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 2fd047edd3e8..56af021e8cfc 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -1267,6 +1267,8 @@ static inline u32 get_panel_width(struct mdss_mdp_ctl *ctl) width = get_panel_xres(&ctl->panel_data->panel_info); if (ctl->panel_data->next && is_pingpong_split(ctl->mfd)) width += get_panel_xres(&ctl->panel_data->next->panel_info); + else if (is_panel_split_link(ctl->mfd)) + width *= (ctl->panel_data->panel_info.mipi.num_of_sublinks); return width; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index bd70535e79f9..f5dd35c7f5f2 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -709,6 +709,8 @@ int mdss_mdp_get_panel_params(struct mdss_mdp_pipe *pipe, *h_total += mdss_panel_get_htotal( &mixer->ctl->panel_data->next->panel_info, false); + else if (is_panel_split_link(mixer->ctl->mfd)) + *h_total *= pinfo->mipi.num_of_sublinks; } else { *v_total = mixer->height; *xres = mixer->width; @@ -4101,6 +4103,9 @@ static void mdss_mdp_ctl_split_display_enable(int enable, } } } + + if (is_panel_split_link(main_ctl->mfd)) + upper = lower = 0; writel_relaxed(upper, main_ctl->mdata->mdp_base + MDSS_MDP_REG_SPLIT_DISPLAY_UPPER_PIPE_CTRL); writel_relaxed(lower, main_ctl->mdata->mdp_base + @@ -4269,7 +4274,8 @@ void mdss_mdp_ctl_restore(bool locked) if (sctl) { mdss_mdp_ctl_restore_sub(sctl); mdss_mdp_ctl_split_display_enable(1, ctl, sctl); - } else if (is_pingpong_split(ctl->mfd)) { + } else if (is_pingpong_split(ctl->mfd) || + is_panel_split_link(ctl->mfd)) { mdss_mdp_ctl_pp_split_display_enable(1, ctl); } @@ -4396,6 +4402,8 @@ int mdss_mdp_ctl_start(struct mdss_mdp_ctl *ctl, bool handoff) } else if (is_pingpong_split(ctl->mfd)) { ctl->slave_intf_num = (ctl->intf_num + 1); mdss_mdp_ctl_pp_split_display_enable(true, ctl); + } else if (is_panel_split_link(ctl->mfd)) { + mdss_mdp_ctl_pp_split_display_enable(true, ctl); } } diff --git a/drivers/video/fbdev/msm/mdss_mdp_hwio.h b/drivers/video/fbdev/msm/mdss_mdp_hwio.h index 7d495232c198..d9e2b042bfc3 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_hwio.h +++ b/drivers/video/fbdev/msm/mdss_mdp_hwio.h @@ -850,4 +850,8 @@ enum mdss_mdp_pingpong_index { #define MDSS_MDP_REG_TRAFFIC_SHAPER_WR_CLIENT(num) (0x060 + (num * 4)) #define MDSS_MDP_REG_TRAFFIC_SHAPER_FIXPOINT_FACTOR 4 +#define MDSS_MDP_REG_SPLIT_LINK 0x00060 +#define MDSS_MDP_REG_SPLIT_LINK_LEFT_LINK_EN BIT(1) +#define MDSS_MDP_REG_SPLIT_LINK_RIGHT_LINK_EN BIT(2) + #endif diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c index a3511a1a07ef..ea55203afc51 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -1682,6 +1682,16 @@ static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg) mdss_bus_bandwidth_ctrl(true); + /* configure the split link to both sublinks */ + if (is_panel_split_link(ctl->mfd)) { + mdp_video_write(ctx, MDSS_MDP_REG_SPLIT_LINK, 0x3); + /* + * ensure split link register is written before + * enabling timegen + */ + wmb(); + } + mdp_video_write(ctx, MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 1); wmb(); diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index 92413e078244..fa1df94976f9 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -533,6 +533,8 @@ struct mipi_panel_info { char lp11_init; u32 init_delay; u32 post_init_delay; + u32 num_of_sublinks; + u32 lanes_per_sublink; }; struct edp_panel_info { @@ -847,6 +849,7 @@ struct mdss_panel_info { bool is_lpm_mode; bool is_split_display; /* two DSIs in one display, pp split or not */ bool use_pingpong_split; + bool split_link_enabled; /* * index[0] = left layer mixer, value of 0 not valid diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c index 03e78733d168..9c156af6b63c 100644 --- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c +++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c @@ -38,16 +38,23 @@ #define MDSS_DSI_DSIPHY_GLBL_TEST_CTRL 0x1d4 #define MDSS_DSI_DSIPHY_CTRL_0 0x170 #define MDSS_DSI_DSIPHY_CTRL_1 0x174 +#define MDSS_DSI_DSIPHY_CMN_CLK_CFG0 0x0010 +#define MDSS_DSI_DSIPHY_CMN_CLK_CFG1 0x0014 + +#define MDSS_DSI_NUM_DATA_LANES 0x04 +#define MDSS_DSI_NUM_CLK_LANES 0x01 #define SW_RESET BIT(2) #define SW_RESET_PLL BIT(0) #define PWRDN_B BIT(7) /* 8996 */ -#define DATALANE_OFFSET_FROM_BASE_8996 0x100 -#define DSIPHY_CMN_PLL_CNTRL 0x0048 +#define DATALANE_OFFSET_FROM_BASE_8996 0x100 +#define CLKLANE_OFFSET_FROM_BASE_8996 0x300 #define DATALANE_SIZE_8996 0x80 +#define CLKLANE_SIZE_8996 0x80 +#define DSIPHY_CMN_PLL_CNTRL 0x0048 #define DSIPHY_CMN_GLBL_TEST_CTRL 0x0018 #define DSIPHY_CMN_CTRL_0 0x001c #define DSIPHY_CMN_CTRL_1 0x0020 @@ -55,6 +62,24 @@ #define DSIPHY_PLL_CLKBUFLR_EN 0x041c #define DSIPHY_PLL_PLL_BANDGAP 0x0508 +#define DSIPHY_LANE_STRENGTH_CTRL_NUM 0x0002 +#define DSIPHY_LANE_STRENGTH_CTRL_OFFSET 0x0004 +#define DSIPHY_LANE_STRENGTH_CTRL_BASE 0x0038 + +#define DSIPHY_LANE_CFG_NUM 0x0004 +#define DSIPHY_LANE_CFG_OFFSET 0x0004 +#define DSIPHY_LANE_CFG_BASE 0x0000 + +#define DSIPHY_LANE_VREG_NUM 0x0001 +#define DSIPHY_LANE_VREG_OFFSET 0x0004 +#define DSIPHY_LANE_VREG_BASE 0x0064 + +#define DSIPHY_LANE_TIMING_CTRL_NUM 0x0008 +#define DSIPHY_LANE_TIMING_CTRL_OFFSET 0x0004 +#define DSIPHY_LANE_TIMING_CTRL_BASE 0x0018 + +#define DSIPHY_LANE_TEST_STR 0x0014 + #define DSIPHY_LANE_STRENGTH_CTRL_1 0x003c #define DSIPHY_LANE_VREG_CNTRL 0x0064 @@ -131,6 +156,8 @@ #define DSIPHY_PLL_RESETSM_CNTRL5 0x043c +#define DSIPHY_CMN_CLK_CFG1_SPLIT_LINK 0x1 + #define PLL_CALC_DATA(addr0, addr1, data0, data1) \ (((data1) << 24) | ((((addr1)/4) & 0xFF) << 16) | \ ((data0) << 8) | (((addr0)/4) & 0xFF)) @@ -911,35 +938,59 @@ static void mdss_dsi_8996_phy_regulator_enable( int j, off, ln, cnt, ln_off; char *ip; void __iomem *base; + struct mdss_panel_info *panel_info; + if (!ctrl) { + pr_warn("%s: null ctrl pdata\n", __func__); + return; + } + + panel_info = &((ctrl->panel_data).panel_info); pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db); - if (pd->regulator_len != 5) { + if (pd->regulator_len != (MDSS_DSI_NUM_DATA_LANES + + MDSS_DSI_NUM_CLK_LANES)) { pr_warn("%s: invalid regulator settings\n", __func__); return; } - /* 4 lanes + clk lane configuration */ - for (ln = 0; ln < 5; ln++) { - /* - * data lane offset frome base: 0x100 - * data lane size: 0x80 - */ - base = ctrl->phy_io.base + - DATALANE_OFFSET_FROM_BASE_8996; - base += (ln * DATALANE_SIZE_8996); /* lane base */ - - /* vreg ctrl, 1 * 5 */ - cnt = 1; + /* + * data lane offset from base: 0x100 + * data lane size: 0x80 + */ + base = ctrl->phy_io.base + DATALANE_OFFSET_FROM_BASE_8996; + /* data lanes configuration */ + for (ln = 0; ln < MDSS_DSI_NUM_DATA_LANES; ln++) { + /* vreg ctrl, 1 * MDSS_DSI_NUM_DATA_LANES */ + cnt = DSIPHY_LANE_VREG_NUM; + off = DSIPHY_LANE_VREG_BASE; ln_off = cnt * ln; ip = &pd->regulator[ln_off]; - off = 0x64; - for (j = 0; j < cnt; j++, off += 4) + for (j = 0; j < cnt; j++) { MIPI_OUTP(base + off, *ip++); + off += DSIPHY_LANE_VREG_OFFSET; + } + base += DATALANE_SIZE_8996; /* next lane */ } - wmb(); /* make sure registers committed */ + /* + * clk lane offset from base: 0x300 + * clk lane size: 0x80 + */ + base = ctrl->phy_io.base + CLKLANE_OFFSET_FROM_BASE_8996; + /* + * clk lane configuration for vreg ctrl + * for split link there are two clock lanes, one + * clock lane per sublink needs to be configured + */ + off = DSIPHY_LANE_VREG_BASE; + ln_off = MDSS_DSI_NUM_DATA_LANES; + ip = &pd->regulator[ln_off]; + MIPI_OUTP(base + off, *ip); + if (panel_info->split_link_enabled) + MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, *ip); + wmb(); /* make sure registers committed */ } static void mdss_dsi_8996_phy_power_off( @@ -948,31 +999,51 @@ static void mdss_dsi_8996_phy_power_off( int ln; void __iomem *base; u32 data; + struct mdss_panel_info *panel_info; + + if (ctrl) { + panel_info = &((ctrl->panel_data).panel_info); + } else { + pr_warn("%s: null ctrl pdata\n", __func__); + return; + } /* Turn off PLL power */ data = MIPI_INP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0); MIPI_OUTP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0, data & ~BIT(7)); - /* 4 lanes + clk lane configuration */ - for (ln = 0; ln < 5; ln++) { - base = ctrl->phy_io.base + - DATALANE_OFFSET_FROM_BASE_8996; - base += (ln * DATALANE_SIZE_8996); /* lane base */ - + /* data lanes configuration */ + base = ctrl->phy_io.base + DATALANE_OFFSET_FROM_BASE_8996; + for (ln = 0; ln < MDSS_DSI_NUM_DATA_LANES; ln++) { /* turn off phy ldo */ - MIPI_OUTP(base + DSIPHY_LANE_VREG_CNTRL, 0x1c); + MIPI_OUTP(base + DSIPHY_LANE_VREG_BASE, 0x1c); + base += DATALANE_SIZE_8996; /* next lane */ } + + /* clk lane configuration */ + base = ctrl->phy_io.base + CLKLANE_OFFSET_FROM_BASE_8996; + /* turn off phy ldo */ + MIPI_OUTP(base + DSIPHY_LANE_VREG_BASE, 0x1c); + if (panel_info->split_link_enabled) + MIPI_OUTP(base + CLKLANE_SIZE_8996 + + DSIPHY_LANE_VREG_BASE, 0x1c); + MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_LDO_CNTRL, 0x1c); - /* 4 lanes + clk lane configuration */ - for (ln = 0; ln < 5; ln++) { - base = ctrl->phy_io.base + - DATALANE_OFFSET_FROM_BASE_8996; - base += (ln * DATALANE_SIZE_8996); /* lane base */ - + /* data lanes configuration */ + base = ctrl->phy_io.base + DATALANE_OFFSET_FROM_BASE_8996; + for (ln = 0; ln < MDSS_DSI_NUM_DATA_LANES; ln++) { MIPI_OUTP(base + DSIPHY_LANE_STRENGTH_CTRL_1, 0x0); + base += DATALANE_SIZE_8996; /* next lane */ } + /* clk lane configuration */ + base = ctrl->phy_io.base + CLKLANE_OFFSET_FROM_BASE_8996; + MIPI_OUTP(base + DSIPHY_LANE_STRENGTH_CTRL_1, 0x0); + if (panel_info->split_link_enabled) + MIPI_OUTP(base + CLKLANE_SIZE_8996 + + DSIPHY_LANE_STRENGTH_CTRL_1, 0x0); + wmb(); /* make sure registers committed */ } @@ -1008,22 +1079,46 @@ static void mdss_dsi_8996_phy_power_on( struct mdss_dsi_phy_ctrl *pd; char *ip; u32 data; + struct mdss_panel_info *panel_info; + + if (ctrl) { + panel_info = &((ctrl->panel_data).panel_info); + } else { + pr_warn("%s: null ctrl pdata\n", __func__); + return; + } pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db); - /* 4 lanes + clk lane configuration */ - for (ln = 0; ln < 5; ln++) { - base = ctrl->phy_io.base + - DATALANE_OFFSET_FROM_BASE_8996; - base += (ln * DATALANE_SIZE_8996); /* lane base */ - - /* strength, 2 * 5 */ - cnt = 2; + /* data lanes configuration */ + base = ctrl->phy_io.base + DATALANE_OFFSET_FROM_BASE_8996; + for (ln = 0; ln < MDSS_DSI_NUM_DATA_LANES; ln++) { + /* strength, 2 * MDSS_DSI_NUM_DATA_LANES */ + cnt = DSIPHY_LANE_STRENGTH_CTRL_NUM; ln_off = cnt * ln; ip = &pd->strength[ln_off]; - off = 0x38; - for (j = 0; j < cnt; j++, off += 4) + off = DSIPHY_LANE_STRENGTH_CTRL_BASE; + for (j = 0; j < cnt; j++, + off += DSIPHY_LANE_STRENGTH_CTRL_OFFSET) MIPI_OUTP(base + off, *ip++); + base += DATALANE_SIZE_8996; /* next lane */ + } + + /* + * clk lane configuration for strength ctrl + * for split link there are two clock lanes, one + * clock lane per sublink needs to be configured + */ + base = ctrl->phy_io.base + CLKLANE_OFFSET_FROM_BASE_8996; + cnt = DSIPHY_LANE_STRENGTH_CTRL_NUM; + ln_off = MDSS_DSI_NUM_DATA_LANES; + ip = &pd->strength[ln_off]; + off = DSIPHY_LANE_STRENGTH_CTRL_BASE; + for (j = 0; j < cnt; j++, + off += DSIPHY_LANE_STRENGTH_CTRL_OFFSET) { + MIPI_OUTP(base + off, *ip); + if (panel_info->split_link_enabled) + MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, *ip); } mdss_dsi_8996_phy_regulator_enable(ctrl); @@ -1051,67 +1146,126 @@ static void mdss_dsi_8996_phy_config(struct mdss_dsi_ctrl_pdata *ctrl) int j, off, ln, cnt, ln_off; char *ip; void __iomem *base; + struct mdss_panel_info *panel_info; + int num_of_lanes = 0; + + if (ctrl) { + panel_info = &((ctrl->panel_data).panel_info); + } else { + pr_warn("%s: null ctrl pdata\n", __func__); + return; + } pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db); + num_of_lanes = MDSS_DSI_NUM_DATA_LANES + MDSS_DSI_NUM_CLK_LANES; MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_LDO_CNTRL, 0x1c); /* clk_en */ MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_GLBL_TEST_CTRL, 0x1); - if (pd->lanecfg_len != 20) { + if (pd->lanecfg_len != (num_of_lanes * DSIPHY_LANE_CFG_NUM)) { pr_err("%s: wrong lane cfg\n", __func__); return; } - if (pd->strength_len != 10) { + if (pd->strength_len != (num_of_lanes * + DSIPHY_LANE_STRENGTH_CTRL_NUM)) { pr_err("%s: wrong strength ctrl\n", __func__); return; } - if (pd->regulator_len != 5) { + if (pd->regulator_len != (num_of_lanes * DSIPHY_LANE_VREG_NUM)) { pr_err("%s: wrong regulator setting\n", __func__); return; } - /* 4 lanes + clk lane configuration */ - for (ln = 0; ln < 5; ln++) { - /* - * data lane offset frome base: 0x100 - * data lane size: 0x80 - */ - base = ctrl->phy_io.base + - DATALANE_OFFSET_FROM_BASE_8996; - base += (ln * DATALANE_SIZE_8996); /* lane base */ - - /* lane cfg, 4 * 5 */ - cnt = 4; + /* data lanes configuration */ + base = ctrl->phy_io.base + DATALANE_OFFSET_FROM_BASE_8996; + for (ln = 0; ln < MDSS_DSI_NUM_DATA_LANES; ln++) { + /* lane cfg, 4 * MDSS_DSI_NUM_DATA_LANES */ + cnt = DSIPHY_LANE_CFG_NUM; + off = DSIPHY_LANE_CFG_BASE; ln_off = cnt * ln; ip = &pd->lanecfg[ln_off]; - off = 0x0; for (j = 0; j < cnt; j++) { MIPI_OUTP(base + off, *ip++); - off += 4; + off += DSIPHY_LANE_CFG_OFFSET; } /* test str */ - MIPI_OUTP(base + 0x14, 0x0088); /* fixed */ + MIPI_OUTP(base + DSIPHY_LANE_TEST_STR, 0x88); /* fixed */ - /* phy timing, 8 * 5 */ - cnt = 8; + /* phy timing, 8 * MDSS_DSI_NUM_DATA_LANES */ + cnt = DSIPHY_LANE_TIMING_CTRL_NUM; + off = DSIPHY_LANE_TIMING_CTRL_BASE; ln_off = cnt * ln; ip = &pd->timing_8996[ln_off]; - off = 0x18; - for (j = 0; j < cnt; j++, off += 4) + for (j = 0; j < cnt; j++) { MIPI_OUTP(base + off, *ip++); + off += DSIPHY_LANE_TIMING_CTRL_OFFSET; + } - /* strength, 2 * 5 */ - cnt = 2; + /* strength, 2 * MDSS_DSI_NUM_DATA_LANES */ + cnt = DSIPHY_LANE_STRENGTH_CTRL_NUM; + off = DSIPHY_LANE_STRENGTH_CTRL_BASE; ln_off = cnt * ln; ip = &pd->strength[ln_off]; - off = 0x38; - for (j = 0; j < cnt; j++, off += 4) + for (j = 0; j < cnt; j++) { MIPI_OUTP(base + off, *ip++); + off += DSIPHY_LANE_STRENGTH_CTRL_OFFSET; + } + + base += DATALANE_SIZE_8996; /* next lane */ + } + + /* + * clk lane configuration + * for split link there are two clock lanes, one + * clock lane per sublink needs to be configured + */ + base = ctrl->phy_io.base + CLKLANE_OFFSET_FROM_BASE_8996; + cnt = DSIPHY_LANE_CFG_NUM; + off = DSIPHY_LANE_CFG_BASE; + ln_off = cnt * MDSS_DSI_NUM_DATA_LANES; + ip = &pd->lanecfg[ln_off]; + for (j = 0; j < cnt; j++, *ip++) { + MIPI_OUTP(base + off, *ip); + if (panel_info->split_link_enabled) + MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, *ip); + off += DSIPHY_LANE_CFG_OFFSET; + } + + /* test str */ + MIPI_OUTP(base + DSIPHY_LANE_TEST_STR, 0x88); /* fixed */ + if (panel_info->split_link_enabled) + MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, 0x88); + + cnt = DSIPHY_LANE_TIMING_CTRL_NUM; + off = DSIPHY_LANE_TIMING_CTRL_BASE; + ln_off = cnt * MDSS_DSI_NUM_DATA_LANES; + ip = &pd->timing_8996[ln_off]; + for (j = 0; j < cnt; j++, *ip++) { + MIPI_OUTP(base + off, *ip); + if (panel_info->split_link_enabled) + MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, *ip); + off += DSIPHY_LANE_TIMING_CTRL_OFFSET; + } + + /* + * clk lane configuration for timing + * for split link there are two clock lanes, one + * clock lane per sublink needs to be configured + */ + cnt = DSIPHY_LANE_STRENGTH_CTRL_NUM; + off = DSIPHY_LANE_STRENGTH_CTRL_BASE; + ln_off = cnt * MDSS_DSI_NUM_DATA_LANES; + ip = &pd->strength[ln_off]; + for (j = 0; j < cnt; j++, *ip++) { + MIPI_OUTP(base + off, *ip); + if (panel_info->split_link_enabled) + MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, *ip); + off += DSIPHY_LANE_STRENGTH_CTRL_OFFSET; } wmb(); /* make sure registers committed */ @@ -1665,6 +1819,9 @@ int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info, u32 dsi_pclk_rate; u8 lanes = 0, bpp; + if (!panel_info) + return -EINVAL; + if (panel_info->mipi.data_lane3) lanes += 1; if (panel_info->mipi.data_lane2) @@ -1690,6 +1847,8 @@ int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info, } h_period = mdss_panel_get_htotal(panel_info, true); + if (panel_info->split_link_enabled) + h_period *= panel_info->mipi.num_of_sublinks; v_period = mdss_panel_get_vtotal(panel_info); if (ctrl_pdata->refresh_clk_rate || is_diff_frame_rate(panel_info, @@ -1710,7 +1869,12 @@ int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info, clk_rate = panel_info->clk_rate; do_div(clk_rate, 8 * bpp); - dsi_pclk_rate = (u32) clk_rate * lanes; + + if (panel_info->split_link_enabled) + dsi_pclk_rate = (u32) clk_rate * + panel_info->mipi.lanes_per_sublink; + else + dsi_pclk_rate = (u32) clk_rate * lanes; if ((dsi_pclk_rate < 3300000) || (dsi_pclk_rate > 250000000)) dsi_pclk_rate = 35000000; @@ -2320,6 +2484,32 @@ int mdss_dsi_pre_clkoff_cb(void *priv, return rc; } +static void mdss_dsi_split_link_clk_cfg(struct mdss_dsi_ctrl_pdata *ctrl, + int enable) +{ + struct mdss_panel_data *pdata = NULL; + void __iomem *base; + u32 data = 0; + + if (ctrl) + pdata = &ctrl->panel_data; + else { + pr_err("%s: ctrl pdata is NULL\n", __func__); + return; + } + + /* + * for split link there are two clock lanes, and + * both clock lanes needs to be enabled + */ + if (pdata->panel_info.split_link_enabled) { + base = ctrl->phy_io.base; + data = MIPI_INP(base + MDSS_DSI_DSIPHY_CMN_CLK_CFG1); + data |= (enable << DSIPHY_CMN_CLK_CFG1_SPLIT_LINK); + MIPI_OUTP(base + MDSS_DSI_DSIPHY_CMN_CLK_CFG1, data); + } +} + int mdss_dsi_post_clkon_cb(void *priv, enum mdss_dsi_clk_type clk, enum mdss_dsi_clk_state curr_state) @@ -2393,6 +2583,9 @@ int mdss_dsi_post_clkon_cb(void *priv, } if (pdata->panel_info.mipi.force_clk_lane_hs) mdss_dsi_cfg_lane_ctrl(ctrl, BIT(28), 1); + + /* enable split link for cmn clk cfg1 */ + mdss_dsi_split_link_clk_cfg(ctrl, 1); } error: return rc;