Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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 = &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
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");