All of lore.kernel.org
 help / color / mirror / Atom feed
From: Daniel Vetter <daniel@ffwll.ch>
To: Hyun Kwon <hyun.kwon@xilinx.com>
Cc: Tejas Upadhyay <tejasu@xilinx.com>,
	devicetree@vger.kernel.org,
	Michal Simek <michal.simek@xilinx.com>,
	dri-devel@lists.freedesktop.org
Subject: Re: [PATCH 10/10] drm: xlnx: zynqmp: Add debugfs
Date: Tue, 9 Jan 2018 10:54:25 +0100	[thread overview]
Message-ID: <20180109095425.GJ26573@phenom.ffwll.local> (raw)
In-Reply-To: <1515117959-18068-11-git-send-email-hyun.kwon@xilinx.com>

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@xilinx.com>
> Signed-off-by: Hyun Kwon <hyun.kwon@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).
-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@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

  reply	other threads:[~2018-01-09  9:54 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-01-05  2:05 [PATCH 00/10] Xilinx ZynqMP DisplayPort subsystem DRM KMS driver Hyun Kwon
     [not found] ` <1515117959-18068-1-git-send-email-hyun.kwon-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org>
2018-01-05  2:05   ` [PATCH 01/10] dt-bindings: display: xlnx: Add Xilinx kms bindings Hyun Kwon
     [not found]     ` <1515117959-18068-2-git-send-email-hyun.kwon-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org>
2018-01-09  4:00       ` Rob Herring
2018-01-11  2:04         ` Hyun Kwon
     [not found]           ` <BY1PR0201MB1000969ECDC38A62F68B7238D6160-QYJsKn8jqXK8fGmG9BO4UxrHTHEw16jenBOFsp37pqbUKgpGm//BTAC/G2K4zDHf@public.gmane.org>
2018-01-11 14:43             ` Rob Herring
     [not found]               ` <CAL_JsqJ_84qg=oJb=HzwgdP9T8osczNT-Eo+u5wjJfT3B8gAQQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2018-01-11 19:22                 ` Hyun Kwon
2018-01-05  2:05   ` [PATCH 02/10] drm: xlnx: Add xlnx crtc of Xilinx DRM KMS Hyun Kwon
2018-01-09  9:37     ` Daniel Vetter
     [not found]       ` <20180109093733.GG26573-dv86pmgwkMBes7Z6vYuT8azUEOm+Xw19@public.gmane.org>
2018-01-11  2:04         ` Hyun Kwon
2018-01-11  7:48           ` Daniel Vetter
2018-01-05  2:05   ` [PATCH 03/10] drm: xlnx: Add xlnx fb " Hyun Kwon
2018-01-09  9:35     ` Daniel Vetter
2018-01-11  2:04       ` Hyun Kwon
2018-01-05  2:05   ` [PATCH 04/10] drm: xlnx: Add xlnx gem " Hyun Kwon
2018-01-05  2:05   ` [PATCH 05/10] drm: xlnx: Xilinx DRM KMS driver Hyun Kwon
     [not found]     ` <1515117959-18068-6-git-send-email-hyun.kwon-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org>
2018-01-09  9:51       ` Daniel Vetter
2018-01-11  2:05         ` Hyun Kwon
2018-01-05  2:05   ` [PATCH 06/10] dt-bindings: display: xlnx: Add ZynqMP DP subsystem bindings Hyun Kwon
2018-01-09  4:07     ` Rob Herring
2018-01-11  2:06       ` Hyun Kwon
2018-01-05  2:05   ` [PATCH 07/10] drm: xlnx: DRM KMS driver for Xilinx ZynqMP DP subsystem display Hyun Kwon
     [not found]     ` <1515117959-18068-8-git-send-email-hyun.kwon-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org>
2018-01-09  9:46       ` Daniel Vetter
     [not found]         ` <20180109094652.GH26573-dv86pmgwkMBes7Z6vYuT8azUEOm+Xw19@public.gmane.org>
2018-01-11  2:04           ` Hyun Kwon
2018-01-05  2:05   ` [PATCH 08/10] drm: xlnx: DRM KMS driver for Xilinx ZynqMP DisplayPort Hyun Kwon
2018-01-05  2:05   ` [PATCH 09/10] drm: xlnx: ZynqMP DP subsystem DRM KMS driver Hyun Kwon
2018-01-05  2:05   ` [PATCH 10/10] drm: xlnx: zynqmp: Add debugfs Hyun Kwon
2018-01-09  9:54     ` Daniel Vetter [this message]
2018-01-11  2:05       ` Hyun Kwon
     [not found]         ` <BY1PR0201MB10001A1C38398BFBAEC56D39D6160-QYJsKn8jqXK8fGmG9BO4UxrHTHEw16jenBOFsp37pqbUKgpGm//BTAC/G2K4zDHf@public.gmane.org>
2018-01-11  8:06           ` Daniel Vetter
     [not found]             ` <20180111080605.GB13066-dv86pmgwkMBes7Z6vYuT8azUEOm+Xw19@public.gmane.org>
2018-01-11 16:57               ` Hyun Kwon
2018-01-09  9:56   ` [PATCH 00/10] Xilinx ZynqMP DisplayPort subsystem DRM KMS driver Daniel Vetter
     [not found]     ` <20180109095649.GK26573-dv86pmgwkMBes7Z6vYuT8azUEOm+Xw19@public.gmane.org>
2018-01-11  2:07       ` Hyun Kwon
     [not found]         ` <BY1PR0201MB10002CAFCC860052538BA14DD6160-QYJsKn8jqXK8fGmG9BO4UxrHTHEw16jenBOFsp37pqbUKgpGm//BTAC/G2K4zDHf@public.gmane.org>
2018-01-11  8:07           ` Daniel Vetter
     [not found]             ` <20180111080738.GC13066-dv86pmgwkMBes7Z6vYuT8azUEOm+Xw19@public.gmane.org>
2018-01-11  8:16               ` Michal Simek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180109095425.GJ26573@phenom.ffwll.local \
    --to=daniel@ffwll.ch \
    --cc=devicetree@vger.kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=hyun.kwon@xilinx.com \
    --cc=michal.simek@xilinx.com \
    --cc=tejasu@xilinx.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.