diff options
-rw-r--r-- | drivers/gpu/drm/imx/imx-drm-core.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/ipuv3-plane.c | 102 | ||||
-rw-r--r-- | drivers/gpu/ipu-v3/Kconfig | 4 | ||||
-rw-r--r-- | drivers/gpu/ipu-v3/ipu-cpmem.c | 1 | ||||
-rw-r--r-- | drivers/gpu/ipu-v3/ipu-ic.c | 1 | ||||
-rw-r--r-- | drivers/gpu/ipu-v3/ipu-pre.c | 29 | ||||
-rw-r--r-- | drivers/gpu/ipu-v3/ipu-prg.c | 84 | ||||
-rw-r--r-- | drivers/gpu/ipu-v3/ipu-prv.h | 4 | ||||
-rw-r--r-- | include/video/imx-ipu-v3.h | 2 |
9 files changed, 189 insertions, 39 deletions
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) { - 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; + + 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) { + 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 <linux/types.h> #include <linux/bitrev.h> #include <linux/io.h> +#include <linux/sizes.h> #include <drm/drm_fourcc.h> #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 <linux/bitrev.h> #include <linux/io.h> #include <linux/err.h> +#include <linux/sizes.h> #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 <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <video/imx-ipu-v3.h> @@ -132,28 +133,25 @@ bool ipu_prg_format_supported(struct ipu_soc *ipu, uint32_t format, if (info->num_planes != 1) return false; - return true; + switch (modifier) { + case DRM_FORMAT_MOD_LINEAR: + case DRM_FORMAT_MOD_VIVANTE_TILED: + case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED: + return true; + default: + return false; + } } EXPORT_SYMBOL_GPL(ipu_prg_format_supported); int ipu_prg_enable(struct ipu_soc *ipu) { struct ipu_prg *prg = ipu->prg_priv; - int ret; if (!prg) return 0; - ret = clk_prepare_enable(prg->clk_axi); - if (ret) - goto fail_disable_ipg; - - return 0; - -fail_disable_ipg: - clk_disable_unprepare(prg->clk_ipg); - - return ret; + return pm_runtime_get_sync(prg->dev); } EXPORT_SYMBOL_GPL(ipu_prg_enable); @@ -164,7 +162,7 @@ void ipu_prg_disable(struct ipu_soc *ipu) if (!prg) return; - clk_disable_unprepare(prg->clk_axi); + pm_runtime_put(prg->dev); } EXPORT_SYMBOL_GPL(ipu_prg_disable); @@ -255,7 +253,7 @@ void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan) if (!chan->enabled || prg_chan < 0) return; - clk_prepare_enable(prg->clk_ipg); + pm_runtime_get_sync(prg->dev); val = readl(prg->regs + IPU_PRG_CTL); val |= IPU_PRG_CTL_BYPASS(prg_chan); @@ -264,7 +262,7 @@ void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan) val = IPU_PRG_REG_UPDATE_REG_UPDATE; writel(val, prg->regs + IPU_PRG_REG_UPDATE); - clk_disable_unprepare(prg->clk_ipg); + pm_runtime_put(prg->dev); ipu_prg_put_pre(prg, prg_chan); @@ -275,7 +273,7 @@ EXPORT_SYMBOL_GPL(ipu_prg_channel_disable); int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan, unsigned int axi_id, unsigned int width, unsigned int height, unsigned int stride, - u32 format, unsigned long *eba) + u32 format, uint64_t modifier, unsigned long *eba) { int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); struct ipu_prg *prg = ipu_chan->ipu->prg_priv; @@ -296,14 +294,10 @@ int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan, return ret; ipu_pre_configure(prg->pres[chan->used_pre], - width, height, stride, format, *eba); + width, height, stride, format, modifier, *eba); - ret = clk_prepare_enable(prg->clk_ipg); - if (ret) { - ipu_prg_put_pre(prg, prg_chan); - return ret; - } + pm_runtime_get_sync(prg->dev); val = (stride - 1) & IPU_PRG_STRIDE_STRIDE_MASK; writel(val, prg->regs + IPU_PRG_STRIDE(prg_chan)); @@ -336,7 +330,7 @@ int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan, (val & IPU_PRG_STATUS_BUFFER1_READY(prg_chan)), 5, 1000); - clk_disable_unprepare(prg->clk_ipg); + pm_runtime_put(prg->dev); chan->enabled = true; return 0; @@ -384,6 +378,12 @@ static int ipu_prg_probe(struct platform_device *pdev) if (ret) return ret; + ret = clk_prepare_enable(prg->clk_axi); + if (ret) { + clk_disable_unprepare(prg->clk_ipg); + return ret; + } + /* init to free running mode */ val = readl(prg->regs + IPU_PRG_CTL); val |= IPU_PRG_CTL_SHADOW_EN; @@ -392,7 +392,8 @@ static int ipu_prg_probe(struct platform_device *pdev) /* disable address threshold */ writel(0xffffffff, prg->regs + IPU_PRG_THD); - clk_disable_unprepare(prg->clk_ipg); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); prg->dev = dev; platform_set_drvdata(pdev, prg); @@ -414,6 +415,40 @@ static int ipu_prg_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int prg_suspend(struct device *dev) +{ + struct ipu_prg *prg = dev_get_drvdata(dev); + + clk_disable_unprepare(prg->clk_axi); + clk_disable_unprepare(prg->clk_ipg); + + return 0; +} + +static int prg_resume(struct device *dev) +{ + struct ipu_prg *prg = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(prg->clk_ipg); + if (ret) + return ret; + + ret = clk_prepare_enable(prg->clk_axi); + if (ret) { + clk_disable_unprepare(prg->clk_ipg); + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops prg_pm_ops = { + SET_RUNTIME_PM_OPS(prg_suspend, prg_resume, NULL) +}; + static const struct of_device_id ipu_prg_dt_ids[] = { { .compatible = "fsl,imx6qp-prg", }, { /* sentinel */ }, @@ -424,6 +459,7 @@ struct platform_driver ipu_prg_drv = { .remove = ipu_prg_remove, .driver = { .name = "imx-ipu-prg", + .pm = &prg_pm_ops, .of_match_table = ipu_prg_dt_ids, }, }; diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h index ac4b8d658500..d6beee99b6b8 100644 --- a/drivers/gpu/ipu-v3/ipu-prv.h +++ b/drivers/gpu/ipu-v3/ipu-prv.h @@ -269,8 +269,8 @@ int ipu_pre_get(struct ipu_pre *pre); void ipu_pre_put(struct ipu_pre *pre); u32 ipu_pre_get_baddr(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); + unsigned int height, unsigned int stride, u32 format, + uint64_t modifier, unsigned int bufaddr); void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr); struct ipu_prg *ipu_prg_lookup_by_phandle(struct device *dev, const char *name, diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index ce4c07688b13..abbad94e14a1 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -344,7 +344,7 @@ void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan); int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan, unsigned int axi_id, unsigned int width, unsigned int height, unsigned int stride, - u32 format, unsigned long *eba); + u32 format, uint64_t modifier, unsigned long *eba); /* * IPU CMOS Sensor Interface (csi) functions |