[PATCH 10/10] drm: xlnx: zynqmp: Add debugfs
Hyun Kwon
hyunk at xilinx.com
Thu Jan 11 02:05:31 UTC 2018
Hi Daniel,
> -----Original Message-----
> From: Daniel Vetter [mailto:daniel.vetter at ffwll.ch] On Behalf Of Daniel
> Vetter
> Sent: Tuesday, January 09, 2018 1:54 AM
> To: Hyun Kwon <hyunk at xilinx.com>
> Cc: dri-devel at lists.freedesktop.org; devicetree at vger.kernel.org; Tejas
> Upadhyay <TEJASU at xilinx.com>; Michal Simek <michal.simek at xilinx.com>
> Subject: Re: [PATCH 10/10] drm: xlnx: zynqmp: Add debugfs
>
> On Thu, Jan 04, 2018 at 06:05:59PM -0800, Hyun Kwon wrote:
> > 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>
>
> Hm, not sure what's the use, it seems to just be for setting/getting
> your driver-private properties. Usually people use modetest and similar
> tools, but if there's demand for setting properties in debugfs (we already
> have all the infrastructure for dumping the entire kms state, see the
> various atomic_print_state callbacks) I think that should be done with
> generic drm code.
>
> I'd drop this patch for now (if there's no other reason for it).
Thanks for the input. It'd be helpful, for my own understanding, if this can be elaborated a little more. We are using standard tools as well as custom script/tool, but some specific properties / controls are hard to be executed with modetest only unless we change the entire set up / design between each run. The debugfs is used to run all (or as much as possible) properties in a single run, and from what I understand, that doesn't violate intended debugfs usage as long as it's not treated as a stable ABI. The intention is to help isolate issues and enhance the diagnostics. I agree, in the long term, this kind of stuff should be handled in generic way, but would it be still reasonable to keep it driver specific in the meantime?
Thanks,
-hyun
> -Daniel
>
> > ---
> > 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 at lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/dri-devel
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
More information about the dri-devel
mailing list