1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Test cases for the DRM DP MST helpers 4 * 5 * Copyright (c) 2022 Maíra Canal <mairacanal@riseup.net> 6 */ 7 8 #include <kunit/test.h> 9 10 #include <drm/display/drm_dp_mst_helper.h> 11 #include <drm/drm_print.h> 12 13 #include "../display/drm_dp_mst_topology_internal.h" 14 15 struct drm_dp_mst_calc_pbn_mode_test { 16 const int clock; 17 const int bpp; 18 const bool dsc; 19 const int expected; 20 }; 21 22 static const struct drm_dp_mst_calc_pbn_mode_test drm_dp_mst_calc_pbn_mode_cases[] = { 23 { 24 .clock = 154000, 25 .bpp = 30, 26 .dsc = false, 27 .expected = 689 28 }, 29 { 30 .clock = 234000, 31 .bpp = 30, 32 .dsc = false, 33 .expected = 1047 34 }, 35 { 36 .clock = 297000, 37 .bpp = 24, 38 .dsc = false, 39 .expected = 1063 40 }, 41 { 42 .clock = 332880, 43 .bpp = 24, 44 .dsc = true, 45 .expected = 50 46 }, 47 { 48 .clock = 324540, 49 .bpp = 24, 50 .dsc = true, 51 .expected = 49 52 }, 53 }; 54 55 static void drm_test_dp_mst_calc_pbn_mode(struct kunit *test) 56 { 57 const struct drm_dp_mst_calc_pbn_mode_test *params = test->param_value; 58 59 KUNIT_EXPECT_EQ(test, drm_dp_calc_pbn_mode(params->clock, params->bpp, params->dsc), 60 params->expected); 61 } 62 63 static void dp_mst_calc_pbn_mode_desc(const struct drm_dp_mst_calc_pbn_mode_test *t, char *desc) 64 { 65 sprintf(desc, "Clock %d BPP %d DSC %s", t->clock, t->bpp, t->dsc ? "enabled" : "disabled"); 66 } 67 68 KUNIT_ARRAY_PARAM(drm_dp_mst_calc_pbn_mode, drm_dp_mst_calc_pbn_mode_cases, 69 dp_mst_calc_pbn_mode_desc); 70 71 static u8 data[] = { 0xff, 0x00, 0xdd }; 72 73 struct drm_dp_mst_sideband_msg_req_test { 74 const char *desc; 75 const struct drm_dp_sideband_msg_req_body in; 76 }; 77 78 static const struct drm_dp_mst_sideband_msg_req_test drm_dp_mst_sideband_msg_req_cases[] = { 79 { 80 .desc = "DP_ENUM_PATH_RESOURCES with port number", 81 .in = { 82 .req_type = DP_ENUM_PATH_RESOURCES, 83 .u.port_num.port_number = 5, 84 }, 85 }, 86 { 87 .desc = "DP_POWER_UP_PHY with port number", 88 .in = { 89 .req_type = DP_POWER_UP_PHY, 90 .u.port_num.port_number = 5, 91 }, 92 }, 93 { 94 .desc = "DP_POWER_DOWN_PHY with port number", 95 .in = { 96 .req_type = DP_POWER_DOWN_PHY, 97 .u.port_num.port_number = 5, 98 }, 99 }, 100 { 101 .desc = "DP_ALLOCATE_PAYLOAD with SDP stream sinks", 102 .in = { 103 .req_type = DP_ALLOCATE_PAYLOAD, 104 .u.allocate_payload.number_sdp_streams = 3, 105 .u.allocate_payload.sdp_stream_sink = { 1, 2, 3 }, 106 }, 107 }, 108 { 109 .desc = "DP_ALLOCATE_PAYLOAD with port number", 110 .in = { 111 .req_type = DP_ALLOCATE_PAYLOAD, 112 .u.allocate_payload.port_number = 0xf, 113 }, 114 }, 115 { 116 .desc = "DP_ALLOCATE_PAYLOAD with VCPI", 117 .in = { 118 .req_type = DP_ALLOCATE_PAYLOAD, 119 .u.allocate_payload.vcpi = 0x7f, 120 }, 121 }, 122 { 123 .desc = "DP_ALLOCATE_PAYLOAD with PBN", 124 .in = { 125 .req_type = DP_ALLOCATE_PAYLOAD, 126 .u.allocate_payload.pbn = U16_MAX, 127 }, 128 }, 129 { 130 .desc = "DP_QUERY_PAYLOAD with port number", 131 .in = { 132 .req_type = DP_QUERY_PAYLOAD, 133 .u.query_payload.port_number = 0xf, 134 }, 135 }, 136 { 137 .desc = "DP_QUERY_PAYLOAD with VCPI", 138 .in = { 139 .req_type = DP_QUERY_PAYLOAD, 140 .u.query_payload.vcpi = 0x7f, 141 }, 142 }, 143 { 144 .desc = "DP_REMOTE_DPCD_READ with port number", 145 .in = { 146 .req_type = DP_REMOTE_DPCD_READ, 147 .u.dpcd_read.port_number = 0xf, 148 }, 149 }, 150 { 151 .desc = "DP_REMOTE_DPCD_READ with DPCD address", 152 .in = { 153 .req_type = DP_REMOTE_DPCD_READ, 154 .u.dpcd_read.dpcd_address = 0xfedcb, 155 }, 156 }, 157 { 158 .desc = "DP_REMOTE_DPCD_READ with max number of bytes", 159 .in = { 160 .req_type = DP_REMOTE_DPCD_READ, 161 .u.dpcd_read.num_bytes = U8_MAX, 162 }, 163 }, 164 { 165 .desc = "DP_REMOTE_DPCD_WRITE with port number", 166 .in = { 167 .req_type = DP_REMOTE_DPCD_WRITE, 168 .u.dpcd_write.port_number = 0xf, 169 }, 170 }, 171 { 172 .desc = "DP_REMOTE_DPCD_WRITE with DPCD address", 173 .in = { 174 .req_type = DP_REMOTE_DPCD_WRITE, 175 .u.dpcd_write.dpcd_address = 0xfedcb, 176 }, 177 }, 178 { 179 .desc = "DP_REMOTE_DPCD_WRITE with data array", 180 .in = { 181 .req_type = DP_REMOTE_DPCD_WRITE, 182 .u.dpcd_write.num_bytes = ARRAY_SIZE(data), 183 .u.dpcd_write.bytes = data, 184 }, 185 }, 186 { 187 .desc = "DP_REMOTE_I2C_READ with port number", 188 .in = { 189 .req_type = DP_REMOTE_I2C_READ, 190 .u.i2c_read.port_number = 0xf, 191 }, 192 }, 193 { 194 .desc = "DP_REMOTE_I2C_READ with I2C device ID", 195 .in = { 196 .req_type = DP_REMOTE_I2C_READ, 197 .u.i2c_read.read_i2c_device_id = 0x7f, 198 }, 199 }, 200 { 201 .desc = "DP_REMOTE_I2C_READ with transactions array", 202 .in = { 203 .req_type = DP_REMOTE_I2C_READ, 204 .u.i2c_read.num_transactions = 3, 205 .u.i2c_read.num_bytes_read = ARRAY_SIZE(data) * 3, 206 .u.i2c_read.transactions = { 207 { .bytes = data, .num_bytes = ARRAY_SIZE(data), .i2c_dev_id = 0x7f, 208 .i2c_transaction_delay = 0xf, }, 209 { .bytes = data, .num_bytes = ARRAY_SIZE(data), .i2c_dev_id = 0x7e, 210 .i2c_transaction_delay = 0xe, }, 211 { .bytes = data, .num_bytes = ARRAY_SIZE(data), .i2c_dev_id = 0x7d, 212 .i2c_transaction_delay = 0xd, }, 213 }, 214 }, 215 }, 216 { 217 .desc = "DP_REMOTE_I2C_WRITE with port number", 218 .in = { 219 .req_type = DP_REMOTE_I2C_WRITE, 220 .u.i2c_write.port_number = 0xf, 221 }, 222 }, 223 { 224 .desc = "DP_REMOTE_I2C_WRITE with I2C device ID", 225 .in = { 226 .req_type = DP_REMOTE_I2C_WRITE, 227 .u.i2c_write.write_i2c_device_id = 0x7f, 228 }, 229 }, 230 { 231 .desc = "DP_REMOTE_I2C_WRITE with data array", 232 .in = { 233 .req_type = DP_REMOTE_I2C_WRITE, 234 .u.i2c_write.num_bytes = ARRAY_SIZE(data), 235 .u.i2c_write.bytes = data, 236 }, 237 }, 238 { 239 .desc = "DP_QUERY_STREAM_ENC_STATUS with stream ID", 240 .in = { 241 .req_type = DP_QUERY_STREAM_ENC_STATUS, 242 .u.enc_status.stream_id = 1, 243 }, 244 }, 245 { 246 .desc = "DP_QUERY_STREAM_ENC_STATUS with client ID", 247 .in = { 248 .req_type = DP_QUERY_STREAM_ENC_STATUS, 249 .u.enc_status.client_id = { 0x4f, 0x7f, 0xb4, 0x00, 0x8c, 0x0d, 0x67 }, 250 }, 251 }, 252 { 253 .desc = "DP_QUERY_STREAM_ENC_STATUS with stream event", 254 .in = { 255 .req_type = DP_QUERY_STREAM_ENC_STATUS, 256 .u.enc_status.stream_event = 3, 257 }, 258 }, 259 { 260 .desc = "DP_QUERY_STREAM_ENC_STATUS with valid stream event", 261 .in = { 262 .req_type = DP_QUERY_STREAM_ENC_STATUS, 263 .u.enc_status.valid_stream_event = 0, 264 }, 265 }, 266 { 267 .desc = "DP_QUERY_STREAM_ENC_STATUS with stream behavior", 268 .in = { 269 .req_type = DP_QUERY_STREAM_ENC_STATUS, 270 .u.enc_status.stream_behavior = 3, 271 }, 272 }, 273 { 274 .desc = "DP_QUERY_STREAM_ENC_STATUS with a valid stream behavior", 275 .in = { 276 .req_type = DP_QUERY_STREAM_ENC_STATUS, 277 .u.enc_status.valid_stream_behavior = 1, 278 } 279 }, 280 }; 281 282 static bool 283 sideband_msg_req_equal(const struct drm_dp_sideband_msg_req_body *in, 284 const struct drm_dp_sideband_msg_req_body *out) 285 { 286 const struct drm_dp_remote_i2c_read_tx *txin, *txout; 287 int i; 288 289 if (in->req_type != out->req_type) 290 return false; 291 292 switch (in->req_type) { 293 /* 294 * Compare struct members manually for request types which can't be 295 * compared simply using memcmp(). This is because said request types 296 * contain pointers to other allocated structs 297 */ 298 case DP_REMOTE_I2C_READ: 299 #define IN in->u.i2c_read 300 #define OUT out->u.i2c_read 301 if (IN.num_bytes_read != OUT.num_bytes_read || 302 IN.num_transactions != OUT.num_transactions || 303 IN.port_number != OUT.port_number || 304 IN.read_i2c_device_id != OUT.read_i2c_device_id) 305 return false; 306 307 for (i = 0; i < IN.num_transactions; i++) { 308 txin = &IN.transactions[i]; 309 txout = &OUT.transactions[i]; 310 311 if (txin->i2c_dev_id != txout->i2c_dev_id || 312 txin->no_stop_bit != txout->no_stop_bit || 313 txin->num_bytes != txout->num_bytes || 314 txin->i2c_transaction_delay != 315 txout->i2c_transaction_delay) 316 return false; 317 318 if (memcmp(txin->bytes, txout->bytes, 319 txin->num_bytes) != 0) 320 return false; 321 } 322 break; 323 #undef IN 324 #undef OUT 325 326 case DP_REMOTE_DPCD_WRITE: 327 #define IN in->u.dpcd_write 328 #define OUT out->u.dpcd_write 329 if (IN.dpcd_address != OUT.dpcd_address || 330 IN.num_bytes != OUT.num_bytes || 331 IN.port_number != OUT.port_number) 332 return false; 333 334 return memcmp(IN.bytes, OUT.bytes, IN.num_bytes) == 0; 335 #undef IN 336 #undef OUT 337 338 case DP_REMOTE_I2C_WRITE: 339 #define IN in->u.i2c_write 340 #define OUT out->u.i2c_write 341 if (IN.port_number != OUT.port_number || 342 IN.write_i2c_device_id != OUT.write_i2c_device_id || 343 IN.num_bytes != OUT.num_bytes) 344 return false; 345 346 return memcmp(IN.bytes, OUT.bytes, IN.num_bytes) == 0; 347 #undef IN 348 #undef OUT 349 350 default: 351 return memcmp(in, out, sizeof(*in)) == 0; 352 } 353 354 return true; 355 } 356 357 static void drm_test_dp_mst_msg_printf(struct drm_printer *p, struct va_format *vaf) 358 { 359 struct kunit *test = p->arg; 360 361 kunit_err(test, "%pV", vaf); 362 } 363 364 static void drm_test_dp_mst_sideband_msg_req_decode(struct kunit *test) 365 { 366 const struct drm_dp_mst_sideband_msg_req_test *params = test->param_value; 367 const struct drm_dp_sideband_msg_req_body *in = ¶ms->in; 368 struct drm_dp_sideband_msg_req_body *out; 369 struct drm_dp_sideband_msg_tx *txmsg; 370 struct drm_printer p = { 371 .printfn = drm_test_dp_mst_msg_printf, 372 .arg = test 373 }; 374 int i; 375 376 out = kunit_kzalloc(test, sizeof(*out), GFP_KERNEL); 377 KUNIT_ASSERT_NOT_NULL(test, out); 378 379 txmsg = kunit_kzalloc(test, sizeof(*txmsg), GFP_KERNEL); 380 KUNIT_ASSERT_NOT_NULL(test, txmsg); 381 382 drm_dp_encode_sideband_req(in, txmsg); 383 KUNIT_EXPECT_GE_MSG(test, drm_dp_decode_sideband_req(txmsg, out), 0, 384 "Failed to decode sideband request"); 385 386 if (!sideband_msg_req_equal(in, out)) { 387 KUNIT_FAIL(test, "Encode/decode failed"); 388 kunit_err(test, "Expected:"); 389 drm_dp_dump_sideband_msg_req_body(in, 1, &p); 390 kunit_err(test, "Got:"); 391 drm_dp_dump_sideband_msg_req_body(out, 1, &p); 392 } 393 394 switch (in->req_type) { 395 case DP_REMOTE_DPCD_WRITE: 396 kfree(out->u.dpcd_write.bytes); 397 break; 398 case DP_REMOTE_I2C_READ: 399 for (i = 0; i < out->u.i2c_read.num_transactions; i++) 400 kfree(out->u.i2c_read.transactions[i].bytes); 401 break; 402 case DP_REMOTE_I2C_WRITE: 403 kfree(out->u.i2c_write.bytes); 404 break; 405 } 406 } 407 408 static void 409 drm_dp_mst_sideband_msg_req_desc(const struct drm_dp_mst_sideband_msg_req_test *t, char *desc) 410 { 411 strcpy(desc, t->desc); 412 } 413 414 KUNIT_ARRAY_PARAM(drm_dp_mst_sideband_msg_req, drm_dp_mst_sideband_msg_req_cases, 415 drm_dp_mst_sideband_msg_req_desc); 416 417 static struct kunit_case drm_dp_mst_helper_tests[] = { 418 KUNIT_CASE_PARAM(drm_test_dp_mst_calc_pbn_mode, drm_dp_mst_calc_pbn_mode_gen_params), 419 KUNIT_CASE_PARAM(drm_test_dp_mst_sideband_msg_req_decode, 420 drm_dp_mst_sideband_msg_req_gen_params), 421 { } 422 }; 423 424 static struct kunit_suite drm_dp_mst_helper_test_suite = { 425 .name = "drm_dp_mst_helper", 426 .test_cases = drm_dp_mst_helper_tests, 427 }; 428 429 kunit_test_suite(drm_dp_mst_helper_test_suite); 430 431 MODULE_LICENSE("GPL"); 432