summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohnnLee <johnnlee@google.com>2021-03-29 13:43:53 +0800
committerJohnnLee <johnnlee@google.com>2021-04-01 11:52:02 +0800
commite471ed45e9ffa031b3a34f3fdb18d48f187cd52b (patch)
treece3c1403594217bbd75dbf830ef668e02ac4a2a8
parentea5b69a0327130b9553a3ad6ede3efb2734be756 (diff)
parentd7cfa594538bd09e8513e7825637c3ec4c679a83 (diff)
downloaddisplay-drivers-e471ed45e9ffa031b3a34f3fdb18d48f187cd52b.tar.gz
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>
-rw-r--r--msm/Makefile3
-rw-r--r--msm/dp/dp_debug.c4
-rw-r--r--msm/dp/dp_display.c13
-rw-r--r--msm/dp/dp_panel.c23
-rw-r--r--msm/dp/dp_panel.h2
-rw-r--r--msm/dsi/dsi_ctrl.c9
-rw-r--r--msm/dsi/dsi_display.c28
-rw-r--r--msm/dsi/dsi_display.h10
-rw-r--r--msm/dsi/dsi_drm.c6
-rw-r--r--msm/dsi/dsi_panel.c83
-rw-r--r--msm/dsi/dsi_panel.h9
-rw-r--r--msm/msm_drv.c8
-rw-r--r--msm/msm_drv.h8
-rw-r--r--msm/msm_fb.c31
-rw-r--r--msm/sde/sde_connector.c15
-rw-r--r--msm/sde/sde_connector.h8
-rw-r--r--msm/sde/sde_crtc.c153
-rw-r--r--msm/sde/sde_crtc.h28
-rw-r--r--msm/sde/sde_encoder.c148
-rw-r--r--msm/sde/sde_encoder.h38
-rw-r--r--msm/sde/sde_encoder_phys.h24
-rw-r--r--msm/sde/sde_encoder_phys_cmd.c2
-rw-r--r--msm/sde/sde_encoder_phys_vid.c14
-rw-r--r--msm/sde/sde_encoder_phys_wb.c74
-rw-r--r--msm/sde/sde_hw_util.c3
-rw-r--r--msm/sde/sde_kms.c124
-rw-r--r--msm/sde/sde_plane.c41
-rw-r--r--msm/sde/sde_rm.c4
-rw-r--r--msm/sde_dbg.c94
-rw-r--r--msm/sde_dbg.h194
-rw-r--r--msm/sde_dbg_evtlog.c48
-rw-r--r--msm/sde_io_util.c3
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(&reglog->spin_lock, flags);
+
+ log = &reglog->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(&reglog->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(&reglog->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);