diff options
author | Maxime Ripard <maxime.ripard@bootlin.com> | 2019-06-19 06:17:51 -0400 |
---|---|---|
committer | Maxime Ripard <maxime.ripard@bootlin.com> | 2019-06-19 06:17:51 -0400 |
commit | 1bf4e09227c345e246062285eba4b8fe660e512e (patch) | |
tree | 45396ee8fa752b77e08db4c2ae3be1f1c21dab2b | |
parent | 3aeeb13d899627fe2b86bdbdcd0927cf7192234f (diff) |
drm/modes: Allow to specify rotation and reflection on the commandline
Rotations and reflections setup are needed in some scenarios to initialise
properly the initial framebuffer. Some drivers already had a bunch of
quirks to deal with this, such as either a private kernel command line
parameter (omapdss) or on the device tree (various panels).
In order to accomodate this, let's create a video mode parameter to deal
with the rotation and reflexion.
Reviewed-by: Noralf Trønnes <noralf@tronnes.org>
Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
Link: https://patchwork.freedesktop.org/patch/msgid/777da16e42db757c1f5b414b5ca34507097fed5c.1560783090.git-series.maxime.ripard@bootlin.com
-rw-r--r-- | Documentation/fb/modedb.txt | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_client_modeset.c | 30 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_modes.c | 114 | ||||
-rw-r--r-- | include/drm/drm_connector.h | 10 |
4 files changed, 146 insertions, 20 deletions
diff --git a/Documentation/fb/modedb.txt b/Documentation/fb/modedb.txt index 16aa08453911..52418c6dbfc4 100644 --- a/Documentation/fb/modedb.txt +++ b/Documentation/fb/modedb.txt | |||
@@ -51,6 +51,18 @@ To force the VGA output to be enabled and drive a specific mode say: | |||
51 | Specifying the option multiple times for different ports is possible, e.g.: | 51 | Specifying the option multiple times for different ports is possible, e.g.: |
52 | video=LVDS-1:d video=HDMI-1:D | 52 | video=LVDS-1:d video=HDMI-1:D |
53 | 53 | ||
54 | Options can also be passed after the mode, using commas as separator. | ||
55 | |||
56 | Sample usage: 720x480,rotate=180 - 720x480 mode, rotated by 180 degrees | ||
57 | |||
58 | Valid options are: | ||
59 | |||
60 | - reflect_x (boolean): Perform an axial symmetry on the X axis | ||
61 | - reflect_y (boolean): Perform an axial symmetry on the Y axis | ||
62 | - rotate (integer): Rotate the initial framebuffer by x | ||
63 | degrees. Valid values are 0, 90, 180 and 270. | ||
64 | |||
65 | |||
54 | ***** oOo ***** oOo ***** oOo ***** oOo ***** oOo ***** oOo ***** oOo ***** | 66 | ***** oOo ***** oOo ***** oOo ***** oOo ***** oOo ***** oOo ***** oOo ***** |
55 | 67 | ||
56 | What is the VESA(TM) Coordinated Video Timings (CVT)? | 68 | What is the VESA(TM) Coordinated Video Timings (CVT)? |
diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index 33d4988f22ae..e95fceac8f8b 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c | |||
@@ -824,6 +824,7 @@ bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation) | |||
824 | { | 824 | { |
825 | struct drm_connector *connector = modeset->connectors[0]; | 825 | struct drm_connector *connector = modeset->connectors[0]; |
826 | struct drm_plane *plane = modeset->crtc->primary; | 826 | struct drm_plane *plane = modeset->crtc->primary; |
827 | struct drm_cmdline_mode *cmdline; | ||
827 | u64 valid_mask = 0; | 828 | u64 valid_mask = 0; |
828 | unsigned int i; | 829 | unsigned int i; |
829 | 830 | ||
@@ -844,6 +845,35 @@ bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation) | |||
844 | *rotation = DRM_MODE_ROTATE_0; | 845 | *rotation = DRM_MODE_ROTATE_0; |
845 | } | 846 | } |
846 | 847 | ||
848 | /** | ||
849 | * The panel already defined the default rotation | ||
850 | * through its orientation. Whatever has been provided | ||
851 | * on the command line needs to be added to that. | ||
852 | * | ||
853 | * Unfortunately, the rotations are at different bit | ||
854 | * indices, so the math to add them up are not as | ||
855 | * trivial as they could. | ||
856 | * | ||
857 | * Reflections on the other hand are pretty trivial to deal with, a | ||
858 | * simple XOR between the two handle the addition nicely. | ||
859 | */ | ||
860 | cmdline = &connector->cmdline_mode; | ||
861 | if (cmdline->specified) { | ||
862 | unsigned int cmdline_rest, panel_rest; | ||
863 | unsigned int cmdline_rot, panel_rot; | ||
864 | unsigned int sum_rot, sum_rest; | ||
865 | |||
866 | panel_rot = ilog2(*rotation & DRM_MODE_ROTATE_MASK); | ||
867 | cmdline_rot = ilog2(cmdline->rotation_reflection & DRM_MODE_ROTATE_MASK); | ||
868 | sum_rot = (panel_rot + cmdline_rot) % 4; | ||
869 | |||
870 | panel_rest = *rotation & ~DRM_MODE_ROTATE_MASK; | ||
871 | cmdline_rest = cmdline->rotation_reflection & ~DRM_MODE_ROTATE_MASK; | ||
872 | sum_rest = panel_rest ^ cmdline_rest; | ||
873 | |||
874 | *rotation = (1 << sum_rot) | sum_rest; | ||
875 | } | ||
876 | |||
847 | /* | 877 | /* |
848 | * TODO: support 90 / 270 degree hardware rotation, | 878 | * TODO: support 90 / 270 degree hardware rotation, |
849 | * depending on the hardware this may require the framebuffer | 879 | * depending on the hardware this may require the framebuffer |
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 429d3be17800..dc6d11292685 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c | |||
@@ -1554,6 +1554,71 @@ static int drm_mode_parse_cmdline_res_mode(const char *str, unsigned int length, | |||
1554 | return 0; | 1554 | return 0; |
1555 | } | 1555 | } |
1556 | 1556 | ||
1557 | static int drm_mode_parse_cmdline_options(char *str, size_t len, | ||
1558 | struct drm_connector *connector, | ||
1559 | struct drm_cmdline_mode *mode) | ||
1560 | { | ||
1561 | unsigned int rotation = 0; | ||
1562 | char *sep = str; | ||
1563 | |||
1564 | while ((sep = strchr(sep, ','))) { | ||
1565 | char *delim, *option; | ||
1566 | |||
1567 | option = sep + 1; | ||
1568 | delim = strchr(option, '='); | ||
1569 | if (!delim) { | ||
1570 | delim = strchr(option, ','); | ||
1571 | |||
1572 | if (!delim) | ||
1573 | delim = str + len; | ||
1574 | } | ||
1575 | |||
1576 | if (!strncmp(option, "rotate", delim - option)) { | ||
1577 | const char *value = delim + 1; | ||
1578 | unsigned int deg; | ||
1579 | |||
1580 | deg = simple_strtol(value, &sep, 10); | ||
1581 | |||
1582 | /* Make sure we have parsed something */ | ||
1583 | if (sep == value) | ||
1584 | return -EINVAL; | ||
1585 | |||
1586 | switch (deg) { | ||
1587 | case 0: | ||
1588 | rotation |= DRM_MODE_ROTATE_0; | ||
1589 | break; | ||
1590 | |||
1591 | case 90: | ||
1592 | rotation |= DRM_MODE_ROTATE_90; | ||
1593 | break; | ||
1594 | |||
1595 | case 180: | ||
1596 | rotation |= DRM_MODE_ROTATE_180; | ||
1597 | break; | ||
1598 | |||
1599 | case 270: | ||
1600 | rotation |= DRM_MODE_ROTATE_270; | ||
1601 | break; | ||
1602 | |||
1603 | default: | ||
1604 | return -EINVAL; | ||
1605 | } | ||
1606 | } else if (!strncmp(option, "reflect_x", delim - option)) { | ||
1607 | rotation |= DRM_MODE_REFLECT_X; | ||
1608 | sep = delim; | ||
1609 | } else if (!strncmp(option, "reflect_y", delim - option)) { | ||
1610 | rotation |= DRM_MODE_REFLECT_Y; | ||
1611 | sep = delim; | ||
1612 | } else { | ||
1613 | return -EINVAL; | ||
1614 | } | ||
1615 | } | ||
1616 | |||
1617 | mode->rotation_reflection = rotation; | ||
1618 | |||
1619 | return 0; | ||
1620 | } | ||
1621 | |||
1557 | /** | 1622 | /** |
1558 | * drm_mode_parse_command_line_for_connector - parse command line modeline for connector | 1623 | * drm_mode_parse_command_line_for_connector - parse command line modeline for connector |
1559 | * @mode_option: optional per connector mode option | 1624 | * @mode_option: optional per connector mode option |
@@ -1569,6 +1634,10 @@ static int drm_mode_parse_cmdline_res_mode(const char *str, unsigned int length, | |||
1569 | * | 1634 | * |
1570 | * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd] | 1635 | * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd] |
1571 | * | 1636 | * |
1637 | * Additionals options can be provided following the mode, using a comma to | ||
1638 | * separate each option. Valid options can be found in | ||
1639 | * Documentation/fb/modedb.txt. | ||
1640 | * | ||
1572 | * The intermediate drm_cmdline_mode structure is required to store additional | 1641 | * The intermediate drm_cmdline_mode structure is required to store additional |
1573 | * options from the command line modline like the force-enable/disable flag. | 1642 | * options from the command line modline like the force-enable/disable flag. |
1574 | * | 1643 | * |
@@ -1581,9 +1650,10 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, | |||
1581 | { | 1650 | { |
1582 | const char *name; | 1651 | const char *name; |
1583 | bool named_mode = false, parse_extras = false; | 1652 | bool named_mode = false, parse_extras = false; |
1584 | unsigned int bpp_off = 0, refresh_off = 0; | 1653 | unsigned int bpp_off = 0, refresh_off = 0, options_off = 0; |
1585 | unsigned int mode_end = 0; | 1654 | unsigned int mode_end = 0; |
1586 | char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL; | 1655 | char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL; |
1656 | char *options_ptr = NULL; | ||
1587 | char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL; | 1657 | char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL; |
1588 | int ret; | 1658 | int ret; |
1589 | 1659 | ||
@@ -1632,13 +1702,18 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, | |||
1632 | mode->refresh_specified = true; | 1702 | mode->refresh_specified = true; |
1633 | } | 1703 | } |
1634 | 1704 | ||
1705 | /* Locate the start of named options */ | ||
1706 | options_ptr = strchr(name, ','); | ||
1707 | if (options_ptr) | ||
1708 | options_off = options_ptr - name; | ||
1709 | |||
1635 | /* Locate the end of the name / resolution, and parse it */ | 1710 | /* Locate the end of the name / resolution, and parse it */ |
1636 | if (bpp_ptr && refresh_ptr) { | 1711 | if (bpp_ptr) { |
1637 | mode_end = min(bpp_off, refresh_off); | ||
1638 | } else if (bpp_ptr) { | ||
1639 | mode_end = bpp_off; | 1712 | mode_end = bpp_off; |
1640 | } else if (refresh_ptr) { | 1713 | } else if (refresh_ptr) { |
1641 | mode_end = refresh_off; | 1714 | mode_end = refresh_off; |
1715 | } else if (options_ptr) { | ||
1716 | mode_end = options_off; | ||
1642 | } else { | 1717 | } else { |
1643 | mode_end = strlen(name); | 1718 | mode_end = strlen(name); |
1644 | parse_extras = true; | 1719 | parse_extras = true; |
@@ -1680,24 +1755,23 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, | |||
1680 | else if (refresh_ptr) | 1755 | else if (refresh_ptr) |
1681 | extra_ptr = refresh_end_ptr; | 1756 | extra_ptr = refresh_end_ptr; |
1682 | 1757 | ||
1683 | if (extra_ptr) { | 1758 | if (extra_ptr && |
1684 | if (!named_mode) { | 1759 | extra_ptr != options_ptr) { |
1685 | int len = strlen(name) - (extra_ptr - name); | 1760 | int len = strlen(name) - (extra_ptr - name); |
1686 | 1761 | ||
1687 | ret = drm_mode_parse_cmdline_extra(extra_ptr, len, | 1762 | ret = drm_mode_parse_cmdline_extra(extra_ptr, len, |
1688 | connector, mode); | 1763 | connector, mode); |
1689 | if (ret) | 1764 | if (ret) |
1690 | return false; | 1765 | return false; |
1691 | } else { | 1766 | } |
1692 | int remaining = strlen(name) - (extra_ptr - name); | ||
1693 | 1767 | ||
1694 | /* | 1768 | if (options_ptr) { |
1695 | * We still have characters to process, while | 1769 | int len = strlen(name) - (options_ptr - name); |
1696 | * we shouldn't have any | 1770 | |
1697 | */ | 1771 | ret = drm_mode_parse_cmdline_options(options_ptr, len, |
1698 | if (remaining > 0) | 1772 | connector, mode); |
1699 | return false; | 1773 | if (ret) |
1700 | } | 1774 | return false; |
1701 | } | 1775 | } |
1702 | 1776 | ||
1703 | return true; | 1777 | return true; |
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index cdf2fb910010..8eebe0432c73 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h | |||
@@ -1025,6 +1025,16 @@ struct drm_cmdline_mode { | |||
1025 | * state to one of the DRM_FORCE_* values. | 1025 | * state to one of the DRM_FORCE_* values. |
1026 | */ | 1026 | */ |
1027 | enum drm_connector_force force; | 1027 | enum drm_connector_force force; |
1028 | |||
1029 | /** | ||
1030 | * @rotation_reflection: | ||
1031 | * | ||
1032 | * Initial rotation and reflection of the mode setup from the | ||
1033 | * command line. See DRM_MODE_ROTATE_* and | ||
1034 | * DRM_MODE_REFLECT_*. The only rotations supported are | ||
1035 | * DRM_MODE_ROTATE_0 and DRM_MODE_ROTATE_180. | ||
1036 | */ | ||
1037 | unsigned int rotation_reflection; | ||
1028 | }; | 1038 | }; |
1029 | 1039 | ||
1030 | /** | 1040 | /** |