1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Kunit test for drm_probe_helper functions
4  */
5 
6 #include <drm/drm_atomic_state_helper.h>
7 #include <drm/drm_connector.h>
8 #include <drm/drm_device.h>
9 #include <drm/drm_drv.h>
10 #include <drm/drm_kunit_helpers.h>
11 #include <drm/drm_mode.h>
12 #include <drm/drm_modes.h>
13 #include <drm/drm_modeset_helper_vtables.h>
14 #include <drm/drm_probe_helper.h>
15 
16 #include <kunit/test.h>
17 
18 struct drm_probe_helper_test_priv {
19 	struct drm_device *drm;
20 	struct drm_connector connector;
21 };
22 
23 static const struct drm_connector_helper_funcs drm_probe_helper_connector_helper_funcs = {
24 };
25 
26 static const struct drm_connector_funcs drm_probe_helper_connector_funcs = {
27 	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
28 	.atomic_duplicate_state	= drm_atomic_helper_connector_duplicate_state,
29 	.reset			= drm_atomic_helper_connector_reset,
30 };
31 
32 static int drm_probe_helper_test_init(struct kunit *test)
33 {
34 	struct drm_probe_helper_test_priv *priv;
35 	struct drm_connector *connector;
36 	int ret;
37 
38 	priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
39 	KUNIT_ASSERT_NOT_NULL(test, priv);
40 	test->priv = priv;
41 
42 	priv->drm = drm_kunit_device_init(test, DRIVER_MODESET | DRIVER_ATOMIC,
43 					  "drm-probe-helper-test");
44 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
45 
46 	connector = &priv->connector;
47 	ret = drmm_connector_init(priv->drm, connector,
48 				  &drm_probe_helper_connector_funcs,
49 				  DRM_MODE_CONNECTOR_Unknown,
50 				  NULL);
51 	KUNIT_ASSERT_EQ(test, ret, 0);
52 
53 	drm_connector_helper_add(connector, &drm_probe_helper_connector_helper_funcs);
54 
55 	return 0;
56 }
57 
58 typedef struct drm_display_mode *(*expected_mode_func_t)(struct drm_device *);
59 
60 struct drm_connector_helper_tv_get_modes_test {
61 	const char *name;
62 	unsigned int supported_tv_modes;
63 	enum drm_connector_tv_mode default_mode;
64 	bool cmdline;
65 	enum drm_connector_tv_mode cmdline_mode;
66 	expected_mode_func_t *expected_modes;
67 	unsigned int num_expected_modes;
68 };
69 
70 #define _TV_MODE_TEST(_name, _supported, _default, _cmdline, _cmdline_mode, ...)		\
71 	{											\
72 		.name = _name,									\
73 		.supported_tv_modes = _supported,						\
74 		.default_mode = _default,							\
75 		.cmdline = _cmdline,								\
76 		.cmdline_mode = _cmdline_mode,							\
77 		.expected_modes = (expected_mode_func_t[]) { __VA_ARGS__ },			\
78 		.num_expected_modes = sizeof((expected_mode_func_t[]) { __VA_ARGS__ }) /	\
79 				      (sizeof(expected_mode_func_t)),				\
80 	}
81 
82 #define TV_MODE_TEST(_name, _supported, _default, ...)			\
83 	_TV_MODE_TEST(_name, _supported, _default, false, 0, __VA_ARGS__)
84 
85 #define TV_MODE_TEST_CMDLINE(_name, _supported, _default, _cmdline, ...) \
86 	_TV_MODE_TEST(_name, _supported, _default, true, _cmdline, __VA_ARGS__)
87 
88 static void
89 drm_test_connector_helper_tv_get_modes_check(struct kunit *test)
90 {
91 	const struct drm_connector_helper_tv_get_modes_test *params = test->param_value;
92 	struct drm_probe_helper_test_priv *priv = test->priv;
93 	struct drm_connector *connector = &priv->connector;
94 	struct drm_cmdline_mode *cmdline = &connector->cmdline_mode;
95 	struct drm_display_mode *mode;
96 	const struct drm_display_mode *expected;
97 	size_t len;
98 	int ret;
99 
100 	if (params->cmdline) {
101 		cmdline->tv_mode_specified = true;
102 		cmdline->tv_mode = params->cmdline_mode;
103 	}
104 
105 	ret = drm_mode_create_tv_properties(priv->drm, params->supported_tv_modes);
106 	KUNIT_ASSERT_EQ(test, ret, 0);
107 
108 	drm_object_attach_property(&connector->base,
109 				   priv->drm->mode_config.tv_mode_property,
110 				   params->default_mode);
111 
112 	mutex_lock(&priv->drm->mode_config.mutex);
113 
114 	ret = drm_connector_helper_tv_get_modes(connector);
115 	KUNIT_EXPECT_EQ(test, ret, params->num_expected_modes);
116 
117 	len = 0;
118 	list_for_each_entry(mode, &connector->probed_modes, head)
119 		len++;
120 	KUNIT_EXPECT_EQ(test, len, params->num_expected_modes);
121 
122 	if (params->num_expected_modes >= 1) {
123 		mode = list_first_entry_or_null(&connector->probed_modes,
124 						struct drm_display_mode, head);
125 		KUNIT_ASSERT_NOT_NULL(test, mode);
126 
127 		expected = params->expected_modes[0](priv->drm);
128 		KUNIT_ASSERT_NOT_NULL(test, expected);
129 
130 		KUNIT_EXPECT_TRUE(test, drm_mode_equal(mode, expected));
131 		KUNIT_EXPECT_TRUE(test, mode->type & DRM_MODE_TYPE_PREFERRED);
132 	}
133 
134 	if (params->num_expected_modes >= 2) {
135 		mode = list_next_entry(mode, head);
136 		KUNIT_ASSERT_NOT_NULL(test, mode);
137 
138 		expected = params->expected_modes[1](priv->drm);
139 		KUNIT_ASSERT_NOT_NULL(test, expected);
140 
141 		KUNIT_EXPECT_TRUE(test, drm_mode_equal(mode, expected));
142 		KUNIT_EXPECT_FALSE(test, mode->type & DRM_MODE_TYPE_PREFERRED);
143 	}
144 
145 	mutex_unlock(&priv->drm->mode_config.mutex);
146 }
147 
148 static const
149 struct drm_connector_helper_tv_get_modes_test drm_connector_helper_tv_get_modes_tests[] = {
150 	{ .name = "None" },
151 	TV_MODE_TEST("PAL",
152 		     BIT(DRM_MODE_TV_MODE_PAL),
153 		     DRM_MODE_TV_MODE_PAL,
154 		     drm_mode_analog_pal_576i),
155 	TV_MODE_TEST("NTSC",
156 		     BIT(DRM_MODE_TV_MODE_NTSC),
157 		     DRM_MODE_TV_MODE_NTSC,
158 		     drm_mode_analog_ntsc_480i),
159 	TV_MODE_TEST("Both, NTSC Default",
160 		     BIT(DRM_MODE_TV_MODE_NTSC) | BIT(DRM_MODE_TV_MODE_PAL),
161 		     DRM_MODE_TV_MODE_NTSC,
162 		     drm_mode_analog_ntsc_480i, drm_mode_analog_pal_576i),
163 	TV_MODE_TEST("Both, PAL Default",
164 		     BIT(DRM_MODE_TV_MODE_NTSC) | BIT(DRM_MODE_TV_MODE_PAL),
165 		     DRM_MODE_TV_MODE_PAL,
166 		     drm_mode_analog_pal_576i, drm_mode_analog_ntsc_480i),
167 	TV_MODE_TEST_CMDLINE("Both, NTSC Default, with PAL on command-line",
168 			     BIT(DRM_MODE_TV_MODE_NTSC) | BIT(DRM_MODE_TV_MODE_PAL),
169 			     DRM_MODE_TV_MODE_NTSC,
170 			     DRM_MODE_TV_MODE_PAL,
171 			     drm_mode_analog_pal_576i, drm_mode_analog_ntsc_480i),
172 	TV_MODE_TEST_CMDLINE("Both, PAL Default, with NTSC on command-line",
173 			     BIT(DRM_MODE_TV_MODE_NTSC) | BIT(DRM_MODE_TV_MODE_PAL),
174 			     DRM_MODE_TV_MODE_PAL,
175 			     DRM_MODE_TV_MODE_NTSC,
176 			     drm_mode_analog_ntsc_480i, drm_mode_analog_pal_576i),
177 };
178 
179 static void
180 drm_connector_helper_tv_get_modes_desc(const struct drm_connector_helper_tv_get_modes_test *t,
181 				       char *desc)
182 {
183 	sprintf(desc, "%s", t->name);
184 }
185 
186 KUNIT_ARRAY_PARAM(drm_connector_helper_tv_get_modes,
187 		  drm_connector_helper_tv_get_modes_tests,
188 		  drm_connector_helper_tv_get_modes_desc);
189 
190 static struct kunit_case drm_test_connector_helper_tv_get_modes_tests[] = {
191 	KUNIT_CASE_PARAM(drm_test_connector_helper_tv_get_modes_check,
192 			 drm_connector_helper_tv_get_modes_gen_params),
193 	{ }
194 };
195 
196 static struct kunit_suite drm_test_connector_helper_tv_get_modes_suite = {
197 	.name = "drm_connector_helper_tv_get_modes",
198 	.init = drm_probe_helper_test_init,
199 	.test_cases = drm_test_connector_helper_tv_get_modes_tests,
200 };
201 
202 kunit_test_suite(drm_test_connector_helper_tv_get_modes_suite);
203 
204 MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>");
205 MODULE_LICENSE("GPL");
206