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 <[email protected]>
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 = 1191
46 },
47 {
48 .clock = 324540,
49 .bpp = 24,
50 .dsc = true,
51 .expected = 1161
52 },
53 };
54
drm_test_dp_mst_calc_pbn_mode(struct kunit * test)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 << 4),
60 params->expected);
61 }
62
dp_mst_calc_pbn_mode_desc(const struct drm_dp_mst_calc_pbn_mode_test * t,char * desc)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 struct drm_dp_mst_calc_pbn_div_test {
72 int link_rate;
73 int lane_count;
74 fixed20_12 expected;
75 };
76
77 #define fp_init(__int, __frac) { \
78 .full = (__int) * (1 << 12) + \
79 (__frac) * (1 << 12) / 100000 \
80 }
81
82 static const struct drm_dp_mst_calc_pbn_div_test drm_dp_mst_calc_pbn_div_dp1_4_cases[] = {
83 /*
84 * UHBR rates (DP Standard v2.1 2.7.6.3, specifying the rounded to
85 * closest value to 2 decimal places):
86 * .expected = .link_rate * .lane_count * 0.9671 / 8 / 54 / 100
87 * DP1.4 rates (DP Standard v2.1 2.6.4.2):
88 * .expected = .link_rate * .lane_count * 0.8000 / 8 / 54 / 100
89 *
90 * truncated to 5 decimal places.
91 */
92 {
93 .link_rate = 2000000,
94 .lane_count = 4,
95 .expected = fp_init(179, 9259), /* 179.09259 */
96 },
97 {
98 .link_rate = 2000000,
99 .lane_count = 2,
100 .expected = fp_init(89, 54629),
101 },
102 {
103 .link_rate = 2000000,
104 .lane_count = 1,
105 .expected = fp_init(44, 77314),
106 },
107 {
108 .link_rate = 1350000,
109 .lane_count = 4,
110 .expected = fp_init(120, 88750),
111 },
112 {
113 .link_rate = 1350000,
114 .lane_count = 2,
115 .expected = fp_init(60, 44375),
116 },
117 {
118 .link_rate = 1350000,
119 .lane_count = 1,
120 .expected = fp_init(30, 22187),
121 },
122 {
123 .link_rate = 1000000,
124 .lane_count = 4,
125 .expected = fp_init(89, 54629),
126 },
127 {
128 .link_rate = 1000000,
129 .lane_count = 2,
130 .expected = fp_init(44, 77314),
131 },
132 {
133 .link_rate = 1000000,
134 .lane_count = 1,
135 .expected = fp_init(22, 38657),
136 },
137 {
138 .link_rate = 810000,
139 .lane_count = 4,
140 .expected = fp_init(60, 0),
141 },
142 {
143 .link_rate = 810000,
144 .lane_count = 2,
145 .expected = fp_init(30, 0),
146 },
147 {
148 .link_rate = 810000,
149 .lane_count = 1,
150 .expected = fp_init(15, 0),
151 },
152 {
153 .link_rate = 540000,
154 .lane_count = 4,
155 .expected = fp_init(40, 0),
156 },
157 {
158 .link_rate = 540000,
159 .lane_count = 2,
160 .expected = fp_init(20, 0),
161 },
162 {
163 .link_rate = 540000,
164 .lane_count = 1,
165 .expected = fp_init(10, 0),
166 },
167 {
168 .link_rate = 270000,
169 .lane_count = 4,
170 .expected = fp_init(20, 0),
171 },
172 {
173 .link_rate = 270000,
174 .lane_count = 2,
175 .expected = fp_init(10, 0),
176 },
177 {
178 .link_rate = 270000,
179 .lane_count = 1,
180 .expected = fp_init(5, 0),
181 },
182 {
183 .link_rate = 162000,
184 .lane_count = 4,
185 .expected = fp_init(12, 0),
186 },
187 {
188 .link_rate = 162000,
189 .lane_count = 2,
190 .expected = fp_init(6, 0),
191 },
192 {
193 .link_rate = 162000,
194 .lane_count = 1,
195 .expected = fp_init(3, 0),
196 },
197 };
198
drm_test_dp_mst_calc_pbn_div(struct kunit * test)199 static void drm_test_dp_mst_calc_pbn_div(struct kunit *test)
200 {
201 const struct drm_dp_mst_calc_pbn_div_test *params = test->param_value;
202
203 KUNIT_EXPECT_EQ(test, drm_dp_get_vc_payload_bw(params->link_rate, params->lane_count).full,
204 params->expected.full);
205 }
206
dp_mst_calc_pbn_div_desc(const struct drm_dp_mst_calc_pbn_div_test * t,char * desc)207 static void dp_mst_calc_pbn_div_desc(const struct drm_dp_mst_calc_pbn_div_test *t, char *desc)
208 {
209 sprintf(desc, "Link rate %d lane count %d", t->link_rate, t->lane_count);
210 }
211
212 KUNIT_ARRAY_PARAM(drm_dp_mst_calc_pbn_div, drm_dp_mst_calc_pbn_div_dp1_4_cases,
213 dp_mst_calc_pbn_div_desc);
214
215 static u8 data[] = { 0xff, 0x00, 0xdd };
216
217 struct drm_dp_mst_sideband_msg_req_test {
218 const char *desc;
219 const struct drm_dp_sideband_msg_req_body in;
220 };
221
222 static const struct drm_dp_mst_sideband_msg_req_test drm_dp_mst_sideband_msg_req_cases[] = {
223 {
224 .desc = "DP_ENUM_PATH_RESOURCES with port number",
225 .in = {
226 .req_type = DP_ENUM_PATH_RESOURCES,
227 .u.port_num.port_number = 5,
228 },
229 },
230 {
231 .desc = "DP_POWER_UP_PHY with port number",
232 .in = {
233 .req_type = DP_POWER_UP_PHY,
234 .u.port_num.port_number = 5,
235 },
236 },
237 {
238 .desc = "DP_POWER_DOWN_PHY with port number",
239 .in = {
240 .req_type = DP_POWER_DOWN_PHY,
241 .u.port_num.port_number = 5,
242 },
243 },
244 {
245 .desc = "DP_ALLOCATE_PAYLOAD with SDP stream sinks",
246 .in = {
247 .req_type = DP_ALLOCATE_PAYLOAD,
248 .u.allocate_payload.number_sdp_streams = 3,
249 .u.allocate_payload.sdp_stream_sink = { 1, 2, 3 },
250 },
251 },
252 {
253 .desc = "DP_ALLOCATE_PAYLOAD with port number",
254 .in = {
255 .req_type = DP_ALLOCATE_PAYLOAD,
256 .u.allocate_payload.port_number = 0xf,
257 },
258 },
259 {
260 .desc = "DP_ALLOCATE_PAYLOAD with VCPI",
261 .in = {
262 .req_type = DP_ALLOCATE_PAYLOAD,
263 .u.allocate_payload.vcpi = 0x7f,
264 },
265 },
266 {
267 .desc = "DP_ALLOCATE_PAYLOAD with PBN",
268 .in = {
269 .req_type = DP_ALLOCATE_PAYLOAD,
270 .u.allocate_payload.pbn = U16_MAX,
271 },
272 },
273 {
274 .desc = "DP_QUERY_PAYLOAD with port number",
275 .in = {
276 .req_type = DP_QUERY_PAYLOAD,
277 .u.query_payload.port_number = 0xf,
278 },
279 },
280 {
281 .desc = "DP_QUERY_PAYLOAD with VCPI",
282 .in = {
283 .req_type = DP_QUERY_PAYLOAD,
284 .u.query_payload.vcpi = 0x7f,
285 },
286 },
287 {
288 .desc = "DP_REMOTE_DPCD_READ with port number",
289 .in = {
290 .req_type = DP_REMOTE_DPCD_READ,
291 .u.dpcd_read.port_number = 0xf,
292 },
293 },
294 {
295 .desc = "DP_REMOTE_DPCD_READ with DPCD address",
296 .in = {
297 .req_type = DP_REMOTE_DPCD_READ,
298 .u.dpcd_read.dpcd_address = 0xfedcb,
299 },
300 },
301 {
302 .desc = "DP_REMOTE_DPCD_READ with max number of bytes",
303 .in = {
304 .req_type = DP_REMOTE_DPCD_READ,
305 .u.dpcd_read.num_bytes = U8_MAX,
306 },
307 },
308 {
309 .desc = "DP_REMOTE_DPCD_WRITE with port number",
310 .in = {
311 .req_type = DP_REMOTE_DPCD_WRITE,
312 .u.dpcd_write.port_number = 0xf,
313 },
314 },
315 {
316 .desc = "DP_REMOTE_DPCD_WRITE with DPCD address",
317 .in = {
318 .req_type = DP_REMOTE_DPCD_WRITE,
319 .u.dpcd_write.dpcd_address = 0xfedcb,
320 },
321 },
322 {
323 .desc = "DP_REMOTE_DPCD_WRITE with data array",
324 .in = {
325 .req_type = DP_REMOTE_DPCD_WRITE,
326 .u.dpcd_write.num_bytes = ARRAY_SIZE(data),
327 .u.dpcd_write.bytes = data,
328 },
329 },
330 {
331 .desc = "DP_REMOTE_I2C_READ with port number",
332 .in = {
333 .req_type = DP_REMOTE_I2C_READ,
334 .u.i2c_read.port_number = 0xf,
335 },
336 },
337 {
338 .desc = "DP_REMOTE_I2C_READ with I2C device ID",
339 .in = {
340 .req_type = DP_REMOTE_I2C_READ,
341 .u.i2c_read.read_i2c_device_id = 0x7f,
342 },
343 },
344 {
345 .desc = "DP_REMOTE_I2C_READ with transactions array",
346 .in = {
347 .req_type = DP_REMOTE_I2C_READ,
348 .u.i2c_read.num_transactions = 3,
349 .u.i2c_read.num_bytes_read = ARRAY_SIZE(data) * 3,
350 .u.i2c_read.transactions = {
351 { .bytes = data, .num_bytes = ARRAY_SIZE(data), .i2c_dev_id = 0x7f,
352 .i2c_transaction_delay = 0xf, },
353 { .bytes = data, .num_bytes = ARRAY_SIZE(data), .i2c_dev_id = 0x7e,
354 .i2c_transaction_delay = 0xe, },
355 { .bytes = data, .num_bytes = ARRAY_SIZE(data), .i2c_dev_id = 0x7d,
356 .i2c_transaction_delay = 0xd, },
357 },
358 },
359 },
360 {
361 .desc = "DP_REMOTE_I2C_WRITE with port number",
362 .in = {
363 .req_type = DP_REMOTE_I2C_WRITE,
364 .u.i2c_write.port_number = 0xf,
365 },
366 },
367 {
368 .desc = "DP_REMOTE_I2C_WRITE with I2C device ID",
369 .in = {
370 .req_type = DP_REMOTE_I2C_WRITE,
371 .u.i2c_write.write_i2c_device_id = 0x7f,
372 },
373 },
374 {
375 .desc = "DP_REMOTE_I2C_WRITE with data array",
376 .in = {
377 .req_type = DP_REMOTE_I2C_WRITE,
378 .u.i2c_write.num_bytes = ARRAY_SIZE(data),
379 .u.i2c_write.bytes = data,
380 },
381 },
382 {
383 .desc = "DP_QUERY_STREAM_ENC_STATUS with stream ID",
384 .in = {
385 .req_type = DP_QUERY_STREAM_ENC_STATUS,
386 .u.enc_status.stream_id = 1,
387 },
388 },
389 {
390 .desc = "DP_QUERY_STREAM_ENC_STATUS with client ID",
391 .in = {
392 .req_type = DP_QUERY_STREAM_ENC_STATUS,
393 .u.enc_status.client_id = { 0x4f, 0x7f, 0xb4, 0x00, 0x8c, 0x0d, 0x67 },
394 },
395 },
396 {
397 .desc = "DP_QUERY_STREAM_ENC_STATUS with stream event",
398 .in = {
399 .req_type = DP_QUERY_STREAM_ENC_STATUS,
400 .u.enc_status.stream_event = 3,
401 },
402 },
403 {
404 .desc = "DP_QUERY_STREAM_ENC_STATUS with valid stream event",
405 .in = {
406 .req_type = DP_QUERY_STREAM_ENC_STATUS,
407 .u.enc_status.valid_stream_event = 0,
408 },
409 },
410 {
411 .desc = "DP_QUERY_STREAM_ENC_STATUS with stream behavior",
412 .in = {
413 .req_type = DP_QUERY_STREAM_ENC_STATUS,
414 .u.enc_status.stream_behavior = 3,
415 },
416 },
417 {
418 .desc = "DP_QUERY_STREAM_ENC_STATUS with a valid stream behavior",
419 .in = {
420 .req_type = DP_QUERY_STREAM_ENC_STATUS,
421 .u.enc_status.valid_stream_behavior = 1,
422 }
423 },
424 };
425
426 static bool
sideband_msg_req_equal(const struct drm_dp_sideband_msg_req_body * in,const struct drm_dp_sideband_msg_req_body * out)427 sideband_msg_req_equal(const struct drm_dp_sideband_msg_req_body *in,
428 const struct drm_dp_sideband_msg_req_body *out)
429 {
430 const struct drm_dp_remote_i2c_read_tx *txin, *txout;
431 int i;
432
433 if (in->req_type != out->req_type)
434 return false;
435
436 switch (in->req_type) {
437 /*
438 * Compare struct members manually for request types which can't be
439 * compared simply using memcmp(). This is because said request types
440 * contain pointers to other allocated structs
441 */
442 case DP_REMOTE_I2C_READ:
443 #define IN in->u.i2c_read
444 #define OUT out->u.i2c_read
445 if (IN.num_bytes_read != OUT.num_bytes_read ||
446 IN.num_transactions != OUT.num_transactions ||
447 IN.port_number != OUT.port_number ||
448 IN.read_i2c_device_id != OUT.read_i2c_device_id)
449 return false;
450
451 for (i = 0; i < IN.num_transactions; i++) {
452 txin = &IN.transactions[i];
453 txout = &OUT.transactions[i];
454
455 if (txin->i2c_dev_id != txout->i2c_dev_id ||
456 txin->no_stop_bit != txout->no_stop_bit ||
457 txin->num_bytes != txout->num_bytes ||
458 txin->i2c_transaction_delay !=
459 txout->i2c_transaction_delay)
460 return false;
461
462 if (memcmp(txin->bytes, txout->bytes,
463 txin->num_bytes) != 0)
464 return false;
465 }
466 break;
467 #undef IN
468 #undef OUT
469
470 case DP_REMOTE_DPCD_WRITE:
471 #define IN in->u.dpcd_write
472 #define OUT out->u.dpcd_write
473 if (IN.dpcd_address != OUT.dpcd_address ||
474 IN.num_bytes != OUT.num_bytes ||
475 IN.port_number != OUT.port_number)
476 return false;
477
478 return memcmp(IN.bytes, OUT.bytes, IN.num_bytes) == 0;
479 #undef IN
480 #undef OUT
481
482 case DP_REMOTE_I2C_WRITE:
483 #define IN in->u.i2c_write
484 #define OUT out->u.i2c_write
485 if (IN.port_number != OUT.port_number ||
486 IN.write_i2c_device_id != OUT.write_i2c_device_id ||
487 IN.num_bytes != OUT.num_bytes)
488 return false;
489
490 return memcmp(IN.bytes, OUT.bytes, IN.num_bytes) == 0;
491 #undef IN
492 #undef OUT
493
494 default:
495 return memcmp(in, out, sizeof(*in)) == 0;
496 }
497
498 return true;
499 }
500
drm_test_dp_mst_msg_printf(struct drm_printer * p,struct va_format * vaf)501 static void drm_test_dp_mst_msg_printf(struct drm_printer *p, struct va_format *vaf)
502 {
503 struct kunit *test = p->arg;
504
505 kunit_err(test, "%pV", vaf);
506 }
507
drm_test_dp_mst_sideband_msg_req_decode(struct kunit * test)508 static void drm_test_dp_mst_sideband_msg_req_decode(struct kunit *test)
509 {
510 const struct drm_dp_mst_sideband_msg_req_test *params = test->param_value;
511 const struct drm_dp_sideband_msg_req_body *in = ¶ms->in;
512 struct drm_dp_sideband_msg_req_body *out;
513 struct drm_dp_sideband_msg_tx *txmsg;
514 struct drm_printer p = {
515 .printfn = drm_test_dp_mst_msg_printf,
516 .arg = test
517 };
518 int i;
519
520 out = kunit_kzalloc(test, sizeof(*out), GFP_KERNEL);
521 KUNIT_ASSERT_NOT_NULL(test, out);
522
523 txmsg = kunit_kzalloc(test, sizeof(*txmsg), GFP_KERNEL);
524 KUNIT_ASSERT_NOT_NULL(test, txmsg);
525
526 drm_dp_encode_sideband_req(in, txmsg);
527 KUNIT_EXPECT_GE_MSG(test, drm_dp_decode_sideband_req(txmsg, out), 0,
528 "Failed to decode sideband request");
529
530 if (!sideband_msg_req_equal(in, out)) {
531 KUNIT_FAIL(test, "Encode/decode failed");
532 kunit_err(test, "Expected:");
533 drm_dp_dump_sideband_msg_req_body(in, 1, &p);
534 kunit_err(test, "Got:");
535 drm_dp_dump_sideband_msg_req_body(out, 1, &p);
536 }
537
538 switch (in->req_type) {
539 case DP_REMOTE_DPCD_WRITE:
540 kfree(out->u.dpcd_write.bytes);
541 break;
542 case DP_REMOTE_I2C_READ:
543 for (i = 0; i < out->u.i2c_read.num_transactions; i++)
544 kfree(out->u.i2c_read.transactions[i].bytes);
545 break;
546 case DP_REMOTE_I2C_WRITE:
547 kfree(out->u.i2c_write.bytes);
548 break;
549 }
550 }
551
552 static void
drm_dp_mst_sideband_msg_req_desc(const struct drm_dp_mst_sideband_msg_req_test * t,char * desc)553 drm_dp_mst_sideband_msg_req_desc(const struct drm_dp_mst_sideband_msg_req_test *t, char *desc)
554 {
555 strcpy(desc, t->desc);
556 }
557
558 KUNIT_ARRAY_PARAM(drm_dp_mst_sideband_msg_req, drm_dp_mst_sideband_msg_req_cases,
559 drm_dp_mst_sideband_msg_req_desc);
560
561 static struct kunit_case drm_dp_mst_helper_tests[] = {
562 KUNIT_CASE_PARAM(drm_test_dp_mst_calc_pbn_mode, drm_dp_mst_calc_pbn_mode_gen_params),
563 KUNIT_CASE_PARAM(drm_test_dp_mst_calc_pbn_div, drm_dp_mst_calc_pbn_div_gen_params),
564 KUNIT_CASE_PARAM(drm_test_dp_mst_sideband_msg_req_decode,
565 drm_dp_mst_sideband_msg_req_gen_params),
566 { }
567 };
568
569 static struct kunit_suite drm_dp_mst_helper_test_suite = {
570 .name = "drm_dp_mst_helper",
571 .test_cases = drm_dp_mst_helper_tests,
572 };
573
574 kunit_test_suite(drm_dp_mst_helper_test_suite);
575
576 MODULE_DESCRIPTION("Test cases for the DRM DP MST helpers");
577 MODULE_LICENSE("GPL");
578