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: Ajay Singh Parmar <aparmar@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/dp/dp_aux.c b/drivers/gpu/drm/msm/dp/dp_aux.c
index 79f2ec9..0a3fb24 100644
--- a/drivers/gpu/drm/msm/dp/dp_aux.c
+++ b/drivers/gpu/drm/msm/dp/dp_aux.c
@@ -1,5 +1,5 @@
/*
- * 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:
diff --git a/drivers/gpu/drm/msm/dp/dp_aux.h b/drivers/gpu/drm/msm/dp/dp_aux.h
index e8cb1cc..bf52d57 100644
--- a/drivers/gpu/drm/msm/dp/dp_aux.h
+++ b/drivers/gpu/drm/msm/dp/dp_aux.h
@@ -1,5 +1,5 @@
/*
- * 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,
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index d9fc7df..bcd8a2a 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -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;
}
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index 229c779..31d8f07 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -1,5 +1,5 @@
/*
- * 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);
diff --git a/drivers/gpu/drm/msm/dp/dp_debug.c b/drivers/gpu/drm/msm/dp/dp_debug.c
index 054b3c8..a63b2c5 100644
--- a/drivers/gpu/drm/msm/dp/dp_debug.c
+++ b/drivers/gpu/drm/msm/dp/dp_debug.c
@@ -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);
- debug->panel->set_edid(debug->panel, edid);
+
+ 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 (dpcd_size != debug->dpcd_size) {
- pr_debug("clearing debug dpcd\n");
+ if (kstrtoint(offset_ch, 16, &offset)) {
+ pr_err("offset kstrtoint error\n");
goto bail;
}
+ 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);
- debug->panel->set_dpcd(debug->panel, dpcd);
+ 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);
+ 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,
- "\tdp_panel\n\t\tmax_pclk_khz = %d\n",
- debug->panel->max_pclk_khz);
- 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);
- 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);
+ "\tbw_code=%d\n", debug->link->link_params.bw_code);
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);
+ "\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);
- devm_kfree(debug->dev, debug->edid);
- devm_kfree(debug->dev, debug->dpcd);
+ if (debug->edid)
+ devm_kfree(debug->dev, debug->edid);
+
+ if (debug->dpcd)
+ devm_kfree(debug->dev, debug->dpcd);
+
devm_kfree(debug->dev, debug);
}
diff --git a/drivers/gpu/drm/msm/dp/dp_debug.h b/drivers/gpu/drm/msm/dp/dp_debug.h
index 3b2d23e..5a5a786 100644
--- a/drivers/gpu/drm/msm/dp/dp_debug.h
+++ b/drivers/gpu/drm/msm/dp/dp_debug.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-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,7 @@
#include "dp_panel.h"
#include "dp_link.h"
#include "dp_usbpd.h"
+#include "dp_aux.h"
/**
* struct dp_debug
@@ -29,6 +30,7 @@
*/
struct dp_debug {
bool debug_en;
+ bool sim_mode;
bool psm_enabled;
int aspect_ratio;
int vdisplay;
@@ -52,7 +54,7 @@ struct dp_debug {
*/
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);
/**
* dp_debug_put()
*
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index c5f62a5..63bc021 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -466,6 +466,7 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp,
bool hpd)
{
u32 timeout_sec;
+ int ret = 0;
dp->dp_display.is_connected = hpd;
@@ -474,16 +475,20 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp,
else
timeout_sec = 10;
+ dp->aux->state |= DP_STATE_NOTIFICATION_SENT;
+
reinit_completion(&dp->notification_comp);
dp_display_send_hpd_event(dp);
if (!wait_for_completion_timeout(&dp->notification_comp,
HZ * timeout_sec)) {
pr_warn("%s timeout\n", hpd ? "connect" : "disconnect");
- return -EINVAL;
+ ret = -EINVAL;
}
- return 0;
+ dp->aux->state &= ~DP_STATE_NOTIFICATION_SENT;
+
+ return ret;
}
static int dp_display_process_hpd_high(struct dp_display_private *dp)
@@ -531,6 +536,7 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)
static void dp_display_host_init(struct dp_display_private *dp)
{
bool flip = false;
+ bool reset;
if (dp->core_initialized) {
pr_debug("DP core already initialized\n");
@@ -540,8 +546,10 @@ static void dp_display_host_init(struct dp_display_private *dp)
if (dp->usbpd->orientation == ORIENTATION_CC2)
flip = true;
+ reset = dp->debug->sim_mode ? false : !dp->usbpd->multi_func;
+
dp->power->init(dp->power, flip);
- dp->ctrl->init(dp->ctrl, flip, dp->usbpd->multi_func);
+ dp->ctrl->init(dp->ctrl, flip, reset);
enable_irq(dp->irq);
dp->core_initialized = true;
}
@@ -557,6 +565,7 @@ static void dp_display_host_deinit(struct dp_display_private *dp)
dp->power->deinit(dp->power);
disable_irq(dp->irq);
dp->core_initialized = false;
+ dp->aux->state = 0;
}
static int dp_display_process_hpd_low(struct dp_display_private *dp)
@@ -935,7 +944,7 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
}
dp->debug = dp_debug_get(dev, dp->panel, dp->usbpd,
- dp->link, &dp->dp_display.connector);
+ dp->link, dp->aux, &dp->dp_display.connector);
if (IS_ERR(dp->debug)) {
rc = PTR_ERR(dp->debug);
pr_err("failed to initialize debug, rc = %d\n", rc);
@@ -1101,6 +1110,7 @@ static int dp_display_post_enable(struct dp_display *dp_display)
end:
/* clear framework event notifier */
dp_display->post_open = NULL;
+ dp->aux->state |= DP_STATE_CTRL_POWERED_ON;
complete_all(&dp->notification_comp);
mutex_unlock(&dp->session_lock);
@@ -1191,7 +1201,7 @@ static int dp_display_disable(struct dp_display *dp_display)
dp->dp_display.is_connected = false;
}
dp->power_on = false;
-
+ dp->aux->state = DP_STATE_CTRL_POWERED_OFF;
end:
complete_all(&dp->notification_comp);
mutex_unlock(&dp->session_lock);
diff --git a/drivers/gpu/drm/msm/dp/dp_link.c b/drivers/gpu/drm/msm/dp/dp_link.c
index 3ca247c..05629dd 100644
--- a/drivers/gpu/drm/msm/dp/dp_link.c
+++ b/drivers/gpu/drm/msm/dp/dp_link.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -724,24 +724,6 @@ static int dp_link_parse_phy_test_params(struct dp_link_private *link)
return ret;
}
-static char *dp_link_get_test_name(u32 test_requested)
-{
- switch (test_requested) {
- case DP_TEST_LINK_TRAINING:
- return DP_LINK_ENUM_STR(DP_TEST_LINK_TRAINING);
- case DP_TEST_LINK_VIDEO_PATTERN:
- return DP_LINK_ENUM_STR(DP_TEST_LINK_VIDEO_PATTERN);
- case DP_TEST_LINK_EDID_READ:
- return DP_LINK_ENUM_STR(DP_TEST_LINK_EDID_READ);
- case DP_TEST_LINK_PHY_TEST_PATTERN:
- return DP_LINK_ENUM_STR(DP_TEST_LINK_PHY_TEST_PATTERN);
- case DP_TEST_LINK_AUDIO_PATTERN:
- return DP_LINK_ENUM_STR(DP_TEST_LINK_AUDIO_PATTERN);
- default:
- return "unknown";
- }
-}
-
/**
* dp_link_is_video_audio_test_requested() - checks for audio/video link request
* @link: link requested by the sink
diff --git a/drivers/gpu/drm/msm/dp/dp_link.h b/drivers/gpu/drm/msm/dp/dp_link.h
index 6f79b6a..46d30a7 100644
--- a/drivers/gpu/drm/msm/dp/dp_link.h
+++ b/drivers/gpu/drm/msm/dp/dp_link.h
@@ -1,5 +1,5 @@
/*
- * 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
@@ -83,6 +83,24 @@ struct dp_link_params {
u32 bw_code;
};
+static inline char *dp_link_get_test_name(u32 test_requested)
+{
+ switch (test_requested) {
+ case DP_TEST_LINK_TRAINING:
+ return DP_LINK_ENUM_STR(DP_TEST_LINK_TRAINING);
+ case DP_TEST_LINK_VIDEO_PATTERN:
+ return DP_LINK_ENUM_STR(DP_TEST_LINK_VIDEO_PATTERN);
+ case DP_TEST_LINK_EDID_READ:
+ return DP_LINK_ENUM_STR(DP_TEST_LINK_EDID_READ);
+ case DP_TEST_LINK_PHY_TEST_PATTERN:
+ return DP_LINK_ENUM_STR(DP_TEST_LINK_PHY_TEST_PATTERN);
+ case DP_TEST_LINK_AUDIO_PATTERN:
+ return DP_LINK_ENUM_STR(DP_TEST_LINK_AUDIO_PATTERN);
+ default:
+ return "unknown";
+ }
+}
+
struct dp_link {
u32 sink_request;
u32 test_response;
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index d455ca1..822097c 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -136,9 +136,6 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
goto end;
}
-
- print_hex_dump(KERN_DEBUG, "[drm-dp] SINK DPCD: ",
- DUMP_PREFIX_NONE, 8, 1, dp_panel->dpcd, rlen, false);
}
rlen = drm_dp_dpcd_read(panel->aux->drm_aux,
@@ -271,35 +268,31 @@ static int dp_panel_set_dpcd(struct dp_panel *dp_panel, u8 *dpcd)
static int dp_panel_read_edid(struct dp_panel *dp_panel,
struct drm_connector *connector)
{
+ int ret = 0;
struct dp_panel_private *panel;
if (!dp_panel) {
pr_err("invalid input\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto end;
}
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
if (panel->custom_edid) {
pr_debug("skip edid read in debug mode\n");
- return 0;
+ goto end;
}
sde_get_edid(connector, &panel->aux->drm_aux->ddc,
(void **)&dp_panel->edid_ctrl);
if (!dp_panel->edid_ctrl->edid) {
pr_err("EDID read failed\n");
- } else {
- u8 *buf = (u8 *)dp_panel->edid_ctrl->edid;
- u32 size = buf[0x7E] ? 256 : 128;
-
- print_hex_dump(KERN_DEBUG, "[drm-dp] SINK EDID: ",
- DUMP_PREFIX_NONE, 16, 1, buf, size, false);
-
- return 0;
+ ret = -EINVAL;
+ goto end;
}
-
- return -EINVAL;
+end:
+ return ret;
}
static int dp_panel_read_sink_caps(struct dp_panel *dp_panel,
diff --git a/drivers/gpu/drm/msm/dp/dp_usbpd.c b/drivers/gpu/drm/msm/dp/dp_usbpd.c
index 2bd3bd4..42eb9b05 100644
--- a/drivers/gpu/drm/msm/dp/dp_usbpd.c
+++ b/drivers/gpu/drm/msm/dp/dp_usbpd.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -426,6 +426,28 @@ static int dp_usbpd_simulate_connect(struct dp_usbpd *dp_usbpd, bool hpd)
return rc;
}
+static int dp_usbpd_simulate_attention(struct dp_usbpd *dp_usbpd, int vdo)
+{
+ int rc = 0;
+ struct dp_usbpd_private *pd;
+
+ if (!dp_usbpd) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ pd = container_of(dp_usbpd, struct dp_usbpd_private, dp_usbpd);
+
+ pd->vdo = vdo;
+ dp_usbpd_get_status(pd);
+
+ if (pd->dp_cb && pd->dp_cb->attention)
+ pd->dp_cb->attention(pd->dev);
+error:
+ return rc;
+}
+
struct dp_usbpd *dp_usbpd_get(struct device *dev, struct dp_usbpd_cb *cb)
{
int rc = 0;
@@ -475,6 +497,7 @@ struct dp_usbpd *dp_usbpd_get(struct device *dev, struct dp_usbpd_cb *cb)
dp_usbpd = &usbpd->dp_usbpd;
dp_usbpd->simulate_connect = dp_usbpd_simulate_connect;
+ dp_usbpd->simulate_attention = dp_usbpd_simulate_attention;
return dp_usbpd;
error:
diff --git a/drivers/gpu/drm/msm/dp/dp_usbpd.h b/drivers/gpu/drm/msm/dp/dp_usbpd.h
index e70ad7d..0a7efd9 100644
--- a/drivers/gpu/drm/msm/dp/dp_usbpd.h
+++ b/drivers/gpu/drm/msm/dp/dp_usbpd.h
@@ -1,5 +1,5 @@
/*
- * 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
@@ -50,6 +50,7 @@ enum dp_usbpd_port {
* @alt_mode_cfg_done: bool to specify alt mode status
* @debug_en: bool to specify debug mode
* @simulate_connect: simulate disconnect or connect for debug mode
+ * @simulate_attention: simulate attention messages for debug mode
*/
struct dp_usbpd {
enum dp_usbpd_port port;
@@ -65,6 +66,7 @@ struct dp_usbpd {
bool debug_en;
int (*simulate_connect)(struct dp_usbpd *dp_usbpd, bool hpd);
+ int (*simulate_attention)(struct dp_usbpd *dp_usbpd, int vdo);
};
/**