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 = 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 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
sideband_msg_req_equal(const struct drm_dp_sideband_msg_req_body * in,const struct drm_dp_sideband_msg_req_body * out)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
drm_test_dp_mst_msg_printf(struct drm_printer * p,struct va_format * vaf)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
drm_test_dp_mst_sideband_msg_req_decode(struct kunit * test)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
drm_dp_mst_sideband_msg_req_desc(const struct drm_dp_mst_sideband_msg_req_test * t,char * desc)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