Commit 94dfec48 authored by Sebastian Reichel's avatar Sebastian Reichel Committed by Philipp Zabel

drm/imx: Add 8 pixel alignment fix

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: default avatarBoris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: default avatarSebastian Reichel <sebastian.reichel@collabora.com>
Link: https://lore.kernel.org/r/20210428222953.235280-3-sebastian.reichel@collabora.comSigned-off-by: default avatarPhilipp Zabel <p.zabel@pengutronix.de>
parent f4b34faa
...@@ -147,9 +147,26 @@ static const struct drm_ioctl_desc imx_drm_ioctls[] = { ...@@ -147,9 +147,26 @@ static const struct drm_ioctl_desc imx_drm_ioctls[] = {
/* none so far */ /* 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 = { static const struct drm_driver imx_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, .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, .ioctls = imx_drm_ioctls,
.num_ioctls = ARRAY_SIZE(imx_drm_ioctls), .num_ioctls = ARRAY_SIZE(imx_drm_ioctls),
.fops = &imx_drm_driver_fops, .fops = &imx_drm_driver_fops,
......
...@@ -274,6 +274,11 @@ imx_ldb_encoder_atomic_mode_set(struct drm_encoder *encoder, ...@@ -274,6 +274,11 @@ imx_ldb_encoder_atomic_mode_set(struct drm_encoder *encoder,
"%s: mode exceeds 85 MHz pixel clock\n", __func__); "%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) { if (dual) {
serial_clk = 3500UL * mode->clock; serial_clk = 3500UL * mode->clock;
imx_ldb_set_clock(ldb, mux, 0, serial_clk, di_clk); imx_ldb_set_clock(ldb, mux, 0, serial_clk, di_clk);
......
...@@ -305,10 +305,19 @@ static void ipu_crtc_mode_set_nofb(struct drm_crtc *crtc) ...@@ -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; sig_cfg.vsync_pin = imx_crtc_state->di_vsync_pin;
drm_display_mode_to_videomode(mode, &sig_cfg.mode); 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, ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di,
mode->flags & DRM_MODE_FLAG_INTERLACE, 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); ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg);
} }
......
...@@ -30,6 +30,11 @@ to_ipu_plane_state(struct drm_plane_state *p) ...@@ -30,6 +30,11 @@ to_ipu_plane_state(struct drm_plane_state *p)
return container_of(p, struct ipu_plane_state, base); 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) static inline struct ipu_plane *to_ipu_plane(struct drm_plane *p)
{ {
return container_of(p, struct ipu_plane, base); return container_of(p, struct ipu_plane, base);
...@@ -440,6 +445,12 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, ...@@ -440,6 +445,12 @@ static int ipu_plane_atomic_check(struct drm_plane *plane,
if (old_fb && fb->pitches[0] != old_fb->pitches[0]) if (old_fb && fb->pitches[0] != old_fb->pitches[0])
crtc_state->mode_changed = true; 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) { switch (fb->format->format) {
case DRM_FORMAT_YUV420: case DRM_FORMAT_YUV420:
case DRM_FORMAT_YVU420: case DRM_FORMAT_YVU420:
...@@ -615,7 +626,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, ...@@ -615,7 +626,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
if (ipu_state->use_pre) { if (ipu_state->use_pre) {
axi_id = ipu_chan_assign_axi_id(ipu_plane->dma); axi_id = ipu_chan_assign_axi_id(ipu_plane->dma);
ipu_prg_channel_configure(ipu_plane->ipu_ch, axi_id, 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, drm_rect_height(&new_state->src) >> 16,
fb->pitches[0], fb->format->format, fb->pitches[0], fb->format->format,
fb->modifier, &eba); fb->modifier, &eba);
...@@ -648,9 +659,9 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, ...@@ -648,9 +659,9 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
break; 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; height = drm_rect_height(&new_state->src) >> 16;
info = drm_format_info(fb->format->format); info = drm_format_info(fb->format->format);
ipu_calculate_bursts(width, info->cpp[0], fb->pitches[0], ipu_calculate_bursts(width, info->cpp[0], fb->pitches[0],
...@@ -715,7 +726,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, ...@@ -715,7 +726,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
ipu_cpmem_zero(ipu_plane->alpha_ch); ipu_cpmem_zero(ipu_plane->alpha_ch);
ipu_cpmem_set_resolution(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); drm_rect_height(&new_state->src) >> 16);
ipu_cpmem_set_format_passthrough(ipu_plane->alpha_ch, 8); ipu_cpmem_set_format_passthrough(ipu_plane->alpha_ch, 8);
ipu_cpmem_set_high_priority(ipu_plane->alpha_ch); ipu_cpmem_set_high_priority(ipu_plane->alpha_ch);
......
...@@ -167,6 +167,11 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, ...@@ -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); 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); map = ipu_bus_format_to_map(bus_format);
/* /*
......
...@@ -506,6 +506,13 @@ int ipu_di_adjust_videomode(struct ipu_di *di, struct videomode *mode) ...@@ -506,6 +506,13 @@ int ipu_di_adjust_videomode(struct ipu_di *di, struct videomode *mode)
{ {
u32 diff; 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) if (mode->vfront_porch >= 2)
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment