diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index 3f2b4afcb8a7..1d053bbefc02 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -257,6 +257,7 @@ static int imx_drm_bind(struct device *dev) drm->mode_config.max_height = 4096; drm->mode_config.funcs = &imx_drm_mode_config_funcs; drm->mode_config.helper_private = &imx_drm_mode_config_helpers; + drm->mode_config.allow_fb_modifiers = true; drm_mode_config_init(drm); diff --git a/drivers/gpu/drm/imx/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3-plane.c index 5a67daedcf4d..57ed56d8623f 100644 --- a/drivers/gpu/drm/imx/ipuv3-plane.c +++ b/drivers/gpu/drm/imx/ipuv3-plane.c @@ -77,6 +77,18 @@ static const uint32_t ipu_plane_formats[] = { DRM_FORMAT_BGRX8888_A8, }; +static const uint64_t ipu_format_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static const uint64_t pre_format_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_VIVANTE_TILED, + DRM_FORMAT_MOD_VIVANTE_SUPER_TILED, + DRM_FORMAT_MOD_INVALID +}; + int ipu_plane_irq(struct ipu_plane *ipu_plane) { return ipu_idmac_channel_irq(ipu_plane->ipu, ipu_plane->ipu_ch, @@ -303,6 +315,22 @@ void ipu_plane_destroy_state(struct drm_plane *plane, kfree(ipu_state); } +static bool ipu_plane_format_mod_supported(struct drm_plane *plane, + uint32_t format, uint64_t modifier) +{ + struct ipu_soc *ipu = to_ipu_plane(plane)->ipu; + + /* linear is supported for all planes and formats */ + if (modifier == DRM_FORMAT_MOD_LINEAR) + return true; + + /* without a PRG there are no supported modifiers */ + if (!ipu_prg_present(ipu)) + return false; + + return ipu_prg_format_supported(ipu, format, modifier); +} + static const struct drm_plane_funcs ipu_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, @@ -310,6 +338,7 @@ static const struct drm_plane_funcs ipu_plane_funcs = { .reset = ipu_plane_state_reset, .atomic_duplicate_state = ipu_plane_duplicate_state, .atomic_destroy_state = ipu_plane_destroy_state, + .format_mod_supported = ipu_plane_format_mod_supported, }; static int ipu_plane_atomic_check(struct drm_plane *plane, @@ -550,8 +579,8 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, ipu_prg_channel_configure(ipu_plane->ipu_ch, axi_id, drm_rect_width(&state->src) >> 16, drm_rect_height(&state->src) >> 16, - fb->pitches[0], - fb->format->format, &eba); + fb->pitches[0], fb->format->format, + fb->modifier, &eba); } if (old_state->fb && !drm_atomic_crtc_needs_modeset(crtc_state)) { @@ -700,18 +729,71 @@ static const struct drm_plane_helper_funcs ipu_plane_helper_funcs = { int ipu_planes_assign_pre(struct drm_device *dev, struct drm_atomic_state *state) { + struct drm_crtc_state *old_crtc_state, *crtc_state; struct drm_plane_state *plane_state; + struct ipu_plane_state *ipu_state; + struct ipu_plane *ipu_plane; struct drm_plane *plane; + struct drm_crtc *crtc; int available_pres = ipu_prg_max_active_channels(); - int i; + int ret, i; + + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, crtc_state, i) { + ret = drm_atomic_add_affected_planes(state, crtc); + if (ret) + return ret; + } + + /* + * We are going over the planes in 2 passes: first we assign PREs to + * planes with a tiling modifier, which need the PREs to resolve into + * linear. Any failure to assign a PRE there is fatal. In the second + * pass we try to assign PREs to linear FBs, to improve memory access + * patterns for them. Failure at this point is non-fatal, as we can + * scan out linear FBs without a PRE. + */ + for_each_new_plane_in_state(state, plane, plane_state, i) { + ipu_state = to_ipu_plane_state(plane_state); + ipu_plane = to_ipu_plane(plane); + + if (!plane_state->fb) { + ipu_state->use_pre = false; + continue; + } + + if (!(plane_state->fb->flags & DRM_MODE_FB_MODIFIERS) || + plane_state->fb->modifier == DRM_FORMAT_MOD_LINEAR) + continue; + + if (!ipu_prg_present(ipu_plane->ipu) || !available_pres) + return -EINVAL; + + if (!ipu_prg_format_supported(ipu_plane->ipu, + plane_state->fb->format->format, + plane_state->fb->modifier)) + return -EINVAL; + + ipu_state->use_pre = true; + available_pres--; + } for_each_new_plane_in_state(state, plane, plane_state, i) { - struct ipu_plane_state *ipu_state = - to_ipu_plane_state(plane_state); - struct ipu_plane *ipu_plane = to_ipu_plane(plane); + ipu_state = to_ipu_plane_state(plane_state); + ipu_plane = to_ipu_plane(plane); + + if (!plane_state->fb) { + ipu_state->use_pre = false; + continue; + } + + if ((plane_state->fb->flags & DRM_MODE_FB_MODIFIERS) && + plane_state->fb->modifier != DRM_FORMAT_MOD_LINEAR) + continue; + + /* make sure that modifier is initialized */ + plane_state->fb->modifier = DRM_FORMAT_MOD_LINEAR; if (ipu_prg_present(ipu_plane->ipu) && available_pres && - plane_state->fb && ipu_prg_format_supported(ipu_plane->ipu, plane_state->fb->format->format, plane_state->fb->modifier)) { @@ -731,6 +813,7 @@ struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu, enum drm_plane_type type) { struct ipu_plane *ipu_plane; + const uint64_t *modifiers = ipu_format_modifiers; int ret; DRM_DEBUG_KMS("channel %d, dp flow %d, possible_crtcs=0x%x\n", @@ -746,10 +829,13 @@ struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu, ipu_plane->dma = dma; ipu_plane->dp_flow = dp; + if (ipu_prg_present(ipu)) + modifiers = pre_format_modifiers; + ret = drm_universal_plane_init(dev, &ipu_plane->base, possible_crtcs, &ipu_plane_funcs, ipu_plane_formats, ARRAY_SIZE(ipu_plane_formats), - NULL, type, NULL); + modifiers, type, NULL); if (ret) { DRM_ERROR("failed to initialize plane\n"); kfree(ipu_plane); diff --git a/drivers/gpu/ipu-v3/Kconfig b/drivers/gpu/ipu-v3/Kconfig index 87a20b3dcf7a..fe6f8c5b4445 100644 --- a/drivers/gpu/ipu-v3/Kconfig +++ b/drivers/gpu/ipu-v3/Kconfig @@ -1,7 +1,9 @@ config IMX_IPUV3_CORE tristate "IPUv3 core support" - depends on SOC_IMX5 || SOC_IMX6Q || ARCH_MULTIPLATFORM + depends on SOC_IMX5 || SOC_IMX6Q || ARCH_MULTIPLATFORM || COMPILE_TEST depends on DRM || !DRM # if DRM=m, this can't be 'y' + select BITREVERSE + select GENERIC_ALLOCATOR if DRM select GENERIC_IRQ_CHIP help Choose this if you have a i.MX5/6 system and want to use the Image diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index 1cb82f445f91..bb9c087e6c0d 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "ipu-prv.h" diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 321eb983c2f5..67cc820253a9 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "ipu-prv.h" /* IC Register Offsets */ diff --git a/drivers/gpu/ipu-v3/ipu-pre.c b/drivers/gpu/ipu-v3/ipu-pre.c index c860a7997cb5..f1cec3d70498 100644 --- a/drivers/gpu/ipu-v3/ipu-pre.c +++ b/drivers/gpu/ipu-v3/ipu-pre.c @@ -49,6 +49,10 @@ #define IPU_PRE_TPR_CTRL 0x070 #define IPU_PRE_TPR_CTRL_TILE_FORMAT(v) ((v & 0xff) << 0) #define IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK 0xff +#define IPU_PRE_TPR_CTRL_TILE_FORMAT_16_BIT (1 << 0) +#define IPU_PRE_TPR_CTRL_TILE_FORMAT_SPLIT_BUF (1 << 4) +#define IPU_PRE_TPR_CTRL_TILE_FORMAT_SINGLE_BUF (1 << 5) +#define IPU_PRE_TPR_CTRL_TILE_FORMAT_SUPER_TILED (1 << 6) #define IPU_PRE_PREFETCH_ENG_CTRL 0x080 #define IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN (1 << 0) @@ -147,7 +151,7 @@ int ipu_pre_get(struct ipu_pre *pre) val = IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN | IPU_PRE_CTRL_HANDSHAKE_EN | IPU_PRE_CTRL_TPR_REST_SEL | - IPU_PRE_CTRL_BLOCK_16 | IPU_PRE_CTRL_SDW_UPDATE; + IPU_PRE_CTRL_SDW_UPDATE; writel(val, pre->regs + IPU_PRE_CTRL); pre->in_use = true; @@ -163,14 +167,17 @@ void ipu_pre_put(struct ipu_pre *pre) void ipu_pre_configure(struct ipu_pre *pre, unsigned int width, unsigned int height, unsigned int stride, u32 format, - unsigned int bufaddr) + uint64_t modifier, unsigned int bufaddr) { const struct drm_format_info *info = drm_format_info(format); u32 active_bpp = info->cpp[0] >> 1; u32 val; /* calculate safe window for ctrl register updates */ - pre->safe_window_end = height - 2; + if (modifier == DRM_FORMAT_MOD_LINEAR) + pre->safe_window_end = height - 2; + else + pre->safe_window_end = DIV_ROUND_UP(height, 4) - 1; writel(bufaddr, pre->regs + IPU_PRE_CUR_BUF); writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF); @@ -203,9 +210,25 @@ void ipu_pre_configure(struct ipu_pre *pre, unsigned int width, writel(pre->buffer_paddr, pre->regs + IPU_PRE_STORE_ENG_ADDR); + val = readl(pre->regs + IPU_PRE_TPR_CTRL); + val &= ~IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK; + if (modifier != DRM_FORMAT_MOD_LINEAR) { + /* only support single buffer formats for now */ + val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_SINGLE_BUF; + if (modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED) + val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_SUPER_TILED; + if (info->cpp[0] == 2) + val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_16_BIT; + } + writel(val, pre->regs + IPU_PRE_TPR_CTRL); + val = readl(pre->regs + IPU_PRE_CTRL); val |= IPU_PRE_CTRL_EN_REPEAT | IPU_PRE_CTRL_ENABLE | IPU_PRE_CTRL_SDW_UPDATE; + if (modifier == DRM_FORMAT_MOD_LINEAR) + val &= ~IPU_PRE_CTRL_BLOCK_EN; + else + val |= IPU_PRE_CTRL_BLOCK_EN; writel(val, pre->regs + IPU_PRE_CTRL); } diff --git a/drivers/gpu/ipu-v3/ipu-prg.c b/drivers/gpu/ipu-v3/ipu-prg.c index 0013ca9f72c8..067365c733c6 100644 --- a/drivers/gpu/ipu-v3/ipu-prg.c +++ b/drivers/gpu/ipu-v3/ipu-prg.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include