2021-06-28 08:10:26

by Sam McNally

[permalink] [raw]
Subject: [PATCH v6 1/3] drm/dp_mst: Add self-tests for up requests

Up requests are decoded by drm_dp_sideband_parse_req(), which operates
on a drm_dp_sideband_msg_rx, unlike down requests. Expand the existing
self-test helper sideband_msg_req_encode_decode() to copy the message
contents and length from a drm_dp_sideband_msg_tx to
drm_dp_sideband_msg_rx and use the parse function under test in place of
decode. Add an additional helper for testing clearly-invalid up
messages, verifying that parse rejects them.

Add support for currently-supported up requests to
drm_dp_dump_sideband_msg_req_body(); add support to
drm_dp_encode_sideband_req() to allow encoding for the self-tests.

Add self-tests for CONNECTION_STATUS_NOTIFY and RESOURCE_STATUS_NOTIFY.

Reviewed-by: Lyude Paul <[email protected]>
Signed-off-by: Sam McNally <[email protected]>
---

(no changes since v5)

Changes in v5:
- Set mock device name to more clearly attribute error/debug logging to
the self-test, in particular for cases where failures are expected

Changes in v4:
- New in v4

drivers/gpu/drm/drm_dp_mst_topology.c | 54 ++++++-
.../gpu/drm/drm_dp_mst_topology_internal.h | 4 +
.../drm/selftests/test-drm_dp_mst_helper.c | 149 ++++++++++++++++--
3 files changed, 192 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index ad0795afc21c..ee58f6517482 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -445,6 +445,37 @@ drm_dp_encode_sideband_req(const struct drm_dp_sideband_msg_req_body *req,
idx++;
}
break;
+ case DP_CONNECTION_STATUS_NOTIFY: {
+ const struct drm_dp_connection_status_notify *msg;
+
+ msg = &req->u.conn_stat;
+ buf[idx] = (msg->port_number & 0xf) << 4;
+ idx++;
+ memcpy(&raw->msg[idx], msg->guid, 16);
+ idx += 16;
+ raw->msg[idx] = 0;
+ raw->msg[idx] |= msg->legacy_device_plug_status ? BIT(6) : 0;
+ raw->msg[idx] |= msg->displayport_device_plug_status ? BIT(5) : 0;
+ raw->msg[idx] |= msg->message_capability_status ? BIT(4) : 0;
+ raw->msg[idx] |= msg->input_port ? BIT(3) : 0;
+ raw->msg[idx] |= FIELD_PREP(GENMASK(2, 0), msg->peer_device_type);
+ idx++;
+ break;
+ }
+ case DP_RESOURCE_STATUS_NOTIFY: {
+ const struct drm_dp_resource_status_notify *msg;
+
+ msg = &req->u.resource_stat;
+ buf[idx] = (msg->port_number & 0xf) << 4;
+ idx++;
+ memcpy(&raw->msg[idx], msg->guid, 16);
+ idx += 16;
+ buf[idx] = (msg->available_pbn & 0xff00) >> 8;
+ idx++;
+ buf[idx] = (msg->available_pbn & 0xff);
+ idx++;
+ break;
+ }
}
raw->cur_len = idx;
}
@@ -675,6 +706,22 @@ drm_dp_dump_sideband_msg_req_body(const struct drm_dp_sideband_msg_req_body *req
req->u.enc_status.stream_behavior,
req->u.enc_status.valid_stream_behavior);
break;
+ case DP_CONNECTION_STATUS_NOTIFY:
+ P("port=%d guid=%*ph legacy=%d displayport=%d messaging=%d input=%d peer_type=%d",
+ req->u.conn_stat.port_number,
+ (int)ARRAY_SIZE(req->u.conn_stat.guid), req->u.conn_stat.guid,
+ req->u.conn_stat.legacy_device_plug_status,
+ req->u.conn_stat.displayport_device_plug_status,
+ req->u.conn_stat.message_capability_status,
+ req->u.conn_stat.input_port,
+ req->u.conn_stat.peer_device_type);
+ break;
+ case DP_RESOURCE_STATUS_NOTIFY:
+ P("port=%d guid=%*ph pbn=%d",
+ req->u.resource_stat.port_number,
+ (int)ARRAY_SIZE(req->u.resource_stat.guid), req->u.resource_stat.guid,
+ req->u.resource_stat.available_pbn);
+ break;
default:
P("???\n");
break;
@@ -1119,9 +1166,9 @@ static bool drm_dp_sideband_parse_resource_status_notify(const struct drm_dp_mst
return false;
}

-static bool drm_dp_sideband_parse_req(const struct drm_dp_mst_topology_mgr *mgr,
- struct drm_dp_sideband_msg_rx *raw,
- struct drm_dp_sideband_msg_req_body *msg)
+bool drm_dp_sideband_parse_req(const struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_sideband_msg_rx *raw,
+ struct drm_dp_sideband_msg_req_body *msg)
{
memset(msg, 0, sizeof(*msg));
msg->req_type = (raw->msg[0] & 0x7f);
@@ -1137,6 +1184,7 @@ static bool drm_dp_sideband_parse_req(const struct drm_dp_mst_topology_mgr *mgr,
return false;
}
}
+EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_dp_sideband_parse_req);

static void build_dpcd_write(struct drm_dp_sideband_msg_tx *msg,
u8 port_num, u32 offset, u8 num_bytes, u8 *bytes)
diff --git a/drivers/gpu/drm/drm_dp_mst_topology_internal.h b/drivers/gpu/drm/drm_dp_mst_topology_internal.h
index eeda9a61c657..0356a2e0dba1 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology_internal.h
+++ b/drivers/gpu/drm/drm_dp_mst_topology_internal.h
@@ -21,4 +21,8 @@ void
drm_dp_dump_sideband_msg_req_body(const struct drm_dp_sideband_msg_req_body *req,
int indent, struct drm_printer *printer);

+bool
+drm_dp_sideband_parse_req(const struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_sideband_msg_rx *raw,
+ struct drm_dp_sideband_msg_req_body *msg);
#endif /* !_DRM_DP_MST_HELPER_INTERNAL_H_ */
diff --git a/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c b/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
index 6b4759ed6bfd..7bbeb1e5bc97 100644
--- a/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
+++ b/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
@@ -13,6 +13,10 @@
#include "../drm_dp_mst_topology_internal.h"
#include "test-drm_modeset_common.h"

+static void mock_release(struct device *dev)
+{
+}
+
int igt_dp_mst_calc_pbn_mode(void *ignored)
{
int pbn, i;
@@ -120,27 +124,60 @@ sideband_msg_req_equal(const struct drm_dp_sideband_msg_req_body *in,
static bool
sideband_msg_req_encode_decode(struct drm_dp_sideband_msg_req_body *in)
{
- struct drm_dp_sideband_msg_req_body *out;
+ struct drm_dp_sideband_msg_req_body *out = NULL;
struct drm_printer p = drm_err_printer(PREFIX_STR);
- struct drm_dp_sideband_msg_tx *txmsg;
+ struct drm_dp_sideband_msg_tx *txmsg = NULL;
+ struct drm_dp_sideband_msg_rx *rxmsg = NULL;
+ struct drm_dp_mst_topology_mgr *mgr = NULL;
int i, ret;
- bool result = true;
+ bool result = false;

out = kzalloc(sizeof(*out), GFP_KERNEL);
if (!out)
- return false;
+ goto out;

txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
if (!txmsg)
- return false;
+ goto out;

- drm_dp_encode_sideband_req(in, txmsg);
- ret = drm_dp_decode_sideband_req(txmsg, out);
- if (ret < 0) {
- drm_printf(&p, "Failed to decode sideband request: %d\n",
- ret);
- result = false;
+ rxmsg = kzalloc(sizeof(*rxmsg), GFP_KERNEL);
+ if (!rxmsg)
goto out;
+
+ mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
+ if (!mgr)
+ goto out;
+
+ mgr->dev = kzalloc(sizeof(*mgr->dev), GFP_KERNEL);
+ if (!mgr->dev)
+ goto out;
+
+ mgr->dev->dev = kzalloc(sizeof(*mgr->dev->dev), GFP_KERNEL);
+ if (!mgr->dev->dev)
+ goto out;
+
+ mgr->dev->dev->release = mock_release;
+ mgr->dev->dev->init_name = PREFIX_STR;
+ device_initialize(mgr->dev->dev);
+
+ drm_dp_encode_sideband_req(in, txmsg);
+ switch (in->req_type) {
+ case DP_CONNECTION_STATUS_NOTIFY:
+ case DP_RESOURCE_STATUS_NOTIFY:
+ memcpy(&rxmsg->msg, txmsg->msg, ARRAY_SIZE(rxmsg->msg));
+ rxmsg->curlen = txmsg->cur_len;
+ if (!drm_dp_sideband_parse_req(mgr, rxmsg, out)) {
+ drm_printf(&p, "Failed to decode sideband request\n");
+ goto out;
+ }
+ break;
+ default:
+ ret = drm_dp_decode_sideband_req(txmsg, out);
+ if (ret < 0) {
+ drm_printf(&p, "Failed to decode sideband request: %d\n", ret);
+ goto out;
+ }
+ break;
}

if (!sideband_msg_req_equal(in, out)) {
@@ -148,9 +185,9 @@ sideband_msg_req_encode_decode(struct drm_dp_sideband_msg_req_body *in)
drm_dp_dump_sideband_msg_req_body(in, 1, &p);
drm_printf(&p, "Got:\n");
drm_dp_dump_sideband_msg_req_body(out, 1, &p);
- result = false;
goto out;
}
+ result = true;

switch (in->req_type) {
case DP_REMOTE_DPCD_WRITE:
@@ -171,6 +208,66 @@ sideband_msg_req_encode_decode(struct drm_dp_sideband_msg_req_body *in)
out:
kfree(out);
kfree(txmsg);
+ kfree(rxmsg);
+ if (mgr) {
+ if (mgr->dev) {
+ put_device(mgr->dev->dev);
+ kfree(mgr->dev);
+ }
+ kfree(mgr);
+ }
+ return result;
+}
+
+static bool
+sideband_msg_req_parse(int req_type)
+{
+ struct drm_dp_sideband_msg_req_body *out = NULL;
+ struct drm_printer p = drm_err_printer(PREFIX_STR);
+ struct drm_dp_sideband_msg_rx *rxmsg = NULL;
+ struct drm_dp_mst_topology_mgr *mgr = NULL;
+ bool result = false;
+
+ out = kzalloc(sizeof(*out), GFP_KERNEL);
+ if (!out)
+ goto out;
+
+ rxmsg = kzalloc(sizeof(*rxmsg), GFP_KERNEL);
+ if (!rxmsg)
+ goto out;
+
+ mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
+ if (!mgr)
+ goto out;
+
+ mgr->dev = kzalloc(sizeof(*mgr->dev), GFP_KERNEL);
+ if (!mgr->dev)
+ goto out;
+
+ mgr->dev->dev = kzalloc(sizeof(*mgr->dev->dev), GFP_KERNEL);
+ if (!mgr->dev->dev)
+ goto out;
+
+ mgr->dev->dev->release = mock_release;
+ mgr->dev->dev->init_name = PREFIX_STR " expected parse failure";
+ device_initialize(mgr->dev->dev);
+
+ rxmsg->curlen = 1;
+ rxmsg->msg[0] = req_type & 0x7f;
+ if (drm_dp_sideband_parse_req(mgr, rxmsg, out))
+ drm_printf(&p, "Unexpectedly decoded invalid sideband request\n");
+ else
+ result = true;
+out:
+ kfree(out);
+ kfree(rxmsg);
+ if (mgr) {
+ if (mgr->dev) {
+ put_device(mgr->dev->dev);
+ kfree(mgr->dev);
+ }
+ kfree(mgr);
+ }
return result;
}

@@ -268,6 +365,34 @@ int igt_dp_mst_sideband_msg_req_decode(void *unused)
in.u.enc_status.valid_stream_behavior = 1;
DO_TEST();

+ in.req_type = DP_CONNECTION_STATUS_NOTIFY;
+ in.u.conn_stat.port_number = 0xf;
+ get_random_bytes(in.u.conn_stat.guid, sizeof(in.u.conn_stat.guid));
+ in.u.conn_stat.legacy_device_plug_status = 1;
+ in.u.conn_stat.displayport_device_plug_status = 0;
+ in.u.conn_stat.message_capability_status = 0;
+ in.u.conn_stat.input_port = 0;
+ in.u.conn_stat.peer_device_type = 7;
+ DO_TEST();
+ in.u.conn_stat.displayport_device_plug_status = 1;
+ DO_TEST();
+ in.u.conn_stat.message_capability_status = 1;
+ DO_TEST();
+ in.u.conn_stat.input_port = 1;
+ DO_TEST();
+
+ in.req_type = DP_RESOURCE_STATUS_NOTIFY;
+ in.u.resource_stat.port_number = 0xf;
+ get_random_bytes(in.u.resource_stat.guid, sizeof(in.u.resource_stat.guid));
+ in.u.resource_stat.available_pbn = 0xcdef;
+ DO_TEST();
+
+#undef DO_TEST
+#define DO_TEST(req_type) FAIL_ON(!sideband_msg_req_parse(req_type))
+ DO_TEST(DP_CONNECTION_STATUS_NOTIFY);
+ DO_TEST(DP_RESOURCE_STATUS_NOTIFY);
+
+ DO_TEST(DP_REMOTE_I2C_WRITE);
#undef DO_TEST
return 0;
}
--
2.32.0.93.g670b81a890-goog


2021-06-28 08:13:19

by Sam McNally

[permalink] [raw]
Subject: [PATCH v6 2/3] drm/dp_mst: Add support for sink event notify messages

Sink event notify messages are used for MST CEC IRQs. Add parsing
support for sink event notify messages in preparation for handling MST
CEC IRQs.

Reviewed-by: Lyude Paul <[email protected]>
Signed-off-by: Sam McNally <[email protected]>
---

(no changes since v4)

Changes in v4:
- Changed logging to use drm_dbg_kms()
- Added self-test

drivers/gpu/drm/drm_dp_mst_topology.c | 57 ++++++++++++++++++-
.../drm/selftests/test-drm_dp_mst_helper.c | 8 +++
include/drm/drm_dp_mst_helper.h | 14 +++++
3 files changed, 78 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index ee58f6517482..1cc1a58cfa8b 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -476,6 +476,20 @@ drm_dp_encode_sideband_req(const struct drm_dp_sideband_msg_req_body *req,
idx++;
break;
}
+ case DP_SINK_EVENT_NOTIFY: {
+ const struct drm_dp_sink_event_notify *msg;
+
+ msg = &req->u.sink_event;
+ buf[idx] = (msg->port_number & 0xf) << 4;
+ idx++;
+ memcpy(&raw->msg[idx], msg->guid, 16);
+ idx += 16;
+ buf[idx] = (msg->event_id & 0xff00) >> 8;
+ idx++;
+ buf[idx] = (msg->event_id & 0xff);
+ idx++;
+ break;
+ }
}
raw->cur_len = idx;
}
@@ -722,6 +736,12 @@ drm_dp_dump_sideband_msg_req_body(const struct drm_dp_sideband_msg_req_body *req
(int)ARRAY_SIZE(req->u.resource_stat.guid), req->u.resource_stat.guid,
req->u.resource_stat.available_pbn);
break;
+ case DP_SINK_EVENT_NOTIFY:
+ P("port=%d guid=%*ph event=%d",
+ req->u.sink_event.port_number,
+ (int)ARRAY_SIZE(req->u.sink_event.guid), req->u.sink_event.guid,
+ req->u.sink_event.event_id);
+ break;
default:
P("???\n");
break;
@@ -1166,6 +1186,30 @@ static bool drm_dp_sideband_parse_resource_status_notify(const struct drm_dp_mst
return false;
}

+static bool drm_dp_sideband_parse_sink_event_notify(const struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_sideband_msg_rx *raw,
+ struct drm_dp_sideband_msg_req_body *msg)
+{
+ int idx = 1;
+
+ msg->u.sink_event.port_number = (raw->msg[idx] & 0xf0) >> 4;
+ idx++;
+ if (idx > raw->curlen)
+ goto fail_len;
+
+ memcpy(msg->u.sink_event.guid, &raw->msg[idx], 16);
+ idx += 16;
+ if (idx > raw->curlen)
+ goto fail_len;
+
+ msg->u.sink_event.event_id = (raw->msg[idx] << 8) | (raw->msg[idx + 1]);
+ idx++;
+ return true;
+fail_len:
+ drm_dbg_kms(mgr->dev, "sink event notify parse length fail %d %d\n", idx, raw->curlen);
+ return false;
+}
+
bool drm_dp_sideband_parse_req(const struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_sideband_msg_rx *raw,
struct drm_dp_sideband_msg_req_body *msg)
@@ -1178,6 +1222,8 @@ bool drm_dp_sideband_parse_req(const struct drm_dp_mst_topology_mgr *mgr,
return drm_dp_sideband_parse_connection_status_notify(mgr, raw, msg);
case DP_RESOURCE_STATUS_NOTIFY:
return drm_dp_sideband_parse_resource_status_notify(mgr, raw, msg);
+ case DP_SINK_EVENT_NOTIFY:
+ return drm_dp_sideband_parse_sink_event_notify(mgr, raw, msg);
default:
drm_err(mgr->dev, "Got unknown request 0x%02x (%s)\n",
msg->req_type, drm_dp_mst_req_type_str(msg->req_type));
@@ -4113,6 +4159,8 @@ drm_dp_mst_process_up_req(struct drm_dp_mst_topology_mgr *mgr,
guid = msg->u.conn_stat.guid;
else if (msg->req_type == DP_RESOURCE_STATUS_NOTIFY)
guid = msg->u.resource_stat.guid;
+ else if (msg->req_type == DP_SINK_EVENT_NOTIFY)
+ guid = msg->u.sink_event.guid;

if (guid)
mstb = drm_dp_get_mst_branch_device_by_guid(mgr, guid);
@@ -4184,7 +4232,8 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
drm_dp_sideband_parse_req(mgr, &mgr->up_req_recv, &up_req->msg);

if (up_req->msg.req_type != DP_CONNECTION_STATUS_NOTIFY &&
- up_req->msg.req_type != DP_RESOURCE_STATUS_NOTIFY) {
+ up_req->msg.req_type != DP_RESOURCE_STATUS_NOTIFY &&
+ up_req->msg.req_type != DP_SINK_EVENT_NOTIFY) {
drm_dbg_kms(mgr->dev, "Received unknown up req type, ignoring: %x\n",
up_req->msg.req_type);
kfree(up_req);
@@ -4212,6 +4261,12 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
drm_dbg_kms(mgr->dev, "Got RSN: pn: %d avail_pbn %d\n",
res_stat->port_number,
res_stat->available_pbn);
+ } else if (up_req->msg.req_type == DP_SINK_EVENT_NOTIFY) {
+ const struct drm_dp_sink_event_notify *sink_event =
+ &up_req->msg.u.sink_event;
+
+ drm_dbg_kms(mgr->dev, "Got SEN: pn: %d event_id %d\n",
+ sink_event->port_number, sink_event->event_id);
}

up_req->hdr = mgr->up_req_recv.initial_hdr;
diff --git a/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c b/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
index 7bbeb1e5bc97..d49c10d52d88 100644
--- a/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
+++ b/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
@@ -164,6 +164,7 @@ sideband_msg_req_encode_decode(struct drm_dp_sideband_msg_req_body *in)
switch (in->req_type) {
case DP_CONNECTION_STATUS_NOTIFY:
case DP_RESOURCE_STATUS_NOTIFY:
+ case DP_SINK_EVENT_NOTIFY:
memcpy(&rxmsg->msg, txmsg->msg, ARRAY_SIZE(rxmsg->msg));
rxmsg->curlen = txmsg->cur_len;
if (!drm_dp_sideband_parse_req(mgr, rxmsg, out)) {
@@ -387,10 +388,17 @@ int igt_dp_mst_sideband_msg_req_decode(void *unused)
in.u.resource_stat.available_pbn = 0xcdef;
DO_TEST();

+ in.req_type = DP_SINK_EVENT_NOTIFY;
+ in.u.sink_event.port_number = 0xf;
+ get_random_bytes(in.u.sink_event.guid, sizeof(in.u.sink_event.guid));
+ in.u.sink_event.event_id = 0xcdef;
+ DO_TEST();
+
#undef DO_TEST
#define DO_TEST(req_type) FAIL_ON(!sideband_msg_req_parse(req_type))
DO_TEST(DP_CONNECTION_STATUS_NOTIFY);
DO_TEST(DP_RESOURCE_STATUS_NOTIFY);
+ DO_TEST(DP_SINK_EVENT_NOTIFY);

DO_TEST(DP_REMOTE_I2C_WRITE);
#undef DO_TEST
diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
index ddb9231d0309..dd1cac5cdb0f 100644
--- a/include/drm/drm_dp_mst_helper.h
+++ b/include/drm/drm_dp_mst_helper.h
@@ -439,6 +439,19 @@ struct drm_dp_resource_status_notify {
u16 available_pbn;
};

+#define DP_SINK_EVENT_PANEL_REPLAY_ACTIVE_FRAME_CRC_ERROR BIT(0)
+#define DP_SINK_EVENT_PANEL_REPLAY_RFB_STORAGE_ERROR BIT(1)
+#define DP_SINK_EVENT_DSC_RC_BUFFER_UNDER_RUN BIT(2)
+#define DP_SINK_EVENT_DSC_RC_BUFFER_OVERFLOW BIT(3)
+#define DP_SINK_EVENT_DSC_CHUNK_LENGTH_ERROR BIT(4)
+#define DP_SINK_EVENT_CEC_IRQ_EVENT BIT(5)
+
+struct drm_dp_sink_event_notify {
+ u8 port_number;
+ u8 guid[16];
+ u16 event_id;
+};
+
struct drm_dp_query_payload_ack_reply {
u8 port_number;
u16 allocated_pbn;
@@ -450,6 +463,7 @@ struct drm_dp_sideband_msg_req_body {
struct drm_dp_connection_status_notify conn_stat;
struct drm_dp_port_number_req port_num;
struct drm_dp_resource_status_notify resource_stat;
+ struct drm_dp_sink_event_notify sink_event;

struct drm_dp_query_payload query_payload;
struct drm_dp_allocate_payload allocate_payload;
--
2.32.0.93.g670b81a890-goog