drm/stm: ltdc: add support of horizontal & vertical mirroring
authorYannick Fertre <yannick.fertre@foss.st.com>
Fri, 3 Jun 2022 13:45:47 +0000 (15:45 +0200)
committerPhilippe Cornu <philippe.cornu@foss.st.com>
Mon, 27 Jun 2022 14:01:40 +0000 (16:01 +0200)
Support of vertical & horizontal mirroring features thanks to
the plane rotation property.

Signed-off-by: Yannick Fertre <yannick.fertre@foss.st.com>
Signed-off-by: Philippe Cornu <philippe.cornu@foss.st.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20220603134547.593790-1-yannick.fertre@foss.st.com
drivers/gpu/drm/stm/ltdc.c
drivers/gpu/drm/stm/ltdc.h

index 359414c1062105b74d2d39697ecf7950e6ffc77a..ba598225e16e85687a8a8d1839a4ed4a6a516111 100644 (file)
 #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 */
 #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 */
 
@@ -1240,7 +1241,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) {
@@ -1292,13 +1294,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);
@@ -1322,76 +1317,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);
 
@@ -1406,7 +1440,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)
@@ -1435,8 +1474,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)
@@ -1580,6 +1619,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;
 
@@ -1594,6 +1634,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,
@@ -1625,6 +1669,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;
@@ -1755,6 +1803,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;
@@ -1771,6 +1820,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;
@@ -1787,6 +1837,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;
index 4855898bd4c003819e562d8fa211c3b2049e96e7..15139980d8ea7d665446399136850c334e4f5ed2 100644 (file)
@@ -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