diff options
author | JohnnLee <johnnlee@google.com> | 2021-03-29 13:43:53 +0800 |
---|---|---|
committer | JohnnLee <johnnlee@google.com> | 2021-04-01 11:52:02 +0800 |
commit | e471ed45e9ffa031b3a34f3fdb18d48f187cd52b (patch) | |
tree | ce3c1403594217bbd75dbf830ef668e02ac4a2a8 | |
parent | ea5b69a0327130b9553a3ad6ede3efb2734be756 (diff) | |
parent | d7cfa594538bd09e8513e7825637c3ec4c679a83 (diff) | |
download | display-drivers-e471ed45e9ffa031b3a34f3fdb18d48f187cd52b.tar.gz |
Merge branch 'LA.UM.9.12.R1.11.00.00.597.108' via branch 'qcom-msm-4.19-7250' into android-msm-pixel-4.19android-s-beta-5_r0.5android-s-beta-4_r0.5android-s-beta-3_r0.5android-12.0.0_r0.6android-msm-redbull-4.19-s-beta-5android-msm-redbull-4.19-s-beta-4android-msm-redbull-4.19-s-beta-3
Conflicts:
msm/dsi/dsi_display.c
msm/dsi/dsi_panel.c
msm/sde/sde_connector.h
msm/sde/sde_crtc.c
msm/sde/sde_encoder.h
msm/sde/sde_kms.c
Bug: 182748782
Change-Id: I01b832cce23a6555aecc876b8cfdaf9cfeb2a012
Signed-off-by: JohnnLee <johnnlee@google.com>
32 files changed, 985 insertions, 267 deletions
diff --git a/msm/Makefile b/msm/Makefile index e6e4e9bd..3a0e02c5 100644 --- a/msm/Makefile +++ b/msm/Makefile @@ -78,8 +78,7 @@ msm_drm-$(CONFIG_DRM_MSM_SDE) += sde/sde_crtc.o \ sde/sde_hw_ds.o \ sde/sde_fence.o \ sde/sde_hw_qdss.o \ - -msm_drm-$(CONFIG_DEBUG_FS) += sde_dbg.o \ + sde_dbg.o \ sde_dbg_evtlog.o \ msm_drm-$(CONFIG_DRM_SDE_WB) += sde/sde_wb.o \ diff --git a/msm/dp/dp_debug.c b/msm/dp/dp_debug.c index 0459adce..6303c1cf 100644 --- a/msm/dp/dp_debug.c +++ b/msm/dp/dp_debug.c @@ -154,7 +154,7 @@ static ssize_t dp_debug_write_edid(struct file *file, edid = debug->edid; bail: kfree(buf); - debug->panel->set_edid(debug->panel, edid); + debug->panel->set_edid(debug->panel, edid, debug->edid_size); /* * print edid status as this code is executed @@ -1628,7 +1628,7 @@ static void dp_debug_set_sim_mode(struct dp_debug_private *debug, bool sim) debug->aux->set_sim_mode(debug->aux, false, NULL, NULL); debug->dp_debug.sim_mode = false; - debug->panel->set_edid(debug->panel, 0); + debug->panel->set_edid(debug->panel, 0, 0); if (debug->edid) { devm_kfree(debug->dev, debug->edid); debug->edid = NULL; diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index cfae5da7..2ff34baf 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -1340,6 +1340,7 @@ static void dp_display_attention_work(struct work_struct *work) { struct dp_display_private *dp = container_of(work, struct dp_display_private, attention_work); + int rc = 0; SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); @@ -1403,16 +1404,20 @@ static void dp_display_attention_work(struct work_struct *work) if (dp->link->sink_request & DP_TEST_LINK_TRAINING) { SDE_EVT32_EXTERNAL(dp->state, DP_TEST_LINK_TRAINING); dp->link->send_test_response(dp->link); - dp->ctrl->link_maintenance(dp->ctrl); + rc = dp->ctrl->link_maintenance(dp->ctrl); } if (dp->link->sink_request & DP_LINK_STATUS_UPDATED) { SDE_EVT32_EXTERNAL(dp->state, DP_LINK_STATUS_UPDATED); - dp->ctrl->link_maintenance(dp->ctrl); + rc = dp->ctrl->link_maintenance(dp->ctrl); } - dp_audio_enable(dp, true); + if (!rc) + dp_audio_enable(dp, true); + mutex_unlock(&dp->session_lock); + if (rc) + goto end; if (dp->link->sink_request & (DP_TEST_LINK_PHY_TEST_PATTERN | DP_TEST_LINK_TRAINING)) @@ -1436,6 +1441,8 @@ cp_irq: mst_attention: dp_display_mst_attention(dp); + +end: SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state); } diff --git a/msm/dp/dp_panel.c b/msm/dp/dp_panel.c index 74f915b5..1c01b0ee 100644 --- a/msm/dp/dp_panel.c +++ b/msm/dp/dp_panel.c @@ -7,6 +7,7 @@ #include <linux/unistd.h> #include <drm/drm_fixed.h> #include "dp_debug.h" +#include <drm/drm_edid.h> #define DP_KHZ_TO_HZ 1000 #define DP_PANEL_DEFAULT_BPP 24 @@ -1937,7 +1938,25 @@ static int dp_panel_set_default_link_params(struct dp_panel *dp_panel) return 0; } -static int dp_panel_set_edid(struct dp_panel *dp_panel, u8 *edid) +static bool dp_panel_validate_edid(struct edid *edid, size_t edid_size) +{ + if (!edid || (edid_size < EDID_LENGTH)) + return false; + + if (EDID_LENGTH * (edid->extensions + 1) > edid_size) { + DP_ERR("edid size does not match allocated.\n"); + return false; + } + + if (!drm_edid_is_valid(edid)) { + DP_ERR("invalid edid.\n"); + return false; + } + return true; +} + +static int dp_panel_set_edid(struct dp_panel *dp_panel, u8 *edid, + size_t edid_size) { struct dp_panel_private *panel; @@ -1948,7 +1967,7 @@ static int dp_panel_set_edid(struct dp_panel *dp_panel, u8 *edid) panel = container_of(dp_panel, struct dp_panel_private, dp_panel); - if (edid) { + if (edid && dp_panel_validate_edid((struct edid *)edid, edid_size)) { dp_panel->edid_ctrl->edid = (struct edid *)edid; panel->custom_edid = true; } else { diff --git a/msm/dp/dp_panel.h b/msm/dp/dp_panel.h index dbc5ba98..36629c3c 100644 --- a/msm/dp/dp_panel.h +++ b/msm/dp/dp_panel.h @@ -146,7 +146,7 @@ struct dp_panel { int (*get_modes)(struct dp_panel *dp_panel, struct drm_connector *connector, struct dp_display_mode *mode); void (*handle_sink_request)(struct dp_panel *dp_panel); - int (*set_edid)(struct dp_panel *dp_panel, u8 *edid); + int (*set_edid)(struct dp_panel *dp_panel, u8 *edid, size_t edid_size); int (*set_dpcd)(struct dp_panel *dp_panel, u8 *dpcd); int (*setup_hdr)(struct dp_panel *dp_panel, struct drm_msm_ext_hdr_metadata *hdr_meta, diff --git a/msm/dsi/dsi_ctrl.c b/msm/dsi/dsi_ctrl.c index 6848962a..54d5dbfd 100644 --- a/msm/dsi/dsi_ctrl.c +++ b/msm/dsi/dsi_ctrl.c @@ -263,6 +263,13 @@ static int dsi_ctrl_debugfs_deinit(struct dsi_ctrl *dsi_ctrl) static int dsi_ctrl_debugfs_init(struct dsi_ctrl *dsi_ctrl, struct dentry *parent) { + char dbg_name[DSI_DEBUG_NAME_LEN]; + + snprintf(dbg_name, DSI_DEBUG_NAME_LEN, "dsi%d_ctrl", + dsi_ctrl->cell_index); + sde_dbg_reg_register_base(dbg_name, + dsi_ctrl->hw.base, + msm_iomap_size(dsi_ctrl->pdev, "dsi_ctrl")); return 0; } static int dsi_ctrl_debugfs_deinit(struct dsi_ctrl *dsi_ctrl) @@ -2128,7 +2135,6 @@ static struct platform_driver dsi_ctrl_driver = { }, }; -#if defined(CONFIG_DEBUG_FS) void dsi_ctrl_debug_dump(u32 *entries, u32 size) { @@ -2150,7 +2156,6 @@ void dsi_ctrl_debug_dump(u32 *entries, u32 size) mutex_unlock(&dsi_ctrl_list_lock); } -#endif /** * dsi_ctrl_get() - get a dsi_ctrl handle from an of_node * @of_node: of_node of the DSI controller. diff --git a/msm/dsi/dsi_display.c b/msm/dsi/dsi_display.c index 20791031..2cf726b9 100644 --- a/msm/dsi/dsi_display.c +++ b/msm/dsi/dsi_display.c @@ -5998,7 +5998,10 @@ int dsi_display_get_info(struct drm_connector *connector, info->max_width = 1920; info->max_height = 1080; info->qsync_min_fps = - display->panel->qsync_min_fps; + display->panel->qsync_caps.qsync_min_fps; + info->has_qsync_min_fps_list = + (display->panel->qsync_caps.qsync_min_fps_list_len > 0) ? + true : false; switch (display->panel->panel_mode) { case DSI_OP_VIDEO_MODE: @@ -6460,6 +6463,25 @@ void dsi_display_set_idle_hint(void *dsi_display, bool is_idle) dsi_panel_wakeup(display->panel); } +int dsi_display_get_qsync_min_fps(void *display_dsi, u32 mode_fps) +{ + struct dsi_display *display = (struct dsi_display *)display_dsi; + struct dsi_panel *panel; + u32 i; + + if (display == NULL || display->panel == NULL) + return -EINVAL; + + panel = display->panel; + for (i = 0; i < panel->dfps_caps.dfps_list_len; i++) { + if (panel->dfps_caps.dfps_list[i] == mode_fps) + return panel->qsync_caps.qsync_min_fps_list[i]; + } + SDE_EVT32(mode_fps); + DSI_DEBUG("Invalid mode_fps %d\n", mode_fps); + return -EINVAL; +} + int dsi_display_find_mode(struct dsi_display *display, const struct dsi_display_mode *cmp, struct dsi_display_mode **out_mode) @@ -7304,7 +7326,7 @@ static int dsi_display_qsync(struct dsi_display *display, bool enable) int i; int rc = 0; - if (!display->panel->qsync_min_fps) { + if (!display->panel->qsync_caps.qsync_min_fps) { DSI_ERR("%s:ERROR: qsync set, but no fps\n", __func__); return 0; } @@ -7332,7 +7354,7 @@ static int dsi_display_qsync(struct dsi_display *display, bool enable) } exit: - SDE_EVT32(enable, display->panel->qsync_min_fps, rc); + SDE_EVT32(enable, display->panel->qsync_caps.qsync_min_fps, rc); mutex_unlock(&display->display_lock); return rc; } diff --git a/msm/dsi/dsi_display.h b/msm/dsi/dsi_display.h index 742f55cb..f7d64489 100644 --- a/msm/dsi/dsi_display.h +++ b/msm/dsi/dsi_display.h @@ -438,6 +438,16 @@ void dsi_display_put_mode(struct dsi_display *display, int dsi_display_get_default_lms(void *dsi_display, u32 *num_lm); /** + * dsi_display_get_qsync_min_fps() - get qsync min fps for given fps + * @display: Handle to display. + * @mode_fps: Fps value of current mode + * + * Return: error code. + */ +int dsi_display_get_qsync_min_fps(void *dsi_display, u32 mode_fps); + + +/** * dsi_display_find_mode() - retrieve cached DSI mode given relevant params * @display: Handle to display. * @cmp: Mode to use as comparison to find original diff --git a/msm/dsi/dsi_drm.c b/msm/dsi/dsi_drm.c index 68d01b03..9ea51375 100644 --- a/msm/dsi/dsi_drm.c +++ b/msm/dsi/dsi_drm.c @@ -594,14 +594,16 @@ int dsi_conn_set_info_blob(struct drm_connector *connector, case DSI_OP_VIDEO_MODE: sde_kms_info_add_keystr(info, "panel mode", "video"); sde_kms_info_add_keystr(info, "qsync support", - panel->qsync_min_fps ? "true" : "false"); + panel->qsync_caps.qsync_min_fps ? + "true" : "false"); break; case DSI_OP_CMD_MODE: sde_kms_info_add_keystr(info, "panel mode", "command"); sde_kms_info_add_keyint(info, "mdp_transfer_time_us", mode_info->mdp_transfer_time_us); sde_kms_info_add_keystr(info, "qsync support", - panel->qsync_min_fps ? "true" : "false"); + panel->qsync_caps.qsync_min_fps ? + "true" : "false"); break; default: DSI_DEBUG("invalid panel type:%d\n", panel->panel_mode); diff --git a/msm/dsi/dsi_panel.c b/msm/dsi/dsi_panel.c index f5cd6e70..2d9a35b9 100644 --- a/msm/dsi/dsi_panel.c +++ b/msm/dsi/dsi_panel.c @@ -1435,8 +1435,15 @@ static int dsi_panel_parse_qsync_caps(struct dsi_panel *panel, struct device_node *of_node) { int rc = 0; - u32 val = 0; + u32 val = 0, i; + struct dsi_qsync_capabilities *qsync_caps = &panel->qsync_caps; + struct dsi_parser_utils *utils = &panel->utils; + const char *name = panel->name; + /** + * "mdss-dsi-qsync-min-refresh-rate" is defined in cmd mode and + * video mode when there is only one qsync min fps present. + */ rc = of_property_read_u32(of_node, "qcom,mdss-dsi-qsync-min-refresh-rate", &val); @@ -1444,8 +1451,75 @@ static int dsi_panel_parse_qsync_caps(struct dsi_panel *panel, DSI_DEBUG("[%s] qsync min fps not defined rc:%d\n", panel->name, rc); - panel->qsync_min_fps = val; + qsync_caps->qsync_min_fps = val; + + /** + * "dsi-supported-qsync-min-fps-list" may be defined in video + * mode, only in dfps case when "qcom,dsi-supported-dfps-list" + * is defined. + */ + qsync_caps->qsync_min_fps_list_len = utils->count_u32_elems(utils->data, + "qcom,dsi-supported-qsync-min-fps-list"); + if (qsync_caps->qsync_min_fps_list_len < 1) + goto qsync_support; + + /** + * qcom,dsi-supported-qsync-min-fps-list cannot be defined + * along with qcom,mdss-dsi-qsync-min-refresh-rate. + */ + if (qsync_caps->qsync_min_fps_list_len >= 1 && + qsync_caps->qsync_min_fps) { + DSI_ERR("[%s] Both qsync nodes are defined\n", + name); + rc = -EINVAL; + goto error; + } + + if (panel->dfps_caps.dfps_list_len != + qsync_caps->qsync_min_fps_list_len) { + DSI_ERR("[%s] Qsync min fps list mismatch with dfps\n", name); + rc = -EINVAL; + goto error; + } + + qsync_caps->qsync_min_fps_list = + kcalloc(qsync_caps->qsync_min_fps_list_len, sizeof(u32), + GFP_KERNEL); + if (!qsync_caps->qsync_min_fps_list) { + rc = -ENOMEM; + goto error; + } + rc = utils->read_u32_array(utils->data, + "qcom,dsi-supported-qsync-min-fps-list", + qsync_caps->qsync_min_fps_list, + qsync_caps->qsync_min_fps_list_len); + if (rc) { + DSI_ERR("[%s] Qsync min fps list parse failed\n", name); + rc = -EINVAL; + goto error; + } + + qsync_caps->qsync_min_fps = qsync_caps->qsync_min_fps_list[0]; + + for (i = 1; i < qsync_caps->qsync_min_fps_list_len; i++) { + if (qsync_caps->qsync_min_fps_list[i] < + qsync_caps->qsync_min_fps) + qsync_caps->qsync_min_fps = + qsync_caps->qsync_min_fps_list[i]; + } + +qsync_support: + /* 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_caps.qsync_min_fps = 0; + +error: + if (rc < 0) { + qsync_caps->qsync_min_fps = 0; + qsync_caps->qsync_min_fps_list_len = 0; + } return rc; } @@ -3435,11 +3509,6 @@ struct dsi_panel *dsi_panel_get(struct device *parent, 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) DSI_ERR("failed to parse dynamic clk config, rc=%d\n", rc); diff --git a/msm/dsi/dsi_panel.h b/msm/dsi/dsi_panel.h index 5f901a6e..29c61aff 100644 --- a/msm/dsi/dsi_panel.h +++ b/msm/dsi/dsi_panel.h @@ -96,6 +96,13 @@ struct dsi_dfps_capabilities { bool dfps_support; }; +struct dsi_qsync_capabilities { + /* qsync disabled if qsync_min_fps = 0 */ + u32 qsync_min_fps; + u32 *qsync_min_fps_list; + int qsync_min_fps_list_len; +}; + struct dsi_dyn_clk_caps { bool dyn_clk_support; u32 *bit_clk_list; @@ -357,7 +364,7 @@ struct dsi_panel { bool panel_initialized; bool te_using_watchdog_timer; - u32 qsync_min_fps; + struct dsi_qsync_capabilities qsync_caps; char dsc_pps_cmd[DSI_CMD_PPS_SIZE]; enum dsi_dms_mode dms_mode; diff --git a/msm/msm_drv.c b/msm/msm_drv.c index 1e959be1..0ab8b1f0 100644 --- a/msm/msm_drv.c +++ b/msm/msm_drv.c @@ -1657,6 +1657,13 @@ static int msm_release(struct inode *inode, struct file *filp) kfree(node); } + /** + * Handle preclose operation here for removing fb's whose + * refcount > 1. This operation is not triggered from upstream + * drm as msm_driver does not support DRIVER_LEGACY feature. + */ + msm_preclose(dev, file_priv); + return drm_release(inode, filp); } @@ -1817,7 +1824,6 @@ static struct drm_driver msm_driver = { DRIVER_ATOMIC | DRIVER_MODESET, .open = msm_open, - .preclose = msm_preclose, .postclose = msm_postclose, .lastclose = msm_lastclose, .irq_handler = msm_irq, diff --git a/msm/msm_drv.h b/msm/msm_drv.h index 525473ce..34263b7a 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark <robdclark@gmail.com> * @@ -512,6 +512,7 @@ struct msm_resource_caps_info { * used instead of panel TE in cmd mode panels * @roi_caps: Region of interest capability info * @qsync_min_fps Minimum fps supported by Qsync feature + * @has_qsync_min_fps_list True if dsi-supported-qsync-min-fps-list exits * @te_source vsync source pin information */ struct msm_display_info { @@ -535,6 +536,8 @@ struct msm_display_info { struct msm_roi_caps roi_caps; uint32_t qsync_min_fps; + bool has_qsync_min_fps_list; + uint32_t te_source; }; @@ -891,7 +894,8 @@ struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev, struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd); struct drm_framebuffer * msm_alloc_stolen_fb(struct drm_device *dev, int w, int h, int p, uint32_t format); - +int msm_fb_obj_get_attrs(struct drm_gem_object *obj, int *fb_ns, + int *fb_sec, int *fb_sec_dir, unsigned long *flags); struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev); void msm_fbdev_free(struct drm_device *dev); diff --git a/msm/msm_fb.c b/msm/msm_fb.c index f5ddd139..6b7b34ad 100644 --- a/msm/msm_fb.c +++ b/msm/msm_fb.c @@ -18,6 +18,7 @@ #include <linux/dma-mapping.h> #include <linux/dma-buf.h> +#include <linux/msm_ion.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_gem_framebuffer_helper.h> @@ -527,3 +528,33 @@ msm_alloc_stolen_fb(struct drm_device *dev, int w, int h, int p, uint32_t format return fb; } + +int msm_fb_obj_get_attrs(struct drm_gem_object *obj, int *fb_ns, + int *fb_sec, int *fb_sec_dir, unsigned long *flags) +{ + + struct msm_gem_object *msm_obj = to_msm_bo(obj); + int ret = 0; + + if (!obj->import_attach) { + DRM_DEBUG("NULL attachment in gem object flags: 0x%x\n", + msm_obj->flags); + return -EINVAL; + } + + ret = dma_buf_get_flags(obj->import_attach->dmabuf, flags); + if (ret) { + DRM_ERROR("dma_buf_get_flags failure, err=%d\n", ret); + return ret; + } + + if (!(*flags & ION_FLAG_SECURE)) + *fb_ns = 1; + else if (*flags & ION_FLAG_CP_PIXEL) + *fb_sec = 1; + else if (*flags & (ION_FLAG_CP_SEC_DISPLAY | + ION_FLAG_CP_CAMERA_PREVIEW)) + *fb_sec_dir = 1; + + return ret; +} diff --git a/msm/sde/sde_connector.c b/msm/sde/sde_connector.c index e7fb277d..efb72e47 100644 --- a/msm/sde/sde_connector.c +++ b/msm/sde/sde_connector.c @@ -1998,8 +1998,6 @@ 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"); @@ -2012,19 +2010,6 @@ static int sde_connector_atomic_check(struct drm_connector *connector, } 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_connector.h b/msm/sde/sde_connector.h index 9d173377..dba34391 100644 --- a/msm/sde/sde_connector.h +++ b/msm/sde/sde_connector.h @@ -336,6 +336,14 @@ struct sde_connector_ops { * @is_idle: true if display is idle, false otherwise */ void (*set_idle_hint)(void *display, bool is_idle); + + /** + * get_qsync_min_fps - Get qsync min fps from qsync-min-fps-list + * @display: Pointer to private display structure + * @mode_fps: Fps value in dfps list + * Returns: Qsync min fps value on success + */ + int (*get_qsync_min_fps)(void *display, u32 mode_fps); }; /** diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index f16fe8c2..cb0dd377 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -420,16 +420,36 @@ static ssize_t vsync_event_show(struct device *device, ktime_to_ns(sde_crtc->vblank_last_cb_time)); } +static ssize_t retire_frame_event_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct drm_crtc *crtc; + struct sde_crtc *sde_crtc; + + if (!device || !buf) { + SDE_ERROR("invalid input param(s)\n"); + return -EAGAIN; + } + + crtc = dev_get_drvdata(device); + sde_crtc = to_sde_crtc(crtc); + SDE_EVT32(DRMID(&sde_crtc->base)); + return scnprintf(buf, PAGE_SIZE, "RETIRE_FRAME_TIME=%llu\n", + ktime_to_ns(sde_crtc->retire_frame_event_time)); +} + static DEVICE_ATTR_RO(vsync_event); static DEVICE_ATTR_RO(measured_fps); static DEVICE_ATTR_RW(fps_periodicity_ms); static DEVICE_ATTR_WO(early_wakeup); +static DEVICE_ATTR_RO(retire_frame_event); static struct attribute *sde_crtc_dev_attrs[] = { &dev_attr_vsync_event.attr, &dev_attr_measured_fps.attr, &dev_attr_fps_periodicity_ms.attr, &dev_attr_early_wakeup.attr, + &dev_attr_retire_frame_event.attr, NULL }; @@ -453,6 +473,8 @@ static void sde_crtc_destroy(struct drm_crtc *crtc) if (sde_crtc->vsync_event_sf) sysfs_put(sde_crtc->vsync_event_sf); + if (sde_crtc->retire_frame_event_sf) + sysfs_put(sde_crtc->retire_frame_event_sf); if (sde_crtc->sysfs_dev) device_unregister(sde_crtc->sysfs_dev); @@ -1747,8 +1769,12 @@ int sde_crtc_state_find_plane_fb_modes(struct drm_crtc_state *state, static void _sde_drm_fb_sec_dir_trans( struct sde_kms_smmu_state_data *smmu_state, uint32_t secure_level, - struct sde_mdss_cfg *catalog, bool old_valid_fb, int *ops) + struct sde_mdss_cfg *catalog, bool old_valid_fb, int *ops, + struct drm_crtc_state *old_crtc_state) { + struct sde_crtc_state *old_cstate = to_sde_crtc_state(old_crtc_state); + int old_secure_session = old_cstate->secure_session; + /* secure display usecase */ if ((smmu_state->state == ATTACHED) && (secure_level == SDE_DRM_SEC_ONLY)) { @@ -1769,6 +1795,10 @@ static void _sde_drm_fb_sec_dir_trans( smmu_state->secure_level = secure_level; smmu_state->transition_type = PRE_COMMIT; *ops |= SDE_KMS_OPS_SECURE_STATE_CHANGE; + if (old_secure_session == + SDE_SECURE_VIDEO_SESSION) + *ops |= (SDE_KMS_OPS_WAIT_FOR_TX_DONE | + SDE_KMS_OPS_CLEANUP_PLANE_FB); } } @@ -1894,7 +1924,7 @@ int sde_crtc_get_secure_transition_ops(struct drm_crtc *crtc, switch (translation_mode) { case SDE_DRM_FB_SEC_DIR_TRANS: _sde_drm_fb_sec_dir_trans(smmu_state, secure_level, - catalog, old_valid_fb, &ops); + catalog, old_valid_fb, &ops, old_crtc_state); if (clone_mode && (ops & SDE_KMS_OPS_SECURE_STATE_CHANGE)) ops |= SDE_KMS_OPS_WAIT_FOR_TX_DONE; break; @@ -2203,6 +2233,12 @@ static void sde_crtc_frame_event_cb(void *data, u32 event) } } + if ((event & SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE) && + (sde_crtc && sde_crtc->retire_frame_event_sf)) { + sde_crtc->retire_frame_event_time = ktime_get(); + sysfs_notify_dirent(sde_crtc->retire_frame_event_sf); + } + fevent->event = event; fevent->crtc = crtc; fevent->connector = cb_data->connector; @@ -2488,17 +2524,16 @@ static void _sde_crtc_set_input_fence_timeout(struct sde_crtc_state *cstate) cstate->input_fence_timeout_ns *= NSEC_PER_MSEC; } -/** - * _sde_crtc_clear_dim_layers_v1 - clear all dim layer settings - * @cstate: Pointer to sde crtc state - */ -static void _sde_crtc_clear_dim_layers_v1(struct sde_crtc_state *cstate) +void _sde_crtc_clear_dim_layers_v1(struct drm_crtc_state *state) { u32 i; + struct sde_crtc_state *cstate; - if (!cstate) + if (!state) return; + cstate = to_sde_crtc_state(state); + for (i = 0; i < cstate->num_dim_layers; i++) memset(&cstate->dim_layer[i], 0, sizeof(cstate->dim_layer[i])); @@ -2527,7 +2562,7 @@ static void _sde_crtc_set_dim_layer_v1(struct drm_crtc *crtc, if (!usr_ptr) { /* usr_ptr is null when setting the default property value */ - _sde_crtc_clear_dim_layers_v1(cstate); + _sde_crtc_clear_dim_layers_v1(&cstate->base); SDE_DEBUG("dim_layer data removed\n"); return; } @@ -4363,6 +4398,55 @@ static int _sde_crtc_check_secure_single_encoder(struct drm_crtc *crtc, return 0; } +static int _sde_crtc_check_secure_transition(struct drm_crtc *crtc, + struct drm_crtc_state *state, bool is_video_mode) +{ + struct sde_crtc_state *old_cstate = to_sde_crtc_state(crtc->state); + struct sde_crtc_state *new_cstate = to_sde_crtc_state(state); + int old_secure_session = old_cstate->secure_session; + int new_secure_session = new_cstate->secure_session; + int ret = 0; + + /* + * Direct transition from Secure Camera to Secure UI(&viceversa) + * is not allowed + */ + if ((old_secure_session == SDE_SECURE_CAMERA_SESSION && + new_secure_session == SDE_SECURE_UI_SESSION) || + (old_secure_session == SDE_SECURE_UI_SESSION && + new_secure_session == SDE_SECURE_CAMERA_SESSION)) { + SDE_EVT32(DRMID(crtc), old_secure_session, + new_secure_session, SDE_EVTLOG_ERROR); + ret = -EINVAL; + } + + /* + * In video mode, null commit is required for transition between + * secure video & secure camera + */ + if (is_video_mode && + ((old_secure_session == SDE_SECURE_CAMERA_SESSION && + new_secure_session == SDE_SECURE_VIDEO_SESSION) || + (old_secure_session == SDE_SECURE_VIDEO_SESSION && + new_secure_session == SDE_SECURE_CAMERA_SESSION))) { + SDE_EVT32(DRMID(crtc), old_secure_session, + new_secure_session, SDE_EVTLOG_ERROR); + ret = -EINVAL; + } + + if (old_secure_session != new_secure_session) + SDE_EVT32(DRMID(crtc), old_secure_session, + new_secure_session); + + SDE_DEBUG("old session: %d new session : %d\n", + old_secure_session, new_secure_session); + if (ret) + SDE_ERROR("invalid transition old:%d new:%d\n", + old_secure_session, new_secure_session); + + return ret; +} + static int _sde_crtc_check_secure_state_smmu_translation(struct drm_crtc *crtc, struct drm_crtc_state *state, struct sde_kms *sde_kms, int secure, int fb_ns, int fb_sec, int fb_sec_dir) @@ -4377,19 +4461,8 @@ static int _sde_crtc_check_secure_state_smmu_translation(struct drm_crtc *crtc, MSM_DISPLAY_VIDEO_MODE); } - /* - * Secure display to secure camera needs without direct - * transition is currently not allowed - */ - if (fb_sec_dir && secure == SDE_DRM_SEC_NON_SEC && - smmu_state->state != ATTACHED && - smmu_state->secure_level == SDE_DRM_SEC_ONLY) { - - SDE_EVT32(DRMID(crtc), fb_ns, fb_sec_dir, - smmu_state->state, smmu_state->secure_level, - secure); + if (_sde_crtc_check_secure_transition(crtc, state, is_video_mode)) goto sec_err; - } /* * In video mode check for null commit before transition @@ -4455,6 +4528,33 @@ static int _sde_crtc_check_secure_conn(struct drm_crtc *crtc, return 0; } +static int _sde_crtc_populate_secure_session(struct drm_crtc_state *state, + int secure, int fb_ns, int fb_sec, int fb_sec_dir) +{ + struct sde_crtc_state *cstate = to_sde_crtc_state(state); + + if (secure == SDE_DRM_SEC_ONLY && fb_sec_dir && !fb_sec && !fb_ns) + cstate->secure_session = SDE_SECURE_UI_SESSION; + else if (secure == SDE_DRM_SEC_NON_SEC && fb_sec_dir && !fb_sec) + cstate->secure_session = SDE_SECURE_CAMERA_SESSION; + else if (secure == SDE_DRM_SEC_NON_SEC && !fb_sec_dir && fb_sec) + cstate->secure_session = SDE_SECURE_VIDEO_SESSION; + else if (secure == SDE_DRM_SEC_NON_SEC && !fb_sec_dir && + !fb_sec && fb_ns) + cstate->secure_session = SDE_NON_SECURE_SESSION; + else if (!fb_sec_dir && !fb_sec && !fb_ns) + cstate->secure_session = SDE_NULL_SESSION; + else { + SDE_ERROR( + "invalid session sec:%d fb_sec_dir:%d fb_sec:%d fb_ns:%d\n", + cstate->secure_session, fb_sec_dir, + fb_sec, fb_ns); + return -EINVAL; + } + + return 0; +} + static int _sde_crtc_check_secure_state(struct drm_crtc *crtc, struct drm_crtc_state *state, struct plane_state pstates[], int cnt) @@ -4485,6 +4585,11 @@ static int _sde_crtc_check_secure_state(struct drm_crtc *crtc, if (rc) return rc; + rc = _sde_crtc_populate_secure_session(state, secure, + fb_ns, fb_sec, fb_sec_dir); + if (rc) + return rc; + rc = _sde_crtc_check_secure_blend_config(crtc, state, pstates, cstate, sde_kms, cnt, secure, fb_ns, fb_sec, fb_sec_dir); if (rc) @@ -6339,6 +6444,12 @@ int sde_crtc_post_init(struct drm_device *dev, struct drm_crtc *crtc) SDE_ERROR("crtc:%d vsync_event sysfs create failed\n", crtc->base.id); + sde_crtc->retire_frame_event_sf = sysfs_get_dirent( + sde_crtc->sysfs_dev->kobj.sd, "retire_frame_event"); + if (!sde_crtc->retire_frame_event_sf) + SDE_ERROR("crtc:%d retire frame event sysfs create failed\n", + crtc->base.id); + end: return rc; } diff --git a/msm/sde/sde_crtc.h b/msm/sde/sde_crtc.h index 9b18dea1..13e0ac8a 100644 --- a/msm/sde/sde_crtc.h +++ b/msm/sde/sde_crtc.h @@ -36,6 +36,22 @@ #define SDE_CRTC_FRAME_EVENT_SIZE (4 * 2) /** + * enum sde_session_type: session type + * @SDE_SECURE_UI_SESSION: secure UI usecase + * @SDE_SECURE_CAMERA_SESSION: secure camera usecase + * @SDE_SECURE_VIDEO_SESSION: secure video usecase + * @SDE_NON_SECURE_SESSION: non secure usecase + * @SDE_NULL_SESSION: null commit usecase + */ +enum sde_session_type { + SDE_SECURE_UI_SESSION, + SDE_SECURE_CAMERA_SESSION, + SDE_SECURE_VIDEO_SESSION, + SDE_NON_SECURE_SESSION, + SDE_NULL_SESSION, +}; + +/** * enum sde_crtc_client_type: crtc client type * @RT_CLIENT: RealTime client like video/cmd mode display * voting through apps rsc @@ -221,11 +237,13 @@ struct sde_crtc_misr_info { * @debugfs_root : Parent of debugfs node * @priv_handle : Pointer to external private handle, if present * @vblank_cb_count : count of vblank callback since last reset + * @retire_frame_event_time : ktime at last retire frame event * @play_count : frame count between crtc enable and disable * @vblank_cb_time : ktime at vblank count reset * @vblank_last_cb_time : ktime at last vblank notification * @sysfs_dev : sysfs device node for crtc * @vsync_event_sf : vsync event notifier sysfs device + * @retire_frame_event_sf :retire frame event notifier sysfs device * @enabled : whether the SDE CRTC is currently enabled. updated in the * commit-thread, not state-swap time which is earlier, so * safe to make decisions on during VBLANK on/off work @@ -296,10 +314,12 @@ struct sde_crtc { u32 vblank_cb_count; u64 play_count; ktime_t vblank_cb_time; + ktime_t retire_frame_event_time; ktime_t vblank_last_cb_time; struct sde_crtc_fps_info fps_info; struct device *sysfs_dev; struct kernfs_node *vsync_event_sf; + struct kernfs_node *retire_frame_event_sf; bool enabled; bool ds_reconfig; @@ -382,6 +402,7 @@ struct sde_crtc { * @ds_cfg: Destination scaler config * @scl3_lut_cfg: QSEED3 lut config * @new_perf: new performance state being requested + * @secure_session: Indicates the type of secure session */ struct sde_crtc_state { struct drm_crtc_state base; @@ -411,6 +432,7 @@ struct sde_crtc_state { struct sde_hw_scaler3_lut_cfg scl3_lut_cfg; struct sde_core_perf_params new_perf; + int secure_session; }; enum sde_crtc_irq_state { @@ -841,6 +863,12 @@ void sde_crtc_get_misr_info(struct drm_crtc *crtc, int sde_crtc_get_num_datapath(struct drm_crtc *crtc, struct drm_connector *connector); +/** + * _sde_crtc_clear_dim_layers_v1 - clear all dim layer settings + * @cstate: Pointer to drm crtc state + */ +void _sde_crtc_clear_dim_layers_v1(struct drm_crtc_state *state); + /* * sde_crtc_set_compression_ratio - set compression ratio src_bpp/target_bpp * @msm_mode_info: Mode info diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index 13de5914..3ad77c7e 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark <robdclark@gmail.com> * @@ -931,6 +931,29 @@ bool sde_encoder_in_clone_mode(struct drm_encoder *drm_enc) return false; } +bool sde_encoder_is_cwb_disabling(struct drm_encoder *drm_enc, + struct drm_crtc *crtc) +{ + struct sde_encoder_virt *sde_enc; + int i; + + if (!drm_enc) + return false; + + sde_enc = to_sde_encoder_virt(drm_enc); + if (sde_enc->disp_info.intf_type != DRM_MODE_CONNECTOR_VIRTUAL) + return false; + + for (i = 0; i < sde_enc->num_phys_encs; i++) { + struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; + + if (sde_encoder_phys_is_cwb_disabling(phys, crtc)) + return true; + } + + return false; +} + static int _sde_encoder_atomic_check_phys_enc(struct sde_encoder_virt *sde_enc, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) @@ -1095,6 +1118,7 @@ static int sde_encoder_virt_atomic_check( struct sde_crtc_state *sde_crtc_state = NULL; enum sde_rm_topology_name old_top; int ret = 0; + bool qsync_dirty = false, has_modeset = false; if (!drm_enc || !crtc_state || !conn_state) { SDE_ERROR("invalid arg(s), drm_enc %d, crtc/conn state %d/%d\n", @@ -1161,6 +1185,22 @@ static int sde_encoder_virt_atomic_check( } drm_mode_set_crtcinfo(adj_mode, 0); + + has_modeset = sde_crtc_atomic_check_has_modeset(conn_state->state, + conn_state->crtc); + qsync_dirty = msm_property_is_dirty(&sde_conn->property_info, + &sde_conn_state->property_state, + CONNECTOR_PROP_QSYNC_MODE); + + if (has_modeset && qsync_dirty && + (msm_is_mode_seamless_poms(adj_mode) || + msm_is_mode_seamless_dms(adj_mode) || + msm_is_mode_seamless_dyn_clk(adj_mode))) { + SDE_ERROR("invalid qsync update during modeset priv flag:%x\n", + adj_mode->private_flags); + return -EINVAL; + } + SDE_EVT32(DRMID(drm_enc), adj_mode->flags, adj_mode->private_flags); return ret; @@ -1352,6 +1392,7 @@ static int _sde_encoder_dsc_n_lm_1_enc_1_intf(struct sde_encoder_virt *sde_enc) struct msm_display_dsc_info *dsc = NULL; struct sde_hw_ctl *hw_ctl; struct sde_ctl_dsc_cfg cfg; + bool half_panel_partial_update; if (hw_dsc == NULL || hw_pp == NULL || !enc_master) { SDE_ERROR_ENC(sde_enc, "invalid params for DSC\n"); @@ -1370,15 +1411,19 @@ static int _sde_encoder_dsc_n_lm_1_enc_1_intf(struct sde_encoder_virt *sde_enc) enc_ip_w = intf_ip_w; _sde_encoder_dsc_initial_line_calc(dsc, enc_ip_w); + half_panel_partial_update = (sde_enc->cur_conn_roi.w <= + sde_enc->cur_master->cached_mode.hdisplay / 2); - ich_res = _sde_encoder_dsc_ich_reset_override_needed(false, dsc); + ich_res = _sde_encoder_dsc_ich_reset_override_needed( + half_panel_partial_update, dsc); if (enc_master->intf_mode == INTF_MODE_VIDEO) dsc_common_mode = DSC_MODE_VIDEO; - SDE_DEBUG_ENC(sde_enc, "pic_w: %d pic_h: %d mode:%d\n", - roi->w, roi->h, dsc_common_mode); - SDE_EVT32(DRMID(&sde_enc->base), roi->w, roi->h, dsc_common_mode); + SDE_DEBUG_ENC(sde_enc, "pic_w: %d pic_h: %d mode:%d ich_res:%d\n", + roi->w, roi->h, dsc_common_mode, ich_res); + SDE_EVT32(DRMID(&sde_enc->base), roi->w, roi->h, + dsc_common_mode, ich_res, half_panel_partial_update); _sde_encoder_dsc_pipe_cfg(hw_dsc, hw_pp, dsc, dsc_common_mode, ich_res, true, hw_dsc_pp, false); @@ -2020,8 +2065,13 @@ static int _sde_encoder_update_rsc_client( qsync_mode = sde_connector_get_qsync_mode( sde_enc->cur_master->connector); - if (sde_encoder_in_clone_mode(drm_enc) || - (disp_info->display_type != SDE_CONNECTOR_PRIMARY) || + /* left primary encoder keep vote */ + if (sde_encoder_in_clone_mode(drm_enc)) { + SDE_EVT32(rsc_state, SDE_EVTLOG_FUNC_CASE1); + return 0; + } + + if ((disp_info->display_type != SDE_CONNECTOR_PRIMARY) || (disp_info->display_type && qsync_mode)) rsc_state = enable ? SDE_RSC_CLK_STATE : SDE_RSC_IDLE_STATE; else if (sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_CMD_MODE)) @@ -3512,6 +3562,33 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc) _sde_encoder_virt_enable_helper(drm_enc); } +void sde_encoder_virt_reset(struct drm_encoder *drm_enc) +{ + struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc); + struct sde_kms *sde_kms = sde_encoder_get_kms(drm_enc); + int i = 0; + + for (i = 0; i < sde_enc->num_phys_encs; i++) { + if (sde_enc->phys_encs[i]) { + sde_enc->phys_encs[i]->cont_splash_enabled = false; + sde_enc->phys_encs[i]->connector = NULL; + } + atomic_set(&sde_enc->frame_done_cnt[i], 0); + } + + sde_enc->cur_master = NULL; + /* + * clear the cached crtc in sde_enc on use case finish, after all the + * outstanding events and timers have been completed + */ + sde_enc->crtc = NULL; + memset(&sde_enc->mode_info, 0, sizeof(sde_enc->mode_info)); + + SDE_DEBUG_ENC(sde_enc, "encoder disabled\n"); + + sde_rm_release(&sde_kms->rm, drm_enc, false); +} + static void sde_encoder_virt_disable(struct drm_encoder *drm_enc) { struct sde_encoder_virt *sde_enc = NULL; @@ -3546,7 +3623,8 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc) SDE_EVT32(DRMID(drm_enc)); /* wait for idle */ - sde_encoder_wait_for_event(drm_enc, MSM_ENC_TX_COMPLETE); + if (!sde_encoder_in_clone_mode(drm_enc)) + sde_encoder_wait_for_event(drm_enc, MSM_ENC_TX_COMPLETE); _sde_encoder_input_handler_unregister(drm_enc); @@ -3589,25 +3667,8 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc) sde_encoder_resource_control(drm_enc, SDE_ENC_RC_EVENT_STOP); - for (i = 0; i < sde_enc->num_phys_encs; i++) { - if (sde_enc->phys_encs[i]) { - sde_enc->phys_encs[i]->cont_splash_enabled = false; - sde_enc->phys_encs[i]->connector = NULL; - } - atomic_set(&sde_enc->frame_done_cnt[i], 0); - } - - sde_enc->cur_master = NULL; - /* - * clear the cached crtc in sde_enc on use case finish, after all the - * outstanding events and timers have been completed - */ - sde_enc->crtc = NULL; - memset(&sde_enc->mode_info, 0, sizeof(sde_enc->mode_info)); - - SDE_DEBUG_ENC(sde_enc, "encoder disabled\n"); - - sde_rm_release(&sde_kms->rm, drm_enc, false); + if (!sde_encoder_in_clone_mode(drm_enc)) + sde_encoder_virt_reset(drm_enc); } void sde_encoder_helper_phys_disable(struct sde_encoder_phys *phys_enc, @@ -3923,10 +3984,12 @@ static void sde_encoder_frame_done_callback( static void sde_encoder_get_qsync_fps_callback( struct drm_encoder *drm_enc, - u32 *qsync_fps) + u32 *qsync_fps, u32 vrr_fps) { struct msm_display_info *disp_info; struct sde_encoder_virt *sde_enc; + int rc = 0; + struct sde_connector *sde_conn; if (!qsync_fps) return; @@ -3940,6 +4003,31 @@ static void sde_encoder_get_qsync_fps_callback( sde_enc = to_sde_encoder_virt(drm_enc); disp_info = &sde_enc->disp_info; *qsync_fps = disp_info->qsync_min_fps; + + /** + * If "dsi-supported-qsync-min-fps-list" is defined, get + * the qsync min fps corresponding to the fps in dfps list + */ + if (disp_info->has_qsync_min_fps_list) { + + if (!sde_enc->cur_master || + !(sde_enc->disp_info.capabilities & + MSM_DISPLAY_CAP_VID_MODE)) { + SDE_ERROR("invalid qsync settings %b\n", + !sde_enc->cur_master); + return; + } + sde_conn = to_sde_connector(sde_enc->cur_master->connector); + + if (sde_conn->ops.get_qsync_min_fps) + rc = sde_conn->ops.get_qsync_min_fps(sde_conn->display, + vrr_fps); + if (rc <= 0) { + SDE_ERROR("invalid qsync min fps %d\n", rc); + return; + } + *qsync_fps = rc; + } } int sde_encoder_idle_request(struct drm_encoder *drm_enc) @@ -4851,6 +4939,10 @@ int sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc, sde_connector_is_qsync_updated( sde_enc->cur_master->connector)) { _helper_flush_qsync(phys); + + if (is_cmd_mode) + _sde_encoder_update_rsc_client(drm_enc, + true); } } } diff --git a/msm/sde/sde_encoder.h b/msm/sde/sde_encoder.h index 32c25d01..aef88918 100644 --- a/msm/sde/sde_encoder.h +++ b/msm/sde/sde_encoder.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark <robdclark@gmail.com> * @@ -313,6 +313,15 @@ void sde_encoder_recovery_events_handler(struct drm_encoder *encoder, */ bool sde_encoder_in_clone_mode(struct drm_encoder *enc); +/* + * sde_encoder_is_cwb_disabling - check if cwb encoder disable is pending + * @drm_enc: Pointer to drm encoder structure + * @drm_crtc: Pointer to drm crtc structure + * @Return: true if cwb encoder disable is pending + */ +bool sde_encoder_is_cwb_disabling(struct drm_encoder *drm_enc, + struct drm_crtc *drm_crtc); + /** * sde_encoder_is_primary_display - checks if underlying display is primary * display or not. @@ -362,4 +371,31 @@ void sde_encoder_uidle_enable(struct drm_encoder *drm_enc, bool enable); */ void sde_encoder_trigger_early_wakeup(struct drm_encoder *drm_enc); +/** + * sde_encoder_virt_reset - delay encoder virt reset + * @drm_enc: Pointer to drm encoder structure + */ +void sde_encoder_virt_reset(struct drm_encoder *drm_enc); + +/** + * sde_encoder_get_kms - retrieve the kms from encoder + * @drm_enc: Pointer to drm encoder structure + */ +static inline struct sde_kms *sde_encoder_get_kms(struct drm_encoder *drm_enc) +{ + struct msm_drm_private *priv; + + if (!drm_enc || !drm_enc->dev) { + SDE_ERROR("invalid encoder\n"); + return NULL; + } + priv = drm_enc->dev->dev_private; + if (!priv || !priv->kms) { + SDE_ERROR("invalid kms\n"); + return NULL; + } + + return to_sde_kms(priv->kms); +} + #endif /* __SDE_ENCODER_H__ */ diff --git a/msm/sde/sde_encoder_phys.h b/msm/sde/sde_encoder_phys.h index 8f35a8fa..22355dc9 100644 --- a/msm/sde/sde_encoder_phys.h +++ b/msm/sde/sde_encoder_phys.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. */ #ifndef __SDE_ENCODER_PHYS_H__ @@ -82,7 +82,7 @@ struct sde_encoder_virt_ops { void (*handle_frame_done)(struct drm_encoder *parent, struct sde_encoder_phys *phys, u32 event); void (*get_qsync_fps)(struct drm_encoder *parent, - u32 *qsync_fps); + u32 *qsync_fps, u32 vrr_fps); }; /** @@ -611,6 +611,26 @@ static inline enum sde_3d_blend_mode sde_encoder_helper_get_3d_blend_mode( } /** + * sde_encoder_phys_is_cwb_disabling - Check if CWB encoder attached to this + * CRTC and it is in SDE_ENC_DISABLING state. + * @phys_enc: Pointer to physical encoder structure + * @crtc: drm crtc + * @Return: true if cwb encoder is in disabling state + */ +static inline bool sde_encoder_phys_is_cwb_disabling( + struct sde_encoder_phys *phys, struct drm_crtc *crtc) +{ + struct sde_encoder_phys_wb *wb_enc; + + if (!phys || !phys->in_clone_mode || + phys->enable_state != SDE_ENC_DISABLING) + return false; + + wb_enc = container_of(phys, struct sde_encoder_phys_wb, base); + return (wb_enc->crtc == crtc) ? true : false; +} + +/** * sde_encoder_helper_split_config - split display configuration helper function * This helper function may be used by physical encoders to configure * the split display related registers. diff --git a/msm/sde/sde_encoder_phys_cmd.c b/msm/sde/sde_encoder_phys_cmd.c index 4f99a496..f9f3873e 100644 --- a/msm/sde/sde_encoder_phys_cmd.c +++ b/msm/sde/sde_encoder_phys_cmd.c @@ -949,7 +949,7 @@ static int _get_tearcheck_threshold(struct sde_encoder_phys *phys_enc, if (phys_enc->parent_ops.get_qsync_fps) phys_enc->parent_ops.get_qsync_fps( - phys_enc->parent, &qsync_min_fps); + phys_enc->parent, &qsync_min_fps, 0); if (!qsync_min_fps || !default_fps || !yres) { SDE_ERROR_CMDENC(cmd_enc, diff --git a/msm/sde/sde_encoder_phys_vid.c b/msm/sde/sde_encoder_phys_vid.c index 54faa7cb..7708cf8f 100644 --- a/msm/sde/sde_encoder_phys_vid.c +++ b/msm/sde/sde_encoder_phys_vid.c @@ -461,7 +461,7 @@ static void sde_encoder_phys_vid_setup_timing_engine( exit: if (phys_enc->parent_ops.get_qsync_fps) phys_enc->parent_ops.get_qsync_fps( - phys_enc->parent, &qsync_min_fps); + phys_enc->parent, &qsync_min_fps, mode.vrefresh); /* only panels which support qsync will have a non-zero min fps */ if (qsync_min_fps) { @@ -1125,13 +1125,21 @@ static void sde_encoder_phys_vid_handle_post_kickoff( static void sde_encoder_phys_vid_prepare_for_commit( struct sde_encoder_phys *phys_enc) { + struct drm_crtc *crtc; - if (!phys_enc) { + if (!phys_enc || !phys_enc->parent) { SDE_ERROR("invalid encoder parameters\n"); return; } - if (sde_connector_is_qsync_updated(phys_enc->connector)) + crtc = phys_enc->parent->crtc; + if (!crtc || !crtc->state) { + SDE_ERROR("invalid crtc or crtc state\n"); + return; + } + + if (!msm_is_mode_seamless_vrr(&crtc->state->adjusted_mode) && + sde_connector_is_qsync_updated(phys_enc->connector)) _sde_encoder_phys_vid_avr_ctrl(phys_enc); } diff --git a/msm/sde/sde_encoder_phys_wb.c b/msm/sde/sde_encoder_phys_wb.c index 1530f0c9..e044ed0f 100644 --- a/msm/sde/sde_encoder_phys_wb.c +++ b/msm/sde/sde_encoder_phys_wb.c @@ -1035,7 +1035,8 @@ static void _sde_encoder_phys_wb_frame_done_helper(void *arg, bool frame_error) SDE_DEBUG("[wb:%d,%u]\n", hw_wb->idx - WB_0, wb_enc->frame_count); /* don't notify upper layer for internal commit */ - if (phys_enc->enable_state == SDE_ENC_DISABLING) + if (phys_enc->enable_state == SDE_ENC_DISABLING && + !phys_enc->in_clone_mode) goto complete; if (phys_enc->parent_ops.handle_frame_done && @@ -1215,6 +1216,32 @@ static int sde_encoder_phys_wb_frame_timeout(struct sde_encoder_phys *phys_enc) return event; } +static void _sde_encoder_phys_wb_reset_state( + struct sde_encoder_phys *phys_enc) +{ + struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc); + + /* + * frame count and kickoff count are only used for debug purpose. Frame + * count can be more than kickoff count at the end of disable call due + * to extra frame_done wait. It does not cause any issue because + * frame_done wait is based on retire_fence count. Leaving these + * counters for debugging purpose. + */ + if (wb_enc->frame_count != wb_enc->kickoff_count) { + SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), + wb_enc->kickoff_count, wb_enc->frame_count, + phys_enc->in_clone_mode); + wb_enc->frame_count = wb_enc->kickoff_count; + } + + phys_enc->enable_state = SDE_ENC_DISABLED; + wb_enc->crtc = NULL; + phys_enc->hw_cdm = NULL; + phys_enc->hw_ctl = NULL; + phys_enc->in_clone_mode = false; +} + static int _sde_encoder_phys_wb_wait_for_commit_done( struct sde_encoder_phys *phys_enc, bool is_disable) { @@ -1298,7 +1325,18 @@ skip_wait: static int sde_encoder_phys_wb_wait_for_commit_done( struct sde_encoder_phys *phys_enc) { - return _sde_encoder_phys_wb_wait_for_commit_done(phys_enc, false); + int rc; + + if (phys_enc->enable_state == SDE_ENC_DISABLING && + phys_enc->in_clone_mode) { + rc = _sde_encoder_phys_wb_wait_for_commit_done(phys_enc, true); + _sde_encoder_phys_wb_reset_state(phys_enc); + sde_encoder_phys_wb_irq_ctrl(phys_enc, false); + } else { + rc = _sde_encoder_phys_wb_wait_for_commit_done(phys_enc, false); + } + + return rc; } static int sde_encoder_phys_wb_wait_for_cwb_done( @@ -1582,7 +1620,9 @@ static void sde_encoder_phys_wb_disable(struct sde_encoder_phys *phys_enc) SDE_DEBUG("[wait_for_done: wb:%d, frame:%u, kickoff:%u]\n", hw_wb->idx - WB_0, wb_enc->frame_count, wb_enc->kickoff_count); - _sde_encoder_phys_wb_wait_for_commit_done(phys_enc, true); + + if (!phys_enc->in_clone_mode || !wb_enc->crtc->state->active) + _sde_encoder_phys_wb_wait_for_commit_done(phys_enc, true); if (!phys_enc->hw_ctl || !phys_enc->parent || !phys_enc->sde_kms || !wb_enc->fb_disable) { @@ -1590,11 +1630,16 @@ static void sde_encoder_phys_wb_disable(struct sde_encoder_phys *phys_enc) goto exit; } - /* avoid reset frame for CWB */ if (phys_enc->in_clone_mode) { _sde_encoder_phys_wb_setup_cwb(phys_enc, false); _sde_encoder_phys_wb_update_cwb_flush(phys_enc, false); - phys_enc->in_clone_mode = false; + phys_enc->enable_state = SDE_ENC_DISABLING; + + if (wb_enc->crtc->state->active) { + sde_encoder_phys_wb_irq_ctrl(phys_enc, true); + return; + } + goto exit; } @@ -1627,24 +1672,7 @@ static void sde_encoder_phys_wb_disable(struct sde_encoder_phys *phys_enc) sde_encoder_phys_wb_irq_ctrl(phys_enc, false); exit: - /* - * frame count and kickoff count are only used for debug purpose. Frame - * count can be more than kickoff count at the end of disable call due - * to extra frame_done wait. It does not cause any issue because - * frame_done wait is based on retire_fence count. Leaving these - * counters for debugging purpose. - */ - if (wb_enc->frame_count != wb_enc->kickoff_count) { - SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), - wb_enc->kickoff_count, wb_enc->frame_count, - phys_enc->in_clone_mode); - wb_enc->frame_count = wb_enc->kickoff_count; - } - - phys_enc->enable_state = SDE_ENC_DISABLED; - wb_enc->crtc = NULL; - phys_enc->hw_cdm = NULL; - phys_enc->hw_ctl = NULL; + _sde_encoder_phys_wb_reset_state(phys_enc); } /** diff --git a/msm/sde/sde_hw_util.c b/msm/sde/sde_hw_util.c index ff4b5dfd..8b65855d 100644 --- a/msm/sde/sde_hw_util.c +++ b/msm/sde/sde_hw_util.c @@ -76,7 +76,10 @@ void sde_reg_write(struct sde_hw_blk_reg_map *c, if (c->log_mask & sde_hw_util_log_mask) SDE_DEBUG_DRIVER("[%s:0x%X] <= 0x%X\n", name, c->blk_off + reg_off, val); + SDE_EVT32_REGWRITE(c->blk_off, reg_off, val); writel_relaxed(val, c->base_off + c->blk_off + reg_off); + SDE_REG_LOG(c->log_mask ? ilog2(c->log_mask)+1 : 0, + val, c->blk_off + reg_off); } int sde_reg_read(struct sde_hw_blk_reg_map *c, u32 reg_off) diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index 6eb5618e..150145ed 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -1141,10 +1141,12 @@ static void sde_kms_check_for_ext_vote(struct sde_kms *sde_kms, * cases, allow the target to go through a gdsc toggle after * crtc is disabled. */ - if (!crtc_enabled && phandle->is_ext_vote_en) { + if (!crtc_enabled && (phandle->is_ext_vote_en || + !dev->dev->power.runtime_auto)) { pm_runtime_put_sync(sde_kms->dev->dev); - SDE_EVT32(phandle->is_ext_vote_en); pm_runtime_get_sync(sde_kms->dev->dev); + SDE_EVT32(phandle->is_ext_vote_en, + dev->dev->power.runtime_auto); } mutex_unlock(&phandle->ext_client_lock); @@ -1223,6 +1225,7 @@ static void sde_kms_wait_for_commit_done(struct msm_kms *kms, struct drm_encoder *encoder; struct drm_device *dev; int ret; + bool cwb_disabling; if (!kms || !crtc || !crtc->state) { SDE_ERROR("invalid params\n"); @@ -1248,8 +1251,14 @@ static void sde_kms_wait_for_commit_done(struct msm_kms *kms, SDE_ATRACE_BEGIN("sde_kms_wait_for_commit_done"); list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - if (encoder->crtc != crtc) - continue; + cwb_disabling = false; + if (encoder->crtc != crtc) { + cwb_disabling = sde_encoder_is_cwb_disabling(encoder, + crtc); + if (!cwb_disabling) + continue; + } + /* * Wait for post-flush if necessary to delay before * plane_cleanup. For example, wait for vsync in case of video @@ -1264,6 +1273,9 @@ static void sde_kms_wait_for_commit_done(struct msm_kms *kms, } sde_crtc_complete_flip(crtc, NULL); + + if (cwb_disabling) + sde_encoder_virt_reset(encoder); } SDE_ATRACE_END("sde_ksm_wait_for_commit_done"); @@ -1580,6 +1592,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev, .get_panel_vfp = dsi_display_get_panel_vfp, .get_default_lms = dsi_display_get_default_lms, .set_idle_hint = dsi_display_set_idle_hint, + .get_qsync_min_fps = dsi_display_get_qsync_min_fps, }; static const struct sde_connector_ops wb_ops = { .post_init = sde_wb_connector_post_init, @@ -2179,6 +2192,48 @@ static void sde_kms_destroy(struct msm_kms *kms) kfree(sde_kms); } +static int sde_kms_set_crtc_for_conn(struct drm_device *dev, + struct drm_encoder *enc, struct drm_atomic_state *state) +{ + struct drm_connector *conn = NULL; + struct drm_connector *tmp_conn = NULL; + struct drm_connector_list_iter conn_iter; + struct drm_crtc_state *crtc_state = NULL; + struct drm_connector_state *conn_state = NULL; + int ret = 0; + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(tmp_conn, &conn_iter) { + if (enc == tmp_conn->state->best_encoder) { + conn = tmp_conn; + break; + } + } + drm_connector_list_iter_end(&conn_iter); + + if (!conn) { + SDE_ERROR("error in finding conn for enc:%d\n", DRMID(enc)); + return -EINVAL; + } + + crtc_state = drm_atomic_get_crtc_state(state, enc->crtc); + conn_state = drm_atomic_get_connector_state(state, conn); + if (IS_ERR(conn_state)) { + SDE_ERROR("error %d getting connector %d state\n", + ret, DRMID(conn)); + return -EINVAL; + } + + crtc_state->active = true; + ret = drm_atomic_set_crtc_for_connector(conn_state, enc->crtc); + if (ret) + SDE_ERROR("error %d setting the crtc\n", ret); + + _sde_crtc_clear_dim_layers_v1(crtc_state); + + return 0; +} + static void _sde_kms_plane_force_remove(struct drm_plane *plane, struct drm_atomic_state *state) { @@ -2210,8 +2265,9 @@ static int _sde_kms_remove_fbs(struct sde_kms *sde_kms, struct drm_file *file, struct drm_framebuffer *fb, *tfb; struct list_head fbs; struct drm_plane *plane; + struct drm_crtc *crtc = NULL; + unsigned int crtc_mask = 0; int ret = 0; - u32 plane_mask = 0; INIT_LIST_HEAD(&fbs); @@ -2220,9 +2276,11 @@ static int _sde_kms_remove_fbs(struct sde_kms *sde_kms, struct drm_file *file, list_move_tail(&fb->filp_head, &fbs); drm_for_each_plane(plane, dev) { - if (plane->fb == fb) { - plane_mask |= - 1 << drm_plane_index(plane); + if (plane->state && + plane->state->fb == fb) { + if (plane->state->crtc) + crtc_mask |= drm_crtc_mask( + plane->state->crtc); _sde_kms_plane_force_remove( plane, state); } @@ -2235,11 +2293,22 @@ static int _sde_kms_remove_fbs(struct sde_kms *sde_kms, struct drm_file *file, if (list_empty(&fbs)) { SDE_DEBUG("skip commit as no fb(s)\n"); - drm_atomic_state_put(state); return 0; } - SDE_DEBUG("committing after removing all the pipes\n"); + drm_for_each_crtc(crtc, dev) { + if ((crtc_mask & drm_crtc_mask(crtc)) && crtc->state->active) { + struct drm_encoder *drm_enc; + + drm_for_each_encoder_mask(drm_enc, crtc->dev, + crtc->state->encoder_mask) + ret = sde_kms_set_crtc_for_conn( + dev, drm_enc, state); + } + } + + SDE_EVT32(state, crtc_mask); + SDE_DEBUG("null commit after removing all the pipes\n"); ret = drm_atomic_commit(state); if (ret) { @@ -2894,12 +2963,7 @@ static void _sde_kms_null_commit(struct drm_device *dev, struct drm_encoder *enc) { struct drm_modeset_acquire_ctx ctx; - struct drm_connector *conn = NULL; - struct drm_connector *tmp_conn = NULL; - struct drm_connector_list_iter conn_iter; struct drm_atomic_state *state = NULL; - struct drm_crtc_state *crtc_state = NULL; - struct drm_connector_state *conn_state = NULL; int retry_cnt = 0; int ret = 0; @@ -2923,32 +2987,10 @@ retry: } state->acquire_ctx = &ctx; - drm_connector_list_iter_begin(dev, &conn_iter); - drm_for_each_connector_iter(tmp_conn, &conn_iter) { - if (enc == tmp_conn->state->best_encoder) { - conn = tmp_conn; - break; - } - } - drm_connector_list_iter_end(&conn_iter); - if (!conn) { - SDE_ERROR("error in finding conn for enc:%d\n", DRMID(enc)); - goto end; - } - - crtc_state = drm_atomic_get_crtc_state(state, enc->crtc); - conn_state = drm_atomic_get_connector_state(state, conn); - if (IS_ERR(conn_state)) { - SDE_ERROR("error %d getting connector %d state\n", - ret, DRMID(conn)); - goto end; - } - - crtc_state->active = true; - ret = drm_atomic_set_crtc_for_connector(conn_state, enc->crtc); + ret = sde_kms_set_crtc_for_conn(dev, enc, state); if (ret) - SDE_ERROR("error %d setting the crtc\n", ret); + goto end; ret = drm_atomic_commit(state); if (ret) @@ -3476,6 +3518,7 @@ static int sde_kms_pd_enable(struct generic_pm_domain *genpd) static int sde_kms_pd_disable(struct generic_pm_domain *genpd) { struct sde_kms *sde_kms = genpd_to_sde_kms(genpd); + struct msm_drm_private *priv; SDE_DEBUG("\n"); @@ -3483,6 +3526,9 @@ static int sde_kms_pd_disable(struct generic_pm_domain *genpd) SDE_EVT32(genpd->device_count); + priv = sde_kms->dev->dev_private; + sde_kms_check_for_ext_vote(sde_kms, &priv->phandle); + return 0; } diff --git a/msm/sde/sde_plane.c b/msm/sde/sde_plane.c index e834c5da..6b2341ba 100644 --- a/msm/sde/sde_plane.c +++ b/msm/sde/sde_plane.c @@ -2562,6 +2562,39 @@ static int _sde_plane_validate_shared_crtc(struct sde_plane *psde, } +static int _sde_plane_validate_fb(struct sde_plane *psde, + struct drm_plane_state *state) +{ + struct sde_plane_state *pstate; + struct drm_framebuffer *fb; + uint32_t fb_ns = 0, fb_sec = 0, fb_sec_dir = 0; + unsigned long flags = 0; + int mode, ret = 0, n, i; + + pstate = to_sde_plane_state(state); + mode = sde_plane_get_property(pstate, + PLANE_PROP_FB_TRANSLATION_MODE); + + fb = state->fb; + n = fb->format->num_planes; + for (i = 0; i < n; i++) { + ret = msm_fb_obj_get_attrs(fb->obj[i], &fb_ns, &fb_sec, + &fb_sec_dir, &flags); + + if (!ret && ((fb_ns && (mode != SDE_DRM_FB_NON_SEC)) || + (fb_sec && (mode != SDE_DRM_FB_SEC)) || + (fb_sec_dir && (mode != SDE_DRM_FB_SEC_DIR_TRANS)))) { + SDE_ERROR_PLANE(psde, "mode:%d fb:%d flag:0x%x rc:%d\n", + mode, fb->base.id, flags, ret); + SDE_EVT32(psde->base.base.id, fb->base.id, flags, + fb_ns, fb_sec, fb_sec_dir, ret, SDE_EVTLOG_ERROR); + return -EINVAL; + } + } + + return 0; +} + static int sde_plane_sspp_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) { @@ -2675,6 +2708,11 @@ static int sde_plane_sspp_atomic_check(struct drm_plane *plane, if (ret) return ret; + ret = _sde_plane_validate_fb(psde, state); + + if (ret) + return ret; + pstate->const_alpha_en = fmt->alpha_enable && (SDE_DRM_BLEND_OP_OPAQUE != sde_plane_get_property(pstate, PLANE_PROP_BLEND_OP)) && @@ -4563,7 +4601,8 @@ struct drm_plane *sde_plane_init(struct drm_device *dev, SDE_ERROR("[%u]SSPP init failed\n", pipe); ret = PTR_ERR(psde->pipe_hw); goto clean_plane; - } else if (!psde->pipe_hw->cap || !psde->pipe_hw->cap->sblk) { + } else if (!psde->pipe_hw || !psde->pipe_hw->cap || + !psde->pipe_hw->cap->sblk) { SDE_ERROR("[%u]SSPP init returned invalid cfg\n", pipe); goto clean_sspp; } diff --git a/msm/sde/sde_rm.c b/msm/sde/sde_rm.c index 7f3716b8..76e908b4 100644 --- a/msm/sde/sde_rm.c +++ b/msm/sde/sde_rm.c @@ -2126,8 +2126,8 @@ struct sde_rm_rsvp *_sde_rm_poll_get_rsvp_nxt_locked(struct sde_rm *rm, usleep_range(sleep, sleep * 2); mutex_lock(&rm->rm_lock); } - - return rsvp_nxt; + /* make sure to get latest rsvp_next to avoid use after free issues */ + return _sde_rm_get_rsvp_nxt(rm, enc); } int sde_rm_reserve( diff --git a/msm/sde_dbg.c b/msm/sde_dbg.c index 6178e1bf..2723d01c 100644 --- a/msm/sde_dbg.c +++ b/msm/sde_dbg.c @@ -197,6 +197,7 @@ struct sde_dbg_regbuf { /** * struct sde_dbg_base - global sde debug base structure * @evtlog: event log instance + * @reglog: reg log instance * @reg_base_list: list of register dumping regions * @dev: device pointer * @mutex: mutex to serialize access to serialze dumps, debugfs access @@ -212,12 +213,15 @@ struct sde_dbg_regbuf { * @dsi_dbg_bus: dump dsi debug bus register * @regbuf: buffer data to track the register dumping in hw recovery * @cur_evt_index: index used for tracking event logs dump in hw recovery + * @cur_reglog_index: index used for tracking register logs dump in hw recovery * @dbgbus_dump_idx: index used for tracking dbg-bus dump in hw recovery * @vbif_dbgbus_dump_idx: index for tracking vbif dumps in hw recovery */ static struct sde_dbg_base { struct sde_dbg_evtlog *evtlog; + struct sde_dbg_reglog *reglog; struct list_head reg_base_list; + void *reg_dump_addr; struct device *dev; struct mutex mutex; @@ -238,6 +242,7 @@ static struct sde_dbg_base { struct sde_dbg_regbuf regbuf; u32 cur_evt_index; + u32 cur_reglog_index; u32 dbgbus_dump_idx; u32 vbif_dbgbus_dump_idx; enum sde_dbg_dump_context dump_mode; @@ -246,6 +251,9 @@ static struct sde_dbg_base { /* sde_dbg_base_evtlog - global pointer to main sde event log for macro use */ struct sde_dbg_evtlog *sde_dbg_base_evtlog; +/* sde_dbg_base_reglog - global pointer to main sde reg log for macro use */ +struct sde_dbg_reglog *sde_dbg_base_reglog; + static void _sde_debug_bus_xbar_dump(void __iomem *mem_base, struct sde_debug_bus_entry *entry, u32 val) { @@ -2922,6 +2930,7 @@ static void _sde_dump_reg(const char *dump_name, u32 reg_dump_flag, char *base_addr, char *addr, size_t len_bytes, u32 **dump_mem) { u32 in_log, in_mem, len_align, len_padded; + struct sde_dbg_base *dbg_base = &sde_dbg_base; u32 *dump_addr = NULL; char *end_addr; int i; @@ -2950,9 +2959,8 @@ static void _sde_dump_reg(const char *dump_name, u32 reg_dump_flag, if (in_mem) { if (dump_mem && !(*dump_mem)) { - phys_addr_t phys = 0; - *dump_mem = dma_alloc_coherent(sde_dbg_base.dev, - len_padded, &phys, GFP_KERNEL); + *dump_mem = dbg_base->reg_dump_addr; + dbg_base->reg_dump_addr += len_padded; } if (dump_mem && *dump_mem) { @@ -3026,6 +3034,49 @@ static u32 _sde_dbg_get_dump_range(struct sde_dbg_reg_offset *range_node, return length; } +static u32 _sde_dbg_get_reg_blk_size(struct sde_dbg_reg_base *dbg) +{ + u32 len, len_align, len_padded; + u32 size = 0; + struct sde_dbg_reg_range *range_node; + + if (!dbg || !dbg->base) { + pr_err("dbg base is null!\n"); + return 0; + } + + if (!list_empty(&dbg->sub_range_list)) { + list_for_each_entry(range_node, &dbg->sub_range_list, head) { + len = _sde_dbg_get_dump_range(&range_node->offset, + dbg->max_offset); + len_align = (len + REG_DUMP_ALIGN - 1) / REG_DUMP_ALIGN; + len_padded = len_align * REG_DUMP_ALIGN; + size += REG_BASE_NAME_LEN + RANGE_NAME_LEN + len_padded; + } + } else { + len = dbg->max_offset; + len_align = (len + REG_DUMP_ALIGN - 1) / REG_DUMP_ALIGN; + len_padded = len_align * REG_DUMP_ALIGN; + size += REG_BASE_NAME_LEN + RANGE_NAME_LEN + len_padded; + } + return size; +} + +static u32 _sde_dbg_get_reg_dump_size(void) +{ + struct sde_dbg_base *dbg_base = &sde_dbg_base; + struct sde_dbg_reg_base *blk_base; + u32 size = 0; + + if (!dbg_base) + return 0; + + list_for_each_entry(blk_base, &dbg_base->reg_base_list, reg_base_head) { + size += _sde_dbg_get_reg_blk_size(blk_base); + } + return size; +} + static int _sde_dump_reg_range_cmp(void *priv, struct list_head *a, struct list_head *b) { @@ -3071,6 +3122,7 @@ static void _sde_dump_reg_by_ranges(struct sde_dbg_reg_base *dbg, char *addr; size_t len; struct sde_dbg_reg_range *range_node; + struct sde_dbg_base *dbg_base = &sde_dbg_base; if (!dbg || !(dbg->base || dbg->cb)) { pr_err("dbg base is null!\n"); @@ -3100,6 +3152,12 @@ static void _sde_dump_reg_by_ranges(struct sde_dbg_reg_base *dbg, addr, range_node->offset.start, range_node->offset.end); + scnprintf(dbg_base->reg_dump_addr, REG_BASE_NAME_LEN, + dbg->name); + dbg_base->reg_dump_addr += REG_BASE_NAME_LEN; + scnprintf(dbg_base->reg_dump_addr, REG_BASE_NAME_LEN, + range_node->range_name); + dbg_base->reg_dump_addr += RANGE_NAME_LEN; _sde_dump_reg(range_node->range_name, reg_dump_flag, dbg->base, addr, len, &range_node->reg_dump); @@ -3112,6 +3170,10 @@ static void _sde_dump_reg_by_ranges(struct sde_dbg_reg_base *dbg, dbg->max_offset); addr = dbg->base; len = dbg->max_offset; + scnprintf(dbg_base->reg_dump_addr, REG_BASE_NAME_LEN, + dbg->name); + dbg_base->reg_dump_addr += REG_BASE_NAME_LEN; + dbg_base->reg_dump_addr += RANGE_NAME_LEN; _sde_dump_reg(dbg->name, reg_dump_flag, dbg->base, addr, len, &dbg->reg_dump); } @@ -3478,9 +3540,16 @@ static void _sde_dump_array(struct sde_dbg_reg_base *blk_arr[], bool dump_dbgbus_vbif_rt, bool dump_all, bool dump_secure) { int i; + u32 reg_dump_size; + struct sde_dbg_base *dbg_base = &sde_dbg_base; + phys_addr_t phys = 0; mutex_lock(&sde_dbg_base.mutex); + reg_dump_size = _sde_dbg_get_reg_dump_size(); + dbg_base->reg_dump_addr = dma_alloc_coherent(sde_dbg_base.dev, + reg_dump_size, &phys, GFP_KERNEL); + if (dump_all) sde_evtlog_dump_all(sde_dbg_base.evtlog); @@ -3658,7 +3727,7 @@ void sde_dbg_ctrl(const char *name, ...) va_end(args); } - +#ifdef CONFIG_DEBUG_FS /* * sde_dbg_debugfs_open - debugfs open handler for evtlog dump * @inode: debugfs inode @@ -4600,6 +4669,15 @@ int sde_dbg_debugfs_register(struct device *dev) return 0; } +#else + +int sde_dbg_debugfs_register(struct device *dev) +{ + return 0; +} + +#endif + static void _sde_dbg_debugfs_destroy(void) { } @@ -4665,6 +4743,12 @@ int sde_dbg_init(struct device *dev) sde_dbg_base_evtlog = sde_dbg_base.evtlog; + sde_dbg_base.reglog = sde_reglog_init(); + if (IS_ERR_OR_NULL(sde_dbg_base.reglog)) + return PTR_ERR(sde_dbg_base.reglog); + + sde_dbg_base_reglog = sde_dbg_base.reglog; + INIT_WORK(&sde_dbg_base.dump_work, _sde_dump_work); sde_dbg_base.work_panic = false; sde_dbg_base.panic_on_err = DEFAULT_PANIC; @@ -4709,6 +4793,8 @@ void sde_dbg_destroy(void) sde_dbg_base_evtlog = NULL; sde_evtlog_destroy(sde_dbg_base.evtlog); sde_dbg_base.evtlog = NULL; + sde_reglog_destroy(sde_dbg_base.reglog); + sde_dbg_base.reglog = NULL; sde_dbg_reg_base_destroy(); mutex_destroy(&sde_dbg_base.mutex); } diff --git a/msm/sde_dbg.h b/msm/sde_dbg.h index e336f323..61764c04 100644 --- a/msm/sde_dbg.h +++ b/msm/sde_dbg.h @@ -35,6 +35,7 @@ enum sde_dbg_evtlog_flag { SDE_EVTLOG_IRQ = BIT(1), SDE_EVTLOG_VERBOSE = BIT(2), SDE_EVTLOG_EXTERNAL = BIT(3), + SDE_EVTLOG_REGWRITE = BIT(4), SDE_EVTLOG_ALWAYS = -1 }; @@ -49,6 +50,34 @@ enum sde_dbg_dump_context { SDE_DBG_DUMP_CLK_ENABLED_CTX, }; +/* + * Define blocks for register write logging. + */ +#define SDE_REG_LOG_DEFAULT 0 +#define SDE_REG_LOG_NONE 1 +#define SDE_REG_LOG_CDM 2 +#define SDE_REG_LOG_DSPP 3 +#define SDE_REG_LOG_INTF 4 +#define SDE_REG_LOG_LM 5 +#define SDE_REG_LOG_CTL 6 +#define SDE_REG_LOG_PINGPONG 7 +#define SDE_REG_LOG_SSPP 8 +#define SDE_REG_LOG_WB 9 +#define SDE_REG_LOG_TOP 10 +#define SDE_REG_LOG_VBIF 11 +#define SDE_REG_LOG_DSC 12 +#define SDE_REG_LOG_ROT 13 +#define SDE_REG_LOG_DS 14 +#define SDE_REG_LOG_REGDMA 15 +#define SDE_REG_LOG_UIDLE 16 +#define SDE_REG_LOG_SID 16 +#define SDE_REG_LOG_QDSS 17 +/* + * 0-32 are reserved for sde_reg_write due to log masks + * Additional blocks are assigned from 33 to avoid conflict + */ +#define SDE_REG_LOG_RSCC 33 + #define SDE_EVTLOG_DEFAULT_ENABLE (SDE_EVTLOG_CRITICAL | SDE_EVTLOG_IRQ | \ SDE_EVTLOG_EXTERNAL) @@ -103,6 +132,44 @@ struct sde_dbg_evtlog { extern struct sde_dbg_evtlog *sde_dbg_base_evtlog; +/* + * reglog keeps this number of entries in memory for debug purpose. This + * number must be greater than number of possible writes in at least one + * single commit. + */ +#define SDE_REGLOG_ENTRY 1024 + +struct sde_dbg_reglog_log { + s64 time; + u32 pid; + u32 addr; + u32 val; + u8 blk_id; +}; + +/** + * @last_dump: Index of last entry to be output during reglog dumps + * @filter_list: Linked list of currently active filter strings + */ +struct sde_dbg_reglog { + struct sde_dbg_reglog_log logs[SDE_REGLOG_ENTRY]; + u32 first; + u32 last; + u32 last_dump; + u32 curr; + u32 next; + u32 enable; + u32 enable_mask; + spinlock_t spin_lock; +}; + +extern struct sde_dbg_reglog *sde_dbg_base_reglog; + +/** + * SDE_REG_LOG - Write register write to the register log + */ +#define SDE_REG_LOG(blk_id, val, addr) sde_reglog_log(blk_id, val, addr) + /** * SDE_EVT32 - Write a list of 32bit values to the event log, default area * ... - variable arguments @@ -134,6 +201,13 @@ extern struct sde_dbg_evtlog *sde_dbg_base_evtlog; #define SDE_EVT32_EXTERNAL(...) sde_evtlog_log(sde_dbg_base_evtlog, __func__, \ __LINE__, SDE_EVTLOG_EXTERNAL, ##__VA_ARGS__, \ SDE_EVTLOG_DATA_LIMITER) +/** + * SDE_EVT32_REGWRITE - Write a list of 32bit values for register writes logging + * ... - variable arguments + */ +#define SDE_EVT32_REGWRITE(...) sde_evtlog_log(sde_dbg_base_evtlog, __func__, \ + __LINE__, SDE_EVTLOG_REGWRITE, ##__VA_ARGS__, \ + SDE_EVTLOG_DATA_LIMITER) /** * SDE_DBG_DUMP - trigger dumping of all sde_dbg facilities @@ -175,7 +249,6 @@ extern struct sde_dbg_evtlog *sde_dbg_base_evtlog; #define SDE_DBG_CTRL(...) sde_dbg_ctrl(__func__, ##__VA_ARGS__, \ SDE_DBG_DUMP_DATA_LIMITER) -#if defined(CONFIG_DEBUG_FS) /** * sde_evtlog_init - allocate a new event log object @@ -184,6 +257,12 @@ extern struct sde_dbg_evtlog *sde_dbg_base_evtlog; struct sde_dbg_evtlog *sde_evtlog_init(void); /** + * sde_reglog_init - allocate a new reg log object + * Returns: reglog or -ERROR + */ +struct sde_dbg_reglog *sde_reglog_init(void); + +/** * sde_evtlog_destroy - destroy previously allocated event log * @evtlog: pointer to evtlog * Returns: none @@ -191,6 +270,13 @@ struct sde_dbg_evtlog *sde_evtlog_init(void); void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog); /** + * sde_reglog_destroy - destroy previously allocated reg log + * @reglog: pointer to reglog + * Returns: none + */ +void sde_reglog_destroy(struct sde_dbg_reglog *reglog); + +/** * sde_evtlog_log - log an entry into the event log. * log collection may be enabled/disabled entirely via debugfs * log area collection may be filtered by user provided flags via debugfs. @@ -204,6 +290,15 @@ void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, const char *name, int line, int flag, ...); /** + * sde_reglog_log - log an entry into the reg log. + * log collection may be enabled/disabled entirely via debugfs + * log area collection may be filtered by user provided flags via debugfs. + * @reglog: pointer to evtlog + * Returns: none + */ +void sde_reglog_log(u8 blk_id, u32 val, u32 addr); + +/** * sde_evtlog_dump_all - print all entries in event log to kernel log * @evtlog: pointer to evtlog * Returns: none @@ -371,101 +466,4 @@ void sde_rsc_debug_dump(u32 mux_sel); */ void dsi_ctrl_debug_dump(u32 *entries, u32 size); -#else -static inline struct sde_dbg_evtlog *sde_evtlog_init(void) -{ - return NULL; -} - -static inline void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog) -{ -} - -static inline void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, - const char *name, int line, int flag, ...) -{ -} - -static inline void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog) -{ -} - -static inline bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, - u32 flag) -{ - return false; -} - -static inline ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog, - char *evtlog_buf, ssize_t evtlog_buf_size, - bool update_last_entry) -{ - return 0; -} - -static inline void sde_dbg_init_dbg_buses(u32 hwversion) -{ -} - -static inline int sde_dbg_init(struct device *dev) -{ - return 0; -} - -static inline int sde_dbg_debugfs_register(struct device *dev) -{ - return 0; -} - -static inline void sde_dbg_destroy(void) -{ -} - -static inline void sde_dbg_dump(enum sde_dbg_dump_context mode, - const char *name, ...) -{ -} - -static inline void sde_dbg_ctrl(const char *name, ...) -{ -} - -static inline int sde_dbg_reg_register_base(const char *name, - void __iomem *base, size_t max_offset) -{ - return 0; -} - -static inline void sde_dbg_reg_register_dump_range(const char *base_name, - const char *range_name, u32 offset_start, u32 offset_end, - uint32_t xin_id) -{ -} - -static inline void sde_dbg_set_sde_top_offset(u32 blk_off) -{ -} - -static inline void sde_evtlog_set_filter( - struct sde_dbg_evtlog *evtlog, char *filter) -{ -} - -static inline int sde_evtlog_get_filter(struct sde_dbg_evtlog *evtlog, - int index, char *buf, size_t bufsz) -{ - return -EINVAL; -} - -static inline void sde_rsc_debug_dump(u32 mux_sel) -{ -} - -static inline void dsi_ctrl_debug_dump(u32 *entries, u32 size) -{ -} - -#endif /* defined(CONFIG_DEBUG_FS) */ - - #endif /* SDE_DBG_H_ */ diff --git a/msm/sde_dbg_evtlog.c b/msm/sde_dbg_evtlog.c index 71ec3283..ddb4c996 100644 --- a/msm/sde_dbg_evtlog.c +++ b/msm/sde_dbg_evtlog.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "sde_dbg:[%s] " fmt, __func__ @@ -101,6 +101,31 @@ exit: spin_unlock_irqrestore(&evtlog->spin_lock, flags); } +void sde_reglog_log(u8 blk_id, u32 val, u32 addr) +{ + unsigned long flags; + struct sde_dbg_reglog_log *log; + struct sde_dbg_reglog *reglog = sde_dbg_base_reglog; + + if (!reglog) + return; + + spin_lock_irqsave(®log->spin_lock, flags); + + log = ®log->logs[reglog->curr]; + + log->blk_id = blk_id; + log->val = val; + log->addr = addr; + log->time = local_clock(); + log->pid = current->pid; + + reglog->curr = (reglog->curr + 1) % SDE_REGLOG_ENTRY; + reglog->last++; + + spin_unlock_irqrestore(®log->spin_lock, flags); +} + /* always dump the last entries which are not dumped yet */ static bool _sde_evtlog_dump_calc_range(struct sde_dbg_evtlog *evtlog, bool update_last_entry, bool full_dump) @@ -211,6 +236,19 @@ struct sde_dbg_evtlog *sde_evtlog_init(void) return evtlog; } +struct sde_dbg_reglog *sde_reglog_init(void) +{ + struct sde_dbg_reglog *reglog; + + reglog = kzalloc(sizeof(*reglog), GFP_KERNEL); + if (!reglog) + return ERR_PTR(-ENOMEM); + + spin_lock_init(®log->spin_lock); + + return reglog; +} + int sde_evtlog_get_filter(struct sde_dbg_evtlog *evtlog, int index, char *buf, size_t bufsz) { @@ -312,3 +350,11 @@ void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog) } kfree(evtlog); } + +void sde_reglog_destroy(struct sde_dbg_reglog *reglog) +{ + if (!reglog) + return; + + kfree(reglog); +} diff --git a/msm/sde_io_util.c b/msm/sde_io_util.c index 09649c59..ad6e89f7 100644 --- a/msm/sde_io_util.c +++ b/msm/sde_io_util.c @@ -9,6 +9,7 @@ #include <linux/regulator/consumer.h> #include <linux/delay.h> #include <linux/sde_io_util.h> +#include "sde_dbg.h" #define MAX_I2C_CMDS 16 void dss_reg_w(struct dss_io_data *io, u32 offset, u32 value, u32 debug) @@ -33,7 +34,9 @@ void dss_reg_w(struct dss_io_data *io, u32 offset, u32 value, u32 debug) DEV_DBG("[%08x] => %08x [%08x]\n", (u32)(unsigned long)(io->base + offset), value, in_val); + SDE_EVT32_REGWRITE(io->base, offset, value, in_val); } + SDE_REG_LOG(SDE_REG_LOG_RSCC, value, offset); } /* dss_reg_w */ EXPORT_SYMBOL(dss_reg_w); |