[PATCH 10/10] drm: xlnx: zynqmp: Add debugfs
Hyun Kwon
hyun.kwon at xilinx.com
Fri Jan 5 02:05:59 UTC 2018
Debugfs can be used to exploit some specific setting. Main purpose
is for testing and debug diagnostic.
Signed-off-by: Tejas Upadhyay <tejasu at xilinx.com>
Signed-off-by: Hyun Kwon <hyun.kwon at 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
More information about the dri-devel
mailing list