]> git.baikalelectronics.ru Git - kernel.git/commitdiff
drm/imx: Add 8 pixel alignment fix
authorSebastian Reichel <sebastian.reichel@collabora.com>
Wed, 28 Apr 2021 22:29:50 +0000 (00:29 +0200)
committerPhilipp Zabel <p.zabel@pengutronix.de>
Mon, 10 May 2021 14:39:54 +0000 (16:39 +0200)
Some standard resolutions like 1366x768 do not work properly with
i.MX6 SoCs, since the horizontal resolution needs to be aligned
to 8 pixels (so 1360x768 or 1368x768 would work).

This patch allocates framebuffers allocated to 8 pixels. The extra
time required to send the extra pixels are removed from the blank
time. In order to expose the correct display size to userspace,
the stride is increased without increasing the width.

Without this patch systems with this display resolution hang
indefinitely during boot up.

Suggested-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://lore.kernel.org/r/20210428222953.235280-3-sebastian.reichel@collabora.com
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
drivers/gpu/drm/imx/imx-drm-core.c
drivers/gpu/drm/imx/imx-ldb.c
drivers/gpu/drm/imx/ipuv3-crtc.c
drivers/gpu/drm/imx/ipuv3-plane.c
drivers/gpu/ipu-v3/ipu-dc.c
drivers/gpu/ipu-v3/ipu-di.c

index be6001d8c9849f6425487da4ec1f425526389c7a..3709d343784a598cd93aa7497c26ce3688167d37 100644 (file)
@@ -147,9 +147,26 @@ static const struct drm_ioctl_desc imx_drm_ioctls[] = {
        /* none so far */
 };
 
+static int imx_drm_dumb_create(struct drm_file *file_priv,
+                              struct drm_device *drm,
+                              struct drm_mode_create_dumb *args)
+{
+       u32 width = args->width;
+       int ret;
+
+       args->width = ALIGN(width, 8);
+
+       ret = drm_gem_cma_dumb_create(file_priv, drm, args);
+       if (ret)
+               return ret;
+
+       args->width = width;
+       return ret;
+}
+
 static const struct drm_driver imx_drm_driver = {
        .driver_features        = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
-       DRM_GEM_CMA_DRIVER_OPS,
+       DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(imx_drm_dumb_create),
        .ioctls                 = imx_drm_ioctls,
        .num_ioctls             = ARRAY_SIZE(imx_drm_ioctls),
        .fops                   = &imx_drm_driver_fops,
index ffdc492c5bc51ea2d5727bd701ef18e3c34df4f0..53132ddf9587b6381e42b33ed60ab5d759c676e4 100644 (file)
@@ -274,6 +274,11 @@ imx_ldb_encoder_atomic_mode_set(struct drm_encoder *encoder,
                         "%s: mode exceeds 85 MHz pixel clock\n", __func__);
        }
 
+       if (!IS_ALIGNED(mode->hdisplay, 8)) {
+               dev_warn(ldb->dev,
+                        "%s: hdisplay does not align to 8 byte\n", __func__);
+       }
+
        if (dual) {
                serial_clk = 3500UL * mode->clock;
                imx_ldb_set_clock(ldb, mux, 0, serial_clk, di_clk);
index e6431a227feb17f7cce698593afa7fd2f9ca908e..9c8829f945b23a293dde34854b4de08f0ed47400 100644 (file)
@@ -305,10 +305,19 @@ static void ipu_crtc_mode_set_nofb(struct drm_crtc *crtc)
        sig_cfg.vsync_pin = imx_crtc_state->di_vsync_pin;
 
        drm_display_mode_to_videomode(mode, &sig_cfg.mode);
+       if (!IS_ALIGNED(sig_cfg.mode.hactive, 8)) {
+               unsigned int new_hactive = ALIGN(sig_cfg.mode.hactive, 8);
+
+               dev_warn(ipu_crtc->dev, "8-pixel align hactive %d -> %d\n",
+                        sig_cfg.mode.hactive, new_hactive);
+
+               sig_cfg.mode.hfront_porch = new_hactive - sig_cfg.mode.hactive;
+               sig_cfg.mode.hactive = new_hactive;
+       }
 
        ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di,
                         mode->flags & DRM_MODE_FLAG_INTERLACE,
-                        imx_crtc_state->bus_format, mode->hdisplay);
+                        imx_crtc_state->bus_format, sig_cfg.mode.hactive);
        ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg);
 }
 
index fc8f4834ed7bcfb02faa9771580293994ae562be..9a7150ac34a6a74fd512ae8643c1a257ec705300 100644 (file)
@@ -30,6 +30,11 @@ to_ipu_plane_state(struct drm_plane_state *p)
        return container_of(p, struct ipu_plane_state, base);
 }
 
+static unsigned int ipu_src_rect_width(const struct drm_plane_state *state)
+{
+       return ALIGN(drm_rect_width(&state->src) >> 16, 8);
+}
+
 static inline struct ipu_plane *to_ipu_plane(struct drm_plane *p)
 {
        return container_of(p, struct ipu_plane, base);
@@ -440,6 +445,12 @@ static int ipu_plane_atomic_check(struct drm_plane *plane,
        if (old_fb && fb->pitches[0] != old_fb->pitches[0])
                crtc_state->mode_changed = true;
 
+       if (ALIGN(fb->width, 8) * fb->format->cpp[0] >
+           fb->pitches[0] + fb->offsets[0]) {
+               dev_warn(dev, "pitch is not big enough for 8 pixels alignment");
+               return -EINVAL;
+       }
+
        switch (fb->format->format) {
        case DRM_FORMAT_YUV420:
        case DRM_FORMAT_YVU420:
@@ -615,7 +626,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
        if (ipu_state->use_pre) {
                axi_id = ipu_chan_assign_axi_id(ipu_plane->dma);
                ipu_prg_channel_configure(ipu_plane->ipu_ch, axi_id,
-                                         drm_rect_width(&new_state->src) >> 16,
+                                         ipu_src_rect_width(new_state),
                                          drm_rect_height(&new_state->src) >> 16,
                                          fb->pitches[0], fb->format->format,
                                          fb->modifier, &eba);
@@ -648,9 +659,9 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
                break;
        }
 
-       ipu_dmfc_config_wait4eot(ipu_plane->dmfc, drm_rect_width(dst));
+       ipu_dmfc_config_wait4eot(ipu_plane->dmfc, ALIGN(drm_rect_width(dst), 8));
 
-       width = drm_rect_width(&new_state->src) >> 16;
+       width = ipu_src_rect_width(new_state);
        height = drm_rect_height(&new_state->src) >> 16;
        info = drm_format_info(fb->format->format);
        ipu_calculate_bursts(width, info->cpp[0], fb->pitches[0],
@@ -715,7 +726,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
 
                ipu_cpmem_zero(ipu_plane->alpha_ch);
                ipu_cpmem_set_resolution(ipu_plane->alpha_ch,
-                                        drm_rect_width(&new_state->src) >> 16,
+                                        ipu_src_rect_width(new_state),
                                         drm_rect_height(&new_state->src) >> 16);
                ipu_cpmem_set_format_passthrough(ipu_plane->alpha_ch, 8);
                ipu_cpmem_set_high_priority(ipu_plane->alpha_ch);
index 34b4075a6a8e531701a0fd43e708b3caec419106..ca96b235491a9c1bec4382468d7b8e33fe82eb4c 100644 (file)
@@ -167,6 +167,11 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
 
        dc->di = ipu_di_get_num(di);
 
+       if (!IS_ALIGNED(width, 8)) {
+               dev_warn(priv->dev,
+                        "%s: hactive does not align to 8 byte\n", __func__);
+       }
+
        map = ipu_bus_format_to_map(bus_format);
 
        /*
index e617f60afeea3d3e7ad36058feb2eb525cb14bb9..666223c6bec4d52da44b38d3a0263f19bae2f4a6 100644 (file)
@@ -506,6 +506,13 @@ int ipu_di_adjust_videomode(struct ipu_di *di, struct videomode *mode)
 {
        u32 diff;
 
+       if (!IS_ALIGNED(mode->hactive, 8) &&
+           mode->hfront_porch < ALIGN(mode->hactive, 8) - mode->hactive) {
+               dev_err(di->ipu->dev, "hactive %d is not aligned to 8 and front porch is too small to compensate\n",
+                       mode->hactive);
+               return -EINVAL;
+       }
+
        if (mode->vfront_porch >= 2)
                return 0;