| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Test cases for for the DRM DP MST helpers |
| */ |
| |
| #define PREFIX_STR "[drm_dp_mst_helper]" |
| |
| #include <linux/random.h> |
| |
| #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; |
| bool dsc; |
| } test_params[] = { |
| { 154000, 30, 689, false }, |
| { 234000, 30, 1047, false }, |
| { 297000, 24, 1063, false }, |
| { 332880, 24, 50, true }, |
| { 324540, 24, 49, true }, |
| }; |
| |
| for (i = 0; i < ARRAY_SIZE(test_params); i++) { |
| pbn = drm_dp_calc_pbn_mode(test_params[i].rate, |
| test_params[i].bpp, |
| test_params[i].dsc); |
| 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; |
| struct drm_printer p = drm_err_printer(PREFIX_STR); |
| struct drm_dp_sideband_msg_tx *txmsg; |
| int i, ret; |
| bool result = true; |
| |
| out = kzalloc(sizeof(*out), GFP_KERNEL); |
| if (!out) |
| return false; |
| |
| txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); |
| if (!txmsg) |
| return false; |
| |
| 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; |
| goto out; |
| } |
| |
| 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); |
| result = false; |
| goto out; |
| } |
| |
| 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)); |
| |
| out: |
| kfree(out); |
| kfree(txmsg); |
| return result; |
| } |
| |
| 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(); |
| |
| in.req_type = DP_QUERY_STREAM_ENC_STATUS; |
| in.u.enc_status.stream_id = 1; |
| DO_TEST(); |
| get_random_bytes(in.u.enc_status.client_id, |
| sizeof(in.u.enc_status.client_id)); |
| DO_TEST(); |
| in.u.enc_status.stream_event = 3; |
| DO_TEST(); |
| in.u.enc_status.valid_stream_event = 0; |
| DO_TEST(); |
| in.u.enc_status.stream_behavior = 3; |
| DO_TEST(); |
| in.u.enc_status.valid_stream_behavior = 1; |
| DO_TEST(); |
| |
| #undef DO_TEST |
| return 0; |
| } |