drm/msm/sde: add debufs node for vsync and underrun status

Add a debugfs node under encoder to update the vsync
and underrun status of all physical encoders.

Change-Id: I56198e7450709f393c878d44b6c467a2d5f8dd65
Signed-off-by: Dhaval Patel <pdhaval@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 1200ea7..535b7d6 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -11,6 +11,9 @@
  */
 
 #define pr_fmt(fmt)	"[drm:%s:%d] " fmt, __func__, __LINE__
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
 #include "msm_drv.h"
 #include "sde_kms.h"
 #include "drm_crtc.h"
@@ -73,6 +76,9 @@
  *				kickoff. Bit0 = phys_encs[0] etc.
  * @pending_kickoff_wq:		Wait queue commit thread to wait on phys_encs
  *				become ready for kickoff in IRQ contexts
+ * @debugfs_root:		Debug file system root file node
+ * @enc_lock:			Lock around physical encoder create/destroy and
+				access.
  */
 struct sde_encoder_virt {
 	struct drm_encoder base;
@@ -90,6 +96,9 @@
 
 	unsigned int pending_kickoff_mask;
 	wait_queue_head_t pending_kickoff_wq;
+
+	struct dentry *debugfs_root;
+	struct mutex enc_lock;
 };
 
 #define to_sde_encoder_virt(x) container_of(x, struct sde_encoder_virt, base)
@@ -222,7 +231,8 @@
 	sde_enc = to_sde_encoder_virt(drm_enc);
 	SDE_DEBUG_ENC(sde_enc, "\n");
 
-	for (i = 0; i < ARRAY_SIZE(sde_enc->phys_encs); i++) {
+	mutex_lock(&sde_enc->enc_lock);
+	for (i = 0; i < sde_enc->num_phys_encs; i++) {
 		struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
 
 		if (phys && phys->ops.destroy) {
@@ -232,13 +242,17 @@
 		}
 	}
 
-	if (sde_enc->num_phys_encs) {
+	if (sde_enc->num_phys_encs)
 		SDE_ERROR_ENC(sde_enc, "expected 0 num_phys_encs not %d\n",
 				sde_enc->num_phys_encs);
-	}
+	sde_enc->num_phys_encs = 0;
+	mutex_unlock(&sde_enc->enc_lock);
 
 	drm_encoder_cleanup(drm_enc);
 	bs_fini(sde_enc);
+	debugfs_remove_recursive(sde_enc->debugfs_root);
+	mutex_destroy(&sde_enc->enc_lock);
+
 	kfree(sde_enc);
 }
 
@@ -757,6 +771,85 @@
 	}
 }
 
+static int _sde_encoder_status_show(struct seq_file *s, void *data)
+{
+	struct sde_encoder_virt *sde_enc;
+	int i;
+
+	if (!s || !s->private)
+		return -EINVAL;
+
+	sde_enc = s->private;
+
+	mutex_lock(&sde_enc->enc_lock);
+	for (i = 0; i < sde_enc->num_phys_encs; i++) {
+		struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
+
+		if (!phys)
+			continue;
+
+		seq_printf(s, "intf:%d    vsync:%8d     underrun:%8d    ",
+				phys->intf_idx - INTF_0,
+				atomic_read(&phys->vsync_cnt),
+				atomic_read(&phys->underrun_cnt));
+
+		switch (phys->intf_mode) {
+		case INTF_MODE_VIDEO:
+			seq_puts(s, "mode: video\n");
+			break;
+		case INTF_MODE_CMD:
+			seq_puts(s, "mode: command\n");
+			break;
+		case INTF_MODE_WB_BLOCK:
+			seq_puts(s, "mode: wb block\n");
+			break;
+		case INTF_MODE_WB_LINE:
+			seq_puts(s, "mode: wb line\n");
+			break;
+		default:
+			seq_puts(s, "mode: ???\n");
+			break;
+		}
+	}
+	mutex_unlock(&sde_enc->enc_lock);
+
+	return 0;
+}
+
+static int _sde_encoder_debugfs_status_open(struct inode *inode,
+		struct file *file)
+{
+	return single_open(file, _sde_encoder_status_show, inode->i_private);
+}
+
+static void _sde_encoder_init_debugfs(struct drm_encoder *drm_enc,
+	struct sde_encoder_virt *sde_enc, struct sde_kms *sde_kms)
+{
+	static const struct file_operations debugfs_status_fops = {
+		.open =		_sde_encoder_debugfs_status_open,
+		.read =		seq_read,
+		.llseek =	seq_lseek,
+		.release =	single_release,
+	};
+	char name[SDE_NAME_SIZE];
+
+	if (!drm_enc || !sde_enc || !sde_kms) {
+		SDE_ERROR("invalid encoder or kms\n");
+		return;
+	}
+
+	snprintf(name, SDE_NAME_SIZE, "encoder%u", drm_enc->base.id);
+
+	/* create overall sub-directory for the encoder */
+	sde_enc->debugfs_root = debugfs_create_dir(name,
+					sde_debugfs_get_root(sde_kms));
+	if (sde_enc->debugfs_root) {
+		/* don't error check these */
+		debugfs_create_file("status", 0644,
+			sde_enc->debugfs_root, sde_enc, &debugfs_status_fops);
+	}
+}
+
 static int sde_encoder_virt_add_phys_encs(
 		u32 display_caps,
 		struct sde_encoder_virt *sde_enc,
@@ -886,6 +979,7 @@
 
 	SDE_DEBUG("dsi_info->num_of_h_tiles %d\n", disp_info->num_of_h_tiles);
 
+	mutex_lock(&sde_enc->enc_lock);
 	for (i = 0; i < disp_info->num_of_h_tiles && !ret; i++) {
 		/*
 		 * Left-most tile is at index 0, content is controller id
@@ -944,6 +1038,7 @@
 						"failed to add phys encs\n");
 		}
 	}
+	mutex_unlock(&sde_enc->enc_lock);
 
 
 	return ret;
@@ -965,6 +1060,7 @@
 		goto fail;
 	}
 
+	mutex_init(&sde_enc->enc_lock);
 	ret = sde_encoder_setup_display(sde_enc, sde_kms, disp_info,
 			&drm_enc_mode);
 	if (ret)
@@ -979,6 +1075,8 @@
 	sde_enc->pending_kickoff_mask = 0;
 	init_waitqueue_head(&sde_enc->pending_kickoff_wq);
 
+	_sde_encoder_init_debugfs(drm_enc, sde_enc, sde_kms);
+
 	SDE_DEBUG_ENC(sde_enc, "created\n");
 
 	return drm_enc;
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index e66f9df..d1ec5c0 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -76,6 +76,8 @@
 #define ktime_compare_safe(A, B) \
 	ktime_compare(ktime_sub((A), (B)), ktime_set(0, 0))
 
+#define SDE_NAME_SIZE  12
+
 /*
  * struct sde_irq_callback - IRQ callback handlers
  * @func: intr handler