disp: msm: dp: add up_req support for mst_sim_helper

Add up_req support for mst_sim_helper when port state is updated.

Add reset support when hotplug becomes low.

Change-Id: I72341bd845f9061a59af6740b9862ebbc4c8979e
Signed-off-by: Xiaowen Wu <wxiaowen@codeaurora.org>
Signed-off-by: Karim Henain <khenain@codeaurora.org>
Signed-off-by: Sudarsan Ramesh <sudarame@codeaurora.org>
Cette révision appartient à :
Xiaowen Wu
2020-07-16 20:01:43 -04:00
révisé par Sudarsan Ramesh
Parent fdb86d6f62
révision 24d245556e

Voir le fichier

@@ -63,6 +63,7 @@ struct dp_mst_sim_context {
struct mutex session_lock;
struct completion session_comp;
struct workqueue_struct *wq;
int reset_cnt;
u8 esi[16];
u8 guid[16];
@@ -77,6 +78,12 @@ struct dp_mst_sim_work {
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
static void dp_sideband_hex_dump(const char *name,
u32 address, u8 *buffer, size_t size)
@@ -634,19 +641,46 @@ static int dp_sideband_build_clear_payload_id_table_rep(
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[1] = DP_DOWN_REP_MSG_RDY;
ctx->esi[1] = val;
ctx->esi[2] = 0;
return 0;
}
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,
@@ -666,10 +700,8 @@ static int dp_mst_sim_clear_esi(struct dp_mst_sim_context *ctx,
for (i = 0; i < msg->size; 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);
}
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->curlen = 0;
mutex_lock(&ctx->session_lock);
while (msg->curlen < size) {
if (ctx->reset_cnt)
break;
/* copy data */
len = min(size - msg->curlen, 44);
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);
/* 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 */
mutex_unlock(&ctx->session_lock);
ctx->host_hpd_irq(ctx->host_dev);
mutex_lock(&ctx->session_lock);
/* 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);
wait_for_completion(&ctx->session_comp);
mutex_lock(&ctx->session_lock);
}
mutex_unlock(&ctx->session_lock);
}
mutex_unlock(&ctx->session_lock);
return 0;
}
@@ -846,6 +885,56 @@ static int dp_mst_sim_down_rep(struct dp_mst_sim_context *ctx,
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)
{
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)
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 &&
msg->address < DP_SINK_COUNT_ESI + 14)
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) {
if (msg->address >= DP_SIDEBAND_MSG_DOWN_REP_BASE &&
msg->address < DP_SIDEBAND_MSG_DOWN_REP_BASE + 256)
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 &&
msg->address < DP_SINK_COUNT_ESI + 14)
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;
}
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,
struct dp_mst_sim_port *ports)
{
struct dp_mst_sim_context *ctx = mst_sim_context;
u8 *edid;
int rc = 0;
u32 update_mask = 0;
u32 i;
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);
/* 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++)
kfree(ctx->ports[i].edid);
kfree(ctx->ports);
@@ -927,6 +1118,10 @@ fail:
}
mutex_unlock(&ctx->session_lock);
if (update_mask)
dp_mst_sim_notify(ctx, update_mask);
return rc;
}