From ad40a300a1ac16dd17cd5ecc92e2fccd98bd1bab Mon Sep 17 00:00:00 2001 From: Jayaprakash Date: Tue, 24 Sep 2019 18:33:20 +0530 Subject: [PATCH 01/15] disp: msm: sde: allow qsync support along with VRR Allow Qsync and VRR features to be supported independently by display driver. Restrict the feature availability in same composition cycle. Change-Id: I696eb72a2b4f9451e142ffdc5acccc8987c36b6d Signed-off-by: Kalyan Thota Signed-off-by: Jayaprakash --- msm/dsi/dsi_panel.c | 15 ++++++------ msm/sde/sde_connector.c | 43 +++++++++++++++++++++++++++++----- msm/sde/sde_crtc.h | 19 +++++++++++++++ msm/sde/sde_encoder_phys_vid.c | 2 +- msm/sde/sde_hw_intf.c | 7 ++++-- 5 files changed, 70 insertions(+), 16 deletions(-) diff --git a/msm/dsi/dsi_panel.c b/msm/dsi/dsi_panel.c index e48deb214e8a..d37561c6a76b 100644 --- a/msm/dsi/dsi_panel.c +++ b/msm/dsi/dsi_panel.c @@ -3257,13 +3257,14 @@ struct dsi_panel *dsi_panel_get(struct device *parent, if (rc) DSI_ERR("failed to parse dfps configuration, rc=%d\n", rc); - if (!(panel->dfps_caps.dfps_support)) { - /* qsync and dfps are mutually exclusive features */ - rc = dsi_panel_parse_qsync_caps(panel, of_node); - if (rc) - DSI_DEBUG("failed to parse qsync features, rc=%d\n", - rc); - } + rc = dsi_panel_parse_qsync_caps(panel, of_node); + if (rc) + DSI_DEBUG("failed to parse qsync features, rc=%d\n", rc); + + /* allow qsync support only if DFPS is with VFP approach */ + if ((panel->dfps_caps.dfps_support) && + !(panel->dfps_caps.type == DSI_DFPS_IMMEDIATE_VFP)) + panel->qsync_min_fps = 0; rc = dsi_panel_parse_dyn_clk_caps(panel); if (rc) diff --git a/msm/sde/sde_connector.c b/msm/sde/sde_connector.c index c45dc8ca521e..1375a4eb2c51 100644 --- a/msm/sde/sde_connector.c +++ b/msm/sde/sde_connector.c @@ -621,17 +621,24 @@ void sde_connector_set_colorspace(struct sde_connector *c_conn) void sde_connector_set_qsync_params(struct drm_connector *connector) { - struct sde_connector *c_conn = to_sde_connector(connector); - u32 qsync_propval; + struct sde_connector *c_conn; + struct sde_connector_state *c_state; + u32 qsync_propval = 0; + bool prop_dirty; if (!connector) return; + c_conn = to_sde_connector(connector); + c_state = to_sde_connector_state(connector->state); c_conn->qsync_updated = false; - qsync_propval = sde_connector_get_property(c_conn->base.state, - CONNECTOR_PROP_QSYNC_MODE); - if (qsync_propval != c_conn->qsync_mode) { + prop_dirty = msm_property_is_dirty(&c_conn->property_info, + &c_state->property_state, + CONNECTOR_PROP_QSYNC_MODE); + if (prop_dirty) { + qsync_propval = sde_connector_get_property(c_conn->base.state, + CONNECTOR_PROP_QSYNC_MODE); SDE_DEBUG("updated qsync mode %d -> %d\n", c_conn->qsync_mode, qsync_propval); c_conn->qsync_updated = true; @@ -1376,6 +1383,10 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector, if (rc) SDE_ERROR_CONN(c_conn, "cannot set hdr info %d\n", rc); break; + case CONNECTOR_PROP_QSYNC_MODE: + msm_property_set_dirty(&c_conn->property_info, + &c_state->property_state, idx); + break; default: break; } @@ -2027,13 +2038,33 @@ static int sde_connector_atomic_check(struct drm_connector *connector, struct drm_connector_state *new_conn_state) { struct sde_connector *c_conn; + struct sde_connector_state *c_state; + bool qsync_dirty = false, has_modeset = false; if (!connector) { SDE_ERROR("invalid connector\n"); - return 0; + return -EINVAL; + } + + if (!new_conn_state) { + SDE_ERROR("invalid connector state\n"); + return -EINVAL; } c_conn = to_sde_connector(connector); + c_state = to_sde_connector_state(new_conn_state); + + has_modeset = sde_crtc_atomic_check_has_modeset(new_conn_state->state, + new_conn_state->crtc); + qsync_dirty = msm_property_is_dirty(&c_conn->property_info, + &c_state->property_state, + CONNECTOR_PROP_QSYNC_MODE); + + SDE_DEBUG("has_modeset %d qsync_dirty %d\n", has_modeset, qsync_dirty); + if (has_modeset && qsync_dirty) { + SDE_ERROR("invalid qsync update during modeset\n"); + return -EINVAL; + } if (c_conn->ops.atomic_check) return c_conn->ops.atomic_check(connector, diff --git a/msm/sde/sde_crtc.h b/msm/sde/sde_crtc.h index 8b8e32ed9782..21e70ed9c265 100644 --- a/msm/sde/sde_crtc.h +++ b/msm/sde/sde_crtc.h @@ -714,6 +714,25 @@ static inline int sde_crtc_get_secure_level(struct drm_crtc *crtc, CRTC_PROP_SECURITY_LEVEL); } +/** sde_crtc_atomic_check_has_modeset - checks if the new_crtc_state in the + * drm_atomic_state has a modeset + * @state : pointer to drm_atomic_state + * @crtc : Pointer to drm crtc structure + * Returns true if crtc has modeset + */ +static inline bool sde_crtc_atomic_check_has_modeset( + struct drm_atomic_state *state, struct drm_crtc *crtc) +{ + struct drm_crtc_state *crtc_state; + + if (!state || !crtc) + return false; + + crtc_state = drm_atomic_get_new_crtc_state(state, + crtc); + return (crtc_state && drm_atomic_crtc_needs_modeset(crtc_state)); +} + /** * sde_crtc_get_secure_transition - determines the operations to be * performed before transitioning to secure state diff --git a/msm/sde/sde_encoder_phys_vid.c b/msm/sde/sde_encoder_phys_vid.c index f290c0e1e9d5..04da2e25f9bf 100644 --- a/msm/sde/sde_encoder_phys_vid.c +++ b/msm/sde/sde_encoder_phys_vid.c @@ -332,7 +332,7 @@ static void _sde_encoder_phys_vid_setup_avr( return; } - if (qsync_min_fps >= default_fps) { + if (qsync_min_fps > default_fps) { SDE_ERROR_VIDENC(vid_enc, "qsync fps %d must be less than default %d\n", qsync_min_fps, default_fps); diff --git a/msm/sde/sde_hw_intf.c b/msm/sde/sde_hw_intf.c index 2bebbca07ef7..8d23b4638bf8 100644 --- a/msm/sde/sde_hw_intf.c +++ b/msm/sde/sde_hw_intf.c @@ -129,7 +129,7 @@ static int sde_hw_intf_avr_setup(struct sde_hw_intf *ctx, u32 min_fps, default_fps, diff_fps; u32 vsync_period_slow; u32 avr_vtotal; - u32 add_porches; + u32 add_porches = 0; if (!ctx || !params || !avr_params) { SDE_ERROR("invalid input parameter(s)\n"); @@ -146,7 +146,10 @@ static int sde_hw_intf_avr_setup(struct sde_hw_intf *ctx, vsync_period = params->vsync_pulse_width + params->v_back_porch + params->height + params->v_front_porch; - add_porches = mult_frac(vsync_period, diff_fps, min_fps); + + if (diff_fps) + add_porches = mult_frac(vsync_period, diff_fps, min_fps); + vsync_period_slow = vsync_period + add_porches; avr_vtotal = vsync_period_slow * hsync_period; From 97f10bf14311eeef7cc9fbe0aec2a93fdf62b6c1 Mon Sep 17 00:00:00 2001 From: Jayaprakash Date: Wed, 16 Oct 2019 15:35:01 +0530 Subject: [PATCH 02/15] disp: msm: sde: update wr_ptr_success state post wait Update the wr_ptr_success state to avoid waiting for two frames pp_done_tx_irq. It ensures frame done callback is triggered, if IRQ's are disabled for long time by some external entity. Change-Id: I9ee72bd65dd7251bf8db0b6843fad02b675935bc Signed-off-by: Jayaprakash --- msm/sde/sde_encoder_phys_cmd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/msm/sde/sde_encoder_phys_cmd.c b/msm/sde/sde_encoder_phys_cmd.c index ba1c6ac84492..3145452c05d2 100644 --- a/msm/sde/sde_encoder_phys_cmd.c +++ b/msm/sde/sde_encoder_phys_cmd.c @@ -1409,6 +1409,8 @@ static bool _sde_encoder_phys_cmd_needs_vsync_change( static int _sde_encoder_phys_cmd_wait_for_wr_ptr( struct sde_encoder_phys *phys_enc) { + struct sde_encoder_phys_cmd *cmd_enc = + to_sde_encoder_phys_cmd(phys_enc); struct sde_encoder_wait_info wait_info = {0}; int ret; bool frame_pending = true; @@ -1439,6 +1441,7 @@ static int _sde_encoder_phys_cmd_wait_for_wr_ptr( ret = frame_pending ? ret : 0; } + cmd_enc->wr_ptr_wait_success = (ret == 0) ? true : false; return ret; } From 985ffddc71a8a01c37ddeedb470599c0402908f6 Mon Sep 17 00:00:00 2001 From: Jayaprakash Date: Thu, 26 Sep 2019 11:55:26 +0530 Subject: [PATCH 03/15] disp: msm: sde: add one-shot qsync mode support Add support to enable one-shot mode during qsync update. This feature ensures the frame drops can be reduced due to delayed software flush for the current commit. Also, add changes to disable the qsync feature post commit. Change-Id: Icb158853f52284bcf8fa641e5f62200c5460b660 Signed-off-by: Kalyan Thota Signed-off-by: Jayaprakash --- msm/dsi/dsi_drm.c | 10 +++++++++- msm/dsi/dsi_drm.h | 4 +++- msm/sde/sde_connector.c | 22 ++++++++++++++++++++++ msm/sde/sde_connector.h | 15 ++++++++++++++- msm/sde/sde_hw_intf.c | 6 ++---- msm/sde/sde_kms.c | 11 ++++++++++- msm/sde/sde_rm.h | 3 +++ 7 files changed, 63 insertions(+), 8 deletions(-) diff --git a/msm/dsi/dsi_drm.c b/msm/dsi/dsi_drm.c index 4bcdef38668e..9fc723f522f7 100644 --- a/msm/dsi/dsi_drm.c +++ b/msm/dsi/dsi_drm.c @@ -915,7 +915,8 @@ void dsi_conn_enable_event(struct drm_connector *connector, event_idx, &event_info, enable); } -int dsi_conn_post_kickoff(struct drm_connector *connector) +int dsi_conn_post_kickoff(struct drm_connector *connector, + struct msm_display_kickoff_params *params) { struct drm_encoder *encoder; struct dsi_bridge *c_bridge; @@ -923,6 +924,7 @@ int dsi_conn_post_kickoff(struct drm_connector *connector) struct dsi_display *display; struct dsi_display_ctrl *m_ctrl, *ctrl; int i, rc = 0; + bool enable; if (!connector || !connector->state) { DSI_ERR("invalid connector or connector state\n"); @@ -968,6 +970,12 @@ int dsi_conn_post_kickoff(struct drm_connector *connector) /* ensure dynamic clk switch flag is reset */ c_bridge->dsi_mode.dsi_mode_flags &= ~DSI_MODE_FLAG_DYN_CLK; + if (params->qsync_update) { + enable = (params->qsync_mode > 0) ? true : false; + display_for_each_ctrl(i, display) + dsi_ctrl_setup_avr(display->ctrl[i].ctrl, enable); + } + return 0; } diff --git a/msm/dsi/dsi_drm.h b/msm/dsi/dsi_drm.h index 8e3386d56dad..559635e87b95 100644 --- a/msm/dsi/dsi_drm.h +++ b/msm/dsi/dsi_drm.h @@ -120,9 +120,11 @@ int dsi_conn_pre_kickoff(struct drm_connector *connector, /** * dsi_display_post_kickoff - program post kickoff-time features * @connector: Pointer to drm connector structure + * @params: Parameters for post kickoff programming * Returns: Zero on success */ -int dsi_conn_post_kickoff(struct drm_connector *connector); +int dsi_conn_post_kickoff(struct drm_connector *connector, + struct msm_display_kickoff_params *params); /** * dsi_convert_to_drm_mode - Update drm mode with dsi mode information diff --git a/msm/sde/sde_connector.c b/msm/sde/sde_connector.c index 1375a4eb2c51..5016e42f56a2 100644 --- a/msm/sde/sde_connector.c +++ b/msm/sde/sde_connector.c @@ -58,6 +58,7 @@ static const struct drm_prop_enum_list e_power_mode[] = { static const struct drm_prop_enum_list e_qsync_mode[] = { {SDE_RM_QSYNC_DISABLED, "none"}, {SDE_RM_QSYNC_CONTINUOUS_MODE, "continuous"}, + {SDE_RM_QSYNC_ONE_SHOT_MODE, "one_shot"}, }; static const struct drm_prop_enum_list e_frame_trigger_mode[] = { {FRAME_DONE_WAIT_DEFAULT, "default"}, @@ -646,6 +647,27 @@ void sde_connector_set_qsync_params(struct drm_connector *connector) } } +void sde_connector_complete_qsync_commit(struct drm_connector *conn, + struct msm_display_kickoff_params *params) +{ + struct sde_connector *c_conn; + + if (!conn || !params) { + SDE_ERROR("invalid params\n"); + return; + } + + c_conn = to_sde_connector(conn); + + if (c_conn && c_conn->qsync_updated && + (c_conn->qsync_mode == SDE_RM_QSYNC_ONE_SHOT_MODE)) { + /* Reset qsync states if mode is one shot */ + params->qsync_mode = c_conn->qsync_mode = 0; + params->qsync_update = true; + SDE_EVT32(conn->base.id, c_conn->qsync_mode); + } +} + static int _sde_connector_update_hdr_metadata(struct sde_connector *c_conn, struct sde_connector_state *c_state) { diff --git a/msm/sde/sde_connector.h b/msm/sde/sde_connector.h index 117cc236ebca..23c9e8a558db 100644 --- a/msm/sde/sde_connector.h +++ b/msm/sde/sde_connector.h @@ -229,9 +229,12 @@ struct sde_connector_ops { /** * post_kickoff - display to program post kickoff-time features * @connector: Pointer to drm connector structure + * @params: Parameter bundle of connector-stored information for + * post kickoff programming into the display * Returns: Zero on success */ - int (*post_kickoff)(struct drm_connector *connector); + int (*post_kickoff)(struct drm_connector *connector, + struct msm_display_kickoff_params *params); /** * post_open - calls connector to process post open functionalities @@ -732,6 +735,16 @@ int sde_connector_get_dpms(struct drm_connector *connector); */ void sde_connector_set_qsync_params(struct drm_connector *connector); +/** + * sde_connector_complete_qsync_commit - callback signalling completion + * of qsync, if modified for the current commit + * @conn - Pointer to drm connector object + * @params - Parameter bundle of connector-stored information for + * post kickoff programming into the display + */ +void sde_connector_complete_qsync_commit(struct drm_connector *conn, + struct msm_display_kickoff_params *params); + /** * sde_connector_get_dyn_hdr_meta - returns pointer to connector state's dynamic * HDR metadata info diff --git a/msm/sde/sde_hw_intf.c b/msm/sde/sde_hw_intf.c index 8d23b4638bf8..a7a68189acb1 100644 --- a/msm/sde/sde_hw_intf.c +++ b/msm/sde/sde_hw_intf.c @@ -83,9 +83,6 @@ #define INTF_TEAR_AUTOREFRESH_CONFIG 0x2B4 #define INTF_TEAR_TEAR_DETECT_CTRL 0x2B8 -#define AVR_CONTINUOUS_MODE 1 -#define AVR_ONE_SHOT_MODE 2 - static struct sde_intf_cfg *_intf_offset(enum sde_intf intf, struct sde_mdss_cfg *m, void __iomem *addr, @@ -171,7 +168,8 @@ static void sde_hw_intf_avr_ctrl(struct sde_hw_intf *ctx, c = &ctx->hw; if (avr_params->avr_mode) { avr_ctrl = BIT(0); - avr_mode = (avr_params->avr_mode == AVR_ONE_SHOT_MODE) ? + avr_mode = + (avr_params->avr_mode == SDE_RM_QSYNC_ONE_SHOT_MODE) ? (BIT(0) | BIT(8)) : 0x0; } diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index 2797a0be4f55..8220dda6cff4 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -46,6 +46,7 @@ #include "sde_plane.h" #include "sde_crtc.h" #include "sde_reg_dma.h" +#include "sde_connector.h" #include #include "soc/qcom/secure_buffer.h" @@ -948,6 +949,7 @@ static void sde_kms_complete_commit(struct msm_kms *kms, struct drm_crtc_state *old_crtc_state; struct drm_connector *connector; struct drm_connector_state *old_conn_state; + struct msm_display_kickoff_params params; int i, rc = 0; if (!kms || !old_state) @@ -980,7 +982,14 @@ static void sde_kms_complete_commit(struct msm_kms *kms, c_conn = to_sde_connector(connector); if (!c_conn->ops.post_kickoff) continue; - rc = c_conn->ops.post_kickoff(connector); + + params.rois = NULL; + params.hdr_meta = NULL; + params.qsync_update = false; + + sde_connector_complete_qsync_commit(connector, ¶ms); + + rc = c_conn->ops.post_kickoff(connector, ¶ms); if (rc) { pr_err("Connector Post kickoff failed rc=%d\n", rc); diff --git a/msm/sde/sde_rm.h b/msm/sde/sde_rm.h index 68823b9e0db0..ea5a0e6a3020 100644 --- a/msm/sde/sde_rm.h +++ b/msm/sde/sde_rm.h @@ -67,10 +67,13 @@ enum sde_rm_topology_control { * disable state. * @SDE_RM_QSYNC_CONTINUOUS_MODE: If set, Qsync is enabled in continuous * mode. + * @SDE_RM_QSYNC_ONE_SHOT_MODE: If set, Qsync is enabled in one shot mode. + * */ enum sde_rm_qsync_modes { SDE_RM_QSYNC_DISABLED, SDE_RM_QSYNC_CONTINUOUS_MODE, + SDE_RM_QSYNC_ONE_SHOT_MODE }; /** From aad3dd4525148340df04852af2b623a5e19c0ae4 Mon Sep 17 00:00:00 2001 From: Jayaprakash Date: Thu, 26 Sep 2019 12:17:07 +0530 Subject: [PATCH 04/15] disp: msm: sde: update avr mode config during commit prepare Add changes to support avr mode config update during prepare commit which happens before gpu fence wait for the input buffers. Change-Id: Ib2cb5b7e1f10501914c003f6cf066b85048f79d4 Signed-off-by: Kalyan Thota Signed-off-by: Jayaprakash --- msm/dsi/dsi_display.c | 33 ++++++++++++++------ msm/dsi/dsi_display.h | 10 ++++++ msm/dsi/dsi_drm.c | 13 +++++++- msm/dsi/dsi_drm.h | 12 +++++++- msm/msm_drv.h | 10 ++++-- msm/sde/sde_connector.c | 56 ++++++++++++++++++++++++++-------- msm/sde/sde_connector.h | 21 +++++++++++-- msm/sde/sde_encoder.c | 22 +++++++++---- msm/sde/sde_encoder_phys_vid.c | 18 +++++++++-- msm/sde/sde_kms.c | 6 ++-- 10 files changed, 160 insertions(+), 41 deletions(-) diff --git a/msm/dsi/dsi_display.c b/msm/dsi/dsi_display.c index 5db993622f26..dc72ba6e9599 100644 --- a/msm/dsi/dsi_display.c +++ b/msm/dsi/dsi_display.c @@ -7011,21 +7011,11 @@ int dsi_display_pre_kickoff(struct drm_connector *connector, { int rc = 0; int i; - bool enable; /* check and setup MISR */ if (display->misr_enable) _dsi_display_setup_misr(display); - if (params->qsync_update) { - enable = (params->qsync_mode > 0) ? true : false; - rc = dsi_display_qsync(display, enable); - if (rc) - DSI_ERR("%s failed to send qsync commands\n", - __func__); - SDE_EVT32(params->qsync_mode, rc); - } - rc = dsi_display_set_roi(display, params->rois); /* dynamic DSI clock setting */ @@ -7106,6 +7096,29 @@ error_out: return rc; } +int dsi_display_pre_commit(void *display, + struct msm_display_conn_params *params) +{ + bool enable = false; + int rc = 0; + + if (!display || !params) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + if (params->qsync_update) { + enable = (params->qsync_mode > 0) ? true : false; + rc = dsi_display_qsync(display, enable); + if (rc) + pr_err("%s failed to send qsync commands\n", + __func__); + SDE_EVT32(params->qsync_mode, rc); + } + + return rc; +} + int dsi_display_enable(struct dsi_display *display) { int rc = 0; diff --git a/msm/dsi/dsi_display.h b/msm/dsi/dsi_display.h index 6dadddeb1875..2e5115f20038 100644 --- a/msm/dsi/dsi_display.h +++ b/msm/dsi/dsi_display.h @@ -679,6 +679,16 @@ int dsi_display_set_power(struct drm_connector *connector, int dsi_display_pre_kickoff(struct drm_connector *connector, struct dsi_display *display, struct msm_display_kickoff_params *params); + +/* + * dsi_display_pre_commit - program pre commit features + * @display: Pointer to private display structure + * @params: Parameters for pre commit time programming + * Returns: Zero on success + */ +int dsi_display_pre_commit(void *display, + struct msm_display_conn_params *params); + /** * dsi_display_get_dst_format() - get dst_format from DSI display * @connector: Pointer to drm connector structure diff --git a/msm/dsi/dsi_drm.c b/msm/dsi/dsi_drm.c index 9fc723f522f7..d1b6cc4bf87d 100644 --- a/msm/dsi/dsi_drm.c +++ b/msm/dsi/dsi_drm.c @@ -901,6 +901,17 @@ int dsi_conn_pre_kickoff(struct drm_connector *connector, return dsi_display_pre_kickoff(connector, display, params); } +int dsi_conn_prepare_commit(void *display, + struct msm_display_conn_params *params) +{ + if (!display || !params) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + return dsi_display_pre_commit(display, params); +} + void dsi_conn_enable_event(struct drm_connector *connector, uint32_t event_idx, bool enable, void *display) { @@ -916,7 +927,7 @@ void dsi_conn_enable_event(struct drm_connector *connector, } int dsi_conn_post_kickoff(struct drm_connector *connector, - struct msm_display_kickoff_params *params) + struct msm_display_conn_params *params) { struct drm_encoder *encoder; struct dsi_bridge *c_bridge; diff --git a/msm/dsi/dsi_drm.h b/msm/dsi/dsi_drm.h index 559635e87b95..4a6b7c8fa97c 100644 --- a/msm/dsi/dsi_drm.h +++ b/msm/dsi/dsi_drm.h @@ -124,7 +124,7 @@ int dsi_conn_pre_kickoff(struct drm_connector *connector, * Returns: Zero on success */ int dsi_conn_post_kickoff(struct drm_connector *connector, - struct msm_display_kickoff_params *params); + struct msm_display_conn_params *params); /** * dsi_convert_to_drm_mode - Update drm mode with dsi mode information @@ -136,4 +136,14 @@ void dsi_convert_to_drm_mode(const struct dsi_display_mode *dsi_mode, u64 dsi_drm_find_bit_clk_rate(void *display, const struct drm_display_mode *drm_mode); + +/** + * dsi_conn_prepare_commit - program pre commit time features + * @display: Pointer to private display structure + * @params: Parameters for pre commit programming + * Returns: Zero on success + */ +int dsi_conn_prepare_commit(void *display, + struct msm_display_conn_params *params); + #endif /* _DSI_DRM_H_ */ diff --git a/msm/msm_drv.h b/msm/msm_drv.h index 419b7c6b9420..a9defc9e511f 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -553,12 +553,18 @@ struct msm_roi_list { /** * struct - msm_display_kickoff_params - info for display features at kickoff * @rois: Regions of interest structure for mapping CRTC to Connector output - * @qsync_mode: Qsync mode, where 0: disabled 1: continuous mode - * @qsync_update: Qsync settings were changed/updated */ struct msm_display_kickoff_params { struct msm_roi_list *rois; struct drm_msm_ext_hdr_metadata *hdr_meta; +}; + +/** + * struct - msm_display_conn_params - info of dpu display features + * @qsync_mode: Qsync mode, where 0: disabled 1: continuous mode 2: oneshot + * @qsync_update: Qsync settings were changed/updated + */ +struct msm_display_conn_params { uint32_t qsync_mode; bool qsync_update; }; diff --git a/msm/sde/sde_connector.c b/msm/sde/sde_connector.c index 5016e42f56a2..7da379b92ce8 100644 --- a/msm/sde/sde_connector.c +++ b/msm/sde/sde_connector.c @@ -640,15 +640,17 @@ void sde_connector_set_qsync_params(struct drm_connector *connector) if (prop_dirty) { qsync_propval = sde_connector_get_property(c_conn->base.state, CONNECTOR_PROP_QSYNC_MODE); - SDE_DEBUG("updated qsync mode %d -> %d\n", c_conn->qsync_mode, - qsync_propval); - c_conn->qsync_updated = true; - c_conn->qsync_mode = qsync_propval; + if (qsync_propval != c_conn->qsync_mode) { + SDE_DEBUG("updated qsync mode %d -> %d\n", + c_conn->qsync_mode, qsync_propval); + c_conn->qsync_updated = true; + c_conn->qsync_mode = qsync_propval; + } } } void sde_connector_complete_qsync_commit(struct drm_connector *conn, - struct msm_display_kickoff_params *params) + struct msm_display_conn_params *params) { struct sde_connector *c_conn; @@ -782,13 +784,6 @@ int sde_connector_pre_kickoff(struct drm_connector *connector) params.rois = &c_state->rois; params.hdr_meta = &c_state->hdr_meta; - params.qsync_update = false; - - if (c_conn->qsync_updated) { - params.qsync_mode = c_conn->qsync_mode; - params.qsync_update = true; - SDE_EVT32(connector->base.id, params.qsync_mode); - } SDE_EVT32_VERBOSE(connector->base.id); @@ -798,6 +793,43 @@ end: return rc; } +int sde_connector_prepare_commit(struct drm_connector *connector) +{ + struct sde_connector *c_conn; + struct sde_connector_state *c_state; + struct msm_display_conn_params params; + int rc; + + if (!connector) { + SDE_ERROR("invalid argument\n"); + return -EINVAL; + } + + c_conn = to_sde_connector(connector); + c_state = to_sde_connector_state(connector->state); + if (!c_conn->display) { + SDE_ERROR("invalid connector display\n"); + return -EINVAL; + } + + if (!c_conn->ops.prepare_commit) + return 0; + + memset(¶ms, 0, sizeof(params)); + + if (c_conn->qsync_updated) { + params.qsync_mode = c_conn->qsync_mode; + params.qsync_update = true; + } + + rc = c_conn->ops.prepare_commit(c_conn->display, ¶ms); + + SDE_EVT32(connector->base.id, params.qsync_mode, + params.qsync_update, rc); + + return rc; +} + void sde_connector_helper_bridge_disable(struct drm_connector *connector) { int rc; diff --git a/msm/sde/sde_connector.h b/msm/sde/sde_connector.h index 23c9e8a558db..9389f135109e 100644 --- a/msm/sde/sde_connector.h +++ b/msm/sde/sde_connector.h @@ -234,7 +234,7 @@ struct sde_connector_ops { * Returns: Zero on success */ int (*post_kickoff)(struct drm_connector *connector, - struct msm_display_kickoff_params *params); + struct msm_display_conn_params *params); /** * post_open - calls connector to process post open functionalities @@ -328,6 +328,16 @@ struct sde_connector_ops { * Returns: zero for success, negetive for failure */ int (*get_default_lms)(void *display, u32 *num_lm); + + /** + * prepare_commit - trigger display to program pre-commit time features + * @display: Pointer to private display structure + * @params: Parameter bundle of connector-stored information for + * pre commit time programming into the display + * Returns: Zero on success + */ + int (*prepare_commit)(void *display, + struct msm_display_conn_params *params); }; /** @@ -743,7 +753,7 @@ void sde_connector_set_qsync_params(struct drm_connector *connector); * post kickoff programming into the display */ void sde_connector_complete_qsync_commit(struct drm_connector *conn, - struct msm_display_kickoff_params *params); + struct msm_display_conn_params *params); /** * sde_connector_get_dyn_hdr_meta - returns pointer to connector state's dynamic @@ -814,6 +824,13 @@ int sde_connector_register_custom_event(struct sde_kms *kms, */ int sde_connector_pre_kickoff(struct drm_connector *connector); +/** + * sde_connector_prepare_commit - trigger commit time feature programming + * @connector: Pointer to drm connector object + * Returns: Zero on success + */ +int sde_connector_prepare_commit(struct drm_connector *connector); + /** * sde_connector_needs_offset - adjust the output fence offset based on * display type diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index a1c9182ff8d3..a3e18f0e83ac 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -4703,11 +4703,6 @@ int sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc, SDE_DEBUG_ENC(sde_enc, "\n"); SDE_EVT32(DRMID(drm_enc)); - /* update the qsync parameters for the current frame */ - if (sde_enc->cur_master) - sde_connector_set_qsync_params( - sde_enc->cur_master->connector); - is_cmd_mode = sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_CMD_MODE); if (sde_enc->cur_master && sde_enc->cur_master->connector @@ -4960,7 +4955,7 @@ void sde_encoder_prepare_commit(struct drm_encoder *drm_enc) { struct sde_encoder_virt *sde_enc; struct sde_encoder_phys *phys; - int i; + int i, rc = 0; struct sde_hw_ctl *ctl; if (!drm_enc) { @@ -4969,6 +4964,11 @@ void sde_encoder_prepare_commit(struct drm_encoder *drm_enc) } sde_enc = to_sde_encoder_virt(drm_enc); + /* update the qsync parameters for the current frame */ + if (sde_enc->cur_master) + sde_connector_set_qsync_params( + sde_enc->cur_master->connector); + for (i = 0; i < sde_enc->num_phys_encs; i++) { phys = sde_enc->phys_encs[i]; if (phys && phys->ops.prepare_commit) @@ -4986,6 +4986,16 @@ void sde_encoder_prepare_commit(struct drm_encoder *drm_enc) ctl->ops.clear_pending_flush(ctl); } } + + if (sde_enc->cur_master && sde_enc->cur_master->connector) { + rc = sde_connector_prepare_commit( + sde_enc->cur_master->connector); + if (rc) + SDE_ERROR_ENC(sde_enc, + "prepare commit failed conn %d rc %d\n", + sde_enc->cur_master->connector->base.id, + rc); + } } void sde_encoder_helper_setup_misr(struct sde_encoder_phys *phys_enc, diff --git a/msm/sde/sde_encoder_phys_vid.c b/msm/sde/sde_encoder_phys_vid.c index 04da2e25f9bf..b2d043a98e1c 100644 --- a/msm/sde/sde_encoder_phys_vid.c +++ b/msm/sde/sde_encoder_phys_vid.c @@ -977,9 +977,6 @@ static int sde_encoder_phys_vid_prepare_for_kickoff( vid_enc->error_count = 0; } - if (sde_connector_is_qsync_updated(phys_enc->connector)) - _sde_encoder_phys_vid_avr_ctrl(phys_enc); - return rc; } @@ -1124,6 +1121,20 @@ static void sde_encoder_phys_vid_handle_post_kickoff( } } +static void sde_encoder_phys_vid_prepare_for_commit( + struct sde_encoder_phys *phys_enc) +{ + + if (!phys_enc) { + SDE_ERROR("invalid encoder parameters\n"); + return; + } + + if (sde_connector_is_qsync_updated(phys_enc->connector)) + _sde_encoder_phys_vid_avr_ctrl(phys_enc); + +} + static void sde_encoder_phys_vid_irq_control(struct sde_encoder_phys *phys_enc, bool enable) { @@ -1258,6 +1269,7 @@ static void sde_encoder_phys_vid_init_ops(struct sde_encoder_phys_ops *ops) ops->get_wr_line_count = sde_encoder_phys_vid_get_line_count; ops->wait_dma_trigger = sde_encoder_phys_vid_wait_dma_trigger; ops->wait_for_active = sde_encoder_phys_vid_wait_for_active; + ops->prepare_commit = sde_encoder_phys_vid_prepare_for_commit; } struct sde_encoder_phys *sde_encoder_phys_vid_init( diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index 8220dda6cff4..4b5a12b6c34e 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -949,7 +949,7 @@ static void sde_kms_complete_commit(struct msm_kms *kms, struct drm_crtc_state *old_crtc_state; struct drm_connector *connector; struct drm_connector_state *old_conn_state; - struct msm_display_kickoff_params params; + struct msm_display_conn_params params; int i, rc = 0; if (!kms || !old_state) @@ -983,9 +983,7 @@ static void sde_kms_complete_commit(struct msm_kms *kms, if (!c_conn->ops.post_kickoff) continue; - params.rois = NULL; - params.hdr_meta = NULL; - params.qsync_update = false; + memset(¶ms, 0, sizeof(params)); sde_connector_complete_qsync_commit(connector, ¶ms); From dd84cf72f455c5ec17ee8f855d70b0748d09daac Mon Sep 17 00:00:00 2001 From: Nilaan Gunabalachandran Date: Mon, 9 Sep 2019 10:55:50 -0400 Subject: [PATCH 05/15] disp: msm: sde: remove dspp blocking RM blocks an lm path with dspp from being allocated if a dspp is not required by topology. This can restrict using all lms to full availability. LM preference already ensures dspp assignment is completed to required built in displays. This change removes blocking conditions. Change-Id: Ifec6f27d4d10a9ea061780ed5f9e9601b893d5a0 Signed-off-by: Nilaan Gunabalachandran --- msm/sde/sde_rm.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/msm/sde/sde_rm.c b/msm/sde/sde_rm.c index eca5b8af62a8..4edb09f62f01 100644 --- a/msm/sde/sde_rm.c +++ b/msm/sde/sde_rm.c @@ -754,7 +754,7 @@ static bool _sde_rm_check_lm( struct sde_rm_hw_blk **ds, struct sde_rm_hw_blk **pp) { - bool is_valid_dspp, is_valid_ds, ret; + bool is_valid_dspp, is_valid_ds, ret = true; is_valid_dspp = (lm_cfg->dspp != DSPP_MAX) ? true : false; is_valid_ds = (lm_cfg->ds != DS_MAX) ? true : false; @@ -771,8 +771,6 @@ static bool _sde_rm_check_lm( ret = is_valid_dspp; else if (RM_RQ_DS(reqs)) ret = is_valid_ds; - else - ret = !(is_valid_dspp || is_valid_ds); if (!ret) { SDE_DEBUG( @@ -1496,17 +1494,12 @@ static int _sde_rm_make_lm_rsvp(struct sde_rm *rm, struct sde_rm_rsvp *rsvp, if (splash_display->lm_cnt != reqs->topology->num_lm) SDE_DEBUG("Configured splash LMs != needed LM cnt\n"); } + /* - * Assign LMs and blocks whose usage is tied to them: DSPP & Pingpong. - * Do assignment preferring to give away low-resource mixers first: - * - Check mixers without DSPPs - * - Only then allow to grab from mixers with DSPP capability + * Assign LMs and blocks whose usage is tied to them: + * DSPP & Pingpong. */ ret = _sde_rm_reserve_lms(rm, rsvp, reqs, hw_ids); - if (ret && !RM_RQ_DSPP(reqs)) { - reqs->top_ctrl |= BIT(SDE_RM_TOPCTL_DSPP); - ret = _sde_rm_reserve_lms(rm, rsvp, reqs, hw_ids); - } return ret; } From 03af5b7083ef020e9d3f59174bba5737fd91399b Mon Sep 17 00:00:00 2001 From: Vara Reddy Date: Tue, 1 Oct 2019 12:38:06 -0700 Subject: [PATCH 06/15] drm/msm/dsi: bypass dsi clock set during changing mode Bypass setting clkrate_change_pending flag if the current mode and the mode to be set has the same preferred clock rate. Change-Id: Id1f6c45e822492427cf3555beeaa5f0e7ea3243c Signed-off-by: Vara Reddy --- msm/dsi/dsi_display.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/msm/dsi/dsi_display.c b/msm/dsi/dsi_display.c index 612e9de9fbdb..44a62a2a7c1c 100644 --- a/msm/dsi/dsi_display.c +++ b/msm/dsi/dsi_display.c @@ -4489,8 +4489,13 @@ static int dsi_display_set_mode_sub(struct dsi_display *display, if ((mode->dsi_mode_flags & DSI_MODE_FLAG_DMS) && (display->panel->panel_mode == DSI_OP_CMD_MODE)) { + u64 cur_bitclk = display->panel->cur_mode->timing.clk_rate_hz; + u64 to_bitclk = mode->timing.clk_rate_hz; commit_phy_timing = true; - atomic_set(&display->clkrate_change_pending, 1); + + /* No need to set clkrate pending flag if clocks are same */ + if (cur_bitclk != to_bitclk) + atomic_set(&display->clkrate_change_pending, 1); dsi_display_validate_dms_fps(display->panel->cur_mode, mode); } From e4a7d473f97ae546f746ff2f68ca4f406f932bf1 Mon Sep 17 00:00:00 2001 From: Veera Sundaram Sankaran Date: Fri, 11 Oct 2019 18:15:17 -0700 Subject: [PATCH 07/15] disp: msm: sde: handle all error cases during sui transitions During secure-ui, the SMMU, MISR and VBIF states are altered based on the enable/disable of secure-ui which is followed by the scm_call. Failure in any of the steps would lead the system to an unstable state. Address all the failures and revert back all steps to get the system back in original state to avoid any issues. Change-Id: I736c6cf018c5992ec33806c00e58bf56b818b8a7 Signed-off-by: Veera Sundaram Sankaran Signed-off-by: Samantha Tran --- msm/sde/sde_crtc.c | 1 + msm/sde/sde_kms.c | 132 ++++++++++++++++++++++++++++++--------------- msm/sde/sde_kms.h | 2 + 3 files changed, 91 insertions(+), 44 deletions(-) diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index 000294129d31..2265a2abaad1 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -1783,6 +1783,7 @@ int sde_crtc_get_secure_transition_ops(struct drm_crtc *crtc, smmu_state = &sde_kms->smmu_state; smmu_state->prev_state = smmu_state->state; + smmu_state->prev_secure_level = smmu_state->secure_level; sde_crtc = to_sde_crtc(crtc); secure_level = sde_crtc_get_secure_level(crtc, crtc->state); diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index 2797a0be4f55..137d2f92833f 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -334,88 +334,118 @@ static int _sde_kms_scm_call(struct sde_kms *sde_kms, int vmid) static int _sde_kms_detach_all_cb(struct sde_kms *sde_kms, u32 vmid) { - u32 ret = 0; + u32 ret; if (atomic_inc_return(&sde_kms->detach_all_cb) > 1) - goto end; + return 0; /* detach_all_contexts */ ret = sde_kms_mmu_detach(sde_kms, false); if (ret) { SDE_ERROR("failed to detach all cb ret:%d\n", ret); - goto end; + goto mmu_error; } ret = _sde_kms_scm_call(sde_kms, vmid); - if (ret) - goto end; + if (ret) { + SDE_ERROR("scm call failed for vmid:%d\n", vmid); + goto scm_error; + } -end: + return 0; + +scm_error: + sde_kms_mmu_attach(sde_kms, false); +mmu_error: + atomic_dec(&sde_kms->detach_all_cb); return ret; } -static int _sde_kms_attach_all_cb(struct sde_kms *sde_kms, int vmid) +static int _sde_kms_attach_all_cb(struct sde_kms *sde_kms, u32 vmid, + u32 old_vmid) { - u32 ret = 0; + u32 ret; if (atomic_dec_return(&sde_kms->detach_all_cb) != 0) - goto end; + return 0; ret = _sde_kms_scm_call(sde_kms, vmid); - if (ret) - goto end; + if (ret) { + SDE_ERROR("scm call failed for vmid:%d\n", vmid); + goto scm_error; + } /* attach_all_contexts */ ret = sde_kms_mmu_attach(sde_kms, false); if (ret) { SDE_ERROR("failed to attach all cb ret:%d\n", ret); - goto end; + goto mmu_error; } -end: + return 0; + +mmu_error: + _sde_kms_scm_call(sde_kms, old_vmid); +scm_error: + atomic_inc(&sde_kms->detach_all_cb); return ret; } static int _sde_kms_detach_sec_cb(struct sde_kms *sde_kms, int vmid) { - u32 ret = 0; + u32 ret; if (atomic_inc_return(&sde_kms->detach_sec_cb) > 1) - goto end; + return 0; /* detach secure_context */ ret = sde_kms_mmu_detach(sde_kms, true); if (ret) { SDE_ERROR("failed to detach sec cb ret:%d\n", ret); - goto end; + goto mmu_error; } ret = _sde_kms_scm_call(sde_kms, vmid); - if (ret) - goto end; + if (ret) { + SDE_ERROR("scm call failed for vmid:%d\n", vmid); + goto scm_error; + } -end: + return 0; + +scm_error: + sde_kms_mmu_attach(sde_kms, true); +mmu_error: + atomic_dec(&sde_kms->detach_sec_cb); return ret; } -static int _sde_kms_attach_sec_cb(struct sde_kms *sde_kms, int vmid) +static int _sde_kms_attach_sec_cb(struct sde_kms *sde_kms, u32 vmid, + u32 old_vmid) { - u32 ret = 0; + u32 ret; if (atomic_dec_return(&sde_kms->detach_sec_cb) != 0) - goto end; + return 0; ret = _sde_kms_scm_call(sde_kms, vmid); - if (ret) - goto end; + if (ret) { + goto scm_error; + SDE_ERROR("scm call failed for vmid:%d\n", vmid); + } ret = sde_kms_mmu_attach(sde_kms, true); if (ret) { SDE_ERROR("failed to attach sec cb ret:%d\n", ret); - goto end; + goto mmu_error; } -end: + return 0; + +mmu_error: + _sde_kms_scm_call(sde_kms, old_vmid); +scm_error: + atomic_inc(&sde_kms->detach_sec_cb); return ret; } @@ -435,6 +465,7 @@ static int _sde_kms_sui_misr_ctrl(struct sde_kms *sde_kms, ret = _sde_kms_secure_ctrl_xin_clients(sde_kms, crtc, true); if (ret) { + sde_crtc_misr_setup(crtc, false, 0); pm_runtime_put_sync(sde_kms->dev->dev); return ret; } @@ -473,8 +504,10 @@ static int _sde_kms_secure_ctrl(struct sde_kms *sde_kms, struct drm_crtc *crtc, /* enable sui misr if requested, before the transition */ if (smmu_state->sui_misr_state == SUI_MISR_ENABLE_REQ) { ret = _sde_kms_sui_misr_ctrl(sde_kms, crtc, true); - if (ret) + if (ret) { + smmu_state->sui_misr_state == NONE; goto end; + } } mutex_lock(&sde_kms->secure_transition_lock); @@ -486,7 +519,8 @@ static int _sde_kms_secure_ctrl(struct sde_kms *sde_kms, struct drm_crtc *crtc, break; case ATTACH_ALL_REQ: - ret = _sde_kms_attach_all_cb(sde_kms, VMID_CP_PIXEL); + ret = _sde_kms_attach_all_cb(sde_kms, VMID_CP_PIXEL, + VMID_CP_SEC_DISPLAY); if (!ret) { smmu_state->state = ATTACHED; smmu_state->secure_level = SDE_DRM_SEC_NON_SEC; @@ -503,7 +537,9 @@ static int _sde_kms_secure_ctrl(struct sde_kms *sde_kms, struct drm_crtc *crtc, break; case ATTACH_SEC_REQ: - ret = _sde_kms_attach_sec_cb(sde_kms, VMID_CP_PIXEL); + vmid = (smmu_state->secure_level == SDE_DRM_SEC_ONLY) ? + VMID_CP_SEC_DISPLAY : VMID_CP_CAMERA_PREVIEW; + ret = _sde_kms_attach_sec_cb(sde_kms, VMID_CP_PIXEL, vmid); if (!ret) { smmu_state->state = ATTACHED; smmu_state->secure_level = SDE_DRM_SEC_NON_SEC; @@ -527,29 +563,34 @@ static int _sde_kms_secure_ctrl(struct sde_kms *sde_kms, struct drm_crtc *crtc, } end: - smmu_state->sui_misr_state = NONE; - smmu_state->transition_type = NONE; smmu_state->transition_error = false; - /* - * If switch failed, toggling secure_level is enough since - * there are only two secure levels - secure/non-secure - */ if (ret) { smmu_state->transition_error = true; + SDE_ERROR( + "crtc%d: req_state %d, new_state %d, sec_lvl %d, ret %d\n", + DRMID(crtc), old_smmu_state, smmu_state->state, + smmu_state->secure_level, ret); + smmu_state->state = smmu_state->prev_state; - smmu_state->secure_level = !smmu_state->secure_level; + smmu_state->secure_level = smmu_state->prev_secure_level; + + if (smmu_state->sui_misr_state == SUI_MISR_ENABLE_REQ) + _sde_kms_sui_misr_ctrl(sde_kms, crtc, false); } - SDE_DEBUG( - "crtc %d: old_state %d, req_state %d, new_state %d, sec_lvl %d, ret %d\n", - DRMID(crtc), smmu_state->prev_state, old_smmu_state, - smmu_state->state, smmu_state->secure_level, ret); - SDE_EVT32(DRMID(crtc), smmu_state->prev_state, - smmu_state->state, smmu_state->transition_type, - smmu_state->transition_error, smmu_state->secure_level, + SDE_DEBUG("crtc %d: req_state %d, new_state %d, sec_lvl %d, ret %d\n", + DRMID(crtc), old_smmu_state, smmu_state->state, + smmu_state->secure_level, ret); + SDE_EVT32(DRMID(crtc), smmu_state->state, smmu_state->prev_state, + smmu_state->transition_type, + smmu_state->transition_error, + smmu_state->secure_level, smmu_state->prev_secure_level, smmu_state->sui_misr_state, ret, SDE_EVTLOG_FUNC_EXIT); + smmu_state->sui_misr_state = NONE; + smmu_state->transition_type = NONE; + return ret; } @@ -565,6 +606,7 @@ static int sde_kms_prepare_secure_transition(struct msm_kms *kms, struct drm_device *dev = sde_kms->dev; int i, ops = 0, ret = 0; bool old_valid_fb = false; + struct sde_kms_smmu_state_data *smmu_state = &sde_kms->smmu_state; for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { if (!crtc->state || !crtc->state->active) @@ -601,8 +643,10 @@ static int sde_kms_prepare_secure_transition(struct msm_kms *kms, return ops; } - if (!ops) + if (!ops) { + smmu_state->transition_error = false; goto no_ops; + } SDE_DEBUG("%d:secure operations(%x) started on state:%pK\n", crtc->base.id, ops, crtc->state); diff --git a/msm/sde/sde_kms.h b/msm/sde/sde_kms.h index 68ef93cf689e..bd0eb520548d 100644 --- a/msm/sde/sde_kms.h +++ b/msm/sde/sde_kms.h @@ -189,6 +189,7 @@ enum frame_trigger_mode_type { * @state: current state of smmu context banks * @prev_state: previous state of smmu context banks * @secure_level: secure level cached from crtc + * @prev_secure_level: previous secure level * @transition_type: transition request type * @transition_error: whether there is error while transitioning the state */ @@ -196,6 +197,7 @@ struct sde_kms_smmu_state_data { uint32_t state; uint32_t prev_state; uint32_t secure_level; + uint32_t prev_secure_level; uint32_t transition_type; uint32_t transition_error; uint32_t sui_misr_state; From 6cdd1f1fe026d2c852aafddb26914d0b98017aa8 Mon Sep 17 00:00:00 2001 From: Veera Sundaram Sankaran Date: Mon, 21 Oct 2019 13:24:38 -0700 Subject: [PATCH 08/15] disp: msm: sde: avoid encoder power-collapse with pending frames The encoder idle work is scheduled during the frame-done event to be executed after a timeout. During the execution, the check for any on-going/pending frames is invalid as it checks for > 1. Fix it to check for any non-zero frame-pending and avoid power-collapse. Change-Id: If7715ee56cc9bfa63787811458f4fc91de540013 Signed-off-by: Veera Sundaram Sankaran --- msm/sde/sde_encoder.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index a1c9182ff8d3..19096d994cd3 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -2581,8 +2581,8 @@ static int _sde_encoder_rc_idle(struct drm_encoder *drm_enc, SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state, SDE_EVTLOG_ERROR); goto end; - } else if (sde_crtc_frame_pending(sde_enc->crtc) > 1) { - SDE_ERROR_ENC(sde_enc, "skip idle entry"); + } else if (sde_crtc_frame_pending(sde_enc->crtc)) { + SDE_DEBUG_ENC(sde_enc, "skip idle entry"); SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state, sde_crtc_frame_pending(sde_enc->crtc), SDE_EVTLOG_ERROR); From c1cef9cfafc03d3645109dd464bee3ed14839581 Mon Sep 17 00:00:00 2001 From: Dhaval Patel Date: Wed, 16 Oct 2019 16:22:34 -0700 Subject: [PATCH 09/15] disp: msm: remove runtime_pm support from rsc driver Remove runtime_pm support from rsc driver. RSC driver does not vote for MMCX. It relies on sde core driver to keep vote and trigger call. That brings additional dependency to manage runtime_pm references during pm_suspend/pm_resume call. This change also updates the runtime_pm call to manage the pm_suspend in msm drm driver. Change-Id: I111771994822c82db53fb6c23e5d942f90fd1af2 Signed-off-by: Dhaval Patel --- msm/sde/sde_kms.c | 10 ++- msm/sde_rsc.c | 174 +++++++++++++++++++++++---------------------- msm/sde_rsc_priv.h | 2 + 3 files changed, 99 insertions(+), 87 deletions(-) diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index 2797a0be4f55..3928eb76d8e3 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -2641,7 +2641,6 @@ static int sde_kms_pm_suspend(struct device *dev) sde_kms = to_sde_kms(ddev_to_msm_kms(ddev)); SDE_EVT32(0); - pm_runtime_put_noidle(dev); /* disable hot-plug polling */ drm_kms_helper_poll_disable(ddev); @@ -2750,6 +2749,15 @@ unlock: } drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); + + /* + * pm runtime driver avoids multiple runtime_suspend API call by + * checking runtime_status. However, this call helps when there is a + * race condition between pm_suspend call and doze_suspend/power_off + * commit. It removes the extra vote from suspend and adds it back + * later to allow power collapse during pm_suspend call + */ + pm_runtime_put_sync(dev); pm_runtime_get_noresume(dev); return ret; diff --git a/msm/sde_rsc.c b/msm/sde_rsc.c index ed380bcbf9b6..f60b7e4fe4e8 100644 --- a/msm/sde_rsc.c +++ b/msm/sde_rsc.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -399,6 +398,86 @@ static u32 sde_rsc_timer_calculate(struct sde_rsc_priv *rsc, return ret; } +static int sde_rsc_resource_disable(struct sde_rsc_priv *rsc) +{ + struct dss_module_power *mp; + u32 reg_bus_hdl; + + if (!rsc) { + pr_err("invalid drv data\n"); + return -EINVAL; + } + + if (atomic_read(&rsc->resource_refcount) == 0) { + pr_err("%pS: invalid rsc resource disable call\n", + __builtin_return_address(0)); + return -EINVAL; + } + + if (atomic_dec_return(&rsc->resource_refcount) != 0) + return 0; + + mp = &rsc->phandle.mp; + msm_dss_enable_clk(mp->clk_config, mp->num_clk, false); + reg_bus_hdl = rsc->phandle.reg_bus_hdl; + if (reg_bus_hdl) + msm_bus_scale_client_update_request(reg_bus_hdl, + VOTE_INDEX_DISABLE); + msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, false); + + return 0; +} + +static int sde_rsc_resource_enable(struct sde_rsc_priv *rsc) +{ + struct dss_module_power *mp; + int rc = 0; + u32 reg_bus_hdl; + + if (!rsc) { + pr_err("invalid drv data\n"); + return -EINVAL; + } + + if (atomic_inc_return(&rsc->resource_refcount) != 1) + return 0; + + mp = &rsc->phandle.mp; + rc = msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, true); + if (rc) { + pr_err("failed to enable vregs rc=%d\n", rc); + goto end; + } + + reg_bus_hdl = rsc->phandle.reg_bus_hdl; + if (reg_bus_hdl) { + rc = msm_bus_scale_client_update_request(reg_bus_hdl, + VOTE_INDEX_LOW); + if (rc) { + pr_err("failed to set reg bus vote rc=%d\n", rc); + goto reg_bus_hdl_err; + } + } + + rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, true); + if (rc) { + pr_err("clock enable failed rc:%d\n", rc); + goto clk_err; + } + + return rc; + +clk_err: + if (reg_bus_hdl) + msm_bus_scale_client_update_request(reg_bus_hdl, + VOTE_INDEX_DISABLE); +reg_bus_hdl_err: + msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, false); +end: + atomic_dec(&rsc->resource_refcount); + return rc; +} + static int sde_rsc_switch_to_cmd(struct sde_rsc_priv *rsc, struct sde_rsc_cmd_config *config, struct sde_rsc_client *caller_client, @@ -824,7 +903,7 @@ int sde_rsc_client_state_update(struct sde_rsc_client *caller_client, caller_client->name, state); if (rsc->current_state == SDE_RSC_IDLE_STATE) - pm_runtime_get_sync(rsc->dev); + sde_rsc_resource_enable(rsc); switch (state) { case SDE_RSC_IDLE_STATE: @@ -882,7 +961,7 @@ int sde_rsc_client_state_update(struct sde_rsc_client *caller_client, clk_disable: if (rsc->current_state == SDE_RSC_IDLE_STATE) - pm_runtime_put_sync(rsc->dev); + sde_rsc_resource_disable(rsc); end: mutex_unlock(&rsc->client_lock); return rc; @@ -959,7 +1038,7 @@ int sde_rsc_client_trigger_vote(struct sde_rsc_client *caller_client, rsc->bw_config.ib_vote[i] = rsc->bw_config.new_ib_vote[i]; } - rc = pm_runtime_get_sync(rsc->dev); + rc = sde_rsc_resource_enable(rsc); if (rc < 0) goto clk_enable_fail; @@ -990,7 +1069,7 @@ int sde_rsc_client_trigger_vote(struct sde_rsc_client *caller_client, rsc->hw_ops.tcs_use_ok(rsc); end: - pm_runtime_put_sync(rsc->dev); + sde_rsc_resource_disable(rsc); clk_enable_fail: mutex_unlock(&rsc->client_lock); @@ -1299,7 +1378,7 @@ static void sde_rsc_deinit(struct platform_device *pdev, if (!rsc) return; - pm_runtime_put_sync(rsc->dev); + sde_rsc_resource_disable(rsc); if (rsc->sw_fs_enabled) regulator_disable(rsc->fs); if (rsc->fs) @@ -1314,82 +1393,6 @@ static void sde_rsc_deinit(struct platform_device *pdev, kfree(rsc); } -#ifdef CONFIG_PM -static int sde_rsc_runtime_suspend(struct device *dev) -{ - struct sde_rsc_priv *rsc = dev_get_drvdata(dev); - struct dss_module_power *mp; - u32 reg_bus_hdl; - - if (!rsc) { - pr_err("invalid drv data\n"); - return -EINVAL; - } - - mp = &rsc->phandle.mp; - msm_dss_enable_clk(mp->clk_config, mp->num_clk, false); - reg_bus_hdl = rsc->phandle.reg_bus_hdl; - if (reg_bus_hdl) - msm_bus_scale_client_update_request(reg_bus_hdl, - VOTE_INDEX_DISABLE); - msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, false); - - return 0; -} - -static int sde_rsc_runtime_resume(struct device *dev) -{ - struct sde_rsc_priv *rsc = dev_get_drvdata(dev); - struct dss_module_power *mp; - int rc = 0; - u32 reg_bus_hdl; - - if (!rsc) { - pr_err("invalid drv data\n"); - return -EINVAL; - } - - mp = &rsc->phandle.mp; - rc = msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, true); - if (rc) { - pr_err("failed to enable vregs rc=%d\n", rc); - goto end; - } - - reg_bus_hdl = rsc->phandle.reg_bus_hdl; - if (reg_bus_hdl) { - rc = msm_bus_scale_client_update_request(reg_bus_hdl, - VOTE_INDEX_LOW); - if (rc) { - pr_err("failed to set reg bus vote rc=%d\n", rc); - goto reg_bus_hdl_err; - } - } - - rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, true); - if (rc) { - pr_err("clock enable failed rc:%d\n", rc); - goto clk_err; - } - - return rc; - -clk_err: - if (reg_bus_hdl) - msm_bus_scale_client_update_request(reg_bus_hdl, - VOTE_INDEX_DISABLE); -reg_bus_hdl_err: - msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, false); -end: - return rc; -} -#endif - -static const struct dev_pm_ops sde_rsc_pm_ops = { - SET_RUNTIME_PM_OPS(sde_rsc_runtime_suspend, - sde_rsc_runtime_resume, NULL) -}; - /** * sde_rsc_bind - bind rsc device with controlling device * @dev: Pointer to base of platform device @@ -1556,8 +1559,7 @@ static int sde_rsc_probe(struct platform_device *pdev) rsc->sw_fs_enabled = true; - pm_runtime_enable(rsc->dev); - ret = pm_runtime_get_sync(rsc->dev); + ret = sde_rsc_resource_enable(rsc); if (ret < 0) { pr_err("failed to enable sde rsc power resources rc:%d\n", ret); goto sde_rsc_fail; @@ -1566,12 +1568,13 @@ static int sde_rsc_probe(struct platform_device *pdev) if (sde_rsc_timer_calculate(rsc, NULL, SDE_RSC_IDLE_STATE)) goto sde_rsc_fail; - pm_runtime_put_sync(rsc->dev); + sde_rsc_resource_disable(rsc); INIT_LIST_HEAD(&rsc->client_list); INIT_LIST_HEAD(&rsc->event_list); mutex_init(&rsc->client_lock); init_waitqueue_head(&rsc->rsc_vsync_waitq); + atomic_set(&rsc->resource_refcount, 0); pr_info("sde rsc index:%d probed successfully\n", SDE_RSC_INDEX + counter); @@ -1644,7 +1647,6 @@ static struct platform_driver sde_rsc_platform_driver = { .name = "sde_rsc", .of_match_table = dt_match, .suppress_bind_attrs = true, - .pm = &sde_rsc_pm_ops, }, }; diff --git a/msm/sde_rsc_priv.h b/msm/sde_rsc_priv.h index b1261e188826..5f63061f4a7a 100644 --- a/msm/sde_rsc_priv.h +++ b/msm/sde_rsc_priv.h @@ -185,6 +185,7 @@ struct sde_rsc_bw_config { * rsc_vsync_waitq: Queue to wait for the vsync. * bw_config: check sde_rsc_bw_config structure description. * dev: rsc device node + * resource_refcount: Track rsc resource refcount */ struct sde_rsc_priv { u32 version; @@ -225,6 +226,7 @@ struct sde_rsc_priv { struct sde_rsc_bw_config bw_config; struct device *dev; + atomic_t resource_refcount; }; /** From 62e17c125b6419171f7a063220c489c9bcab9a12 Mon Sep 17 00:00:00 2001 From: Gopikrishnaiah Anandan Date: Mon, 30 Sep 2019 12:15:33 -0700 Subject: [PATCH 10/15] disp: msm: update debug dump for ltm block Local tone mapping hardware block has defined test points for debug bus. Change updates the missing test points and enables dumping of the registers in case of DPU hang. Change-Id: I5bb5419255e84427c0a1393f8e9322b981eea084 Signed-off-by: Gopikrishnaiah Anandan --- msm/sde_dbg.c | 71 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 17 deletions(-) diff --git a/msm/sde_dbg.c b/msm/sde_dbg.c index f08da511a296..28b253fa20e6 100644 --- a/msm/sde_dbg.c +++ b/msm/sde_dbg.c @@ -45,6 +45,8 @@ #define DBGBUS_PERIPH 0x418 #define TEST_MASK(id, tp) ((id << 4) | (tp << 1) | BIT(0)) +#define TEST_EXT_MASK(id, tp) (((tp >> 3) << 24) | (id << 4) \ + | ((tp & 0x7) << 1) | BIT(0)) /* following offsets are with respect to MDP VBIF base for DBG BUS access */ #define MMSS_VBIF_CLKON 0x4 @@ -271,6 +273,13 @@ static void _sde_debug_bus_ppb0_dump(void __iomem *mem_base, entry->wr_addr, entry->block_id, entry->test_id, val); } +static void _sde_debug_bus_ltm_dump(void __iomem *mem_base, + struct sde_debug_bus_entry *entry, u32 val) +{ + dev_info(sde_dbg_base.dev, "ltm 0x%x %d %d 0x%x\n", + entry->wr_addr, entry->block_id, entry->test_id, val); +} + static void _sde_debug_bus_ppb1_dump(void __iomem *mem_base, struct sde_debug_bus_entry *entry, u32 val) { @@ -1997,23 +2006,47 @@ static struct sde_debug_bus_entry dbg_bus_sde_kona[] = { { DBGBUS_DSPP, 9, 0}, /* ltm */ - { DBGBUS_DSPP, 45, 0}, - { DBGBUS_DSPP, 45, 1}, - { DBGBUS_DSPP, 45, 2}, - { DBGBUS_DSPP, 45, 3}, - { DBGBUS_DSPP, 45, 4}, - { DBGBUS_DSPP, 45, 5}, - { DBGBUS_DSPP, 45, 6}, - { DBGBUS_DSPP, 45, 7}, + { DBGBUS_DSPP, 45, 0, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 45, 1, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 45, 2, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 45, 3, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 45, 4, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 45, 5, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 45, 6, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 45, 7, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 45, 8, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 45, 9, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 45, 10, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 45, 11, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 45, 12, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 45, 13, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 45, 14, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 45, 15, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 45, 16, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 45, 17, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 45, 18, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 45, 31, _sde_debug_bus_ltm_dump}, - { DBGBUS_DSPP, 46, 0}, - { DBGBUS_DSPP, 46, 1}, - { DBGBUS_DSPP, 46, 2}, - { DBGBUS_DSPP, 46, 3}, - { DBGBUS_DSPP, 46, 4}, - { DBGBUS_DSPP, 46, 5}, - { DBGBUS_DSPP, 46, 6}, - { DBGBUS_DSPP, 46, 7}, + { DBGBUS_DSPP, 46, 0, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 46, 1, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 46, 2, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 46, 3, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 46, 4, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 46, 5, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 46, 6, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 46, 7, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 46, 8, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 46, 9, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 46, 10, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 46, 11, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 46, 12, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 46, 13, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 46, 14, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 46, 15, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 46, 16, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 46, 17, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 46, 18, _sde_debug_bus_ltm_dump}, + { DBGBUS_DSPP, 46, 31, _sde_debug_bus_ltm_dump}, /* blend */ /* LM0 */ @@ -3216,7 +3249,11 @@ static void _sde_dbg_dump_sde_dbg_bus(struct sde_dbg_sde_debug_bus *bus) for (i = 0; i < bus->cmn.entries_size; i++) { head = bus->entries + i; - writel_relaxed(TEST_MASK(head->block_id, head->test_id), + if (head->test_id > 0x7) + writel_relaxed(TEST_EXT_MASK(head->block_id, + head->test_id), mem_base + head->wr_addr); + else + writel_relaxed(TEST_MASK(head->block_id, head->test_id), mem_base + head->wr_addr); wmb(); /* make sure test bits were written */ From 4f3de7c54882dbae753f88de7ce1c12fad1941f4 Mon Sep 17 00:00:00 2001 From: Jayaprakash Date: Wed, 23 Oct 2019 17:07:08 +0530 Subject: [PATCH 11/15] disp: msm: dsi: reject seamless commit with active changed Reject composition if any seamless transition such as VRR, dynclk is requested during power on/off commits. Change-Id: I731bfc06b3bd1e7ae920c12cbc68f95f5cc01687 Signed-off-by: Kalyan Thota Signed-off-by: Jayaprakash --- msm/dsi/dsi_drm.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/msm/dsi/dsi_drm.c b/msm/dsi/dsi_drm.c index 41bef925d52f..785f197884d8 100644 --- a/msm/dsi/dsi_drm.c +++ b/msm/dsi/dsi_drm.c @@ -423,6 +423,15 @@ static bool dsi_bridge_mode_fixup(struct drm_bridge *bridge, dsi_mode.dsi_mode_flags |= DSI_MODE_FLAG_DMS; } + /* Reject seamless transition when active changed */ + if (crtc_state->active_changed && + ((dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_VRR) || + (dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_DYN_CLK))) { + DSI_ERR("seamless upon active changed 0x%x %d\n", + dsi_mode.dsi_mode_flags, crtc_state->active_changed); + return false; + } + /* convert back to drm mode, propagating the private info & flags */ dsi_convert_to_drm_mode(&dsi_mode, adjusted_mode); From 1e60728ab8c0b08ace93ce4692d1f4c6fb238c5f Mon Sep 17 00:00:00 2001 From: Ravikanth Tuniki Date: Wed, 23 Oct 2019 17:00:52 +0530 Subject: [PATCH 12/15] disp: msm: sde: Fix 32-bit compilation issues 1.Typecast to avoid distinct pointer type comparison 2.Keep DMA mask aligned with api definition. 3.Add Suffix for literals 4.Remove multfrac func to avoid uncompatible division. 5.64-bit division( operator "/") on 32-bit platforms is not supported. Using platform independent API's here Change-Id: I0e7305418e53876bd1adf00c1963f85cbdf980cc Signed-off-by: Ravikanth Tuniki --- msm/dsi/dsi_ctrl.c | 4 ++-- msm/dsi/dsi_panel.c | 30 +++++++++++++++++++----------- msm/dsi/dsi_phy_timing_v4_0.c | 6 +++--- msm/msm_smmu.c | 2 +- msm/sde/sde_crtc.c | 4 ++-- msm/sde_power_handle.h | 4 ++-- pll/dsi_pll.h | 4 ++-- pll/dsi_pll_14nm_util.c | 5 +++-- pll/dsi_pll_7nm.c | 12 ++++++------ rotator/sde_rotator_smmu.c | 2 +- 10 files changed, 41 insertions(+), 32 deletions(-) diff --git a/msm/dsi/dsi_ctrl.c b/msm/dsi/dsi_ctrl.c index 599e8a9f5e90..241aa21f8cad 100644 --- a/msm/dsi/dsi_ctrl.c +++ b/msm/dsi/dsi_ctrl.c @@ -847,8 +847,8 @@ static int dsi_ctrl_update_link_freqs(struct dsi_ctrl *dsi_ctrl, bit_rate = config->bit_clk_rate_hz_override * num_of_lanes; } else if (config->panel_mode == DSI_OP_CMD_MODE) { /* Calculate the bit rate needed to match dsi transfer time */ - bit_rate = mult_frac(min_dsi_clk_hz, frame_time_us, - dsi_transfer_time_us); + bit_rate = min_dsi_clk_hz * frame_time_us; + do_div(bit_rate, dsi_transfer_time_us); bit_rate = bit_rate * num_of_lanes; } else { h_period = DSI_H_TOTAL_DSC(timing); diff --git a/msm/dsi/dsi_panel.c b/msm/dsi/dsi_panel.c index e48deb214e8a..c0bfe10c6121 100644 --- a/msm/dsi/dsi_panel.c +++ b/msm/dsi/dsi_panel.c @@ -2492,6 +2492,7 @@ static int dsi_panel_parse_phy_timing(struct dsi_display_mode *mode, u32 len, i; int rc = 0; struct dsi_display_mode_priv_info *priv_info; + u64 pixel_clk_khz; if (!mode || !mode->priv_info) return -EINVAL; @@ -2520,9 +2521,11 @@ static int dsi_panel_parse_phy_timing(struct dsi_display_mode *mode, * function dsi_panel_calc_dsi_transfer_time( ) * as we set it based on dsi clock or mdp transfer time. */ - mode->pixel_clk_khz = (DSI_H_TOTAL_DSC(&mode->timing) * + pixel_clk_khz = (DSI_H_TOTAL_DSC(&mode->timing) * DSI_V_TOTAL(&mode->timing) * - mode->timing.refresh_rate) / 1000; + mode->timing.refresh_rate); + do_div(pixel_clk_khz, 1000); + mode->pixel_clk_khz = pixel_clk_khz; } return rc; @@ -3570,7 +3573,8 @@ void dsi_panel_calc_dsi_transfer_time(struct dsi_host_common_cfg *config, struct dsi_display_mode *mode, u32 frame_threshold_us) { u32 frame_time_us,nslices; - u64 min_bitclk_hz, total_active_pixels, bits_per_line, pclk_rate_hz; + u64 min_bitclk_hz, total_active_pixels, bits_per_line, pclk_rate_hz, + dsi_transfer_time_us, pixel_clk_khz; struct msm_display_dsc_info *dsc = mode->timing.dsc; struct dsi_mode_info *timing = &mode->timing; struct dsi_display_mode *display_mode; @@ -3605,15 +3609,18 @@ void dsi_panel_calc_dsi_transfer_time(struct dsi_host_common_cfg *config, * timing->v_active)); /* calculate the actual bitclk needed to transfer the frame */ min_bitclk_hz = (total_active_pixels * (timing->refresh_rate) * - (config->bpp)) / (config->num_data_lanes); + (config->bpp)); + do_div(min_bitclk_hz, config->num_data_lanes); } timing->min_dsi_clk_hz = min_bitclk_hz; if (timing->clk_rate_hz) { /* adjust the transfer time proportionately for bit clk*/ - timing->dsi_transfer_time_us = mult_frac(frame_time_us, - min_bitclk_hz, timing->clk_rate_hz); + dsi_transfer_time_us = frame_time_us * min_bitclk_hz; + do_div(dsi_transfer_time_us, timing->clk_rate_hz); + timing->dsi_transfer_time_us = dsi_transfer_time_us; + } else if (mode->priv_info->mdp_transfer_time_us) { timing->dsi_transfer_time_us = mode->priv_info->mdp_transfer_time_us; @@ -3655,13 +3662,14 @@ void dsi_panel_calc_dsi_transfer_time(struct dsi_host_common_cfg *config, } /* Calculate pclk_khz to update modeinfo */ - pclk_rate_hz = mult_frac(min_bitclk_hz, frame_time_us, - timing->dsi_transfer_time_us); + pclk_rate_hz = min_bitclk_hz * frame_time_us; + do_div(pclk_rate_hz, timing->dsi_transfer_time_us); - display_mode->pixel_clk_khz = mult_frac(pclk_rate_hz, - config->num_data_lanes, config->bpp); + pixel_clk_khz = pclk_rate_hz * config->num_data_lanes; + do_div(pixel_clk_khz, config->bpp); + display_mode->pixel_clk_khz = pixel_clk_khz; - do_div(display_mode->pixel_clk_khz, 1000); + display_mode->pixel_clk_khz = display_mode->pixel_clk_khz / 1000; } diff --git a/msm/dsi/dsi_phy_timing_v4_0.c b/msm/dsi/dsi_phy_timing_v4_0.c index 8e8927adc8dc..eb6a8f7c2276 100644 --- a/msm/dsi/dsi_phy_timing_v4_0.c +++ b/msm/dsi/dsi_phy_timing_v4_0.c @@ -26,7 +26,7 @@ int32_t dsi_phy_hw_v4_0_calc_clk_zero(s64 rec_temp1, s64 mult) s64 rec_temp2, rec_temp3; rec_temp2 = rec_temp1; - rec_temp3 = roundup(div_s64(rec_temp2, 8), mult); + rec_temp3 = roundup64(div_s64(rec_temp2, 8), mult); return (div_s64(rec_temp3, mult) - 1); } @@ -37,7 +37,7 @@ int32_t dsi_phy_hw_v4_0_calc_clk_trail_rec_min(s64 temp_mul, rec_temp1 = temp_mul; rec_temp2 = div_s64(rec_temp1, 8); - rec_temp3 = roundup(rec_temp2, mult); + rec_temp3 = roundup64(rec_temp2, mult); return (div_s64(rec_temp3, mult) - 1); } @@ -53,7 +53,7 @@ int32_t dsi_phy_hw_v4_0_calc_hs_zero(s64 temp1, s64 mult) { s64 rec_temp2, rec_min; - rec_temp2 = roundup((temp1 / 8), mult); + rec_temp2 = roundup64((temp1 / 8), mult); rec_min = rec_temp2 - (1 * mult); return div_s64(rec_min, mult); } diff --git a/msm/msm_smmu.c b/msm/msm_smmu.c index d61f3450228c..8742b3041705 100644 --- a/msm/msm_smmu.c +++ b/msm/msm_smmu.c @@ -451,7 +451,7 @@ static int msm_smmu_probe(struct platform_device *pdev) client->dev->dma_parms = devm_kzalloc(client->dev, sizeof(*client->dev->dma_parms), GFP_KERNEL); dma_set_max_seg_size(client->dev, DMA_BIT_MASK(32)); - dma_set_seg_boundary(client->dev, DMA_BIT_MASK(64)); + dma_set_seg_boundary(client->dev, (unsigned long)DMA_BIT_MASK(64)); iommu_set_fault_handler(client->domain, msm_smmu_fault_handler, (void *)client); diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index 2265a2abaad1..e0c24b929aa3 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -276,7 +276,7 @@ static ssize_t measured_fps_show(struct device *device, { struct drm_crtc *crtc; struct sde_crtc *sde_crtc; - unsigned int fps_int, fps_decimal; + uint64_t fps_int, fps_decimal; u64 fps = 0, frame_count = 0; ktime_t current_time; int i = 0, current_time_index; @@ -353,7 +353,7 @@ static ssize_t measured_fps_show(struct device *device, } } - fps_int = (unsigned int) sde_crtc->fps_info.measured_fps; + fps_int = (uint64_t) sde_crtc->fps_info.measured_fps; fps_decimal = do_div(fps_int, 10); return scnprintf(buf, PAGE_SIZE, "fps: %d.%d duration:%d frame_count:%lld\n", fps_int, fps_decimal, diff --git a/msm/sde_power_handle.h b/msm/sde_power_handle.h index 3505c2b0ed15..2587c3734fce 100644 --- a/msm/sde_power_handle.h +++ b/msm/sde_power_handle.h @@ -14,8 +14,8 @@ #define SDE_POWER_HANDLE_ENABLE_NRT_BUS_IB_QUOTA 0 #define SDE_POWER_HANDLE_DISABLE_BUS_IB_QUOTA 0 -#define SDE_POWER_HANDLE_CONT_SPLASH_BUS_IB_QUOTA 3000000000 -#define SDE_POWER_HANDLE_CONT_SPLASH_BUS_AB_QUOTA 3000000000 +#define SDE_POWER_HANDLE_CONT_SPLASH_BUS_IB_QUOTA 3000000000ULL +#define SDE_POWER_HANDLE_CONT_SPLASH_BUS_AB_QUOTA 3000000000ULL #include #include diff --git a/pll/dsi_pll.h b/pll/dsi_pll.h index f944ad100191..35aa81494120 100644 --- a/pll/dsi_pll.h +++ b/pll/dsi_pll.h @@ -23,8 +23,8 @@ struct lpfr_cfg { struct dsi_pll_vco_clk { struct clk_hw hw; unsigned long ref_clk_rate; - unsigned long min_rate; - unsigned long max_rate; + u64 min_rate; + u64 max_rate; u32 pll_en_seq_cnt; struct lpfr_cfg *lpfr_lut; u32 lpfr_lut_size; diff --git a/pll/dsi_pll_14nm_util.c b/pll/dsi_pll_14nm_util.c index 74f6eaf3718b..5cd7869cf77f 100644 --- a/pll/dsi_pll_14nm_util.c +++ b/pll/dsi_pll_14nm_util.c @@ -1062,10 +1062,11 @@ long pll_vco_round_rate_14nm(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { unsigned long rrate = rate; - u32 div; + u64 div; struct dsi_pll_vco_clk *vco = to_vco_clk_hw(hw); - div = vco->min_rate / rate; + div = vco->min_rate; + do_div(div, rate); if (div > 15) { /* rate < 86.67 Mhz */ pr_err("rate=%lu NOT supportted\n", rate); diff --git a/pll/dsi_pll_7nm.c b/pll/dsi_pll_7nm.c index 3cd11e7a8ff7..6eaa7f383b38 100644 --- a/pll/dsi_pll_7nm.c +++ b/pll/dsi_pll_7nm.c @@ -576,11 +576,11 @@ static void dsi_pll_calc_dec_frac(struct dsi_pll_7nm *pll, break; case MDSS_DSI_PLL_7NM_V4_1: default: - if (pll_freq <= 1000000000) + if (pll_freq <= 1000000000ULL) regs->pll_clock_inverters = 0xA0; - else if (pll_freq <= 2500000000) + else if (pll_freq <= 2500000000ULL) regs->pll_clock_inverters = 0x20; - else if (pll_freq <= 3020000000) + else if (pll_freq <= 3020000000ULL) regs->pll_clock_inverters = 0x00; else regs->pll_clock_inverters = 0x40; @@ -680,16 +680,16 @@ static void dsi_pll_config_hzindep_reg(struct dsi_pll_7nm *pll, break; case MDSS_DSI_PLL_7NM_V4_1: default: - if (vco_rate < 3100000000) + if (vco_rate < 3100000000ULL) MDSS_PLL_REG_W(pll_base, PLL_ANALOG_CONTROLS_FIVE_1, 0x01); else MDSS_PLL_REG_W(pll_base, PLL_ANALOG_CONTROLS_FIVE_1, 0x03); - if (vco_rate < 1520000000) + if (vco_rate < 1520000000ULL) MDSS_PLL_REG_W(pll_base, PLL_VCO_CONFIG_1, 0x08); - else if (vco_rate < 2990000000) + else if (vco_rate < 2990000000ULL) MDSS_PLL_REG_W(pll_base, PLL_VCO_CONFIG_1, 0x01); else MDSS_PLL_REG_W(pll_base, PLL_VCO_CONFIG_1, 0x00); diff --git a/rotator/sde_rotator_smmu.c b/rotator/sde_rotator_smmu.c index 6e251541b0ea..5e323dabb489 100644 --- a/rotator/sde_rotator_smmu.c +++ b/rotator/sde_rotator_smmu.c @@ -617,7 +617,7 @@ int sde_smmu_probe(struct platform_device *pdev) sizeof(*dev->dma_parms), GFP_KERNEL); dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); - dma_set_seg_boundary(dev, DMA_BIT_MASK(64)); + dma_set_seg_boundary(dev, (unsigned long)DMA_BIT_MASK(64)); iommu_set_fault_handler(sde_smmu->rot_domain, sde_smmu_fault_handler, (void *)sde_smmu); From f1f06fc585f755f3f80ed31735314b96917d8281 Mon Sep 17 00:00:00 2001 From: Samantha Tran Date: Mon, 28 Oct 2019 13:20:21 -0700 Subject: [PATCH 13/15] disp: msm: adjust smmu detach sequence to include unmapping Device needs to remove mappings prior to iommu detach call. Change-Id: I1bfe771ec34bd75742c40aaa4d95528d82386f48 Signed-off-by: Samantha Tran --- msm/msm_smmu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/msm/msm_smmu.c b/msm/msm_smmu.c index d61f3450228c..3d086785f8ea 100644 --- a/msm/msm_smmu.c +++ b/msm/msm_smmu.c @@ -94,6 +94,7 @@ static void msm_smmu_detach(struct msm_mmu *mmu, const char * const *names, return; pm_runtime_get_sync(mmu->dev); + msm_dma_unmap_all_for_dev(client->dev); iommu_detach_device(client->domain, client->dev); pm_runtime_put_sync(mmu->dev); From 6ed9f4a3d3398ad08718c4baf143475027774d6e Mon Sep 17 00:00:00 2001 From: Yuan Zhao Date: Fri, 11 Oct 2019 10:26:51 +0800 Subject: [PATCH 14/15] disp: msm: dsi: Config panel test pin to input mode when panel off The pin was configured to input mode when panel on. But if continuous splash was on, driver will not power on panel when booting up, the first time panel off, this gpio was the default mode or the UEFI config mode. Change-Id: Ib352343848ab38cab828cc10388b366aeac8905d Signed-off-by: Yuan Zhao --- msm/dsi/dsi_panel.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/msm/dsi/dsi_panel.c b/msm/dsi/dsi_panel.c index d37561c6a76b..069f0f7f873a 100644 --- a/msm/dsi/dsi_panel.c +++ b/msm/dsi/dsi_panel.c @@ -499,6 +499,13 @@ static int dsi_panel_power_off(struct dsi_panel *panel) if (gpio_is_valid(panel->reset_config.lcd_mode_sel_gpio)) gpio_set_value(panel->reset_config.lcd_mode_sel_gpio, 0); + if (gpio_is_valid(panel->panel_test_gpio)) { + rc = gpio_direction_input(panel->panel_test_gpio); + if (rc) + DSI_WARN("set dir for panel test gpio failed rc=%d\n", + rc); + } + rc = dsi_panel_set_pinctrl_state(panel, false); if (rc) { DSI_ERR("[%s] failed set pinctrl state, rc=%d\n", panel->name, From 8bc240b71dd3a0759a8975a8ab3984183c313257 Mon Sep 17 00:00:00 2001 From: Satya Rama Aditya Pinapala Date: Fri, 30 Aug 2019 10:39:00 -0700 Subject: [PATCH 15/15] disp: msm: dsi: handle wait for dma cmd completion The current solution triggers the DMA command and waits till the command dma done and ISR signals completion. This change introduces asynchronous wait after a DCS command has been triggered. Enable this mode only during pre kickoff, so as to not block commit thread. Change-Id: Iead7b6328883e844147d47ff68dc878943879553 Signed-off-by: Satya Rama Aditya Pinapala --- msm/dsi/dsi_ctrl.c | 245 ++++++++++++++++++++++++++++------------ msm/dsi/dsi_ctrl.h | 34 +++++- msm/dsi/dsi_display.c | 55 ++++++++- msm/dsi/dsi_display.h | 5 + msm/sde/sde_connector.c | 11 ++ 5 files changed, 269 insertions(+), 81 deletions(-) diff --git a/msm/dsi/dsi_ctrl.c b/msm/dsi/dsi_ctrl.c index 241aa21f8cad..c12685ae91f0 100644 --- a/msm/dsi/dsi_ctrl.c +++ b/msm/dsi/dsi_ctrl.c @@ -258,6 +258,95 @@ dsi_ctrl_get_aspace(struct dsi_ctrl *dsi_ctrl, return msm_gem_smmu_address_space_get(dsi_ctrl->drm_dev, domain); } +static void dsi_ctrl_flush_cmd_dma_queue(struct dsi_ctrl *dsi_ctrl) +{ + u32 status; + u32 mask = DSI_CMD_MODE_DMA_DONE; + struct dsi_ctrl_hw_ops dsi_hw_ops = dsi_ctrl->hw.ops; + + /* + * If a command is triggered right after another command, + * check if the previous command transfer is completed. If + * transfer is done, cancel any work that has been + * queued. Otherwise wait till the work is scheduled and + * completed before triggering the next command by + * flushing the workqueue. + */ + status = dsi_hw_ops.get_interrupt_status(&dsi_ctrl->hw); + if (atomic_read(&dsi_ctrl->dma_irq_trig)) { + cancel_work_sync(&dsi_ctrl->dma_cmd_wait); + } else if (status & mask) { + atomic_set(&dsi_ctrl->dma_irq_trig, 1); + status |= (DSI_CMD_MODE_DMA_DONE | DSI_BTA_DONE); + dsi_hw_ops.clear_interrupt_status( + &dsi_ctrl->hw, + status); + dsi_ctrl_disable_status_interrupt(dsi_ctrl, + DSI_SINT_CMD_MODE_DMA_DONE); + complete_all(&dsi_ctrl->irq_info.cmd_dma_done); + cancel_work_sync(&dsi_ctrl->dma_cmd_wait); + DSI_CTRL_DEBUG(dsi_ctrl, + "dma_tx done but irq not yet triggered\n"); + } else { + flush_workqueue(dsi_ctrl->dma_cmd_workq); + } +} + +static void dsi_ctrl_dma_cmd_wait_for_done(struct work_struct *work) +{ + int ret = 0; + struct dsi_ctrl *dsi_ctrl = NULL; + u32 status; + u32 mask = DSI_CMD_MODE_DMA_DONE; + struct dsi_ctrl_hw_ops dsi_hw_ops; + + dsi_ctrl = container_of(work, struct dsi_ctrl, dma_cmd_wait); + dsi_hw_ops = dsi_ctrl->hw.ops; + SDE_EVT32(dsi_ctrl->cell_index, SDE_EVTLOG_FUNC_ENTRY); + + /* + * This atomic state will be set if ISR has been triggered, + * so the wait is not needed. + */ + if (atomic_read(&dsi_ctrl->dma_irq_trig)) + goto done; + /* + * If IRQ wasn't triggered check interrupt status register for + * transfer done before waiting. + */ + status = dsi_hw_ops.get_interrupt_status(&dsi_ctrl->hw); + if (status & mask) { + status |= (DSI_CMD_MODE_DMA_DONE | DSI_BTA_DONE); + dsi_hw_ops.clear_interrupt_status(&dsi_ctrl->hw, + status); + dsi_ctrl_disable_status_interrupt(dsi_ctrl, + DSI_SINT_CMD_MODE_DMA_DONE); + goto done; + } + + ret = wait_for_completion_timeout( + &dsi_ctrl->irq_info.cmd_dma_done, + msecs_to_jiffies(DSI_CTRL_TX_TO_MS)); + if (ret == 0) { + status = dsi_hw_ops.get_interrupt_status(&dsi_ctrl->hw); + if (status & mask) { + status |= (DSI_CMD_MODE_DMA_DONE | DSI_BTA_DONE); + dsi_hw_ops.clear_interrupt_status(&dsi_ctrl->hw, + status); + DSI_CTRL_WARN(dsi_ctrl, + "dma_tx done but irq not triggered\n"); + } else { + DSI_CTRL_ERR(dsi_ctrl, + "Command transfer failed\n"); + } + dsi_ctrl_disable_status_interrupt(dsi_ctrl, + DSI_SINT_CMD_MODE_DMA_DONE); + } + +done: + dsi_ctrl->dma_wait_queued = false; +} + static int dsi_ctrl_check_state(struct dsi_ctrl *dsi_ctrl, enum dsi_ctrl_driver_ops op, u32 op_state) @@ -1106,12 +1195,12 @@ static void dsi_kickoff_msg_tx(struct dsi_ctrl *dsi_ctrl, struct dsi_ctrl_cmd_dma_info *cmd_mem, u32 flags) { - int rc = 0, ret = 0; u32 hw_flags = 0; u32 line_no = 0x1; struct dsi_mode_info *timing; struct dsi_ctrl_hw_ops dsi_hw_ops = dsi_ctrl->hw.ops; + SDE_EVT32(dsi_ctrl->cell_index, SDE_EVTLOG_FUNC_ENTRY, flags); /* check if custom dma scheduling line needed */ if ((dsi_ctrl->host_config.panel_mode == DSI_OP_VIDEO_MODE) && (flags & DSI_CTRL_CMD_CUSTOM_DMA_SCHED)) @@ -1156,11 +1245,13 @@ static void dsi_kickoff_msg_tx(struct dsi_ctrl *dsi_ctrl, if (!(flags & DSI_CTRL_CMD_DEFER_TRIGGER)) { dsi_ctrl_wait_for_video_done(dsi_ctrl); - dsi_ctrl_enable_status_interrupt(dsi_ctrl, - DSI_SINT_CMD_MODE_DMA_DONE, NULL); if (dsi_hw_ops.mask_error_intr) dsi_hw_ops.mask_error_intr(&dsi_ctrl->hw, BIT(DSI_FIFO_OVERFLOW), true); + + atomic_set(&dsi_ctrl->dma_irq_trig, 0); + dsi_ctrl_enable_status_interrupt(dsi_ctrl, + DSI_SINT_CMD_MODE_DMA_DONE, NULL); reinit_completion(&dsi_ctrl->irq_info.cmd_dma_done); if (flags & DSI_CTRL_CMD_FETCH_MEMORY) { @@ -1180,34 +1271,13 @@ static void dsi_kickoff_msg_tx(struct dsi_ctrl *dsi_ctrl, cmd, hw_flags); } - - ret = wait_for_completion_timeout( - &dsi_ctrl->irq_info.cmd_dma_done, - msecs_to_jiffies(DSI_CTRL_TX_TO_MS)); - - if (ret == 0) { - u32 status = dsi_hw_ops.get_interrupt_status( - &dsi_ctrl->hw); - u32 mask = DSI_CMD_MODE_DMA_DONE; - - if (status & mask) { - status |= (DSI_CMD_MODE_DMA_DONE | - DSI_BTA_DONE); - dsi_hw_ops.clear_interrupt_status( - &dsi_ctrl->hw, - status); - dsi_ctrl_disable_status_interrupt(dsi_ctrl, - DSI_SINT_CMD_MODE_DMA_DONE); - complete_all(&dsi_ctrl->irq_info.cmd_dma_done); - DSI_CTRL_WARN(dsi_ctrl, - "dma_tx done but irq not triggered\n"); - } else { - rc = -ETIMEDOUT; - dsi_ctrl_disable_status_interrupt(dsi_ctrl, - DSI_SINT_CMD_MODE_DMA_DONE); - DSI_CTRL_ERR(dsi_ctrl, - "Command transfer failed\n"); - } + if (flags & DSI_CTRL_CMD_ASYNC_WAIT) { + dsi_ctrl->dma_wait_queued = true; + queue_work(dsi_ctrl->dma_cmd_workq, + &dsi_ctrl->dma_cmd_wait); + } else { + dsi_ctrl->dma_wait_queued = false; + dsi_ctrl_dma_cmd_wait_for_done(&dsi_ctrl->dma_cmd_wait); } if (dsi_hw_ops.mask_error_intr && !dsi_ctrl->esd_check_underway) @@ -1227,6 +1297,20 @@ static void dsi_kickoff_msg_tx(struct dsi_ctrl *dsi_ctrl, } } +static u32 dsi_ctrl_validate_msg_flags(const struct mipi_dsi_msg *msg, + u32 flags) +{ + /* + * ASYNC command wait mode is not supported for FIFO commands. + * Waiting after a command is transferred cannot be guaranteed + * if DSI_CTRL_CMD_ASYNC_WAIT flag is set. + */ + if ((flags & DSI_CTRL_CMD_FIFO_STORE) || + msg->wait_ms) + flags &= ~DSI_CTRL_CMD_ASYNC_WAIT; + return flags; +} + static int dsi_message_tx(struct dsi_ctrl *dsi_ctrl, const struct mipi_dsi_msg *msg, u32 flags) @@ -1252,6 +1336,11 @@ static int dsi_message_tx(struct dsi_ctrl *dsi_ctrl, goto error; } + flags = dsi_ctrl_validate_msg_flags(msg, flags); + + if (dsi_ctrl->dma_wait_queued) + dsi_ctrl_flush_cmd_dma_queue(dsi_ctrl); + if (flags & DSI_CTRL_CMD_NON_EMBEDDED_MODE) { cmd_mem.offset = dsi_ctrl->cmd_buffer_iova; cmd_mem.en_broadcast = (flags & DSI_CTRL_CMD_BROADCAST) ? @@ -1793,6 +1882,9 @@ static int dsi_ctrl_dev_probe(struct platform_device *pdev) dsi_ctrl->irq_info.irq_num = -1; dsi_ctrl->irq_info.irq_stat_mask = 0x0; + INIT_WORK(&dsi_ctrl->dma_cmd_wait, dsi_ctrl_dma_cmd_wait_for_done); + atomic_set(&dsi_ctrl->dma_irq_trig, 0); + spin_lock_init(&dsi_ctrl->irq_info.irq_lock); rc = dsi_ctrl_dts_parse(dsi_ctrl, pdev->dev.of_node); @@ -1896,6 +1988,7 @@ static int dsi_ctrl_dev_remove(struct platform_device *pdev) DSI_CTRL_ERR(dsi_ctrl, "failed to deinitialize clocks, rc=%d\n", rc); + atomic_set(&dsi_ctrl->dma_irq_trig, 0); mutex_unlock(&dsi_ctrl->ctrl_lock); mutex_destroy(&dsi_ctrl->ctrl_lock); @@ -2213,10 +2306,9 @@ exit: return rc; } -int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl) +int dsi_ctrl_timing_setup(struct dsi_ctrl *dsi_ctrl) { int rc = 0; - if (!dsi_ctrl) { DSI_CTRL_ERR(dsi_ctrl, "Invalid params\n"); return -EINVAL; @@ -2224,12 +2316,6 @@ int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl) mutex_lock(&dsi_ctrl->ctrl_lock); - dsi_ctrl->hw.ops.setup_lane_map(&dsi_ctrl->hw, - &dsi_ctrl->host_config.lane_map); - - dsi_ctrl->hw.ops.host_setup(&dsi_ctrl->hw, - &dsi_ctrl->host_config.common_config); - if (dsi_ctrl->host_config.panel_mode == DSI_OP_CMD_MODE) { dsi_ctrl->hw.ops.cmd_engine_setup(&dsi_ctrl->hw, &dsi_ctrl->host_config.common_config, @@ -2250,8 +2336,29 @@ int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl) dsi_ctrl->hw.ops.video_engine_en(&dsi_ctrl->hw, true); } + mutex_unlock(&dsi_ctrl->ctrl_lock); + return rc; +} + +int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl) +{ + int rc = 0; + + rc = dsi_ctrl_timing_setup(dsi_ctrl); + if (rc) + return -EINVAL; + + mutex_lock(&dsi_ctrl->ctrl_lock); + + dsi_ctrl->hw.ops.setup_lane_map(&dsi_ctrl->hw, + &dsi_ctrl->host_config.lane_map); + + dsi_ctrl->hw.ops.host_setup(&dsi_ctrl->hw, + &dsi_ctrl->host_config.common_config); + dsi_ctrl->hw.ops.enable_status_interrupts(&dsi_ctrl->hw, 0x0); dsi_ctrl_enable_error_interrupts(dsi_ctrl); + dsi_ctrl->hw.ops.ctrl_en(&dsi_ctrl->hw, true); mutex_unlock(&dsi_ctrl->ctrl_lock); @@ -2489,6 +2596,7 @@ static irqreturn_t dsi_ctrl_isr(int irq, void *ptr) dsi_ctrl_handle_error_status(dsi_ctrl, errors); if (status & DSI_CMD_MODE_DMA_DONE) { + atomic_set(&dsi_ctrl->dma_irq_trig, 1); dsi_ctrl_disable_status_interrupt(dsi_ctrl, DSI_SINT_CMD_MODE_DMA_DONE); complete_all(&dsi_ctrl->irq_info.cmd_dma_done); @@ -2603,6 +2711,7 @@ void dsi_ctrl_enable_status_interrupt(struct dsi_ctrl *dsi_ctrl, intr_idx >= DSI_STATUS_INTERRUPT_COUNT) return; + SDE_EVT32(dsi_ctrl->cell_index, SDE_EVTLOG_FUNC_ENTRY); spin_lock_irqsave(&dsi_ctrl->irq_info.irq_lock, flags); if (dsi_ctrl->irq_info.irq_stat_refcount[intr_idx] == 0) { @@ -2632,6 +2741,7 @@ void dsi_ctrl_disable_status_interrupt(struct dsi_ctrl *dsi_ctrl, intr_idx >= DSI_STATUS_INTERRUPT_COUNT) return; + SDE_EVT32(dsi_ctrl->cell_index, SDE_EVTLOG_FUNC_ENTRY); spin_lock_irqsave(&dsi_ctrl->irq_info.irq_lock, flags); if (dsi_ctrl->irq_info.irq_stat_refcount[intr_idx]) @@ -3070,15 +3180,17 @@ error: */ int dsi_ctrl_cmd_tx_trigger(struct dsi_ctrl *dsi_ctrl, u32 flags) { - int rc = 0, ret = 0; - u32 status = 0; - u32 mask = (DSI_CMD_MODE_DMA_DONE); + int rc = 0; + struct dsi_ctrl_hw_ops dsi_hw_ops; if (!dsi_ctrl) { DSI_CTRL_ERR(dsi_ctrl, "Invalid params\n"); return -EINVAL; } + dsi_hw_ops = dsi_ctrl->hw.ops; + + SDE_EVT32(dsi_ctrl->cell_index, SDE_EVTLOG_FUNC_ENTRY, flags); /* Dont trigger the command if this is not the last ocmmand */ if (!(flags & DSI_CTRL_CMD_LAST_COMMAND)) return rc; @@ -3086,52 +3198,37 @@ int dsi_ctrl_cmd_tx_trigger(struct dsi_ctrl *dsi_ctrl, u32 flags) mutex_lock(&dsi_ctrl->ctrl_lock); if (!(flags & DSI_CTRL_CMD_BROADCAST_MASTER)) - dsi_ctrl->hw.ops.trigger_command_dma(&dsi_ctrl->hw); + dsi_hw_ops.trigger_command_dma(&dsi_ctrl->hw); if ((flags & DSI_CTRL_CMD_BROADCAST) && (flags & DSI_CTRL_CMD_BROADCAST_MASTER)) { dsi_ctrl_wait_for_video_done(dsi_ctrl); + if (dsi_hw_ops.mask_error_intr) + dsi_hw_ops.mask_error_intr(&dsi_ctrl->hw, + BIT(DSI_FIFO_OVERFLOW), true); + atomic_set(&dsi_ctrl->dma_irq_trig, 0); dsi_ctrl_enable_status_interrupt(dsi_ctrl, DSI_SINT_CMD_MODE_DMA_DONE, NULL); - if (dsi_ctrl->hw.ops.mask_error_intr) - dsi_ctrl->hw.ops.mask_error_intr(&dsi_ctrl->hw, - BIT(DSI_FIFO_OVERFLOW), true); reinit_completion(&dsi_ctrl->irq_info.cmd_dma_done); /* trigger command */ - dsi_ctrl->hw.ops.trigger_command_dma(&dsi_ctrl->hw); - - ret = wait_for_completion_timeout( - &dsi_ctrl->irq_info.cmd_dma_done, - msecs_to_jiffies(DSI_CTRL_TX_TO_MS)); - - if (ret == 0) { - status = dsi_ctrl->hw.ops.get_interrupt_status( - &dsi_ctrl->hw); - if (status & mask) { - status |= (DSI_CMD_MODE_DMA_DONE | - DSI_BTA_DONE); - dsi_ctrl->hw.ops.clear_interrupt_status( - &dsi_ctrl->hw, - status); - dsi_ctrl_disable_status_interrupt(dsi_ctrl, - DSI_SINT_CMD_MODE_DMA_DONE); - complete_all(&dsi_ctrl->irq_info.cmd_dma_done); - DSI_CTRL_WARN(dsi_ctrl, "dma_tx done but irq not triggered\n"); - } else { - rc = -ETIMEDOUT; - dsi_ctrl_disable_status_interrupt(dsi_ctrl, - DSI_SINT_CMD_MODE_DMA_DONE); - DSI_CTRL_ERR(dsi_ctrl, "Command transfer failed\n"); - } + dsi_hw_ops.trigger_command_dma(&dsi_ctrl->hw); + if (flags & DSI_CTRL_CMD_ASYNC_WAIT) { + dsi_ctrl->dma_wait_queued = true; + queue_work(dsi_ctrl->dma_cmd_workq, + &dsi_ctrl->dma_cmd_wait); + } else { + dsi_ctrl->dma_wait_queued = false; + dsi_ctrl_dma_cmd_wait_for_done(&dsi_ctrl->dma_cmd_wait); } - if (dsi_ctrl->hw.ops.mask_error_intr && + + if (dsi_hw_ops.mask_error_intr && !dsi_ctrl->esd_check_underway) - dsi_ctrl->hw.ops.mask_error_intr(&dsi_ctrl->hw, + dsi_hw_ops.mask_error_intr(&dsi_ctrl->hw, BIT(DSI_FIFO_OVERFLOW), false); if (flags & DSI_CTRL_CMD_NON_EMBEDDED_MODE) { - dsi_ctrl->hw.ops.soft_reset(&dsi_ctrl->hw); + dsi_hw_ops.soft_reset(&dsi_ctrl->hw); dsi_ctrl->cmd_len = 0; } } diff --git a/msm/dsi/dsi_ctrl.h b/msm/dsi/dsi_ctrl.h index 8144f6d53170..77bb3c332f7a 100644 --- a/msm/dsi/dsi_ctrl.h +++ b/msm/dsi/dsi_ctrl.h @@ -31,6 +31,8 @@ * @DSI_CTRL_CMD_NON_EMBEDDED_MODE:Transfer cmd packets in non embedded mode. * @DSI_CTRL_CMD_CUSTOM_DMA_SCHED: Use the dma scheduling line number defined in * 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. */ #define DSI_CTRL_CMD_READ 0x1 #define DSI_CTRL_CMD_BROADCAST 0x2 @@ -41,6 +43,7 @@ #define DSI_CTRL_CMD_LAST_COMMAND 0x40 #define DSI_CTRL_CMD_NON_EMBEDDED_MODE 0x80 #define DSI_CTRL_CMD_CUSTOM_DMA_SCHED 0x100 +#define DSI_CTRL_CMD_ASYNC_WAIT 0x200 /* DSI embedded mode fifo size * If the command is greater than 256 bytes it is sent in non-embedded mode. @@ -217,6 +220,13 @@ struct dsi_ctrl_interrupts { * @vaddr: CPU virtual address of cmd buffer. * @secure_mode: Indicates if secure-session is in progress * @esd_check_underway: Indicates if esd status check is in progress + * @dma_cmd_wait: Work object waiting on DMA command transfer done. + * @dma_cmd_workq: Pointer to the workqueue of DMA command transfer done + * wait sequence. + * @dma_wait_queued: Indicates if any DMA command transfer wait work + * is queued. + * @dma_irq_trig: Atomic state to indicate DMA done IRQ + * triggered. * @debugfs_root: Root for debugfs entries. * @misr_enable: Frame MISR enable/disable * @misr_cache: Cached Frame MISR value @@ -267,6 +277,10 @@ struct dsi_ctrl { void *vaddr; bool secure_mode; bool esd_check_underway; + struct work_struct dma_cmd_wait; + struct workqueue_struct *dma_cmd_workq; + bool dma_wait_queued; + atomic_t dma_irq_trig; /* Debug Information */ struct dentry *debugfs_root; @@ -485,18 +499,30 @@ int dsi_ctrl_host_deinit(struct dsi_ctrl *dsi_ctrl); int dsi_ctrl_set_ulps(struct dsi_ctrl *dsi_ctrl, bool enable); /** - * dsi_ctrl_setup() - Setup DSI host hardware while coming out of idle screen. + * dsi_ctrl_timing_setup() - Setup DSI host config * @dsi_ctrl: DSI controller handle. * * Initializes DSI controller hardware with host configuration provided by - * dsi_ctrl_update_host_config(). Initialization can be performed only during - * DSI_CTRL_POWER_CORE_CLK_ON state and after the PHY SW reset has been - * performed. + * dsi_ctrl_update_host_config(). This is called while setting up DSI host + * through dsi_ctrl_setup() and after any ROI change. * * Also used to program the video mode timing values. * * Return: error code. */ +int dsi_ctrl_timing_setup(struct dsi_ctrl *dsi_ctrl); + +/** + * dsi_ctrl_setup() - Setup DSI host hardware while coming out of idle screen. + * @dsi_ctrl: DSI controller handle. + * + * Initialization of DSI controller hardware with host configuration and + * enabling required interrupts. Initialization can be performed only during + * DSI_CTRL_POWER_CORE_CLK_ON state and after the PHY SW reset has been + * performed. + * + * Return: error code. + */ int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl); /** diff --git a/msm/dsi/dsi_display.c b/msm/dsi/dsi_display.c index 17149dc0281b..8c799f00836c 100644 --- a/msm/dsi/dsi_display.c +++ b/msm/dsi/dsi_display.c @@ -2699,6 +2699,12 @@ static int dsi_display_broadcast_cmd(struct dsi_display *display, flags |= DSI_CTRL_CMD_LAST_COMMAND; m_flags |= DSI_CTRL_CMD_LAST_COMMAND; } + + if (display->queue_cmd_waits) { + flags |= DSI_CTRL_CMD_ASYNC_WAIT; + m_flags |= DSI_CTRL_CMD_ASYNC_WAIT; + } + /* * 1. Setup commands in FIFO * 2. Trigger commands @@ -2852,9 +2858,13 @@ static ssize_t dsi_host_transfer(struct mipi_dsi_host *host, } else { int ctrl_idx = (msg->flags & MIPI_DSI_MSG_UNICAST) ? msg->ctrl : 0; + u32 cmd_flags = DSI_CTRL_CMD_FETCH_MEMORY; + + if (display->queue_cmd_waits) + cmd_flags |= DSI_CTRL_CMD_ASYNC_WAIT; rc = dsi_ctrl_cmd_transfer(display->ctrl[ctrl_idx].ctrl, msg, - DSI_CTRL_CMD_FETCH_MEMORY); + cmd_flags); if (rc) { DSI_ERR("[%s] cmd transfer failed, rc=%d\n", display->name, rc); @@ -3151,6 +3161,22 @@ int dsi_pre_clkoff_cb(void *priv, struct dsi_display *display = priv; struct dsi_display_ctrl *ctrl; + + /* + * If Idle Power Collapse occurs immediately after a CMD + * transfer with an asynchronous wait for DMA done, ensure + * that the work queued is scheduled and completed before turning + * off the clocks and disabling interrupts to validate the command + * transfer. + */ + display_for_each_ctrl(i, display) { + ctrl = &display->ctrl[i]; + if (!ctrl->ctrl || !ctrl->ctrl->dma_wait_queued) + continue; + flush_workqueue(display->dma_cmd_workq); + cancel_work_sync(&ctrl->ctrl->dma_cmd_wait); + ctrl->ctrl->dma_wait_queued = false; + } if ((clk & DSI_LINK_CLK) && (new_state == DSI_CLK_OFF) && (l_type & DSI_LINK_LP_CLK)) { /* @@ -4836,6 +4862,7 @@ static int dsi_display_bind(struct device *dev, goto error_ctrl_deinit; } + display_ctrl->ctrl->dma_cmd_workq = display->dma_cmd_workq; memcpy(&info.c_clks[i], (&display_ctrl->ctrl->clk_info.core_clks), sizeof(struct dsi_core_clk_info)); @@ -5013,6 +5040,7 @@ static void dsi_display_unbind(struct device *dev, DSI_ERR("[%s] failed to deinit phy%d driver, rc=%d\n", display->name, i, rc); + display->ctrl->ctrl->dma_cmd_workq = NULL; rc = dsi_ctrl_drv_deinit(display_ctrl->ctrl); if (rc) DSI_ERR("[%s] failed to deinit ctrl%d driver, rc=%d\n", @@ -5101,6 +5129,14 @@ int dsi_display_dev_probe(struct platform_device *pdev) goto end; } + display->dma_cmd_workq = create_singlethread_workqueue( + "dsi_dma_cmd_workq"); + if (!display->dma_cmd_workq) { + DSI_ERR("failed to create work queue\n"); + rc = -EINVAL; + goto end; + } + display->display_type = of_get_property(pdev->dev.of_node, "label", NULL); if (!display->display_type) @@ -5164,8 +5200,9 @@ end: int dsi_display_dev_remove(struct platform_device *pdev) { - int rc = 0; + int rc = 0i, i = 0; struct dsi_display *display; + struct dsi_display_ctrl *ctrl; if (!pdev) { DSI_ERR("Invalid device\n"); @@ -5177,6 +5214,18 @@ int dsi_display_dev_remove(struct platform_device *pdev) /* decrement ref count */ of_node_put(display->panel_node); + if (display->dma_cmd_workq) { + flush_workqueue(display->dma_cmd_workq); + destroy_workqueue(display->dma_cmd_workq); + display->dma_cmd_workq = NULL; + display_for_each_ctrl(i, display) { + ctrl = &display->ctrl[i]; + if (!ctrl->ctrl) + continue; + ctrl->ctrl->dma_cmd_workq = NULL; + } + } + (void)_dsi_display_dev_deinit(display); platform_set_drvdata(pdev, NULL); @@ -7003,7 +7052,7 @@ static int dsi_display_set_roi(struct dsi_display *display, } /* re-program the ctrl with the timing based on the new roi */ - rc = dsi_ctrl_setup(ctrl->ctrl); + rc = dsi_ctrl_timing_setup(ctrl->ctrl); if (rc) { DSI_ERR("dsi_ctrl_setup failed rc %d\n", rc); return rc; diff --git a/msm/dsi/dsi_display.h b/msm/dsi/dsi_display.h index 2e5115f20038..4c41f3dd7316 100644 --- a/msm/dsi/dsi_display.h +++ b/msm/dsi/dsi_display.h @@ -182,6 +182,9 @@ struct dsi_display_ext_bridge { * @esd_trigger field indicating ESD trigger through debugfs * @te_source vsync source pin information * @clk_gating_config Clocks for which clock gating needs to be enabled + * @queue_cmd_waits Indicates if wait for dma commands done has to be queued. + * @dma_cmd_workq: Pointer to the workqueue of DMA command transfer done + * wait sequence. */ struct dsi_display { struct platform_device *pdev; @@ -266,6 +269,8 @@ struct dsi_display { u32 te_source; u32 clk_gating_config; + bool queue_cmd_waits; + struct workqueue_struct *dma_cmd_workq; }; int dsi_display_dev_probe(struct platform_device *pdev); diff --git a/msm/sde/sde_connector.c b/msm/sde/sde_connector.c index 7da379b92ce8..321ffdfb9531 100644 --- a/msm/sde/sde_connector.c +++ b/msm/sde/sde_connector.c @@ -759,6 +759,7 @@ int sde_connector_pre_kickoff(struct drm_connector *connector) struct sde_connector *c_conn; struct sde_connector_state *c_state; struct msm_display_kickoff_params params; + struct dsi_display *display; int rc; if (!connector) { @@ -773,6 +774,15 @@ int sde_connector_pre_kickoff(struct drm_connector *connector) return -EINVAL; } + /* + * During pre kickoff DCS commands have to have an + * asynchronous wait to avoid an unnecessary stall + * in pre-kickoff. This flag must be reset at the + * end of display pre-kickoff. + */ + display = (struct dsi_display *)c_conn->display; + display->queue_cmd_waits = true; + rc = _sde_connector_update_dirty_properties(connector); if (rc) { SDE_EVT32(connector->base.id, SDE_EVTLOG_ERROR); @@ -789,6 +799,7 @@ int sde_connector_pre_kickoff(struct drm_connector *connector) rc = c_conn->ops.pre_kickoff(connector, c_conn->display, ¶ms); + display->queue_cmd_waits = false; end: return rc; }