Line data Source code
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 5 : static void drm_test_dp_mst_calc_pbn_mode(struct kunit *test)
56 : {
57 5 : const struct drm_dp_mst_calc_pbn_mode_test *params = test->param_value;
58 :
59 5 : KUNIT_EXPECT_EQ(test, drm_dp_calc_pbn_mode(params->clock, params->bpp, params->dsc),
60 : params->expected);
61 5 : }
62 :
63 5 : static void dp_mst_calc_pbn_mode_desc(const struct drm_dp_mst_calc_pbn_mode_test *t, char *desc)
64 : {
65 5 : sprintf(desc, "Clock %d BPP %d DSC %s", t->clock, t->bpp, t->dsc ? "enabled" : "disabled");
66 5 : }
67 :
68 6 : 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 27 : 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 27 : if (in->req_type != out->req_type)
290 : return false;
291 :
292 27 : 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 3 : if (IN.num_bytes_read != OUT.num_bytes_read ||
302 3 : IN.num_transactions != OUT.num_transactions ||
303 3 : IN.port_number != OUT.port_number ||
304 3 : IN.read_i2c_device_id != OUT.read_i2c_device_id)
305 : return false;
306 :
307 3 : for (i = 0; i < IN.num_transactions; i++) {
308 3 : txin = &IN.transactions[i];
309 3 : txout = &OUT.transactions[i];
310 :
311 6 : if (txin->i2c_dev_id != txout->i2c_dev_id ||
312 6 : txin->no_stop_bit != txout->no_stop_bit ||
313 6 : txin->num_bytes != txout->num_bytes ||
314 3 : txin->i2c_transaction_delay !=
315 3 : txout->i2c_transaction_delay)
316 : return false;
317 :
318 3 : 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 6 : if (IN.dpcd_address != OUT.dpcd_address ||
330 6 : IN.num_bytes != OUT.num_bytes ||
331 3 : IN.port_number != OUT.port_number)
332 : return false;
333 :
334 3 : 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 3 : if (IN.port_number != OUT.port_number ||
342 3 : IN.write_i2c_device_id != OUT.write_i2c_device_id ||
343 : IN.num_bytes != OUT.num_bytes)
344 : return false;
345 :
346 3 : return memcmp(IN.bytes, OUT.bytes, IN.num_bytes) == 0;
347 : #undef IN
348 : #undef OUT
349 :
350 : default:
351 18 : return memcmp(in, out, sizeof(*in)) == 0;
352 : }
353 :
354 : return true;
355 : }
356 :
357 0 : static void drm_test_dp_mst_msg_printf(struct drm_printer *p, struct va_format *vaf)
358 : {
359 0 : struct kunit *test = p->arg;
360 :
361 0 : kunit_err(test, "%pV", vaf);
362 0 : }
363 :
364 27 : static void drm_test_dp_mst_sideband_msg_req_decode(struct kunit *test)
365 : {
366 27 : const struct drm_dp_mst_sideband_msg_req_test *params = test->param_value;
367 27 : 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 27 : struct drm_printer p = {
371 : .printfn = drm_test_dp_mst_msg_printf,
372 : .arg = test
373 : };
374 : int i;
375 :
376 27 : out = kunit_kzalloc(test, sizeof(*out), GFP_KERNEL);
377 27 : KUNIT_ASSERT_NOT_NULL(test, out);
378 :
379 27 : txmsg = kunit_kzalloc(test, sizeof(*txmsg), GFP_KERNEL);
380 27 : KUNIT_ASSERT_NOT_NULL(test, txmsg);
381 :
382 27 : drm_dp_encode_sideband_req(in, txmsg);
383 27 : KUNIT_EXPECT_GE_MSG(test, drm_dp_decode_sideband_req(txmsg, out), 0,
384 : "Failed to decode sideband request");
385 :
386 27 : if (!sideband_msg_req_equal(in, out)) {
387 0 : KUNIT_FAIL(test, "Encode/decode failed");
388 0 : kunit_err(test, "Expected:");
389 0 : drm_dp_dump_sideband_msg_req_body(in, 1, &p);
390 0 : kunit_err(test, "Got:");
391 0 : drm_dp_dump_sideband_msg_req_body(out, 1, &p);
392 : }
393 :
394 27 : switch (in->req_type) {
395 : case DP_REMOTE_DPCD_WRITE:
396 3 : kfree(out->u.dpcd_write.bytes);
397 3 : break;
398 : case DP_REMOTE_I2C_READ:
399 3 : for (i = 0; i < out->u.i2c_read.num_transactions; i++)
400 3 : kfree(out->u.i2c_read.transactions[i].bytes);
401 : break;
402 : case DP_REMOTE_I2C_WRITE:
403 3 : kfree(out->u.i2c_write.bytes);
404 3 : break;
405 : }
406 27 : }
407 :
408 : static void
409 27 : drm_dp_mst_sideband_msg_req_desc(const struct drm_dp_mst_sideband_msg_req_test *t, char *desc)
410 : {
411 27 : strcpy(desc, t->desc);
412 27 : }
413 :
414 28 : 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");
|