180° rotation support for NVIDIA Tegra DRM
Subsystems (6)
cover 180° rotation support for NVIDIA Tegra DRM
Hello!
This series adds 180° display plane rotation support to the NVIDIA Tegra DRM driver which is needed for devices that have display panel physically mounted upside-down, like Nexus 7 tablet device for example [1]. Since DRM panel rotation is a new thing for a userspace, currently only Opentegra Xorg driver handles the rotated display panel [2], but this is good enough for the start.
Note that later on it should be possible to implement a transparent 180° display rotation for Tegra DRM driver which will remove the need to have a bleeding edge userspace that knows how to rotate display planes and I'm slowly working on it. For the starter we can go with the minimal rotation support, so it's not a blocker.
This series is based on the work that was made by Derek Basehore for the Mediatek driver [3], his patches are included into this patchset. I added my tested-by tags to the Derek's patches.
Please review and apply, thanks in advance!
[1] https://patchwork.ozlabs.org/project/linux-tegra/patch/[email protected]/
[2] https://github.com/grate-driver/xf86-video-opentegra/commit/28eb20a3959bbe5bc3a3b67e55977093fd5114ca
[3] https://lkml.org/lkml/2020/3/5/1119
Derek Basehore (2):
drm/panel: Add helper for reading DT rotation
drm/panel: Set display info in panel attach
Dmitry Osipenko (4):
drm/panel: lvds: Set up panel orientation drm/tegra: plane: Rename bottom_up to reflect_y drm/tegra: plane: Support horizontal reflection mode drm/tegra: plane: Support 180° rotation
drivers/gpu/drm/drm_panel.c | 56 ++++++++++++++++++++++++++++ drivers/gpu/drm/panel/panel-lvds.c | 6 +++ drivers/gpu/drm/tegra/dc.c | 43 ++++++++++++++++++---- drivers/gpu/drm/tegra/dc.h | 3 +- drivers/gpu/drm/tegra/plane.c | 3 +- drivers/gpu/drm/tegra/plane.h | 3 +- include/drm/drm_panel.h | 59 ++++++++++++++++++++++++++++++ 7 files changed, 162 insertions(+), 11 deletions(-)
Signature · 6 lines
2.26.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 1/6 drm/panel: Add helper for reading DT rotation
From: Derek Basehore <[email protected]>
This adds a helper function for reading the rotation (panel orientation) from the device tree.
--- drivers/gpu/drm/drm_panel.c | 43 +++++++++++++++++++++++++++++++++++++ include/drm/drm_panel.h | 9 ++++++++ 2 files changed, 52 insertions(+)
drivers/gpu/drm/drm_panel.c +43 -0
@@ -300,6 +300,49 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np)return ERR_PTR (-EPROBE_DEFER ); }EXPORT_SYMBOL (of_drm_find_panel); + +/** +* of_drm_get_panel_orientation - look up the orientation of the panel through +* the "rotation" binding from a device tree node +* @np: device tree node of the panel +* @orientation: orientation enum to be filled in +* +* Looks up the rotation of a panel in the device tree. The orientation of the +* panel is expressed as a property name "rotation" in the device tree. The +* rotation in the device tree is counter clockwise. +* +* Return: 0 when a valid rotation value (0, 90, 180, or 270) is read or the +* rotation property doesn't exist. -EERROR otherwise. +*/ +int of_drm_get_panel_orientation (const struct device_node *np, +enum drm_panel_orientation *orientation) +{ +int rotation, ret; + + ret =of_property_read_u32 (np,"rotation" , &rotation); +if (ret == -EINVAL ) { +/* Don't return an error if there's no rotation property. */ + *orientation =DRM_MODE_PANEL_ORIENTATION_UNKNOWN ; +return 0 ; + } + +if (ret <0 ) +return ret; + +if (rotation ==0 ) + *orientation =DRM_MODE_PANEL_ORIENTATION_NORMAL ; +else if (rotation ==90 ) + *orientation =DRM_MODE_PANEL_ORIENTATION_RIGHT_UP ; +else if (rotation ==180 ) + *orientation =DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP ; +else if (rotation ==270 ) + *orientation =DRM_MODE_PANEL_ORIENTATION_LEFT_UP ; +else +return -EINVAL ; + +return 0 ; +} +EXPORT_SYMBOL (of_drm_get_panel_orientation);#endif #if IS_REACHABLE (CONFIG_BACKLIGHT_CLASS_DEVICE )
include/drm/drm_panel.h +9 -0
@@ -35,6 +35,8 @@ struct drm_device;struct drm_panel ;struct display_timing ; +enum drm_panel_orientation ; +/** * struct drm_panel_funcs - perform operations on a given panel *
@@ -191,11 +193,18 @@ int drm_panel_get_modes(struct drm_panel *panel, struct drm_connector *connector#if defined(CONFIG_OF ) && defined(CONFIG_DRM_PANEL )struct drm_panel *of_drm_find_panel (const struct device_node *np); +int of_drm_get_panel_orientation (const struct device_node *np, +enum drm_panel_orientation *orientation);#else static inline struct drm_panel *of_drm_find_panel (const struct device_node *np) {return ERR_PTR (-ENODEV ); } +static inline int of_drm_get_panel_orientation (const struct device_node *np, +enum drm_panel_orientation *orientation) +{ +return -ENODEV ; +}#endif #if IS_ENABLED(CONFIG_DRM_PANEL) && (IS_BUILTIN(CONFIG_BACKLIGHT_CLASS_DEVICE) || \
Signature · 1 line
2.26.0
PATCH 2/6 drm/panel: Set display info in panel attach
In reply to Dmitry OsipenkoFrom: Derek Basehore <[email protected]>
Devicetree systems can set panel orientation via a panel binding, but there's no way, as is, to propagate this setting to the connector, where the property need to be added.
To address this, this patch sets orientation, as well as other fixed values for the panel, in the drm_panel_attach function. These values are stored from probe in the drm_panel struct.
--- drivers/gpu/drm/drm_panel.c | 13 ++++++++++ include/drm/drm_panel.h | 50 +++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
drivers/gpu/drm/drm_panel.c +13 -0
@@ -114,6 +114,19 @@ EXPORT_SYMBOL(drm_panel_remove);*/ int drm_panel_attach (struct drm_panel *panel,struct drm_connector *connector) { +struct drm_display_info *info; + + info = &connector->display_info; + info->width_mm = panel->width_mm; + info->height_mm = panel->height_mm; + info->bpc = panel->bpc; + info->panel_orientation = panel->orientation; + info->bus_flags = panel->bus_flags; +if (panel->bus_formats) +drm_display_info_set_bus_formats (&connector->display_info, + panel->bus_formats, + panel->num_bus_formats); +return 0 ; }EXPORT_SYMBOL (drm_panel_attach);
include/drm/drm_panel.h +50 -0
56 lines not on this page · raw message
Signature · 1 line
2.26.0
Re: [PATCH v1 2/6] drm/panel: Set display info in panel attach
14.06.2020 22:03, Dmitry Osipenko пишет:
Quoted 9 lines
From: Derek Basehore <[email protected]>
Devicetree systems can set panel orientation via a panel binding, but there's no way, as is, to propagate this setting to the connector, where the property need to be added.
To address this, this patch sets orientation, as well as other fixed values for the panel, in thedrm_panel_attachfunction. These values are stored from probe in thedrm_panelstruct.
Quoted 5 lines
Signed-off-by Derek Basehore <[email protected]>Reviewed-by Sam Ravnborg <[email protected]>Tested-by Dmitry Osipenko <[email protected]>Signed-off-by Dmitry Osipenko <[email protected]>---
Ah, I just realized that this patch is obsolete now! I also got email bounces saying that some email servers didn't like the UTF symbol in name of the first email, so will make a v2 shortly.
PATCH 3/6 drm/panel: lvds: Set up panel orientation
In reply to Dmitry OsipenkoThe panel orientation needs to parsed from a device-tree and assigned to the panel's connector in order to make orientation property available to userspace. That's what this patch does for the generic LVDS panel.
--- drivers/gpu/drm/panel/panel-lvds.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/gpu/drm/panel/panel-lvds.c +6 -0
@@ -99,6 +99,7 @@ static int panel_lvds_get_modes(struct drm_panel *panel, connector->display_info.bus_flags = lvds->data_mirror ?DRM_BUS_FLAG_DATA_LSB_TO_MSB :DRM_BUS_FLAG_DATA_MSB_TO_LSB ; +drm_connector_set_panel_orientation (connector, lvds->panel.orientation);return 1 ; }
@@ -223,6 +224,11 @@ static int panel_lvds_probe(struct platform_device *pdev)drm_panel_init (&lvds->panel, lvds->dev, &panel_lvds_funcs,DRM_MODE_CONNECTOR_LVDS ); + ret =of_drm_get_panel_orientation (lvds->dev->of_node, + &lvds->panel.orientation); +if (ret) +return ret; + ret =drm_panel_of_backlight (&lvds->panel);if (ret)return ret;
Signature · 1 line
2.26.0
PATCH 4/6 drm/tegra: plane: Rename bottom_up to reflect_y
In reply to Dmitry OsipenkoThis makes the naming consistent with the DRM core.
--- drivers/gpu/drm/tegra/dc.c | 10 +++++----- drivers/gpu/drm/tegra/dc.h | 2 +- drivers/gpu/drm/tegra/plane.c | 2 +- drivers/gpu/drm/tegra/plane.h | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-)
drivers/gpu/drm/tegra/dc.c +5 -5
@@ -404,7 +404,7 @@ static void tegra_dc_setup_window(struct tegra_plane *plane,tegra_plane_writel (plane, window->stride[0 ],DC_WIN_LINE_STRIDE ); } -if (window->bottom_up) +if (window->reflect_y) v_offset += window->src.h -1 ;tegra_plane_writel (plane, h_offset,DC_WINBUF_ADDR_H_OFFSET );
@@ -470,7 +470,7 @@ static void tegra_dc_setup_window(struct tegra_plane *plane, value |=COLOR_EXPAND ; } -if (window->bottom_up) +if (window->reflect_y) value |=V_DIRECTION ;if (tegra_plane_use_horizontal_filtering (plane, window)) {
@@ -642,9 +642,9 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, rotation =drm_rotation_simplify (state->rotation, rotation);if (rotation &DRM_MODE_REFLECT_Y ) - plane_state->bottom_up = true; + plane_state->reflect_y = true;else - plane_state->bottom_up = false; + plane_state->reflect_y = false;/* * Tegra doesn't support different strides for U and V planes so we
@@ -706,7 +706,7 @@ static void tegra_plane_atomic_update(struct drm_plane *plane, window.dst.w =drm_rect_width (&plane->state->dst); window.dst.h =drm_rect_height (&plane->state->dst); window.bits_per_pixel = fb->format->cpp[0 ] *8 ; - window.bottom_up =tegra_fb_is_bottom_up (fb) || state->bottom_up; + window.reflect_y =tegra_fb_is_bottom_up (fb) || state->reflect_y;/* copy from state */ window.zpos = plane->state->normalized_zpos;
drivers/gpu/drm/tegra/dc.h +1 -1
@@ -136,7 +136,7 @@ struct tegra_dc_window {unsigned int stride[2 ];unsigned long base[3 ];unsigned int zpos; -bool bottom_up; +bool reflect_y;struct tegra_bo_tiling tiling;u32 format;
drivers/gpu/drm/tegra/plane.c +1 -1
@@ -61,7 +61,7 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane) copy->tiling = state->tiling; copy->format = state->format; copy->swap = state->swap; - copy->bottom_up = state->bottom_up; + copy->reflect_y = state->reflect_y; copy->opaque = state->opaque;for (i =0 ; i <2 ; i++)
drivers/gpu/drm/tegra/plane.h +1 -1
@@ -46,7 +46,7 @@ struct tegra_plane_state {u32 format;u32 swap; -bool bottom_up; +bool reflect_y;/* used for legacy blending support only */ struct tegra_plane_legacy_blending_state blending[2 ];
Signature · 1 line
2.26.0
PATCH 5/6 drm/tegra: plane: Support horizontal reflection mode
In reply to Dmitry OsipenkoSupport horizontal reflection mode which will allow to support 180° rotation mode when combined with the vertical reflection.
--- drivers/gpu/drm/tegra/dc.c | 24 ++++++++++++++++++++---- drivers/gpu/drm/tegra/dc.h | 1 + drivers/gpu/drm/tegra/plane.c | 1 + drivers/gpu/drm/tegra/plane.h | 1 + 4 files changed, 23 insertions(+), 4 deletions(-)
drivers/gpu/drm/tegra/dc.c +20 -4
@@ -368,6 +368,12 @@ static void tegra_dc_setup_window(struct tegra_plane *plane, h_size = window->src.w * bpp; v_size = window->src.h; +if (window->reflect_x) + h_offset += (window->src.w -1 ) * bpp; + +if (window->reflect_y) + v_offset += window->src.h -1 ; + value =V_PRESCALED_SIZE (v_size) |H_PRESCALED_SIZE (h_size);tegra_plane_writel (plane, value,DC_WIN_PRESCALED_SIZE );
@@ -404,9 +410,6 @@ static void tegra_dc_setup_window(struct tegra_plane *plane,tegra_plane_writel (plane, window->stride[0 ],DC_WIN_LINE_STRIDE ); } -if (window->reflect_y) - v_offset += window->src.h -1 ; -tegra_plane_writel (plane, h_offset,DC_WINBUF_ADDR_H_OFFSET );tegra_plane_writel (plane, v_offset,DC_WINBUF_ADDR_V_OFFSET );
@@ -470,6 +473,9 @@ static void tegra_dc_setup_window(struct tegra_plane *plane, value |=COLOR_EXPAND ; } +if (window->reflect_x) + value |=H_DIRECTION ; +if (window->reflect_y) value |=V_DIRECTION ;
@@ -601,7 +607,9 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) {struct tegra_plane_state *plane_state =to_tegra_plane_state (state); -unsigned int rotation =DRM_MODE_ROTATE_0 |DRM_MODE_REFLECT_Y ; +unsigned int rotation =DRM_MODE_ROTATE_0 | +DRM_MODE_REFLECT_X | +DRM_MODE_REFLECT_Y ;struct tegra_bo_tiling *tiling = &plane_state->tiling;struct tegra_plane *tegra =to_tegra_plane (plane);struct tegra_dc *dc =to_tegra_dc (state->crtc);
@@ -641,6 +649,11 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, rotation =drm_rotation_simplify (state->rotation, rotation); +if (rotation &DRM_MODE_REFLECT_X ) + plane_state->reflect_x = true; +else + plane_state->reflect_x = false; +if (rotation &DRM_MODE_REFLECT_Y ) plane_state->reflect_y = true;else
@@ -706,6 +719,7 @@ static void tegra_plane_atomic_update(struct drm_plane *plane, window.dst.w =drm_rect_width (&plane->state->dst); window.dst.h =drm_rect_height (&plane->state->dst); window.bits_per_pixel = fb->format->cpp[0 ] *8 ; + window.reflect_x = state->reflect_x; window.reflect_y =tegra_fb_is_bottom_up (fb) || state->reflect_y;/* copy from state */
@@ -792,6 +806,7 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, err =drm_plane_create_rotation_property (&plane->base,DRM_MODE_ROTATE_0 ,DRM_MODE_ROTATE_0 | +DRM_MODE_REFLECT_X |DRM_MODE_REFLECT_Y );if (err <0 ) dev_err(dc->dev, "failed to create rotation property: %d\n",
@@ -1079,6 +1094,7 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, err =drm_plane_create_rotation_property (&plane->base,DRM_MODE_ROTATE_0 ,DRM_MODE_ROTATE_0 | +DRM_MODE_REFLECT_X |DRM_MODE_REFLECT_Y );if (err <0 ) dev_err(dc->dev, "failed to create rotation property: %d\n",
drivers/gpu/drm/tegra/dc.h +1 -0
@@ -136,6 +136,7 @@ struct tegra_dc_window {unsigned int stride[2 ];unsigned long base[3 ];unsigned int zpos; +bool reflect_x;bool reflect_y;struct tegra_bo_tiling tiling;
drivers/gpu/drm/tegra/plane.c +1 -0
@@ -61,6 +61,7 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane) copy->tiling = state->tiling; copy->format = state->format; copy->swap = state->swap; + copy->reflect_x = state->reflect_x; copy->reflect_y = state->reflect_y; copy->opaque = state->opaque;
drivers/gpu/drm/tegra/plane.h +1 -0
@@ -46,6 +46,7 @@ struct tegra_plane_state {u32 format;u32 swap; +bool reflect_x;bool reflect_y;/* used for legacy blending support only */
Signature · 1 line
2.26.0
PATCH 6/6 drm/tegra: plane: Support 180° rotation
In reply to Dmitry OsipenkoCombining horizontal and vertical reflections gives us 180 degrees of rotation.
--- drivers/gpu/drm/tegra/dc.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
drivers/gpu/drm/tegra/dc.c +12 -1
@@ -608,6 +608,7 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, {struct tegra_plane_state *plane_state =to_tegra_plane_state (state);unsigned int rotation =DRM_MODE_ROTATE_0 | +DRM_MODE_ROTATE_180 |DRM_MODE_REFLECT_X |DRM_MODE_REFLECT_Y ;struct tegra_bo_tiling *tiling = &plane_state->tiling;
@@ -659,6 +660,14 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,else plane_state->reflect_y = false; +if (tegra_fb_is_bottom_up (state->fb)) + plane_state->reflect_y = true; + +if (rotation &DRM_MODE_ROTATE_180 ) { + plane_state->reflect_x = !plane_state->reflect_x; + plane_state->reflect_y = !plane_state->reflect_y; + } +/* * Tegra doesn't support different strides for U and V planes so we * error out if the user tries to display a framebuffer with such a
@@ -720,7 +729,7 @@ static void tegra_plane_atomic_update(struct drm_plane *plane, window.dst.h =drm_rect_height (&plane->state->dst); window.bits_per_pixel = fb->format->cpp[0 ] *8 ; window.reflect_x = state->reflect_x; - window.reflect_y =tegra_fb_is_bottom_up (fb) || state->reflect_y; + window.reflect_y = state->reflect_y;/* copy from state */ window.zpos = plane->state->normalized_zpos;
@@ -806,6 +815,7 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, err =drm_plane_create_rotation_property (&plane->base,DRM_MODE_ROTATE_0 ,DRM_MODE_ROTATE_0 | +DRM_MODE_ROTATE_180 |DRM_MODE_REFLECT_X |DRM_MODE_REFLECT_Y );if (err <0 )
@@ -1094,6 +1104,7 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, err =drm_plane_create_rotation_property (&plane->base,DRM_MODE_ROTATE_0 ,DRM_MODE_ROTATE_0 | +DRM_MODE_ROTATE_180 |DRM_MODE_REFLECT_X |DRM_MODE_REFLECT_Y );if (err <0 )
Signature · 1 line
2.26.0