1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2022 Maxime Ripard <mripard@kernel.org> |
4 | */ |
5 | |
6 | #include <kunit/test.h> |
7 | |
8 | #include <drm/drm_connector.h> |
9 | #include <drm/drm_edid.h> |
10 | #include <drm/drm_drv.h> |
11 | #include <drm/drm_kunit_helpers.h> |
12 | #include <drm/drm_modes.h> |
13 | #include <drm/drm_modeset_helper_vtables.h> |
14 | #include <drm/drm_probe_helper.h> |
15 | |
16 | struct drm_client_modeset_test_priv { |
17 | struct drm_device *drm; |
18 | struct device *dev; |
19 | struct drm_connector connector; |
20 | }; |
21 | |
22 | static int drm_client_modeset_connector_get_modes(struct drm_connector *connector) |
23 | { |
24 | struct drm_display_mode *mode; |
25 | int count; |
26 | |
27 | count = drm_add_modes_noedid(connector, hdisplay: 1920, vdisplay: 1200); |
28 | |
29 | mode = drm_mode_analog_ntsc_480i(dev: connector->dev); |
30 | if (!mode) |
31 | return count; |
32 | |
33 | drm_mode_probed_add(connector, mode); |
34 | count += 1; |
35 | |
36 | mode = drm_mode_analog_pal_576i(dev: connector->dev); |
37 | if (!mode) |
38 | return count; |
39 | |
40 | drm_mode_probed_add(connector, mode); |
41 | count += 1; |
42 | |
43 | return count; |
44 | } |
45 | |
46 | static const struct drm_connector_helper_funcs drm_client_modeset_connector_helper_funcs = { |
47 | .get_modes = drm_client_modeset_connector_get_modes, |
48 | }; |
49 | |
50 | static const struct drm_connector_funcs drm_client_modeset_connector_funcs = { |
51 | }; |
52 | |
53 | static int drm_client_modeset_test_init(struct kunit *test) |
54 | { |
55 | struct drm_client_modeset_test_priv *priv; |
56 | int ret; |
57 | |
58 | priv = kunit_kzalloc(test, size: sizeof(*priv), GFP_KERNEL); |
59 | KUNIT_ASSERT_NOT_NULL(test, priv); |
60 | |
61 | test->priv = priv; |
62 | |
63 | priv->dev = drm_kunit_helper_alloc_device(test); |
64 | KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev); |
65 | |
66 | priv->drm = __drm_kunit_helper_alloc_drm_device(test, dev: priv->dev, |
67 | size: sizeof(*priv->drm), offset: 0, |
68 | features: DRIVER_MODESET); |
69 | KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm); |
70 | |
71 | ret = drmm_connector_init(dev: priv->drm, connector: &priv->connector, |
72 | funcs: &drm_client_modeset_connector_funcs, |
73 | DRM_MODE_CONNECTOR_Unknown, |
74 | NULL); |
75 | KUNIT_ASSERT_EQ(test, ret, 0); |
76 | |
77 | drm_connector_helper_add(connector: &priv->connector, funcs: &drm_client_modeset_connector_helper_funcs); |
78 | |
79 | priv->connector.interlace_allowed = true; |
80 | priv->connector.doublescan_allowed = true; |
81 | |
82 | return 0; |
83 | } |
84 | |
85 | static void drm_test_pick_cmdline_res_1920_1080_60(struct kunit *test) |
86 | { |
87 | struct drm_client_modeset_test_priv *priv = test->priv; |
88 | struct drm_device *drm = priv->drm; |
89 | struct drm_connector *connector = &priv->connector; |
90 | struct drm_cmdline_mode *cmdline_mode = &connector->cmdline_mode; |
91 | struct drm_display_mode *expected_mode, *mode; |
92 | const char *cmdline = "1920x1080@60" ; |
93 | int ret; |
94 | |
95 | expected_mode = drm_mode_find_dmt(dev: priv->drm, hsize: 1920, vsize: 1080, fresh: 60, rb: false); |
96 | KUNIT_ASSERT_NOT_NULL(test, expected_mode); |
97 | |
98 | KUNIT_ASSERT_TRUE(test, |
99 | drm_mode_parse_command_line_for_connector(cmdline, |
100 | connector, |
101 | cmdline_mode)); |
102 | |
103 | mutex_lock(&drm->mode_config.mutex); |
104 | ret = drm_helper_probe_single_connector_modes(connector, maxX: 1920, maxY: 1080); |
105 | mutex_unlock(lock: &drm->mode_config.mutex); |
106 | KUNIT_ASSERT_GT(test, ret, 0); |
107 | |
108 | mode = drm_connector_pick_cmdline_mode(connector); |
109 | KUNIT_ASSERT_NOT_NULL(test, mode); |
110 | |
111 | KUNIT_EXPECT_TRUE(test, drm_mode_equal(expected_mode, mode)); |
112 | } |
113 | |
114 | struct drm_connector_pick_cmdline_mode_test { |
115 | const char *cmdline; |
116 | struct drm_display_mode *(*func)(struct drm_device *drm); |
117 | }; |
118 | |
119 | #define TEST_CMDLINE(_cmdline, _fn) \ |
120 | { \ |
121 | .cmdline = _cmdline, \ |
122 | .func = _fn, \ |
123 | } |
124 | |
125 | static void drm_test_pick_cmdline_named(struct kunit *test) |
126 | { |
127 | const struct drm_connector_pick_cmdline_mode_test *params = test->param_value; |
128 | struct drm_client_modeset_test_priv *priv = test->priv; |
129 | struct drm_device *drm = priv->drm; |
130 | struct drm_connector *connector = &priv->connector; |
131 | struct drm_cmdline_mode *cmdline_mode = &connector->cmdline_mode; |
132 | const struct drm_display_mode *expected_mode, *mode; |
133 | const char *cmdline = params->cmdline; |
134 | int ret; |
135 | |
136 | KUNIT_ASSERT_TRUE(test, |
137 | drm_mode_parse_command_line_for_connector(cmdline, |
138 | connector, |
139 | cmdline_mode)); |
140 | |
141 | mutex_lock(&drm->mode_config.mutex); |
142 | ret = drm_helper_probe_single_connector_modes(connector, maxX: 1920, maxY: 1080); |
143 | mutex_unlock(lock: &drm->mode_config.mutex); |
144 | KUNIT_ASSERT_GT(test, ret, 0); |
145 | |
146 | mode = drm_connector_pick_cmdline_mode(connector); |
147 | KUNIT_ASSERT_NOT_NULL(test, mode); |
148 | |
149 | expected_mode = params->func(drm); |
150 | KUNIT_ASSERT_NOT_NULL(test, expected_mode); |
151 | |
152 | KUNIT_EXPECT_TRUE(test, drm_mode_equal(expected_mode, mode)); |
153 | } |
154 | |
155 | static const |
156 | struct drm_connector_pick_cmdline_mode_test drm_connector_pick_cmdline_mode_tests[] = { |
157 | TEST_CMDLINE("NTSC" , drm_mode_analog_ntsc_480i), |
158 | TEST_CMDLINE("NTSC-J" , drm_mode_analog_ntsc_480i), |
159 | TEST_CMDLINE("PAL" , drm_mode_analog_pal_576i), |
160 | TEST_CMDLINE("PAL-M" , drm_mode_analog_ntsc_480i), |
161 | }; |
162 | |
163 | static void |
164 | drm_connector_pick_cmdline_mode_desc(const struct drm_connector_pick_cmdline_mode_test *t, |
165 | char *desc) |
166 | { |
167 | sprintf(buf: desc, fmt: "%s" , t->cmdline); |
168 | } |
169 | |
170 | KUNIT_ARRAY_PARAM(drm_connector_pick_cmdline_mode, |
171 | drm_connector_pick_cmdline_mode_tests, |
172 | drm_connector_pick_cmdline_mode_desc); |
173 | |
174 | static struct kunit_case drm_test_pick_cmdline_tests[] = { |
175 | KUNIT_CASE(drm_test_pick_cmdline_res_1920_1080_60), |
176 | KUNIT_CASE_PARAM(drm_test_pick_cmdline_named, |
177 | drm_connector_pick_cmdline_mode_gen_params), |
178 | {} |
179 | }; |
180 | |
181 | static struct kunit_suite drm_test_pick_cmdline_test_suite = { |
182 | .name = "drm_test_pick_cmdline" , |
183 | .init = drm_client_modeset_test_init, |
184 | .test_cases = drm_test_pick_cmdline_tests |
185 | }; |
186 | |
187 | kunit_test_suite(drm_test_pick_cmdline_test_suite); |
188 | |
189 | /* |
190 | * This file is included directly by drm_client_modeset.c so we can't |
191 | * use any MODULE_* macro here. |
192 | */ |
193 | |