Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/probe-helper: Provide a TV get_modes helper

Most of the TV connectors will need a similar get_modes implementation
that will, depending on the drivers' capabilities, register the 480i and
576i modes.

That implementation will also need to set the preferred flag and order
the modes based on the driver and users preferrence.

This is especially important to guarantee that a userspace stack such as
Xorg can start and pick up the preferred mode while maintaining a
working output.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
Tested-by: Mateusz Kwiatkowski <kfyatek+publicgit@gmail.com>
Acked-in-principle-or-something-like-that-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://lore.kernel.org/r/20220728-rpi-analog-tv-properties-v10-12-256dad125326@cerno.tech
Signed-off-by: Maxime Ripard <maxime@cerno.tech>

authored by

Noralf Trønnes and committed by
Maxime Ripard
1e4a91db 0740ac38

+289
+82
drivers/gpu/drm/drm_probe_helper.c
··· 1146 1146 return count; 1147 1147 } 1148 1148 EXPORT_SYMBOL(drm_connector_helper_get_modes); 1149 + 1150 + /** 1151 + * drm_connector_helper_tv_get_modes - Fills the modes availables to a TV connector 1152 + * @connector: The connector 1153 + * 1154 + * Fills the available modes for a TV connector based on the supported 1155 + * TV modes, and the default mode expressed by the kernel command line. 1156 + * 1157 + * This can be used as the default TV connector helper .get_modes() hook 1158 + * if the driver does not need any special processing. 1159 + * 1160 + * Returns: 1161 + * The number of modes added to the connector. 1162 + */ 1163 + int drm_connector_helper_tv_get_modes(struct drm_connector *connector) 1164 + { 1165 + struct drm_device *dev = connector->dev; 1166 + struct drm_property *tv_mode_property = 1167 + dev->mode_config.tv_mode_property; 1168 + struct drm_cmdline_mode *cmdline = &connector->cmdline_mode; 1169 + unsigned int ntsc_modes = BIT(DRM_MODE_TV_MODE_NTSC) | 1170 + BIT(DRM_MODE_TV_MODE_NTSC_443) | 1171 + BIT(DRM_MODE_TV_MODE_NTSC_J) | 1172 + BIT(DRM_MODE_TV_MODE_PAL_M); 1173 + unsigned int pal_modes = BIT(DRM_MODE_TV_MODE_PAL) | 1174 + BIT(DRM_MODE_TV_MODE_PAL_N) | 1175 + BIT(DRM_MODE_TV_MODE_SECAM); 1176 + unsigned int tv_modes[2] = { UINT_MAX, UINT_MAX }; 1177 + unsigned int i, supported_tv_modes = 0; 1178 + 1179 + if (!tv_mode_property) 1180 + return 0; 1181 + 1182 + for (i = 0; i < tv_mode_property->num_values; i++) 1183 + supported_tv_modes |= BIT(tv_mode_property->values[i]); 1184 + 1185 + if ((supported_tv_modes & ntsc_modes) && 1186 + (supported_tv_modes & pal_modes)) { 1187 + uint64_t default_mode; 1188 + 1189 + if (drm_object_property_get_default_value(&connector->base, 1190 + tv_mode_property, 1191 + &default_mode)) 1192 + return 0; 1193 + 1194 + if (cmdline->tv_mode_specified) 1195 + default_mode = cmdline->tv_mode; 1196 + 1197 + if (BIT(default_mode) & ntsc_modes) { 1198 + tv_modes[0] = DRM_MODE_TV_MODE_NTSC; 1199 + tv_modes[1] = DRM_MODE_TV_MODE_PAL; 1200 + } else { 1201 + tv_modes[0] = DRM_MODE_TV_MODE_PAL; 1202 + tv_modes[1] = DRM_MODE_TV_MODE_NTSC; 1203 + } 1204 + } else if (supported_tv_modes & ntsc_modes) { 1205 + tv_modes[0] = DRM_MODE_TV_MODE_NTSC; 1206 + } else if (supported_tv_modes & pal_modes) { 1207 + tv_modes[0] = DRM_MODE_TV_MODE_PAL; 1208 + } else { 1209 + return 0; 1210 + } 1211 + 1212 + for (i = 0; i < ARRAY_SIZE(tv_modes); i++) { 1213 + struct drm_display_mode *mode; 1214 + 1215 + if (tv_modes[i] == DRM_MODE_TV_MODE_NTSC) 1216 + mode = drm_mode_analog_ntsc_480i(dev); 1217 + else if (tv_modes[i] == DRM_MODE_TV_MODE_PAL) 1218 + mode = drm_mode_analog_pal_576i(dev); 1219 + else 1220 + break; 1221 + if (!mode) 1222 + return i; 1223 + if (!i) 1224 + mode->type |= DRM_MODE_TYPE_PREFERRED; 1225 + drm_mode_probed_add(connector, mode); 1226 + } 1227 + 1228 + return i; 1229 + } 1230 + EXPORT_SYMBOL(drm_connector_helper_tv_get_modes);
+1
drivers/gpu/drm/tests/Makefile
··· 13 13 drm_mm_test.o \ 14 14 drm_modes_test.o \ 15 15 drm_plane_helper_test.o \ 16 + drm_probe_helper_test.o \ 16 17 drm_rect_test.o
+205
drivers/gpu/drm/tests/drm_probe_helper_test.c
··· 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_mode.h> 11 + #include <drm/drm_modes.h> 12 + #include <drm/drm_modeset_helper_vtables.h> 13 + #include <drm/drm_probe_helper.h> 14 + 15 + #include <kunit/test.h> 16 + 17 + #include "drm_kunit_helpers.h" 18 + 19 + struct drm_probe_helper_test_priv { 20 + struct drm_device *drm; 21 + struct drm_connector connector; 22 + }; 23 + 24 + static const struct drm_connector_helper_funcs drm_probe_helper_connector_helper_funcs = { 25 + }; 26 + 27 + static const struct drm_connector_funcs drm_probe_helper_connector_funcs = { 28 + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 29 + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 30 + .reset = drm_atomic_helper_connector_reset, 31 + }; 32 + 33 + static int drm_probe_helper_test_init(struct kunit *test) 34 + { 35 + struct drm_probe_helper_test_priv *priv; 36 + struct drm_connector *connector; 37 + int ret; 38 + 39 + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); 40 + KUNIT_ASSERT_NOT_NULL(test, priv); 41 + test->priv = priv; 42 + 43 + priv->drm = drm_kunit_device_init(test, DRIVER_MODESET | DRIVER_ATOMIC, 44 + "drm-probe-helper-test"); 45 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm); 46 + 47 + connector = &priv->connector; 48 + ret = drmm_connector_init(priv->drm, connector, 49 + &drm_probe_helper_connector_funcs, 50 + DRM_MODE_CONNECTOR_Unknown, 51 + NULL); 52 + KUNIT_ASSERT_EQ(test, ret, 0); 53 + 54 + drm_connector_helper_add(connector, &drm_probe_helper_connector_helper_funcs); 55 + 56 + return 0; 57 + } 58 + 59 + typedef struct drm_display_mode *(*expected_mode_func_t)(struct drm_device *); 60 + 61 + struct drm_connector_helper_tv_get_modes_test { 62 + const char *name; 63 + unsigned int supported_tv_modes; 64 + enum drm_connector_tv_mode default_mode; 65 + bool cmdline; 66 + enum drm_connector_tv_mode cmdline_mode; 67 + expected_mode_func_t *expected_modes; 68 + unsigned int num_expected_modes; 69 + }; 70 + 71 + #define _TV_MODE_TEST(_name, _supported, _default, _cmdline, _cmdline_mode, ...) \ 72 + { \ 73 + .name = _name, \ 74 + .supported_tv_modes = _supported, \ 75 + .default_mode = _default, \ 76 + .cmdline = _cmdline, \ 77 + .cmdline_mode = _cmdline_mode, \ 78 + .expected_modes = (expected_mode_func_t[]) { __VA_ARGS__ }, \ 79 + .num_expected_modes = sizeof((expected_mode_func_t[]) { __VA_ARGS__ }) / \ 80 + (sizeof(expected_mode_func_t)), \ 81 + } 82 + 83 + #define TV_MODE_TEST(_name, _supported, _default, ...) \ 84 + _TV_MODE_TEST(_name, _supported, _default, false, 0, __VA_ARGS__) 85 + 86 + #define TV_MODE_TEST_CMDLINE(_name, _supported, _default, _cmdline, ...) \ 87 + _TV_MODE_TEST(_name, _supported, _default, true, _cmdline, __VA_ARGS__) 88 + 89 + static void 90 + drm_test_connector_helper_tv_get_modes_check(struct kunit *test) 91 + { 92 + const struct drm_connector_helper_tv_get_modes_test *params = test->param_value; 93 + struct drm_probe_helper_test_priv *priv = test->priv; 94 + struct drm_connector *connector = &priv->connector; 95 + struct drm_cmdline_mode *cmdline = &connector->cmdline_mode; 96 + struct drm_display_mode *mode; 97 + const struct drm_display_mode *expected; 98 + size_t len; 99 + int ret; 100 + 101 + if (params->cmdline) { 102 + cmdline->tv_mode_specified = true; 103 + cmdline->tv_mode = params->cmdline_mode; 104 + } 105 + 106 + ret = drm_mode_create_tv_properties(priv->drm, params->supported_tv_modes); 107 + KUNIT_ASSERT_EQ(test, ret, 0); 108 + 109 + drm_object_attach_property(&connector->base, 110 + priv->drm->mode_config.tv_mode_property, 111 + params->default_mode); 112 + 113 + mutex_lock(&priv->drm->mode_config.mutex); 114 + 115 + ret = drm_connector_helper_tv_get_modes(connector); 116 + KUNIT_EXPECT_EQ(test, ret, params->num_expected_modes); 117 + 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");
+1
include/drm/drm_probe_helper.h
··· 35 35 int drm_connector_helper_get_modes_fixed(struct drm_connector *connector, 36 36 const struct drm_display_mode *fixed_mode); 37 37 int drm_connector_helper_get_modes(struct drm_connector *connector); 38 + int drm_connector_helper_tv_get_modes(struct drm_connector *connector); 38 39 39 40 #endif