Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3188c322 authored by Ajay Singh Parmar's avatar Ajay Singh Parmar Committed by Govinda Rajulu Chenna
Browse files

drm/msm/dp: add support for simulation



Add debugfs node to enable/disable simulation mode.
Avoid AUX communication in simulation mode and override
it with debugfs data. This can be used for a quick
DP validation by triggering hot plug using debugfs nodes.

Change-Id: Id6a5112a4565feb15decf36b346c8cfd9783fd6f
Signed-off-by: default avatarAjay Singh Parmar <aparmar@codeaurora.org>
parent 2ec51c41
Loading
Loading
Loading
Loading
+157 −26
Original line number Diff line number Diff line
/*
 * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
 * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -29,22 +29,25 @@ struct dp_aux_private {
	struct dp_aux dp_aux;
	struct dp_catalog_aux *catalog;
	struct dp_aux_cfg *cfg;

	struct mutex mutex;
	struct completion comp;
	struct drm_dp_aux drm_aux;

	u32 aux_error_num;
	u32 retry_cnt;
	bool cmd_busy;
	bool native;
	bool read;
	bool no_send_addr;
	bool no_send_stop;

	u32 offset;
	u32 segment;
	u32 aux_error_num;
	u32 retry_cnt;

	atomic_t aborted;

	struct drm_dp_aux drm_aux;
	u8 *dpcd;
	u8 *edid;
};

static char *dp_aux_get_error(u32 aux_error)
@@ -320,6 +323,7 @@ static void dp_aux_update_offset_and_segment(struct dp_aux_private *aux,
 *
 * @aux: DP AUX private structure
 * @input_msg: input message from DRM upstream APIs
 * @send_seg: send the seg to sink
 *
 * return: void
 *
@@ -327,7 +331,7 @@ static void dp_aux_update_offset_and_segment(struct dp_aux_private *aux,
 * sinks that do not handle the i2c middle-of-transaction flag correctly.
 */
static void dp_aux_transfer_helper(struct dp_aux_private *aux,
		struct drm_dp_aux_msg *input_msg)
		struct drm_dp_aux_msg *input_msg, bool send_seg)
{
	struct drm_dp_aux_msg helper_msg;
	u32 const message_size = 0x10;
@@ -346,7 +350,7 @@ static void dp_aux_transfer_helper(struct dp_aux_private *aux,
	 * duplicate AUX transactions related to this while reading the
	 * first 16 bytes of each block.
	 */
	if (!(aux->offset % edid_block_length))
	if (!(aux->offset % edid_block_length) || !send_seg)
		goto end;

	aux->read = false;
@@ -388,26 +392,16 @@ static void dp_aux_transfer_helper(struct dp_aux_private *aux,
		aux->segment = 0x0; /* reset segment at end of block */
}

/*
 * This function does the real job to process an AUX transaction.
 * It will call aux_reset() function to reset the AUX channel,
 * if the waiting is timeout.
 */
static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
		struct drm_dp_aux_msg *msg)
static int dp_aux_transfer_ready(struct dp_aux_private *aux,
		struct drm_dp_aux_msg *msg, bool send_seg)
{
	ssize_t ret;
	int ret = 0;
	int const aux_cmd_native_max = 16;
	int const aux_cmd_i2c_max = 128;
	int const retry_count = 5;
	struct dp_aux_private *aux = container_of(drm_aux,
		struct dp_aux_private, drm_aux);

	mutex_lock(&aux->mutex);

	if (atomic_read(&aux->aborted)) {
		ret = -ETIMEDOUT;
		goto unlock_exit;
		goto error;
	}

	aux->native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
@@ -416,8 +410,7 @@ static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
	if ((msg->size == 0) || (msg->buffer == NULL)) {
		msg->reply = aux->native ?
			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
		ret = msg->size;
		goto unlock_exit;
		goto error;
	}

	/* msg sanity check */
@@ -426,14 +419,14 @@ static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
		pr_err("%s: invalid msg: size(%zu), request(%x)\n",
			__func__, msg->size, msg->request);
		ret = -EINVAL;
		goto unlock_exit;
		goto error;
	}

	dp_aux_update_offset_and_segment(aux, msg);
	dp_aux_transfer_helper(aux, msg);

	dp_aux_transfer_helper(aux, msg, send_seg);

	aux->read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
	aux->cmd_busy = true;

	if (aux->read) {
		aux->no_send_addr = true;
@@ -443,6 +436,98 @@ static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
		aux->no_send_stop = true;
	}

	aux->cmd_busy = true;
error:
	return ret;
}

static ssize_t dp_aux_transfer_debug(struct drm_dp_aux *drm_aux,
		struct drm_dp_aux_msg *msg)
{
	u8 buf[SZ_64];
	u32 timeout;
	ssize_t ret;
	struct dp_aux_private *aux = container_of(drm_aux,
		struct dp_aux_private, drm_aux);

	ret = dp_aux_transfer_ready(aux, msg, false);
	if (ret)
		goto end;

	aux->aux_error_num = DP_AUX_ERR_NONE;

	if (aux->native) {
		if (aux->read && ((msg->address + msg->size) < SZ_1K)) {
			aux->dp_aux.reg = msg->address;

			reinit_completion(&aux->comp);
			timeout = wait_for_completion_timeout(&aux->comp, HZ);
			if (!timeout)
				pr_err("aux timeout for 0x%x\n", msg->address);

			aux->dp_aux.reg = 0xFFFF;

			memcpy(msg->buffer, aux->dpcd + msg->address,
				msg->size);
			aux->aux_error_num = DP_AUX_ERR_NONE;
		} else {
			memset(msg->buffer, 0, msg->size);
		}
	} else {
		if (aux->read && msg->address == 0x50) {
			memcpy(msg->buffer,
				aux->edid + aux->offset - 16,
				msg->size);
		}
	}

	if (aux->aux_error_num == DP_AUX_ERR_NONE) {
		snprintf(buf, SZ_64, "[drm-dp] dbg: %5s %5s %5xh(%2zu): ",
			aux->native ? "NATIVE" : "I2C",
			aux->read ? "READ" : "WRITE",
			msg->address, msg->size);

		print_hex_dump(KERN_DEBUG, buf,
			DUMP_PREFIX_NONE, 8, 1, msg->buffer, msg->size, false);

		msg->reply = aux->native ?
			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
	} else {
		/* Reply defer to retry */
		msg->reply = aux->native ?
			DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER;
	}

	ret = msg->size;
end:
	return ret;
}

/*
 * This function does the real job to process an AUX transaction.
 * It will call aux_reset() function to reset the AUX channel,
 * if the waiting is timeout.
 */
static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
		struct drm_dp_aux_msg *msg)
{
	u8 buf[SZ_64];
	ssize_t ret;
	int const retry_count = 5;
	struct dp_aux_private *aux = container_of(drm_aux,
		struct dp_aux_private, drm_aux);

	mutex_lock(&aux->mutex);

	ret = dp_aux_transfer_ready(aux, msg, true);
	if (ret)
		goto unlock_exit;

	if (!aux->cmd_busy) {
		ret = msg->size;
		goto unlock_exit;
	}

	ret = dp_aux_cmd_fifo_tx(aux, msg);
	if ((ret < 0) && aux->native && !atomic_read(&aux->aborted)) {
		aux->retry_cnt++;
@@ -459,6 +544,14 @@ static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
		if (aux->read)
			dp_aux_cmd_fifo_rx(aux, msg);

		snprintf(buf, SZ_64, "[drm-dp] %5s %5s %5xh(%2zu): ",
			aux->native ? "NATIVE" : "I2C",
			aux->read ? "READ" : "WRITE",
			msg->address, msg->size);

		print_hex_dump(KERN_DEBUG, buf,
			DUMP_PREFIX_NONE, 8, 1, msg->buffer, msg->size, false);

		msg->reply = aux->native ?
			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
	} else {
@@ -558,6 +651,41 @@ static void dp_aux_deregister(struct dp_aux *dp_aux)
	drm_dp_aux_unregister(&aux->drm_aux);
}

static void dp_aux_dpcd_updated(struct dp_aux *dp_aux)
{
	struct dp_aux_private *aux;

	if (!dp_aux) {
		pr_err("invalid input\n");
		return;
	}

	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);

	complete(&aux->comp);
}

static void dp_aux_set_sim_mode(struct dp_aux *dp_aux, bool en,
		u8 *edid, u8 *dpcd)
{
	struct dp_aux_private *aux;

	if (!dp_aux) {
		pr_err("invalid input\n");
		return;
	}

	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);

	aux->edid = edid;
	aux->dpcd = dpcd;

	if (en)
		aux->drm_aux.transfer = dp_aux_transfer_debug;
	else
		aux->drm_aux.transfer = dp_aux_transfer;
}

struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
		struct dp_aux_cfg *aux_cfg)
{
@@ -586,6 +714,7 @@ struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
	aux->cfg = aux_cfg;
	dp_aux = &aux->dp_aux;
	aux->retry_cnt = 0;
	aux->dp_aux.reg = 0xFFFF;

	dp_aux->isr     = dp_aux_isr;
	dp_aux->init    = dp_aux_init;
@@ -594,6 +723,8 @@ struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
	dp_aux->drm_aux_deregister = dp_aux_deregister;
	dp_aux->reconfig = dp_aux_reconfig;
	dp_aux->abort = dp_aux_abort_transaction;
	dp_aux->dpcd_updated = dp_aux_dpcd_updated;
	dp_aux->set_sim_mode = dp_aux_set_sim_mode;

	return dp_aux;
error:
+19 −1
Original line number Diff line number Diff line
/*
 * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
 * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -18,6 +18,19 @@
#include "dp_catalog.h"
#include "drm_dp_helper.h"

#define DP_STATE_NOTIFICATION_SENT          BIT(0)
#define DP_STATE_TRAIN_1_STARTED            BIT(1)
#define DP_STATE_TRAIN_1_SUCCEEDED          BIT(2)
#define DP_STATE_TRAIN_1_FAILED             BIT(3)
#define DP_STATE_TRAIN_2_STARTED            BIT(4)
#define DP_STATE_TRAIN_2_SUCCEEDED          BIT(5)
#define DP_STATE_TRAIN_2_FAILED             BIT(6)
#define DP_STATE_CTRL_POWERED_ON            BIT(7)
#define DP_STATE_CTRL_POWERED_OFF           BIT(8)
#define DP_STATE_LINK_MAINTENANCE_STARTED   BIT(9)
#define DP_STATE_LINK_MAINTENANCE_COMPLETED BIT(10)
#define DP_STATE_LINK_MAINTENANCE_FAILED    BIT(11)

enum dp_aux_error {
	DP_AUX_ERR_NONE	= 0,
	DP_AUX_ERR_ADDR	= -1,
@@ -29,6 +42,9 @@ enum dp_aux_error {
};

struct dp_aux {
	u32 reg;
	u32 state;

	struct drm_dp_aux *drm_aux;
	int (*drm_aux_register)(struct dp_aux *aux);
	void (*drm_aux_deregister)(struct dp_aux *aux);
@@ -37,6 +53,8 @@ struct dp_aux {
	void (*deinit)(struct dp_aux *aux);
	void (*reconfig)(struct dp_aux *aux);
	void (*abort)(struct dp_aux *aux);
	void (*dpcd_updated)(struct dp_aux *aux);
	void (*set_sim_mode)(struct dp_aux *aux, bool en, u8 *edid, u8 *dpcd);
};

struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
+38 −7
Original line number Diff line number Diff line
@@ -820,6 +820,10 @@ static int dp_ctrl_link_train_1(struct dp_ctrl_private *ctrl)
	u8 link_status[DP_LINK_STATUS_SIZE];
	int const maximum_retries = 5;

	ctrl->aux->state &= ~DP_STATE_TRAIN_1_FAILED;
	ctrl->aux->state &= ~DP_STATE_TRAIN_1_SUCCEEDED;
	ctrl->aux->state |= DP_STATE_TRAIN_1_STARTED;

	dp_ctrl_state_ctrl(ctrl, 0);
	/* Make sure to clear the current pattern before starting a new one */
	wmb();
@@ -829,13 +833,13 @@ static int dp_ctrl_link_train_1(struct dp_ctrl_private *ctrl)
		DP_LINK_SCRAMBLING_DISABLE); /* train_1 */
	if (ret <= 0) {
		ret = -EINVAL;
		return ret;
		goto end;
	}

	ret = dp_ctrl_update_vx_px(ctrl);
	if (ret <= 0) {
		ret = -EINVAL;
		return ret;
		goto end;
	}

	tries = 0;
@@ -879,6 +883,13 @@ static int dp_ctrl_link_train_1(struct dp_ctrl_private *ctrl)
			break;
		}
	}
end:
	ctrl->aux->state &= ~DP_STATE_TRAIN_1_STARTED;

	if (ret)
		ctrl->aux->state |= DP_STATE_TRAIN_1_FAILED;
	else
		ctrl->aux->state |= DP_STATE_TRAIN_1_SUCCEEDED;

	return ret;
}
@@ -922,6 +933,10 @@ static int dp_ctrl_link_training_2(struct dp_ctrl_private *ctrl)
	int const maximum_retries = 5;
	u8 link_status[DP_LINK_STATUS_SIZE];

	ctrl->aux->state &= ~DP_STATE_TRAIN_2_FAILED;
	ctrl->aux->state &= ~DP_STATE_TRAIN_2_SUCCEEDED;
	ctrl->aux->state |= DP_STATE_TRAIN_2_STARTED;

	dp_ctrl_state_ctrl(ctrl, 0);
	/* Make sure to clear the current pattern before starting a new one */
	wmb();
@@ -934,14 +949,14 @@ static int dp_ctrl_link_training_2(struct dp_ctrl_private *ctrl)
	ret = dp_ctrl_update_vx_px(ctrl);
	if (ret <= 0) {
		ret = -EINVAL;
		return ret;
		goto end;
	}
	ctrl->catalog->set_pattern(ctrl->catalog, pattern);
	ret = dp_ctrl_train_pattern_set(ctrl,
		pattern | DP_RECOVERED_CLOCK_OUT_EN);
	if (ret <= 0) {
		ret = -EINVAL;
		return ret;
		goto end;
	}

	do  {
@@ -968,7 +983,13 @@ static int dp_ctrl_link_training_2(struct dp_ctrl_private *ctrl)
			break;
		}
	} while (!atomic_read(&ctrl->aborted));
end:
	ctrl->aux->state &= ~DP_STATE_TRAIN_2_STARTED;

	if (ret)
		ctrl->aux->state |= DP_STATE_TRAIN_2_FAILED;
	else
		ctrl->aux->state |= DP_STATE_TRAIN_2_SUCCEEDED;
	return ret;
}

@@ -1109,8 +1130,7 @@ static int dp_ctrl_disable_mainlink_clocks(struct dp_ctrl_private *ctrl)
	return ctrl->power->clk_enable(ctrl->power, DP_CTRL_PM, false);
}

static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl,
	bool flip, bool multi_func)
static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip, bool reset)
{
	struct dp_ctrl_private *ctrl;
	struct dp_catalog_ctrl *catalog;
@@ -1125,7 +1145,7 @@ static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl,
	ctrl->orientation = flip;
	catalog = ctrl->catalog;

	if (!multi_func) {
	if (reset) {
		catalog->usb_reset(ctrl->catalog, flip);
		catalog->phy_reset(ctrl->catalog);
	}
@@ -1192,6 +1212,10 @@ static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl)
		return -EINVAL;
	}

	ctrl->aux->state &= ~DP_STATE_LINK_MAINTENANCE_COMPLETED;
	ctrl->aux->state &= ~DP_STATE_LINK_MAINTENANCE_FAILED;
	ctrl->aux->state |= DP_STATE_LINK_MAINTENANCE_STARTED;

	ctrl->dp_ctrl.push_idle(&ctrl->dp_ctrl);
	ctrl->dp_ctrl.reset(&ctrl->dp_ctrl);

@@ -1231,6 +1255,13 @@ static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl)
		ret = dp_ctrl_setup_main_link(ctrl, true);
	} while (ret == -EAGAIN);

	ctrl->aux->state &= ~DP_STATE_LINK_MAINTENANCE_STARTED;

	if (ret)
		ctrl->aux->state |= DP_STATE_LINK_MAINTENANCE_FAILED;
	else
		ctrl->aux->state |= DP_STATE_LINK_MAINTENANCE_COMPLETED;

	return ret;
}

+2 −2
Original line number Diff line number Diff line
/*
 * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
 * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -23,7 +23,7 @@
#include "dp_catalog.h"

struct dp_ctrl {
	int (*init)(struct dp_ctrl *dp_ctrl, bool flip, bool multi_func);
	int (*init)(struct dp_ctrl *dp_ctrl, bool flip, bool reset);
	void (*deinit)(struct dp_ctrl *dp_ctrl);
	int (*on)(struct dp_ctrl *dp_ctrl);
	void (*off)(struct dp_ctrl *dp_ctrl);
+238 −116
Original line number Diff line number Diff line
@@ -36,15 +36,52 @@ struct dp_debug_private {
	u8 *dpcd;
	u32 dpcd_size;

	int vdo;

	struct dp_usbpd *usbpd;
	struct dp_link *link;
	struct dp_panel *panel;
	struct dp_aux *aux;
	struct drm_connector **connector;
	struct device *dev;

	struct work_struct sim_work;
	struct dp_debug dp_debug;
};

static int dp_debug_get_edid_buf(struct dp_debug_private *debug)
{
	int rc = 0;

	if (!debug->edid) {
		debug->edid = devm_kzalloc(debug->dev, SZ_256, GFP_KERNEL);
		if (!debug->edid) {
			rc = -ENOMEM;
			goto end;
		}

		debug->edid_size = SZ_256;
	}
end:
	return rc;
}

static int dp_debug_get_dpcd_buf(struct dp_debug_private *debug)
{
	int rc = 0;

	if (!debug->dpcd) {
		debug->dpcd = devm_kzalloc(debug->dev, SZ_1K, GFP_KERNEL);
		if (!debug->dpcd) {
			rc = -ENOMEM;
			goto end;
		}

		debug->dpcd_size = SZ_1K;
	}
end:
	return rc;
}

static ssize_t dp_debug_write_edid(struct file *file,
		const char __user *user_buff, size_t count, loff_t *ppos)
{
@@ -75,7 +112,8 @@ static ssize_t dp_debug_write_edid(struct file *file,
	edid_size = size / char_to_nib;
	buf_t = buf;

	memset(debug->edid, 0, debug->edid_size);
	if (dp_debug_get_edid_buf(debug))
		goto bail;

	if (edid_size != debug->edid_size) {
		pr_debug("clearing debug edid\n");
@@ -100,13 +138,13 @@ static ssize_t dp_debug_write_edid(struct file *file,
		buf_t += char_to_nib;
	}

	print_hex_dump(KERN_DEBUG, "DEBUG EDID: ", DUMP_PREFIX_NONE,
		16, 1, debug->edid, debug->edid_size, false);

	edid = debug->edid;
bail:
	kfree(buf);

	if (!debug->dp_debug.sim_mode)
		debug->panel->set_edid(debug->panel, edid);

	return rc;
}

@@ -119,8 +157,8 @@ static ssize_t dp_debug_write_dpcd(struct file *file,
	size_t dpcd_size = 0;
	size_t size = 0, dpcd_buf_index = 0;
	ssize_t rc = count;

	pr_debug("count=%zu\n", count);
	char offset_ch[5];
	u32 offset;

	if (!debug)
		return -ENODEV;
@@ -128,7 +166,7 @@ static ssize_t dp_debug_write_dpcd(struct file *file,
	if (*ppos)
		goto bail;

	size = min_t(size_t, count, SZ_32);
	size = min_t(size_t, count, SZ_2K);

	buf = kzalloc(size, GFP_KERNEL);
	if (!buf) {
@@ -139,16 +177,30 @@ static ssize_t dp_debug_write_dpcd(struct file *file,
	if (copy_from_user(buf, user_buff, size))
		goto bail;

	dpcd_size = size / char_to_nib;
	buf_t = buf;
	memcpy(offset_ch, buf, 4);
	offset_ch[4] = '\0';

	memset(debug->dpcd, 0, debug->dpcd_size);
	if (kstrtoint(offset_ch, 16, &offset)) {
		pr_err("offset kstrtoint error\n");
		goto bail;
	}

	if (dpcd_size != debug->dpcd_size) {
		pr_debug("clearing debug dpcd\n");
	if (dp_debug_get_dpcd_buf(debug))
		goto bail;

	if (offset == 0xFFFF) {
		pr_err("clearing dpcd\n");
		memset(debug->dpcd, 0, debug->dpcd_size);
		goto bail;
	}

	size -= 4;

	dpcd_size = size / char_to_nib;
	buf_t = buf + 4;

	dpcd_buf_index = offset;

	while (dpcd_size--) {
		char t[3];
		int d;
@@ -167,16 +219,39 @@ static ssize_t dp_debug_write_dpcd(struct file *file,
		buf_t += char_to_nib;
	}

	print_hex_dump(KERN_DEBUG, "DEBUG DPCD: ", DUMP_PREFIX_NONE,
		8, 1, debug->dpcd, debug->dpcd_size, false);

	dpcd = debug->dpcd;
bail:
	kfree(buf);
	if (debug->dp_debug.sim_mode)
		debug->aux->dpcd_updated(debug->aux);
	else
		debug->panel->set_dpcd(debug->panel, dpcd);

	return rc;
}

static ssize_t dp_debug_read_dpcd(struct file *file,
		char __user *user_buff, size_t count, loff_t *ppos)
{
	struct dp_debug_private *debug = file->private_data;
	char buf[SZ_8];
	u32 len = 0;

	if (!debug)
		return -ENODEV;

	if (*ppos)
		return 0;

	len += snprintf(buf, SZ_8, "0x%x\n", debug->aux->reg);

	if (copy_to_user(user_buff, buf, len))
		return -EFAULT;

	*ppos += len;
	return len;
}

static ssize_t dp_debug_write_hpd(struct file *file,
		const char __user *user_buff, size_t count, loff_t *ppos)
{
@@ -421,7 +496,6 @@ static ssize_t dp_debug_read_info(struct file *file, char __user *user_buff,
	struct dp_debug_private *debug = file->private_data;
	char *buf;
	u32 len = 0, rc = 0;
	u64 lclk = 0;
	u32 max_size = SZ_4K;

	if (!debug)
@@ -434,124 +508,60 @@ static ssize_t dp_debug_read_info(struct file *file, char __user *user_buff,
	if (!buf)
		return -ENOMEM;

	rc = snprintf(buf + len, max_size, "\tname = %s\n", DEBUG_NAME);
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

	rc = snprintf(buf + len, max_size,
		"\tdp_panel\n\t\tmax_pclk_khz = %d\n",
		debug->panel->max_pclk_khz);
	rc = snprintf(buf + len, max_size, "\tstate=0x%x\n", debug->aux->state);
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

	rc = snprintf(buf + len, max_size,
		"\tdrm_dp_link\n\t\trate = %u\n",
	rc = snprintf(buf + len, max_size, "\tlink_rate=%u\n",
		debug->panel->link_info.rate);
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

	rc = snprintf(buf + len, max_size,
		"\t\tnum_lanes = %u\n",
	rc = snprintf(buf + len, max_size, "\tnum_lanes=%u\n",
		debug->panel->link_info.num_lanes);
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

	rc = snprintf(buf + len, max_size,
		"\t\tcapabilities = %lu\n",
		debug->panel->link_info.capabilities);
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

	rc = snprintf(buf + len, max_size,
		"\tdp_panel_info:\n\t\tactive = %dx%d\n",
	rc = snprintf(buf + len, max_size, "\tresolution=%dx%d@%dHz\n",
		debug->panel->pinfo.h_active,
		debug->panel->pinfo.v_active);
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

	rc = snprintf(buf + len, max_size,
		"\t\tback_porch = %dx%d\n",
		debug->panel->pinfo.h_back_porch,
		debug->panel->pinfo.v_back_porch);
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

	rc = snprintf(buf + len, max_size,
		"\t\tfront_porch = %dx%d\n",
		debug->panel->pinfo.h_front_porch,
		debug->panel->pinfo.v_front_porch);
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

	rc = snprintf(buf + len, max_size,
		"\t\tsync_width = %dx%d\n",
		debug->panel->pinfo.h_sync_width,
		debug->panel->pinfo.v_sync_width);
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

	rc = snprintf(buf + len, max_size,
		"\t\tactive_low = %dx%d\n",
		debug->panel->pinfo.h_active_low,
		debug->panel->pinfo.v_active_low);
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

	rc = snprintf(buf + len, max_size,
		"\t\th_skew = %d\n",
		debug->panel->pinfo.h_skew);
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

	rc = snprintf(buf + len, max_size,
		"\t\trefresh rate = %d\n",
		debug->panel->pinfo.v_active,
		debug->panel->pinfo.refresh_rate);
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

	rc = snprintf(buf + len, max_size,
		"\t\tpixel clock khz = %d\n",
	rc = snprintf(buf + len, max_size, "\tpclock=%dKHz\n",
		debug->panel->pinfo.pixel_clk_khz);
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

	rc = snprintf(buf + len, max_size,
		"\t\tbpp = %d\n",
	rc = snprintf(buf + len, max_size, "\tbpp=%d\n",
		debug->panel->pinfo.bpp);
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

	/* Link Information */
	rc = snprintf(buf + len, max_size,
		"\tdp_link:\n\t\ttest_requested = %d\n",
		debug->link->sink_request);
	rc = snprintf(buf + len, max_size, "\ttest_req=%s\n",
		dp_link_get_test_name(debug->link->sink_request));
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

	rc = snprintf(buf + len, max_size,
		"\t\tlane_count = %d\n", debug->link->link_params.lane_count);
		"\tlane_count=%d\n", debug->link->link_params.lane_count);
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

	rc = snprintf(buf + len, max_size,
		"\t\tbw_code = %d\n", debug->link->link_params.bw_code);
		"\tbw_code=%d\n", debug->link->link_params.bw_code);
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

	lclk = drm_dp_bw_code_to_link_rate(
			debug->link->link_params.bw_code) * 1000;
	rc = snprintf(buf + len, max_size,
		"\t\tlclk = %lld\n", lclk);
		"\tv_level=%d\n", debug->link->phy_params.v_level);
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

	rc = snprintf(buf + len, max_size,
		"\t\tv_level = %d\n", debug->link->phy_params.v_level);
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

	rc = snprintf(buf + len, max_size,
		"\t\tp_level = %d\n", debug->link->phy_params.p_level);
		"\tp_level=%d\n", debug->link->phy_params.p_level);
	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
		goto error;

@@ -816,6 +826,90 @@ static ssize_t dp_debug_read_hdr(struct file *file,
	return rc;
}

static ssize_t dp_debug_write_sim(struct file *file,
		const char __user *user_buff, size_t count, loff_t *ppos)
{
	struct dp_debug_private *debug = file->private_data;
	char buf[SZ_8];
	size_t len = 0;
	int sim;

	if (!debug)
		return -ENODEV;

	if (*ppos)
		return 0;

	/* Leave room for termination char */
	len = min_t(size_t, count, SZ_8 - 1);
	if (copy_from_user(buf, user_buff, len))
		goto end;

	buf[len] = '\0';

	if (kstrtoint(buf, 10, &sim) != 0)
		goto end;

	if (sim) {
		if (dp_debug_get_edid_buf(debug))
			goto end;

		if (dp_debug_get_dpcd_buf(debug))
			goto error;
	} else {
		if (debug->edid) {
			devm_kfree(debug->dev, debug->edid);
			debug->edid = NULL;
		}

		if (debug->dpcd) {
			devm_kfree(debug->dev, debug->dpcd);
			debug->dpcd = NULL;
		}
	}

	debug->dp_debug.sim_mode = !!sim;

	debug->aux->set_sim_mode(debug->aux, debug->dp_debug.sim_mode,
			debug->edid, debug->dpcd);
end:
	return len;
error:
	devm_kfree(debug->dev, debug->edid);
	return len;
}

static ssize_t dp_debug_write_attention(struct file *file,
		const char __user *user_buff, size_t count, loff_t *ppos)
{
	struct dp_debug_private *debug = file->private_data;
	char buf[SZ_8];
	size_t len = 0;
	int vdo;

	if (!debug)
		return -ENODEV;

	if (*ppos)
		return 0;

	/* Leave room for termination char */
	len = min_t(size_t, count, SZ_8 - 1);
	if (copy_from_user(buf, user_buff, len))
		goto end;

	buf[len] = '\0';

	if (kstrtoint(buf, 10, &vdo) != 0)
		goto end;

	debug->vdo = vdo;

	schedule_work(&debug->sim_work);
end:
	return len;
}

static const struct file_operations dp_debug_fops = {
	.open = simple_open,
	.read = dp_debug_read_info,
@@ -840,6 +934,7 @@ static const struct file_operations edid_fops = {
static const struct file_operations dpcd_fops = {
	.open = simple_open,
	.write = dp_debug_write_dpcd,
	.read = dp_debug_read_dpcd,
};

static const struct file_operations connected_fops = {
@@ -865,6 +960,16 @@ static const struct file_operations hdr_fops = {
	.read = dp_debug_read_hdr,
};

static const struct file_operations sim_fops = {
	.open = simple_open,
	.write = dp_debug_write_sim,
};

static const struct file_operations attention_fops = {
	.open = simple_open,
	.write = dp_debug_write_attention,
};

static int dp_debug_init(struct dp_debug *dp_debug)
{
	int rc = 0;
@@ -967,6 +1072,26 @@ static int dp_debug_init(struct dp_debug *dp_debug)
		goto error_remove_dir;
	}

	file = debugfs_create_file("sim", 0644, dir,
		debug, &sim_fops);

	if (IS_ERR_OR_NULL(file)) {
		rc = PTR_ERR(file);
		pr_err("[%s] debugfs sim failed, rc=%d\n",
			DEBUG_NAME, rc);
		goto error_remove_dir;
	}

	file = debugfs_create_file("attention", 0644, dir,
		debug, &attention_fops);

	if (IS_ERR_OR_NULL(file)) {
		rc = PTR_ERR(file);
		pr_err("[%s] debugfs attention failed, rc=%d\n",
			DEBUG_NAME, rc);
		goto error_remove_dir;
	}

	return 0;

error_remove_dir:
@@ -977,9 +1102,17 @@ static int dp_debug_init(struct dp_debug *dp_debug)
	return rc;
}

static void dp_debug_sim_work(struct work_struct *work)
{
	struct dp_debug_private *debug =
		container_of(work, typeof(*debug), sim_work);

	debug->usbpd->simulate_attention(debug->usbpd, debug->vdo);
}

struct dp_debug *dp_debug_get(struct device *dev, struct dp_panel *panel,
			struct dp_usbpd *usbpd, struct dp_link *link,
			struct drm_connector **connector)
			struct dp_aux *aux, struct drm_connector **connector)
{
	int rc = 0;
	struct dp_debug_private *debug;
@@ -997,28 +1130,13 @@ struct dp_debug *dp_debug_get(struct device *dev, struct dp_panel *panel,
		goto error;
	}

	debug->edid = devm_kzalloc(dev, SZ_256, GFP_KERNEL);
	if (!debug->edid) {
		rc = -ENOMEM;
		kfree(debug);
		goto error;
	}

	debug->edid_size = SZ_256;

	debug->dpcd = devm_kzalloc(dev, SZ_16, GFP_KERNEL);
	if (!debug->dpcd) {
		rc = -ENOMEM;
		kfree(debug);
		goto error;
	}

	debug->dpcd_size = SZ_16;
	INIT_WORK(&debug->sim_work, dp_debug_sim_work);

	debug->dp_debug.debug_en = false;
	debug->usbpd = usbpd;
	debug->link = link;
	debug->panel = panel;
	debug->aux = aux;
	debug->dev = dev;
	debug->connector = connector;

@@ -1063,7 +1181,11 @@ void dp_debug_put(struct dp_debug *dp_debug)

	dp_debug_deinit(dp_debug);

	if (debug->edid)
		devm_kfree(debug->dev, debug->edid);

	if (debug->dpcd)
		devm_kfree(debug->dev, debug->dpcd);

	devm_kfree(debug->dev, debug);
}
Loading