diff --git a/msm/dsi/dsi_catalog.c b/msm/dsi/dsi_catalog.c index 1499566f2b..37a790cc65 100644 --- a/msm/dsi/dsi_catalog.c +++ b/msm/dsi/dsi_catalog.c @@ -84,6 +84,7 @@ static void dsi_catalog_cmn_init(struct dsi_ctrl_hw *ctrl, ctrl->ops.configure_cmddma_window = NULL; ctrl->ops.reset_trig_ctrl = NULL; ctrl->ops.log_line_count = NULL; + ctrl->ops.splitlink_cmd_setup = NULL; break; case DSI_CTRL_VERSION_2_0: ctrl->ops.setup_lane_map = dsi_ctrl_hw_20_setup_lane_map; @@ -102,6 +103,7 @@ static void dsi_catalog_cmn_init(struct dsi_ctrl_hw *ctrl, ctrl->ops.configure_cmddma_window = NULL; ctrl->ops.reset_trig_ctrl = NULL; ctrl->ops.log_line_count = NULL; + ctrl->ops.splitlink_cmd_setup = NULL; break; case DSI_CTRL_VERSION_2_2: case DSI_CTRL_VERSION_2_3: @@ -129,6 +131,7 @@ static void dsi_catalog_cmn_init(struct dsi_ctrl_hw *ctrl, ctrl->ops.reset_trig_ctrl = dsi_ctrl_hw_22_reset_trigger_controls; ctrl->ops.log_line_count = dsi_ctrl_hw_22_log_line_count; + ctrl->ops.splitlink_cmd_setup = dsi_ctrl_hw_22_configure_splitlink; break; default: break; diff --git a/msm/dsi/dsi_catalog.h b/msm/dsi/dsi_catalog.h index f76aae181e..7ca0dac0b2 100644 --- a/msm/dsi/dsi_catalog.h +++ b/msm/dsi/dsi_catalog.h @@ -290,4 +290,6 @@ int dsi_catalog_phy_pll_setup(struct dsi_phy_hw *phy, u32 pll_ver); int dsi_pll_5nm_configure(void *pll, bool commit); int dsi_pll_5nm_toggle(void *pll, bool prepare); +void dsi_ctrl_hw_22_configure_splitlink(struct dsi_ctrl_hw *ctrl, + struct dsi_host_common_cfg *common_cfg, u32 sublink); #endif /* _DSI_CATALOG_H_ */ diff --git a/msm/dsi/dsi_ctrl.c b/msm/dsi/dsi_ctrl.c index 2c5ee1b8df..7552ed9217 100644 --- a/msm/dsi/dsi_ctrl.c +++ b/msm/dsi/dsi_ctrl.c @@ -997,7 +997,7 @@ static int dsi_ctrl_update_link_freqs(struct dsi_ctrl *dsi_ctrl, if (host_cfg->data_lanes & DSI_DATA_LANE_3) num_of_lanes++; - if (split_link->split_link_enabled) + if (split_link->enabled) num_of_lanes = split_link->lanes_per_sublink; config->common_config.num_data_lanes = num_of_lanes; @@ -1360,6 +1360,9 @@ static void dsi_kickoff_msg_tx(struct dsi_ctrl *dsi_ctrl, { u32 hw_flags = 0; struct dsi_ctrl_hw_ops dsi_hw_ops = dsi_ctrl->hw.ops; + struct dsi_split_link_config *split_link; + + split_link = &(dsi_ctrl->host_config.common_config.split_link); SDE_EVT32(dsi_ctrl->cell_index, SDE_EVTLOG_FUNC_ENTRY, flags, msg->flags); @@ -1368,6 +1371,10 @@ static void dsi_kickoff_msg_tx(struct dsi_ctrl *dsi_ctrl, dsi_hw_ops.reset_trig_ctrl(&dsi_ctrl->hw, &dsi_ctrl->host_config.common_config); + if (dsi_hw_ops.splitlink_cmd_setup && split_link->enabled) + dsi_hw_ops.splitlink_cmd_setup(&dsi_ctrl->hw, + &dsi_ctrl->host_config.common_config, flags); + /* * Always enable DMA scheduling for video mode panel. * diff --git a/msm/dsi/dsi_ctrl.h b/msm/dsi/dsi_ctrl.h index e9d3fa5fdb..52a99e7a55 100644 --- a/msm/dsi/dsi_ctrl.h +++ b/msm/dsi/dsi_ctrl.h @@ -33,6 +33,8 @@ * display panel dtsi file instead of default. * @DSI_CTRL_CMD_ASYNC_WAIT: Command flag to indicate that the wait for done * for this command is asynchronous and must be queued. + * @DSI_CTRL_CMD_SUBLINK0: Send the command in splitlink sublink0 only. + * @DSI_CTRL_CMD_SUBLINK1: Send the command in splitlink sublink1 only. */ #define DSI_CTRL_CMD_READ 0x1 #define DSI_CTRL_CMD_BROADCAST 0x2 @@ -44,6 +46,8 @@ #define DSI_CTRL_CMD_NON_EMBEDDED_MODE 0x80 #define DSI_CTRL_CMD_CUSTOM_DMA_SCHED 0x100 #define DSI_CTRL_CMD_ASYNC_WAIT 0x200 +#define DSI_CTRL_CMD_SUBLINK0 0x400 +#define DSI_CTRL_CMD_SUBLINK1 0x800 /* DSI embedded mode fifo size * If the command is greater than 256 bytes it is sent in non-embedded mode. diff --git a/msm/dsi/dsi_ctrl_hw.h b/msm/dsi/dsi_ctrl_hw.h index 92689496fb..a1a7cf1bc9 100644 --- a/msm/dsi/dsi_ctrl_hw.h +++ b/msm/dsi/dsi_ctrl_hw.h @@ -869,6 +869,15 @@ struct dsi_ctrl_hw_ops { * @cmd_mode: Boolean to indicate command mode operation. */ u32 (*log_line_count)(struct dsi_ctrl_hw *ctrl, bool cmd_mode); + + /** + * hw.ops.splitlink_cmd_setup() - configure the sublink to transfer + * @ctrl: Pointer to the controller host hardware. + * @common_cfg: Common configuration parameters. + * @sublink: Which sublink to transfer the command. + */ + void (*splitlink_cmd_setup)(struct dsi_ctrl_hw *ctrl, + struct dsi_host_common_cfg *common_cfg, u32 sublink); }; /* diff --git a/msm/dsi/dsi_ctrl_hw_2_2.c b/msm/dsi/dsi_ctrl_hw_2_2.c index 5ae0929e09..0a9d8e845d 100644 --- a/msm/dsi/dsi_ctrl_hw_2_2.c +++ b/msm/dsi/dsi_ctrl_hw_2_2.c @@ -1,11 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. */ #include #include "dsi_ctrl_hw.h" #include "dsi_ctrl_reg.h" #include "dsi_hw.h" +#include "dsi_ctrl.h" #include "dsi_catalog.h" #define DISP_CC_MISC_CMD_REG_OFF 0x00 @@ -280,3 +281,27 @@ u32 dsi_ctrl_hw_22_log_line_count(struct dsi_ctrl_hw *ctrl, bool cmd_mode) + MDP_INTF_LINE_COUNT_OFFSET); return reg; } + +void dsi_ctrl_hw_22_configure_splitlink(struct dsi_ctrl_hw *ctrl, + struct dsi_host_common_cfg *common_cfg, u32 flags) +{ + u32 reg = 0; + + reg = DSI_R32(ctrl, DSI_SPLIT_LINK); + + /* DMA_LINK_SEL */ + reg &= ~(0x7 << 12); + + /* Send command to both sublinks unless specified */ + if (flags & DSI_CTRL_CMD_SUBLINK0) + reg |= BIT(12); + else if (flags & DSI_CTRL_CMD_SUBLINK1) + reg |= BIT(13); + else + reg |= (BIT(12) | BIT(13)); + + DSI_W32(ctrl, DSI_SPLIT_LINK, reg); + + /* Make sure the split link config is updated */ + wmb(); +} diff --git a/msm/dsi/dsi_ctrl_hw_cmn.c b/msm/dsi/dsi_ctrl_hw_cmn.c index 8512085ed3..f48c35d909 100644 --- a/msm/dsi/dsi_ctrl_hw_cmn.c +++ b/msm/dsi/dsi_ctrl_hw_cmn.c @@ -56,7 +56,7 @@ static void dsi_split_link_setup(struct dsi_ctrl_hw *ctrl, { u32 reg; - if (!cfg->split_link.split_link_enabled) + if (!cfg->split_link.enabled) return; reg = DSI_R32(ctrl, DSI_SPLIT_LINK); @@ -69,6 +69,14 @@ static void dsi_split_link_setup(struct dsi_ctrl_hw *ctrl, reg &= ~(0x7 << 20); reg |= DSI_CTRL_MDP0_LINK_SEL; + /* COMMAND_INPUT_SWAP|VIDEO_INPUT_SWAP */ + if (cfg->split_link.sublink_swap) { + if (cfg->split_link.panel_mode == DSI_OP_CMD_MODE) + reg |= BIT(8); + else + reg |= BIT(4); + } + /* EN */ reg |= 0x1; diff --git a/msm/dsi/dsi_defs.h b/msm/dsi/dsi_defs.h index 9248e8cf4f..2ab858ec6a 100644 --- a/msm/dsi/dsi_defs.h +++ b/msm/dsi/dsi_defs.h @@ -443,14 +443,20 @@ struct dsi_mode_info { /** * struct dsi_split_link_config - Split Link Configuration - * @split_link_enabled: Split Link Enabled. + * @enabled: Split Link Enabled. + * @sublink_swap: Split link left right sublinks swap. * @num_sublinks: Number of sublinks. * @lanes_per_sublink: Number of lanes per sublink. + * @panel_mode: Specifies cmd or video mode. + * @lanes_enabled: Specifies what all lanes are enabled. */ struct dsi_split_link_config { - bool split_link_enabled; + bool enabled; + bool sublink_swap; u32 num_sublinks; u32 lanes_per_sublink; + u8 lanes_enabled; + enum dsi_op_mode panel_mode; }; /** diff --git a/msm/dsi/dsi_display.c b/msm/dsi/dsi_display.c index 57ba99a520..b09b8a9552 100644 --- a/msm/dsi/dsi_display.c +++ b/msm/dsi/dsi_display.c @@ -2055,7 +2055,7 @@ static void adjust_timing_by_ctrl_count(const struct dsi_display *display, struct dsi_display_mode *mode) { struct dsi_host_common_cfg *host = &display->panel->host_config; - bool is_split_link = host->split_link.split_link_enabled; + bool is_split_link = host->split_link.enabled; u32 sublinks_count = host->split_link.num_sublinks; if (is_split_link && sublinks_count > 1) { @@ -4104,6 +4104,7 @@ static int dsi_display_res_init(struct dsi_display *display) display_for_each_ctrl(i, display) { struct msm_dsi_phy *phy = display->ctrl[i].phy; + struct dsi_host_common_cfg *host = &display->panel->host_config; phy->cfg.force_clk_lane_hs = display->panel->host_config.force_hs_clk_lane; @@ -4117,6 +4118,10 @@ static int dsi_display_res_init(struct dsi_display *display) if ((display->panel->dyn_clk_caps.dyn_clk_support) && (display->panel->panel_mode == DSI_OP_VIDEO_MODE)) dsi_phy_pll_parse_dfps_data(phy); + + phy->cfg.split_link.enabled = host->split_link.enabled; + phy->cfg.split_link.num_sublinks = host->split_link.num_sublinks; + phy->cfg.split_link.lanes_per_sublink = host->split_link.lanes_per_sublink; } rc = dsi_display_parse_lane_map(display); @@ -5296,16 +5301,9 @@ static int dsi_display_validate_split_link(struct dsi_display *display) struct dsi_display_ctrl *ctrl; struct dsi_host_common_cfg *host = &display->panel->host_config; - if (!host->split_link.split_link_enabled) + if (!host->split_link.enabled) return 0; - if (display->panel->panel_mode == DSI_OP_CMD_MODE) { - DSI_ERR("[%s] split link is not supported in command mode\n", - display->name); - rc = -ENOTSUPP; - goto error; - } - display_for_each_ctrl(i, display) { ctrl = &display->ctrl[i]; if (!ctrl->ctrl->split_link_supported) { @@ -5316,13 +5314,14 @@ static int dsi_display_validate_split_link(struct dsi_display *display) } set_bit(DSI_PHY_SPLIT_LINK, ctrl->phy->hw.feature_map); + host->split_link.panel_mode = display->panel->panel_mode; } DSI_DEBUG("Split link is enabled\n"); return 0; error: - host->split_link.split_link_enabled = false; + host->split_link.enabled = false; return rc; } @@ -6552,7 +6551,7 @@ int dsi_display_get_info(struct drm_connector *connector, info->te_source = display->te_source; host = &display->panel->host_config; - if (host->split_link.split_link_enabled) + if (host->split_link.enabled) info->capabilities |= MSM_DISPLAY_SPLIT_LINK; info->dsc_count = display->panel->dsc_count; @@ -6818,7 +6817,7 @@ int dsi_display_get_modes(struct dsi_display *display, display_mode.timing.mdp_transfer_time_us; } - is_split_link = host->split_link.split_link_enabled; + is_split_link = host->split_link.enabled; sublinks_count = host->split_link.num_sublinks; if (is_split_link && sublinks_count > 1) { display_mode.timing.h_active *= sublinks_count; @@ -6916,7 +6915,7 @@ int dsi_display_get_panel_vfp(void *dsi_display, } host = &display->panel->host_config; - if (host->split_link.split_link_enabled) + if (host->split_link.enabled) h_active *= host->split_link.num_sublinks; else h_active *= display->ctrl_count; diff --git a/msm/dsi/dsi_panel.c b/msm/dsi/dsi_panel.c index 838ffb2449..1a5916fa3d 100644 --- a/msm/dsi/dsi_panel.c +++ b/msm/dsi/dsi_panel.c @@ -1157,7 +1157,7 @@ static void dsi_panel_parse_split_link_config(struct dsi_host_common_cfg *host, if (!supported) { DSI_DEBUG("[%s] Split link is not supported\n", name); - split_link->split_link_enabled = false; + split_link->enabled = false; return; } @@ -1177,9 +1177,14 @@ static void dsi_panel_parse_split_link_config(struct dsi_host_common_cfg *host, split_link->lanes_per_sublink = val; } + supported = utils->read_bool(utils->data, "qcom,split-link-sublink-swap"); + + if (!supported) + split_link->sublink_swap = false; + DSI_DEBUG("[%s] Split link is supported %d-%d\n", name, split_link->num_sublinks, split_link->lanes_per_sublink); - split_link->split_link_enabled = true; + split_link->enabled = true; } static int dsi_panel_parse_host_config(struct dsi_panel *panel) diff --git a/msm/dsi/dsi_phy_hw.h b/msm/dsi/dsi_phy_hw.h index dfd213250c..cf940e3a57 100644 --- a/msm/dsi/dsi_phy_hw.h +++ b/msm/dsi/dsi_phy_hw.h @@ -116,6 +116,7 @@ struct dsi_phy_per_lane_cfgs { * @force_clk_lane_hs:Boolean whether to force clock lane in HS mode. * @phy_type: Phy-type (Dphy/Cphy). * @bit_clk_rate_hz: DSI bit clk rate in HZ. + * @split_link: DSI split link config data. */ struct dsi_phy_cfg { struct dsi_phy_per_lane_cfgs lanecfg; @@ -128,6 +129,7 @@ struct dsi_phy_cfg { bool force_clk_lane_hs; enum dsi_phy_type phy_type; unsigned long bit_clk_rate_hz; + struct dsi_split_link_config split_link; }; struct dsi_phy_hw; diff --git a/msm/dsi/dsi_phy_hw_v4_0.c b/msm/dsi/dsi_phy_hw_v4_0.c index 1705c3bd25..380566121e 100644 --- a/msm/dsi/dsi_phy_hw_v4_0.c +++ b/msm/dsi/dsi_phy_hw_v4_0.c @@ -7,6 +7,7 @@ #include #include #include "dsi_hw.h" +#include "dsi_defs.h" #include "dsi_phy_hw.h" #include "dsi_catalog.h" @@ -61,6 +62,7 @@ #define DSIPHY_CMN_LANE_STATUS0 0x148 #define DSIPHY_CMN_LANE_STATUS1 0x14C #define DSIPHY_CMN_GLBL_DIGTOP_SPARE10 0x1AC +#define DSIPHY_CMN_CMN_SL_DSI_LANE_CTRL1 0x1B4 /* n = 0..3 for data lanes and n = 4 for clock lane */ #define DSIPHY_LNX_CFG0(n) (0x200 + (0x80 * (n))) @@ -156,12 +158,17 @@ static void dsi_phy_hw_v4_0_lane_settings(struct dsi_phy_hw *phy, u8 tx_dctrl_v4[] = {0x00, 0x00, 0x00, 0x04, 0x01}; u8 tx_dctrl_v4_1[] = {0x40, 0x40, 0x40, 0x46, 0x41}; u8 *tx_dctrl; + bool split_link_enabled; + u32 lanes_per_sublink; if (phy->version >= DSI_PHY_VERSION_4_1) tx_dctrl = &tx_dctrl_v4_1[0]; else tx_dctrl = &tx_dctrl_v4[0]; + split_link_enabled = cfg->split_link.enabled; + lanes_per_sublink = cfg->split_link.lanes_per_sublink; + /* Strength ctrl settings */ for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++) { /* @@ -182,6 +189,19 @@ static void dsi_phy_hw_v4_0_lane_settings(struct dsi_phy_hw *phy, DSI_W32(phy, DSIPHY_LNX_TX_DCTRL(i), tx_dctrl[i]); } + /* remove below check if cphy splitlink is enabled */ + if (split_link_enabled && (cfg->phy_type == DSI_PHY_TYPE_CPHY)) + return; + + /* Configure the splitlink clock lane with clk lane settings */ + if (split_link_enabled) { + DSI_W32(phy, DSIPHY_LNX_LPRX_CTRL(5), 0x0); + DSI_W32(phy, DSIPHY_LNX_PIN_SWAP(5), 0x0); + DSI_W32(phy, DSIPHY_LNX_CFG0(5), cfg->lanecfg.lane[4][0]); + DSI_W32(phy, DSIPHY_LNX_CFG1(5), cfg->lanecfg.lane[4][1]); + DSI_W32(phy, DSIPHY_LNX_CFG2(5), cfg->lanecfg.lane[4][2]); + DSI_W32(phy, DSIPHY_LNX_TX_DCTRL(5), tx_dctrl[4]); + } } void dsi_phy_hw_v4_0_commit_phy_timing(struct dsi_phy_hw *phy, @@ -331,6 +351,8 @@ static void dsi_phy_hw_dphy_enable(struct dsi_phy_hw *phy, u32 glbl_hstx_str_ctrl_0 = 0; u32 glbl_rescode_top_ctrl = 0; u32 glbl_rescode_bot_ctrl = 0; + bool split_link_enabled; + u32 lanes_per_sublink; /* Alter PHY configurations if data rate less than 1.5GHZ*/ if (cfg->bit_clk_rate_hz <= 1500000000) @@ -356,10 +378,17 @@ static void dsi_phy_hw_dphy_enable(struct dsi_phy_hw *phy, glbl_rescode_bot_ctrl = 0x3c; } + split_link_enabled = cfg->split_link.enabled; + lanes_per_sublink = cfg->split_link.lanes_per_sublink; /* de-assert digital and pll power down */ data = BIT(6) | BIT(5); DSI_W32(phy, DSIPHY_CMN_CTRL_0, data); + if (split_link_enabled) { + data = DSI_R32(phy, DSIPHY_CMN_GLBL_CTRL); + /* set SPLIT_LINK_ENABLE in global control */ + DSI_W32(phy, DSIPHY_CMN_GLBL_CTRL, (data | BIT(5))); + } /* Assert PLL core reset */ DSI_W32(phy, DSIPHY_CMN_PLL_CNTRL, 0x00); @@ -389,10 +418,23 @@ static void dsi_phy_hw_dphy_enable(struct dsi_phy_hw *phy, glbl_rescode_bot_ctrl); DSI_W32(phy, DSIPHY_CMN_GLBL_LPTX_STR_CTRL, 0x55); - /* Remove power down from all blocks */ - DSI_W32(phy, DSIPHY_CMN_CTRL_0, 0x7f); + if (split_link_enabled) { + if (lanes_per_sublink == 1) { + /* remove Lane1 and Lane3 configs */ + DSI_W32(phy, DSIPHY_CMN_CTRL_0, 0xed); + DSI_W32(phy, DSIPHY_CMN_LANE_CTRL0, 0x35); + } else { + /* enable all together with sublink clock */ + DSI_W32(phy, DSIPHY_CMN_CTRL_0, 0xff); + DSI_W32(phy, DSIPHY_CMN_LANE_CTRL0, 0x3F); + } - DSI_W32(phy, DSIPHY_CMN_LANE_CTRL0, 0x1F); + DSI_W32(phy, DSIPHY_CMN_CMN_SL_DSI_LANE_CTRL1, 0x03); + } else { + /* Remove power down from all blocks */ + DSI_W32(phy, DSIPHY_CMN_CTRL_0, 0x7f); + DSI_W32(phy, DSIPHY_CMN_LANE_CTRL0, 0x1F); + } /* Select full-rate mode */ DSI_W32(phy, DSIPHY_CMN_CTRL_2, 0x40); @@ -472,8 +514,8 @@ void dsi_phy_hw_v4_0_disable(struct dsi_phy_hw *phy, dsi_phy_hw_v4_0_config_lpcdrx(phy, cfg, false); data = DSI_R32(phy, DSIPHY_CMN_CTRL_0); - /* disable all lanes */ - data &= ~0x1F; + /* disable all lanes and splitlink clk lane*/ + data &= ~0x9F; DSI_W32(phy, DSIPHY_CMN_CTRL_0, data); DSI_W32(phy, DSIPHY_CMN_LANE_CTRL0, 0);