summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
authorAlan Kwong <akwong@codeaurora.org>2016-10-24 21:39:26 -0400
committerAlan Kwong <akwong@codeaurora.org>2016-12-14 09:59:56 -0500
commit40b9c0ee802c5a963aa448008177dc31b040cb49 (patch)
tree0c6b070dec66faae32a62021270af9b0aae0c0f4 /drivers/gpu
parented93daaea77966c21d6b3807a8e3f94bcd07f11b (diff)
drm/msm/sde: add debugfs entry for danger and safe status
Add debugfs entry for current danger and safe status of each pipe and current overall status for debugging purposes. Change-Id: I3e784d0218df40c184a431409c864a3208a90b45 Signed-off-by: Alan Kwong <akwong@codeaurora.org>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_top.c62
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_top.h28
-rw-r--r--drivers/gpu/drm/msm/sde/sde_kms.c110
-rw-r--r--drivers/gpu/drm/msm/sde/sde_kms.h3
-rw-r--r--drivers/gpu/drm/msm/sde/sde_plane.c147
5 files changed, 350 insertions, 0 deletions
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.c b/drivers/gpu/drm/msm/sde/sde_hw_top.c
index 045d4c24bb29..1a5d469e6e7e 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_top.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_top.c
@@ -22,6 +22,9 @@
#define FLD_INTF_2_SW_TRG_MUX BIT(8)
#define FLD_TE_LINE_INTER_WATERLEVEL_MASK 0xFFFF
+#define DANGER_STATUS 0x360
+#define SAFE_STATUS 0x364
+
#define TE_LINE_INTERVAL 0x3F4
#define TRAFFIC_SHAPER_EN BIT(31)
@@ -139,6 +142,63 @@ static bool sde_hw_setup_clk_force_ctrl(struct sde_hw_mdp *mdp,
return clk_forced_on;
}
+
+static void sde_hw_get_danger_status(struct sde_hw_mdp *mdp,
+ struct sde_danger_safe_status *status)
+{
+ struct sde_hw_blk_reg_map *c = &mdp->hw;
+ u32 value;
+
+ value = SDE_REG_READ(c, DANGER_STATUS);
+ status->mdp = (value >> 0) & 0x3;
+ status->sspp[SSPP_VIG0] = (value >> 4) & 0x3;
+ status->sspp[SSPP_VIG1] = (value >> 6) & 0x3;
+ status->sspp[SSPP_VIG2] = (value >> 8) & 0x3;
+ status->sspp[SSPP_VIG3] = (value >> 10) & 0x3;
+ status->sspp[SSPP_RGB0] = (value >> 12) & 0x3;
+ status->sspp[SSPP_RGB1] = (value >> 14) & 0x3;
+ status->sspp[SSPP_RGB2] = (value >> 16) & 0x3;
+ status->sspp[SSPP_RGB3] = (value >> 18) & 0x3;
+ status->sspp[SSPP_DMA0] = (value >> 20) & 0x3;
+ status->sspp[SSPP_DMA1] = (value >> 22) & 0x3;
+ status->sspp[SSPP_DMA2] = (value >> 28) & 0x3;
+ status->sspp[SSPP_DMA3] = (value >> 30) & 0x3;
+ status->sspp[SSPP_CURSOR0] = (value >> 24) & 0x3;
+ status->sspp[SSPP_CURSOR1] = (value >> 26) & 0x3;
+ status->wb[WB_0] = 0;
+ status->wb[WB_1] = 0;
+ status->wb[WB_2] = (value >> 2) & 0x3;
+ status->wb[WB_3] = 0;
+}
+
+static void sde_hw_get_safe_status(struct sde_hw_mdp *mdp,
+ struct sde_danger_safe_status *status)
+{
+ struct sde_hw_blk_reg_map *c = &mdp->hw;
+ u32 value;
+
+ value = SDE_REG_READ(c, SAFE_STATUS);
+ status->mdp = (value >> 0) & 0x1;
+ status->sspp[SSPP_VIG0] = (value >> 4) & 0x1;
+ status->sspp[SSPP_VIG1] = (value >> 6) & 0x1;
+ status->sspp[SSPP_VIG2] = (value >> 8) & 0x1;
+ status->sspp[SSPP_VIG3] = (value >> 10) & 0x1;
+ status->sspp[SSPP_RGB0] = (value >> 12) & 0x1;
+ status->sspp[SSPP_RGB1] = (value >> 14) & 0x1;
+ status->sspp[SSPP_RGB2] = (value >> 16) & 0x1;
+ status->sspp[SSPP_RGB3] = (value >> 18) & 0x1;
+ status->sspp[SSPP_DMA0] = (value >> 20) & 0x1;
+ status->sspp[SSPP_DMA1] = (value >> 22) & 0x1;
+ status->sspp[SSPP_DMA2] = (value >> 28) & 0x1;
+ status->sspp[SSPP_DMA3] = (value >> 30) & 0x1;
+ status->sspp[SSPP_CURSOR0] = (value >> 24) & 0x1;
+ status->sspp[SSPP_CURSOR1] = (value >> 26) & 0x1;
+ status->wb[WB_0] = 0;
+ status->wb[WB_1] = 0;
+ status->wb[WB_2] = (value >> 2) & 0x1;
+ status->wb[WB_3] = 0;
+}
+
static void _setup_mdp_ops(struct sde_hw_mdp_ops *ops,
unsigned long cap)
{
@@ -146,6 +206,8 @@ static void _setup_mdp_ops(struct sde_hw_mdp_ops *ops,
ops->setup_pp_split = sde_hw_setup_pp_split;
ops->setup_cdm_output = sde_hw_setup_cdm_output;
ops->setup_clk_force_ctrl = sde_hw_setup_clk_force_ctrl;
+ ops->get_danger_status = sde_hw_get_danger_status;
+ ops->get_safe_status = sde_hw_get_safe_status;
}
static const struct sde_mdp_cfg *_top_offset(enum sde_mdp mdp,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.h b/drivers/gpu/drm/msm/sde/sde_hw_top.h
index eb8ff8685908..780d051e7408 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_top.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_top.h
@@ -65,6 +65,18 @@ struct cdm_output_cfg {
};
/**
+ * struct sde_danger_safe_status: danger and safe status signals
+ * @mdp: top level status
+ * @sspp: source pipe status
+ * @wb: writebck output status
+ */
+struct sde_danger_safe_status {
+ u8 mdp;
+ u8 sspp[SSPP_MAX];
+ u8 wb[WB_MAX];
+};
+
+/**
* struct sde_hw_mdp_ops - interface to the MDP TOP Hw driver functions
* Assumption is these functions will be called after clocks are enabled.
* @setup_split_pipe : Programs the pipe control registers
@@ -113,6 +125,22 @@ struct sde_hw_mdp_ops {
*/
bool (*setup_clk_force_ctrl)(struct sde_hw_mdp *mdp,
enum sde_clk_ctrl_type clk_ctrl, bool enable);
+
+ /**
+ * get_danger_status - get danger status
+ * @mdp: mdp top context driver
+ * @status: Pointer to danger safe status
+ */
+ void (*get_danger_status)(struct sde_hw_mdp *mdp,
+ struct sde_danger_safe_status *status);
+
+ /**
+ * get_safe_status - get safe status
+ * @mdp: mdp top context driver
+ * @status: Pointer to danger safe status
+ */
+ void (*get_safe_status)(struct sde_hw_mdp *mdp,
+ struct sde_danger_safe_status *status);
};
struct sde_hw_mdp {
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 1b523767f523..7d67404caef4 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -78,6 +78,101 @@ bool sde_is_custom_client(void)
return sdecustom;
}
+#ifdef CONFIG_DEBUG_FS
+static int _sde_danger_signal_status(struct seq_file *s,
+ bool danger_status)
+{
+ struct sde_kms *kms = (struct sde_kms *)s->private;
+ struct msm_drm_private *priv;
+ struct sde_danger_safe_status status;
+ int i;
+
+ if (!kms || !kms->dev || !kms->dev->dev_private || !kms->hw_mdp) {
+ SDE_ERROR("invalid arg(s)\n");
+ return 0;
+ }
+
+ priv = kms->dev->dev_private;
+ memset(&status, 0, sizeof(struct sde_danger_safe_status));
+
+ sde_power_resource_enable(&priv->phandle, kms->core_client, true);
+ if (danger_status) {
+ seq_puts(s, "\nDanger signal status:\n");
+ if (kms->hw_mdp->ops.get_danger_status)
+ kms->hw_mdp->ops.get_danger_status(kms->hw_mdp,
+ &status);
+ } else {
+ seq_puts(s, "\nSafe signal status:\n");
+ if (kms->hw_mdp->ops.get_danger_status)
+ kms->hw_mdp->ops.get_danger_status(kms->hw_mdp,
+ &status);
+ }
+ sde_power_resource_enable(&priv->phandle, kms->core_client, false);
+
+ seq_printf(s, "MDP : 0x%x\n", status.mdp);
+
+ for (i = SSPP_VIG0; i < SSPP_MAX; i++)
+ seq_printf(s, "SSPP%d : 0x%x \t", i - SSPP_VIG0,
+ status.sspp[i]);
+ seq_puts(s, "\n");
+
+ for (i = WB_0; i < WB_MAX; i++)
+ seq_printf(s, "WB%d : 0x%x \t", i - WB_0,
+ status.wb[i]);
+ seq_puts(s, "\n");
+
+ return 0;
+}
+
+#define DEFINE_SDE_DEBUGFS_SEQ_FOPS(__prefix) \
+static int __prefix ## _open(struct inode *inode, struct file *file) \
+{ \
+ return single_open(file, __prefix ## _show, inode->i_private); \
+} \
+static const struct file_operations __prefix ## _fops = { \
+ .owner = THIS_MODULE, \
+ .open = __prefix ## _open, \
+ .release = single_release, \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+}
+
+static int sde_debugfs_danger_stats_show(struct seq_file *s, void *v)
+{
+ return _sde_danger_signal_status(s, true);
+}
+DEFINE_SDE_DEBUGFS_SEQ_FOPS(sde_debugfs_danger_stats);
+
+static int sde_debugfs_safe_stats_show(struct seq_file *s, void *v)
+{
+ return _sde_danger_signal_status(s, false);
+}
+DEFINE_SDE_DEBUGFS_SEQ_FOPS(sde_debugfs_safe_stats);
+
+static void sde_debugfs_danger_destroy(struct sde_kms *sde_kms)
+{
+ debugfs_remove_recursive(sde_kms->debugfs_danger);
+ sde_kms->debugfs_danger = NULL;
+}
+
+static int sde_debugfs_danger_init(struct sde_kms *sde_kms,
+ struct dentry *parent)
+{
+ sde_kms->debugfs_danger = debugfs_create_dir("danger",
+ parent);
+ if (!sde_kms->debugfs_danger) {
+ SDE_ERROR("failed to create danger debugfs\n");
+ return -EINVAL;
+ }
+
+ debugfs_create_file("danger_status", 0644, sde_kms->debugfs_danger,
+ sde_kms, &sde_debugfs_danger_stats_fops);
+ debugfs_create_file("safe_status", 0644, sde_kms->debugfs_danger,
+ sde_kms, &sde_debugfs_safe_stats_fops);
+
+ return 0;
+}
+
static int _sde_debugfs_show_regset32(struct seq_file *s, void *data)
{
struct sde_debugfs_regset32 *regset;
@@ -197,6 +292,8 @@ static int _sde_debugfs_init(struct sde_kms *sde_kms)
if (!sde_kms->debugfs_debug)
SDE_ERROR("failed to create debugfs debug directory\n");
+ sde_debugfs_danger_init(sde_kms, sde_kms->debugfs_debug);
+
return 0;
}
@@ -204,12 +301,25 @@ static void _sde_debugfs_destroy(struct sde_kms *sde_kms)
{
/* don't need to NULL check debugfs_root */
if (sde_kms) {
+ sde_debugfs_danger_destroy(sde_kms);
debugfs_remove_recursive(sde_kms->debugfs_debug);
sde_kms->debugfs_debug = 0;
debugfs_remove_recursive(sde_kms->debugfs_root);
sde_kms->debugfs_root = 0;
}
}
+#else
+static void sde_debugfs_danger_destroy(struct sde_kms *sde_kms,
+ struct dentry *parent)
+{
+}
+
+static int sde_debugfs_danger_init(struct sde_kms *sde_kms,
+ struct dentry *parent)
+{
+ return 0;
+}
+#endif
static int sde_kms_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc)
{
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index ea2d6841d11c..572409bcc218 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -121,6 +121,7 @@ struct sde_kms {
/* directory entry for debugfs */
void *debugfs_root;
struct dentry *debugfs_debug;
+ struct dentry *debugfs_danger;
/* io/register spaces: */
void __iomem *mmio, *vbif[VBIF_MAX];
@@ -143,6 +144,8 @@ struct sde_kms {
void **dsi_displays;
int wb_display_count;
void **wb_displays;
+
+ bool has_danger_ctrl;
};
struct vsync_info {
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 63e19b3f0bea..08d746fb32d6 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -406,6 +406,39 @@ static void _sde_plane_set_qos_ctrl(struct drm_plane *plane,
&psde->pipe_qos_cfg);
}
+int sde_plane_danger_signal_ctrl(struct drm_plane *plane, bool enable)
+{
+ struct sde_plane *psde;
+ struct msm_drm_private *priv;
+ struct sde_kms *sde_kms;
+
+ if (!plane || !plane->dev) {
+ SDE_ERROR("invalid arguments\n");
+ return -EINVAL;
+ }
+
+ priv = plane->dev->dev_private;
+ if (!priv || !priv->kms) {
+ SDE_ERROR("invalid KMS reference\n");
+ return -EINVAL;
+ }
+
+ sde_kms = to_sde_kms(priv->kms);
+ psde = to_sde_plane(plane);
+
+ if (!psde->is_rt_pipe)
+ goto end;
+
+ sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true);
+
+ _sde_plane_set_qos_ctrl(plane, enable, SDE_PLANE_QOS_PANIC_CTRL);
+
+ sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
+
+end:
+ return 0;
+}
+
/**
* _sde_plane_set_ot_limit - set OT limit for the given plane
* @plane: Pointer to drm plane
@@ -2069,6 +2102,98 @@ enum sde_sspp sde_plane_pipe(struct drm_plane *plane)
return plane ? to_sde_plane(plane)->pipe : SSPP_NONE;
}
+static ssize_t _sde_plane_danger_read(struct file *file,
+ char __user *buff, size_t count, loff_t *ppos)
+{
+ struct sde_kms *kms = file->private_data;
+ struct sde_mdss_cfg *cfg = kms->catalog;
+ int len = 0;
+ char buf[40] = {'\0'};
+
+ if (!cfg)
+ return -ENODEV;
+
+ if (*ppos)
+ return 0; /* the end */
+
+ len = snprintf(buf, sizeof(buf), "%d\n", !kms->has_danger_ctrl);
+ if (len < 0 || len >= sizeof(buf))
+ return 0;
+
+ if ((count < sizeof(buf)) || copy_to_user(buff, buf, len))
+ return -EFAULT;
+
+ *ppos += len; /* increase offset */
+
+ return len;
+}
+
+static void _sde_plane_set_danger_state(struct sde_kms *kms, bool enable)
+{
+ struct drm_plane *plane;
+
+ drm_for_each_plane(plane, kms->dev) {
+ if (plane->fb && plane->state) {
+ sde_plane_danger_signal_ctrl(plane, enable);
+ SDE_DEBUG("plane:%d img:%dx%d ",
+ plane->base.id, plane->fb->width,
+ plane->fb->height);
+ SDE_DEBUG("src[%d,%d,%d,%d] dst[%d,%d,%d,%d]\n",
+ plane->state->src_x >> 16,
+ plane->state->src_y >> 16,
+ plane->state->src_w >> 16,
+ plane->state->src_h >> 16,
+ plane->state->crtc_x, plane->state->crtc_y,
+ plane->state->crtc_w, plane->state->crtc_h);
+ } else {
+ SDE_DEBUG("Inactive plane:%d\n", plane->base.id);
+ }
+ }
+}
+
+static ssize_t _sde_plane_danger_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct sde_kms *kms = file->private_data;
+ struct sde_mdss_cfg *cfg = kms->catalog;
+ int disable_panic;
+ char buf[10];
+
+ if (!cfg)
+ return -EFAULT;
+
+ if (count >= sizeof(buf))
+ return -EFAULT;
+
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ buf[count] = 0; /* end of string */
+
+ if (kstrtoint(buf, 0, &disable_panic))
+ return -EFAULT;
+
+ if (disable_panic) {
+ /* Disable panic signal for all active pipes */
+ SDE_DEBUG("Disabling danger:\n");
+ _sde_plane_set_danger_state(kms, false);
+ kms->has_danger_ctrl = false;
+ } else {
+ /* Enable panic signal for all active pipes */
+ SDE_DEBUG("Enabling danger:\n");
+ kms->has_danger_ctrl = true;
+ _sde_plane_set_danger_state(kms, true);
+ }
+
+ return count;
+}
+
+static const struct file_operations sde_plane_danger_enable = {
+ .open = simple_open,
+ .read = _sde_plane_danger_read,
+ .write = _sde_plane_danger_write,
+};
+
static void _sde_plane_init_debugfs(struct sde_plane *psde, struct sde_kms *kms)
{
const struct sde_sspp_sub_blks *sblk = 0;
@@ -2111,6 +2236,28 @@ static void _sde_plane_init_debugfs(struct sde_plane *psde, struct sde_kms *kms)
kms);
sde_debugfs_create_regset32("csc_blk", S_IRUGO,
psde->debugfs_root, &psde->debugfs_csc);
+
+ debugfs_create_u32("xin_id",
+ S_IRUGO,
+ psde->debugfs_root,
+ (u32 *) &cfg->xin_id);
+ debugfs_create_u32("clk_ctrl",
+ S_IRUGO,
+ psde->debugfs_root,
+ (u32 *) &cfg->clk_ctrl);
+ debugfs_create_x32("creq_vblank",
+ S_IRUGO | S_IWUSR,
+ psde->debugfs_root,
+ (u32 *) &sblk->creq_vblank);
+ debugfs_create_x32("danger_vblank",
+ S_IRUGO | S_IWUSR,
+ psde->debugfs_root,
+ (u32 *) &sblk->danger_vblank);
+
+ debugfs_create_file("disable_danger",
+ S_IRUGO | S_IWUSR,
+ psde->debugfs_root,
+ kms, &sde_plane_danger_enable);
}
}
}