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 = &params->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