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

drm/modes: parse_cmdline: Allow specifying stand-alone options

Some options which can be specified on the commandline, such as
margin_right=..., margin_left=..., etc. are applied not only to the
specified mode, but to all modes. As such it would be nice if the user
can simply say e.g.
video=HDMI-1:margin_right=14,margin_left=24,margin_bottom=36,margin_top=42

This commit refactors drm_mode_parse_command_line_for_connector() to
add support for this, and as a nice side effect also cleans up the
function a bit.

Acked-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-8-hdegoede@redhat.com

+86 -58
+34 -58
drivers/gpu/drm/drm_modes.c
··· 1677 1677 "PAL", 1678 1678 }; 1679 1679 1680 - static bool drm_named_mode_is_in_whitelist(const char *mode, unsigned int size) 1681 - { 1682 - int i; 1683 - 1684 - for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++) 1685 - if (!strncmp(mode, drm_named_modes_whitelist[i], size)) 1686 - return true; 1687 - 1688 - return false; 1689 - } 1690 - 1691 1680 /** 1692 1681 * drm_mode_parse_command_line_for_connector - parse command line modeline for connector 1693 1682 * @mode_option: optional per connector mode option ··· 1707 1718 struct drm_cmdline_mode *mode) 1708 1719 { 1709 1720 const char *name; 1710 - bool named_mode = false, parse_extras = false; 1721 + bool freestanding = false, parse_extras = false; 1711 1722 unsigned int bpp_off = 0, refresh_off = 0, options_off = 0; 1712 1723 unsigned int mode_end = 0; 1713 1724 const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL; ··· 1727 1738 1728 1739 name = mode_option; 1729 1740 1730 - /* 1731 - * This is a bit convoluted. To differentiate between the 1732 - * named modes and poorly formatted resolutions, we need a 1733 - * bunch of things: 1734 - * - We need to make sure that the first character (which 1735 - * would be our resolution in X) is a digit. 1736 - * - If not, then it's either a named mode or a force on/off. 1737 - * To distinguish between the two, we need to run the 1738 - * extra parsing function, and if not, then we consider it 1739 - * a named mode. 1740 - * 1741 - * If this isn't enough, we should add more heuristics here, 1742 - * and matching unit-tests. 1743 - */ 1744 - if (!isdigit(name[0]) && name[0] != 'x') { 1745 - unsigned int namelen = strlen(name); 1746 - 1747 - /* 1748 - * Only the force on/off options can be in that case, 1749 - * and they all take a single character. 1750 - */ 1751 - if (namelen == 1) { 1752 - ret = drm_mode_parse_cmdline_extra(name, namelen, true, 1753 - connector, mode); 1754 - if (!ret) 1755 - return true; 1756 - } 1757 - 1758 - named_mode = true; 1759 - } 1760 - 1761 1741 /* Try to locate the bpp and refresh specifiers, if any */ 1762 1742 bpp_ptr = strchr(name, '-'); 1763 1743 if (bpp_ptr) 1764 1744 bpp_off = bpp_ptr - name; 1765 1745 1766 1746 refresh_ptr = strchr(name, '@'); 1767 - if (refresh_ptr) { 1768 - if (named_mode) 1769 - return false; 1770 - 1747 + if (refresh_ptr) 1771 1748 refresh_off = refresh_ptr - name; 1772 - } 1773 1749 1774 1750 /* Locate the start of named options */ 1775 1751 options_ptr = strchr(name, ','); ··· 1754 1800 parse_extras = true; 1755 1801 } 1756 1802 1757 - if (named_mode) { 1758 - if (mode_end + 1 > DRM_DISPLAY_MODE_LEN) 1759 - return false; 1803 + /* First check for a named mode */ 1804 + for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++) { 1805 + ret = str_has_prefix(name, drm_named_modes_whitelist[i]); 1806 + if (ret == mode_end) { 1807 + if (refresh_ptr) 1808 + return false; /* named + refresh is invalid */ 1760 1809 1761 - if (!drm_named_mode_is_in_whitelist(name, mode_end)) 1762 - return false; 1810 + strcpy(mode->name, drm_named_modes_whitelist[i]); 1811 + mode->specified = true; 1812 + break; 1813 + } 1814 + } 1763 1815 1764 - strscpy(mode->name, name, mode_end + 1); 1765 - } else { 1816 + /* No named mode? Check for a normal mode argument, e.g. 1024x768 */ 1817 + if (!mode->specified && isdigit(name[0])) { 1766 1818 ret = drm_mode_parse_cmdline_res_mode(name, mode_end, 1767 1819 parse_extras, 1768 1820 connector, 1769 1821 mode); 1770 1822 if (ret) 1771 1823 return false; 1824 + 1825 + mode->specified = true; 1772 1826 } 1773 - mode->specified = true; 1827 + 1828 + /* No mode? Check for freestanding extras and/or options */ 1829 + if (!mode->specified) { 1830 + unsigned int len = strlen(mode_option); 1831 + 1832 + if (bpp_ptr || refresh_ptr) 1833 + return false; /* syntax error */ 1834 + 1835 + if (len == 1 || (len >= 2 && mode_option[1] == ',')) 1836 + extra_ptr = mode_option; 1837 + else 1838 + options_ptr = mode_option - 1; 1839 + 1840 + freestanding = true; 1841 + } 1774 1842 1775 1843 if (bpp_ptr) { 1776 1844 ret = drm_mode_parse_cmdline_bpp(bpp_ptr, &bpp_end_ptr, mode); ··· 1828 1852 else 1829 1853 len = strlen(extra_ptr); 1830 1854 1831 - ret = drm_mode_parse_cmdline_extra(extra_ptr, len, false, 1855 + ret = drm_mode_parse_cmdline_extra(extra_ptr, len, freestanding, 1832 1856 connector, mode); 1833 1857 if (ret) 1834 1858 return false; ··· 1836 1860 1837 1861 if (options_ptr) { 1838 1862 ret = drm_mode_parse_cmdline_options(options_ptr + 1, 1839 - false, 1863 + freestanding, 1840 1864 connector, mode); 1841 1865 if (ret) 1842 1866 return false;
+2
drivers/gpu/drm/selftests/drm_cmdline_selftests.h
··· 62 62 cmdline_test(drm_cmdline_test_invalid_option) 63 63 cmdline_test(drm_cmdline_test_bpp_extra_and_option) 64 64 cmdline_test(drm_cmdline_test_extra_and_option) 65 + cmdline_test(drm_cmdline_test_freestanding_options) 66 + cmdline_test(drm_cmdline_test_freestanding_force_e_and_options)
+50
drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
··· 1042 1042 return 0; 1043 1043 } 1044 1044 1045 + static int drm_cmdline_test_freestanding_options(void *ignored) 1046 + { 1047 + struct drm_cmdline_mode mode = { }; 1048 + 1049 + FAIL_ON(!drm_mode_parse_command_line_for_connector("margin_right=14,margin_left=24,margin_bottom=36,margin_top=42", 1050 + &no_connector, 1051 + &mode)); 1052 + FAIL_ON(mode.specified); 1053 + FAIL_ON(mode.refresh_specified); 1054 + FAIL_ON(mode.bpp_specified); 1055 + 1056 + FAIL_ON(mode.tv_margins.right != 14); 1057 + FAIL_ON(mode.tv_margins.left != 24); 1058 + FAIL_ON(mode.tv_margins.bottom != 36); 1059 + FAIL_ON(mode.tv_margins.top != 42); 1060 + 1061 + FAIL_ON(mode.rb); 1062 + FAIL_ON(mode.cvt); 1063 + FAIL_ON(mode.interlace); 1064 + FAIL_ON(mode.margins); 1065 + FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); 1066 + 1067 + return 0; 1068 + } 1069 + 1070 + static int drm_cmdline_test_freestanding_force_e_and_options(void *ignored) 1071 + { 1072 + struct drm_cmdline_mode mode = { }; 1073 + 1074 + FAIL_ON(!drm_mode_parse_command_line_for_connector("e,margin_right=14,margin_left=24,margin_bottom=36,margin_top=42", 1075 + &no_connector, 1076 + &mode)); 1077 + FAIL_ON(mode.specified); 1078 + FAIL_ON(mode.refresh_specified); 1079 + FAIL_ON(mode.bpp_specified); 1080 + 1081 + FAIL_ON(mode.tv_margins.right != 14); 1082 + FAIL_ON(mode.tv_margins.left != 24); 1083 + FAIL_ON(mode.tv_margins.bottom != 36); 1084 + FAIL_ON(mode.tv_margins.top != 42); 1085 + 1086 + FAIL_ON(mode.rb); 1087 + FAIL_ON(mode.cvt); 1088 + FAIL_ON(mode.interlace); 1089 + FAIL_ON(mode.margins); 1090 + FAIL_ON(mode.force != DRM_FORCE_ON); 1091 + 1092 + return 0; 1093 + } 1094 + 1045 1095 #include "drm_selftest.c" 1046 1096 1047 1097 static int __init test_drm_cmdline_init(void)