|
@@ -63,6 +63,7 @@ struct dp_mst_sim_context {
|
|
struct mutex session_lock;
|
|
struct mutex session_lock;
|
|
struct completion session_comp;
|
|
struct completion session_comp;
|
|
struct workqueue_struct *wq;
|
|
struct workqueue_struct *wq;
|
|
|
|
+ int reset_cnt;
|
|
|
|
|
|
u8 esi[16];
|
|
u8 esi[16];
|
|
u8 guid[16];
|
|
u8 guid[16];
|
|
@@ -77,6 +78,12 @@ struct dp_mst_sim_work {
|
|
size_t size;
|
|
size_t size;
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+struct dp_mst_notify_work {
|
|
|
|
+ struct work_struct base;
|
|
|
|
+ struct dp_mst_sim_context *ctx;
|
|
|
|
+ u32 port_mask;
|
|
|
|
+};
|
|
|
|
+
|
|
#ifdef CONFIG_DYNAMIC_DEBUG
|
|
#ifdef CONFIG_DYNAMIC_DEBUG
|
|
static void dp_sideband_hex_dump(const char *name,
|
|
static void dp_sideband_hex_dump(const char *name,
|
|
u32 address, u8 *buffer, size_t size)
|
|
u32 address, u8 *buffer, size_t size)
|
|
@@ -634,19 +641,46 @@ static int dp_sideband_build_clear_payload_id_table_rep(
|
|
return idx;
|
|
return idx;
|
|
}
|
|
}
|
|
|
|
|
|
-static inline int dp_sideband_update_esi(struct dp_mst_sim_context *ctx)
|
|
|
|
|
|
+static int dp_sideband_build_connection_notify_req(
|
|
|
|
+ struct dp_mst_sim_context *ctx, int port_idx)
|
|
|
|
+{
|
|
|
|
+ struct dp_mst_sim_port *port = &ctx->ports[port_idx];
|
|
|
|
+ u8 *buf = ctx->down_rep.msg;
|
|
|
|
+ int idx = 0;
|
|
|
|
+
|
|
|
|
+ buf[idx] = DP_CONNECTION_STATUS_NOTIFY;
|
|
|
|
+ idx++;
|
|
|
|
+
|
|
|
|
+ buf[idx] = port_idx << 4;
|
|
|
|
+ idx++;
|
|
|
|
+
|
|
|
|
+ memcpy(&buf[idx], &port->peer_guid, 16);
|
|
|
|
+ idx += 16;
|
|
|
|
+
|
|
|
|
+ buf[idx] = (port->ldps << 6) |
|
|
|
|
+ (port->ddps << 5) |
|
|
|
|
+ (port->mcs << 4) |
|
|
|
|
+ (port->input << 3) |
|
|
|
|
+ (port->pdt & 0x7);
|
|
|
|
+ idx++;
|
|
|
|
+
|
|
|
|
+ return idx;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline int dp_sideband_update_esi(
|
|
|
|
+ struct dp_mst_sim_context *ctx, u8 val)
|
|
{
|
|
{
|
|
ctx->esi[0] = ctx->port_num;
|
|
ctx->esi[0] = ctx->port_num;
|
|
- ctx->esi[1] = DP_DOWN_REP_MSG_RDY;
|
|
|
|
|
|
+ ctx->esi[1] = val;
|
|
ctx->esi[2] = 0;
|
|
ctx->esi[2] = 0;
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static inline bool dp_sideband_pending_esi(
|
|
static inline bool dp_sideband_pending_esi(
|
|
- struct dp_mst_sim_context *ctx)
|
|
|
|
|
|
+ struct dp_mst_sim_context *ctx, u8 val)
|
|
{
|
|
{
|
|
- return !!(ctx->esi[1] & DP_DOWN_REP_MSG_RDY);
|
|
|
|
|
|
+ return !!(ctx->esi[1] & val);
|
|
}
|
|
}
|
|
|
|
|
|
static int dp_mst_sim_clear_esi(struct dp_mst_sim_context *ctx,
|
|
static int dp_mst_sim_clear_esi(struct dp_mst_sim_context *ctx,
|
|
@@ -666,10 +700,8 @@ static int dp_mst_sim_clear_esi(struct dp_mst_sim_context *ctx,
|
|
for (i = 0; i < msg->size; i++)
|
|
for (i = 0; i < msg->size; i++)
|
|
ctx->esi[addr + i] &= ~((u8 *)msg->buffer)[i];
|
|
ctx->esi[addr + i] &= ~((u8 *)msg->buffer)[i];
|
|
|
|
|
|
- if ((old_esi & DP_DOWN_REP_MSG_RDY) &&
|
|
|
|
- !(ctx->esi[1] & DP_DOWN_REP_MSG_RDY)) {
|
|
|
|
|
|
+ if (old_esi != ctx->esi[1])
|
|
complete(&ctx->session_comp);
|
|
complete(&ctx->session_comp);
|
|
- }
|
|
|
|
|
|
|
|
mutex_unlock(&ctx->session_lock);
|
|
mutex_unlock(&ctx->session_lock);
|
|
|
|
|
|
@@ -744,7 +776,12 @@ static int dp_mst_sim_down_req_internal(struct dp_mst_sim_context *ctx,
|
|
msg = &ctx->down_rep;
|
|
msg = &ctx->down_rep;
|
|
msg->curlen = 0;
|
|
msg->curlen = 0;
|
|
|
|
|
|
|
|
+ mutex_lock(&ctx->session_lock);
|
|
|
|
+
|
|
while (msg->curlen < size) {
|
|
while (msg->curlen < size) {
|
|
|
|
+ if (ctx->reset_cnt)
|
|
|
|
+ break;
|
|
|
|
+
|
|
/* copy data */
|
|
/* copy data */
|
|
len = min(size - msg->curlen, 44);
|
|
len = min(size - msg->curlen, 44);
|
|
memcpy(&ctx->dpcd[3], &msg->msg[msg->curlen], len);
|
|
memcpy(&ctx->dpcd[3], &msg->msg[msg->curlen], len);
|
|
@@ -766,23 +803,25 @@ static int dp_mst_sim_down_req_internal(struct dp_mst_sim_context *ctx,
|
|
ctx->dpcd[len + 3] = dp_mst_sim_msg_data_crc4(&ctx->dpcd[3], len);
|
|
ctx->dpcd[len + 3] = dp_mst_sim_msg_data_crc4(&ctx->dpcd[3], len);
|
|
|
|
|
|
/* update esi */
|
|
/* update esi */
|
|
- mutex_lock(&ctx->session_lock);
|
|
|
|
- dp_sideband_update_esi(ctx);
|
|
|
|
- mutex_unlock(&ctx->session_lock);
|
|
|
|
|
|
+ dp_sideband_update_esi(ctx, DP_DOWN_REP_MSG_RDY);
|
|
|
|
|
|
/* notify host */
|
|
/* notify host */
|
|
|
|
+ mutex_unlock(&ctx->session_lock);
|
|
ctx->host_hpd_irq(ctx->host_dev);
|
|
ctx->host_hpd_irq(ctx->host_dev);
|
|
|
|
+ mutex_lock(&ctx->session_lock);
|
|
|
|
|
|
/* wait until esi is cleared */
|
|
/* wait until esi is cleared */
|
|
- mutex_lock(&ctx->session_lock);
|
|
|
|
- while (dp_sideband_pending_esi(ctx)) {
|
|
|
|
|
|
+ while (dp_sideband_pending_esi(ctx, DP_DOWN_REP_MSG_RDY)) {
|
|
|
|
+ if (ctx->reset_cnt)
|
|
|
|
+ break;
|
|
mutex_unlock(&ctx->session_lock);
|
|
mutex_unlock(&ctx->session_lock);
|
|
wait_for_completion(&ctx->session_comp);
|
|
wait_for_completion(&ctx->session_comp);
|
|
mutex_lock(&ctx->session_lock);
|
|
mutex_lock(&ctx->session_lock);
|
|
}
|
|
}
|
|
- mutex_unlock(&ctx->session_lock);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ mutex_unlock(&ctx->session_lock);
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -846,6 +885,56 @@ static int dp_mst_sim_down_rep(struct dp_mst_sim_context *ctx,
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int dp_mst_sim_up_req(struct dp_mst_sim_context *ctx,
|
|
|
|
+ struct drm_dp_aux_msg *msg)
|
|
|
|
+{
|
|
|
|
+ u32 addr = msg->address - DP_SIDEBAND_MSG_UP_REQ_BASE;
|
|
|
|
+
|
|
|
|
+ memcpy(msg->buffer, &ctx->dpcd[addr], msg->size);
|
|
|
|
+ msg->reply = DP_AUX_NATIVE_REPLY_ACK;
|
|
|
|
+
|
|
|
|
+ dp_sideband_hex_dump("up_req",
|
|
|
|
+ addr, msg->buffer, msg->size);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void dp_mst_sim_reset_work(struct work_struct *work)
|
|
|
|
+{
|
|
|
|
+ struct dp_mst_notify_work *notify_work =
|
|
|
|
+ container_of(work, struct dp_mst_notify_work, base);
|
|
|
|
+ struct dp_mst_sim_context *ctx = notify_work->ctx;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&ctx->session_lock);
|
|
|
|
+ --ctx->reset_cnt;
|
|
|
|
+ reinit_completion(&ctx->session_comp);
|
|
|
|
+ mutex_unlock(&ctx->session_lock);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int dp_mst_sim_reset(struct dp_mst_sim_context *ctx,
|
|
|
|
+ struct drm_dp_aux_msg *msg)
|
|
|
|
+{
|
|
|
|
+ struct dp_mst_notify_work *work;
|
|
|
|
+
|
|
|
|
+ if (!msg->size || ((u8 *)msg->buffer)[0])
|
|
|
|
+ return msg->size;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&ctx->session_lock);
|
|
|
|
+ ++ctx->reset_cnt;
|
|
|
|
+ complete(&ctx->session_comp);
|
|
|
|
+ mutex_unlock(&ctx->session_lock);
|
|
|
|
+
|
|
|
|
+ work = kzalloc(sizeof(*work), GFP_KERNEL);
|
|
|
|
+ if (!work)
|
|
|
|
+ return msg->size;
|
|
|
|
+
|
|
|
|
+ work->ctx = ctx;
|
|
|
|
+ INIT_WORK(&work->base, dp_mst_sim_reset_work);
|
|
|
|
+ queue_work(ctx->wq, &work->base);
|
|
|
|
+
|
|
|
|
+ return msg->size;
|
|
|
|
+}
|
|
|
|
+
|
|
int dp_mst_sim_transfer(void *mst_sim_context, struct drm_dp_aux_msg *msg)
|
|
int dp_mst_sim_transfer(void *mst_sim_context, struct drm_dp_aux_msg *msg)
|
|
{
|
|
{
|
|
struct dp_mst_sim_context *ctx = mst_sim_context;
|
|
struct dp_mst_sim_context *ctx = mst_sim_context;
|
|
@@ -858,14 +947,26 @@ int dp_mst_sim_transfer(void *mst_sim_context, struct drm_dp_aux_msg *msg)
|
|
msg->address < DP_SIDEBAND_MSG_DOWN_REQ_BASE + 256)
|
|
msg->address < DP_SIDEBAND_MSG_DOWN_REQ_BASE + 256)
|
|
return dp_mst_sim_down_req(mst_sim_context, msg);
|
|
return dp_mst_sim_down_req(mst_sim_context, msg);
|
|
|
|
|
|
|
|
+ if (msg->address >= DP_SIDEBAND_MSG_UP_REP_BASE &&
|
|
|
|
+ msg->address < DP_SIDEBAND_MSG_UP_REP_BASE + 256)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
if (msg->address >= DP_SINK_COUNT_ESI &&
|
|
if (msg->address >= DP_SINK_COUNT_ESI &&
|
|
msg->address < DP_SINK_COUNT_ESI + 14)
|
|
msg->address < DP_SINK_COUNT_ESI + 14)
|
|
return dp_mst_sim_clear_esi(mst_sim_context, msg);
|
|
return dp_mst_sim_clear_esi(mst_sim_context, msg);
|
|
|
|
+
|
|
|
|
+ if (msg->address == DP_MSTM_CTRL)
|
|
|
|
+ return dp_mst_sim_reset(mst_sim_context, msg);
|
|
|
|
+
|
|
} else if (msg->request == DP_AUX_NATIVE_READ) {
|
|
} else if (msg->request == DP_AUX_NATIVE_READ) {
|
|
if (msg->address >= DP_SIDEBAND_MSG_DOWN_REP_BASE &&
|
|
if (msg->address >= DP_SIDEBAND_MSG_DOWN_REP_BASE &&
|
|
msg->address < DP_SIDEBAND_MSG_DOWN_REP_BASE + 256)
|
|
msg->address < DP_SIDEBAND_MSG_DOWN_REP_BASE + 256)
|
|
return dp_mst_sim_down_rep(mst_sim_context, msg);
|
|
return dp_mst_sim_down_rep(mst_sim_context, msg);
|
|
|
|
|
|
|
|
+ if (msg->address >= DP_SIDEBAND_MSG_UP_REQ_BASE &&
|
|
|
|
+ msg->address < DP_SIDEBAND_MSG_UP_REQ_BASE + 256)
|
|
|
|
+ return dp_mst_sim_up_req(mst_sim_context, msg);
|
|
|
|
+
|
|
if (msg->address >= DP_SINK_COUNT_ESI &&
|
|
if (msg->address >= DP_SINK_COUNT_ESI &&
|
|
msg->address < DP_SINK_COUNT_ESI + 14)
|
|
msg->address < DP_SINK_COUNT_ESI + 14)
|
|
return dp_mst_sim_read_esi(mst_sim_context, msg);
|
|
return dp_mst_sim_read_esi(mst_sim_context, msg);
|
|
@@ -874,12 +975,90 @@ int dp_mst_sim_transfer(void *mst_sim_context, struct drm_dp_aux_msg *msg)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void dp_mst_sim_up_req_work(struct work_struct *work)
|
|
|
|
+{
|
|
|
|
+ struct dp_mst_notify_work *notify_work =
|
|
|
|
+ container_of(work, struct dp_mst_notify_work, base);
|
|
|
|
+ struct dp_mst_sim_context *ctx = notify_work->ctx;
|
|
|
|
+ struct drm_dp_sideband_msg_rx *msg = &ctx->down_rep;
|
|
|
|
+ struct drm_dp_sideband_msg_hdr hdr;
|
|
|
|
+ int len, hdr_len, i;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&ctx->session_lock);
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < ctx->port_num; i++) {
|
|
|
|
+ if (ctx->reset_cnt)
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ if (!(notify_work->port_mask & (1 << i)))
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ len = dp_sideband_build_connection_notify_req(ctx, i);
|
|
|
|
+
|
|
|
|
+ /* copy data */
|
|
|
|
+ memcpy(&ctx->dpcd[3], msg->msg, len);
|
|
|
|
+
|
|
|
|
+ /* build header */
|
|
|
|
+ memset(&hdr, 0, sizeof(struct drm_dp_sideband_msg_hdr));
|
|
|
|
+ hdr.broadcast = 0;
|
|
|
|
+ hdr.path_msg = 0;
|
|
|
|
+ hdr.lct = 1;
|
|
|
|
+ hdr.lcr = 0;
|
|
|
|
+ hdr.seqno = 0;
|
|
|
|
+ hdr.msg_len = len + 1;
|
|
|
|
+ hdr.eomt = 1;
|
|
|
|
+ hdr.somt = 1;
|
|
|
|
+ dp_mst_sim_encode_sideband_msg_hdr(&hdr, ctx->dpcd, &hdr_len);
|
|
|
|
+
|
|
|
|
+ /* build crc */
|
|
|
|
+ ctx->dpcd[len + 3] = dp_mst_sim_msg_data_crc4(&ctx->dpcd[3], len);
|
|
|
|
+
|
|
|
|
+ /* update esi */
|
|
|
|
+ dp_sideband_update_esi(ctx, DP_UP_REQ_MSG_RDY);
|
|
|
|
+
|
|
|
|
+ /* notify host */
|
|
|
|
+ mutex_unlock(&ctx->session_lock);
|
|
|
|
+ ctx->host_hpd_irq(ctx->host_dev);
|
|
|
|
+ mutex_lock(&ctx->session_lock);
|
|
|
|
+
|
|
|
|
+ /* wait until esi is cleared */
|
|
|
|
+ while (dp_sideband_pending_esi(ctx, DP_UP_REQ_MSG_RDY)) {
|
|
|
|
+ if (ctx->reset_cnt)
|
|
|
|
+ break;
|
|
|
|
+ mutex_unlock(&ctx->session_lock);
|
|
|
|
+ wait_for_completion(&ctx->session_comp);
|
|
|
|
+ mutex_lock(&ctx->session_lock);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mutex_unlock(&ctx->session_lock);
|
|
|
|
+
|
|
|
|
+ kfree(notify_work);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void dp_mst_sim_notify(struct dp_mst_sim_context *ctx,
|
|
|
|
+ u32 port_mask)
|
|
|
|
+{
|
|
|
|
+ struct dp_mst_notify_work *work;
|
|
|
|
+
|
|
|
|
+ work = kzalloc(sizeof(*work), GFP_KERNEL);
|
|
|
|
+ if (!work)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ work->ctx = ctx;
|
|
|
|
+ work->port_mask = port_mask;
|
|
|
|
+
|
|
|
|
+ INIT_WORK(&work->base, dp_mst_sim_up_req_work);
|
|
|
|
+ queue_work(ctx->wq, &work->base);
|
|
|
|
+}
|
|
|
|
+
|
|
int dp_mst_sim_update(void *mst_sim_context, u32 port_num,
|
|
int dp_mst_sim_update(void *mst_sim_context, u32 port_num,
|
|
struct dp_mst_sim_port *ports)
|
|
struct dp_mst_sim_port *ports)
|
|
{
|
|
{
|
|
struct dp_mst_sim_context *ctx = mst_sim_context;
|
|
struct dp_mst_sim_context *ctx = mst_sim_context;
|
|
u8 *edid;
|
|
u8 *edid;
|
|
int rc = 0;
|
|
int rc = 0;
|
|
|
|
+ u32 update_mask = 0;
|
|
u32 i;
|
|
u32 i;
|
|
|
|
|
|
if (!ctx || port_num >= 15)
|
|
if (!ctx || port_num >= 15)
|
|
@@ -887,6 +1066,18 @@ int dp_mst_sim_update(void *mst_sim_context, u32 port_num,
|
|
|
|
|
|
mutex_lock(&ctx->session_lock);
|
|
mutex_lock(&ctx->session_lock);
|
|
|
|
|
|
|
|
+ /* get update mask */
|
|
|
|
+ if (port_num && ctx->port_num == port_num) {
|
|
|
|
+ for (i = 0; i < port_num; i++) {
|
|
|
|
+ if (ports[i].pdt != ctx->ports[i].pdt ||
|
|
|
|
+ ports[i].input != ctx->ports[i].input ||
|
|
|
|
+ ports[i].ldps != ctx->ports[i].ldps ||
|
|
|
|
+ ports[i].ddps != ctx->ports[i].ddps ||
|
|
|
|
+ ports[i].mcs != ctx->ports[i].mcs)
|
|
|
|
+ update_mask |= (1 << i);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
for (i = 0; i < ctx->port_num; i++)
|
|
for (i = 0; i < ctx->port_num; i++)
|
|
kfree(ctx->ports[i].edid);
|
|
kfree(ctx->ports[i].edid);
|
|
kfree(ctx->ports);
|
|
kfree(ctx->ports);
|
|
@@ -927,6 +1118,10 @@ fail:
|
|
}
|
|
}
|
|
|
|
|
|
mutex_unlock(&ctx->session_lock);
|
|
mutex_unlock(&ctx->session_lock);
|
|
|
|
+
|
|
|
|
+ if (update_mask)
|
|
|
|
+ dp_mst_sim_notify(ctx, update_mask);
|
|
|
|
+
|
|
return rc;
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
|