|
@@ -78,6 +78,11 @@ static int dsi_display_config_clk_gating(struct dsi_display *display,
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (display->panel->host_config.force_hs_clk_lane) {
|
|
|
|
+ pr_debug("no dsi clock gating for continuous clock mode\n");
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
mctrl = &display->ctrl[display->clk_master_idx];
|
|
mctrl = &display->ctrl[display->clk_master_idx];
|
|
if (!mctrl) {
|
|
if (!mctrl) {
|
|
pr_err("Invalid controller\n");
|
|
pr_err("Invalid controller\n");
|
|
@@ -586,8 +591,11 @@ static bool dsi_display_validate_reg_read(struct dsi_panel *panel)
|
|
for (j = 0; j < config->groups; ++j) {
|
|
for (j = 0; j < config->groups; ++j) {
|
|
for (i = 0; i < len; ++i) {
|
|
for (i = 0; i < len; ++i) {
|
|
if (config->return_buf[i] !=
|
|
if (config->return_buf[i] !=
|
|
- config->status_value[group + i])
|
|
|
|
|
|
+ config->status_value[group + i]) {
|
|
|
|
+ DRM_ERROR("mismatch: 0x%x\n",
|
|
|
|
+ config->return_buf[i]);
|
|
break;
|
|
break;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
if (i == len)
|
|
if (i == len)
|
|
@@ -828,6 +836,11 @@ int dsi_display_check_status(struct drm_connector *connector, void *display,
|
|
if (te_check_override && gpio_is_valid(dsi_display->disp_te_gpio))
|
|
if (te_check_override && gpio_is_valid(dsi_display->disp_te_gpio))
|
|
status_mode = ESD_MODE_PANEL_TE;
|
|
status_mode = ESD_MODE_PANEL_TE;
|
|
|
|
|
|
|
|
+ if (status_mode == ESD_MODE_PANEL_TE) {
|
|
|
|
+ rc = dsi_display_status_check_te(dsi_display);
|
|
|
|
+ goto exit;
|
|
|
|
+ }
|
|
|
|
+
|
|
dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
|
|
dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
|
|
DSI_ALL_CLKS, DSI_CLK_ON);
|
|
DSI_ALL_CLKS, DSI_CLK_ON);
|
|
|
|
|
|
@@ -843,23 +856,25 @@ int dsi_display_check_status(struct drm_connector *connector, void *display,
|
|
} else if (status_mode == ESD_MODE_PANEL_TE) {
|
|
} else if (status_mode == ESD_MODE_PANEL_TE) {
|
|
rc = dsi_display_status_check_te(dsi_display);
|
|
rc = dsi_display_status_check_te(dsi_display);
|
|
} else {
|
|
} else {
|
|
- pr_warn("unsupported check status mode\n");
|
|
|
|
|
|
+ pr_warn("Unsupported check status mode: %d\n", status_mode);
|
|
panel->esd_config.esd_enabled = false;
|
|
panel->esd_config.esd_enabled = false;
|
|
}
|
|
}
|
|
|
|
|
|
- /* Unmask error interrupts */
|
|
|
|
|
|
+ /* Unmask error interrupts if check passed*/
|
|
if (rc > 0) {
|
|
if (rc > 0) {
|
|
dsi_display_set_ctrl_esd_check_flag(dsi_display, false);
|
|
dsi_display_set_ctrl_esd_check_flag(dsi_display, false);
|
|
dsi_display_mask_ctrl_error_interrupts(dsi_display, mask,
|
|
dsi_display_mask_ctrl_error_interrupts(dsi_display, mask,
|
|
false);
|
|
false);
|
|
- } else {
|
|
|
|
- /* Handle Panel failures during display disable sequence */
|
|
|
|
- atomic_set(&panel->esd_recovery_pending, 1);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
|
|
dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
|
|
DSI_ALL_CLKS, DSI_CLK_OFF);
|
|
DSI_ALL_CLKS, DSI_CLK_OFF);
|
|
|
|
|
|
|
|
+exit:
|
|
|
|
+ /* Handle Panel failures during display disable sequence */
|
|
|
|
+ if (rc <=0)
|
|
|
|
+ atomic_set(&panel->esd_recovery_pending, 1);
|
|
|
|
+
|
|
release_panel_lock:
|
|
release_panel_lock:
|
|
dsi_panel_release_panel_lock(panel);
|
|
dsi_panel_release_panel_lock(panel);
|
|
SDE_EVT32(SDE_EVTLOG_FUNC_EXIT);
|
|
SDE_EVT32(SDE_EVTLOG_FUNC_EXIT);
|
|
@@ -1620,12 +1635,25 @@ static int dsi_display_debugfs_deinit(struct dsi_display *display)
|
|
static void adjust_timing_by_ctrl_count(const struct dsi_display *display,
|
|
static void adjust_timing_by_ctrl_count(const struct dsi_display *display,
|
|
struct dsi_display_mode *mode)
|
|
struct dsi_display_mode *mode)
|
|
{
|
|
{
|
|
- mode->timing.h_active /= display->ctrl_count;
|
|
|
|
- mode->timing.h_front_porch /= display->ctrl_count;
|
|
|
|
- mode->timing.h_sync_width /= display->ctrl_count;
|
|
|
|
- mode->timing.h_back_porch /= display->ctrl_count;
|
|
|
|
- mode->timing.h_skew /= display->ctrl_count;
|
|
|
|
- mode->pixel_clk_khz /= display->ctrl_count;
|
|
|
|
|
|
+ struct dsi_host_common_cfg *host = &display->panel->host_config;
|
|
|
|
+ bool is_split_link = host->split_link.split_link_enabled;
|
|
|
|
+ u32 sublinks_count = host->split_link.num_sublinks;
|
|
|
|
+
|
|
|
|
+ if (is_split_link && sublinks_count > 1) {
|
|
|
|
+ mode->timing.h_active /= sublinks_count;
|
|
|
|
+ mode->timing.h_front_porch /= sublinks_count;
|
|
|
|
+ mode->timing.h_sync_width /= sublinks_count;
|
|
|
|
+ mode->timing.h_back_porch /= sublinks_count;
|
|
|
|
+ mode->timing.h_skew /= sublinks_count;
|
|
|
|
+ mode->pixel_clk_khz /= sublinks_count;
|
|
|
|
+ } else {
|
|
|
|
+ mode->timing.h_active /= display->ctrl_count;
|
|
|
|
+ mode->timing.h_front_porch /= display->ctrl_count;
|
|
|
|
+ mode->timing.h_sync_width /= display->ctrl_count;
|
|
|
|
+ mode->timing.h_back_porch /= display->ctrl_count;
|
|
|
|
+ mode->timing.h_skew /= display->ctrl_count;
|
|
|
|
+ mode->pixel_clk_khz /= display->ctrl_count;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
static int dsi_display_is_ulps_req_valid(struct dsi_display *display,
|
|
static int dsi_display_is_ulps_req_valid(struct dsi_display *display,
|
|
@@ -2366,7 +2394,9 @@ static int dsi_display_ctrl_init(struct dsi_display *display)
|
|
} else {
|
|
} else {
|
|
display_for_each_ctrl(i, display) {
|
|
display_for_each_ctrl(i, display) {
|
|
ctrl = &display->ctrl[i];
|
|
ctrl = &display->ctrl[i];
|
|
- rc = dsi_ctrl_update_host_init_state(ctrl->ctrl, true);
|
|
|
|
|
|
+ rc = dsi_ctrl_update_host_state(ctrl->ctrl,
|
|
|
|
+ DSI_CTRL_OP_HOST_INIT,
|
|
|
|
+ true);
|
|
if (rc)
|
|
if (rc)
|
|
pr_debug("host init update failed rc=%d\n", rc);
|
|
pr_debug("host init update failed rc=%d\n", rc);
|
|
}
|
|
}
|
|
@@ -2450,6 +2480,25 @@ static int dsi_display_ctrl_host_disable(struct dsi_display *display)
|
|
struct dsi_display_ctrl *m_ctrl, *ctrl;
|
|
struct dsi_display_ctrl *m_ctrl, *ctrl;
|
|
|
|
|
|
m_ctrl = &display->ctrl[display->cmd_master_idx];
|
|
m_ctrl = &display->ctrl[display->cmd_master_idx];
|
|
|
|
+ /*
|
|
|
|
+ * For platforms where ULPS is controlled by DSI controller block,
|
|
|
|
+ * do not disable dsi controller block if lanes are to be
|
|
|
|
+ * kept in ULPS during suspend. So just update the SW state
|
|
|
|
+ * and return early.
|
|
|
|
+ */
|
|
|
|
+ if (display->panel->ulps_suspend_enabled &&
|
|
|
|
+ !m_ctrl->phy->hw.ops.ulps_ops.ulps_request) {
|
|
|
|
+ display_for_each_ctrl(i, display) {
|
|
|
|
+ ctrl = &display->ctrl[i];
|
|
|
|
+ rc = dsi_ctrl_update_host_state(ctrl->ctrl,
|
|
|
|
+ DSI_CTRL_OP_HOST_ENGINE,
|
|
|
|
+ false);
|
|
|
|
+ if (rc)
|
|
|
|
+ pr_debug("host state update failed %d\n", rc);
|
|
|
|
+ }
|
|
|
|
+ return rc;
|
|
|
|
+ }
|
|
|
|
+
|
|
display_for_each_ctrl(i, display) {
|
|
display_for_each_ctrl(i, display) {
|
|
ctrl = &display->ctrl[i];
|
|
ctrl = &display->ctrl[i];
|
|
if (!ctrl->ctrl || (ctrl == m_ctrl))
|
|
if (!ctrl->ctrl || (ctrl == m_ctrl))
|
|
@@ -4609,6 +4658,42 @@ static int dsi_display_force_update_dsi_clk(struct dsi_display *display)
|
|
return rc;
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int dsi_display_validate_split_link(struct dsi_display *display)
|
|
|
|
+{
|
|
|
|
+ int i, rc = 0;
|
|
|
|
+ struct dsi_display_ctrl *ctrl;
|
|
|
|
+ struct dsi_host_common_cfg *host = &display->panel->host_config;
|
|
|
|
+
|
|
|
|
+ if (!host->split_link.split_link_enabled)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ if (display->panel->panel_mode == DSI_OP_CMD_MODE) {
|
|
|
|
+ pr_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) {
|
|
|
|
+ pr_err("[%s] split link is not supported by hw\n",
|
|
|
|
+ display->name);
|
|
|
|
+ rc = -ENOTSUPP;
|
|
|
|
+ goto error;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ set_bit(DSI_PHY_SPLIT_LINK, ctrl->phy->hw.feature_map);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pr_debug("Split link is enabled\n");
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+error:
|
|
|
|
+ host->split_link.split_link_enabled = false;
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* dsi_display_bind - bind dsi device with controlling device
|
|
* dsi_display_bind - bind dsi device with controlling device
|
|
* @dev: Pointer to base of platform device
|
|
* @dev: Pointer to base of platform device
|
|
@@ -4649,8 +4734,28 @@ static int dsi_display_bind(struct device *dev,
|
|
|
|
|
|
if (!display->fw)
|
|
if (!display->fw)
|
|
display->name = display->panel_node->name;
|
|
display->name = display->panel_node->name;
|
|
|
|
+
|
|
|
|
+ /* defer bind if ext bridge driver is not loaded */
|
|
|
|
+ if (display->panel && display->panel->host_config.ext_bridge_mode) {
|
|
|
|
+ for (i = 0; i < display->ext_bridge_cnt; i++) {
|
|
|
|
+ if (!of_drm_find_bridge(
|
|
|
|
+ display->ext_bridge[i].node_of)) {
|
|
|
|
+ pr_debug("defer for bridge[%d] %s\n", i,
|
|
|
|
+ display->ext_bridge[i].node_of->full_name);
|
|
|
|
+ return -EPROBE_DEFER;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
mutex_lock(&display->display_lock);
|
|
mutex_lock(&display->display_lock);
|
|
|
|
|
|
|
|
+ rc = dsi_display_validate_split_link(display);
|
|
|
|
+ if (rc) {
|
|
|
|
+ pr_err("[%s] split link validation failed, rc=%d\n",
|
|
|
|
+ display->name, rc);
|
|
|
|
+ goto error;
|
|
|
|
+ }
|
|
|
|
+
|
|
rc = dsi_display_debugfs_init(display);
|
|
rc = dsi_display_debugfs_init(display);
|
|
if (rc) {
|
|
if (rc) {
|
|
pr_err("[%s] debugfs init failed, rc=%d\n", display->name, rc);
|
|
pr_err("[%s] debugfs init failed, rc=%d\n", display->name, rc);
|
|
@@ -5273,7 +5378,7 @@ static struct dsi_display_ext_bridge *dsi_display_ext_get_bridge(
|
|
sde_conn = to_sde_connector(conn_iter);
|
|
sde_conn = to_sde_connector(conn_iter);
|
|
if (sde_conn->encoder == bridge->encoder) {
|
|
if (sde_conn->encoder == bridge->encoder) {
|
|
display = sde_conn->display;
|
|
display = sde_conn->display;
|
|
- for (i = 0; i < display->ctrl_count; i++) {
|
|
|
|
|
|
+ display_for_each_ctrl(i, display) {
|
|
if (display->ext_bridge[i].bridge == bridge)
|
|
if (display->ext_bridge[i].bridge == bridge)
|
|
return &display->ext_bridge[i];
|
|
return &display->ext_bridge[i];
|
|
}
|
|
}
|
|
@@ -5440,6 +5545,9 @@ int dsi_display_drm_ext_bridge_init(struct dsi_display *display,
|
|
struct drm_bridge *prev_bridge = bridge;
|
|
struct drm_bridge *prev_bridge = bridge;
|
|
int rc = 0, i;
|
|
int rc = 0, i;
|
|
|
|
|
|
|
|
+ if (display->panel && !display->panel->host_config.ext_bridge_mode)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
for (i = 0; i < display->ext_bridge_cnt; i++) {
|
|
for (i = 0; i < display->ext_bridge_cnt; i++) {
|
|
struct dsi_display_ext_bridge *ext_bridge_info =
|
|
struct dsi_display_ext_bridge *ext_bridge_info =
|
|
&display->ext_bridge[i];
|
|
&display->ext_bridge[i];
|
|
@@ -5750,8 +5858,10 @@ int dsi_display_get_modes(struct dsi_display *display,
|
|
{
|
|
{
|
|
struct dsi_dfps_capabilities dfps_caps;
|
|
struct dsi_dfps_capabilities dfps_caps;
|
|
struct dsi_display_ctrl *ctrl;
|
|
struct dsi_display_ctrl *ctrl;
|
|
|
|
+ struct dsi_host_common_cfg *host = &display->panel->host_config;
|
|
|
|
+ bool is_split_link;
|
|
u32 num_dfps_rates, panel_mode_count, total_mode_count;
|
|
u32 num_dfps_rates, panel_mode_count, total_mode_count;
|
|
- u32 mode_idx, array_idx = 0;
|
|
|
|
|
|
+ u32 sublinks_count, mode_idx, array_idx = 0;
|
|
struct dsi_dyn_clk_caps *dyn_clk_caps;
|
|
struct dsi_dyn_clk_caps *dyn_clk_caps;
|
|
int i, start, end, rc = -EINVAL;
|
|
int i, start, end, rc = -EINVAL;
|
|
|
|
|
|
@@ -5827,15 +5937,25 @@ int dsi_display_get_modes(struct dsi_display *display,
|
|
panel_mode.timing.dsi_transfer_time_us;
|
|
panel_mode.timing.dsi_transfer_time_us;
|
|
}
|
|
}
|
|
|
|
|
|
- panel_mode.timing.h_active *= display->ctrl_count;
|
|
|
|
- panel_mode.timing.h_front_porch *= display->ctrl_count;
|
|
|
|
- panel_mode.timing.h_sync_width *= display->ctrl_count;
|
|
|
|
- panel_mode.timing.h_back_porch *= display->ctrl_count;
|
|
|
|
- panel_mode.timing.h_skew *= display->ctrl_count;
|
|
|
|
- panel_mode.pixel_clk_khz *= display->ctrl_count;
|
|
|
|
|
|
+ is_split_link = host->split_link.split_link_enabled;
|
|
|
|
+ sublinks_count = host->split_link.num_sublinks;
|
|
|
|
+ if (is_split_link && sublinks_count > 1) {
|
|
|
|
+ panel_mode.timing.h_active *= sublinks_count;
|
|
|
|
+ panel_mode.timing.h_front_porch *= sublinks_count;
|
|
|
|
+ panel_mode.timing.h_sync_width *= sublinks_count;
|
|
|
|
+ panel_mode.timing.h_back_porch *= sublinks_count;
|
|
|
|
+ panel_mode.timing.h_skew *= sublinks_count;
|
|
|
|
+ panel_mode.pixel_clk_khz *= sublinks_count;
|
|
|
|
+ } else {
|
|
|
|
+ panel_mode.timing.h_active *= display->ctrl_count;
|
|
|
|
+ panel_mode.timing.h_front_porch *= display->ctrl_count;
|
|
|
|
+ panel_mode.timing.h_sync_width *= display->ctrl_count;
|
|
|
|
+ panel_mode.timing.h_back_porch *= display->ctrl_count;
|
|
|
|
+ panel_mode.timing.h_skew *= display->ctrl_count;
|
|
|
|
+ panel_mode.pixel_clk_khz *= display->ctrl_count;
|
|
|
|
+ }
|
|
|
|
|
|
start = array_idx;
|
|
start = array_idx;
|
|
-
|
|
|
|
for (i = 0; i < num_dfps_rates; i++) {
|
|
for (i = 0; i < num_dfps_rates; i++) {
|
|
struct dsi_display_mode *sub_mode =
|
|
struct dsi_display_mode *sub_mode =
|
|
&display->modes[array_idx];
|
|
&display->modes[array_idx];
|
|
@@ -5886,6 +6006,7 @@ int dsi_display_get_panel_vfp(void *dsi_display,
|
|
u32 count, refresh_rate = 0;
|
|
u32 count, refresh_rate = 0;
|
|
struct dsi_dfps_capabilities dfps_caps;
|
|
struct dsi_dfps_capabilities dfps_caps;
|
|
struct dsi_display *display = (struct dsi_display *)dsi_display;
|
|
struct dsi_display *display = (struct dsi_display *)dsi_display;
|
|
|
|
+ struct dsi_host_common_cfg *host;
|
|
|
|
|
|
if (!display)
|
|
if (!display)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
@@ -5909,7 +6030,11 @@ int dsi_display_get_panel_vfp(void *dsi_display,
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
|
|
- h_active *= display->ctrl_count;
|
|
|
|
|
|
+ host = &display->panel->host_config;
|
|
|
|
+ if (host->split_link.split_link_enabled)
|
|
|
|
+ h_active *= host->split_link.num_sublinks;
|
|
|
|
+ else
|
|
|
|
+ h_active *= display->ctrl_count;
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
|
for (i = 0; i < count; i++) {
|
|
struct dsi_display_mode *m = &display->modes[i];
|
|
struct dsi_display_mode *m = &display->modes[i];
|
|
@@ -6574,7 +6699,8 @@ int dsi_display_prepare(struct dsi_display *display)
|
|
if (display->is_cont_splash_enabled &&
|
|
if (display->is_cont_splash_enabled &&
|
|
display->config.panel_mode == DSI_OP_VIDEO_MODE) {
|
|
display->config.panel_mode == DSI_OP_VIDEO_MODE) {
|
|
pr_err("DMS not supported on first frame\n");
|
|
pr_err("DMS not supported on first frame\n");
|
|
- return -EINVAL;
|
|
|
|
|
|
+ rc = -EINVAL;
|
|
|
|
+ goto error;
|
|
}
|
|
}
|
|
|
|
|
|
/* update dsi ctrl for new mode */
|
|
/* update dsi ctrl for new mode */
|
|
@@ -6771,8 +6897,7 @@ static int dsi_display_qsync(struct dsi_display *display, bool enable)
|
|
|
|
|
|
mutex_lock(&display->display_lock);
|
|
mutex_lock(&display->display_lock);
|
|
|
|
|
|
- for (i = 0; i < display->ctrl_count; i++) {
|
|
|
|
-
|
|
|
|
|
|
+ display_for_each_ctrl(i, display) {
|
|
if (enable) {
|
|
if (enable) {
|
|
/* send the commands to enable qsync */
|
|
/* send the commands to enable qsync */
|
|
rc = dsi_panel_send_qsync_on_dcs(display->panel, i);
|
|
rc = dsi_panel_send_qsync_on_dcs(display->panel, i);
|