Support of vertical & horizontal mirroring features thanks to
the plane rotation property.
Signed-off-by: Yannick Fertre <[email protected]>
---
drivers/gpu/drm/stm/ltdc.c | 163 ++++++++++++++++++++++++-------------
drivers/gpu/drm/stm/ltdc.h | 1 +
2 files changed, 108 insertions(+), 56 deletions(-)
diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
index 00a6bc1b1d7c..ff2075dd9474 100644
--- a/drivers/gpu/drm/stm/ltdc.c
+++ b/drivers/gpu/drm/stm/ltdc.c
@@ -180,6 +180,7 @@
#define LXCR_LEN BIT(0) /* Layer ENable */
#define LXCR_COLKEN BIT(1) /* Color Keying Enable */
#define LXCR_CLUTEN BIT(4) /* Color Look-Up Table ENable */
+#define LXCR_HMEN BIT(8) /* Horizontal Mirroring ENable */
#define LXWHPCR_WHSTPOS GENMASK(11, 0) /* Window Horizontal StarT POSition */
#define LXWHPCR_WHSPPOS GENMASK(27, 16) /* Window Horizontal StoP POSition */
@@ -197,7 +198,7 @@
#define LXBFCR_BOR GENMASK(18, 16) /* Blending ORder */
#define LXCFBLR_CFBLL GENMASK(12, 0) /* Color Frame Buffer Line Length */
-#define LXCFBLR_CFBP GENMASK(28, 16) /* Color Frame Buffer Pitch in bytes */
+#define LXCFBLR_CFBP GENMASK(31, 16) /* Color Frame Buffer Pitch in bytes */
#define LXCFBLNR_CFBLN GENMASK(10, 0) /* Color Frame Buffer Line Number */
@@ -1237,7 +1238,8 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
u32 y0 = newstate->crtc_y;
u32 y1 = newstate->crtc_y + newstate->crtc_h - 1;
u32 src_x, src_y, src_w, src_h;
- u32 val, pitch_in_bytes, line_length, line_number, paddr, ahbp, avbp, bpcr;
+ u32 val, pitch_in_bytes, line_length, line_number, ahbp, avbp, bpcr;
+ u32 paddr, paddr1, paddr2;
enum ltdc_pix_fmt pf;
if (!newstate->crtc || !fb) {
@@ -1289,13 +1291,6 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
}
regmap_write_bits(ldev->regmap, LTDC_L1PFCR + lofs, LXPFCR_PF, val);
- /* Configures the color frame buffer pitch in bytes & line length */
- pitch_in_bytes = fb->pitches[0];
- line_length = fb->format->cpp[0] *
- (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
- val = ((pitch_in_bytes << 16) | line_length);
- regmap_write_bits(ldev->regmap, LTDC_L1CFBLR + lofs, LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
-
/* Specifies the constant alpha value */
val = newstate->alpha >> 8;
regmap_write_bits(ldev->regmap, LTDC_L1CACR + lofs, LXCACR_CONSTA, val);
@@ -1319,76 +1314,115 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
LXBFCR_BF2 | LXBFCR_BF1, val);
}
- /* Configures the frame buffer line number */
- line_number = y1 - y0 + 1;
- regmap_write_bits(ldev->regmap, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, line_number);
-
/* Sets the FB address */
paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 0);
+ if (newstate->rotation & DRM_MODE_REFLECT_X)
+ paddr += (fb->format->cpp[0] * (x1 - x0 + 1)) - 1;
+
+ if (newstate->rotation & DRM_MODE_REFLECT_Y)
+ paddr += (fb->pitches[0] * (y1 - y0));
+
DRM_DEBUG_DRIVER("fb: phys 0x%08x", paddr);
regmap_write(ldev->regmap, LTDC_L1CFBAR + lofs, paddr);
+ /* Configures the color frame buffer pitch in bytes & line length */
+ line_length = fb->format->cpp[0] *
+ (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
+
+ if (newstate->rotation & DRM_MODE_REFLECT_Y)
+ /* Compute negative value (signed on 16 bits) for the picth */
+ pitch_in_bytes = 0x10000 - fb->pitches[0];
+ else
+ pitch_in_bytes = fb->pitches[0];
+
+ val = (pitch_in_bytes << 16) | line_length;
+ regmap_write_bits(ldev->regmap, LTDC_L1CFBLR + lofs, LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
+
+ /* Configures the frame buffer line number */
+ line_number = y1 - y0 + 1;
+ regmap_write_bits(ldev->regmap, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, line_number);
+
if (ldev->caps.ycbcr_input) {
if (fb->format->is_yuv) {
switch (fb->format->format) {
case DRM_FORMAT_NV12:
case DRM_FORMAT_NV21:
- /* Configure the auxiliary frame buffer address 0 & 1 */
- paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
- regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
- regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr + 1);
+ /* Configure the auxiliary frame buffer address 0 */
+ paddr1 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
+
+ if (newstate->rotation & DRM_MODE_REFLECT_X)
+ paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
- /* Configure the buffer length */
- val = ((pitch_in_bytes << 16) | line_length);
- regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
+ if (newstate->rotation & DRM_MODE_REFLECT_Y)
+ paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
- /* Configure the frame buffer line number */
- val = (line_number >> 1);
- regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
+ regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
break;
case DRM_FORMAT_YUV420:
- /* Configure the auxiliary frame buffer address 0 */
- paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
- regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
-
- /* Configure the auxiliary frame buffer address 1 */
- paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
- regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr);
+ /* Configure the auxiliary frame buffer address 0 & 1 */
+ paddr1 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
+ paddr2 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
- line_length = ((fb->format->cpp[0] * (x1 - x0 + 1)) >> 1) +
- (ldev->caps.bus_width >> 3) - 1;
+ if (newstate->rotation & DRM_MODE_REFLECT_X) {
+ paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
+ paddr2 += ((fb->format->cpp[2] * (x1 - x0 + 1)) >> 1) - 1;
+ }
- /* Configure the buffer length */
- val = (((pitch_in_bytes >> 1) << 16) | line_length);
- regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
+ if (newstate->rotation & DRM_MODE_REFLECT_Y) {
+ paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
+ paddr2 += (fb->pitches[2] * (y1 - y0 - 1)) >> 1;
+ }
- /* Configure the frame buffer line number */
- val = (line_number >> 1);
- regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
+ regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
+ regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr2);
break;
case DRM_FORMAT_YVU420:
- /* Configure the auxiliary frame buffer address 0 */
- paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
- regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
-
- /* Configure the auxiliary frame buffer address 1 */
- paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
- regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr);
+ /* Configure the auxiliary frame buffer address 0 & 1 */
+ paddr1 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
+ paddr2 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
- line_length = ((fb->format->cpp[0] * (x1 - x0 + 1)) >> 1) +
- (ldev->caps.bus_width >> 3) - 1;
+ if (newstate->rotation & DRM_MODE_REFLECT_X) {
+ paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
+ paddr2 += ((fb->format->cpp[2] * (x1 - x0 + 1)) >> 1) - 1;
+ }
- /* Configure the buffer length */
- val = (((pitch_in_bytes >> 1) << 16) | line_length);
- regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
+ if (newstate->rotation & DRM_MODE_REFLECT_Y) {
+ paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
+ paddr2 += (fb->pitches[2] * (y1 - y0 - 1)) >> 1;
+ }
- /* Configure the frame buffer line number */
- val = (line_number >> 1);
- regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
+ regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
+ regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr2);
break;
}
+ /*
+ * Set the length and the number of lines of the auxiliary
+ * buffers if the framebuffer contains more than one plane.
+ */
+ if (fb->format->num_planes > 1) {
+ if (newstate->rotation & DRM_MODE_REFLECT_Y)
+ /*
+ * Compute negative value (signed on 16 bits)
+ * for the picth
+ */
+ pitch_in_bytes = 0x10000 - fb->pitches[1];
+ else
+ pitch_in_bytes = fb->pitches[1];
+
+ line_length = ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) +
+ (ldev->caps.bus_width >> 3) - 1;
+
+ /* Configure the auxiliary buffer length */
+ val = (pitch_in_bytes << 16) | line_length;
+ regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
+
+ /* Configure the auxiliary frame buffer line number */
+ val = line_number >> 1;
+ regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
+ }
+
/* Configure YCbC conversion coefficient */
ltdc_set_ycbcr_coeffs(plane);
@@ -1403,7 +1437,12 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
/* Enable layer and CLUT if needed */
val = fb->format->format == DRM_FORMAT_C8 ? LXCR_CLUTEN : 0;
val |= LXCR_LEN;
- regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN, val);
+
+ /* Enable horizontal mirroring if requested */
+ if (newstate->rotation & DRM_MODE_REFLECT_X)
+ val |= LXCR_HMEN;
+
+ regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN | LXCR_HMEN, val);
/* Commit shadow registers = update plane at next vblank */
if (ldev->caps.plane_reg_shadow)
@@ -1432,8 +1471,8 @@ static void ltdc_plane_atomic_disable(struct drm_plane *plane,
struct ltdc_device *ldev = plane_to_ltdc(plane);
u32 lofs = plane->index * LAY_OFS;
- /* disable layer */
- regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN, 0);
+ /* Disable layer */
+ regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN | LXCR_HMEN, 0);
/* Commit shadow registers = update plane at next vblank */
if (ldev->caps.plane_reg_shadow)
@@ -1577,6 +1616,7 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
{
struct ltdc_device *ldev = ddev->dev_private;
struct drm_plane *primary, *overlay;
+ int supported_rotations = DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y;
unsigned int i;
int ret;
@@ -1591,6 +1631,10 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
else
drm_plane_create_zpos_immutable_property(primary, 0);
+ if (ldev->caps.plane_rotation)
+ drm_plane_create_rotation_property(primary, DRM_MODE_ROTATE_0,
+ supported_rotations);
+
/* Init CRTC according to its hardware features */
if (ldev->caps.crc)
ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
@@ -1622,6 +1666,10 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
drm_plane_create_zpos_property(overlay, i, 0, ldev->caps.nb_layers - 1);
else
drm_plane_create_zpos_immutable_property(overlay, i);
+
+ if (ldev->caps.plane_rotation)
+ drm_plane_create_rotation_property(overlay, DRM_MODE_ROTATE_0,
+ supported_rotations);
}
return 0;
@@ -1752,6 +1800,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
ldev->caps.plane_reg_shadow = false;
ldev->caps.crc = false;
ldev->caps.dynamic_zorder = false;
+ ldev->caps.plane_rotation = false;
break;
case HWVER_20101:
ldev->caps.layer_ofs = LAY_OFS_0;
@@ -1768,6 +1817,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
ldev->caps.plane_reg_shadow = false;
ldev->caps.crc = false;
ldev->caps.dynamic_zorder = false;
+ ldev->caps.plane_rotation = false;
break;
case HWVER_40100:
ldev->caps.layer_ofs = LAY_OFS_1;
@@ -1784,6 +1834,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
ldev->caps.plane_reg_shadow = true;
ldev->caps.crc = true;
ldev->caps.dynamic_zorder = true;
+ ldev->caps.plane_rotation = true;
break;
default:
return -ENODEV;
diff --git a/drivers/gpu/drm/stm/ltdc.h b/drivers/gpu/drm/stm/ltdc.h
index 4855898bd4c0..15139980d8ea 100644
--- a/drivers/gpu/drm/stm/ltdc.h
+++ b/drivers/gpu/drm/stm/ltdc.h
@@ -29,6 +29,7 @@ struct ltdc_caps {
bool plane_reg_shadow; /* plane shadow registers ability */
bool crc; /* cyclic redundancy check supported */
bool dynamic_zorder; /* dynamic z-order */
+ bool plane_rotation; /* plane rotation */
};
#define LTDC_MAX_LAYER 4
--
2.25.1
On 6/3/22 15:45, Yannick Fertre wrote:
> Support of vertical & horizontal mirroring features thanks to
> the plane rotation property.
>
> Signed-off-by: Yannick Fertre <[email protected]>
> ---
> drivers/gpu/drm/stm/ltdc.c | 163 ++++++++++++++++++++++++-------------
> drivers/gpu/drm/stm/ltdc.h | 1 +
> 2 files changed, 108 insertions(+), 56 deletions(-)
>
> diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
> index 00a6bc1b1d7c..ff2075dd9474 100644
> --- a/drivers/gpu/drm/stm/ltdc.c
> +++ b/drivers/gpu/drm/stm/ltdc.c
> @@ -180,6 +180,7 @@
> #define LXCR_LEN BIT(0) /* Layer ENable */
> #define LXCR_COLKEN BIT(1) /* Color Keying Enable */
> #define LXCR_CLUTEN BIT(4) /* Color Look-Up Table ENable */
> +#define LXCR_HMEN BIT(8) /* Horizontal Mirroring ENable */
>
> #define LXWHPCR_WHSTPOS GENMASK(11, 0) /* Window Horizontal StarT POSition */
> #define LXWHPCR_WHSPPOS GENMASK(27, 16) /* Window Horizontal StoP POSition */
> @@ -197,7 +198,7 @@
> #define LXBFCR_BOR GENMASK(18, 16) /* Blending ORder */
>
> #define LXCFBLR_CFBLL GENMASK(12, 0) /* Color Frame Buffer Line Length */
> -#define LXCFBLR_CFBP GENMASK(28, 16) /* Color Frame Buffer Pitch in bytes */
> +#define LXCFBLR_CFBP GENMASK(31, 16) /* Color Frame Buffer Pitch in bytes */
>
> #define LXCFBLNR_CFBLN GENMASK(10, 0) /* Color Frame Buffer Line Number */
>
> @@ -1237,7 +1238,8 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
> u32 y0 = newstate->crtc_y;
> u32 y1 = newstate->crtc_y + newstate->crtc_h - 1;
> u32 src_x, src_y, src_w, src_h;
> - u32 val, pitch_in_bytes, line_length, line_number, paddr, ahbp, avbp, bpcr;
> + u32 val, pitch_in_bytes, line_length, line_number, ahbp, avbp, bpcr;
> + u32 paddr, paddr1, paddr2;
> enum ltdc_pix_fmt pf;
>
> if (!newstate->crtc || !fb) {
> @@ -1289,13 +1291,6 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
> }
> regmap_write_bits(ldev->regmap, LTDC_L1PFCR + lofs, LXPFCR_PF, val);
>
> - /* Configures the color frame buffer pitch in bytes & line length */
> - pitch_in_bytes = fb->pitches[0];
> - line_length = fb->format->cpp[0] *
> - (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
> - val = ((pitch_in_bytes << 16) | line_length);
> - regmap_write_bits(ldev->regmap, LTDC_L1CFBLR + lofs, LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
> -
> /* Specifies the constant alpha value */
> val = newstate->alpha >> 8;
> regmap_write_bits(ldev->regmap, LTDC_L1CACR + lofs, LXCACR_CONSTA, val);
> @@ -1319,76 +1314,115 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
> LXBFCR_BF2 | LXBFCR_BF1, val);
> }
>
> - /* Configures the frame buffer line number */
> - line_number = y1 - y0 + 1;
> - regmap_write_bits(ldev->regmap, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, line_number);
> -
> /* Sets the FB address */
> paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 0);
>
> + if (newstate->rotation & DRM_MODE_REFLECT_X)
> + paddr += (fb->format->cpp[0] * (x1 - x0 + 1)) - 1;
> +
> + if (newstate->rotation & DRM_MODE_REFLECT_Y)
> + paddr += (fb->pitches[0] * (y1 - y0));
> +
> DRM_DEBUG_DRIVER("fb: phys 0x%08x", paddr);
> regmap_write(ldev->regmap, LTDC_L1CFBAR + lofs, paddr);
>
> + /* Configures the color frame buffer pitch in bytes & line length */
> + line_length = fb->format->cpp[0] *
> + (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
> +
> + if (newstate->rotation & DRM_MODE_REFLECT_Y)
> + /* Compute negative value (signed on 16 bits) for the picth */
> + pitch_in_bytes = 0x10000 - fb->pitches[0];
> + else
> + pitch_in_bytes = fb->pitches[0];
> +
> + val = (pitch_in_bytes << 16) | line_length;
> + regmap_write_bits(ldev->regmap, LTDC_L1CFBLR + lofs, LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
> +
> + /* Configures the frame buffer line number */
> + line_number = y1 - y0 + 1;
> + regmap_write_bits(ldev->regmap, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, line_number);
> +
> if (ldev->caps.ycbcr_input) {
> if (fb->format->is_yuv) {
> switch (fb->format->format) {
> case DRM_FORMAT_NV12:
> case DRM_FORMAT_NV21:
> - /* Configure the auxiliary frame buffer address 0 & 1 */
> - paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
> - regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
> - regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr + 1);
> + /* Configure the auxiliary frame buffer address 0 */
> + paddr1 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
> +
> + if (newstate->rotation & DRM_MODE_REFLECT_X)
> + paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
>
> - /* Configure the buffer length */
> - val = ((pitch_in_bytes << 16) | line_length);
> - regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
> + if (newstate->rotation & DRM_MODE_REFLECT_Y)
> + paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
>
> - /* Configure the frame buffer line number */
> - val = (line_number >> 1);
> - regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
> + regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
> break;
> case DRM_FORMAT_YUV420:
> - /* Configure the auxiliary frame buffer address 0 */
> - paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
> - regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
> -
> - /* Configure the auxiliary frame buffer address 1 */
> - paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
> - regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr);
> + /* Configure the auxiliary frame buffer address 0 & 1 */
> + paddr1 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
> + paddr2 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
>
> - line_length = ((fb->format->cpp[0] * (x1 - x0 + 1)) >> 1) +
> - (ldev->caps.bus_width >> 3) - 1;
> + if (newstate->rotation & DRM_MODE_REFLECT_X) {
> + paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
> + paddr2 += ((fb->format->cpp[2] * (x1 - x0 + 1)) >> 1) - 1;
> + }
>
> - /* Configure the buffer length */
> - val = (((pitch_in_bytes >> 1) << 16) | line_length);
> - regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
> + if (newstate->rotation & DRM_MODE_REFLECT_Y) {
> + paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
> + paddr2 += (fb->pitches[2] * (y1 - y0 - 1)) >> 1;
> + }
>
> - /* Configure the frame buffer line number */
> - val = (line_number >> 1);
> - regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
> + regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
> + regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr2);
> break;
> case DRM_FORMAT_YVU420:
> - /* Configure the auxiliary frame buffer address 0 */
> - paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
> - regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
> -
> - /* Configure the auxiliary frame buffer address 1 */
> - paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
> - regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr);
> + /* Configure the auxiliary frame buffer address 0 & 1 */
> + paddr1 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
> + paddr2 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
>
> - line_length = ((fb->format->cpp[0] * (x1 - x0 + 1)) >> 1) +
> - (ldev->caps.bus_width >> 3) - 1;
> + if (newstate->rotation & DRM_MODE_REFLECT_X) {
> + paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
> + paddr2 += ((fb->format->cpp[2] * (x1 - x0 + 1)) >> 1) - 1;
> + }
>
> - /* Configure the buffer length */
> - val = (((pitch_in_bytes >> 1) << 16) | line_length);
> - regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
> + if (newstate->rotation & DRM_MODE_REFLECT_Y) {
> + paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
> + paddr2 += (fb->pitches[2] * (y1 - y0 - 1)) >> 1;
> + }
>
> - /* Configure the frame buffer line number */
> - val = (line_number >> 1);
> - regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
> + regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
> + regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr2);
> break;
> }
>
> + /*
> + * Set the length and the number of lines of the auxiliary
> + * buffers if the framebuffer contains more than one plane.
> + */
> + if (fb->format->num_planes > 1) {
> + if (newstate->rotation & DRM_MODE_REFLECT_Y)
> + /*
> + * Compute negative value (signed on 16 bits)
> + * for the picth
> + */
> + pitch_in_bytes = 0x10000 - fb->pitches[1];
> + else
> + pitch_in_bytes = fb->pitches[1];
> +
> + line_length = ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) +
> + (ldev->caps.bus_width >> 3) - 1;
> +
> + /* Configure the auxiliary buffer length */
> + val = (pitch_in_bytes << 16) | line_length;
> + regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
> +
> + /* Configure the auxiliary frame buffer line number */
> + val = line_number >> 1;
> + regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
> + }
> +
> /* Configure YCbC conversion coefficient */
> ltdc_set_ycbcr_coeffs(plane);
>
> @@ -1403,7 +1437,12 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
> /* Enable layer and CLUT if needed */
> val = fb->format->format == DRM_FORMAT_C8 ? LXCR_CLUTEN : 0;
> val |= LXCR_LEN;
> - regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN, val);
> +
> + /* Enable horizontal mirroring if requested */
> + if (newstate->rotation & DRM_MODE_REFLECT_X)
> + val |= LXCR_HMEN;
> +
> + regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN | LXCR_HMEN, val);
>
> /* Commit shadow registers = update plane at next vblank */
> if (ldev->caps.plane_reg_shadow)
> @@ -1432,8 +1471,8 @@ static void ltdc_plane_atomic_disable(struct drm_plane *plane,
> struct ltdc_device *ldev = plane_to_ltdc(plane);
> u32 lofs = plane->index * LAY_OFS;
>
> - /* disable layer */
> - regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN, 0);
> + /* Disable layer */
> + regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN | LXCR_HMEN, 0);
>
> /* Commit shadow registers = update plane at next vblank */
> if (ldev->caps.plane_reg_shadow)
> @@ -1577,6 +1616,7 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
> {
> struct ltdc_device *ldev = ddev->dev_private;
> struct drm_plane *primary, *overlay;
> + int supported_rotations = DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y;
> unsigned int i;
> int ret;
>
> @@ -1591,6 +1631,10 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
> else
> drm_plane_create_zpos_immutable_property(primary, 0);
>
> + if (ldev->caps.plane_rotation)
> + drm_plane_create_rotation_property(primary, DRM_MODE_ROTATE_0,
> + supported_rotations);
> +
> /* Init CRTC according to its hardware features */
> if (ldev->caps.crc)
> ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
> @@ -1622,6 +1666,10 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
> drm_plane_create_zpos_property(overlay, i, 0, ldev->caps.nb_layers - 1);
> else
> drm_plane_create_zpos_immutable_property(overlay, i);
> +
> + if (ldev->caps.plane_rotation)
> + drm_plane_create_rotation_property(overlay, DRM_MODE_ROTATE_0,
> + supported_rotations);
> }
>
> return 0;
> @@ -1752,6 +1800,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
> ldev->caps.plane_reg_shadow = false;
> ldev->caps.crc = false;
> ldev->caps.dynamic_zorder = false;
> + ldev->caps.plane_rotation = false;
> break;
> case HWVER_20101:
> ldev->caps.layer_ofs = LAY_OFS_0;
> @@ -1768,6 +1817,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
> ldev->caps.plane_reg_shadow = false;
> ldev->caps.crc = false;
> ldev->caps.dynamic_zorder = false;
> + ldev->caps.plane_rotation = false;
> break;
> case HWVER_40100:
> ldev->caps.layer_ofs = LAY_OFS_1;
> @@ -1784,6 +1834,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
> ldev->caps.plane_reg_shadow = true;
> ldev->caps.crc = true;
> ldev->caps.dynamic_zorder = true;
> + ldev->caps.plane_rotation = true;
> break;
> default:
> return -ENODEV;
> diff --git a/drivers/gpu/drm/stm/ltdc.h b/drivers/gpu/drm/stm/ltdc.h
> index 4855898bd4c0..15139980d8ea 100644
> --- a/drivers/gpu/drm/stm/ltdc.h
> +++ b/drivers/gpu/drm/stm/ltdc.h
> @@ -29,6 +29,7 @@ struct ltdc_caps {
> bool plane_reg_shadow; /* plane shadow registers ability */
> bool crc; /* cyclic redundancy check supported */
> bool dynamic_zorder; /* dynamic z-order */
> + bool plane_rotation; /* plane rotation */
> };
>
> #define LTDC_MAX_LAYER 4
Dear Yannick,
Many thanks for your patch,
Applied on drm-misc-next.
Have a good day
Philippe :-)