forked from Minki/linux
2f015ec6ea
Unfortunately the DP MST helpers do not have much in the way of debugging utilities. So, let's add some! This adds basic debugging output for down sideband requests that we send from the driver, so that we can actually discern what's happening when sideband requests timeout. Since there wasn't really a good way of testing that any of this worked, I ended up writing simple selftests that lightly test sideband message encoding and decoding as well. Enjoy! Changes since v1: * Clean up DO_TEST() and sideband_msg_req_encode_decode() - danvet * Get rid of pr_fmt(), just define a prefix string instead and use drm_printf() * Check highest bit of VCPI in drm_dp_decode_sideband_req() - danvet * Make the switch case order between drm_dp_decode_sideband_req() and drm_dp_encode_sideband_req() the same - danvet * Only check DRM_UT_DP - danvet * Clean up sideband_msg_req_equal() from selftests a bit, and add comments explaining why we can't just use memcmp - danvet Cc: Juston Li <juston.li@intel.com> Cc: Imre Deak <imre.deak@intel.com> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com> Cc: Harry Wentland <hwentlan@amd.com> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> Signed-off-by: Lyude Paul <lyude@redhat.com> Link: https://patchwork.freedesktop.org/patch/msgid/20190903204645.25487-8-lyude@redhat.com
239 lines
5.9 KiB
C
239 lines
5.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Test cases for for the DRM DP MST helpers
|
|
*/
|
|
|
|
#define PREFIX_STR "[drm_dp_mst_helper]"
|
|
|
|
#include <drm/drm_dp_mst_helper.h>
|
|
#include <drm/drm_print.h>
|
|
|
|
#include "../drm_dp_mst_topology_internal.h"
|
|
#include "test-drm_modeset_common.h"
|
|
|
|
int igt_dp_mst_calc_pbn_mode(void *ignored)
|
|
{
|
|
int pbn, i;
|
|
const struct {
|
|
int rate;
|
|
int bpp;
|
|
int expected;
|
|
} test_params[] = {
|
|
{ 154000, 30, 689 },
|
|
{ 234000, 30, 1047 },
|
|
{ 297000, 24, 1063 },
|
|
};
|
|
|
|
for (i = 0; i < ARRAY_SIZE(test_params); i++) {
|
|
pbn = drm_dp_calc_pbn_mode(test_params[i].rate,
|
|
test_params[i].bpp);
|
|
FAIL(pbn != test_params[i].expected,
|
|
"Expected PBN %d for clock %d bpp %d, got %d\n",
|
|
test_params[i].expected, test_params[i].rate,
|
|
test_params[i].bpp, pbn);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool
|
|
sideband_msg_req_equal(const struct drm_dp_sideband_msg_req_body *in,
|
|
const struct drm_dp_sideband_msg_req_body *out)
|
|
{
|
|
const struct drm_dp_remote_i2c_read_tx *txin, *txout;
|
|
int i;
|
|
|
|
if (in->req_type != out->req_type)
|
|
return false;
|
|
|
|
switch (in->req_type) {
|
|
/*
|
|
* Compare struct members manually for request types which can't be
|
|
* compared simply using memcmp(). This is because said request types
|
|
* contain pointers to other allocated structs
|
|
*/
|
|
case DP_REMOTE_I2C_READ:
|
|
#define IN in->u.i2c_read
|
|
#define OUT out->u.i2c_read
|
|
if (IN.num_bytes_read != OUT.num_bytes_read ||
|
|
IN.num_transactions != OUT.num_transactions ||
|
|
IN.port_number != OUT.port_number ||
|
|
IN.read_i2c_device_id != OUT.read_i2c_device_id)
|
|
return false;
|
|
|
|
for (i = 0; i < IN.num_transactions; i++) {
|
|
txin = &IN.transactions[i];
|
|
txout = &OUT.transactions[i];
|
|
|
|
if (txin->i2c_dev_id != txout->i2c_dev_id ||
|
|
txin->no_stop_bit != txout->no_stop_bit ||
|
|
txin->num_bytes != txout->num_bytes ||
|
|
txin->i2c_transaction_delay !=
|
|
txout->i2c_transaction_delay)
|
|
return false;
|
|
|
|
if (memcmp(txin->bytes, txout->bytes,
|
|
txin->num_bytes) != 0)
|
|
return false;
|
|
}
|
|
break;
|
|
#undef IN
|
|
#undef OUT
|
|
|
|
case DP_REMOTE_DPCD_WRITE:
|
|
#define IN in->u.dpcd_write
|
|
#define OUT out->u.dpcd_write
|
|
if (IN.dpcd_address != OUT.dpcd_address ||
|
|
IN.num_bytes != OUT.num_bytes ||
|
|
IN.port_number != OUT.port_number)
|
|
return false;
|
|
|
|
return memcmp(IN.bytes, OUT.bytes, IN.num_bytes) == 0;
|
|
#undef IN
|
|
#undef OUT
|
|
|
|
case DP_REMOTE_I2C_WRITE:
|
|
#define IN in->u.i2c_write
|
|
#define OUT out->u.i2c_write
|
|
if (IN.port_number != OUT.port_number ||
|
|
IN.write_i2c_device_id != OUT.write_i2c_device_id ||
|
|
IN.num_bytes != OUT.num_bytes)
|
|
return false;
|
|
|
|
return memcmp(IN.bytes, OUT.bytes, IN.num_bytes) == 0;
|
|
#undef IN
|
|
#undef OUT
|
|
|
|
default:
|
|
return memcmp(in, out, sizeof(*in)) == 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
sideband_msg_req_encode_decode(struct drm_dp_sideband_msg_req_body *in)
|
|
{
|
|
struct drm_dp_sideband_msg_req_body out = {0};
|
|
struct drm_printer p = drm_err_printer(PREFIX_STR);
|
|
struct drm_dp_sideband_msg_tx txmsg;
|
|
int i, ret;
|
|
|
|
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);
|
|
return false;
|
|
}
|
|
|
|
if (!sideband_msg_req_equal(in, &out)) {
|
|
drm_printf(&p, "Encode/decode failed, expected:\n");
|
|
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);
|
|
return false;
|
|
}
|
|
|
|
switch (in->req_type) {
|
|
case DP_REMOTE_DPCD_WRITE:
|
|
kfree(out.u.dpcd_write.bytes);
|
|
break;
|
|
case DP_REMOTE_I2C_READ:
|
|
for (i = 0; i < out.u.i2c_read.num_transactions; i++)
|
|
kfree(out.u.i2c_read.transactions[i].bytes);
|
|
break;
|
|
case DP_REMOTE_I2C_WRITE:
|
|
kfree(out.u.i2c_write.bytes);
|
|
break;
|
|
}
|
|
|
|
/* Clear everything but the req_type for the input */
|
|
memset(&in->u, 0, sizeof(in->u));
|
|
|
|
return true;
|
|
}
|
|
|
|
int igt_dp_mst_sideband_msg_req_decode(void *unused)
|
|
{
|
|
struct drm_dp_sideband_msg_req_body in = { 0 };
|
|
u8 data[] = { 0xff, 0x0, 0xdd };
|
|
int i;
|
|
|
|
#define DO_TEST() FAIL_ON(!sideband_msg_req_encode_decode(&in))
|
|
|
|
in.req_type = DP_ENUM_PATH_RESOURCES;
|
|
in.u.port_num.port_number = 5;
|
|
DO_TEST();
|
|
|
|
in.req_type = DP_POWER_UP_PHY;
|
|
in.u.port_num.port_number = 5;
|
|
DO_TEST();
|
|
|
|
in.req_type = DP_POWER_DOWN_PHY;
|
|
in.u.port_num.port_number = 5;
|
|
DO_TEST();
|
|
|
|
in.req_type = DP_ALLOCATE_PAYLOAD;
|
|
in.u.allocate_payload.number_sdp_streams = 3;
|
|
for (i = 0; i < in.u.allocate_payload.number_sdp_streams; i++)
|
|
in.u.allocate_payload.sdp_stream_sink[i] = i + 1;
|
|
DO_TEST();
|
|
in.u.allocate_payload.port_number = 0xf;
|
|
DO_TEST();
|
|
in.u.allocate_payload.vcpi = 0x7f;
|
|
DO_TEST();
|
|
in.u.allocate_payload.pbn = U16_MAX;
|
|
DO_TEST();
|
|
|
|
in.req_type = DP_QUERY_PAYLOAD;
|
|
in.u.query_payload.port_number = 0xf;
|
|
DO_TEST();
|
|
in.u.query_payload.vcpi = 0x7f;
|
|
DO_TEST();
|
|
|
|
in.req_type = DP_REMOTE_DPCD_READ;
|
|
in.u.dpcd_read.port_number = 0xf;
|
|
DO_TEST();
|
|
in.u.dpcd_read.dpcd_address = 0xfedcb;
|
|
DO_TEST();
|
|
in.u.dpcd_read.num_bytes = U8_MAX;
|
|
DO_TEST();
|
|
|
|
in.req_type = DP_REMOTE_DPCD_WRITE;
|
|
in.u.dpcd_write.port_number = 0xf;
|
|
DO_TEST();
|
|
in.u.dpcd_write.dpcd_address = 0xfedcb;
|
|
DO_TEST();
|
|
in.u.dpcd_write.num_bytes = ARRAY_SIZE(data);
|
|
in.u.dpcd_write.bytes = data;
|
|
DO_TEST();
|
|
|
|
in.req_type = DP_REMOTE_I2C_READ;
|
|
in.u.i2c_read.port_number = 0xf;
|
|
DO_TEST();
|
|
in.u.i2c_read.read_i2c_device_id = 0x7f;
|
|
DO_TEST();
|
|
in.u.i2c_read.num_transactions = 3;
|
|
in.u.i2c_read.num_bytes_read = ARRAY_SIZE(data) * 3;
|
|
for (i = 0; i < in.u.i2c_read.num_transactions; i++) {
|
|
in.u.i2c_read.transactions[i].bytes = data;
|
|
in.u.i2c_read.transactions[i].num_bytes = ARRAY_SIZE(data);
|
|
in.u.i2c_read.transactions[i].i2c_dev_id = 0x7f & ~i;
|
|
in.u.i2c_read.transactions[i].i2c_transaction_delay = 0xf & ~i;
|
|
}
|
|
DO_TEST();
|
|
|
|
in.req_type = DP_REMOTE_I2C_WRITE;
|
|
in.u.i2c_write.port_number = 0xf;
|
|
DO_TEST();
|
|
in.u.i2c_write.write_i2c_device_id = 0x7f;
|
|
DO_TEST();
|
|
in.u.i2c_write.num_bytes = ARRAY_SIZE(data);
|
|
in.u.i2c_write.bytes = data;
|
|
DO_TEST();
|
|
|
|
#undef DO_TEST
|
|
return 0;
|
|
}
|