Debugfs can be used to exploit some specific setting. Main purpose
is for testing and debug diagnostic.

Signed-off-by: Tejas Upadhyay <tej...@xilinx.com>
Signed-off-by: Hyun Kwon <hyun.k...@xilinx.com>
---
 drivers/gpu/drm/xlnx/Kconfig       |  21 +++
 drivers/gpu/drm/xlnx/zynqmp_disp.c | 326 +++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/xlnx/zynqmp_dp.c   | 304 ++++++++++++++++++++++++++++++++++
 3 files changed, 651 insertions(+)

diff --git a/drivers/gpu/drm/xlnx/Kconfig b/drivers/gpu/drm/xlnx/Kconfig
index 7c5529c..befce0f 100644
--- a/drivers/gpu/drm/xlnx/Kconfig
+++ b/drivers/gpu/drm/xlnx/Kconfig
@@ -21,3 +21,24 @@ config DRM_ZYNQMP_DPSUB
          this option if you have a Xilinx ZynqMP SoC with DisplayPort
          subsystem. The driver provides the kernel mode setting
          functionlaities for ZynqMP DP subsystem.
+
+config DRM_ZYNQMP_DISP_DEBUG_FS
+       bool "ZynqMP Display debugfs"
+       depends on DEBUG_FS && DRM_ZYNQMP_DPSUB
+       select DRM_ZYNQMP_DP_DEBUG_FS
+       help
+         Enable the debugfs code for DP Sub driver. The debugfs code
+         enables debugging or testing related features. It exposes some
+         low level controls to the user space to help testing automation,
+         as well as can enable additional diagnostic or statistical
+         information.
+
+config DRM_ZYNQMP_DP_DEBUG_FS
+       bool "ZynqMP DP debugfs"
+       depends on DEBUG_FS && DRM_ZYNQMP_DPSUB
+       help
+         Enable the debugfs code for DP driver. The debugfs code
+         enables debugging or testing related features. It exposes some
+         low level controls to the user space to help testing automation,
+         as well as can enable additional diagnostic or statistical
+         information.
diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c 
b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index 68f829c..9fe6d49 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -17,6 +17,7 @@
 #include <drm/drm_plane_helper.h>
 
 #include <linux/clk.h>
+#include <linux/debugfs.h>
 #include <linux/device.h>
 #include <linux/dmaengine.h>
 #include <linux/interrupt.h>
@@ -508,6 +509,325 @@ static void zynqmp_disp_set(void __iomem *base, int 
offset, u32 set)
        zynqmp_disp_write(base, offset, zynqmp_disp_read(base, offset) | set);
 }
 
+#ifdef CONFIG_DRM_ZYNQMP_DISP_DEBUG_FS
+
+#define ZYNQMP_DISP_DEBUGFS_READ_MAX_SIZE      32UL
+#define ZYNQMP_DISP_DEBUGFS_MAX_BG_COLOR_VAL   0xFFF
+#define IN_RANGE(x, min, max) ({               \
+               typeof(x) _x = (x);             \
+               _x >= (min) && _x <= (max); })
+
+/* Match xilinx_dp_testcases vs dp_debugfs_reqs[] entry */
+enum zynqmp_disp_testcases {
+       DP_SUB_TC_BG_COLOR,
+       DP_SUB_TC_OUTPUT_FMT,
+       DP_SUB_TC_NONE
+};
+
+struct zynqmp_disp_debugfs {
+       enum zynqmp_disp_testcases testcase;
+       u16 r_value;
+       u16 g_value;
+       u16 b_value;
+       u32 output_fmt;
+       struct zynqmp_disp *zynqmp_disp;
+};
+
+static struct dentry *zynqmp_disp_debugfs_dir;
+struct zynqmp_disp_debugfs disp_debugfs;
+struct zynqmp_disp_debugfs_request {
+       const char *req;
+       enum zynqmp_disp_testcases tc;
+       ssize_t (*read_handler)(char **kern_buff);
+       ssize_t (*write_handler)(char **cmd);
+};
+
+static void
+zynqmp_disp_set_output_fmt(struct zynqmp_disp *disp, unsigned int id);
+static s64 zynqmp_disp_debugfs_argument_value(char *arg)
+{
+       s64 value;
+
+       if (!arg)
+               return -1;
+
+       if (!kstrtos64(arg, 0, &value))
+               return value;
+
+       return -1;
+}
+
+static ssize_t
+zynqmp_disp_debugfs_background_color_write(char **disp_test_arg)
+{
+       char *r_color, *g_color, *b_color;
+       s64 r_val, g_val, b_val;
+
+       r_color = strsep(disp_test_arg, " ");
+       g_color = strsep(disp_test_arg, " ");
+       b_color = strsep(disp_test_arg, " ");
+
+       /* char * to int conversion */
+       r_val = zynqmp_disp_debugfs_argument_value(r_color);
+       g_val = zynqmp_disp_debugfs_argument_value(g_color);
+       b_val = zynqmp_disp_debugfs_argument_value(b_color);
+
+       if (!(IN_RANGE(r_val, 0, ZYNQMP_DISP_DEBUGFS_MAX_BG_COLOR_VAL) &&
+             IN_RANGE(g_val, 0, ZYNQMP_DISP_DEBUGFS_MAX_BG_COLOR_VAL) &&
+             IN_RANGE(b_val, 0, ZYNQMP_DISP_DEBUGFS_MAX_BG_COLOR_VAL)))
+               return -EINVAL;
+
+       disp_debugfs.r_value = r_val;
+       disp_debugfs.g_value = g_val;
+       disp_debugfs.b_value = b_val;
+
+       disp_debugfs.testcase = DP_SUB_TC_BG_COLOR;
+
+       return 0;
+}
+
+static ssize_t
+zynqmp_disp_debugfs_output_display_format_write(char **disp_test_arg)
+{
+       char *output_format;
+       struct zynqmp_disp *disp = disp_debugfs.zynqmp_disp;
+
+       /* Read the value from a user value */
+       output_format = strsep(disp_test_arg, " ");
+       if (strncmp(output_format, "rgb", 3) == 0) {
+               disp_debugfs.output_fmt =
+                       ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_RGB;
+       } else if (strncmp(output_format, "ycbcr444", 8) == 0) {
+               disp_debugfs.output_fmt =
+                       ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR444;
+       } else if (strncmp(output_format, "ycbcr422", 8) == 0) {
+               disp_debugfs.output_fmt =
+                       ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR422;
+       } else if (strncmp(output_format, "yonly", 5) == 0) {
+               disp_debugfs.output_fmt =
+                       ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YONLY;
+       } else {
+               dev_err(disp->dev, "Invalid output format\n");
+               return -EINVAL;
+       }
+
+       disp_debugfs.testcase = DP_SUB_TC_OUTPUT_FMT;
+
+       return 0;
+}
+
+static ssize_t
+zynqmp_disp_debugfs_output_display_format_read(char **kern_buff)
+{
+       size_t out_str_len;
+
+       disp_debugfs.testcase = DP_SUB_TC_NONE;
+       disp_debugfs.output_fmt = 0;
+
+       out_str_len = strlen("Success");
+       out_str_len = min(ZYNQMP_DISP_DEBUGFS_READ_MAX_SIZE, out_str_len);
+       snprintf(*kern_buff, out_str_len, "%s", "Success");
+
+       return 0;
+}
+
+static ssize_t
+zynqmp_disp_debugfs_background_color_read(char **kern_buff)
+{
+       size_t out_str_len;
+
+       disp_debugfs.testcase = DP_SUB_TC_NONE;
+       disp_debugfs.r_value = 0;
+       disp_debugfs.g_value = 0;
+       disp_debugfs.b_value = 0;
+
+       out_str_len = strlen("Success");
+       out_str_len = min(ZYNQMP_DISP_DEBUGFS_READ_MAX_SIZE, out_str_len);
+       snprintf(*kern_buff, out_str_len, "%s", "Success");
+
+       return 0;
+}
+
+/* Match xilinx_dp_testcases vs dp_debugfs_reqs[] entry */
+struct zynqmp_disp_debugfs_request disp_debugfs_reqs[] = {
+       {"BACKGROUND_COLOR", DP_SUB_TC_BG_COLOR,
+               zynqmp_disp_debugfs_background_color_read,
+               zynqmp_disp_debugfs_background_color_write},
+       {"OUTPUT_DISPLAY_FORMAT", DP_SUB_TC_OUTPUT_FMT,
+               zynqmp_disp_debugfs_output_display_format_read,
+               zynqmp_disp_debugfs_output_display_format_write},
+};
+
+static ssize_t
+zynqmp_disp_debugfs_write(struct file *f, const char __user *buf,
+                         size_t size, loff_t *pos)
+{
+       char *kern_buff, *disp_test_req, *kern_buff_start;
+       int ret;
+       unsigned int i;
+
+       if (*pos != 0 || size <= 0)
+               return -EINVAL;
+
+       if (disp_debugfs.testcase != DP_SUB_TC_NONE)
+               return -EBUSY;
+
+       kern_buff = kzalloc(size, GFP_KERNEL);
+       if (!kern_buff)
+               return -ENOMEM;
+       kern_buff_start = kern_buff;
+
+       ret = strncpy_from_user(kern_buff, buf, size);
+       if (ret < 0) {
+               kfree(kern_buff_start);
+               return ret;
+       }
+
+       /* Read the testcase name and argument from a user request */
+       disp_test_req = strsep(&kern_buff, " ");
+
+       for (i = 0; i < ARRAY_SIZE(disp_debugfs_reqs); i++) {
+               if (!strcasecmp(disp_test_req, disp_debugfs_reqs[i].req))
+                       if (!disp_debugfs_reqs[i].write_handler(&kern_buff)) {
+                               kfree(kern_buff_start);
+                               return size;
+                       }
+       }
+       kfree(kern_buff_start);
+       return -EINVAL;
+}
+
+static ssize_t zynqmp_disp_debugfs_read(struct file *f, char __user *buf,
+                                       size_t size, loff_t *pos)
+{
+       char *kern_buff = NULL;
+       size_t kern_buff_len, out_str_len;
+       enum zynqmp_disp_testcases tc;
+       int ret;
+
+       if (size <= 0)
+               return -EINVAL;
+
+       if (*pos != 0)
+               return 0;
+
+       kern_buff = kzalloc(ZYNQMP_DISP_DEBUGFS_READ_MAX_SIZE, GFP_KERNEL);
+       if (!kern_buff) {
+               disp_debugfs.testcase = DP_SUB_TC_NONE;
+               return -ENOMEM;
+       }
+
+       tc = disp_debugfs.testcase;
+       if (tc == DP_SUB_TC_NONE) {
+               out_str_len = strlen("No testcase executed");
+               out_str_len = min(ZYNQMP_DISP_DEBUGFS_READ_MAX_SIZE,
+                                 out_str_len);
+               snprintf(kern_buff, out_str_len, "%s", "No testcase executed");
+       } else {
+               ret = disp_debugfs_reqs[tc].read_handler(&kern_buff);
+               if (ret) {
+                       kfree(kern_buff);
+                       return ret;
+               }
+       }
+
+       kern_buff_len = strlen(kern_buff);
+       size = min(size, kern_buff_len);
+
+       ret = copy_to_user(buf, kern_buff, size);
+
+       kfree(kern_buff);
+       if (ret)
+               return ret;
+
+       *pos = size + 1;
+       return size;
+}
+
+static const struct file_operations fops_zynqmp_disp_dbgfs = {
+       .owner = THIS_MODULE,
+       .read = zynqmp_disp_debugfs_read,
+       .write = zynqmp_disp_debugfs_write,
+};
+
+static int zynqmp_disp_debugfs_init(struct zynqmp_disp *disp)
+{
+       int err;
+       struct dentry *zynqmp_disp_debugfs_file;
+
+       disp_debugfs.testcase = DP_SUB_TC_NONE;
+       disp_debugfs.zynqmp_disp = disp;
+
+       zynqmp_disp_debugfs_dir = debugfs_create_dir("disp", NULL);
+       if (!zynqmp_disp_debugfs_dir) {
+               dev_err(disp->dev, "debugfs_create_dir failed\n");
+               return -ENODEV;
+       }
+
+       zynqmp_disp_debugfs_file =
+               debugfs_create_file("testcase", 0444,
+                                   zynqmp_disp_debugfs_dir, NULL,
+                                   &fops_zynqmp_disp_dbgfs);
+       if (!zynqmp_disp_debugfs_file) {
+               dev_err(disp->dev, "debugfs_create_file testcase failed\n");
+               err = -ENODEV;
+               goto err_dbgfs;
+       }
+       return 0;
+
+err_dbgfs:
+       debugfs_remove_recursive(zynqmp_disp_debugfs_dir);
+       zynqmp_disp_debugfs_dir = NULL;
+       return err;
+}
+
+static void zynqmp_disp_debugfs_exit(struct zynqmp_disp *disp)
+{
+       debugfs_remove_recursive(zynqmp_disp_debugfs_dir);
+       zynqmp_disp_debugfs_dir = NULL;
+}
+
+static void zynqmp_disp_debugfs_bg_color(struct zynqmp_disp *disp)
+{
+       if (disp_debugfs.testcase == DP_SUB_TC_BG_COLOR) {
+               zynqmp_disp_write(disp->blend.base,
+                                 ZYNQMP_DISP_V_BLEND_BG_CLR_0,
+                                 disp_debugfs.r_value);
+               zynqmp_disp_write(disp->blend.base,
+                                 ZYNQMP_DISP_V_BLEND_BG_CLR_1,
+                                 disp_debugfs.g_value);
+               zynqmp_disp_write(disp->blend.base,
+                                 ZYNQMP_DISP_V_BLEND_BG_CLR_2,
+                                 disp_debugfs.b_value);
+       }
+}
+
+static void
+zynqmp_disp_set_debugfs_output_fmt(struct zynqmp_disp *disp)
+{
+       if (disp_debugfs.testcase == DP_SUB_TC_OUTPUT_FMT)
+               zynqmp_disp_set_output_fmt(disp, disp_debugfs.output_fmt);
+}
+#else
+static void zynqmp_disp_debugfs_exit(struct zynqmp_disp *disp)
+{
+}
+
+static int zynqmp_disp_debugfs_init(struct zynqmp_disp *disp)
+{
+       return 0;
+}
+
+static void zynqmp_disp_debugfs_bg_color(struct zynqmp_disp *disp)
+{
+}
+
+static void
+zynqmp_disp_set_debugfs_output_fmt(struct zynqmp_disp *disp)
+{
+}
+#endif /* CONFIG_DP_DEBUG_FS */
+
 /*
  * Clock functions
  */
@@ -597,6 +917,8 @@ zynqmp_disp_blend_set_output_fmt(struct zynqmp_disp_blend 
*blend, u32 fmt)
        u32 *offsets;
        u32 offset, i;
 
+       if (fmt == ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR422)
+               fmt |= ZYNQMP_DISP_V_BLEND_OUTPUT_EN_DOWNSAMPLE;
        zynqmp_disp_write(blend->base, ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT, fmt);
        if (fmt == ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_RGB) {
                coeffs = reset_coeffs;
@@ -1941,6 +2263,7 @@ static void zynqmp_disp_set_bg_color(struct zynqmp_disp 
*disp,
                                     u32 c0, u32 c1, u32 c2)
 {
        zynqmp_disp_blend_set_bg_color(&disp->blend, c0, c1, c2);
+       zynqmp_disp_debugfs_bg_color(disp);
 }
 
 /**
@@ -2572,6 +2895,7 @@ zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc,
                return;
        }
        zynqmp_disp_set_output_fmt(disp, disp->color);
+       zynqmp_disp_set_debugfs_output_fmt(disp);
        zynqmp_disp_set_bg_color(disp, disp->bg_c0, disp->bg_c1, disp->bg_c2);
        zynqmp_disp_enable(disp);
        /* Delay of 3 vblank intervals for timing gen to be stable */
@@ -2911,6 +3235,7 @@ int zynqmp_disp_probe(struct platform_device *pdev)
        ret = zynqmp_disp_layer_create(disp);
        if (ret)
                goto error_aclk;
+       zynqmp_disp_debugfs_init(disp);
 
        return 0;
 
@@ -2924,6 +3249,7 @@ int zynqmp_disp_remove(struct platform_device *pdev)
        struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev);
        struct zynqmp_disp *disp = dpsub->disp;
 
+       zynqmp_disp_debugfs_exit(disp);
        zynqmp_disp_layer_destroy(disp);
        if (disp->audclk)
                zynqmp_disp_clk_disable(disp->audclk, &disp->audclk_en);
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index ce3c7c5..66fbad0 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -16,6 +16,7 @@
 #include <drm/drm_dp_helper.h>
 #include <drm/drm_of.h>
 
+#include <linux/debugfs.h>
 #include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/module.h>
@@ -371,6 +372,306 @@ static void zynqmp_dp_set(void __iomem *base, int offset, 
u32 set)
 }
 
 /*
+ * Debugfs functions
+ */
+
+#ifdef CONFIG_DRM_ZYNQMP_DP_DEBUG_FS
+
+#define ZYNQMP_DP_DEBUGFS_READ_MAX_SIZE        32UL
+#define ZYNQMP_DP_DEBUGFS_UINT8_MAX_STR        "255"
+#define IN_RANGE(x, min, max) ({               \
+               typeof(x) _x = (x);             \
+               _x >= (min) && _x <= (max); })
+
+/* Match zynqmp_dp_testcases vs debugfs_reqs[] entry */
+enum zynqmp_dp_testcases {
+       DP_TC_LINK_RATE,
+       DP_TC_LANE_COUNT,
+       DP_TC_OUTPUT_FMT,
+       DP_TC_NONE
+};
+
+struct zynqmp_dp_debugfs {
+       enum zynqmp_dp_testcases testcase;
+       u8 link_rate;
+       u8 lane_cnt;
+       u8 old_output_fmt;
+       struct zynqmp_dp *dp;
+};
+
+static struct dentry *zynqmp_dp_debugfs_dir;
+static struct zynqmp_dp_debugfs dp_debugfs;
+struct zynqmp_dp_debugfs_request {
+       const char *req;
+       enum zynqmp_dp_testcases tc;
+       ssize_t (*read_handler)(char **kern_buff);
+       ssize_t (*write_handler)(char **cmd);
+};
+
+static s64 zynqmp_dp_debugfs_argument_value(char *arg)
+{
+       s64 value;
+
+       if (!arg)
+               return -1;
+
+       if (!kstrtos64(arg, 0, &value))
+               return value;
+
+       return -1;
+}
+
+static ssize_t zynqmp_dp_debugfs_max_linkrate_write(char **dp_test_arg)
+{
+       char *link_rate_arg;
+       s64 link_rate;
+
+       link_rate_arg = strsep(dp_test_arg, " ");
+       link_rate = zynqmp_dp_debugfs_argument_value(link_rate_arg);
+       if (link_rate < 0 || (link_rate != DP_HIGH_BIT_RATE2 &&
+                             link_rate != DP_HIGH_BIT_RATE &&
+                             link_rate != DP_REDUCED_BIT_RATE))
+               return -EINVAL;
+
+       dp_debugfs.link_rate = drm_dp_link_rate_to_bw_code(link_rate);
+       dp_debugfs.testcase = DP_TC_LINK_RATE;
+
+       return 0;
+}
+
+static ssize_t zynqmp_dp_debugfs_max_lanecnt_write(char **dp_test_arg)
+{
+       char *lane_cnt_arg;
+       s64 lane_count;
+
+       lane_cnt_arg = strsep(dp_test_arg, " ");
+       lane_count = zynqmp_dp_debugfs_argument_value(lane_cnt_arg);
+       if (lane_count < 0 || !IN_RANGE(lane_count, 1,
+                                       ZYNQMP_DP_MAX_LANES))
+               return -EINVAL;
+
+       dp_debugfs.lane_cnt = lane_count;
+       dp_debugfs.testcase = DP_TC_LANE_COUNT;
+
+       return 0;
+}
+
+static ssize_t zynqmp_dp_debugfs_max_linkrate_read(char **kern_buff)
+{
+       struct zynqmp_dp *dp = dp_debugfs.dp;
+       size_t output_str_len;
+       u8 dpcd_link_bw;
+       int ret;
+
+       dp_debugfs.testcase = DP_TC_NONE;
+       dp_debugfs.link_rate = 0;
+
+       /* Getting Sink Side Link Rate */
+       ret = drm_dp_dpcd_readb(&dp->aux, DP_LINK_BW_SET, &dpcd_link_bw);
+       if (ret < 0) {
+               dev_err(dp->dev, "Failed to read link rate via AUX.\n");
+               kfree(*kern_buff);
+               return ret;
+       }
+
+       output_str_len = strlen(ZYNQMP_DP_DEBUGFS_UINT8_MAX_STR);
+       output_str_len = min(ZYNQMP_DP_DEBUGFS_READ_MAX_SIZE, output_str_len);
+       snprintf(*kern_buff, output_str_len, "%u", dpcd_link_bw);
+
+       return 0;
+}
+
+static ssize_t zynqmp_dp_debugfs_max_lanecnt_read(char **kern_buff)
+{
+       struct zynqmp_dp *dp = dp_debugfs.dp;
+       size_t output_str_len;
+       u8 dpcd_lane_cnt;
+       int ret;
+
+       dp_debugfs.testcase = DP_TC_NONE;
+       dp_debugfs.lane_cnt = 0;
+
+       /* Getting Sink Side Lane Count */
+       ret = drm_dp_dpcd_readb(&dp->aux, DP_LANE_COUNT_SET, &dpcd_lane_cnt);
+       if (ret < 0) {
+               dev_err(dp->dev, "Failed to read link rate via AUX.\n");
+               kfree(*kern_buff);
+               return ret;
+       }
+
+       dpcd_lane_cnt &= DP_LANE_COUNT_MASK;
+       output_str_len = strlen(ZYNQMP_DP_DEBUGFS_UINT8_MAX_STR);
+       output_str_len = min(ZYNQMP_DP_DEBUGFS_READ_MAX_SIZE, output_str_len);
+       snprintf(*kern_buff, output_str_len, "%u", dpcd_lane_cnt);
+
+       return 0;
+}
+
+/* Match zynqmp_dp_testcases vs dp_debugfs_reqs[] entry */
+static struct zynqmp_dp_debugfs_request debugfs_reqs[] = {
+       {"LINK_RATE", DP_TC_LINK_RATE,
+                       zynqmp_dp_debugfs_max_linkrate_read,
+                       zynqmp_dp_debugfs_max_linkrate_write},
+       {"LANE_COUNT", DP_TC_LANE_COUNT,
+                       zynqmp_dp_debugfs_max_lanecnt_read,
+                       zynqmp_dp_debugfs_max_lanecnt_write},
+};
+
+static ssize_t zynqmp_dp_debugfs_read(struct file *f, char __user *buf,
+                                     size_t size, loff_t *pos)
+{
+       char *kern_buff = NULL;
+       size_t kern_buff_len, out_str_len;
+       enum zynqmp_dp_testcases tc;
+       int ret;
+
+       if (size <= 0)
+               return -EINVAL;
+
+       if (*pos != 0)
+               return 0;
+
+       kern_buff = kzalloc(ZYNQMP_DP_DEBUGFS_READ_MAX_SIZE, GFP_KERNEL);
+       if (!kern_buff) {
+               dp_debugfs.testcase = DP_TC_NONE;
+               return -ENOMEM;
+       }
+
+       tc = dp_debugfs.testcase;
+       if (tc == DP_TC_NONE) {
+               out_str_len = strlen("No testcase executed");
+               out_str_len = min(ZYNQMP_DP_DEBUGFS_READ_MAX_SIZE, out_str_len);
+               snprintf(kern_buff, out_str_len, "%s", "No testcase executed");
+       } else {
+               ret = debugfs_reqs[tc].read_handler(&kern_buff);
+               if (ret) {
+                       kfree(kern_buff);
+                       return ret;
+               }
+       }
+
+       kern_buff_len = strlen(kern_buff);
+       size = min(size, kern_buff_len);
+
+       ret = copy_to_user(buf, kern_buff, size);
+
+       kfree(kern_buff);
+       if (ret)
+               return ret;
+
+       *pos = size + 1;
+       return size;
+}
+
+static ssize_t
+zynqmp_dp_debugfs_write(struct file *f, const char __user *buf,
+                       size_t size, loff_t *pos)
+{
+       char *kern_buff, *kern_buff_start;
+       char *dp_test_req;
+       int ret;
+       int i;
+
+       if (*pos != 0 || size <= 0)
+               return -EINVAL;
+
+       if (dp_debugfs.testcase != DP_TC_NONE)
+               return -EBUSY;
+
+       kern_buff = kzalloc(size, GFP_KERNEL);
+       if (!kern_buff)
+               return -ENOMEM;
+       kern_buff_start = kern_buff;
+
+       ret = strncpy_from_user(kern_buff, buf, size);
+       if (ret < 0) {
+               kfree(kern_buff_start);
+               return ret;
+       }
+
+       /* Read the testcase name and argument from a user request */
+       dp_test_req = strsep(&kern_buff, " ");
+
+       for (i = 0; i < ARRAY_SIZE(debugfs_reqs); i++) {
+               if (!strcasecmp(dp_test_req, debugfs_reqs[i].req))
+                       if (!debugfs_reqs[i].write_handler(&kern_buff)) {
+                               kfree(kern_buff_start);
+                               return size;
+                       }
+       }
+
+       kfree(kern_buff_start);
+       return -EINVAL;
+}
+
+static const struct file_operations fops_zynqmp_dp_dbgfs = {
+       .owner = THIS_MODULE,
+       .read = zynqmp_dp_debugfs_read,
+       .write = zynqmp_dp_debugfs_write,
+};
+
+static int zynqmp_dp_debugfs_init(struct zynqmp_dp *dp)
+{
+       int err;
+       struct dentry *zynqmp_dp_debugfs_file;
+
+       dp_debugfs.testcase = DP_TC_NONE;
+       dp_debugfs.dp = dp;
+
+       zynqmp_dp_debugfs_dir = debugfs_create_dir("dp", NULL);
+       if (!zynqmp_dp_debugfs_dir) {
+               dev_err(dp->dev, "debugfs_create_dir failed\n");
+               return -ENODEV;
+       }
+
+       zynqmp_dp_debugfs_file =
+               debugfs_create_file("testcase", 0444, zynqmp_dp_debugfs_dir,
+                                   NULL, &fops_zynqmp_dp_dbgfs);
+       if (!zynqmp_dp_debugfs_file) {
+               dev_err(dp->dev, "debugfs_create_file testcase failed\n");
+               err = -ENODEV;
+               goto err_dbgfs;
+       }
+       return 0;
+
+err_dbgfs:
+       debugfs_remove_recursive(zynqmp_dp_debugfs_dir);
+       zynqmp_dp_debugfs_dir = NULL;
+       return err;
+}
+
+static void zynqmp_dp_debugfs_exit(struct zynqmp_dp *dp)
+{
+       debugfs_remove_recursive(zynqmp_dp_debugfs_dir);
+       zynqmp_dp_debugfs_dir = NULL;
+}
+
+static void zynqmp_dp_debugfs_mode_config(struct zynqmp_dp *dp)
+{
+       dp->mode.bw_code =
+               dp_debugfs.link_rate ? dp_debugfs.link_rate : dp->mode.bw_code;
+       dp->mode.lane_cnt =
+               dp_debugfs.lane_cnt ? dp_debugfs.lane_cnt : dp->mode.lane_cnt;
+}
+
+#else /* DRM_ZYNQMP_DP_DEBUG_FS */
+
+static int zynqmp_dp_debugfs_init(struct zynqmp_dp *dp)
+{
+       return 0;
+}
+
+static void zynqmp_dp_debugfs_exit(struct zynqmp_dp *dp)
+{
+}
+
+static void zynqmp_dp_debugfs_mode_config(struct zynqmp_dp *dp)
+{
+}
+
+#endif /* DRM_ZYNQMP_DP_DEBUG_FS */
+
+/*
  * Internal functions: used by zynqmp_disp.c
  */
 
@@ -597,6 +898,7 @@ static int zynqmp_dp_mode_configure(struct zynqmp_dp *dp, 
int pclock,
                        dp->mode.bw_code = bws[i];
                        dp->mode.lane_cnt = lane_cnt;
                        dp->mode.pclock = pclock;
+                       zynqmp_dp_debugfs_mode_config(dp);
                        return dp->mode.bw_code;
                }
        }
@@ -1840,6 +2142,7 @@ int zynqmp_dp_probe(struct platform_device *pdev)
        dpsub = platform_get_drvdata(pdev);
        dpsub->dp = dp;
        dp->dpsub = dpsub;
+       zynqmp_dp_debugfs_init(dp);
 
        return 0;
 
@@ -1855,6 +2158,7 @@ int zynqmp_dp_remove(struct platform_device *pdev)
        struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev);
        struct zynqmp_dp *dp = dpsub->dp;
 
+       zynqmp_dp_debugfs_exit(dp);
        zynqmp_dp_write(dp->iomem, ZYNQMP_DP_TX_ENABLE, 0);
        drm_dp_aux_unregister(&dp->aux);
        zynqmp_dp_exit_phy(dp);
-- 
2.7.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to