Loading...
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
15struct 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
22static 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
55static 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
63static 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
68KUNIT_ARRAY_PARAM(drm_dp_mst_calc_pbn_mode, drm_dp_mst_calc_pbn_mode_cases,
69 dp_mst_calc_pbn_mode_desc);
70
71struct 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
82static 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
199static 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 /* mgr->dev is only needed by drm_dbg_kms(), but it's not called for the test cases. */
203 struct drm_dp_mst_topology_mgr *mgr = test->priv;
204
205 KUNIT_EXPECT_EQ(test, drm_dp_get_vc_payload_bw(mgr, params->link_rate, params->lane_count).full,
206 params->expected.full);
207}
208
209static void dp_mst_calc_pbn_div_desc(const struct drm_dp_mst_calc_pbn_div_test *t, char *desc)
210{
211 sprintf(desc, "Link rate %d lane count %d", t->link_rate, t->lane_count);
212}
213
214KUNIT_ARRAY_PARAM(drm_dp_mst_calc_pbn_div, drm_dp_mst_calc_pbn_div_dp1_4_cases,
215 dp_mst_calc_pbn_div_desc);
216
217static u8 data[] = { 0xff, 0x00, 0xdd };
218
219struct drm_dp_mst_sideband_msg_req_test {
220 const char *desc;
221 const struct drm_dp_sideband_msg_req_body in;
222};
223
224static const struct drm_dp_mst_sideband_msg_req_test drm_dp_mst_sideband_msg_req_cases[] = {
225 {
226 .desc = "DP_ENUM_PATH_RESOURCES with port number",
227 .in = {
228 .req_type = DP_ENUM_PATH_RESOURCES,
229 .u.port_num.port_number = 5,
230 },
231 },
232 {
233 .desc = "DP_POWER_UP_PHY with port number",
234 .in = {
235 .req_type = DP_POWER_UP_PHY,
236 .u.port_num.port_number = 5,
237 },
238 },
239 {
240 .desc = "DP_POWER_DOWN_PHY with port number",
241 .in = {
242 .req_type = DP_POWER_DOWN_PHY,
243 .u.port_num.port_number = 5,
244 },
245 },
246 {
247 .desc = "DP_ALLOCATE_PAYLOAD with SDP stream sinks",
248 .in = {
249 .req_type = DP_ALLOCATE_PAYLOAD,
250 .u.allocate_payload.number_sdp_streams = 3,
251 .u.allocate_payload.sdp_stream_sink = { 1, 2, 3 },
252 },
253 },
254 {
255 .desc = "DP_ALLOCATE_PAYLOAD with port number",
256 .in = {
257 .req_type = DP_ALLOCATE_PAYLOAD,
258 .u.allocate_payload.port_number = 0xf,
259 },
260 },
261 {
262 .desc = "DP_ALLOCATE_PAYLOAD with VCPI",
263 .in = {
264 .req_type = DP_ALLOCATE_PAYLOAD,
265 .u.allocate_payload.vcpi = 0x7f,
266 },
267 },
268 {
269 .desc = "DP_ALLOCATE_PAYLOAD with PBN",
270 .in = {
271 .req_type = DP_ALLOCATE_PAYLOAD,
272 .u.allocate_payload.pbn = U16_MAX,
273 },
274 },
275 {
276 .desc = "DP_QUERY_PAYLOAD with port number",
277 .in = {
278 .req_type = DP_QUERY_PAYLOAD,
279 .u.query_payload.port_number = 0xf,
280 },
281 },
282 {
283 .desc = "DP_QUERY_PAYLOAD with VCPI",
284 .in = {
285 .req_type = DP_QUERY_PAYLOAD,
286 .u.query_payload.vcpi = 0x7f,
287 },
288 },
289 {
290 .desc = "DP_REMOTE_DPCD_READ with port number",
291 .in = {
292 .req_type = DP_REMOTE_DPCD_READ,
293 .u.dpcd_read.port_number = 0xf,
294 },
295 },
296 {
297 .desc = "DP_REMOTE_DPCD_READ with DPCD address",
298 .in = {
299 .req_type = DP_REMOTE_DPCD_READ,
300 .u.dpcd_read.dpcd_address = 0xfedcb,
301 },
302 },
303 {
304 .desc = "DP_REMOTE_DPCD_READ with max number of bytes",
305 .in = {
306 .req_type = DP_REMOTE_DPCD_READ,
307 .u.dpcd_read.num_bytes = U8_MAX,
308 },
309 },
310 {
311 .desc = "DP_REMOTE_DPCD_WRITE with port number",
312 .in = {
313 .req_type = DP_REMOTE_DPCD_WRITE,
314 .u.dpcd_write.port_number = 0xf,
315 },
316 },
317 {
318 .desc = "DP_REMOTE_DPCD_WRITE with DPCD address",
319 .in = {
320 .req_type = DP_REMOTE_DPCD_WRITE,
321 .u.dpcd_write.dpcd_address = 0xfedcb,
322 },
323 },
324 {
325 .desc = "DP_REMOTE_DPCD_WRITE with data array",
326 .in = {
327 .req_type = DP_REMOTE_DPCD_WRITE,
328 .u.dpcd_write.num_bytes = ARRAY_SIZE(data),
329 .u.dpcd_write.bytes = data,
330 },
331 },
332 {
333 .desc = "DP_REMOTE_I2C_READ with port number",
334 .in = {
335 .req_type = DP_REMOTE_I2C_READ,
336 .u.i2c_read.port_number = 0xf,
337 },
338 },
339 {
340 .desc = "DP_REMOTE_I2C_READ with I2C device ID",
341 .in = {
342 .req_type = DP_REMOTE_I2C_READ,
343 .u.i2c_read.read_i2c_device_id = 0x7f,
344 },
345 },
346 {
347 .desc = "DP_REMOTE_I2C_READ with transactions array",
348 .in = {
349 .req_type = DP_REMOTE_I2C_READ,
350 .u.i2c_read.num_transactions = 3,
351 .u.i2c_read.num_bytes_read = ARRAY_SIZE(data) * 3,
352 .u.i2c_read.transactions = {
353 { .bytes = data, .num_bytes = ARRAY_SIZE(data), .i2c_dev_id = 0x7f,
354 .i2c_transaction_delay = 0xf, },
355 { .bytes = data, .num_bytes = ARRAY_SIZE(data), .i2c_dev_id = 0x7e,
356 .i2c_transaction_delay = 0xe, },
357 { .bytes = data, .num_bytes = ARRAY_SIZE(data), .i2c_dev_id = 0x7d,
358 .i2c_transaction_delay = 0xd, },
359 },
360 },
361 },
362 {
363 .desc = "DP_REMOTE_I2C_WRITE with port number",
364 .in = {
365 .req_type = DP_REMOTE_I2C_WRITE,
366 .u.i2c_write.port_number = 0xf,
367 },
368 },
369 {
370 .desc = "DP_REMOTE_I2C_WRITE with I2C device ID",
371 .in = {
372 .req_type = DP_REMOTE_I2C_WRITE,
373 .u.i2c_write.write_i2c_device_id = 0x7f,
374 },
375 },
376 {
377 .desc = "DP_REMOTE_I2C_WRITE with data array",
378 .in = {
379 .req_type = DP_REMOTE_I2C_WRITE,
380 .u.i2c_write.num_bytes = ARRAY_SIZE(data),
381 .u.i2c_write.bytes = data,
382 },
383 },
384 {
385 .desc = "DP_QUERY_STREAM_ENC_STATUS with stream ID",
386 .in = {
387 .req_type = DP_QUERY_STREAM_ENC_STATUS,
388 .u.enc_status.stream_id = 1,
389 },
390 },
391 {
392 .desc = "DP_QUERY_STREAM_ENC_STATUS with client ID",
393 .in = {
394 .req_type = DP_QUERY_STREAM_ENC_STATUS,
395 .u.enc_status.client_id = { 0x4f, 0x7f, 0xb4, 0x00, 0x8c, 0x0d, 0x67 },
396 },
397 },
398 {
399 .desc = "DP_QUERY_STREAM_ENC_STATUS with stream event",
400 .in = {
401 .req_type = DP_QUERY_STREAM_ENC_STATUS,
402 .u.enc_status.stream_event = 3,
403 },
404 },
405 {
406 .desc = "DP_QUERY_STREAM_ENC_STATUS with valid stream event",
407 .in = {
408 .req_type = DP_QUERY_STREAM_ENC_STATUS,
409 .u.enc_status.valid_stream_event = 0,
410 },
411 },
412 {
413 .desc = "DP_QUERY_STREAM_ENC_STATUS with stream behavior",
414 .in = {
415 .req_type = DP_QUERY_STREAM_ENC_STATUS,
416 .u.enc_status.stream_behavior = 3,
417 },
418 },
419 {
420 .desc = "DP_QUERY_STREAM_ENC_STATUS with a valid stream behavior",
421 .in = {
422 .req_type = DP_QUERY_STREAM_ENC_STATUS,
423 .u.enc_status.valid_stream_behavior = 1,
424 }
425 },
426};
427
428static bool
429sideband_msg_req_equal(const struct drm_dp_sideband_msg_req_body *in,
430 const struct drm_dp_sideband_msg_req_body *out)
431{
432 const struct drm_dp_remote_i2c_read_tx *txin, *txout;
433 int i;
434
435 if (in->req_type != out->req_type)
436 return false;
437
438 switch (in->req_type) {
439 /*
440 * Compare struct members manually for request types which can't be
441 * compared simply using memcmp(). This is because said request types
442 * contain pointers to other allocated structs
443 */
444 case DP_REMOTE_I2C_READ:
445#define IN in->u.i2c_read
446#define OUT out->u.i2c_read
447 if (IN.num_bytes_read != OUT.num_bytes_read ||
448 IN.num_transactions != OUT.num_transactions ||
449 IN.port_number != OUT.port_number ||
450 IN.read_i2c_device_id != OUT.read_i2c_device_id)
451 return false;
452
453 for (i = 0; i < IN.num_transactions; i++) {
454 txin = &IN.transactions[i];
455 txout = &OUT.transactions[i];
456
457 if (txin->i2c_dev_id != txout->i2c_dev_id ||
458 txin->no_stop_bit != txout->no_stop_bit ||
459 txin->num_bytes != txout->num_bytes ||
460 txin->i2c_transaction_delay !=
461 txout->i2c_transaction_delay)
462 return false;
463
464 if (memcmp(txin->bytes, txout->bytes,
465 txin->num_bytes) != 0)
466 return false;
467 }
468 break;
469#undef IN
470#undef OUT
471
472 case DP_REMOTE_DPCD_WRITE:
473#define IN in->u.dpcd_write
474#define OUT out->u.dpcd_write
475 if (IN.dpcd_address != OUT.dpcd_address ||
476 IN.num_bytes != OUT.num_bytes ||
477 IN.port_number != OUT.port_number)
478 return false;
479
480 return memcmp(IN.bytes, OUT.bytes, IN.num_bytes) == 0;
481#undef IN
482#undef OUT
483
484 case DP_REMOTE_I2C_WRITE:
485#define IN in->u.i2c_write
486#define OUT out->u.i2c_write
487 if (IN.port_number != OUT.port_number ||
488 IN.write_i2c_device_id != OUT.write_i2c_device_id ||
489 IN.num_bytes != OUT.num_bytes)
490 return false;
491
492 return memcmp(IN.bytes, OUT.bytes, IN.num_bytes) == 0;
493#undef IN
494#undef OUT
495
496 default:
497 return memcmp(in, out, sizeof(*in)) == 0;
498 }
499
500 return true;
501}
502
503static void drm_test_dp_mst_msg_printf(struct drm_printer *p, struct va_format *vaf)
504{
505 struct kunit *test = p->arg;
506
507 kunit_err(test, "%pV", vaf);
508}
509
510static void drm_test_dp_mst_sideband_msg_req_decode(struct kunit *test)
511{
512 const struct drm_dp_mst_sideband_msg_req_test *params = test->param_value;
513 const struct drm_dp_sideband_msg_req_body *in = ¶ms->in;
514 struct drm_dp_sideband_msg_req_body *out;
515 struct drm_dp_sideband_msg_tx *txmsg;
516 struct drm_printer p = {
517 .printfn = drm_test_dp_mst_msg_printf,
518 .arg = test
519 };
520 int i;
521
522 out = kunit_kzalloc(test, sizeof(*out), GFP_KERNEL);
523 KUNIT_ASSERT_NOT_NULL(test, out);
524
525 txmsg = kunit_kzalloc(test, sizeof(*txmsg), GFP_KERNEL);
526 KUNIT_ASSERT_NOT_NULL(test, txmsg);
527
528 drm_dp_encode_sideband_req(in, txmsg);
529 KUNIT_EXPECT_GE_MSG(test, drm_dp_decode_sideband_req(txmsg, out), 0,
530 "Failed to decode sideband request");
531
532 if (!sideband_msg_req_equal(in, out)) {
533 KUNIT_FAIL(test, "Encode/decode failed");
534 kunit_err(test, "Expected:");
535 drm_dp_dump_sideband_msg_req_body(in, 1, &p);
536 kunit_err(test, "Got:");
537 drm_dp_dump_sideband_msg_req_body(out, 1, &p);
538 }
539
540 switch (in->req_type) {
541 case DP_REMOTE_DPCD_WRITE:
542 kfree(out->u.dpcd_write.bytes);
543 break;
544 case DP_REMOTE_I2C_READ:
545 for (i = 0; i < out->u.i2c_read.num_transactions; i++)
546 kfree(out->u.i2c_read.transactions[i].bytes);
547 break;
548 case DP_REMOTE_I2C_WRITE:
549 kfree(out->u.i2c_write.bytes);
550 break;
551 }
552}
553
554static void
555drm_dp_mst_sideband_msg_req_desc(const struct drm_dp_mst_sideband_msg_req_test *t, char *desc)
556{
557 strcpy(desc, t->desc);
558}
559
560KUNIT_ARRAY_PARAM(drm_dp_mst_sideband_msg_req, drm_dp_mst_sideband_msg_req_cases,
561 drm_dp_mst_sideband_msg_req_desc);
562
563static struct kunit_case drm_dp_mst_helper_tests[] = {
564 KUNIT_CASE_PARAM(drm_test_dp_mst_calc_pbn_mode, drm_dp_mst_calc_pbn_mode_gen_params),
565 KUNIT_CASE_PARAM(drm_test_dp_mst_calc_pbn_div, drm_dp_mst_calc_pbn_div_gen_params),
566 KUNIT_CASE_PARAM(drm_test_dp_mst_sideband_msg_req_decode,
567 drm_dp_mst_sideband_msg_req_gen_params),
568 { }
569};
570
571static int drm_dp_mst_helper_tests_init(struct kunit *test)
572{
573 struct drm_dp_mst_topology_mgr *mgr;
574
575 mgr = kunit_kzalloc(test, sizeof(*mgr), GFP_KERNEL);
576 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mgr);
577
578 test->priv = mgr;
579
580 return 0;
581}
582
583static struct kunit_suite drm_dp_mst_helper_test_suite = {
584 .name = "drm_dp_mst_helper",
585 .init = drm_dp_mst_helper_tests_init,
586 .test_cases = drm_dp_mst_helper_tests,
587};
588
589kunit_test_suite(drm_dp_mst_helper_test_suite);
590
591MODULE_DESCRIPTION("Test cases for the DRM DP MST helpers");
592MODULE_LICENSE("GPL");
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
15struct 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
22static 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
55static 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
63static 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
68KUNIT_ARRAY_PARAM(drm_dp_mst_calc_pbn_mode, drm_dp_mst_calc_pbn_mode_cases,
69 dp_mst_calc_pbn_mode_desc);
70
71static u8 data[] = { 0xff, 0x00, 0xdd };
72
73struct drm_dp_mst_sideband_msg_req_test {
74 const char *desc;
75 const struct drm_dp_sideband_msg_req_body in;
76};
77
78static 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
282static bool
283sideband_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
357static 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
364static 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
408static void
409drm_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
414KUNIT_ARRAY_PARAM(drm_dp_mst_sideband_msg_req, drm_dp_mst_sideband_msg_req_cases,
415 drm_dp_mst_sideband_msg_req_desc);
416
417static 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
424static 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
429kunit_test_suite(drm_dp_mst_helper_test_suite);
430
431MODULE_LICENSE("GPL");