diff options
Diffstat (limited to 'target/linux/layerscape/patches-5.4/805-display-0049-Revert-gpu-Move-ipu-v3-to-imx-folder.patch')
-rw-r--r-- | target/linux/layerscape/patches-5.4/805-display-0049-Revert-gpu-Move-ipu-v3-to-imx-folder.patch | 19991 |
1 files changed, 0 insertions, 19991 deletions
diff --git a/target/linux/layerscape/patches-5.4/805-display-0049-Revert-gpu-Move-ipu-v3-to-imx-folder.patch b/target/linux/layerscape/patches-5.4/805-display-0049-Revert-gpu-Move-ipu-v3-to-imx-folder.patch deleted file mode 100644 index 1c1886b4f9..0000000000 --- a/target/linux/layerscape/patches-5.4/805-display-0049-Revert-gpu-Move-ipu-v3-to-imx-folder.patch +++ /dev/null @@ -1,19991 +0,0 @@ -From 9a941f832d8b88fb29b40e4b3f9c2b42a8a9b000 Mon Sep 17 00:00:00 2001 -From: Yangbo Lu <yangbo.lu@nxp.com> -Date: Mon, 2 Mar 2020 13:46:13 +0800 -Subject: [PATCH] Revert "gpu: Move ipu-v3 to imx folder" - -This reverts commit 9da4beb1c9ce90f97c8d3b5c6254da576746d2cd. ---- - drivers/gpu/Makefile | 2 +- - drivers/gpu/imx/Kconfig | 1 - - drivers/gpu/imx/Makefile | 1 - - drivers/gpu/imx/ipu-v3/Kconfig | 11 - - drivers/gpu/imx/ipu-v3/Makefile | 10 - - drivers/gpu/imx/ipu-v3/ipu-common.c | 1565 ------------------ - drivers/gpu/imx/ipu-v3/ipu-cpmem.c | 976 ----------- - drivers/gpu/imx/ipu-v3/ipu-csi.c | 821 --------- - drivers/gpu/imx/ipu-v3/ipu-dc.c | 420 ----- - drivers/gpu/imx/ipu-v3/ipu-di.c | 745 --------- - drivers/gpu/imx/ipu-v3/ipu-dmfc.c | 214 --- - drivers/gpu/imx/ipu-v3/ipu-dp.c | 357 ---- - drivers/gpu/imx/ipu-v3/ipu-ic.c | 761 --------- - drivers/gpu/imx/ipu-v3/ipu-image-convert.c | 2475 ---------------------------- - drivers/gpu/imx/ipu-v3/ipu-pre.c | 346 ---- - drivers/gpu/imx/ipu-v3/ipu-prg.c | 483 ------ - drivers/gpu/imx/ipu-v3/ipu-prv.h | 274 --- - drivers/gpu/imx/ipu-v3/ipu-smfc.c | 202 --- - drivers/gpu/imx/ipu-v3/ipu-vdi.c | 234 --- - drivers/gpu/ipu-v3/Kconfig | 11 + - drivers/gpu/ipu-v3/Makefile | 10 + - drivers/gpu/ipu-v3/ipu-common.c | 1565 ++++++++++++++++++ - drivers/gpu/ipu-v3/ipu-cpmem.c | 976 +++++++++++ - drivers/gpu/ipu-v3/ipu-csi.c | 821 +++++++++ - drivers/gpu/ipu-v3/ipu-dc.c | 420 +++++ - drivers/gpu/ipu-v3/ipu-di.c | 745 +++++++++ - drivers/gpu/ipu-v3/ipu-dmfc.c | 214 +++ - drivers/gpu/ipu-v3/ipu-dp.c | 357 ++++ - drivers/gpu/ipu-v3/ipu-ic.c | 761 +++++++++ - drivers/gpu/ipu-v3/ipu-image-convert.c | 2475 ++++++++++++++++++++++++++++ - drivers/gpu/ipu-v3/ipu-pre.c | 346 ++++ - drivers/gpu/ipu-v3/ipu-prg.c | 483 ++++++ - drivers/gpu/ipu-v3/ipu-prv.h | 274 +++ - drivers/gpu/ipu-v3/ipu-smfc.c | 202 +++ - drivers/gpu/ipu-v3/ipu-vdi.c | 234 +++ - drivers/video/Kconfig | 2 +- - 36 files changed, 9896 insertions(+), 9898 deletions(-) - delete mode 100644 drivers/gpu/imx/Kconfig - delete mode 100644 drivers/gpu/imx/Makefile - delete mode 100644 drivers/gpu/imx/ipu-v3/Kconfig - delete mode 100644 drivers/gpu/imx/ipu-v3/Makefile - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-common.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-cpmem.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-csi.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-dc.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-di.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-dmfc.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-dp.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-ic.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-image-convert.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-pre.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-prg.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-prv.h - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-smfc.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-vdi.c - create mode 100644 drivers/gpu/ipu-v3/Kconfig - create mode 100644 drivers/gpu/ipu-v3/Makefile - create mode 100644 drivers/gpu/ipu-v3/ipu-common.c - create mode 100644 drivers/gpu/ipu-v3/ipu-cpmem.c - create mode 100644 drivers/gpu/ipu-v3/ipu-csi.c - create mode 100644 drivers/gpu/ipu-v3/ipu-dc.c - create mode 100644 drivers/gpu/ipu-v3/ipu-di.c - create mode 100644 drivers/gpu/ipu-v3/ipu-dmfc.c - create mode 100644 drivers/gpu/ipu-v3/ipu-dp.c - create mode 100644 drivers/gpu/ipu-v3/ipu-ic.c - create mode 100644 drivers/gpu/ipu-v3/ipu-image-convert.c - create mode 100644 drivers/gpu/ipu-v3/ipu-pre.c - create mode 100644 drivers/gpu/ipu-v3/ipu-prg.c - create mode 100644 drivers/gpu/ipu-v3/ipu-prv.h - create mode 100644 drivers/gpu/ipu-v3/ipu-smfc.c - create mode 100644 drivers/gpu/ipu-v3/ipu-vdi.c - ---- a/drivers/gpu/Makefile -+++ b/drivers/gpu/Makefile -@@ -3,5 +3,5 @@ - # taken to initialize them in the correct order. Link order is the only way - # to ensure this currently. - obj-$(CONFIG_TEGRA_HOST1X) += host1x/ --obj-y += imx/ - obj-y += drm/ vga/ -+obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/ ---- a/drivers/gpu/imx/Kconfig -+++ /dev/null -@@ -1 +0,0 @@ --source "drivers/gpu/imx/ipu-v3/Kconfig" ---- a/drivers/gpu/imx/Makefile -+++ /dev/null -@@ -1 +0,0 @@ --obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/ ---- a/drivers/gpu/imx/ipu-v3/Kconfig -+++ /dev/null -@@ -1,11 +0,0 @@ --# SPDX-License-Identifier: GPL-2.0-only --config IMX_IPUV3_CORE -- tristate "IPUv3 core support" -- 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 -- Processing Unit. This option only enables IPU base support. ---- a/drivers/gpu/imx/ipu-v3/Makefile -+++ /dev/null -@@ -1,10 +0,0 @@ --# SPDX-License-Identifier: GPL-2.0 --obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o -- --imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \ -- ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-ic-csc.o \ -- ipu-image-convert.o ipu-smfc.o ipu-vdi.o -- --ifdef CONFIG_DRM -- imx-ipu-v3-objs += ipu-pre.o ipu-prg.o --endif ---- a/drivers/gpu/imx/ipu-v3/ipu-common.c -+++ /dev/null -@@ -1,1565 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#include <linux/module.h> --#include <linux/export.h> --#include <linux/types.h> --#include <linux/reset.h> --#include <linux/platform_device.h> --#include <linux/err.h> --#include <linux/spinlock.h> --#include <linux/delay.h> --#include <linux/interrupt.h> --#include <linux/io.h> --#include <linux/clk.h> --#include <linux/list.h> --#include <linux/irq.h> --#include <linux/irqchip/chained_irq.h> --#include <linux/irqdomain.h> --#include <linux/of_device.h> --#include <linux/of_graph.h> -- --#include <drm/drm_fourcc.h> -- --#include <video/imx-ipu-v3.h> --#include "ipu-prv.h" -- --static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset) --{ -- return readl(ipu->cm_reg + offset); --} -- --static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset) --{ -- writel(value, ipu->cm_reg + offset); --} -- --int ipu_get_num(struct ipu_soc *ipu) --{ -- return ipu->id; --} --EXPORT_SYMBOL_GPL(ipu_get_num); -- --void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync) --{ -- u32 val; -- -- val = ipu_cm_read(ipu, IPU_SRM_PRI2); -- val &= ~DP_S_SRM_MODE_MASK; -- val |= sync ? DP_S_SRM_MODE_NEXT_FRAME : -- DP_S_SRM_MODE_NOW; -- ipu_cm_write(ipu, val, IPU_SRM_PRI2); --} --EXPORT_SYMBOL_GPL(ipu_srm_dp_update); -- --enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc) --{ -- switch (drm_fourcc) { -- case DRM_FORMAT_ARGB1555: -- case DRM_FORMAT_ABGR1555: -- case DRM_FORMAT_RGBA5551: -- case DRM_FORMAT_BGRA5551: -- case DRM_FORMAT_RGB565: -- case DRM_FORMAT_BGR565: -- case DRM_FORMAT_RGB888: -- case DRM_FORMAT_BGR888: -- case DRM_FORMAT_ARGB4444: -- case DRM_FORMAT_XRGB8888: -- case DRM_FORMAT_XBGR8888: -- case DRM_FORMAT_RGBX8888: -- case DRM_FORMAT_BGRX8888: -- case DRM_FORMAT_ARGB8888: -- case DRM_FORMAT_ABGR8888: -- case DRM_FORMAT_RGBA8888: -- case DRM_FORMAT_BGRA8888: -- case DRM_FORMAT_RGB565_A8: -- case DRM_FORMAT_BGR565_A8: -- case DRM_FORMAT_RGB888_A8: -- case DRM_FORMAT_BGR888_A8: -- case DRM_FORMAT_RGBX8888_A8: -- case DRM_FORMAT_BGRX8888_A8: -- return IPUV3_COLORSPACE_RGB; -- case DRM_FORMAT_YUYV: -- case DRM_FORMAT_UYVY: -- case DRM_FORMAT_YUV420: -- case DRM_FORMAT_YVU420: -- case DRM_FORMAT_YUV422: -- case DRM_FORMAT_YVU422: -- case DRM_FORMAT_YUV444: -- case DRM_FORMAT_YVU444: -- case DRM_FORMAT_NV12: -- case DRM_FORMAT_NV21: -- case DRM_FORMAT_NV16: -- case DRM_FORMAT_NV61: -- return IPUV3_COLORSPACE_YUV; -- default: -- return IPUV3_COLORSPACE_UNKNOWN; -- } --} --EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace); -- --enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat) --{ -- switch (pixelformat) { -- case V4L2_PIX_FMT_YUV420: -- case V4L2_PIX_FMT_YVU420: -- case V4L2_PIX_FMT_YUV422P: -- case V4L2_PIX_FMT_UYVY: -- case V4L2_PIX_FMT_YUYV: -- case V4L2_PIX_FMT_NV12: -- case V4L2_PIX_FMT_NV21: -- case V4L2_PIX_FMT_NV16: -- case V4L2_PIX_FMT_NV61: -- return IPUV3_COLORSPACE_YUV; -- case V4L2_PIX_FMT_RGB565: -- case V4L2_PIX_FMT_BGR24: -- case V4L2_PIX_FMT_RGB24: -- case V4L2_PIX_FMT_ABGR32: -- case V4L2_PIX_FMT_XBGR32: -- case V4L2_PIX_FMT_BGRA32: -- case V4L2_PIX_FMT_BGRX32: -- case V4L2_PIX_FMT_RGBA32: -- case V4L2_PIX_FMT_RGBX32: -- case V4L2_PIX_FMT_ARGB32: -- case V4L2_PIX_FMT_XRGB32: -- return IPUV3_COLORSPACE_RGB; -- default: -- return IPUV3_COLORSPACE_UNKNOWN; -- } --} --EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace); -- --bool ipu_pixelformat_is_planar(u32 pixelformat) --{ -- switch (pixelformat) { -- case V4L2_PIX_FMT_YUV420: -- case V4L2_PIX_FMT_YVU420: -- case V4L2_PIX_FMT_YUV422P: -- case V4L2_PIX_FMT_NV12: -- case V4L2_PIX_FMT_NV21: -- case V4L2_PIX_FMT_NV16: -- case V4L2_PIX_FMT_NV61: -- return true; -- } -- -- return false; --} --EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar); -- --enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code) --{ -- switch (mbus_code & 0xf000) { -- case 0x1000: -- return IPUV3_COLORSPACE_RGB; -- case 0x2000: -- return IPUV3_COLORSPACE_YUV; -- default: -- return IPUV3_COLORSPACE_UNKNOWN; -- } --} --EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace); -- --int ipu_stride_to_bytes(u32 pixel_stride, u32 pixelformat) --{ -- switch (pixelformat) { -- case V4L2_PIX_FMT_YUV420: -- case V4L2_PIX_FMT_YVU420: -- case V4L2_PIX_FMT_YUV422P: -- case V4L2_PIX_FMT_NV12: -- case V4L2_PIX_FMT_NV21: -- case V4L2_PIX_FMT_NV16: -- case V4L2_PIX_FMT_NV61: -- /* -- * for the planar YUV formats, the stride passed to -- * cpmem must be the stride in bytes of the Y plane. -- * And all the planar YUV formats have an 8-bit -- * Y component. -- */ -- return (8 * pixel_stride) >> 3; -- case V4L2_PIX_FMT_RGB565: -- case V4L2_PIX_FMT_YUYV: -- case V4L2_PIX_FMT_UYVY: -- return (16 * pixel_stride) >> 3; -- case V4L2_PIX_FMT_BGR24: -- case V4L2_PIX_FMT_RGB24: -- return (24 * pixel_stride) >> 3; -- case V4L2_PIX_FMT_BGR32: -- case V4L2_PIX_FMT_RGB32: -- case V4L2_PIX_FMT_XBGR32: -- case V4L2_PIX_FMT_XRGB32: -- return (32 * pixel_stride) >> 3; -- default: -- break; -- } -- -- return -EINVAL; --} --EXPORT_SYMBOL_GPL(ipu_stride_to_bytes); -- --int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees, -- bool hflip, bool vflip) --{ -- u32 r90, vf, hf; -- -- switch (degrees) { -- case 0: -- vf = hf = r90 = 0; -- break; -- case 90: -- vf = hf = 0; -- r90 = 1; -- break; -- case 180: -- vf = hf = 1; -- r90 = 0; -- break; -- case 270: -- vf = hf = r90 = 1; -- break; -- default: -- return -EINVAL; -- } -- -- hf ^= (u32)hflip; -- vf ^= (u32)vflip; -- -- *mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf); -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode); -- --int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode, -- bool hflip, bool vflip) --{ -- u32 r90, vf, hf; -- -- r90 = ((u32)mode >> 2) & 0x1; -- hf = ((u32)mode >> 1) & 0x1; -- vf = ((u32)mode >> 0) & 0x1; -- hf ^= (u32)hflip; -- vf ^= (u32)vflip; -- -- switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) { -- case IPU_ROTATE_NONE: -- *degrees = 0; -- break; -- case IPU_ROTATE_90_RIGHT: -- *degrees = 90; -- break; -- case IPU_ROTATE_180: -- *degrees = 180; -- break; -- case IPU_ROTATE_90_LEFT: -- *degrees = 270; -- break; -- default: -- return -EINVAL; -- } -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees); -- --struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num) --{ -- struct ipuv3_channel *channel; -- -- dev_dbg(ipu->dev, "%s %d\n", __func__, num); -- -- if (num > 63) -- return ERR_PTR(-ENODEV); -- -- mutex_lock(&ipu->channel_lock); -- -- list_for_each_entry(channel, &ipu->channels, list) { -- if (channel->num == num) { -- channel = ERR_PTR(-EBUSY); -- goto out; -- } -- } -- -- channel = kzalloc(sizeof(*channel), GFP_KERNEL); -- if (!channel) { -- channel = ERR_PTR(-ENOMEM); -- goto out; -- } -- -- channel->num = num; -- channel->ipu = ipu; -- list_add(&channel->list, &ipu->channels); -- --out: -- mutex_unlock(&ipu->channel_lock); -- -- return channel; --} --EXPORT_SYMBOL_GPL(ipu_idmac_get); -- --void ipu_idmac_put(struct ipuv3_channel *channel) --{ -- struct ipu_soc *ipu = channel->ipu; -- -- dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num); -- -- mutex_lock(&ipu->channel_lock); -- -- list_del(&channel->list); -- kfree(channel); -- -- mutex_unlock(&ipu->channel_lock); --} --EXPORT_SYMBOL_GPL(ipu_idmac_put); -- --#define idma_mask(ch) (1 << ((ch) & 0x1f)) -- --/* -- * This is an undocumented feature, a write one to a channel bit in -- * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's -- * internal current buffer pointer so that transfers start from buffer -- * 0 on the next channel enable (that's the theory anyway, the imx6 TRM -- * only says these are read-only registers). This operation is required -- * for channel linking to work correctly, for instance video capture -- * pipelines that carry out image rotations will fail after the first -- * streaming unless this function is called for each channel before -- * re-enabling the channels. -- */ --static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned int chno = channel->num; -- -- ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno)); --} -- --void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel, -- bool doublebuffer) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned long flags; -- u32 reg; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); -- if (doublebuffer) -- reg |= idma_mask(channel->num); -- else -- reg &= ~idma_mask(channel->num); -- ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num)); -- -- __ipu_idmac_reset_current_buffer(channel); -- -- spin_unlock_irqrestore(&ipu->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer); -- --static const struct { -- int chnum; -- u32 reg; -- int shift; --} idmac_lock_en_info[] = { -- { .chnum = 5, .reg = IDMAC_CH_LOCK_EN_1, .shift = 0, }, -- { .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift = 2, }, -- { .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift = 4, }, -- { .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift = 6, }, -- { .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift = 8, }, -- { .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, }, -- { .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, }, -- { .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, }, -- { .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, }, -- { .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, }, -- { .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, }, -- { .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift = 0, }, -- { .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift = 2, }, -- { .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift = 4, }, -- { .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift = 6, }, -- { .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift = 8, }, -- { .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, }, --}; -- --int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned long flags; -- u32 bursts, regval; -- int i; -- -- switch (num_bursts) { -- case 0: -- case 1: -- bursts = 0x00; /* locking disabled */ -- break; -- case 2: -- bursts = 0x01; -- break; -- case 4: -- bursts = 0x02; -- break; -- case 8: -- bursts = 0x03; -- break; -- default: -- return -EINVAL; -- } -- -- /* -- * IPUv3EX / i.MX51 has a different register layout, and on IPUv3M / -- * i.MX53 channel arbitration locking doesn't seem to work properly. -- * Allow enabling the lock feature on IPUv3H / i.MX6 only. -- */ -- if (bursts && ipu->ipu_type != IPUV3H) -- return -EINVAL; -- -- for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) { -- if (channel->num == idmac_lock_en_info[i].chnum) -- break; -- } -- if (i >= ARRAY_SIZE(idmac_lock_en_info)) -- return -EINVAL; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg); -- regval &= ~(0x03 << idmac_lock_en_info[i].shift); -- regval |= (bursts << idmac_lock_en_info[i].shift); -- ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg); -- -- spin_unlock_irqrestore(&ipu->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable); -- --int ipu_module_enable(struct ipu_soc *ipu, u32 mask) --{ -- unsigned long lock_flags; -- u32 val; -- -- spin_lock_irqsave(&ipu->lock, lock_flags); -- -- val = ipu_cm_read(ipu, IPU_DISP_GEN); -- -- if (mask & IPU_CONF_DI0_EN) -- val |= IPU_DI0_COUNTER_RELEASE; -- if (mask & IPU_CONF_DI1_EN) -- val |= IPU_DI1_COUNTER_RELEASE; -- -- ipu_cm_write(ipu, val, IPU_DISP_GEN); -- -- val = ipu_cm_read(ipu, IPU_CONF); -- val |= mask; -- ipu_cm_write(ipu, val, IPU_CONF); -- -- spin_unlock_irqrestore(&ipu->lock, lock_flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_module_enable); -- --int ipu_module_disable(struct ipu_soc *ipu, u32 mask) --{ -- unsigned long lock_flags; -- u32 val; -- -- spin_lock_irqsave(&ipu->lock, lock_flags); -- -- val = ipu_cm_read(ipu, IPU_CONF); -- val &= ~mask; -- ipu_cm_write(ipu, val, IPU_CONF); -- -- val = ipu_cm_read(ipu, IPU_DISP_GEN); -- -- if (mask & IPU_CONF_DI0_EN) -- val &= ~IPU_DI0_COUNTER_RELEASE; -- if (mask & IPU_CONF_DI1_EN) -- val &= ~IPU_DI1_COUNTER_RELEASE; -- -- ipu_cm_write(ipu, val, IPU_DISP_GEN); -- -- spin_unlock_irqrestore(&ipu->lock, lock_flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_module_disable); -- --int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned int chno = channel->num; -- -- return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0; --} --EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer); -- --bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned long flags; -- u32 reg = 0; -- -- spin_lock_irqsave(&ipu->lock, flags); -- switch (buf_num) { -- case 0: -- reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)); -- break; -- case 1: -- reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)); -- break; -- case 2: -- reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num)); -- break; -- } -- spin_unlock_irqrestore(&ipu->lock, flags); -- -- return ((reg & idma_mask(channel->num)) != 0); --} --EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready); -- --void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned int chno = channel->num; -- unsigned long flags; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- /* Mark buffer as ready. */ -- if (buf_num == 0) -- ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); -- else -- ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); -- -- spin_unlock_irqrestore(&ipu->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer); -- --void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned int chno = channel->num; -- unsigned long flags; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */ -- switch (buf_num) { -- case 0: -- ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); -- break; -- case 1: -- ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); -- break; -- case 2: -- ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno)); -- break; -- default: -- break; -- } -- ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ -- -- spin_unlock_irqrestore(&ipu->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer); -- --int ipu_idmac_enable_channel(struct ipuv3_channel *channel) --{ -- struct ipu_soc *ipu = channel->ipu; -- u32 val; -- unsigned long flags; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); -- val |= idma_mask(channel->num); -- ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); -- -- spin_unlock_irqrestore(&ipu->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel); -- --bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno) --{ -- return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno)); --} --EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy); -- --int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned long timeout; -- -- timeout = jiffies + msecs_to_jiffies(ms); -- while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) & -- idma_mask(channel->num)) { -- if (time_after(jiffies, timeout)) -- return -ETIMEDOUT; -- cpu_relax(); -- } -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); -- --int ipu_idmac_disable_channel(struct ipuv3_channel *channel) --{ -- struct ipu_soc *ipu = channel->ipu; -- u32 val; -- unsigned long flags; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- /* Disable DMA channel(s) */ -- val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); -- val &= ~idma_mask(channel->num); -- ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); -- -- __ipu_idmac_reset_current_buffer(channel); -- -- /* Set channel buffers NOT to be ready */ -- ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */ -- -- if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) & -- idma_mask(channel->num)) { -- ipu_cm_write(ipu, idma_mask(channel->num), -- IPU_CHA_BUF0_RDY(channel->num)); -- } -- -- if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) & -- idma_mask(channel->num)) { -- ipu_cm_write(ipu, idma_mask(channel->num), -- IPU_CHA_BUF1_RDY(channel->num)); -- } -- -- ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ -- -- /* Reset the double buffer */ -- val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); -- val &= ~idma_mask(channel->num); -- ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num)); -- -- spin_unlock_irqrestore(&ipu->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel); -- --/* -- * The imx6 rev. D TRM says that enabling the WM feature will increase -- * a channel's priority. Refer to Table 36-8 Calculated priority value. -- * The sub-module that is the sink or source for the channel must enable -- * watermark signal for this to take effect (SMFC_WM for instance). -- */ --void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned long flags; -- u32 val; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num)); -- if (enable) -- val |= 1 << (channel->num % 32); -- else -- val &= ~(1 << (channel->num % 32)); -- ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num)); -- -- spin_unlock_irqrestore(&ipu->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark); -- --static int ipu_memory_reset(struct ipu_soc *ipu) --{ -- unsigned long timeout; -- -- ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); -- -- timeout = jiffies + msecs_to_jiffies(1000); -- while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { -- if (time_after(jiffies, timeout)) -- return -ETIME; -- cpu_relax(); -- } -- -- return 0; --} -- --/* -- * Set the source mux for the given CSI. Selects either parallel or -- * MIPI CSI2 sources. -- */ --void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2) --{ -- unsigned long flags; -- u32 val, mask; -- -- mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE : -- IPU_CONF_CSI0_DATA_SOURCE; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- val = ipu_cm_read(ipu, IPU_CONF); -- if (mipi_csi2) -- val |= mask; -- else -- val &= ~mask; -- ipu_cm_write(ipu, val, IPU_CONF); -- -- spin_unlock_irqrestore(&ipu->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux); -- --/* -- * Set the source mux for the IC. Selects either CSI[01] or the VDI. -- */ --void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi) --{ -- unsigned long flags; -- u32 val; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- val = ipu_cm_read(ipu, IPU_CONF); -- if (vdi) -- val |= IPU_CONF_IC_INPUT; -- else -- val &= ~IPU_CONF_IC_INPUT; -- -- if (csi_id == 1) -- val |= IPU_CONF_CSI_SEL; -- else -- val &= ~IPU_CONF_CSI_SEL; -- -- ipu_cm_write(ipu, val, IPU_CONF); -- -- spin_unlock_irqrestore(&ipu->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux); -- -- --/* Frame Synchronization Unit Channel Linking */ -- --struct fsu_link_reg_info { -- int chno; -- u32 reg; -- u32 mask; -- u32 val; --}; -- --struct fsu_link_info { -- struct fsu_link_reg_info src; -- struct fsu_link_reg_info sink; --}; -- --static const struct fsu_link_info fsu_link_info[] = { -- { -- .src = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2, -- FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC }, -- .sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1, -- FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC }, -- }, { -- .src = { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2, -- FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF }, -- .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1, -- FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF }, -- }, { -- .src = { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2, -- FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP }, -- .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1, -- FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP }, -- }, { -- .src = { IPUV3_CHANNEL_CSI_DIRECT, 0 }, -- .sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1, -- FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT }, -- }, --}; -- --static const struct fsu_link_info *find_fsu_link_info(int src, int sink) --{ -- int i; -- -- for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) { -- if (src == fsu_link_info[i].src.chno && -- sink == fsu_link_info[i].sink.chno) -- return &fsu_link_info[i]; -- } -- -- return NULL; --} -- --/* -- * Links a source channel to a sink channel in the FSU. -- */ --int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch) --{ -- const struct fsu_link_info *link; -- u32 src_reg, sink_reg; -- unsigned long flags; -- -- link = find_fsu_link_info(src_ch, sink_ch); -- if (!link) -- return -EINVAL; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- if (link->src.mask) { -- src_reg = ipu_cm_read(ipu, link->src.reg); -- src_reg &= ~link->src.mask; -- src_reg |= link->src.val; -- ipu_cm_write(ipu, src_reg, link->src.reg); -- } -- -- if (link->sink.mask) { -- sink_reg = ipu_cm_read(ipu, link->sink.reg); -- sink_reg &= ~link->sink.mask; -- sink_reg |= link->sink.val; -- ipu_cm_write(ipu, sink_reg, link->sink.reg); -- } -- -- spin_unlock_irqrestore(&ipu->lock, flags); -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_fsu_link); -- --/* -- * Unlinks source and sink channels in the FSU. -- */ --int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch) --{ -- const struct fsu_link_info *link; -- u32 src_reg, sink_reg; -- unsigned long flags; -- -- link = find_fsu_link_info(src_ch, sink_ch); -- if (!link) -- return -EINVAL; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- if (link->src.mask) { -- src_reg = ipu_cm_read(ipu, link->src.reg); -- src_reg &= ~link->src.mask; -- ipu_cm_write(ipu, src_reg, link->src.reg); -- } -- -- if (link->sink.mask) { -- sink_reg = ipu_cm_read(ipu, link->sink.reg); -- sink_reg &= ~link->sink.mask; -- ipu_cm_write(ipu, sink_reg, link->sink.reg); -- } -- -- spin_unlock_irqrestore(&ipu->lock, flags); -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_fsu_unlink); -- --/* Link IDMAC channels in the FSU */ --int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink) --{ -- return ipu_fsu_link(src->ipu, src->num, sink->num); --} --EXPORT_SYMBOL_GPL(ipu_idmac_link); -- --/* Unlink IDMAC channels in the FSU */ --int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink) --{ -- return ipu_fsu_unlink(src->ipu, src->num, sink->num); --} --EXPORT_SYMBOL_GPL(ipu_idmac_unlink); -- --struct ipu_devtype { -- const char *name; -- unsigned long cm_ofs; -- unsigned long cpmem_ofs; -- unsigned long srm_ofs; -- unsigned long tpm_ofs; -- unsigned long csi0_ofs; -- unsigned long csi1_ofs; -- unsigned long ic_ofs; -- unsigned long disp0_ofs; -- unsigned long disp1_ofs; -- unsigned long dc_tmpl_ofs; -- unsigned long vdi_ofs; -- enum ipuv3_type type; --}; -- --static struct ipu_devtype ipu_type_imx51 = { -- .name = "IPUv3EX", -- .cm_ofs = 0x1e000000, -- .cpmem_ofs = 0x1f000000, -- .srm_ofs = 0x1f040000, -- .tpm_ofs = 0x1f060000, -- .csi0_ofs = 0x1e030000, -- .csi1_ofs = 0x1e038000, -- .ic_ofs = 0x1e020000, -- .disp0_ofs = 0x1e040000, -- .disp1_ofs = 0x1e048000, -- .dc_tmpl_ofs = 0x1f080000, -- .vdi_ofs = 0x1e068000, -- .type = IPUV3EX, --}; -- --static struct ipu_devtype ipu_type_imx53 = { -- .name = "IPUv3M", -- .cm_ofs = 0x06000000, -- .cpmem_ofs = 0x07000000, -- .srm_ofs = 0x07040000, -- .tpm_ofs = 0x07060000, -- .csi0_ofs = 0x06030000, -- .csi1_ofs = 0x06038000, -- .ic_ofs = 0x06020000, -- .disp0_ofs = 0x06040000, -- .disp1_ofs = 0x06048000, -- .dc_tmpl_ofs = 0x07080000, -- .vdi_ofs = 0x06068000, -- .type = IPUV3M, --}; -- --static struct ipu_devtype ipu_type_imx6q = { -- .name = "IPUv3H", -- .cm_ofs = 0x00200000, -- .cpmem_ofs = 0x00300000, -- .srm_ofs = 0x00340000, -- .tpm_ofs = 0x00360000, -- .csi0_ofs = 0x00230000, -- .csi1_ofs = 0x00238000, -- .ic_ofs = 0x00220000, -- .disp0_ofs = 0x00240000, -- .disp1_ofs = 0x00248000, -- .dc_tmpl_ofs = 0x00380000, -- .vdi_ofs = 0x00268000, -- .type = IPUV3H, --}; -- --static const struct of_device_id imx_ipu_dt_ids[] = { -- { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, -- { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, -- { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, -- { .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6q, }, -- { /* sentinel */ } --}; --MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); -- --static int ipu_submodules_init(struct ipu_soc *ipu, -- struct platform_device *pdev, unsigned long ipu_base, -- struct clk *ipu_clk) --{ -- char *unit; -- int ret; -- struct device *dev = &pdev->dev; -- const struct ipu_devtype *devtype = ipu->devtype; -- -- ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs); -- if (ret) { -- unit = "cpmem"; -- goto err_cpmem; -- } -- -- ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs, -- IPU_CONF_CSI0_EN, ipu_clk); -- if (ret) { -- unit = "csi0"; -- goto err_csi_0; -- } -- -- ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs, -- IPU_CONF_CSI1_EN, ipu_clk); -- if (ret) { -- unit = "csi1"; -- goto err_csi_1; -- } -- -- ret = ipu_ic_init(ipu, dev, -- ipu_base + devtype->ic_ofs, -- ipu_base + devtype->tpm_ofs); -- if (ret) { -- unit = "ic"; -- goto err_ic; -- } -- -- ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs, -- IPU_CONF_VDI_EN | IPU_CONF_ISP_EN | -- IPU_CONF_IC_INPUT); -- if (ret) { -- unit = "vdi"; -- goto err_vdi; -- } -- -- ret = ipu_image_convert_init(ipu, dev); -- if (ret) { -- unit = "image_convert"; -- goto err_image_convert; -- } -- -- ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, -- IPU_CONF_DI0_EN, ipu_clk); -- if (ret) { -- unit = "di0"; -- goto err_di_0; -- } -- -- ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs, -- IPU_CONF_DI1_EN, ipu_clk); -- if (ret) { -- unit = "di1"; -- goto err_di_1; -- } -- -- ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs + -- IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs); -- if (ret) { -- unit = "dc_template"; -- goto err_dc; -- } -- -- ret = ipu_dmfc_init(ipu, dev, ipu_base + -- devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk); -- if (ret) { -- unit = "dmfc"; -- goto err_dmfc; -- } -- -- ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs); -- if (ret) { -- unit = "dp"; -- goto err_dp; -- } -- -- ret = ipu_smfc_init(ipu, dev, ipu_base + -- devtype->cm_ofs + IPU_CM_SMFC_REG_OFS); -- if (ret) { -- unit = "smfc"; -- goto err_smfc; -- } -- -- return 0; -- --err_smfc: -- ipu_dp_exit(ipu); --err_dp: -- ipu_dmfc_exit(ipu); --err_dmfc: -- ipu_dc_exit(ipu); --err_dc: -- ipu_di_exit(ipu, 1); --err_di_1: -- ipu_di_exit(ipu, 0); --err_di_0: -- ipu_image_convert_exit(ipu); --err_image_convert: -- ipu_vdi_exit(ipu); --err_vdi: -- ipu_ic_exit(ipu); --err_ic: -- ipu_csi_exit(ipu, 1); --err_csi_1: -- ipu_csi_exit(ipu, 0); --err_csi_0: -- ipu_cpmem_exit(ipu); --err_cpmem: -- dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret); -- return ret; --} -- --static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs) --{ -- unsigned long status; -- int i, bit, irq; -- -- for (i = 0; i < num_regs; i++) { -- -- status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i])); -- status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i])); -- -- for_each_set_bit(bit, &status, 32) { -- irq = irq_linear_revmap(ipu->domain, -- regs[i] * 32 + bit); -- if (irq) -- generic_handle_irq(irq); -- } -- } --} -- --static void ipu_irq_handler(struct irq_desc *desc) --{ -- struct ipu_soc *ipu = irq_desc_get_handler_data(desc); -- struct irq_chip *chip = irq_desc_get_chip(desc); -- static const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14}; -- -- chained_irq_enter(chip, desc); -- -- ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); -- -- chained_irq_exit(chip, desc); --} -- --static void ipu_err_irq_handler(struct irq_desc *desc) --{ -- struct ipu_soc *ipu = irq_desc_get_handler_data(desc); -- struct irq_chip *chip = irq_desc_get_chip(desc); -- static const int int_reg[] = { 4, 5, 8, 9}; -- -- chained_irq_enter(chip, desc); -- -- ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); -- -- chained_irq_exit(chip, desc); --} -- --int ipu_map_irq(struct ipu_soc *ipu, int irq) --{ -- int virq; -- -- virq = irq_linear_revmap(ipu->domain, irq); -- if (!virq) -- virq = irq_create_mapping(ipu->domain, irq); -- -- return virq; --} --EXPORT_SYMBOL_GPL(ipu_map_irq); -- --int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, -- enum ipu_channel_irq irq_type) --{ -- return ipu_map_irq(ipu, irq_type + channel->num); --} --EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq); -- --static void ipu_submodules_exit(struct ipu_soc *ipu) --{ -- ipu_smfc_exit(ipu); -- ipu_dp_exit(ipu); -- ipu_dmfc_exit(ipu); -- ipu_dc_exit(ipu); -- ipu_di_exit(ipu, 1); -- ipu_di_exit(ipu, 0); -- ipu_image_convert_exit(ipu); -- ipu_vdi_exit(ipu); -- ipu_ic_exit(ipu); -- ipu_csi_exit(ipu, 1); -- ipu_csi_exit(ipu, 0); -- ipu_cpmem_exit(ipu); --} -- --static int platform_remove_devices_fn(struct device *dev, void *unused) --{ -- struct platform_device *pdev = to_platform_device(dev); -- -- platform_device_unregister(pdev); -- -- return 0; --} -- --static void platform_device_unregister_children(struct platform_device *pdev) --{ -- device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn); --} -- --struct ipu_platform_reg { -- struct ipu_client_platformdata pdata; -- const char *name; --}; -- --/* These must be in the order of the corresponding device tree port nodes */ --static struct ipu_platform_reg client_reg[] = { -- { -- .pdata = { -- .csi = 0, -- .dma[0] = IPUV3_CHANNEL_CSI0, -- .dma[1] = -EINVAL, -- }, -- .name = "imx-ipuv3-csi", -- }, { -- .pdata = { -- .csi = 1, -- .dma[0] = IPUV3_CHANNEL_CSI1, -- .dma[1] = -EINVAL, -- }, -- .name = "imx-ipuv3-csi", -- }, { -- .pdata = { -- .di = 0, -- .dc = 5, -- .dp = IPU_DP_FLOW_SYNC_BG, -- .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC, -- .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC, -- }, -- .name = "imx-ipuv3-crtc", -- }, { -- .pdata = { -- .di = 1, -- .dc = 1, -- .dp = -EINVAL, -- .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC, -- .dma[1] = -EINVAL, -- }, -- .name = "imx-ipuv3-crtc", -- }, --}; -- --static DEFINE_MUTEX(ipu_client_id_mutex); --static int ipu_client_id; -- --static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) --{ -- struct device *dev = ipu->dev; -- unsigned i; -- int id, ret; -- -- mutex_lock(&ipu_client_id_mutex); -- id = ipu_client_id; -- ipu_client_id += ARRAY_SIZE(client_reg); -- mutex_unlock(&ipu_client_id_mutex); -- -- for (i = 0; i < ARRAY_SIZE(client_reg); i++) { -- struct ipu_platform_reg *reg = &client_reg[i]; -- struct platform_device *pdev; -- struct device_node *of_node; -- -- /* Associate subdevice with the corresponding port node */ -- of_node = of_graph_get_port_by_id(dev->of_node, i); -- if (!of_node) { -- dev_info(dev, -- "no port@%d node in %pOF, not using %s%d\n", -- i, dev->of_node, -- (i / 2) ? "DI" : "CSI", i % 2); -- continue; -- } -- -- pdev = platform_device_alloc(reg->name, id++); -- if (!pdev) { -- ret = -ENOMEM; -- goto err_register; -- } -- -- pdev->dev.parent = dev; -- -- reg->pdata.of_node = of_node; -- ret = platform_device_add_data(pdev, ®->pdata, -- sizeof(reg->pdata)); -- if (!ret) -- ret = platform_device_add(pdev); -- if (ret) { -- platform_device_put(pdev); -- goto err_register; -- } -- } -- -- return 0; -- --err_register: -- platform_device_unregister_children(to_platform_device(dev)); -- -- return ret; --} -- -- --static int ipu_irq_init(struct ipu_soc *ipu) --{ -- struct irq_chip_generic *gc; -- struct irq_chip_type *ct; -- unsigned long unused[IPU_NUM_IRQS / 32] = { -- 0x400100d0, 0xffe000fd, -- 0x400100d0, 0xffe000fd, -- 0x400100d0, 0xffe000fd, -- 0x4077ffff, 0xffe7e1fd, -- 0x23fffffe, 0x8880fff0, -- 0xf98fe7d0, 0xfff81fff, -- 0x400100d0, 0xffe000fd, -- 0x00000000, -- }; -- int ret, i; -- -- ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS, -- &irq_generic_chip_ops, ipu); -- if (!ipu->domain) { -- dev_err(ipu->dev, "failed to add irq domain\n"); -- return -ENODEV; -- } -- -- ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU", -- handle_level_irq, 0, 0, 0); -- if (ret < 0) { -- dev_err(ipu->dev, "failed to alloc generic irq chips\n"); -- irq_domain_remove(ipu->domain); -- return ret; -- } -- -- /* Mask and clear all interrupts */ -- for (i = 0; i < IPU_NUM_IRQS; i += 32) { -- ipu_cm_write(ipu, 0, IPU_INT_CTRL(i / 32)); -- ipu_cm_write(ipu, ~unused[i / 32], IPU_INT_STAT(i / 32)); -- } -- -- for (i = 0; i < IPU_NUM_IRQS; i += 32) { -- gc = irq_get_domain_generic_chip(ipu->domain, i); -- gc->reg_base = ipu->cm_reg; -- gc->unused = unused[i / 32]; -- ct = gc->chip_types; -- ct->chip.irq_ack = irq_gc_ack_set_bit; -- ct->chip.irq_mask = irq_gc_mask_clr_bit; -- ct->chip.irq_unmask = irq_gc_mask_set_bit; -- ct->regs.ack = IPU_INT_STAT(i / 32); -- ct->regs.mask = IPU_INT_CTRL(i / 32); -- } -- -- irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu); -- irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler, -- ipu); -- -- return 0; --} -- --static void ipu_irq_exit(struct ipu_soc *ipu) --{ -- int i, irq; -- -- irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL); -- irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL); -- -- /* TODO: remove irq_domain_generic_chips */ -- -- for (i = 0; i < IPU_NUM_IRQS; i++) { -- irq = irq_linear_revmap(ipu->domain, i); -- if (irq) -- irq_dispose_mapping(irq); -- } -- -- irq_domain_remove(ipu->domain); --} -- --void ipu_dump(struct ipu_soc *ipu) --{ -- int i; -- -- dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_CONF)); -- dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_CONF)); -- dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_CHA_EN(0))); -- dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_CHA_EN(32))); -- dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_CHA_PRI(0))); -- dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_CHA_PRI(32))); -- dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_BAND_EN(0))); -- dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_BAND_EN(32))); -- dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0))); -- dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32))); -- dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_FS_PROC_FLOW1)); -- dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_FS_PROC_FLOW2)); -- dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_FS_PROC_FLOW3)); -- dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_FS_DISP_FLOW1)); -- for (i = 0; i < 15; i++) -- dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i, -- ipu_cm_read(ipu, IPU_INT_CTRL(i))); --} --EXPORT_SYMBOL_GPL(ipu_dump); -- --static int ipu_probe(struct platform_device *pdev) --{ -- struct device_node *np = pdev->dev.of_node; -- struct ipu_soc *ipu; -- struct resource *res; -- unsigned long ipu_base; -- int ret, irq_sync, irq_err; -- const struct ipu_devtype *devtype; -- -- devtype = of_device_get_match_data(&pdev->dev); -- if (!devtype) -- return -EINVAL; -- -- irq_sync = platform_get_irq(pdev, 0); -- irq_err = platform_get_irq(pdev, 1); -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- -- dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n", -- irq_sync, irq_err); -- -- if (!res || irq_sync < 0 || irq_err < 0) -- return -ENODEV; -- -- ipu_base = res->start; -- -- ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL); -- if (!ipu) -- return -ENODEV; -- -- ipu->id = of_alias_get_id(np, "ipu"); -- if (ipu->id < 0) -- ipu->id = 0; -- -- if (of_device_is_compatible(np, "fsl,imx6qp-ipu") && -- IS_ENABLED(CONFIG_DRM)) { -- ipu->prg_priv = ipu_prg_lookup_by_phandle(&pdev->dev, -- "fsl,prg", ipu->id); -- if (!ipu->prg_priv) -- return -EPROBE_DEFER; -- } -- -- ipu->devtype = devtype; -- ipu->ipu_type = devtype->type; -- -- spin_lock_init(&ipu->lock); -- mutex_init(&ipu->channel_lock); -- INIT_LIST_HEAD(&ipu->channels); -- -- dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n", -- ipu_base + devtype->cm_ofs); -- dev_dbg(&pdev->dev, "idmac: 0x%08lx\n", -- ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS); -- dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n", -- ipu_base + devtype->cpmem_ofs); -- dev_dbg(&pdev->dev, "csi0: 0x%08lx\n", -- ipu_base + devtype->csi0_ofs); -- dev_dbg(&pdev->dev, "csi1: 0x%08lx\n", -- ipu_base + devtype->csi1_ofs); -- dev_dbg(&pdev->dev, "ic: 0x%08lx\n", -- ipu_base + devtype->ic_ofs); -- dev_dbg(&pdev->dev, "disp0: 0x%08lx\n", -- ipu_base + devtype->disp0_ofs); -- dev_dbg(&pdev->dev, "disp1: 0x%08lx\n", -- ipu_base + devtype->disp1_ofs); -- dev_dbg(&pdev->dev, "srm: 0x%08lx\n", -- ipu_base + devtype->srm_ofs); -- dev_dbg(&pdev->dev, "tpm: 0x%08lx\n", -- ipu_base + devtype->tpm_ofs); -- dev_dbg(&pdev->dev, "dc: 0x%08lx\n", -- ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS); -- dev_dbg(&pdev->dev, "ic: 0x%08lx\n", -- ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS); -- dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n", -- ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS); -- dev_dbg(&pdev->dev, "vdi: 0x%08lx\n", -- ipu_base + devtype->vdi_ofs); -- -- ipu->cm_reg = devm_ioremap(&pdev->dev, -- ipu_base + devtype->cm_ofs, PAGE_SIZE); -- ipu->idmac_reg = devm_ioremap(&pdev->dev, -- ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS, -- PAGE_SIZE); -- -- if (!ipu->cm_reg || !ipu->idmac_reg) -- return -ENOMEM; -- -- ipu->clk = devm_clk_get(&pdev->dev, "bus"); -- if (IS_ERR(ipu->clk)) { -- ret = PTR_ERR(ipu->clk); -- dev_err(&pdev->dev, "clk_get failed with %d", ret); -- return ret; -- } -- -- platform_set_drvdata(pdev, ipu); -- -- ret = clk_prepare_enable(ipu->clk); -- if (ret) { -- dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); -- return ret; -- } -- -- ipu->dev = &pdev->dev; -- ipu->irq_sync = irq_sync; -- ipu->irq_err = irq_err; -- -- ret = device_reset(&pdev->dev); -- if (ret) { -- dev_err(&pdev->dev, "failed to reset: %d\n", ret); -- goto out_failed_reset; -- } -- ret = ipu_memory_reset(ipu); -- if (ret) -- goto out_failed_reset; -- -- ret = ipu_irq_init(ipu); -- if (ret) -- goto out_failed_irq; -- -- /* Set MCU_T to divide MCU access window into 2 */ -- ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), -- IPU_DISP_GEN); -- -- ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk); -- if (ret) -- goto failed_submodules_init; -- -- ret = ipu_add_client_devices(ipu, ipu_base); -- if (ret) { -- dev_err(&pdev->dev, "adding client devices failed with %d\n", -- ret); -- goto failed_add_clients; -- } -- -- dev_info(&pdev->dev, "%s probed\n", devtype->name); -- -- return 0; -- --failed_add_clients: -- ipu_submodules_exit(ipu); --failed_submodules_init: -- ipu_irq_exit(ipu); --out_failed_irq: --out_failed_reset: -- clk_disable_unprepare(ipu->clk); -- return ret; --} -- --static int ipu_remove(struct platform_device *pdev) --{ -- struct ipu_soc *ipu = platform_get_drvdata(pdev); -- -- platform_device_unregister_children(pdev); -- ipu_submodules_exit(ipu); -- ipu_irq_exit(ipu); -- -- clk_disable_unprepare(ipu->clk); -- -- return 0; --} -- --static struct platform_driver imx_ipu_driver = { -- .driver = { -- .name = "imx-ipuv3", -- .of_match_table = imx_ipu_dt_ids, -- }, -- .probe = ipu_probe, -- .remove = ipu_remove, --}; -- --static struct platform_driver * const drivers[] = { --#if IS_ENABLED(CONFIG_DRM) -- &ipu_pre_drv, -- &ipu_prg_drv, --#endif -- &imx_ipu_driver, --}; -- --static int __init imx_ipu_init(void) --{ -- return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); --} --module_init(imx_ipu_init); -- --static void __exit imx_ipu_exit(void) --{ -- platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); --} --module_exit(imx_ipu_exit); -- --MODULE_ALIAS("platform:imx-ipuv3"); --MODULE_DESCRIPTION("i.MX IPU v3 driver"); --MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); --MODULE_LICENSE("GPL"); ---- a/drivers/gpu/imx/ipu-v3/ipu-cpmem.c -+++ /dev/null -@@ -1,976 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (C) 2012 Mentor Graphics Inc. -- * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. -- */ --#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" -- --struct ipu_cpmem_word { -- u32 data[5]; -- u32 res[3]; --}; -- --struct ipu_ch_param { -- struct ipu_cpmem_word word[2]; --}; -- --struct ipu_cpmem { -- struct ipu_ch_param __iomem *base; -- u32 module; -- spinlock_t lock; -- int use_count; -- struct ipu_soc *ipu; --}; -- --#define IPU_CPMEM_WORD(word, ofs, size) ((((word) * 160 + (ofs)) << 8) | (size)) -- --#define IPU_FIELD_UBO IPU_CPMEM_WORD(0, 46, 22) --#define IPU_FIELD_VBO IPU_CPMEM_WORD(0, 68, 22) --#define IPU_FIELD_IOX IPU_CPMEM_WORD(0, 90, 4) --#define IPU_FIELD_RDRW IPU_CPMEM_WORD(0, 94, 1) --#define IPU_FIELD_SO IPU_CPMEM_WORD(0, 113, 1) --#define IPU_FIELD_SLY IPU_CPMEM_WORD(1, 102, 14) --#define IPU_FIELD_SLUV IPU_CPMEM_WORD(1, 128, 14) -- --#define IPU_FIELD_XV IPU_CPMEM_WORD(0, 0, 10) --#define IPU_FIELD_YV IPU_CPMEM_WORD(0, 10, 9) --#define IPU_FIELD_XB IPU_CPMEM_WORD(0, 19, 13) --#define IPU_FIELD_YB IPU_CPMEM_WORD(0, 32, 12) --#define IPU_FIELD_NSB_B IPU_CPMEM_WORD(0, 44, 1) --#define IPU_FIELD_CF IPU_CPMEM_WORD(0, 45, 1) --#define IPU_FIELD_SX IPU_CPMEM_WORD(0, 46, 12) --#define IPU_FIELD_SY IPU_CPMEM_WORD(0, 58, 11) --#define IPU_FIELD_NS IPU_CPMEM_WORD(0, 69, 10) --#define IPU_FIELD_SDX IPU_CPMEM_WORD(0, 79, 7) --#define IPU_FIELD_SM IPU_CPMEM_WORD(0, 86, 10) --#define IPU_FIELD_SCC IPU_CPMEM_WORD(0, 96, 1) --#define IPU_FIELD_SCE IPU_CPMEM_WORD(0, 97, 1) --#define IPU_FIELD_SDY IPU_CPMEM_WORD(0, 98, 7) --#define IPU_FIELD_SDRX IPU_CPMEM_WORD(0, 105, 1) --#define IPU_FIELD_SDRY IPU_CPMEM_WORD(0, 106, 1) --#define IPU_FIELD_BPP IPU_CPMEM_WORD(0, 107, 3) --#define IPU_FIELD_DEC_SEL IPU_CPMEM_WORD(0, 110, 2) --#define IPU_FIELD_DIM IPU_CPMEM_WORD(0, 112, 1) --#define IPU_FIELD_BNDM IPU_CPMEM_WORD(0, 114, 3) --#define IPU_FIELD_BM IPU_CPMEM_WORD(0, 117, 2) --#define IPU_FIELD_ROT IPU_CPMEM_WORD(0, 119, 1) --#define IPU_FIELD_ROT_HF_VF IPU_CPMEM_WORD(0, 119, 3) --#define IPU_FIELD_HF IPU_CPMEM_WORD(0, 120, 1) --#define IPU_FIELD_VF IPU_CPMEM_WORD(0, 121, 1) --#define IPU_FIELD_THE IPU_CPMEM_WORD(0, 122, 1) --#define IPU_FIELD_CAP IPU_CPMEM_WORD(0, 123, 1) --#define IPU_FIELD_CAE IPU_CPMEM_WORD(0, 124, 1) --#define IPU_FIELD_FW IPU_CPMEM_WORD(0, 125, 13) --#define IPU_FIELD_FH IPU_CPMEM_WORD(0, 138, 12) --#define IPU_FIELD_EBA0 IPU_CPMEM_WORD(1, 0, 29) --#define IPU_FIELD_EBA1 IPU_CPMEM_WORD(1, 29, 29) --#define IPU_FIELD_ILO IPU_CPMEM_WORD(1, 58, 20) --#define IPU_FIELD_NPB IPU_CPMEM_WORD(1, 78, 7) --#define IPU_FIELD_PFS IPU_CPMEM_WORD(1, 85, 4) --#define IPU_FIELD_ALU IPU_CPMEM_WORD(1, 89, 1) --#define IPU_FIELD_ALBM IPU_CPMEM_WORD(1, 90, 3) --#define IPU_FIELD_ID IPU_CPMEM_WORD(1, 93, 2) --#define IPU_FIELD_TH IPU_CPMEM_WORD(1, 95, 7) --#define IPU_FIELD_SL IPU_CPMEM_WORD(1, 102, 14) --#define IPU_FIELD_WID0 IPU_CPMEM_WORD(1, 116, 3) --#define IPU_FIELD_WID1 IPU_CPMEM_WORD(1, 119, 3) --#define IPU_FIELD_WID2 IPU_CPMEM_WORD(1, 122, 3) --#define IPU_FIELD_WID3 IPU_CPMEM_WORD(1, 125, 3) --#define IPU_FIELD_OFS0 IPU_CPMEM_WORD(1, 128, 5) --#define IPU_FIELD_OFS1 IPU_CPMEM_WORD(1, 133, 5) --#define IPU_FIELD_OFS2 IPU_CPMEM_WORD(1, 138, 5) --#define IPU_FIELD_OFS3 IPU_CPMEM_WORD(1, 143, 5) --#define IPU_FIELD_SXYS IPU_CPMEM_WORD(1, 148, 1) --#define IPU_FIELD_CRE IPU_CPMEM_WORD(1, 149, 1) --#define IPU_FIELD_DEC_SEL2 IPU_CPMEM_WORD(1, 150, 1) -- --static inline struct ipu_ch_param __iomem * --ipu_get_cpmem(struct ipuv3_channel *ch) --{ -- struct ipu_cpmem *cpmem = ch->ipu->cpmem_priv; -- -- return cpmem->base + ch->num; --} -- --static void ipu_ch_param_write_field(struct ipuv3_channel *ch, u32 wbs, u32 v) --{ -- struct ipu_ch_param __iomem *base = ipu_get_cpmem(ch); -- u32 bit = (wbs >> 8) % 160; -- u32 size = wbs & 0xff; -- u32 word = (wbs >> 8) / 160; -- u32 i = bit / 32; -- u32 ofs = bit % 32; -- u32 mask = (1 << size) - 1; -- u32 val; -- -- pr_debug("%s %d %d %d\n", __func__, word, bit , size); -- -- val = readl(&base->word[word].data[i]); -- val &= ~(mask << ofs); -- val |= v << ofs; -- writel(val, &base->word[word].data[i]); -- -- if ((bit + size - 1) / 32 > i) { -- val = readl(&base->word[word].data[i + 1]); -- val &= ~(mask >> (ofs ? (32 - ofs) : 0)); -- val |= v >> (ofs ? (32 - ofs) : 0); -- writel(val, &base->word[word].data[i + 1]); -- } --} -- --static u32 ipu_ch_param_read_field(struct ipuv3_channel *ch, u32 wbs) --{ -- struct ipu_ch_param __iomem *base = ipu_get_cpmem(ch); -- u32 bit = (wbs >> 8) % 160; -- u32 size = wbs & 0xff; -- u32 word = (wbs >> 8) / 160; -- u32 i = bit / 32; -- u32 ofs = bit % 32; -- u32 mask = (1 << size) - 1; -- u32 val = 0; -- -- pr_debug("%s %d %d %d\n", __func__, word, bit , size); -- -- val = (readl(&base->word[word].data[i]) >> ofs) & mask; -- -- if ((bit + size - 1) / 32 > i) { -- u32 tmp; -- -- tmp = readl(&base->word[word].data[i + 1]); -- tmp &= mask >> (ofs ? (32 - ofs) : 0); -- val |= tmp << (ofs ? (32 - ofs) : 0); -- } -- -- return val; --} -- --/* -- * The V4L2 spec defines packed RGB formats in memory byte order, which from -- * point of view of the IPU corresponds to little-endian words with the first -- * component in the least significant bits. -- * The DRM pixel formats and IPU internal representation are ordered the other -- * way around, with the first named component ordered at the most significant -- * bits. Further, V4L2 formats are not well defined: -- * https://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html -- * We choose the interpretation which matches GStreamer behavior. -- */ --static int v4l2_pix_fmt_to_drm_fourcc(u32 pixelformat) --{ -- switch (pixelformat) { -- case V4L2_PIX_FMT_RGB565: -- /* -- * Here we choose the 'corrected' interpretation of RGBP, a -- * little-endian 16-bit word with the red component at the most -- * significant bits: -- * g[2:0]b[4:0] r[4:0]g[5:3] <=> [16:0] R:G:B -- */ -- return DRM_FORMAT_RGB565; -- case V4L2_PIX_FMT_BGR24: -- /* B G R <=> [24:0] R:G:B */ -- return DRM_FORMAT_RGB888; -- case V4L2_PIX_FMT_RGB24: -- /* R G B <=> [24:0] B:G:R */ -- return DRM_FORMAT_BGR888; -- case V4L2_PIX_FMT_BGR32: -- /* B G R A <=> [32:0] A:B:G:R */ -- return DRM_FORMAT_XRGB8888; -- case V4L2_PIX_FMT_RGB32: -- /* R G B A <=> [32:0] A:B:G:R */ -- return DRM_FORMAT_XBGR8888; -- case V4L2_PIX_FMT_ABGR32: -- /* B G R A <=> [32:0] A:R:G:B */ -- return DRM_FORMAT_ARGB8888; -- case V4L2_PIX_FMT_XBGR32: -- /* B G R X <=> [32:0] X:R:G:B */ -- return DRM_FORMAT_XRGB8888; -- case V4L2_PIX_FMT_BGRA32: -- /* A B G R <=> [32:0] R:G:B:A */ -- return DRM_FORMAT_RGBA8888; -- case V4L2_PIX_FMT_BGRX32: -- /* X B G R <=> [32:0] R:G:B:X */ -- return DRM_FORMAT_RGBX8888; -- case V4L2_PIX_FMT_RGBA32: -- /* R G B A <=> [32:0] A:B:G:R */ -- return DRM_FORMAT_ABGR8888; -- case V4L2_PIX_FMT_RGBX32: -- /* R G B X <=> [32:0] X:B:G:R */ -- return DRM_FORMAT_XBGR8888; -- case V4L2_PIX_FMT_ARGB32: -- /* A R G B <=> [32:0] B:G:R:A */ -- return DRM_FORMAT_BGRA8888; -- case V4L2_PIX_FMT_XRGB32: -- /* X R G B <=> [32:0] B:G:R:X */ -- return DRM_FORMAT_BGRX8888; -- case V4L2_PIX_FMT_UYVY: -- return DRM_FORMAT_UYVY; -- case V4L2_PIX_FMT_YUYV: -- return DRM_FORMAT_YUYV; -- case V4L2_PIX_FMT_YUV420: -- return DRM_FORMAT_YUV420; -- case V4L2_PIX_FMT_YUV422P: -- return DRM_FORMAT_YUV422; -- case V4L2_PIX_FMT_YVU420: -- return DRM_FORMAT_YVU420; -- case V4L2_PIX_FMT_NV12: -- return DRM_FORMAT_NV12; -- case V4L2_PIX_FMT_NV16: -- return DRM_FORMAT_NV16; -- } -- -- return -EINVAL; --} -- --void ipu_cpmem_zero(struct ipuv3_channel *ch) --{ -- struct ipu_ch_param __iomem *p = ipu_get_cpmem(ch); -- void __iomem *base = p; -- int i; -- -- for (i = 0; i < sizeof(*p) / sizeof(u32); i++) -- writel(0, base + i * sizeof(u32)); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_zero); -- --void ipu_cpmem_set_resolution(struct ipuv3_channel *ch, int xres, int yres) --{ -- ipu_ch_param_write_field(ch, IPU_FIELD_FW, xres - 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_FH, yres - 1); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_resolution); -- --void ipu_cpmem_skip_odd_chroma_rows(struct ipuv3_channel *ch) --{ -- ipu_ch_param_write_field(ch, IPU_FIELD_RDRW, 1); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_skip_odd_chroma_rows); -- --void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride) --{ -- ipu_ch_param_write_field(ch, IPU_FIELD_SLY, stride - 1); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_stride); -- --void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch) --{ -- struct ipu_soc *ipu = ch->ipu; -- u32 val; -- -- if (ipu->ipu_type == IPUV3EX) -- ipu_ch_param_write_field(ch, IPU_FIELD_ID, 1); -- -- val = ipu_idmac_read(ipu, IDMAC_CHA_PRI(ch->num)); -- val |= 1 << (ch->num % 32); -- ipu_idmac_write(ipu, val, IDMAC_CHA_PRI(ch->num)); --}; --EXPORT_SYMBOL_GPL(ipu_cpmem_set_high_priority); -- --void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf) --{ -- WARN_ON_ONCE(buf & 0x7); -- -- if (bufnum) -- ipu_ch_param_write_field(ch, IPU_FIELD_EBA1, buf >> 3); -- else -- ipu_ch_param_write_field(ch, IPU_FIELD_EBA0, buf >> 3); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer); -- --void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) --{ -- WARN_ON_ONCE((u_off & 0x7) || (v_off & 0x7)); -- -- ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_off / 8); -- ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_off / 8); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -- --void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, -- u32 pixelformat) --{ -- u32 ilo, sly, sluv; -- -- if (stride < 0) { -- stride = -stride; -- ilo = 0x100000 - (stride / 8); -- } else { -- ilo = stride / 8; -- } -- -- sly = (stride * 2) - 1; -- -- switch (pixelformat) { -- case V4L2_PIX_FMT_YUV420: -- case V4L2_PIX_FMT_YVU420: -- sluv = stride / 2 - 1; -- break; -- case V4L2_PIX_FMT_NV12: -- sluv = stride - 1; -- break; -- case V4L2_PIX_FMT_YUV422P: -- sluv = stride - 1; -- break; -- case V4L2_PIX_FMT_NV16: -- sluv = stride * 2 - 1; -- break; -- default: -- sluv = 0; -- break; -- } -- -- ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); -- ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); -- if (sluv) -- ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); --}; --EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); -- --void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id) --{ -- id &= 0x3; -- ipu_ch_param_write_field(ch, IPU_FIELD_ID, id); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_axi_id); -- --int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch) --{ -- return ipu_ch_param_read_field(ch, IPU_FIELD_NPB) + 1; --} --EXPORT_SYMBOL_GPL(ipu_cpmem_get_burstsize); -- --void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize) --{ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, burstsize - 1); --}; --EXPORT_SYMBOL_GPL(ipu_cpmem_set_burstsize); -- --void ipu_cpmem_set_block_mode(struct ipuv3_channel *ch) --{ -- ipu_ch_param_write_field(ch, IPU_FIELD_BM, 1); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_block_mode); -- --void ipu_cpmem_set_rotation(struct ipuv3_channel *ch, -- enum ipu_rotate_mode rot) --{ -- u32 temp_rot = bitrev8(rot) >> 5; -- -- ipu_ch_param_write_field(ch, IPU_FIELD_ROT_HF_VF, temp_rot); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_rotation); -- --int ipu_cpmem_set_format_rgb(struct ipuv3_channel *ch, -- const struct ipu_rgb *rgb) --{ -- int bpp = 0, npb = 0, ro, go, bo, to; -- -- ro = rgb->bits_per_pixel - rgb->red.length - rgb->red.offset; -- go = rgb->bits_per_pixel - rgb->green.length - rgb->green.offset; -- bo = rgb->bits_per_pixel - rgb->blue.length - rgb->blue.offset; -- to = rgb->bits_per_pixel - rgb->transp.length - rgb->transp.offset; -- -- ipu_ch_param_write_field(ch, IPU_FIELD_WID0, rgb->red.length - 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_OFS0, ro); -- ipu_ch_param_write_field(ch, IPU_FIELD_WID1, rgb->green.length - 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_OFS1, go); -- ipu_ch_param_write_field(ch, IPU_FIELD_WID2, rgb->blue.length - 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_OFS2, bo); -- -- if (rgb->transp.length) { -- ipu_ch_param_write_field(ch, IPU_FIELD_WID3, -- rgb->transp.length - 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_OFS3, to); -- } else { -- ipu_ch_param_write_field(ch, IPU_FIELD_WID3, 7); -- ipu_ch_param_write_field(ch, IPU_FIELD_OFS3, -- rgb->bits_per_pixel); -- } -- -- switch (rgb->bits_per_pixel) { -- case 32: -- bpp = 0; -- npb = 15; -- break; -- case 24: -- bpp = 1; -- npb = 19; -- break; -- case 16: -- bpp = 3; -- npb = 31; -- break; -- case 8: -- bpp = 5; -- npb = 63; -- break; -- default: -- return -EINVAL; -- } -- ipu_ch_param_write_field(ch, IPU_FIELD_BPP, bpp); -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, npb); -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 7); /* rgb mode */ -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_rgb); -- --int ipu_cpmem_set_format_passthrough(struct ipuv3_channel *ch, int width) --{ -- int bpp = 0, npb = 0; -- -- switch (width) { -- case 32: -- bpp = 0; -- npb = 15; -- break; -- case 24: -- bpp = 1; -- npb = 19; -- break; -- case 16: -- bpp = 3; -- npb = 31; -- break; -- case 8: -- bpp = 5; -- npb = 63; -- break; -- default: -- return -EINVAL; -- } -- -- ipu_ch_param_write_field(ch, IPU_FIELD_BPP, bpp); -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, npb); -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 6); /* raw mode */ -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_passthrough); -- --void ipu_cpmem_set_yuv_interleaved(struct ipuv3_channel *ch, u32 pixel_format) --{ -- switch (pixel_format) { -- case V4L2_PIX_FMT_UYVY: -- ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0xA);/* pix fmt */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */ -- break; -- case V4L2_PIX_FMT_YUYV: -- ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0x8);/* pix fmt */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */ -- break; -- } --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_interleaved); -- --void ipu_cpmem_set_yuv_planar_full(struct ipuv3_channel *ch, -- unsigned int uv_stride, -- unsigned int u_offset, unsigned int v_offset) --{ -- WARN_ON_ONCE((u_offset & 0x7) || (v_offset & 0x7)); -- -- ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, uv_stride - 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_offset / 8); -- ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_offset / 8); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar_full); -- --static const struct ipu_rgb def_xrgb_32 = { -- .red = { .offset = 16, .length = 8, }, -- .green = { .offset = 8, .length = 8, }, -- .blue = { .offset = 0, .length = 8, }, -- .transp = { .offset = 24, .length = 8, }, -- .bits_per_pixel = 32, --}; -- --static const struct ipu_rgb def_xbgr_32 = { -- .red = { .offset = 0, .length = 8, }, -- .green = { .offset = 8, .length = 8, }, -- .blue = { .offset = 16, .length = 8, }, -- .transp = { .offset = 24, .length = 8, }, -- .bits_per_pixel = 32, --}; -- --static const struct ipu_rgb def_rgbx_32 = { -- .red = { .offset = 24, .length = 8, }, -- .green = { .offset = 16, .length = 8, }, -- .blue = { .offset = 8, .length = 8, }, -- .transp = { .offset = 0, .length = 8, }, -- .bits_per_pixel = 32, --}; -- --static const struct ipu_rgb def_bgrx_32 = { -- .red = { .offset = 8, .length = 8, }, -- .green = { .offset = 16, .length = 8, }, -- .blue = { .offset = 24, .length = 8, }, -- .transp = { .offset = 0, .length = 8, }, -- .bits_per_pixel = 32, --}; -- --static const struct ipu_rgb def_rgb_24 = { -- .red = { .offset = 16, .length = 8, }, -- .green = { .offset = 8, .length = 8, }, -- .blue = { .offset = 0, .length = 8, }, -- .transp = { .offset = 0, .length = 0, }, -- .bits_per_pixel = 24, --}; -- --static const struct ipu_rgb def_bgr_24 = { -- .red = { .offset = 0, .length = 8, }, -- .green = { .offset = 8, .length = 8, }, -- .blue = { .offset = 16, .length = 8, }, -- .transp = { .offset = 0, .length = 0, }, -- .bits_per_pixel = 24, --}; -- --static const struct ipu_rgb def_rgb_16 = { -- .red = { .offset = 11, .length = 5, }, -- .green = { .offset = 5, .length = 6, }, -- .blue = { .offset = 0, .length = 5, }, -- .transp = { .offset = 0, .length = 0, }, -- .bits_per_pixel = 16, --}; -- --static const struct ipu_rgb def_bgr_16 = { -- .red = { .offset = 0, .length = 5, }, -- .green = { .offset = 5, .length = 6, }, -- .blue = { .offset = 11, .length = 5, }, -- .transp = { .offset = 0, .length = 0, }, -- .bits_per_pixel = 16, --}; -- --static const struct ipu_rgb def_argb_16 = { -- .red = { .offset = 10, .length = 5, }, -- .green = { .offset = 5, .length = 5, }, -- .blue = { .offset = 0, .length = 5, }, -- .transp = { .offset = 15, .length = 1, }, -- .bits_per_pixel = 16, --}; -- --static const struct ipu_rgb def_argb_16_4444 = { -- .red = { .offset = 8, .length = 4, }, -- .green = { .offset = 4, .length = 4, }, -- .blue = { .offset = 0, .length = 4, }, -- .transp = { .offset = 12, .length = 4, }, -- .bits_per_pixel = 16, --}; -- --static const struct ipu_rgb def_abgr_16 = { -- .red = { .offset = 0, .length = 5, }, -- .green = { .offset = 5, .length = 5, }, -- .blue = { .offset = 10, .length = 5, }, -- .transp = { .offset = 15, .length = 1, }, -- .bits_per_pixel = 16, --}; -- --static const struct ipu_rgb def_rgba_16 = { -- .red = { .offset = 11, .length = 5, }, -- .green = { .offset = 6, .length = 5, }, -- .blue = { .offset = 1, .length = 5, }, -- .transp = { .offset = 0, .length = 1, }, -- .bits_per_pixel = 16, --}; -- --static const struct ipu_rgb def_bgra_16 = { -- .red = { .offset = 1, .length = 5, }, -- .green = { .offset = 6, .length = 5, }, -- .blue = { .offset = 11, .length = 5, }, -- .transp = { .offset = 0, .length = 1, }, -- .bits_per_pixel = 16, --}; -- --#define Y_OFFSET(pix, x, y) ((x) + pix->width * (y)) --#define U_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -- (pix->width * ((y) / 2) / 2) + (x) / 2) --#define V_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -- (pix->width * pix->height / 4) + \ -- (pix->width * ((y) / 2) / 2) + (x) / 2) --#define U2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -- (pix->width * (y) / 2) + (x) / 2) --#define V2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -- (pix->width * pix->height / 2) + \ -- (pix->width * (y) / 2) + (x) / 2) --#define UV_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -- (pix->width * ((y) / 2)) + (x)) --#define UV2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -- (pix->width * y) + (x)) -- --#define NUM_ALPHA_CHANNELS 7 -- --/* See Table 37-12. Alpha channels mapping. */ --static int ipu_channel_albm(int ch_num) --{ -- switch (ch_num) { -- case IPUV3_CHANNEL_G_MEM_IC_PRP_VF: return 0; -- case IPUV3_CHANNEL_G_MEM_IC_PP: return 1; -- case IPUV3_CHANNEL_MEM_FG_SYNC: return 2; -- case IPUV3_CHANNEL_MEM_FG_ASYNC: return 3; -- case IPUV3_CHANNEL_MEM_BG_SYNC: return 4; -- case IPUV3_CHANNEL_MEM_BG_ASYNC: return 5; -- case IPUV3_CHANNEL_MEM_VDI_PLANE1_COMB: return 6; -- default: -- return -EINVAL; -- } --} -- --static void ipu_cpmem_set_separate_alpha(struct ipuv3_channel *ch) --{ -- struct ipu_soc *ipu = ch->ipu; -- int albm; -- u32 val; -- -- albm = ipu_channel_albm(ch->num); -- if (albm < 0) -- return; -- -- ipu_ch_param_write_field(ch, IPU_FIELD_ALU, 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_ALBM, albm); -- ipu_ch_param_write_field(ch, IPU_FIELD_CRE, 1); -- -- val = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA); -- val |= BIT(ch->num); -- ipu_idmac_write(ipu, val, IDMAC_SEP_ALPHA); --} -- --int ipu_cpmem_set_fmt(struct ipuv3_channel *ch, u32 drm_fourcc) --{ -- switch (drm_fourcc) { -- case DRM_FORMAT_YUV420: -- case DRM_FORMAT_YVU420: -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 2); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_YUV422: -- case DRM_FORMAT_YVU422: -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 1); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_YUV444: -- case DRM_FORMAT_YVU444: -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_NV12: -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 4); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_NV16: -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 3); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_UYVY: -- /* bits/pixel */ -- ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0xA); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_YUYV: -- /* bits/pixel */ -- ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0x8); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_ABGR8888: -- case DRM_FORMAT_XBGR8888: -- ipu_cpmem_set_format_rgb(ch, &def_xbgr_32); -- break; -- case DRM_FORMAT_ARGB8888: -- case DRM_FORMAT_XRGB8888: -- ipu_cpmem_set_format_rgb(ch, &def_xrgb_32); -- break; -- case DRM_FORMAT_RGBA8888: -- case DRM_FORMAT_RGBX8888: -- case DRM_FORMAT_RGBX8888_A8: -- ipu_cpmem_set_format_rgb(ch, &def_rgbx_32); -- break; -- case DRM_FORMAT_BGRA8888: -- case DRM_FORMAT_BGRX8888: -- case DRM_FORMAT_BGRX8888_A8: -- ipu_cpmem_set_format_rgb(ch, &def_bgrx_32); -- break; -- case DRM_FORMAT_BGR888: -- case DRM_FORMAT_BGR888_A8: -- ipu_cpmem_set_format_rgb(ch, &def_bgr_24); -- break; -- case DRM_FORMAT_RGB888: -- case DRM_FORMAT_RGB888_A8: -- ipu_cpmem_set_format_rgb(ch, &def_rgb_24); -- break; -- case DRM_FORMAT_RGB565: -- case DRM_FORMAT_RGB565_A8: -- ipu_cpmem_set_format_rgb(ch, &def_rgb_16); -- break; -- case DRM_FORMAT_BGR565: -- case DRM_FORMAT_BGR565_A8: -- ipu_cpmem_set_format_rgb(ch, &def_bgr_16); -- break; -- case DRM_FORMAT_ARGB1555: -- ipu_cpmem_set_format_rgb(ch, &def_argb_16); -- break; -- case DRM_FORMAT_ABGR1555: -- ipu_cpmem_set_format_rgb(ch, &def_abgr_16); -- break; -- case DRM_FORMAT_RGBA5551: -- ipu_cpmem_set_format_rgb(ch, &def_rgba_16); -- break; -- case DRM_FORMAT_BGRA5551: -- ipu_cpmem_set_format_rgb(ch, &def_bgra_16); -- break; -- case DRM_FORMAT_ARGB4444: -- ipu_cpmem_set_format_rgb(ch, &def_argb_16_4444); -- break; -- default: -- return -EINVAL; -- } -- -- switch (drm_fourcc) { -- case DRM_FORMAT_RGB565_A8: -- case DRM_FORMAT_BGR565_A8: -- case DRM_FORMAT_RGB888_A8: -- case DRM_FORMAT_BGR888_A8: -- case DRM_FORMAT_RGBX8888_A8: -- case DRM_FORMAT_BGRX8888_A8: -- ipu_ch_param_write_field(ch, IPU_FIELD_WID3, 7); -- ipu_cpmem_set_separate_alpha(ch); -- break; -- default: -- break; -- } -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt); -- --int ipu_cpmem_set_image(struct ipuv3_channel *ch, struct ipu_image *image) --{ -- struct v4l2_pix_format *pix = &image->pix; -- int offset, u_offset, v_offset; -- int ret = 0; -- -- pr_debug("%s: resolution: %dx%d stride: %d\n", -- __func__, pix->width, pix->height, -- pix->bytesperline); -- -- ipu_cpmem_set_resolution(ch, image->rect.width, image->rect.height); -- ipu_cpmem_set_stride(ch, pix->bytesperline); -- -- ipu_cpmem_set_fmt(ch, v4l2_pix_fmt_to_drm_fourcc(pix->pixelformat)); -- -- switch (pix->pixelformat) { -- case V4L2_PIX_FMT_YUV420: -- offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -- u_offset = image->u_offset ? -- image->u_offset : U_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- v_offset = image->v_offset ? -- image->v_offset : V_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- -- ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2, -- u_offset, v_offset); -- break; -- case V4L2_PIX_FMT_YVU420: -- offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -- u_offset = image->u_offset ? -- image->u_offset : V_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- v_offset = image->v_offset ? -- image->v_offset : U_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- -- ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2, -- u_offset, v_offset); -- break; -- case V4L2_PIX_FMT_YUV422P: -- offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -- u_offset = image->u_offset ? -- image->u_offset : U2_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- v_offset = image->v_offset ? -- image->v_offset : V2_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- -- ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2, -- u_offset, v_offset); -- break; -- case V4L2_PIX_FMT_NV12: -- offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -- u_offset = image->u_offset ? -- image->u_offset : UV_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- v_offset = image->v_offset ? image->v_offset : 0; -- -- ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline, -- u_offset, v_offset); -- break; -- case V4L2_PIX_FMT_NV16: -- offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -- u_offset = image->u_offset ? -- image->u_offset : UV2_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- v_offset = image->v_offset ? image->v_offset : 0; -- -- ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline, -- u_offset, v_offset); -- break; -- case V4L2_PIX_FMT_UYVY: -- case V4L2_PIX_FMT_YUYV: -- case V4L2_PIX_FMT_RGB565: -- offset = image->rect.left * 2 + -- image->rect.top * pix->bytesperline; -- break; -- case V4L2_PIX_FMT_RGB32: -- case V4L2_PIX_FMT_BGR32: -- case V4L2_PIX_FMT_ABGR32: -- case V4L2_PIX_FMT_XBGR32: -- case V4L2_PIX_FMT_BGRA32: -- case V4L2_PIX_FMT_BGRX32: -- case V4L2_PIX_FMT_RGBA32: -- case V4L2_PIX_FMT_RGBX32: -- case V4L2_PIX_FMT_ARGB32: -- case V4L2_PIX_FMT_XRGB32: -- offset = image->rect.left * 4 + -- image->rect.top * pix->bytesperline; -- break; -- case V4L2_PIX_FMT_RGB24: -- case V4L2_PIX_FMT_BGR24: -- offset = image->rect.left * 3 + -- image->rect.top * pix->bytesperline; -- break; -- case V4L2_PIX_FMT_SBGGR8: -- case V4L2_PIX_FMT_SGBRG8: -- case V4L2_PIX_FMT_SGRBG8: -- case V4L2_PIX_FMT_SRGGB8: -- case V4L2_PIX_FMT_GREY: -- offset = image->rect.left + image->rect.top * pix->bytesperline; -- break; -- case V4L2_PIX_FMT_SBGGR16: -- case V4L2_PIX_FMT_SGBRG16: -- case V4L2_PIX_FMT_SGRBG16: -- case V4L2_PIX_FMT_SRGGB16: -- case V4L2_PIX_FMT_Y16: -- offset = image->rect.left * 2 + -- image->rect.top * pix->bytesperline; -- break; -- default: -- /* This should not happen */ -- WARN_ON(1); -- offset = 0; -- ret = -EINVAL; -- } -- -- ipu_cpmem_set_buffer(ch, 0, image->phys0 + offset); -- ipu_cpmem_set_buffer(ch, 1, image->phys1 + offset); -- -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_image); -- --void ipu_cpmem_dump(struct ipuv3_channel *ch) --{ -- struct ipu_ch_param __iomem *p = ipu_get_cpmem(ch); -- struct ipu_soc *ipu = ch->ipu; -- int chno = ch->num; -- -- dev_dbg(ipu->dev, "ch %d word 0 - %08X %08X %08X %08X %08X\n", chno, -- readl(&p->word[0].data[0]), -- readl(&p->word[0].data[1]), -- readl(&p->word[0].data[2]), -- readl(&p->word[0].data[3]), -- readl(&p->word[0].data[4])); -- dev_dbg(ipu->dev, "ch %d word 1 - %08X %08X %08X %08X %08X\n", chno, -- readl(&p->word[1].data[0]), -- readl(&p->word[1].data[1]), -- readl(&p->word[1].data[2]), -- readl(&p->word[1].data[3]), -- readl(&p->word[1].data[4])); -- dev_dbg(ipu->dev, "PFS 0x%x, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_PFS)); -- dev_dbg(ipu->dev, "BPP 0x%x, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_BPP)); -- dev_dbg(ipu->dev, "NPB 0x%x\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_NPB)); -- -- dev_dbg(ipu->dev, "FW %d, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_FW)); -- dev_dbg(ipu->dev, "FH %d, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_FH)); -- dev_dbg(ipu->dev, "EBA0 0x%x\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_EBA0) << 3); -- dev_dbg(ipu->dev, "EBA1 0x%x\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_EBA1) << 3); -- dev_dbg(ipu->dev, "Stride %d\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_SL)); -- dev_dbg(ipu->dev, "scan_order %d\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_SO)); -- dev_dbg(ipu->dev, "uv_stride %d\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_SLUV)); -- dev_dbg(ipu->dev, "u_offset 0x%x\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_UBO) << 3); -- dev_dbg(ipu->dev, "v_offset 0x%x\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_VBO) << 3); -- -- dev_dbg(ipu->dev, "Width0 %d+1, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_WID0)); -- dev_dbg(ipu->dev, "Width1 %d+1, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_WID1)); -- dev_dbg(ipu->dev, "Width2 %d+1, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_WID2)); -- dev_dbg(ipu->dev, "Width3 %d+1, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_WID3)); -- dev_dbg(ipu->dev, "Offset0 %d, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_OFS0)); -- dev_dbg(ipu->dev, "Offset1 %d, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_OFS1)); -- dev_dbg(ipu->dev, "Offset2 %d, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_OFS2)); -- dev_dbg(ipu->dev, "Offset3 %d\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_OFS3)); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_dump); -- --int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base) --{ -- struct ipu_cpmem *cpmem; -- -- cpmem = devm_kzalloc(dev, sizeof(*cpmem), GFP_KERNEL); -- if (!cpmem) -- return -ENOMEM; -- -- ipu->cpmem_priv = cpmem; -- -- spin_lock_init(&cpmem->lock); -- cpmem->base = devm_ioremap(dev, base, SZ_128K); -- if (!cpmem->base) -- return -ENOMEM; -- -- dev_dbg(dev, "CPMEM base: 0x%08lx remapped to %p\n", -- base, cpmem->base); -- cpmem->ipu = ipu; -- -- return 0; --} -- --void ipu_cpmem_exit(struct ipu_soc *ipu) --{ --} ---- a/drivers/gpu/imx/ipu-v3/ipu-csi.c -+++ /dev/null -@@ -1,821 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (C) 2012-2014 Mentor Graphics Inc. -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#include <linux/export.h> --#include <linux/module.h> --#include <linux/types.h> --#include <linux/errno.h> --#include <linux/delay.h> --#include <linux/io.h> --#include <linux/err.h> --#include <linux/platform_device.h> --#include <linux/videodev2.h> --#include <uapi/linux/v4l2-mediabus.h> --#include <linux/clk.h> --#include <linux/clk-provider.h> --#include <linux/clkdev.h> -- --#include "ipu-prv.h" -- --struct ipu_csi { -- void __iomem *base; -- int id; -- u32 module; -- struct clk *clk_ipu; /* IPU bus clock */ -- spinlock_t lock; -- bool inuse; -- struct ipu_soc *ipu; --}; -- --/* CSI Register Offsets */ --#define CSI_SENS_CONF 0x0000 --#define CSI_SENS_FRM_SIZE 0x0004 --#define CSI_ACT_FRM_SIZE 0x0008 --#define CSI_OUT_FRM_CTRL 0x000c --#define CSI_TST_CTRL 0x0010 --#define CSI_CCIR_CODE_1 0x0014 --#define CSI_CCIR_CODE_2 0x0018 --#define CSI_CCIR_CODE_3 0x001c --#define CSI_MIPI_DI 0x0020 --#define CSI_SKIP 0x0024 --#define CSI_CPD_CTRL 0x0028 --#define CSI_CPD_RC(n) (0x002c + ((n)*4)) --#define CSI_CPD_RS(n) (0x004c + ((n)*4)) --#define CSI_CPD_GRC(n) (0x005c + ((n)*4)) --#define CSI_CPD_GRS(n) (0x007c + ((n)*4)) --#define CSI_CPD_GBC(n) (0x008c + ((n)*4)) --#define CSI_CPD_GBS(n) (0x00Ac + ((n)*4)) --#define CSI_CPD_BC(n) (0x00Bc + ((n)*4)) --#define CSI_CPD_BS(n) (0x00Dc + ((n)*4)) --#define CSI_CPD_OFFSET1 0x00ec --#define CSI_CPD_OFFSET2 0x00f0 -- --/* CSI Register Fields */ --#define CSI_SENS_CONF_DATA_FMT_SHIFT 8 --#define CSI_SENS_CONF_DATA_FMT_MASK 0x00000700 --#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 0L --#define CSI_SENS_CONF_DATA_FMT_YUV422_YUYV 1L --#define CSI_SENS_CONF_DATA_FMT_YUV422_UYVY 2L --#define CSI_SENS_CONF_DATA_FMT_BAYER 3L --#define CSI_SENS_CONF_DATA_FMT_RGB565 4L --#define CSI_SENS_CONF_DATA_FMT_RGB555 5L --#define CSI_SENS_CONF_DATA_FMT_RGB444 6L --#define CSI_SENS_CONF_DATA_FMT_JPEG 7L -- --#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0 --#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1 --#define CSI_SENS_CONF_DATA_POL_SHIFT 2 --#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3 --#define CSI_SENS_CONF_SENS_PRTCL_MASK 0x00000070 --#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4 --#define CSI_SENS_CONF_PACK_TIGHT_SHIFT 7 --#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 11 --#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15 --#define CSI_SENS_CONF_DIVRATIO_SHIFT 16 -- --#define CSI_SENS_CONF_DIVRATIO_MASK 0x00ff0000 --#define CSI_SENS_CONF_DATA_DEST_SHIFT 24 --#define CSI_SENS_CONF_DATA_DEST_MASK 0x07000000 --#define CSI_SENS_CONF_JPEG8_EN_SHIFT 27 --#define CSI_SENS_CONF_JPEG_EN_SHIFT 28 --#define CSI_SENS_CONF_FORCE_EOF_SHIFT 29 --#define CSI_SENS_CONF_DATA_EN_POL_SHIFT 31 -- --#define CSI_DATA_DEST_IC 2 --#define CSI_DATA_DEST_IDMAC 4 -- --#define CSI_CCIR_ERR_DET_EN 0x01000000 --#define CSI_HORI_DOWNSIZE_EN 0x80000000 --#define CSI_VERT_DOWNSIZE_EN 0x40000000 --#define CSI_TEST_GEN_MODE_EN 0x01000000 -- --#define CSI_HSC_MASK 0x1fff0000 --#define CSI_HSC_SHIFT 16 --#define CSI_VSC_MASK 0x00000fff --#define CSI_VSC_SHIFT 0 -- --#define CSI_TEST_GEN_R_MASK 0x000000ff --#define CSI_TEST_GEN_R_SHIFT 0 --#define CSI_TEST_GEN_G_MASK 0x0000ff00 --#define CSI_TEST_GEN_G_SHIFT 8 --#define CSI_TEST_GEN_B_MASK 0x00ff0000 --#define CSI_TEST_GEN_B_SHIFT 16 -- --#define CSI_MAX_RATIO_SKIP_SMFC_MASK 0x00000007 --#define CSI_MAX_RATIO_SKIP_SMFC_SHIFT 0 --#define CSI_SKIP_SMFC_MASK 0x000000f8 --#define CSI_SKIP_SMFC_SHIFT 3 --#define CSI_ID_2_SKIP_MASK 0x00000300 --#define CSI_ID_2_SKIP_SHIFT 8 -- --#define CSI_COLOR_FIRST_ROW_MASK 0x00000002 --#define CSI_COLOR_FIRST_COMP_MASK 0x00000001 -- --/* MIPI CSI-2 data types */ --#define MIPI_DT_YUV420 0x18 /* YYY.../UYVY.... */ --#define MIPI_DT_YUV420_LEGACY 0x1a /* UYY.../VYY... */ --#define MIPI_DT_YUV422 0x1e /* UYVY... */ --#define MIPI_DT_RGB444 0x20 --#define MIPI_DT_RGB555 0x21 --#define MIPI_DT_RGB565 0x22 --#define MIPI_DT_RGB666 0x23 --#define MIPI_DT_RGB888 0x24 --#define MIPI_DT_RAW6 0x28 --#define MIPI_DT_RAW7 0x29 --#define MIPI_DT_RAW8 0x2a --#define MIPI_DT_RAW10 0x2b --#define MIPI_DT_RAW12 0x2c --#define MIPI_DT_RAW14 0x2d -- --/* -- * Bitfield of CSI bus signal polarities and modes. -- */ --struct ipu_csi_bus_config { -- unsigned data_width:4; -- unsigned clk_mode:3; -- unsigned ext_vsync:1; -- unsigned vsync_pol:1; -- unsigned hsync_pol:1; -- unsigned pixclk_pol:1; -- unsigned data_pol:1; -- unsigned sens_clksrc:1; -- unsigned pack_tight:1; -- unsigned force_eof:1; -- unsigned data_en_pol:1; -- -- unsigned data_fmt; -- unsigned mipi_dt; --}; -- --/* -- * Enumeration of CSI data bus widths. -- */ --enum ipu_csi_data_width { -- IPU_CSI_DATA_WIDTH_4 = 0, -- IPU_CSI_DATA_WIDTH_8 = 1, -- IPU_CSI_DATA_WIDTH_10 = 3, -- IPU_CSI_DATA_WIDTH_12 = 5, -- IPU_CSI_DATA_WIDTH_16 = 9, --}; -- --/* -- * Enumeration of CSI clock modes. -- */ --enum ipu_csi_clk_mode { -- IPU_CSI_CLK_MODE_GATED_CLK, -- IPU_CSI_CLK_MODE_NONGATED_CLK, -- IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE, -- IPU_CSI_CLK_MODE_CCIR656_INTERLACED, -- IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR, -- IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR, -- IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR, -- IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR, --}; -- --static inline u32 ipu_csi_read(struct ipu_csi *csi, unsigned offset) --{ -- return readl(csi->base + offset); --} -- --static inline void ipu_csi_write(struct ipu_csi *csi, u32 value, -- unsigned offset) --{ -- writel(value, csi->base + offset); --} -- --/* -- * Set mclk division ratio for generating test mode mclk. Only used -- * for test generator. -- */ --static int ipu_csi_set_testgen_mclk(struct ipu_csi *csi, u32 pixel_clk, -- u32 ipu_clk) --{ -- u32 temp; -- int div_ratio; -- -- div_ratio = (ipu_clk / pixel_clk) - 1; -- -- if (div_ratio > 0xFF || div_ratio < 0) { -- dev_err(csi->ipu->dev, -- "value of pixel_clk extends normal range\n"); -- return -EINVAL; -- } -- -- temp = ipu_csi_read(csi, CSI_SENS_CONF); -- temp &= ~CSI_SENS_CONF_DIVRATIO_MASK; -- ipu_csi_write(csi, temp | (div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT), -- CSI_SENS_CONF); -- -- return 0; --} -- --/* -- * Find the CSI data format and data width for the given V4L2 media -- * bus pixel format code. -- */ --static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, -- enum v4l2_mbus_type mbus_type) --{ -- switch (mbus_code) { -- case MEDIA_BUS_FMT_BGR565_2X8_BE: -- case MEDIA_BUS_FMT_BGR565_2X8_LE: -- case MEDIA_BUS_FMT_RGB565_2X8_BE: -- case MEDIA_BUS_FMT_RGB565_2X8_LE: -- if (mbus_type == V4L2_MBUS_CSI2_DPHY) -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565; -- else -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -- cfg->mipi_dt = MIPI_DT_RGB565; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE: -- case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB444; -- cfg->mipi_dt = MIPI_DT_RGB444; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: -- case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555; -- cfg->mipi_dt = MIPI_DT_RGB555; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_RGB888_1X24: -- case MEDIA_BUS_FMT_BGR888_1X24: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444; -- cfg->mipi_dt = MIPI_DT_RGB888; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_UYVY8_2X8: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY; -- cfg->mipi_dt = MIPI_DT_YUV422; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_YUYV8_2X8: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV; -- cfg->mipi_dt = MIPI_DT_YUV422; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_UYVY8_1X16: -- case MEDIA_BUS_FMT_YUYV8_1X16: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -- cfg->mipi_dt = MIPI_DT_YUV422; -- cfg->data_width = IPU_CSI_DATA_WIDTH_16; -- break; -- case MEDIA_BUS_FMT_SBGGR8_1X8: -- case MEDIA_BUS_FMT_SGBRG8_1X8: -- case MEDIA_BUS_FMT_SGRBG8_1X8: -- case MEDIA_BUS_FMT_SRGGB8_1X8: -- case MEDIA_BUS_FMT_Y8_1X8: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -- cfg->mipi_dt = MIPI_DT_RAW8; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8: -- case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8: -- case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8: -- case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8: -- case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE: -- case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE: -- case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE: -- case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -- cfg->mipi_dt = MIPI_DT_RAW10; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_SBGGR10_1X10: -- case MEDIA_BUS_FMT_SGBRG10_1X10: -- case MEDIA_BUS_FMT_SGRBG10_1X10: -- case MEDIA_BUS_FMT_SRGGB10_1X10: -- case MEDIA_BUS_FMT_Y10_1X10: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -- cfg->mipi_dt = MIPI_DT_RAW10; -- cfg->data_width = IPU_CSI_DATA_WIDTH_10; -- break; -- case MEDIA_BUS_FMT_SBGGR12_1X12: -- case MEDIA_BUS_FMT_SGBRG12_1X12: -- case MEDIA_BUS_FMT_SGRBG12_1X12: -- case MEDIA_BUS_FMT_SRGGB12_1X12: -- case MEDIA_BUS_FMT_Y12_1X12: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -- cfg->mipi_dt = MIPI_DT_RAW12; -- cfg->data_width = IPU_CSI_DATA_WIDTH_12; -- break; -- case MEDIA_BUS_FMT_JPEG_1X8: -- /* TODO */ -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_JPEG; -- cfg->mipi_dt = MIPI_DT_RAW8; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- default: -- return -EINVAL; -- } -- -- return 0; --} -- --/* translate alternate field mode based on given standard */ --static inline enum v4l2_field --ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) --{ -- return (field != V4L2_FIELD_ALTERNATE) ? field : -- ((std & V4L2_STD_525_60) ? -- V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); --} -- --/* -- * Fill a CSI bus config struct from mbus_config and mbus_framefmt. -- */ --static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, -- const struct v4l2_mbus_config *mbus_cfg, -- const struct v4l2_mbus_framefmt *mbus_fmt) --{ -- int ret; -- -- memset(csicfg, 0, sizeof(*csicfg)); -- -- ret = mbus_code_to_bus_cfg(csicfg, mbus_fmt->code, mbus_cfg->type); -- if (ret < 0) -- return ret; -- -- switch (mbus_cfg->type) { -- case V4L2_MBUS_PARALLEL: -- csicfg->ext_vsync = 1; -- csicfg->vsync_pol = (mbus_cfg->flags & -- V4L2_MBUS_VSYNC_ACTIVE_LOW) ? 1 : 0; -- csicfg->hsync_pol = (mbus_cfg->flags & -- V4L2_MBUS_HSYNC_ACTIVE_LOW) ? 1 : 0; -- csicfg->pixclk_pol = (mbus_cfg->flags & -- V4L2_MBUS_PCLK_SAMPLE_FALLING) ? 1 : 0; -- csicfg->clk_mode = IPU_CSI_CLK_MODE_GATED_CLK; -- break; -- case V4L2_MBUS_BT656: -- csicfg->ext_vsync = 0; -- if (V4L2_FIELD_HAS_BOTH(mbus_fmt->field) || -- mbus_fmt->field == V4L2_FIELD_ALTERNATE) -- csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED; -- else -- csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE; -- break; -- case V4L2_MBUS_CSI2_DPHY: -- /* -- * MIPI CSI-2 requires non gated clock mode, all other -- * parameters are not applicable for MIPI CSI-2 bus. -- */ -- csicfg->clk_mode = IPU_CSI_CLK_MODE_NONGATED_CLK; -- break; -- default: -- /* will never get here, keep compiler quiet */ -- break; -- } -- -- return 0; --} -- --static int --ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, -- const struct v4l2_mbus_framefmt *infmt, -- const struct v4l2_mbus_framefmt *outfmt, -- v4l2_std_id std) --{ -- enum v4l2_field infield, outfield; -- bool swap_fields; -- -- /* get translated field type of input and output */ -- infield = ipu_csi_translate_field(infmt->field, std); -- outfield = ipu_csi_translate_field(outfmt->field, std); -- -- /* -- * Write the H-V-F codes the CSI will match against the -- * incoming data for start/end of active and blanking -- * field intervals. If input and output field types are -- * sequential but not the same (one is SEQ_BT and the other -- * is SEQ_TB), swap the F-bit so that the CSI will capture -- * field 1 lines before field 0 lines. -- */ -- swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && -- V4L2_FIELD_IS_SEQUENTIAL(outfield) && -- infield != outfield); -- -- if (!swap_fields) { -- /* -- * Field0BlankEnd = 110, Field0BlankStart = 010 -- * Field0ActiveEnd = 100, Field0ActiveStart = 000 -- * Field1BlankEnd = 111, Field1BlankStart = 011 -- * Field1ActiveEnd = 101, Field1ActiveStart = 001 -- */ -- ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, -- CSI_CCIR_CODE_1); -- ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); -- } else { -- dev_dbg(csi->ipu->dev, "capture field swap\n"); -- -- /* same as above but with F-bit inverted */ -- ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, -- CSI_CCIR_CODE_1); -- ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); -- } -- -- ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); -- -- return 0; --} -- -- --int ipu_csi_init_interface(struct ipu_csi *csi, -- const struct v4l2_mbus_config *mbus_cfg, -- const struct v4l2_mbus_framefmt *infmt, -- const struct v4l2_mbus_framefmt *outfmt) --{ -- struct ipu_csi_bus_config cfg; -- unsigned long flags; -- u32 width, height, data = 0; -- v4l2_std_id std; -- int ret; -- -- ret = fill_csi_bus_cfg(&cfg, mbus_cfg, infmt); -- if (ret < 0) -- return ret; -- -- /* set default sensor frame width and height */ -- width = infmt->width; -- height = infmt->height; -- if (infmt->field == V4L2_FIELD_ALTERNATE) -- height *= 2; -- -- /* Set the CSI_SENS_CONF register remaining fields */ -- data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT | -- cfg.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT | -- cfg.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT | -- cfg.vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT | -- cfg.hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT | -- cfg.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT | -- cfg.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT | -- cfg.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT | -- cfg.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT | -- cfg.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT | -- cfg.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- ipu_csi_write(csi, data, CSI_SENS_CONF); -- -- /* Set CCIR registers */ -- -- switch (cfg.clk_mode) { -- case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: -- ipu_csi_write(csi, 0x40030, CSI_CCIR_CODE_1); -- ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); -- break; -- case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: -- if (width == 720 && height == 480) { -- std = V4L2_STD_NTSC; -- height = 525; -- } else if (width == 720 && height == 576) { -- std = V4L2_STD_PAL; -- height = 625; -- } else { -- dev_err(csi->ipu->dev, -- "Unsupported interlaced video mode\n"); -- ret = -EINVAL; -- goto out_unlock; -- } -- -- ret = ipu_csi_set_bt_interlaced_codes(csi, infmt, outfmt, std); -- if (ret) -- goto out_unlock; -- break; -- case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: -- case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: -- case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: -- case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: -- ipu_csi_write(csi, 0x40030 | CSI_CCIR_ERR_DET_EN, -- CSI_CCIR_CODE_1); -- ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); -- break; -- case IPU_CSI_CLK_MODE_GATED_CLK: -- case IPU_CSI_CLK_MODE_NONGATED_CLK: -- ipu_csi_write(csi, 0, CSI_CCIR_CODE_1); -- break; -- } -- -- /* Setup sensor frame size */ -- ipu_csi_write(csi, (width - 1) | ((height - 1) << 16), -- CSI_SENS_FRM_SIZE); -- -- dev_dbg(csi->ipu->dev, "CSI_SENS_CONF = 0x%08X\n", -- ipu_csi_read(csi, CSI_SENS_CONF)); -- dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n", -- ipu_csi_read(csi, CSI_ACT_FRM_SIZE)); -- --out_unlock: -- spin_unlock_irqrestore(&csi->lock, flags); -- -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_csi_init_interface); -- --bool ipu_csi_is_interlaced(struct ipu_csi *csi) --{ -- unsigned long flags; -- u32 sensor_protocol; -- -- spin_lock_irqsave(&csi->lock, flags); -- sensor_protocol = -- (ipu_csi_read(csi, CSI_SENS_CONF) & -- CSI_SENS_CONF_SENS_PRTCL_MASK) >> -- CSI_SENS_CONF_SENS_PRTCL_SHIFT; -- spin_unlock_irqrestore(&csi->lock, flags); -- -- switch (sensor_protocol) { -- case IPU_CSI_CLK_MODE_GATED_CLK: -- case IPU_CSI_CLK_MODE_NONGATED_CLK: -- case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: -- case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: -- case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: -- return false; -- case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: -- case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: -- case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: -- return true; -- default: -- dev_err(csi->ipu->dev, -- "CSI %d sensor protocol unsupported\n", csi->id); -- return false; -- } --} --EXPORT_SYMBOL_GPL(ipu_csi_is_interlaced); -- --void ipu_csi_get_window(struct ipu_csi *csi, struct v4l2_rect *w) --{ -- unsigned long flags; -- u32 reg; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- reg = ipu_csi_read(csi, CSI_ACT_FRM_SIZE); -- w->width = (reg & 0xFFFF) + 1; -- w->height = (reg >> 16 & 0xFFFF) + 1; -- -- reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); -- w->left = (reg & CSI_HSC_MASK) >> CSI_HSC_SHIFT; -- w->top = (reg & CSI_VSC_MASK) >> CSI_VSC_SHIFT; -- -- spin_unlock_irqrestore(&csi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_csi_get_window); -- --void ipu_csi_set_window(struct ipu_csi *csi, struct v4l2_rect *w) --{ -- unsigned long flags; -- u32 reg; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- ipu_csi_write(csi, (w->width - 1) | ((w->height - 1) << 16), -- CSI_ACT_FRM_SIZE); -- -- reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); -- reg &= ~(CSI_HSC_MASK | CSI_VSC_MASK); -- reg |= ((w->top << CSI_VSC_SHIFT) | (w->left << CSI_HSC_SHIFT)); -- ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL); -- -- spin_unlock_irqrestore(&csi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_csi_set_window); -- --void ipu_csi_set_downsize(struct ipu_csi *csi, bool horiz, bool vert) --{ -- unsigned long flags; -- u32 reg; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); -- reg &= ~(CSI_HORI_DOWNSIZE_EN | CSI_VERT_DOWNSIZE_EN); -- reg |= (horiz ? CSI_HORI_DOWNSIZE_EN : 0) | -- (vert ? CSI_VERT_DOWNSIZE_EN : 0); -- ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL); -- -- spin_unlock_irqrestore(&csi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_csi_set_downsize); -- --void ipu_csi_set_test_generator(struct ipu_csi *csi, bool active, -- u32 r_value, u32 g_value, u32 b_value, -- u32 pix_clk) --{ -- unsigned long flags; -- u32 ipu_clk = clk_get_rate(csi->clk_ipu); -- u32 temp; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- temp = ipu_csi_read(csi, CSI_TST_CTRL); -- -- if (!active) { -- temp &= ~CSI_TEST_GEN_MODE_EN; -- ipu_csi_write(csi, temp, CSI_TST_CTRL); -- } else { -- /* Set sensb_mclk div_ratio */ -- ipu_csi_set_testgen_mclk(csi, pix_clk, ipu_clk); -- -- temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK | -- CSI_TEST_GEN_B_MASK); -- temp |= CSI_TEST_GEN_MODE_EN; -- temp |= (r_value << CSI_TEST_GEN_R_SHIFT) | -- (g_value << CSI_TEST_GEN_G_SHIFT) | -- (b_value << CSI_TEST_GEN_B_SHIFT); -- ipu_csi_write(csi, temp, CSI_TST_CTRL); -- } -- -- spin_unlock_irqrestore(&csi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_csi_set_test_generator); -- --int ipu_csi_set_mipi_datatype(struct ipu_csi *csi, u32 vc, -- struct v4l2_mbus_framefmt *mbus_fmt) --{ -- struct ipu_csi_bus_config cfg; -- unsigned long flags; -- u32 temp; -- int ret; -- -- if (vc > 3) -- return -EINVAL; -- -- ret = mbus_code_to_bus_cfg(&cfg, mbus_fmt->code, V4L2_MBUS_CSI2_DPHY); -- if (ret < 0) -- return ret; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- temp = ipu_csi_read(csi, CSI_MIPI_DI); -- temp &= ~(0xff << (vc * 8)); -- temp |= (cfg.mipi_dt << (vc * 8)); -- ipu_csi_write(csi, temp, CSI_MIPI_DI); -- -- spin_unlock_irqrestore(&csi->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_csi_set_mipi_datatype); -- --int ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip, -- u32 max_ratio, u32 id) --{ -- unsigned long flags; -- u32 temp; -- -- if (max_ratio > 5 || id > 3) -- return -EINVAL; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- temp = ipu_csi_read(csi, CSI_SKIP); -- temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK | -- CSI_SKIP_SMFC_MASK); -- temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) | -- (id << CSI_ID_2_SKIP_SHIFT) | -- (skip << CSI_SKIP_SMFC_SHIFT); -- ipu_csi_write(csi, temp, CSI_SKIP); -- -- spin_unlock_irqrestore(&csi->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_csi_set_skip_smfc); -- --int ipu_csi_set_dest(struct ipu_csi *csi, enum ipu_csi_dest csi_dest) --{ -- unsigned long flags; -- u32 csi_sens_conf, dest; -- -- if (csi_dest == IPU_CSI_DEST_IDMAC) -- dest = CSI_DATA_DEST_IDMAC; -- else -- dest = CSI_DATA_DEST_IC; /* IC or VDIC */ -- -- spin_lock_irqsave(&csi->lock, flags); -- -- csi_sens_conf = ipu_csi_read(csi, CSI_SENS_CONF); -- csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK; -- csi_sens_conf |= (dest << CSI_SENS_CONF_DATA_DEST_SHIFT); -- ipu_csi_write(csi, csi_sens_conf, CSI_SENS_CONF); -- -- spin_unlock_irqrestore(&csi->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_csi_set_dest); -- --int ipu_csi_enable(struct ipu_csi *csi) --{ -- ipu_module_enable(csi->ipu, csi->module); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_csi_enable); -- --int ipu_csi_disable(struct ipu_csi *csi) --{ -- ipu_module_disable(csi->ipu, csi->module); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_csi_disable); -- --struct ipu_csi *ipu_csi_get(struct ipu_soc *ipu, int id) --{ -- unsigned long flags; -- struct ipu_csi *csi, *ret; -- -- if (id > 1) -- return ERR_PTR(-EINVAL); -- -- csi = ipu->csi_priv[id]; -- ret = csi; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- if (csi->inuse) { -- ret = ERR_PTR(-EBUSY); -- goto unlock; -- } -- -- csi->inuse = true; --unlock: -- spin_unlock_irqrestore(&csi->lock, flags); -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_csi_get); -- --void ipu_csi_put(struct ipu_csi *csi) --{ -- unsigned long flags; -- -- spin_lock_irqsave(&csi->lock, flags); -- csi->inuse = false; -- spin_unlock_irqrestore(&csi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_csi_put); -- --int ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id, -- unsigned long base, u32 module, struct clk *clk_ipu) --{ -- struct ipu_csi *csi; -- -- if (id > 1) -- return -ENODEV; -- -- csi = devm_kzalloc(dev, sizeof(*csi), GFP_KERNEL); -- if (!csi) -- return -ENOMEM; -- -- ipu->csi_priv[id] = csi; -- -- spin_lock_init(&csi->lock); -- csi->module = module; -- csi->id = id; -- csi->clk_ipu = clk_ipu; -- csi->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!csi->base) -- return -ENOMEM; -- -- dev_dbg(dev, "CSI%d base: 0x%08lx remapped to %p\n", -- id, base, csi->base); -- csi->ipu = ipu; -- -- return 0; --} -- --void ipu_csi_exit(struct ipu_soc *ipu, int id) --{ --} -- --void ipu_csi_dump(struct ipu_csi *csi) --{ -- dev_dbg(csi->ipu->dev, "CSI_SENS_CONF: %08x\n", -- ipu_csi_read(csi, CSI_SENS_CONF)); -- dev_dbg(csi->ipu->dev, "CSI_SENS_FRM_SIZE: %08x\n", -- ipu_csi_read(csi, CSI_SENS_FRM_SIZE)); -- dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE: %08x\n", -- ipu_csi_read(csi, CSI_ACT_FRM_SIZE)); -- dev_dbg(csi->ipu->dev, "CSI_OUT_FRM_CTRL: %08x\n", -- ipu_csi_read(csi, CSI_OUT_FRM_CTRL)); -- dev_dbg(csi->ipu->dev, "CSI_TST_CTRL: %08x\n", -- ipu_csi_read(csi, CSI_TST_CTRL)); -- dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_1: %08x\n", -- ipu_csi_read(csi, CSI_CCIR_CODE_1)); -- dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_2: %08x\n", -- ipu_csi_read(csi, CSI_CCIR_CODE_2)); -- dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_3: %08x\n", -- ipu_csi_read(csi, CSI_CCIR_CODE_3)); -- dev_dbg(csi->ipu->dev, "CSI_MIPI_DI: %08x\n", -- ipu_csi_read(csi, CSI_MIPI_DI)); -- dev_dbg(csi->ipu->dev, "CSI_SKIP: %08x\n", -- ipu_csi_read(csi, CSI_SKIP)); --} --EXPORT_SYMBOL_GPL(ipu_csi_dump); ---- a/drivers/gpu/imx/ipu-v3/ipu-dc.c -+++ /dev/null -@@ -1,420 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ -- --#include <linux/export.h> --#include <linux/module.h> --#include <linux/types.h> --#include <linux/errno.h> --#include <linux/delay.h> --#include <linux/interrupt.h> --#include <linux/io.h> -- --#include <video/imx-ipu-v3.h> --#include "ipu-prv.h" -- --#define DC_MAP_CONF_PTR(n) (0x108 + ((n) & ~0x1) * 2) --#define DC_MAP_CONF_VAL(n) (0x144 + ((n) & ~0x1) * 2) -- --#define DC_EVT_NF 0 --#define DC_EVT_NL 1 --#define DC_EVT_EOF 2 --#define DC_EVT_NFIELD 3 --#define DC_EVT_EOL 4 --#define DC_EVT_EOFIELD 5 --#define DC_EVT_NEW_ADDR 6 --#define DC_EVT_NEW_CHAN 7 --#define DC_EVT_NEW_DATA 8 -- --#define DC_EVT_NEW_ADDR_W_0 0 --#define DC_EVT_NEW_ADDR_W_1 1 --#define DC_EVT_NEW_CHAN_W_0 2 --#define DC_EVT_NEW_CHAN_W_1 3 --#define DC_EVT_NEW_DATA_W_0 4 --#define DC_EVT_NEW_DATA_W_1 5 --#define DC_EVT_NEW_ADDR_R_0 6 --#define DC_EVT_NEW_ADDR_R_1 7 --#define DC_EVT_NEW_CHAN_R_0 8 --#define DC_EVT_NEW_CHAN_R_1 9 --#define DC_EVT_NEW_DATA_R_0 10 --#define DC_EVT_NEW_DATA_R_1 11 -- --#define DC_WR_CH_CONF 0x0 --#define DC_WR_CH_ADDR 0x4 --#define DC_RL_CH(evt) (8 + ((evt) & ~0x1) * 2) -- --#define DC_GEN 0xd4 --#define DC_DISP_CONF1(disp) (0xd8 + (disp) * 4) --#define DC_DISP_CONF2(disp) (0xe8 + (disp) * 4) --#define DC_STAT 0x1c8 -- --#define WROD(lf) (0x18 | ((lf) << 1)) --#define WRG 0x01 --#define WCLK 0xc9 -- --#define SYNC_WAVE 0 --#define NULL_WAVE (-1) -- --#define DC_GEN_SYNC_1_6_SYNC (2 << 1) --#define DC_GEN_SYNC_PRIORITY_1 (1 << 7) -- --#define DC_WR_CH_CONF_WORD_SIZE_8 (0 << 0) --#define DC_WR_CH_CONF_WORD_SIZE_16 (1 << 0) --#define DC_WR_CH_CONF_WORD_SIZE_24 (2 << 0) --#define DC_WR_CH_CONF_WORD_SIZE_32 (3 << 0) --#define DC_WR_CH_CONF_DISP_ID_PARALLEL(i) (((i) & 0x1) << 3) --#define DC_WR_CH_CONF_DISP_ID_SERIAL (2 << 3) --#define DC_WR_CH_CONF_DISP_ID_ASYNC (3 << 4) --#define DC_WR_CH_CONF_FIELD_MODE (1 << 9) --#define DC_WR_CH_CONF_PROG_TYPE_NORMAL (4 << 5) --#define DC_WR_CH_CONF_PROG_TYPE_MASK (7 << 5) --#define DC_WR_CH_CONF_PROG_DI_ID (1 << 2) --#define DC_WR_CH_CONF_PROG_DISP_ID(i) (((i) & 0x1) << 3) -- --#define IPU_DC_NUM_CHANNELS 10 -- --struct ipu_dc_priv; -- --enum ipu_dc_map { -- IPU_DC_MAP_RGB24, -- IPU_DC_MAP_RGB565, -- IPU_DC_MAP_GBR24, /* TVEv2 */ -- IPU_DC_MAP_BGR666, -- IPU_DC_MAP_LVDS666, -- IPU_DC_MAP_BGR24, --}; -- --struct ipu_dc { -- /* The display interface number assigned to this dc channel */ -- unsigned int di; -- void __iomem *base; -- struct ipu_dc_priv *priv; -- int chno; -- bool in_use; --}; -- --struct ipu_dc_priv { -- void __iomem *dc_reg; -- void __iomem *dc_tmpl_reg; -- struct ipu_soc *ipu; -- struct device *dev; -- struct ipu_dc channels[IPU_DC_NUM_CHANNELS]; -- struct mutex mutex; -- struct completion comp; -- int use_count; --}; -- --static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority) --{ -- u32 reg; -- -- reg = readl(dc->base + DC_RL_CH(event)); -- reg &= ~(0xffff << (16 * (event & 0x1))); -- reg |= ((addr << 8) | priority) << (16 * (event & 0x1)); -- writel(reg, dc->base + DC_RL_CH(event)); --} -- --static void dc_write_tmpl(struct ipu_dc *dc, int word, u32 opcode, u32 operand, -- int map, int wave, int glue, int sync, int stop) --{ -- struct ipu_dc_priv *priv = dc->priv; -- u32 reg1, reg2; -- -- if (opcode == WCLK) { -- reg1 = (operand << 20) & 0xfff00000; -- reg2 = operand >> 12 | opcode << 1 | stop << 9; -- } else if (opcode == WRG) { -- reg1 = sync | glue << 4 | ++wave << 11 | ((operand << 15) & 0xffff8000); -- reg2 = operand >> 17 | opcode << 7 | stop << 9; -- } else { -- reg1 = sync | glue << 4 | ++wave << 11 | ++map << 15 | ((operand << 20) & 0xfff00000); -- reg2 = operand >> 12 | opcode << 4 | stop << 9; -- } -- writel(reg1, priv->dc_tmpl_reg + word * 8); -- writel(reg2, priv->dc_tmpl_reg + word * 8 + 4); --} -- --static int ipu_bus_format_to_map(u32 fmt) --{ -- switch (fmt) { -- default: -- WARN_ON(1); -- /* fall-through */ -- case MEDIA_BUS_FMT_RGB888_1X24: -- return IPU_DC_MAP_RGB24; -- case MEDIA_BUS_FMT_RGB565_1X16: -- return IPU_DC_MAP_RGB565; -- case MEDIA_BUS_FMT_GBR888_1X24: -- return IPU_DC_MAP_GBR24; -- case MEDIA_BUS_FMT_RGB666_1X18: -- return IPU_DC_MAP_BGR666; -- case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: -- return IPU_DC_MAP_LVDS666; -- case MEDIA_BUS_FMT_BGR888_1X24: -- return IPU_DC_MAP_BGR24; -- } --} -- --int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, -- u32 bus_format, u32 width) --{ -- struct ipu_dc_priv *priv = dc->priv; -- int addr, sync; -- u32 reg = 0; -- int map; -- -- dc->di = ipu_di_get_num(di); -- -- map = ipu_bus_format_to_map(bus_format); -- -- /* -- * In interlaced mode we need more counters to create the asymmetric -- * per-field VSYNC signals. The pixel active signal synchronising DC -- * to DI moves to signal generator #6 (see ipu-di.c). In progressive -- * mode counter #5 is used. -- */ -- sync = interlaced ? 6 : 5; -- -- /* Reserve 5 microcode template words for each DI */ -- if (dc->di) -- addr = 5; -- else -- addr = 0; -- -- if (interlaced) { -- dc_link_event(dc, DC_EVT_NL, addr, 3); -- dc_link_event(dc, DC_EVT_EOL, addr, 2); -- dc_link_event(dc, DC_EVT_NEW_DATA, addr, 1); -- -- /* Init template microcode */ -- dc_write_tmpl(dc, addr, WROD(0), 0, map, SYNC_WAVE, 0, sync, 1); -- } else { -- dc_link_event(dc, DC_EVT_NL, addr + 2, 3); -- dc_link_event(dc, DC_EVT_EOL, addr + 3, 2); -- dc_link_event(dc, DC_EVT_NEW_DATA, addr + 1, 1); -- -- /* Init template microcode */ -- dc_write_tmpl(dc, addr + 2, WROD(0), 0, map, SYNC_WAVE, 8, sync, 1); -- dc_write_tmpl(dc, addr + 3, WROD(0), 0, map, SYNC_WAVE, 4, sync, 0); -- dc_write_tmpl(dc, addr + 4, WRG, 0, map, NULL_WAVE, 0, 0, 1); -- dc_write_tmpl(dc, addr + 1, WROD(0), 0, map, SYNC_WAVE, 0, sync, 1); -- } -- -- dc_link_event(dc, DC_EVT_NF, 0, 0); -- dc_link_event(dc, DC_EVT_NFIELD, 0, 0); -- dc_link_event(dc, DC_EVT_EOF, 0, 0); -- dc_link_event(dc, DC_EVT_EOFIELD, 0, 0); -- dc_link_event(dc, DC_EVT_NEW_CHAN, 0, 0); -- dc_link_event(dc, DC_EVT_NEW_ADDR, 0, 0); -- -- reg = readl(dc->base + DC_WR_CH_CONF); -- if (interlaced) -- reg |= DC_WR_CH_CONF_FIELD_MODE; -- else -- reg &= ~DC_WR_CH_CONF_FIELD_MODE; -- writel(reg, dc->base + DC_WR_CH_CONF); -- -- writel(0x0, dc->base + DC_WR_CH_ADDR); -- writel(width, priv->dc_reg + DC_DISP_CONF2(dc->di)); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dc_init_sync); -- --void ipu_dc_enable(struct ipu_soc *ipu) --{ -- struct ipu_dc_priv *priv = ipu->dc_priv; -- -- mutex_lock(&priv->mutex); -- -- if (!priv->use_count) -- ipu_module_enable(priv->ipu, IPU_CONF_DC_EN); -- -- priv->use_count++; -- -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dc_enable); -- --void ipu_dc_enable_channel(struct ipu_dc *dc) --{ -- u32 reg; -- -- reg = readl(dc->base + DC_WR_CH_CONF); -- reg |= DC_WR_CH_CONF_PROG_TYPE_NORMAL; -- writel(reg, dc->base + DC_WR_CH_CONF); --} --EXPORT_SYMBOL_GPL(ipu_dc_enable_channel); -- --void ipu_dc_disable_channel(struct ipu_dc *dc) --{ -- u32 val; -- -- val = readl(dc->base + DC_WR_CH_CONF); -- val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; -- writel(val, dc->base + DC_WR_CH_CONF); --} --EXPORT_SYMBOL_GPL(ipu_dc_disable_channel); -- --void ipu_dc_disable(struct ipu_soc *ipu) --{ -- struct ipu_dc_priv *priv = ipu->dc_priv; -- -- mutex_lock(&priv->mutex); -- -- priv->use_count--; -- if (!priv->use_count) -- ipu_module_disable(priv->ipu, IPU_CONF_DC_EN); -- -- if (priv->use_count < 0) -- priv->use_count = 0; -- -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dc_disable); -- --static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map, -- int byte_num, int offset, int mask) --{ -- int ptr = map * 3 + byte_num; -- u32 reg; -- -- reg = readl(priv->dc_reg + DC_MAP_CONF_VAL(ptr)); -- reg &= ~(0xffff << (16 * (ptr & 0x1))); -- reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1)); -- writel(reg, priv->dc_reg + DC_MAP_CONF_VAL(ptr)); -- -- reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map)); -- reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num))); -- reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num)); -- writel(reg, priv->dc_reg + DC_MAP_CONF_PTR(map)); --} -- --static void ipu_dc_map_clear(struct ipu_dc_priv *priv, int map) --{ -- u32 reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map)); -- -- writel(reg & ~(0xffff << (16 * (map & 0x1))), -- priv->dc_reg + DC_MAP_CONF_PTR(map)); --} -- --struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel) --{ -- struct ipu_dc_priv *priv = ipu->dc_priv; -- struct ipu_dc *dc; -- -- if (channel >= IPU_DC_NUM_CHANNELS) -- return ERR_PTR(-ENODEV); -- -- dc = &priv->channels[channel]; -- -- mutex_lock(&priv->mutex); -- -- if (dc->in_use) { -- mutex_unlock(&priv->mutex); -- return ERR_PTR(-EBUSY); -- } -- -- dc->in_use = true; -- -- mutex_unlock(&priv->mutex); -- -- return dc; --} --EXPORT_SYMBOL_GPL(ipu_dc_get); -- --void ipu_dc_put(struct ipu_dc *dc) --{ -- struct ipu_dc_priv *priv = dc->priv; -- -- mutex_lock(&priv->mutex); -- dc->in_use = false; -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dc_put); -- --int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, -- unsigned long base, unsigned long template_base) --{ -- struct ipu_dc_priv *priv; -- static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c, -- 0x78, 0, 0x94, 0xb4}; -- int i; -- -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- -- mutex_init(&priv->mutex); -- -- priv->dev = dev; -- priv->ipu = ipu; -- priv->dc_reg = devm_ioremap(dev, base, PAGE_SIZE); -- priv->dc_tmpl_reg = devm_ioremap(dev, template_base, PAGE_SIZE); -- if (!priv->dc_reg || !priv->dc_tmpl_reg) -- return -ENOMEM; -- -- for (i = 0; i < IPU_DC_NUM_CHANNELS; i++) { -- priv->channels[i].chno = i; -- priv->channels[i].priv = priv; -- priv->channels[i].base = priv->dc_reg + channel_offsets[i]; -- } -- -- writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) | -- DC_WR_CH_CONF_PROG_DI_ID, -- priv->channels[1].base + DC_WR_CH_CONF); -- writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(0), -- priv->channels[5].base + DC_WR_CH_CONF); -- -- writel(DC_GEN_SYNC_1_6_SYNC | DC_GEN_SYNC_PRIORITY_1, -- priv->dc_reg + DC_GEN); -- -- ipu->dc_priv = priv; -- -- dev_dbg(dev, "DC base: 0x%08lx template base: 0x%08lx\n", -- base, template_base); -- -- /* rgb24 */ -- ipu_dc_map_clear(priv, IPU_DC_MAP_RGB24); -- ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 0, 7, 0xff); /* blue */ -- ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 1, 15, 0xff); /* green */ -- ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 2, 23, 0xff); /* red */ -- -- /* rgb565 */ -- ipu_dc_map_clear(priv, IPU_DC_MAP_RGB565); -- ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 0, 4, 0xf8); /* blue */ -- ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 1, 10, 0xfc); /* green */ -- ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 2, 15, 0xf8); /* red */ -- -- /* gbr24 */ -- ipu_dc_map_clear(priv, IPU_DC_MAP_GBR24); -- ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 2, 15, 0xff); /* green */ -- ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 1, 7, 0xff); /* blue */ -- ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 0, 23, 0xff); /* red */ -- -- /* bgr666 */ -- ipu_dc_map_clear(priv, IPU_DC_MAP_BGR666); -- ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 0, 5, 0xfc); /* blue */ -- ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 1, 11, 0xfc); /* green */ -- ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 2, 17, 0xfc); /* red */ -- -- /* lvds666 */ -- ipu_dc_map_clear(priv, IPU_DC_MAP_LVDS666); -- ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 0, 5, 0xfc); /* blue */ -- ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 1, 13, 0xfc); /* green */ -- ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 2, 21, 0xfc); /* red */ -- -- /* bgr24 */ -- ipu_dc_map_clear(priv, IPU_DC_MAP_BGR24); -- ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 2, 7, 0xff); /* red */ -- ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 1, 15, 0xff); /* green */ -- ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 0, 23, 0xff); /* blue */ -- -- return 0; --} -- --void ipu_dc_exit(struct ipu_soc *ipu) --{ --} ---- a/drivers/gpu/imx/ipu-v3/ipu-di.c -+++ /dev/null -@@ -1,745 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#include <linux/export.h> --#include <linux/module.h> --#include <linux/types.h> --#include <linux/errno.h> --#include <linux/io.h> --#include <linux/err.h> --#include <linux/platform_device.h> -- --#include <video/imx-ipu-v3.h> --#include "ipu-prv.h" -- --struct ipu_di { -- void __iomem *base; -- int id; -- u32 module; -- struct clk *clk_di; /* display input clock */ -- struct clk *clk_ipu; /* IPU bus clock */ -- struct clk *clk_di_pixel; /* resulting pixel clock */ -- bool inuse; -- struct ipu_soc *ipu; --}; -- --static DEFINE_MUTEX(di_mutex); -- --struct di_sync_config { -- int run_count; -- int run_src; -- int offset_count; -- int offset_src; -- int repeat_count; -- int cnt_clr_src; -- int cnt_polarity_gen_en; -- int cnt_polarity_clr_src; -- int cnt_polarity_trigger_src; -- int cnt_up; -- int cnt_down; --}; -- --enum di_pins { -- DI_PIN11 = 0, -- DI_PIN12 = 1, -- DI_PIN13 = 2, -- DI_PIN14 = 3, -- DI_PIN15 = 4, -- DI_PIN16 = 5, -- DI_PIN17 = 6, -- DI_PIN_CS = 7, -- -- DI_PIN_SER_CLK = 0, -- DI_PIN_SER_RS = 1, --}; -- --enum di_sync_wave { -- DI_SYNC_NONE = 0, -- DI_SYNC_CLK = 1, -- DI_SYNC_INT_HSYNC = 2, -- DI_SYNC_HSYNC = 3, -- DI_SYNC_VSYNC = 4, -- DI_SYNC_DE = 6, -- -- DI_SYNC_CNT1 = 2, /* counter >= 2 only */ -- DI_SYNC_CNT4 = 5, /* counter >= 5 only */ -- DI_SYNC_CNT5 = 6, /* counter >= 6 only */ --}; -- --#define SYNC_WAVE 0 -- --#define DI_GENERAL 0x0000 --#define DI_BS_CLKGEN0 0x0004 --#define DI_BS_CLKGEN1 0x0008 --#define DI_SW_GEN0(gen) (0x000c + 4 * ((gen) - 1)) --#define DI_SW_GEN1(gen) (0x0030 + 4 * ((gen) - 1)) --#define DI_STP_REP(gen) (0x0148 + 4 * (((gen) - 1)/2)) --#define DI_SYNC_AS_GEN 0x0054 --#define DI_DW_GEN(gen) (0x0058 + 4 * (gen)) --#define DI_DW_SET(gen, set) (0x0088 + 4 * ((gen) + 0xc * (set))) --#define DI_SER_CONF 0x015c --#define DI_SSC 0x0160 --#define DI_POL 0x0164 --#define DI_AW0 0x0168 --#define DI_AW1 0x016c --#define DI_SCR_CONF 0x0170 --#define DI_STAT 0x0174 -- --#define DI_SW_GEN0_RUN_COUNT(x) ((x) << 19) --#define DI_SW_GEN0_RUN_SRC(x) ((x) << 16) --#define DI_SW_GEN0_OFFSET_COUNT(x) ((x) << 3) --#define DI_SW_GEN0_OFFSET_SRC(x) ((x) << 0) -- --#define DI_SW_GEN1_CNT_POL_GEN_EN(x) ((x) << 29) --#define DI_SW_GEN1_CNT_CLR_SRC(x) ((x) << 25) --#define DI_SW_GEN1_CNT_POL_TRIGGER_SRC(x) ((x) << 12) --#define DI_SW_GEN1_CNT_POL_CLR_SRC(x) ((x) << 9) --#define DI_SW_GEN1_CNT_DOWN(x) ((x) << 16) --#define DI_SW_GEN1_CNT_UP(x) (x) --#define DI_SW_GEN1_AUTO_RELOAD (0x10000000) -- --#define DI_DW_GEN_ACCESS_SIZE_OFFSET 24 --#define DI_DW_GEN_COMPONENT_SIZE_OFFSET 16 -- --#define DI_GEN_POLARITY_1 (1 << 0) --#define DI_GEN_POLARITY_2 (1 << 1) --#define DI_GEN_POLARITY_3 (1 << 2) --#define DI_GEN_POLARITY_4 (1 << 3) --#define DI_GEN_POLARITY_5 (1 << 4) --#define DI_GEN_POLARITY_6 (1 << 5) --#define DI_GEN_POLARITY_7 (1 << 6) --#define DI_GEN_POLARITY_8 (1 << 7) --#define DI_GEN_POLARITY_DISP_CLK (1 << 17) --#define DI_GEN_DI_CLK_EXT (1 << 20) --#define DI_GEN_DI_VSYNC_EXT (1 << 21) -- --#define DI_POL_DRDY_DATA_POLARITY (1 << 7) --#define DI_POL_DRDY_POLARITY_15 (1 << 4) -- --#define DI_VSYNC_SEL_OFFSET 13 -- --static inline u32 ipu_di_read(struct ipu_di *di, unsigned offset) --{ -- return readl(di->base + offset); --} -- --static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset) --{ -- writel(value, di->base + offset); --} -- --static void ipu_di_data_wave_config(struct ipu_di *di, -- int wave_gen, -- int access_size, int component_size) --{ -- u32 reg; -- reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) | -- (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET); -- ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); --} -- --static void ipu_di_data_pin_config(struct ipu_di *di, int wave_gen, int di_pin, -- int set, int up, int down) --{ -- u32 reg; -- -- reg = ipu_di_read(di, DI_DW_GEN(wave_gen)); -- reg &= ~(0x3 << (di_pin * 2)); -- reg |= set << (di_pin * 2); -- ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); -- -- ipu_di_write(di, (down << 16) | up, DI_DW_SET(wave_gen, set)); --} -- --static void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config, -- int start, int count) --{ -- u32 reg; -- int i; -- -- for (i = 0; i < count; i++) { -- struct di_sync_config *c = &config[i]; -- int wave_gen = start + i + 1; -- -- if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) || -- (c->repeat_count >= 0x1000) || -- (c->cnt_up >= 0x400) || -- (c->cnt_down >= 0x400)) { -- dev_err(di->ipu->dev, "DI%d counters out of range.\n", -- di->id); -- return; -- } -- -- reg = DI_SW_GEN0_RUN_COUNT(c->run_count) | -- DI_SW_GEN0_RUN_SRC(c->run_src) | -- DI_SW_GEN0_OFFSET_COUNT(c->offset_count) | -- DI_SW_GEN0_OFFSET_SRC(c->offset_src); -- ipu_di_write(di, reg, DI_SW_GEN0(wave_gen)); -- -- reg = DI_SW_GEN1_CNT_POL_GEN_EN(c->cnt_polarity_gen_en) | -- DI_SW_GEN1_CNT_CLR_SRC(c->cnt_clr_src) | -- DI_SW_GEN1_CNT_POL_TRIGGER_SRC( -- c->cnt_polarity_trigger_src) | -- DI_SW_GEN1_CNT_POL_CLR_SRC(c->cnt_polarity_clr_src) | -- DI_SW_GEN1_CNT_DOWN(c->cnt_down) | -- DI_SW_GEN1_CNT_UP(c->cnt_up); -- -- /* Enable auto reload */ -- if (c->repeat_count == 0) -- reg |= DI_SW_GEN1_AUTO_RELOAD; -- -- ipu_di_write(di, reg, DI_SW_GEN1(wave_gen)); -- -- reg = ipu_di_read(di, DI_STP_REP(wave_gen)); -- reg &= ~(0xffff << (16 * ((wave_gen - 1) & 0x1))); -- reg |= c->repeat_count << (16 * ((wave_gen - 1) & 0x1)); -- ipu_di_write(di, reg, DI_STP_REP(wave_gen)); -- } --} -- --static void ipu_di_sync_config_interlaced(struct ipu_di *di, -- struct ipu_di_signal_cfg *sig) --{ -- u32 h_total = sig->mode.hactive + sig->mode.hsync_len + -- sig->mode.hback_porch + sig->mode.hfront_porch; -- u32 v_total = sig->mode.vactive + sig->mode.vsync_len + -- sig->mode.vback_porch + sig->mode.vfront_porch; -- struct di_sync_config cfg[] = { -- { -- /* 1: internal VSYNC for each frame */ -- .run_count = v_total * 2 - 1, -- .run_src = 3, /* == counter 7 */ -- }, { -- /* PIN2: HSYNC waveform */ -- .run_count = h_total - 1, -- .run_src = DI_SYNC_CLK, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_CLK, -- .cnt_down = sig->mode.hsync_len * 2, -- }, { -- /* PIN3: VSYNC waveform */ -- .run_count = v_total - 1, -- .run_src = 4, /* == counter 7 */ -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = 4, /* == counter 7 */ -- .cnt_down = sig->mode.vsync_len * 2, -- .cnt_clr_src = DI_SYNC_CNT1, -- }, { -- /* 4: Field */ -- .run_count = v_total / 2, -- .run_src = DI_SYNC_HSYNC, -- .offset_count = h_total / 2, -- .offset_src = DI_SYNC_CLK, -- .repeat_count = 2, -- .cnt_clr_src = DI_SYNC_CNT1, -- }, { -- /* 5: Active lines */ -- .run_src = DI_SYNC_HSYNC, -- .offset_count = (sig->mode.vsync_len + -- sig->mode.vback_porch) / 2, -- .offset_src = DI_SYNC_HSYNC, -- .repeat_count = sig->mode.vactive / 2, -- .cnt_clr_src = DI_SYNC_CNT4, -- }, { -- /* 6: Active pixel, referenced by DC */ -- .run_src = DI_SYNC_CLK, -- .offset_count = sig->mode.hsync_len + -- sig->mode.hback_porch, -- .offset_src = DI_SYNC_CLK, -- .repeat_count = sig->mode.hactive, -- .cnt_clr_src = DI_SYNC_CNT5, -- }, { -- /* 7: Half line HSYNC */ -- .run_count = h_total / 2 - 1, -- .run_src = DI_SYNC_CLK, -- } -- }; -- -- ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); -- -- ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF); --} -- --static void ipu_di_sync_config_noninterlaced(struct ipu_di *di, -- struct ipu_di_signal_cfg *sig, int div) --{ -- u32 h_total = sig->mode.hactive + sig->mode.hsync_len + -- sig->mode.hback_porch + sig->mode.hfront_porch; -- u32 v_total = sig->mode.vactive + sig->mode.vsync_len + -- sig->mode.vback_porch + sig->mode.vfront_porch; -- struct di_sync_config cfg[] = { -- { -- /* 1: INT_HSYNC */ -- .run_count = h_total - 1, -- .run_src = DI_SYNC_CLK, -- } , { -- /* PIN2: HSYNC */ -- .run_count = h_total - 1, -- .run_src = DI_SYNC_CLK, -- .offset_count = div * sig->v_to_h_sync, -- .offset_src = DI_SYNC_CLK, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_CLK, -- .cnt_down = sig->mode.hsync_len * 2, -- } , { -- /* PIN3: VSYNC */ -- .run_count = v_total - 1, -- .run_src = DI_SYNC_INT_HSYNC, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, -- .cnt_down = sig->mode.vsync_len * 2, -- } , { -- /* 4: Line Active */ -- .run_src = DI_SYNC_HSYNC, -- .offset_count = sig->mode.vsync_len + -- sig->mode.vback_porch, -- .offset_src = DI_SYNC_HSYNC, -- .repeat_count = sig->mode.vactive, -- .cnt_clr_src = DI_SYNC_VSYNC, -- } , { -- /* 5: Pixel Active, referenced by DC */ -- .run_src = DI_SYNC_CLK, -- .offset_count = sig->mode.hsync_len + -- sig->mode.hback_porch, -- .offset_src = DI_SYNC_CLK, -- .repeat_count = sig->mode.hactive, -- .cnt_clr_src = 5, /* Line Active */ -- } , { -- /* unused */ -- } , { -- /* unused */ -- } , { -- /* unused */ -- } , { -- /* unused */ -- }, -- }; -- /* can't use #7 and #8 for line active and pixel active counters */ -- struct di_sync_config cfg_vga[] = { -- { -- /* 1: INT_HSYNC */ -- .run_count = h_total - 1, -- .run_src = DI_SYNC_CLK, -- } , { -- /* 2: VSYNC */ -- .run_count = v_total - 1, -- .run_src = DI_SYNC_INT_HSYNC, -- } , { -- /* 3: Line Active */ -- .run_src = DI_SYNC_INT_HSYNC, -- .offset_count = sig->mode.vsync_len + -- sig->mode.vback_porch, -- .offset_src = DI_SYNC_INT_HSYNC, -- .repeat_count = sig->mode.vactive, -- .cnt_clr_src = 3 /* VSYNC */, -- } , { -- /* PIN4: HSYNC for VGA via TVEv2 on TQ MBa53 */ -- .run_count = h_total - 1, -- .run_src = DI_SYNC_CLK, -- .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ -- .offset_src = DI_SYNC_CLK, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_CLK, -- .cnt_down = sig->mode.hsync_len * 2, -- } , { -- /* 5: Pixel Active signal to DC */ -- .run_src = DI_SYNC_CLK, -- .offset_count = sig->mode.hsync_len + -- sig->mode.hback_porch, -- .offset_src = DI_SYNC_CLK, -- .repeat_count = sig->mode.hactive, -- .cnt_clr_src = 4, /* Line Active */ -- } , { -- /* PIN6: VSYNC for VGA via TVEv2 on TQ MBa53 */ -- .run_count = v_total - 1, -- .run_src = DI_SYNC_INT_HSYNC, -- .offset_count = 1, /* magic value from Freescale TVE driver */ -- .offset_src = DI_SYNC_INT_HSYNC, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, -- .cnt_down = sig->mode.vsync_len * 2, -- } , { -- /* PIN4: HSYNC for VGA via TVEv2 on i.MX53-QSB */ -- .run_count = h_total - 1, -- .run_src = DI_SYNC_CLK, -- .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ -- .offset_src = DI_SYNC_CLK, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_CLK, -- .cnt_down = sig->mode.hsync_len * 2, -- } , { -- /* PIN6: VSYNC for VGA via TVEv2 on i.MX53-QSB */ -- .run_count = v_total - 1, -- .run_src = DI_SYNC_INT_HSYNC, -- .offset_count = 1, /* magic value from Freescale TVE driver */ -- .offset_src = DI_SYNC_INT_HSYNC, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, -- .cnt_down = sig->mode.vsync_len * 2, -- } , { -- /* unused */ -- }, -- }; -- -- ipu_di_write(di, v_total - 1, DI_SCR_CONF); -- if (sig->hsync_pin == 2 && sig->vsync_pin == 3) -- ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); -- else -- ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga)); --} -- --static void ipu_di_config_clock(struct ipu_di *di, -- const struct ipu_di_signal_cfg *sig) --{ -- struct clk *clk; -- unsigned clkgen0; -- uint32_t val; -- -- if (sig->clkflags & IPU_DI_CLKMODE_EXT) { -- /* -- * CLKMODE_EXT means we must use the DI clock: this is -- * needed for things like LVDS which needs to feed the -- * DI and LDB with the same pixel clock. -- */ -- clk = di->clk_di; -- -- if (sig->clkflags & IPU_DI_CLKMODE_SYNC) { -- /* -- * CLKMODE_SYNC means that we want the DI to be -- * clocked at the same rate as the parent clock. -- * This is needed (eg) for LDB which needs to be -- * fed with the same pixel clock. We assume that -- * the LDB clock has already been set correctly. -- */ -- clkgen0 = 1 << 4; -- } else { -- /* -- * We can use the divider. We should really have -- * a flag here indicating whether the bridge can -- * cope with a fractional divider or not. For the -- * time being, let's go for simplicitly and -- * reliability. -- */ -- unsigned long in_rate; -- unsigned div; -- -- clk_set_rate(clk, sig->mode.pixelclock); -- -- in_rate = clk_get_rate(clk); -- div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock); -- div = clamp(div, 1U, 255U); -- -- clkgen0 = div << 4; -- } -- } else { -- /* -- * For other interfaces, we can arbitarily select between -- * the DI specific clock and the internal IPU clock. See -- * DI_GENERAL bit 20. We select the IPU clock if it can -- * give us a clock rate within 1% of the requested frequency, -- * otherwise we use the DI clock. -- */ -- unsigned long rate, clkrate; -- unsigned div, error; -- -- clkrate = clk_get_rate(di->clk_ipu); -- div = DIV_ROUND_CLOSEST(clkrate, sig->mode.pixelclock); -- div = clamp(div, 1U, 255U); -- rate = clkrate / div; -- -- error = rate / (sig->mode.pixelclock / 1000); -- -- dev_dbg(di->ipu->dev, " IPU clock can give %lu with divider %u, error %d.%u%%\n", -- rate, div, (signed)(error - 1000) / 10, error % 10); -- -- /* Allow a 1% error */ -- if (error < 1010 && error >= 990) { -- clk = di->clk_ipu; -- -- clkgen0 = div << 4; -- } else { -- unsigned long in_rate; -- unsigned div; -- -- clk = di->clk_di; -- -- clk_set_rate(clk, sig->mode.pixelclock); -- -- in_rate = clk_get_rate(clk); -- div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock); -- div = clamp(div, 1U, 255U); -- -- clkgen0 = div << 4; -- } -- } -- -- di->clk_di_pixel = clk; -- -- /* Set the divider */ -- ipu_di_write(di, clkgen0, DI_BS_CLKGEN0); -- -- /* -- * Set the high/low periods. Bits 24:16 give us the falling edge, -- * and bits 8:0 give the rising edge. LSB is fraction, and is -- * based on the divider above. We want a 50% duty cycle, so set -- * the falling edge to be half the divider. -- */ -- ipu_di_write(di, (clkgen0 >> 4) << 16, DI_BS_CLKGEN1); -- -- /* Finally select the input clock */ -- val = ipu_di_read(di, DI_GENERAL) & ~DI_GEN_DI_CLK_EXT; -- if (clk == di->clk_di) -- val |= DI_GEN_DI_CLK_EXT; -- ipu_di_write(di, val, DI_GENERAL); -- -- dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, %luHz\n", -- sig->mode.pixelclock, -- clk_get_rate(di->clk_ipu), -- clk_get_rate(di->clk_di), -- clk == di->clk_di ? "DI" : "IPU", -- clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4)); --} -- --/* -- * This function is called to adjust a video mode to IPU restrictions. -- * It is meant to be called from drm crtc mode_fixup() methods. -- */ --int ipu_di_adjust_videomode(struct ipu_di *di, struct videomode *mode) --{ -- u32 diff; -- -- if (mode->vfront_porch >= 2) -- return 0; -- -- diff = 2 - mode->vfront_porch; -- -- if (mode->vback_porch >= diff) { -- mode->vfront_porch = 2; -- mode->vback_porch -= diff; -- } else if (mode->vsync_len > diff) { -- mode->vfront_porch = 2; -- mode->vsync_len = mode->vsync_len - diff; -- } else { -- dev_warn(di->ipu->dev, "failed to adjust videomode\n"); -- return -EINVAL; -- } -- -- dev_dbg(di->ipu->dev, "videomode adapted for IPU restrictions\n"); -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_di_adjust_videomode); -- --static u32 ipu_di_gen_polarity(int pin) --{ -- switch (pin) { -- case 1: -- return DI_GEN_POLARITY_1; -- case 2: -- return DI_GEN_POLARITY_2; -- case 3: -- return DI_GEN_POLARITY_3; -- case 4: -- return DI_GEN_POLARITY_4; -- case 5: -- return DI_GEN_POLARITY_5; -- case 6: -- return DI_GEN_POLARITY_6; -- case 7: -- return DI_GEN_POLARITY_7; -- case 8: -- return DI_GEN_POLARITY_8; -- } -- return 0; --} -- --int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) --{ -- u32 reg; -- u32 di_gen, vsync_cnt; -- u32 div; -- -- dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n", -- di->id, sig->mode.hactive, sig->mode.vactive); -- -- dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n", -- clk_get_rate(di->clk_ipu), -- clk_get_rate(di->clk_di), -- sig->mode.pixelclock); -- -- mutex_lock(&di_mutex); -- -- ipu_di_config_clock(di, sig); -- -- div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff; -- div = div / 16; /* Now divider is integer portion */ -- -- /* Setup pixel clock timing */ -- /* Down time is half of period */ -- ipu_di_write(di, (div << 16), DI_BS_CLKGEN1); -- -- ipu_di_data_wave_config(di, SYNC_WAVE, div - 1, div - 1); -- ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div * 2); -- -- di_gen = ipu_di_read(di, DI_GENERAL) & DI_GEN_DI_CLK_EXT; -- di_gen |= DI_GEN_DI_VSYNC_EXT; -- -- if (sig->mode.flags & DISPLAY_FLAGS_INTERLACED) { -- ipu_di_sync_config_interlaced(di, sig); -- -- /* set y_sel = 1 */ -- di_gen |= 0x10000000; -- -- vsync_cnt = 3; -- } else { -- ipu_di_sync_config_noninterlaced(di, sig, div); -- -- vsync_cnt = 3; -- if (di->id == 1) -- /* -- * TODO: change only for TVEv2, parallel display -- * uses pin 2 / 3 -- */ -- if (!(sig->hsync_pin == 2 && sig->vsync_pin == 3)) -- vsync_cnt = 6; -- } -- -- if (sig->mode.flags & DISPLAY_FLAGS_HSYNC_HIGH) -- di_gen |= ipu_di_gen_polarity(sig->hsync_pin); -- if (sig->mode.flags & DISPLAY_FLAGS_VSYNC_HIGH) -- di_gen |= ipu_di_gen_polarity(sig->vsync_pin); -- -- if (sig->clk_pol) -- di_gen |= DI_GEN_POLARITY_DISP_CLK; -- -- ipu_di_write(di, di_gen, DI_GENERAL); -- -- ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002, -- DI_SYNC_AS_GEN); -- -- reg = ipu_di_read(di, DI_POL); -- reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15); -- -- if (sig->enable_pol) -- reg |= DI_POL_DRDY_POLARITY_15; -- if (sig->data_pol) -- reg |= DI_POL_DRDY_DATA_POLARITY; -- -- ipu_di_write(di, reg, DI_POL); -- -- mutex_unlock(&di_mutex); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel); -- --int ipu_di_enable(struct ipu_di *di) --{ -- int ret; -- -- WARN_ON(IS_ERR(di->clk_di_pixel)); -- -- ret = clk_prepare_enable(di->clk_di_pixel); -- if (ret) -- return ret; -- -- ipu_module_enable(di->ipu, di->module); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_di_enable); -- --int ipu_di_disable(struct ipu_di *di) --{ -- WARN_ON(IS_ERR(di->clk_di_pixel)); -- -- ipu_module_disable(di->ipu, di->module); -- -- clk_disable_unprepare(di->clk_di_pixel); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_di_disable); -- --int ipu_di_get_num(struct ipu_di *di) --{ -- return di->id; --} --EXPORT_SYMBOL_GPL(ipu_di_get_num); -- --static DEFINE_MUTEX(ipu_di_lock); -- --struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp) --{ -- struct ipu_di *di; -- -- if (disp > 1) -- return ERR_PTR(-EINVAL); -- -- di = ipu->di_priv[disp]; -- -- mutex_lock(&ipu_di_lock); -- -- if (di->inuse) { -- di = ERR_PTR(-EBUSY); -- goto out; -- } -- -- di->inuse = true; --out: -- mutex_unlock(&ipu_di_lock); -- -- return di; --} --EXPORT_SYMBOL_GPL(ipu_di_get); -- --void ipu_di_put(struct ipu_di *di) --{ -- mutex_lock(&ipu_di_lock); -- -- di->inuse = false; -- -- mutex_unlock(&ipu_di_lock); --} --EXPORT_SYMBOL_GPL(ipu_di_put); -- --int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, -- unsigned long base, -- u32 module, struct clk *clk_ipu) --{ -- struct ipu_di *di; -- -- if (id > 1) -- return -ENODEV; -- -- di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL); -- if (!di) -- return -ENOMEM; -- -- ipu->di_priv[id] = di; -- -- di->clk_di = devm_clk_get(dev, id ? "di1" : "di0"); -- if (IS_ERR(di->clk_di)) -- return PTR_ERR(di->clk_di); -- -- di->module = module; -- di->id = id; -- di->clk_ipu = clk_ipu; -- di->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!di->base) -- return -ENOMEM; -- -- ipu_di_write(di, 0x10, DI_BS_CLKGEN0); -- -- dev_dbg(dev, "DI%d base: 0x%08lx remapped to %p\n", -- id, base, di->base); -- di->inuse = false; -- di->ipu = ipu; -- -- return 0; --} -- --void ipu_di_exit(struct ipu_soc *ipu, int id) --{ --} ---- a/drivers/gpu/imx/ipu-v3/ipu-dmfc.c -+++ /dev/null -@@ -1,214 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#include <linux/export.h> --#include <linux/types.h> --#include <linux/errno.h> --#include <linux/io.h> -- --#include <video/imx-ipu-v3.h> --#include "ipu-prv.h" -- --#define DMFC_RD_CHAN 0x0000 --#define DMFC_WR_CHAN 0x0004 --#define DMFC_WR_CHAN_DEF 0x0008 --#define DMFC_DP_CHAN 0x000c --#define DMFC_DP_CHAN_DEF 0x0010 --#define DMFC_GENERAL1 0x0014 --#define DMFC_GENERAL2 0x0018 --#define DMFC_IC_CTRL 0x001c --#define DMFC_WR_CHAN_ALT 0x0020 --#define DMFC_WR_CHAN_DEF_ALT 0x0024 --#define DMFC_DP_CHAN_ALT 0x0028 --#define DMFC_DP_CHAN_DEF_ALT 0x002c --#define DMFC_GENERAL1_ALT 0x0030 --#define DMFC_STAT 0x0034 -- --#define DMFC_WR_CHAN_1_28 0 --#define DMFC_WR_CHAN_2_41 8 --#define DMFC_WR_CHAN_1C_42 16 --#define DMFC_WR_CHAN_2C_43 24 -- --#define DMFC_DP_CHAN_5B_23 0 --#define DMFC_DP_CHAN_5F_27 8 --#define DMFC_DP_CHAN_6B_24 16 --#define DMFC_DP_CHAN_6F_29 24 -- --struct dmfc_channel_data { -- int ipu_channel; -- unsigned long channel_reg; -- unsigned long shift; -- unsigned eot_shift; -- unsigned max_fifo_lines; --}; -- --static const struct dmfc_channel_data dmfcdata[] = { -- { -- .ipu_channel = IPUV3_CHANNEL_MEM_BG_SYNC, -- .channel_reg = DMFC_DP_CHAN, -- .shift = DMFC_DP_CHAN_5B_23, -- .eot_shift = 20, -- .max_fifo_lines = 3, -- }, { -- .ipu_channel = 24, -- .channel_reg = DMFC_DP_CHAN, -- .shift = DMFC_DP_CHAN_6B_24, -- .eot_shift = 22, -- .max_fifo_lines = 1, -- }, { -- .ipu_channel = IPUV3_CHANNEL_MEM_FG_SYNC, -- .channel_reg = DMFC_DP_CHAN, -- .shift = DMFC_DP_CHAN_5F_27, -- .eot_shift = 21, -- .max_fifo_lines = 2, -- }, { -- .ipu_channel = IPUV3_CHANNEL_MEM_DC_SYNC, -- .channel_reg = DMFC_WR_CHAN, -- .shift = DMFC_WR_CHAN_1_28, -- .eot_shift = 16, -- .max_fifo_lines = 2, -- }, { -- .ipu_channel = 29, -- .channel_reg = DMFC_DP_CHAN, -- .shift = DMFC_DP_CHAN_6F_29, -- .eot_shift = 23, -- .max_fifo_lines = 1, -- }, --}; -- --#define DMFC_NUM_CHANNELS ARRAY_SIZE(dmfcdata) -- --struct ipu_dmfc_priv; -- --struct dmfc_channel { -- unsigned slots; -- struct ipu_soc *ipu; -- struct ipu_dmfc_priv *priv; -- const struct dmfc_channel_data *data; --}; -- --struct ipu_dmfc_priv { -- struct ipu_soc *ipu; -- struct device *dev; -- struct dmfc_channel channels[DMFC_NUM_CHANNELS]; -- struct mutex mutex; -- void __iomem *base; -- int use_count; --}; -- --int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc) --{ -- struct ipu_dmfc_priv *priv = dmfc->priv; -- mutex_lock(&priv->mutex); -- -- if (!priv->use_count) -- ipu_module_enable(priv->ipu, IPU_CONF_DMFC_EN); -- -- priv->use_count++; -- -- mutex_unlock(&priv->mutex); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel); -- --void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc) --{ -- struct ipu_dmfc_priv *priv = dmfc->priv; -- -- mutex_lock(&priv->mutex); -- -- priv->use_count--; -- -- if (!priv->use_count) -- ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN); -- -- if (priv->use_count < 0) -- priv->use_count = 0; -- -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dmfc_disable_channel); -- --void ipu_dmfc_config_wait4eot(struct dmfc_channel *dmfc, int width) --{ -- struct ipu_dmfc_priv *priv = dmfc->priv; -- u32 dmfc_gen1; -- -- mutex_lock(&priv->mutex); -- -- dmfc_gen1 = readl(priv->base + DMFC_GENERAL1); -- -- if ((dmfc->slots * 64 * 4) / width > dmfc->data->max_fifo_lines) -- dmfc_gen1 |= 1 << dmfc->data->eot_shift; -- else -- dmfc_gen1 &= ~(1 << dmfc->data->eot_shift); -- -- writel(dmfc_gen1, priv->base + DMFC_GENERAL1); -- -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dmfc_config_wait4eot); -- --struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipu_channel) --{ -- struct ipu_dmfc_priv *priv = ipu->dmfc_priv; -- int i; -- -- for (i = 0; i < DMFC_NUM_CHANNELS; i++) -- if (dmfcdata[i].ipu_channel == ipu_channel) -- return &priv->channels[i]; -- return ERR_PTR(-ENODEV); --} --EXPORT_SYMBOL_GPL(ipu_dmfc_get); -- --void ipu_dmfc_put(struct dmfc_channel *dmfc) --{ --} --EXPORT_SYMBOL_GPL(ipu_dmfc_put); -- --int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, -- struct clk *ipu_clk) --{ -- struct ipu_dmfc_priv *priv; -- int i; -- -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- -- priv->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!priv->base) -- return -ENOMEM; -- -- priv->dev = dev; -- priv->ipu = ipu; -- mutex_init(&priv->mutex); -- -- ipu->dmfc_priv = priv; -- -- for (i = 0; i < DMFC_NUM_CHANNELS; i++) { -- priv->channels[i].priv = priv; -- priv->channels[i].ipu = ipu; -- priv->channels[i].data = &dmfcdata[i]; -- -- if (dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_BG_SYNC || -- dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_FG_SYNC || -- dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_DC_SYNC) -- priv->channels[i].slots = 2; -- } -- -- writel(0x00000050, priv->base + DMFC_WR_CHAN); -- writel(0x00005654, priv->base + DMFC_DP_CHAN); -- writel(0x202020f6, priv->base + DMFC_WR_CHAN_DEF); -- writel(0x2020f6f6, priv->base + DMFC_DP_CHAN_DEF); -- writel(0x00000003, priv->base + DMFC_GENERAL1); -- -- return 0; --} -- --void ipu_dmfc_exit(struct ipu_soc *ipu) --{ --} ---- a/drivers/gpu/imx/ipu-v3/ipu-dp.c -+++ /dev/null -@@ -1,357 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#include <linux/export.h> --#include <linux/kernel.h> --#include <linux/types.h> --#include <linux/errno.h> --#include <linux/io.h> --#include <linux/err.h> -- --#include <video/imx-ipu-v3.h> --#include "ipu-prv.h" -- --#define DP_SYNC 0 --#define DP_ASYNC0 0x60 --#define DP_ASYNC1 0xBC -- --#define DP_COM_CONF 0x0 --#define DP_GRAPH_WIND_CTRL 0x0004 --#define DP_FG_POS 0x0008 --#define DP_CSC_A_0 0x0044 --#define DP_CSC_A_1 0x0048 --#define DP_CSC_A_2 0x004C --#define DP_CSC_A_3 0x0050 --#define DP_CSC_0 0x0054 --#define DP_CSC_1 0x0058 -- --#define DP_COM_CONF_FG_EN (1 << 0) --#define DP_COM_CONF_GWSEL (1 << 1) --#define DP_COM_CONF_GWAM (1 << 2) --#define DP_COM_CONF_GWCKE (1 << 3) --#define DP_COM_CONF_CSC_DEF_MASK (3 << 8) --#define DP_COM_CONF_CSC_DEF_OFFSET 8 --#define DP_COM_CONF_CSC_DEF_FG (3 << 8) --#define DP_COM_CONF_CSC_DEF_BG (2 << 8) --#define DP_COM_CONF_CSC_DEF_BOTH (1 << 8) -- --#define IPUV3_NUM_FLOWS 3 -- --struct ipu_dp_priv; -- --struct ipu_dp { -- u32 flow; -- bool in_use; -- bool foreground; -- enum ipu_color_space in_cs; --}; -- --struct ipu_flow { -- struct ipu_dp foreground; -- struct ipu_dp background; -- enum ipu_color_space out_cs; -- void __iomem *base; -- struct ipu_dp_priv *priv; --}; -- --struct ipu_dp_priv { -- struct ipu_soc *ipu; -- struct device *dev; -- void __iomem *base; -- struct ipu_flow flow[IPUV3_NUM_FLOWS]; -- struct mutex mutex; -- int use_count; --}; -- --static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1}; -- --static inline struct ipu_flow *to_flow(struct ipu_dp *dp) --{ -- if (dp->foreground) -- return container_of(dp, struct ipu_flow, foreground); -- else -- return container_of(dp, struct ipu_flow, background); --} -- --int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, -- u8 alpha, bool bg_chan) --{ -- struct ipu_flow *flow = to_flow(dp); -- struct ipu_dp_priv *priv = flow->priv; -- u32 reg; -- -- mutex_lock(&priv->mutex); -- -- reg = readl(flow->base + DP_COM_CONF); -- if (bg_chan) -- reg &= ~DP_COM_CONF_GWSEL; -- else -- reg |= DP_COM_CONF_GWSEL; -- writel(reg, flow->base + DP_COM_CONF); -- -- if (enable) { -- reg = readl(flow->base + DP_GRAPH_WIND_CTRL) & 0x00FFFFFFL; -- writel(reg | ((u32) alpha << 24), -- flow->base + DP_GRAPH_WIND_CTRL); -- -- reg = readl(flow->base + DP_COM_CONF); -- writel(reg | DP_COM_CONF_GWAM, flow->base + DP_COM_CONF); -- } else { -- reg = readl(flow->base + DP_COM_CONF); -- writel(reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF); -- } -- -- ipu_srm_dp_update(priv->ipu, true); -- -- mutex_unlock(&priv->mutex); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha); -- --int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos) --{ -- struct ipu_flow *flow = to_flow(dp); -- struct ipu_dp_priv *priv = flow->priv; -- -- writel((x_pos << 16) | y_pos, flow->base + DP_FG_POS); -- -- ipu_srm_dp_update(priv->ipu, true); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos); -- --static void ipu_dp_csc_init(struct ipu_flow *flow, -- enum ipu_color_space in, -- enum ipu_color_space out, -- u32 place) --{ -- u32 reg; -- -- reg = readl(flow->base + DP_COM_CONF); -- reg &= ~DP_COM_CONF_CSC_DEF_MASK; -- -- if (in == out) { -- writel(reg, flow->base + DP_COM_CONF); -- return; -- } -- -- if (in == IPUV3_COLORSPACE_RGB && out == IPUV3_COLORSPACE_YUV) { -- writel(0x099 | (0x12d << 16), flow->base + DP_CSC_A_0); -- writel(0x03a | (0x3a9 << 16), flow->base + DP_CSC_A_1); -- writel(0x356 | (0x100 << 16), flow->base + DP_CSC_A_2); -- writel(0x100 | (0x329 << 16), flow->base + DP_CSC_A_3); -- writel(0x3d6 | (0x0000 << 16) | (2 << 30), -- flow->base + DP_CSC_0); -- writel(0x200 | (2 << 14) | (0x200 << 16) | (2 << 30), -- flow->base + DP_CSC_1); -- } else { -- writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0); -- writel(0x0cc | (0x095 << 16), flow->base + DP_CSC_A_1); -- writel(0x3ce | (0x398 << 16), flow->base + DP_CSC_A_2); -- writel(0x095 | (0x0ff << 16), flow->base + DP_CSC_A_3); -- writel(0x000 | (0x3e42 << 16) | (1 << 30), -- flow->base + DP_CSC_0); -- writel(0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30), -- flow->base + DP_CSC_1); -- } -- -- reg |= place; -- -- writel(reg, flow->base + DP_COM_CONF); --} -- --int ipu_dp_setup_channel(struct ipu_dp *dp, -- enum ipu_color_space in, -- enum ipu_color_space out) --{ -- struct ipu_flow *flow = to_flow(dp); -- struct ipu_dp_priv *priv = flow->priv; -- -- mutex_lock(&priv->mutex); -- -- dp->in_cs = in; -- -- if (!dp->foreground) -- flow->out_cs = out; -- -- if (flow->foreground.in_cs == flow->background.in_cs) { -- /* -- * foreground and background are of same colorspace, put -- * colorspace converter after combining unit. -- */ -- ipu_dp_csc_init(flow, flow->foreground.in_cs, flow->out_cs, -- DP_COM_CONF_CSC_DEF_BOTH); -- } else { -- if (flow->foreground.in_cs == IPUV3_COLORSPACE_UNKNOWN || -- flow->foreground.in_cs == flow->out_cs) -- /* -- * foreground identical to output, apply color -- * conversion on background -- */ -- ipu_dp_csc_init(flow, flow->background.in_cs, -- flow->out_cs, DP_COM_CONF_CSC_DEF_BG); -- else -- ipu_dp_csc_init(flow, flow->foreground.in_cs, -- flow->out_cs, DP_COM_CONF_CSC_DEF_FG); -- } -- -- ipu_srm_dp_update(priv->ipu, true); -- -- mutex_unlock(&priv->mutex); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dp_setup_channel); -- --int ipu_dp_enable(struct ipu_soc *ipu) --{ -- struct ipu_dp_priv *priv = ipu->dp_priv; -- -- mutex_lock(&priv->mutex); -- -- if (!priv->use_count) -- ipu_module_enable(priv->ipu, IPU_CONF_DP_EN); -- -- priv->use_count++; -- -- mutex_unlock(&priv->mutex); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dp_enable); -- --int ipu_dp_enable_channel(struct ipu_dp *dp) --{ -- struct ipu_flow *flow = to_flow(dp); -- struct ipu_dp_priv *priv = flow->priv; -- u32 reg; -- -- if (!dp->foreground) -- return 0; -- -- mutex_lock(&priv->mutex); -- -- reg = readl(flow->base + DP_COM_CONF); -- reg |= DP_COM_CONF_FG_EN; -- writel(reg, flow->base + DP_COM_CONF); -- -- ipu_srm_dp_update(priv->ipu, true); -- -- mutex_unlock(&priv->mutex); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dp_enable_channel); -- --void ipu_dp_disable_channel(struct ipu_dp *dp, bool sync) --{ -- struct ipu_flow *flow = to_flow(dp); -- struct ipu_dp_priv *priv = flow->priv; -- u32 reg, csc; -- -- dp->in_cs = IPUV3_COLORSPACE_UNKNOWN; -- -- if (!dp->foreground) -- return; -- -- mutex_lock(&priv->mutex); -- -- reg = readl(flow->base + DP_COM_CONF); -- csc = reg & DP_COM_CONF_CSC_DEF_MASK; -- reg &= ~DP_COM_CONF_CSC_DEF_MASK; -- if (csc == DP_COM_CONF_CSC_DEF_BOTH || csc == DP_COM_CONF_CSC_DEF_BG) -- reg |= DP_COM_CONF_CSC_DEF_BG; -- -- reg &= ~DP_COM_CONF_FG_EN; -- writel(reg, flow->base + DP_COM_CONF); -- -- writel(0, flow->base + DP_FG_POS); -- ipu_srm_dp_update(priv->ipu, sync); -- -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dp_disable_channel); -- --void ipu_dp_disable(struct ipu_soc *ipu) --{ -- struct ipu_dp_priv *priv = ipu->dp_priv; -- -- mutex_lock(&priv->mutex); -- -- priv->use_count--; -- -- if (!priv->use_count) -- ipu_module_disable(priv->ipu, IPU_CONF_DP_EN); -- -- if (priv->use_count < 0) -- priv->use_count = 0; -- -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dp_disable); -- --struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow) --{ -- struct ipu_dp_priv *priv = ipu->dp_priv; -- struct ipu_dp *dp; -- -- if ((flow >> 1) >= IPUV3_NUM_FLOWS) -- return ERR_PTR(-EINVAL); -- -- if (flow & 1) -- dp = &priv->flow[flow >> 1].foreground; -- else -- dp = &priv->flow[flow >> 1].background; -- -- if (dp->in_use) -- return ERR_PTR(-EBUSY); -- -- dp->in_use = true; -- -- return dp; --} --EXPORT_SYMBOL_GPL(ipu_dp_get); -- --void ipu_dp_put(struct ipu_dp *dp) --{ -- dp->in_use = false; --} --EXPORT_SYMBOL_GPL(ipu_dp_put); -- --int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base) --{ -- struct ipu_dp_priv *priv; -- int i; -- -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- priv->dev = dev; -- priv->ipu = ipu; -- -- ipu->dp_priv = priv; -- -- priv->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!priv->base) -- return -ENOMEM; -- -- mutex_init(&priv->mutex); -- -- for (i = 0; i < IPUV3_NUM_FLOWS; i++) { -- priv->flow[i].background.in_cs = IPUV3_COLORSPACE_UNKNOWN; -- priv->flow[i].foreground.in_cs = IPUV3_COLORSPACE_UNKNOWN; -- priv->flow[i].foreground.foreground = true; -- priv->flow[i].base = priv->base + ipu_dp_flow_base[i]; -- priv->flow[i].priv = priv; -- } -- -- return 0; --} -- --void ipu_dp_exit(struct ipu_soc *ipu) --{ --} ---- a/drivers/gpu/imx/ipu-v3/ipu-ic.c -+++ /dev/null -@@ -1,761 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (C) 2012-2014 Mentor Graphics Inc. -- * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. -- */ -- --#include <linux/types.h> --#include <linux/init.h> --#include <linux/errno.h> --#include <linux/spinlock.h> --#include <linux/bitrev.h> --#include <linux/io.h> --#include <linux/err.h> --#include <linux/sizes.h> --#include "ipu-prv.h" -- --/* IC Register Offsets */ --#define IC_CONF 0x0000 --#define IC_PRP_ENC_RSC 0x0004 --#define IC_PRP_VF_RSC 0x0008 --#define IC_PP_RSC 0x000C --#define IC_CMBP_1 0x0010 --#define IC_CMBP_2 0x0014 --#define IC_IDMAC_1 0x0018 --#define IC_IDMAC_2 0x001C --#define IC_IDMAC_3 0x0020 --#define IC_IDMAC_4 0x0024 -- --/* IC Register Fields */ --#define IC_CONF_PRPENC_EN (1 << 0) --#define IC_CONF_PRPENC_CSC1 (1 << 1) --#define IC_CONF_PRPENC_ROT_EN (1 << 2) --#define IC_CONF_PRPVF_EN (1 << 8) --#define IC_CONF_PRPVF_CSC1 (1 << 9) --#define IC_CONF_PRPVF_CSC2 (1 << 10) --#define IC_CONF_PRPVF_CMB (1 << 11) --#define IC_CONF_PRPVF_ROT_EN (1 << 12) --#define IC_CONF_PP_EN (1 << 16) --#define IC_CONF_PP_CSC1 (1 << 17) --#define IC_CONF_PP_CSC2 (1 << 18) --#define IC_CONF_PP_CMB (1 << 19) --#define IC_CONF_PP_ROT_EN (1 << 20) --#define IC_CONF_IC_GLB_LOC_A (1 << 28) --#define IC_CONF_KEY_COLOR_EN (1 << 29) --#define IC_CONF_RWS_EN (1 << 30) --#define IC_CONF_CSI_MEM_WR_EN (1 << 31) -- --#define IC_IDMAC_1_CB0_BURST_16 (1 << 0) --#define IC_IDMAC_1_CB1_BURST_16 (1 << 1) --#define IC_IDMAC_1_CB2_BURST_16 (1 << 2) --#define IC_IDMAC_1_CB3_BURST_16 (1 << 3) --#define IC_IDMAC_1_CB4_BURST_16 (1 << 4) --#define IC_IDMAC_1_CB5_BURST_16 (1 << 5) --#define IC_IDMAC_1_CB6_BURST_16 (1 << 6) --#define IC_IDMAC_1_CB7_BURST_16 (1 << 7) --#define IC_IDMAC_1_PRPENC_ROT_MASK (0x7 << 11) --#define IC_IDMAC_1_PRPENC_ROT_OFFSET 11 --#define IC_IDMAC_1_PRPVF_ROT_MASK (0x7 << 14) --#define IC_IDMAC_1_PRPVF_ROT_OFFSET 14 --#define IC_IDMAC_1_PP_ROT_MASK (0x7 << 17) --#define IC_IDMAC_1_PP_ROT_OFFSET 17 --#define IC_IDMAC_1_PP_FLIP_RS (1 << 22) --#define IC_IDMAC_1_PRPVF_FLIP_RS (1 << 21) --#define IC_IDMAC_1_PRPENC_FLIP_RS (1 << 20) -- --#define IC_IDMAC_2_PRPENC_HEIGHT_MASK (0x3ff << 0) --#define IC_IDMAC_2_PRPENC_HEIGHT_OFFSET 0 --#define IC_IDMAC_2_PRPVF_HEIGHT_MASK (0x3ff << 10) --#define IC_IDMAC_2_PRPVF_HEIGHT_OFFSET 10 --#define IC_IDMAC_2_PP_HEIGHT_MASK (0x3ff << 20) --#define IC_IDMAC_2_PP_HEIGHT_OFFSET 20 -- --#define IC_IDMAC_3_PRPENC_WIDTH_MASK (0x3ff << 0) --#define IC_IDMAC_3_PRPENC_WIDTH_OFFSET 0 --#define IC_IDMAC_3_PRPVF_WIDTH_MASK (0x3ff << 10) --#define IC_IDMAC_3_PRPVF_WIDTH_OFFSET 10 --#define IC_IDMAC_3_PP_WIDTH_MASK (0x3ff << 20) --#define IC_IDMAC_3_PP_WIDTH_OFFSET 20 -- --struct ic_task_regoffs { -- u32 rsc; -- u32 tpmem_csc[2]; --}; -- --struct ic_task_bitfields { -- u32 ic_conf_en; -- u32 ic_conf_rot_en; -- u32 ic_conf_cmb_en; -- u32 ic_conf_csc1_en; -- u32 ic_conf_csc2_en; -- u32 ic_cmb_galpha_bit; --}; -- --static const struct ic_task_regoffs ic_task_reg[IC_NUM_TASKS] = { -- [IC_TASK_ENCODER] = { -- .rsc = IC_PRP_ENC_RSC, -- .tpmem_csc = {0x2008, 0}, -- }, -- [IC_TASK_VIEWFINDER] = { -- .rsc = IC_PRP_VF_RSC, -- .tpmem_csc = {0x4028, 0x4040}, -- }, -- [IC_TASK_POST_PROCESSOR] = { -- .rsc = IC_PP_RSC, -- .tpmem_csc = {0x6060, 0x6078}, -- }, --}; -- --static const struct ic_task_bitfields ic_task_bit[IC_NUM_TASKS] = { -- [IC_TASK_ENCODER] = { -- .ic_conf_en = IC_CONF_PRPENC_EN, -- .ic_conf_rot_en = IC_CONF_PRPENC_ROT_EN, -- .ic_conf_cmb_en = 0, /* NA */ -- .ic_conf_csc1_en = IC_CONF_PRPENC_CSC1, -- .ic_conf_csc2_en = 0, /* NA */ -- .ic_cmb_galpha_bit = 0, /* NA */ -- }, -- [IC_TASK_VIEWFINDER] = { -- .ic_conf_en = IC_CONF_PRPVF_EN, -- .ic_conf_rot_en = IC_CONF_PRPVF_ROT_EN, -- .ic_conf_cmb_en = IC_CONF_PRPVF_CMB, -- .ic_conf_csc1_en = IC_CONF_PRPVF_CSC1, -- .ic_conf_csc2_en = IC_CONF_PRPVF_CSC2, -- .ic_cmb_galpha_bit = 0, -- }, -- [IC_TASK_POST_PROCESSOR] = { -- .ic_conf_en = IC_CONF_PP_EN, -- .ic_conf_rot_en = IC_CONF_PP_ROT_EN, -- .ic_conf_cmb_en = IC_CONF_PP_CMB, -- .ic_conf_csc1_en = IC_CONF_PP_CSC1, -- .ic_conf_csc2_en = IC_CONF_PP_CSC2, -- .ic_cmb_galpha_bit = 8, -- }, --}; -- --struct ipu_ic_priv; -- --struct ipu_ic { -- enum ipu_ic_task task; -- const struct ic_task_regoffs *reg; -- const struct ic_task_bitfields *bit; -- -- struct ipu_ic_colorspace in_cs; -- struct ipu_ic_colorspace g_in_cs; -- struct ipu_ic_colorspace out_cs; -- -- bool graphics; -- bool rotation; -- bool in_use; -- -- struct ipu_ic_priv *priv; --}; -- --struct ipu_ic_priv { -- void __iomem *base; -- void __iomem *tpmem_base; -- spinlock_t lock; -- struct ipu_soc *ipu; -- int use_count; -- int irt_use_count; -- struct ipu_ic task[IC_NUM_TASKS]; --}; -- --static inline u32 ipu_ic_read(struct ipu_ic *ic, unsigned offset) --{ -- return readl(ic->priv->base + offset); --} -- --static inline void ipu_ic_write(struct ipu_ic *ic, u32 value, unsigned offset) --{ -- writel(value, ic->priv->base + offset); --} -- --static int init_csc(struct ipu_ic *ic, -- const struct ipu_ic_csc *csc, -- int csc_index) --{ -- struct ipu_ic_priv *priv = ic->priv; -- u32 __iomem *base; -- const u16 (*c)[3]; -- const u16 *a; -- u32 param; -- -- base = (u32 __iomem *) -- (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); -- -- /* Cast to unsigned */ -- c = (const u16 (*)[3])csc->params.coeff; -- a = (const u16 *)csc->params.offset; -- -- param = ((a[0] & 0x1f) << 27) | ((c[0][0] & 0x1ff) << 18) | -- ((c[1][1] & 0x1ff) << 9) | (c[2][2] & 0x1ff); -- writel(param, base++); -- -- param = ((a[0] & 0x1fe0) >> 5) | (csc->params.scale << 8) | -- (csc->params.sat << 10); -- writel(param, base++); -- -- param = ((a[1] & 0x1f) << 27) | ((c[0][1] & 0x1ff) << 18) | -- ((c[1][0] & 0x1ff) << 9) | (c[2][0] & 0x1ff); -- writel(param, base++); -- -- param = ((a[1] & 0x1fe0) >> 5); -- writel(param, base++); -- -- param = ((a[2] & 0x1f) << 27) | ((c[0][2] & 0x1ff) << 18) | -- ((c[1][2] & 0x1ff) << 9) | (c[2][1] & 0x1ff); -- writel(param, base++); -- -- param = ((a[2] & 0x1fe0) >> 5); -- writel(param, base++); -- -- return 0; --} -- --static int calc_resize_coeffs(struct ipu_ic *ic, -- u32 in_size, u32 out_size, -- u32 *resize_coeff, -- u32 *downsize_coeff) --{ -- struct ipu_ic_priv *priv = ic->priv; -- struct ipu_soc *ipu = priv->ipu; -- u32 temp_size, temp_downsize; -- -- /* -- * Input size cannot be more than 4096, and output size cannot -- * be more than 1024 -- */ -- if (in_size > 4096) { -- dev_err(ipu->dev, "Unsupported resize (in_size > 4096)\n"); -- return -EINVAL; -- } -- if (out_size > 1024) { -- dev_err(ipu->dev, "Unsupported resize (out_size > 1024)\n"); -- return -EINVAL; -- } -- -- /* Cannot downsize more than 4:1 */ -- if ((out_size << 2) < in_size) { -- dev_err(ipu->dev, "Unsupported downsize\n"); -- return -EINVAL; -- } -- -- /* Compute downsizing coefficient */ -- temp_downsize = 0; -- temp_size = in_size; -- while (((temp_size > 1024) || (temp_size >= out_size * 2)) && -- (temp_downsize < 2)) { -- temp_size >>= 1; -- temp_downsize++; -- } -- *downsize_coeff = temp_downsize; -- -- /* -- * compute resizing coefficient using the following equation: -- * resize_coeff = M * (SI - 1) / (SO - 1) -- * where M = 2^13, SI = input size, SO = output size -- */ -- *resize_coeff = (8192L * (temp_size - 1)) / (out_size - 1); -- if (*resize_coeff >= 16384L) { -- dev_err(ipu->dev, "Warning! Overflow on resize coeff.\n"); -- *resize_coeff = 0x3FFF; -- } -- -- return 0; --} -- --void ipu_ic_task_enable(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- unsigned long flags; -- u32 ic_conf; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- ic_conf = ipu_ic_read(ic, IC_CONF); -- -- ic_conf |= ic->bit->ic_conf_en; -- -- if (ic->rotation) -- ic_conf |= ic->bit->ic_conf_rot_en; -- -- if (ic->in_cs.cs != ic->out_cs.cs) -- ic_conf |= ic->bit->ic_conf_csc1_en; -- -- if (ic->graphics) { -- ic_conf |= ic->bit->ic_conf_cmb_en; -- ic_conf |= ic->bit->ic_conf_csc1_en; -- -- if (ic->g_in_cs.cs != ic->out_cs.cs) -- ic_conf |= ic->bit->ic_conf_csc2_en; -- } -- -- ipu_ic_write(ic, ic_conf, IC_CONF); -- -- spin_unlock_irqrestore(&priv->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_ic_task_enable); -- --void ipu_ic_task_disable(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- unsigned long flags; -- u32 ic_conf; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- ic_conf = ipu_ic_read(ic, IC_CONF); -- -- ic_conf &= ~(ic->bit->ic_conf_en | -- ic->bit->ic_conf_csc1_en | -- ic->bit->ic_conf_rot_en); -- if (ic->bit->ic_conf_csc2_en) -- ic_conf &= ~ic->bit->ic_conf_csc2_en; -- if (ic->bit->ic_conf_cmb_en) -- ic_conf &= ~ic->bit->ic_conf_cmb_en; -- -- ipu_ic_write(ic, ic_conf, IC_CONF); -- -- spin_unlock_irqrestore(&priv->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_ic_task_disable); -- --int ipu_ic_task_graphics_init(struct ipu_ic *ic, -- const struct ipu_ic_colorspace *g_in_cs, -- bool galpha_en, u32 galpha, -- bool colorkey_en, u32 colorkey) --{ -- struct ipu_ic_priv *priv = ic->priv; -- struct ipu_ic_csc csc2; -- unsigned long flags; -- u32 reg, ic_conf; -- int ret = 0; -- -- if (ic->task == IC_TASK_ENCODER) -- return -EINVAL; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- ic_conf = ipu_ic_read(ic, IC_CONF); -- -- if (!(ic_conf & ic->bit->ic_conf_csc1_en)) { -- struct ipu_ic_csc csc1; -- -- ret = ipu_ic_calc_csc(&csc1, -- V4L2_YCBCR_ENC_601, -- V4L2_QUANTIZATION_FULL_RANGE, -- IPUV3_COLORSPACE_RGB, -- V4L2_YCBCR_ENC_601, -- V4L2_QUANTIZATION_FULL_RANGE, -- IPUV3_COLORSPACE_RGB); -- if (ret) -- goto unlock; -- -- /* need transparent CSC1 conversion */ -- ret = init_csc(ic, &csc1, 0); -- if (ret) -- goto unlock; -- } -- -- ic->g_in_cs = *g_in_cs; -- csc2.in_cs = ic->g_in_cs; -- csc2.out_cs = ic->out_cs; -- -- ret = __ipu_ic_calc_csc(&csc2); -- if (ret) -- goto unlock; -- -- ret = init_csc(ic, &csc2, 1); -- if (ret) -- goto unlock; -- -- if (galpha_en) { -- ic_conf |= IC_CONF_IC_GLB_LOC_A; -- reg = ipu_ic_read(ic, IC_CMBP_1); -- reg &= ~(0xff << ic->bit->ic_cmb_galpha_bit); -- reg |= (galpha << ic->bit->ic_cmb_galpha_bit); -- ipu_ic_write(ic, reg, IC_CMBP_1); -- } else -- ic_conf &= ~IC_CONF_IC_GLB_LOC_A; -- -- if (colorkey_en) { -- ic_conf |= IC_CONF_KEY_COLOR_EN; -- ipu_ic_write(ic, colorkey, IC_CMBP_2); -- } else -- ic_conf &= ~IC_CONF_KEY_COLOR_EN; -- -- ipu_ic_write(ic, ic_conf, IC_CONF); -- -- ic->graphics = true; --unlock: -- spin_unlock_irqrestore(&priv->lock, flags); -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_ic_task_graphics_init); -- --int ipu_ic_task_init_rsc(struct ipu_ic *ic, -- const struct ipu_ic_csc *csc, -- int in_width, int in_height, -- int out_width, int out_height, -- u32 rsc) --{ -- struct ipu_ic_priv *priv = ic->priv; -- u32 downsize_coeff, resize_coeff; -- unsigned long flags; -- int ret = 0; -- -- if (!rsc) { -- /* Setup vertical resizing */ -- -- ret = calc_resize_coeffs(ic, in_height, out_height, -- &resize_coeff, &downsize_coeff); -- if (ret) -- return ret; -- -- rsc = (downsize_coeff << 30) | (resize_coeff << 16); -- -- /* Setup horizontal resizing */ -- ret = calc_resize_coeffs(ic, in_width, out_width, -- &resize_coeff, &downsize_coeff); -- if (ret) -- return ret; -- -- rsc |= (downsize_coeff << 14) | resize_coeff; -- } -- -- spin_lock_irqsave(&priv->lock, flags); -- -- ipu_ic_write(ic, rsc, ic->reg->rsc); -- -- /* Setup color space conversion */ -- ic->in_cs = csc->in_cs; -- ic->out_cs = csc->out_cs; -- -- ret = init_csc(ic, csc, 0); -- -- spin_unlock_irqrestore(&priv->lock, flags); -- return ret; --} -- --int ipu_ic_task_init(struct ipu_ic *ic, -- const struct ipu_ic_csc *csc, -- int in_width, int in_height, -- int out_width, int out_height) --{ -- return ipu_ic_task_init_rsc(ic, csc, -- in_width, in_height, -- out_width, out_height, 0); --} --EXPORT_SYMBOL_GPL(ipu_ic_task_init); -- --int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel, -- u32 width, u32 height, int burst_size, -- enum ipu_rotate_mode rot) --{ -- struct ipu_ic_priv *priv = ic->priv; -- struct ipu_soc *ipu = priv->ipu; -- u32 ic_idmac_1, ic_idmac_2, ic_idmac_3; -- u32 temp_rot = bitrev8(rot) >> 5; -- bool need_hor_flip = false; -- unsigned long flags; -- int ret = 0; -- -- if ((burst_size != 8) && (burst_size != 16)) { -- dev_err(ipu->dev, "Illegal burst length for IC\n"); -- return -EINVAL; -- } -- -- width--; -- height--; -- -- if (temp_rot & 0x2) /* Need horizontal flip */ -- need_hor_flip = true; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- ic_idmac_1 = ipu_ic_read(ic, IC_IDMAC_1); -- ic_idmac_2 = ipu_ic_read(ic, IC_IDMAC_2); -- ic_idmac_3 = ipu_ic_read(ic, IC_IDMAC_3); -- -- switch (channel->num) { -- case IPUV3_CHANNEL_IC_PP_MEM: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB2_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB2_BURST_16; -- -- if (need_hor_flip) -- ic_idmac_1 |= IC_IDMAC_1_PP_FLIP_RS; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_PP_FLIP_RS; -- -- ic_idmac_2 &= ~IC_IDMAC_2_PP_HEIGHT_MASK; -- ic_idmac_2 |= height << IC_IDMAC_2_PP_HEIGHT_OFFSET; -- -- ic_idmac_3 &= ~IC_IDMAC_3_PP_WIDTH_MASK; -- ic_idmac_3 |= width << IC_IDMAC_3_PP_WIDTH_OFFSET; -- break; -- case IPUV3_CHANNEL_MEM_IC_PP: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB5_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB5_BURST_16; -- break; -- case IPUV3_CHANNEL_MEM_ROT_PP: -- ic_idmac_1 &= ~IC_IDMAC_1_PP_ROT_MASK; -- ic_idmac_1 |= temp_rot << IC_IDMAC_1_PP_ROT_OFFSET; -- break; -- case IPUV3_CHANNEL_MEM_IC_PRP_VF: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB6_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB6_BURST_16; -- break; -- case IPUV3_CHANNEL_IC_PRP_ENC_MEM: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB0_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB0_BURST_16; -- -- if (need_hor_flip) -- ic_idmac_1 |= IC_IDMAC_1_PRPENC_FLIP_RS; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_FLIP_RS; -- -- ic_idmac_2 &= ~IC_IDMAC_2_PRPENC_HEIGHT_MASK; -- ic_idmac_2 |= height << IC_IDMAC_2_PRPENC_HEIGHT_OFFSET; -- -- ic_idmac_3 &= ~IC_IDMAC_3_PRPENC_WIDTH_MASK; -- ic_idmac_3 |= width << IC_IDMAC_3_PRPENC_WIDTH_OFFSET; -- break; -- case IPUV3_CHANNEL_MEM_ROT_ENC: -- ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_ROT_MASK; -- ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPENC_ROT_OFFSET; -- break; -- case IPUV3_CHANNEL_IC_PRP_VF_MEM: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB1_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB1_BURST_16; -- -- if (need_hor_flip) -- ic_idmac_1 |= IC_IDMAC_1_PRPVF_FLIP_RS; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_FLIP_RS; -- -- ic_idmac_2 &= ~IC_IDMAC_2_PRPVF_HEIGHT_MASK; -- ic_idmac_2 |= height << IC_IDMAC_2_PRPVF_HEIGHT_OFFSET; -- -- ic_idmac_3 &= ~IC_IDMAC_3_PRPVF_WIDTH_MASK; -- ic_idmac_3 |= width << IC_IDMAC_3_PRPVF_WIDTH_OFFSET; -- break; -- case IPUV3_CHANNEL_MEM_ROT_VF: -- ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_ROT_MASK; -- ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPVF_ROT_OFFSET; -- break; -- case IPUV3_CHANNEL_G_MEM_IC_PRP_VF: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB3_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB3_BURST_16; -- break; -- case IPUV3_CHANNEL_G_MEM_IC_PP: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB4_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB4_BURST_16; -- break; -- case IPUV3_CHANNEL_VDI_MEM_IC_VF: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB7_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB7_BURST_16; -- break; -- default: -- goto unlock; -- } -- -- ipu_ic_write(ic, ic_idmac_1, IC_IDMAC_1); -- ipu_ic_write(ic, ic_idmac_2, IC_IDMAC_2); -- ipu_ic_write(ic, ic_idmac_3, IC_IDMAC_3); -- -- if (ipu_rot_mode_is_irt(rot)) -- ic->rotation = true; -- --unlock: -- spin_unlock_irqrestore(&priv->lock, flags); -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init); -- --static void ipu_irt_enable(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- -- if (!priv->irt_use_count) -- ipu_module_enable(priv->ipu, IPU_CONF_ROT_EN); -- -- priv->irt_use_count++; --} -- --static void ipu_irt_disable(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- -- if (priv->irt_use_count) { -- if (!--priv->irt_use_count) -- ipu_module_disable(priv->ipu, IPU_CONF_ROT_EN); -- } --} -- --int ipu_ic_enable(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- unsigned long flags; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- if (!priv->use_count) -- ipu_module_enable(priv->ipu, IPU_CONF_IC_EN); -- -- priv->use_count++; -- -- if (ic->rotation) -- ipu_irt_enable(ic); -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_ic_enable); -- --int ipu_ic_disable(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- unsigned long flags; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- priv->use_count--; -- -- if (!priv->use_count) -- ipu_module_disable(priv->ipu, IPU_CONF_IC_EN); -- -- if (priv->use_count < 0) -- priv->use_count = 0; -- -- if (ic->rotation) -- ipu_irt_disable(ic); -- -- ic->rotation = ic->graphics = false; -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_ic_disable); -- --struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task) --{ -- struct ipu_ic_priv *priv = ipu->ic_priv; -- unsigned long flags; -- struct ipu_ic *ic, *ret; -- -- if (task >= IC_NUM_TASKS) -- return ERR_PTR(-EINVAL); -- -- ic = &priv->task[task]; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- if (ic->in_use) { -- ret = ERR_PTR(-EBUSY); -- goto unlock; -- } -- -- ic->in_use = true; -- ret = ic; -- --unlock: -- spin_unlock_irqrestore(&priv->lock, flags); -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_ic_get); -- --void ipu_ic_put(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- unsigned long flags; -- -- spin_lock_irqsave(&priv->lock, flags); -- ic->in_use = false; -- spin_unlock_irqrestore(&priv->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_ic_put); -- --int ipu_ic_init(struct ipu_soc *ipu, struct device *dev, -- unsigned long base, unsigned long tpmem_base) --{ -- struct ipu_ic_priv *priv; -- int i; -- -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- -- ipu->ic_priv = priv; -- -- spin_lock_init(&priv->lock); -- priv->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!priv->base) -- return -ENOMEM; -- priv->tpmem_base = devm_ioremap(dev, tpmem_base, SZ_64K); -- if (!priv->tpmem_base) -- return -ENOMEM; -- -- dev_dbg(dev, "IC base: 0x%08lx remapped to %p\n", base, priv->base); -- -- priv->ipu = ipu; -- -- for (i = 0; i < IC_NUM_TASKS; i++) { -- priv->task[i].task = i; -- priv->task[i].priv = priv; -- priv->task[i].reg = &ic_task_reg[i]; -- priv->task[i].bit = &ic_task_bit[i]; -- } -- -- return 0; --} -- --void ipu_ic_exit(struct ipu_soc *ipu) --{ --} -- --void ipu_ic_dump(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- struct ipu_soc *ipu = priv->ipu; -- -- dev_dbg(ipu->dev, "IC_CONF = \t0x%08X\n", -- ipu_ic_read(ic, IC_CONF)); -- dev_dbg(ipu->dev, "IC_PRP_ENC_RSC = \t0x%08X\n", -- ipu_ic_read(ic, IC_PRP_ENC_RSC)); -- dev_dbg(ipu->dev, "IC_PRP_VF_RSC = \t0x%08X\n", -- ipu_ic_read(ic, IC_PRP_VF_RSC)); -- dev_dbg(ipu->dev, "IC_PP_RSC = \t0x%08X\n", -- ipu_ic_read(ic, IC_PP_RSC)); -- dev_dbg(ipu->dev, "IC_CMBP_1 = \t0x%08X\n", -- ipu_ic_read(ic, IC_CMBP_1)); -- dev_dbg(ipu->dev, "IC_CMBP_2 = \t0x%08X\n", -- ipu_ic_read(ic, IC_CMBP_2)); -- dev_dbg(ipu->dev, "IC_IDMAC_1 = \t0x%08X\n", -- ipu_ic_read(ic, IC_IDMAC_1)); -- dev_dbg(ipu->dev, "IC_IDMAC_2 = \t0x%08X\n", -- ipu_ic_read(ic, IC_IDMAC_2)); -- dev_dbg(ipu->dev, "IC_IDMAC_3 = \t0x%08X\n", -- ipu_ic_read(ic, IC_IDMAC_3)); -- dev_dbg(ipu->dev, "IC_IDMAC_4 = \t0x%08X\n", -- ipu_ic_read(ic, IC_IDMAC_4)); --} --EXPORT_SYMBOL_GPL(ipu_ic_dump); ---- a/drivers/gpu/imx/ipu-v3/ipu-image-convert.c -+++ /dev/null -@@ -1,2475 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (C) 2012-2016 Mentor Graphics Inc. -- * -- * Queued image conversion support, with tiling and rotation. -- */ -- --#include <linux/interrupt.h> --#include <linux/dma-mapping.h> --#include <video/imx-ipu-image-convert.h> --#include "ipu-prv.h" -- --/* -- * The IC Resizer has a restriction that the output frame from the -- * resizer must be 1024 or less in both width (pixels) and height -- * (lines). -- * -- * The image converter attempts to split up a conversion when -- * the desired output (converted) frame resolution exceeds the -- * IC resizer limit of 1024 in either dimension. -- * -- * If either dimension of the output frame exceeds the limit, the -- * dimension is split into 1, 2, or 4 equal stripes, for a maximum -- * of 4*4 or 16 tiles. A conversion is then carried out for each -- * tile (but taking care to pass the full frame stride length to -- * the DMA channel's parameter memory!). IDMA double-buffering is used -- * to convert each tile back-to-back when possible (see note below -- * when double_buffering boolean is set). -- * -- * Note that the input frame must be split up into the same number -- * of tiles as the output frame: -- * -- * +---------+-----+ -- * +-----+---+ | A | B | -- * | A | B | | | | -- * +-----+---+ --> +---------+-----+ -- * | C | D | | C | D | -- * +-----+---+ | | | -- * +---------+-----+ -- * -- * Clockwise 90° rotations are handled by first rescaling into a -- * reusable temporary tile buffer and then rotating with the 8x8 -- * block rotator, writing to the correct destination: -- * -- * +-----+-----+ -- * | | | -- * +-----+---+ +---------+ | C | A | -- * | A | B | | A,B, | | | | | -- * +-----+---+ --> | C,D | | --> | | | -- * | C | D | +---------+ +-----+-----+ -- * +-----+---+ | D | B | -- * | | | -- * +-----+-----+ -- * -- * If the 8x8 block rotator is used, horizontal or vertical flipping -- * is done during the rotation step, otherwise flipping is done -- * during the scaling step. -- * With rotation or flipping, tile order changes between input and -- * output image. Tiles are numbered row major from top left to bottom -- * right for both input and output image. -- */ -- --#define MAX_STRIPES_W 4 --#define MAX_STRIPES_H 4 --#define MAX_TILES (MAX_STRIPES_W * MAX_STRIPES_H) -- --#define MIN_W 16 --#define MIN_H 8 --#define MAX_W 4096 --#define MAX_H 4096 -- --enum ipu_image_convert_type { -- IMAGE_CONVERT_IN = 0, -- IMAGE_CONVERT_OUT, --}; -- --struct ipu_image_convert_dma_buf { -- void *virt; -- dma_addr_t phys; -- unsigned long len; --}; -- --struct ipu_image_convert_dma_chan { -- int in; -- int out; -- int rot_in; -- int rot_out; -- int vdi_in_p; -- int vdi_in; -- int vdi_in_n; --}; -- --/* dimensions of one tile */ --struct ipu_image_tile { -- u32 width; -- u32 height; -- u32 left; -- u32 top; -- /* size and strides are in bytes */ -- u32 size; -- u32 stride; -- u32 rot_stride; -- /* start Y or packed offset of this tile */ -- u32 offset; -- /* offset from start to tile in U plane, for planar formats */ -- u32 u_off; -- /* offset from start to tile in V plane, for planar formats */ -- u32 v_off; --}; -- --struct ipu_image_convert_image { -- struct ipu_image base; -- enum ipu_image_convert_type type; -- -- const struct ipu_image_pixfmt *fmt; -- unsigned int stride; -- -- /* # of rows (horizontal stripes) if dest height is > 1024 */ -- unsigned int num_rows; -- /* # of columns (vertical stripes) if dest width is > 1024 */ -- unsigned int num_cols; -- -- struct ipu_image_tile tile[MAX_TILES]; --}; -- --struct ipu_image_pixfmt { -- u32 fourcc; /* V4L2 fourcc */ -- int bpp; /* total bpp */ -- int uv_width_dec; /* decimation in width for U/V planes */ -- int uv_height_dec; /* decimation in height for U/V planes */ -- bool planar; /* planar format */ -- bool uv_swapped; /* U and V planes are swapped */ -- bool uv_packed; /* partial planar (U and V in same plane) */ --}; -- --struct ipu_image_convert_ctx; --struct ipu_image_convert_chan; --struct ipu_image_convert_priv; -- --struct ipu_image_convert_ctx { -- struct ipu_image_convert_chan *chan; -- -- ipu_image_convert_cb_t complete; -- void *complete_context; -- -- /* Source/destination image data and rotation mode */ -- struct ipu_image_convert_image in; -- struct ipu_image_convert_image out; -- struct ipu_ic_csc csc; -- enum ipu_rotate_mode rot_mode; -- u32 downsize_coeff_h; -- u32 downsize_coeff_v; -- u32 image_resize_coeff_h; -- u32 image_resize_coeff_v; -- u32 resize_coeffs_h[MAX_STRIPES_W]; -- u32 resize_coeffs_v[MAX_STRIPES_H]; -- -- /* intermediate buffer for rotation */ -- struct ipu_image_convert_dma_buf rot_intermediate[2]; -- -- /* current buffer number for double buffering */ -- int cur_buf_num; -- -- bool aborting; -- struct completion aborted; -- -- /* can we use double-buffering for this conversion operation? */ -- bool double_buffering; -- /* num_rows * num_cols */ -- unsigned int num_tiles; -- /* next tile to process */ -- unsigned int next_tile; -- /* where to place converted tile in dest image */ -- unsigned int out_tile_map[MAX_TILES]; -- -- struct list_head list; --}; -- --struct ipu_image_convert_chan { -- struct ipu_image_convert_priv *priv; -- -- enum ipu_ic_task ic_task; -- const struct ipu_image_convert_dma_chan *dma_ch; -- -- struct ipu_ic *ic; -- struct ipuv3_channel *in_chan; -- struct ipuv3_channel *out_chan; -- struct ipuv3_channel *rotation_in_chan; -- struct ipuv3_channel *rotation_out_chan; -- -- /* the IPU end-of-frame irqs */ -- int out_eof_irq; -- int rot_out_eof_irq; -- -- spinlock_t irqlock; -- -- /* list of convert contexts */ -- struct list_head ctx_list; -- /* queue of conversion runs */ -- struct list_head pending_q; -- /* queue of completed runs */ -- struct list_head done_q; -- -- /* the current conversion run */ -- struct ipu_image_convert_run *current_run; --}; -- --struct ipu_image_convert_priv { -- struct ipu_image_convert_chan chan[IC_NUM_TASKS]; -- struct ipu_soc *ipu; --}; -- --static const struct ipu_image_convert_dma_chan --image_convert_dma_chan[IC_NUM_TASKS] = { -- [IC_TASK_VIEWFINDER] = { -- .in = IPUV3_CHANNEL_MEM_IC_PRP_VF, -- .out = IPUV3_CHANNEL_IC_PRP_VF_MEM, -- .rot_in = IPUV3_CHANNEL_MEM_ROT_VF, -- .rot_out = IPUV3_CHANNEL_ROT_VF_MEM, -- .vdi_in_p = IPUV3_CHANNEL_MEM_VDI_PREV, -- .vdi_in = IPUV3_CHANNEL_MEM_VDI_CUR, -- .vdi_in_n = IPUV3_CHANNEL_MEM_VDI_NEXT, -- }, -- [IC_TASK_POST_PROCESSOR] = { -- .in = IPUV3_CHANNEL_MEM_IC_PP, -- .out = IPUV3_CHANNEL_IC_PP_MEM, -- .rot_in = IPUV3_CHANNEL_MEM_ROT_PP, -- .rot_out = IPUV3_CHANNEL_ROT_PP_MEM, -- }, --}; -- --static const struct ipu_image_pixfmt image_convert_formats[] = { -- { -- .fourcc = V4L2_PIX_FMT_RGB565, -- .bpp = 16, -- }, { -- .fourcc = V4L2_PIX_FMT_RGB24, -- .bpp = 24, -- }, { -- .fourcc = V4L2_PIX_FMT_BGR24, -- .bpp = 24, -- }, { -- .fourcc = V4L2_PIX_FMT_RGB32, -- .bpp = 32, -- }, { -- .fourcc = V4L2_PIX_FMT_BGR32, -- .bpp = 32, -- }, { -- .fourcc = V4L2_PIX_FMT_XRGB32, -- .bpp = 32, -- }, { -- .fourcc = V4L2_PIX_FMT_XBGR32, -- .bpp = 32, -- }, { -- .fourcc = V4L2_PIX_FMT_BGRX32, -- .bpp = 32, -- }, { -- .fourcc = V4L2_PIX_FMT_RGBX32, -- .bpp = 32, -- }, { -- .fourcc = V4L2_PIX_FMT_YUYV, -- .bpp = 16, -- .uv_width_dec = 2, -- .uv_height_dec = 1, -- }, { -- .fourcc = V4L2_PIX_FMT_UYVY, -- .bpp = 16, -- .uv_width_dec = 2, -- .uv_height_dec = 1, -- }, { -- .fourcc = V4L2_PIX_FMT_YUV420, -- .bpp = 12, -- .planar = true, -- .uv_width_dec = 2, -- .uv_height_dec = 2, -- }, { -- .fourcc = V4L2_PIX_FMT_YVU420, -- .bpp = 12, -- .planar = true, -- .uv_width_dec = 2, -- .uv_height_dec = 2, -- .uv_swapped = true, -- }, { -- .fourcc = V4L2_PIX_FMT_NV12, -- .bpp = 12, -- .planar = true, -- .uv_width_dec = 2, -- .uv_height_dec = 2, -- .uv_packed = true, -- }, { -- .fourcc = V4L2_PIX_FMT_YUV422P, -- .bpp = 16, -- .planar = true, -- .uv_width_dec = 2, -- .uv_height_dec = 1, -- }, { -- .fourcc = V4L2_PIX_FMT_NV16, -- .bpp = 16, -- .planar = true, -- .uv_width_dec = 2, -- .uv_height_dec = 1, -- .uv_packed = true, -- }, --}; -- --static const struct ipu_image_pixfmt *get_format(u32 fourcc) --{ -- const struct ipu_image_pixfmt *ret = NULL; -- unsigned int i; -- -- for (i = 0; i < ARRAY_SIZE(image_convert_formats); i++) { -- if (image_convert_formats[i].fourcc == fourcc) { -- ret = &image_convert_formats[i]; -- break; -- } -- } -- -- return ret; --} -- --static void dump_format(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *ic_image) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- -- dev_dbg(priv->ipu->dev, -- "task %u: ctx %p: %s format: %dx%d (%dx%d tiles), %c%c%c%c\n", -- chan->ic_task, ctx, -- ic_image->type == IMAGE_CONVERT_OUT ? "Output" : "Input", -- ic_image->base.pix.width, ic_image->base.pix.height, -- ic_image->num_cols, ic_image->num_rows, -- ic_image->fmt->fourcc & 0xff, -- (ic_image->fmt->fourcc >> 8) & 0xff, -- (ic_image->fmt->fourcc >> 16) & 0xff, -- (ic_image->fmt->fourcc >> 24) & 0xff); --} -- --int ipu_image_convert_enum_format(int index, u32 *fourcc) --{ -- const struct ipu_image_pixfmt *fmt; -- -- if (index >= (int)ARRAY_SIZE(image_convert_formats)) -- return -EINVAL; -- -- /* Format found */ -- fmt = &image_convert_formats[index]; -- *fourcc = fmt->fourcc; -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_image_convert_enum_format); -- --static void free_dma_buf(struct ipu_image_convert_priv *priv, -- struct ipu_image_convert_dma_buf *buf) --{ -- if (buf->virt) -- dma_free_coherent(priv->ipu->dev, -- buf->len, buf->virt, buf->phys); -- buf->virt = NULL; -- buf->phys = 0; --} -- --static int alloc_dma_buf(struct ipu_image_convert_priv *priv, -- struct ipu_image_convert_dma_buf *buf, -- int size) --{ -- buf->len = PAGE_ALIGN(size); -- buf->virt = dma_alloc_coherent(priv->ipu->dev, buf->len, &buf->phys, -- GFP_DMA | GFP_KERNEL); -- if (!buf->virt) { -- dev_err(priv->ipu->dev, "failed to alloc dma buffer\n"); -- return -ENOMEM; -- } -- -- return 0; --} -- --static inline int num_stripes(int dim) --{ -- return (dim - 1) / 1024 + 1; --} -- --/* -- * Calculate downsizing coefficients, which are the same for all tiles, -- * and initial bilinear resizing coefficients, which are used to find the -- * best seam positions. -- * Also determine the number of tiles necessary to guarantee that no tile -- * is larger than 1024 pixels in either dimension at the output and between -- * IC downsizing and main processing sections. -- */ --static int calc_image_resize_coefficients(struct ipu_image_convert_ctx *ctx, -- struct ipu_image *in, -- struct ipu_image *out) --{ -- u32 downsized_width = in->rect.width; -- u32 downsized_height = in->rect.height; -- u32 downsize_coeff_v = 0; -- u32 downsize_coeff_h = 0; -- u32 resized_width = out->rect.width; -- u32 resized_height = out->rect.height; -- u32 resize_coeff_h; -- u32 resize_coeff_v; -- u32 cols; -- u32 rows; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- resized_width = out->rect.height; -- resized_height = out->rect.width; -- } -- -- /* Do not let invalid input lead to an endless loop below */ -- if (WARN_ON(resized_width == 0 || resized_height == 0)) -- return -EINVAL; -- -- while (downsized_width >= resized_width * 2) { -- downsized_width >>= 1; -- downsize_coeff_h++; -- } -- -- while (downsized_height >= resized_height * 2) { -- downsized_height >>= 1; -- downsize_coeff_v++; -- } -- -- /* -- * Calculate the bilinear resizing coefficients that could be used if -- * we were converting with a single tile. The bottom right output pixel -- * should sample as close as possible to the bottom right input pixel -- * out of the decimator, but not overshoot it: -- */ -- resize_coeff_h = 8192 * (downsized_width - 1) / (resized_width - 1); -- resize_coeff_v = 8192 * (downsized_height - 1) / (resized_height - 1); -- -- /* -- * Both the output of the IC downsizing section before being passed to -- * the IC main processing section and the final output of the IC main -- * processing section must be <= 1024 pixels in both dimensions. -- */ -- cols = num_stripes(max_t(u32, downsized_width, resized_width)); -- rows = num_stripes(max_t(u32, downsized_height, resized_height)); -- -- dev_dbg(ctx->chan->priv->ipu->dev, -- "%s: hscale: >>%u, *8192/%u vscale: >>%u, *8192/%u, %ux%u tiles\n", -- __func__, downsize_coeff_h, resize_coeff_h, downsize_coeff_v, -- resize_coeff_v, cols, rows); -- -- if (downsize_coeff_h > 2 || downsize_coeff_v > 2 || -- resize_coeff_h > 0x3fff || resize_coeff_v > 0x3fff) -- return -EINVAL; -- -- ctx->downsize_coeff_h = downsize_coeff_h; -- ctx->downsize_coeff_v = downsize_coeff_v; -- ctx->image_resize_coeff_h = resize_coeff_h; -- ctx->image_resize_coeff_v = resize_coeff_v; -- ctx->in.num_cols = cols; -- ctx->in.num_rows = rows; -- -- return 0; --} -- --#define round_closest(x, y) round_down((x) + (y)/2, (y)) -- --/* -- * Find the best aligned seam position for the given column / row index. -- * Rotation and image offsets are out of scope. -- * -- * @index: column / row index, used to calculate valid interval -- * @in_edge: input right / bottom edge -- * @out_edge: output right / bottom edge -- * @in_align: input alignment, either horizontal 8-byte line start address -- * alignment, or pixel alignment due to image format -- * @out_align: output alignment, either horizontal 8-byte line start address -- * alignment, or pixel alignment due to image format or rotator -- * block size -- * @in_burst: horizontal input burst size in case of horizontal flip -- * @out_burst: horizontal output burst size or rotator block size -- * @downsize_coeff: downsizing section coefficient -- * @resize_coeff: main processing section resizing coefficient -- * @_in_seam: aligned input seam position return value -- * @_out_seam: aligned output seam position return value -- */ --static void find_best_seam(struct ipu_image_convert_ctx *ctx, -- unsigned int index, -- unsigned int in_edge, -- unsigned int out_edge, -- unsigned int in_align, -- unsigned int out_align, -- unsigned int in_burst, -- unsigned int out_burst, -- unsigned int downsize_coeff, -- unsigned int resize_coeff, -- u32 *_in_seam, -- u32 *_out_seam) --{ -- struct device *dev = ctx->chan->priv->ipu->dev; -- unsigned int out_pos; -- /* Input / output seam position candidates */ -- unsigned int out_seam = 0; -- unsigned int in_seam = 0; -- unsigned int min_diff = UINT_MAX; -- unsigned int out_start; -- unsigned int out_end; -- unsigned int in_start; -- unsigned int in_end; -- -- /* Start within 1024 pixels of the right / bottom edge */ -- out_start = max_t(int, index * out_align, out_edge - 1024); -- /* End before having to add more columns to the left / rows above */ -- out_end = min_t(unsigned int, out_edge, index * 1024 + 1); -- -- /* -- * Limit input seam position to make sure that the downsized input tile -- * to the right or bottom does not exceed 1024 pixels. -- */ -- in_start = max_t(int, index * in_align, -- in_edge - (1024 << downsize_coeff)); -- in_end = min_t(unsigned int, in_edge, -- index * (1024 << downsize_coeff) + 1); -- -- /* -- * Output tiles must start at a multiple of 8 bytes horizontally and -- * possibly at an even line horizontally depending on the pixel format. -- * Only consider output aligned positions for the seam. -- */ -- out_start = round_up(out_start, out_align); -- for (out_pos = out_start; out_pos < out_end; out_pos += out_align) { -- unsigned int in_pos; -- unsigned int in_pos_aligned; -- unsigned int in_pos_rounded; -- unsigned int abs_diff; -- -- /* -- * Tiles in the right row / bottom column may not be allowed to -- * overshoot horizontally / vertically. out_burst may be the -- * actual DMA burst size, or the rotator block size. -- */ -- if ((out_burst > 1) && (out_edge - out_pos) % out_burst) -- continue; -- -- /* -- * Input sample position, corresponding to out_pos, 19.13 fixed -- * point. -- */ -- in_pos = (out_pos * resize_coeff) << downsize_coeff; -- /* -- * The closest input sample position that we could actually -- * start the input tile at, 19.13 fixed point. -- */ -- in_pos_aligned = round_closest(in_pos, 8192U * in_align); -- /* Convert 19.13 fixed point to integer */ -- in_pos_rounded = in_pos_aligned / 8192U; -- -- if (in_pos_rounded < in_start) -- continue; -- if (in_pos_rounded >= in_end) -- break; -- -- if ((in_burst > 1) && -- (in_edge - in_pos_rounded) % in_burst) -- continue; -- -- if (in_pos < in_pos_aligned) -- abs_diff = in_pos_aligned - in_pos; -- else -- abs_diff = in_pos - in_pos_aligned; -- -- if (abs_diff < min_diff) { -- in_seam = in_pos_rounded; -- out_seam = out_pos; -- min_diff = abs_diff; -- } -- } -- -- *_out_seam = out_seam; -- *_in_seam = in_seam; -- -- dev_dbg(dev, "%s: out_seam %u(%u) in [%u, %u], in_seam %u(%u) in [%u, %u] diff %u.%03u\n", -- __func__, out_seam, out_align, out_start, out_end, -- in_seam, in_align, in_start, in_end, min_diff / 8192, -- DIV_ROUND_CLOSEST(min_diff % 8192 * 1000, 8192)); --} -- --/* -- * Tile left edges are required to be aligned to multiples of 8 bytes -- * by the IDMAC. -- */ --static inline u32 tile_left_align(const struct ipu_image_pixfmt *fmt) --{ -- if (fmt->planar) -- return fmt->uv_packed ? 8 : 8 * fmt->uv_width_dec; -- else -- return fmt->bpp == 32 ? 2 : fmt->bpp == 16 ? 4 : 8; --} -- --/* -- * Tile top edge alignment is only limited by chroma subsampling. -- */ --static inline u32 tile_top_align(const struct ipu_image_pixfmt *fmt) --{ -- return fmt->uv_height_dec > 1 ? 2 : 1; --} -- --static inline u32 tile_width_align(enum ipu_image_convert_type type, -- const struct ipu_image_pixfmt *fmt, -- enum ipu_rotate_mode rot_mode) --{ -- if (type == IMAGE_CONVERT_IN) { -- /* -- * The IC burst reads 8 pixels at a time. Reading beyond the -- * end of the line is usually acceptable. Those pixels are -- * ignored, unless the IC has to write the scaled line in -- * reverse. -- */ -- return (!ipu_rot_mode_is_irt(rot_mode) && -- (rot_mode & IPU_ROT_BIT_HFLIP)) ? 8 : 2; -- } -- -- /* -- * Align to 16x16 pixel blocks for planar 4:2:0 chroma subsampled -- * formats to guarantee 8-byte aligned line start addresses in the -- * chroma planes when IRT is used. Align to 8x8 pixel IRT block size -- * for all other formats. -- */ -- return (ipu_rot_mode_is_irt(rot_mode) && -- fmt->planar && !fmt->uv_packed) ? -- 8 * fmt->uv_width_dec : 8; --} -- --static inline u32 tile_height_align(enum ipu_image_convert_type type, -- const struct ipu_image_pixfmt *fmt, -- enum ipu_rotate_mode rot_mode) --{ -- if (type == IMAGE_CONVERT_IN || !ipu_rot_mode_is_irt(rot_mode)) -- return 2; -- -- /* -- * Align to 16x16 pixel blocks for planar 4:2:0 chroma subsampled -- * formats to guarantee 8-byte aligned line start addresses in the -- * chroma planes when IRT is used. Align to 8x8 pixel IRT block size -- * for all other formats. -- */ -- return (fmt->planar && !fmt->uv_packed) ? 8 * fmt->uv_width_dec : 8; --} -- --/* -- * Fill in left position and width and for all tiles in an input column, and -- * for all corresponding output tiles. If the 90° rotator is used, the output -- * tiles are in a row, and output tile top position and height are set. -- */ --static void fill_tile_column(struct ipu_image_convert_ctx *ctx, -- unsigned int col, -- struct ipu_image_convert_image *in, -- unsigned int in_left, unsigned int in_width, -- struct ipu_image_convert_image *out, -- unsigned int out_left, unsigned int out_width) --{ -- unsigned int row, tile_idx; -- struct ipu_image_tile *in_tile, *out_tile; -- -- for (row = 0; row < in->num_rows; row++) { -- tile_idx = in->num_cols * row + col; -- in_tile = &in->tile[tile_idx]; -- out_tile = &out->tile[ctx->out_tile_map[tile_idx]]; -- -- in_tile->left = in_left; -- in_tile->width = in_width; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- out_tile->top = out_left; -- out_tile->height = out_width; -- } else { -- out_tile->left = out_left; -- out_tile->width = out_width; -- } -- } --} -- --/* -- * Fill in top position and height and for all tiles in an input row, and -- * for all corresponding output tiles. If the 90° rotator is used, the output -- * tiles are in a column, and output tile left position and width are set. -- */ --static void fill_tile_row(struct ipu_image_convert_ctx *ctx, unsigned int row, -- struct ipu_image_convert_image *in, -- unsigned int in_top, unsigned int in_height, -- struct ipu_image_convert_image *out, -- unsigned int out_top, unsigned int out_height) --{ -- unsigned int col, tile_idx; -- struct ipu_image_tile *in_tile, *out_tile; -- -- for (col = 0; col < in->num_cols; col++) { -- tile_idx = in->num_cols * row + col; -- in_tile = &in->tile[tile_idx]; -- out_tile = &out->tile[ctx->out_tile_map[tile_idx]]; -- -- in_tile->top = in_top; -- in_tile->height = in_height; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- out_tile->left = out_top; -- out_tile->width = out_height; -- } else { -- out_tile->top = out_top; -- out_tile->height = out_height; -- } -- } --} -- --/* -- * Find the best horizontal and vertical seam positions to split into tiles. -- * Minimize the fractional part of the input sampling position for the -- * top / left pixels of each tile. -- */ --static void find_seams(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *in, -- struct ipu_image_convert_image *out) --{ -- struct device *dev = ctx->chan->priv->ipu->dev; -- unsigned int resized_width = out->base.rect.width; -- unsigned int resized_height = out->base.rect.height; -- unsigned int col; -- unsigned int row; -- unsigned int in_left_align = tile_left_align(in->fmt); -- unsigned int in_top_align = tile_top_align(in->fmt); -- unsigned int out_left_align = tile_left_align(out->fmt); -- unsigned int out_top_align = tile_top_align(out->fmt); -- unsigned int out_width_align = tile_width_align(out->type, out->fmt, -- ctx->rot_mode); -- unsigned int out_height_align = tile_height_align(out->type, out->fmt, -- ctx->rot_mode); -- unsigned int in_right = in->base.rect.width; -- unsigned int in_bottom = in->base.rect.height; -- unsigned int out_right = out->base.rect.width; -- unsigned int out_bottom = out->base.rect.height; -- unsigned int flipped_out_left; -- unsigned int flipped_out_top; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- /* Switch width/height and align top left to IRT block size */ -- resized_width = out->base.rect.height; -- resized_height = out->base.rect.width; -- out_left_align = out_height_align; -- out_top_align = out_width_align; -- out_width_align = out_left_align; -- out_height_align = out_top_align; -- out_right = out->base.rect.height; -- out_bottom = out->base.rect.width; -- } -- -- for (col = in->num_cols - 1; col > 0; col--) { -- bool allow_in_overshoot = ipu_rot_mode_is_irt(ctx->rot_mode) || -- !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); -- bool allow_out_overshoot = (col < in->num_cols - 1) && -- !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); -- unsigned int in_left; -- unsigned int out_left; -- -- /* -- * Align input width to burst length if the scaling step flips -- * horizontally. -- */ -- -- find_best_seam(ctx, col, -- in_right, out_right, -- in_left_align, out_left_align, -- allow_in_overshoot ? 1 : 8 /* burst length */, -- allow_out_overshoot ? 1 : out_width_align, -- ctx->downsize_coeff_h, ctx->image_resize_coeff_h, -- &in_left, &out_left); -- -- if (ctx->rot_mode & IPU_ROT_BIT_HFLIP) -- flipped_out_left = resized_width - out_right; -- else -- flipped_out_left = out_left; -- -- fill_tile_column(ctx, col, in, in_left, in_right - in_left, -- out, flipped_out_left, out_right - out_left); -- -- dev_dbg(dev, "%s: col %u: %u, %u -> %u, %u\n", __func__, col, -- in_left, in_right - in_left, -- flipped_out_left, out_right - out_left); -- -- in_right = in_left; -- out_right = out_left; -- } -- -- flipped_out_left = (ctx->rot_mode & IPU_ROT_BIT_HFLIP) ? -- resized_width - out_right : 0; -- -- fill_tile_column(ctx, 0, in, 0, in_right, -- out, flipped_out_left, out_right); -- -- dev_dbg(dev, "%s: col 0: 0, %u -> %u, %u\n", __func__, -- in_right, flipped_out_left, out_right); -- -- for (row = in->num_rows - 1; row > 0; row--) { -- bool allow_overshoot = row < in->num_rows - 1; -- unsigned int in_top; -- unsigned int out_top; -- -- find_best_seam(ctx, row, -- in_bottom, out_bottom, -- in_top_align, out_top_align, -- 1, allow_overshoot ? 1 : out_height_align, -- ctx->downsize_coeff_v, ctx->image_resize_coeff_v, -- &in_top, &out_top); -- -- if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^ -- ipu_rot_mode_is_irt(ctx->rot_mode)) -- flipped_out_top = resized_height - out_bottom; -- else -- flipped_out_top = out_top; -- -- fill_tile_row(ctx, row, in, in_top, in_bottom - in_top, -- out, flipped_out_top, out_bottom - out_top); -- -- dev_dbg(dev, "%s: row %u: %u, %u -> %u, %u\n", __func__, row, -- in_top, in_bottom - in_top, -- flipped_out_top, out_bottom - out_top); -- -- in_bottom = in_top; -- out_bottom = out_top; -- } -- -- if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^ -- ipu_rot_mode_is_irt(ctx->rot_mode)) -- flipped_out_top = resized_height - out_bottom; -- else -- flipped_out_top = 0; -- -- fill_tile_row(ctx, 0, in, 0, in_bottom, -- out, flipped_out_top, out_bottom); -- -- dev_dbg(dev, "%s: row 0: 0, %u -> %u, %u\n", __func__, -- in_bottom, flipped_out_top, out_bottom); --} -- --static int calc_tile_dimensions(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *image) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- unsigned int max_width = 1024; -- unsigned int max_height = 1024; -- unsigned int i; -- -- if (image->type == IMAGE_CONVERT_IN) { -- /* Up to 4096x4096 input tile size */ -- max_width <<= ctx->downsize_coeff_h; -- max_height <<= ctx->downsize_coeff_v; -- } -- -- for (i = 0; i < ctx->num_tiles; i++) { -- struct ipu_image_tile *tile; -- const unsigned int row = i / image->num_cols; -- const unsigned int col = i % image->num_cols; -- -- if (image->type == IMAGE_CONVERT_OUT) -- tile = &image->tile[ctx->out_tile_map[i]]; -- else -- tile = &image->tile[i]; -- -- tile->size = ((tile->height * image->fmt->bpp) >> 3) * -- tile->width; -- -- if (image->fmt->planar) { -- tile->stride = tile->width; -- tile->rot_stride = tile->height; -- } else { -- tile->stride = -- (image->fmt->bpp * tile->width) >> 3; -- tile->rot_stride = -- (image->fmt->bpp * tile->height) >> 3; -- } -- -- dev_dbg(priv->ipu->dev, -- "task %u: ctx %p: %s@[%u,%u]: %ux%u@%u,%u\n", -- chan->ic_task, ctx, -- image->type == IMAGE_CONVERT_IN ? "Input" : "Output", -- row, col, -- tile->width, tile->height, tile->left, tile->top); -- -- if (!tile->width || tile->width > max_width || -- !tile->height || tile->height > max_height) { -- dev_err(priv->ipu->dev, "invalid %s tile size: %ux%u\n", -- image->type == IMAGE_CONVERT_IN ? "input" : -- "output", tile->width, tile->height); -- return -EINVAL; -- } -- } -- -- return 0; --} -- --/* -- * Use the rotation transformation to find the tile coordinates -- * (row, col) of a tile in the destination frame that corresponds -- * to the given tile coordinates of a source frame. The destination -- * coordinate is then converted to a tile index. -- */ --static int transform_tile_index(struct ipu_image_convert_ctx *ctx, -- int src_row, int src_col) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_image *s_image = &ctx->in; -- struct ipu_image_convert_image *d_image = &ctx->out; -- int dst_row, dst_col; -- -- /* with no rotation it's a 1:1 mapping */ -- if (ctx->rot_mode == IPU_ROTATE_NONE) -- return src_row * s_image->num_cols + src_col; -- -- /* -- * before doing the transform, first we have to translate -- * source row,col for an origin in the center of s_image -- */ -- src_row = src_row * 2 - (s_image->num_rows - 1); -- src_col = src_col * 2 - (s_image->num_cols - 1); -- -- /* do the rotation transform */ -- if (ctx->rot_mode & IPU_ROT_BIT_90) { -- dst_col = -src_row; -- dst_row = src_col; -- } else { -- dst_col = src_col; -- dst_row = src_row; -- } -- -- /* apply flip */ -- if (ctx->rot_mode & IPU_ROT_BIT_HFLIP) -- dst_col = -dst_col; -- if (ctx->rot_mode & IPU_ROT_BIT_VFLIP) -- dst_row = -dst_row; -- -- dev_dbg(priv->ipu->dev, "task %u: ctx %p: [%d,%d] --> [%d,%d]\n", -- chan->ic_task, ctx, src_col, src_row, dst_col, dst_row); -- -- /* -- * finally translate dest row,col using an origin in upper -- * left of d_image -- */ -- dst_row += d_image->num_rows - 1; -- dst_col += d_image->num_cols - 1; -- dst_row /= 2; -- dst_col /= 2; -- -- return dst_row * d_image->num_cols + dst_col; --} -- --/* -- * Fill the out_tile_map[] with transformed destination tile indeces. -- */ --static void calc_out_tile_map(struct ipu_image_convert_ctx *ctx) --{ -- struct ipu_image_convert_image *s_image = &ctx->in; -- unsigned int row, col, tile = 0; -- -- for (row = 0; row < s_image->num_rows; row++) { -- for (col = 0; col < s_image->num_cols; col++) { -- ctx->out_tile_map[tile] = -- transform_tile_index(ctx, row, col); -- tile++; -- } -- } --} -- --static int calc_tile_offsets_planar(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *image) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- const struct ipu_image_pixfmt *fmt = image->fmt; -- unsigned int row, col, tile = 0; -- u32 H, top, y_stride, uv_stride; -- u32 uv_row_off, uv_col_off, uv_off, u_off, v_off, tmp; -- u32 y_row_off, y_col_off, y_off; -- u32 y_size, uv_size; -- -- /* setup some convenience vars */ -- H = image->base.pix.height; -- -- y_stride = image->stride; -- uv_stride = y_stride / fmt->uv_width_dec; -- if (fmt->uv_packed) -- uv_stride *= 2; -- -- y_size = H * y_stride; -- uv_size = y_size / (fmt->uv_width_dec * fmt->uv_height_dec); -- -- for (row = 0; row < image->num_rows; row++) { -- top = image->tile[tile].top; -- y_row_off = top * y_stride; -- uv_row_off = (top * uv_stride) / fmt->uv_height_dec; -- -- for (col = 0; col < image->num_cols; col++) { -- y_col_off = image->tile[tile].left; -- uv_col_off = y_col_off / fmt->uv_width_dec; -- if (fmt->uv_packed) -- uv_col_off *= 2; -- -- y_off = y_row_off + y_col_off; -- uv_off = uv_row_off + uv_col_off; -- -- u_off = y_size - y_off + uv_off; -- v_off = (fmt->uv_packed) ? 0 : u_off + uv_size; -- if (fmt->uv_swapped) { -- tmp = u_off; -- u_off = v_off; -- v_off = tmp; -- } -- -- image->tile[tile].offset = y_off; -- image->tile[tile].u_off = u_off; -- image->tile[tile++].v_off = v_off; -- -- if ((y_off & 0x7) || (u_off & 0x7) || (v_off & 0x7)) { -- dev_err(priv->ipu->dev, -- "task %u: ctx %p: %s@[%d,%d]: " -- "y_off %08x, u_off %08x, v_off %08x\n", -- chan->ic_task, ctx, -- image->type == IMAGE_CONVERT_IN ? -- "Input" : "Output", row, col, -- y_off, u_off, v_off); -- return -EINVAL; -- } -- } -- } -- -- return 0; --} -- --static int calc_tile_offsets_packed(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *image) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- const struct ipu_image_pixfmt *fmt = image->fmt; -- unsigned int row, col, tile = 0; -- u32 bpp, stride, offset; -- u32 row_off, col_off; -- -- /* setup some convenience vars */ -- stride = image->stride; -- bpp = fmt->bpp; -- -- for (row = 0; row < image->num_rows; row++) { -- row_off = image->tile[tile].top * stride; -- -- for (col = 0; col < image->num_cols; col++) { -- col_off = (image->tile[tile].left * bpp) >> 3; -- -- offset = row_off + col_off; -- -- image->tile[tile].offset = offset; -- image->tile[tile].u_off = 0; -- image->tile[tile++].v_off = 0; -- -- if (offset & 0x7) { -- dev_err(priv->ipu->dev, -- "task %u: ctx %p: %s@[%d,%d]: " -- "phys %08x\n", -- chan->ic_task, ctx, -- image->type == IMAGE_CONVERT_IN ? -- "Input" : "Output", row, col, -- row_off + col_off); -- return -EINVAL; -- } -- } -- } -- -- return 0; --} -- --static int calc_tile_offsets(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *image) --{ -- if (image->fmt->planar) -- return calc_tile_offsets_planar(ctx, image); -- -- return calc_tile_offsets_packed(ctx, image); --} -- --/* -- * Calculate the resizing ratio for the IC main processing section given input -- * size, fixed downsizing coefficient, and output size. -- * Either round to closest for the next tile's first pixel to minimize seams -- * and distortion (for all but right column / bottom row), or round down to -- * avoid sampling beyond the edges of the input image for this tile's last -- * pixel. -- * Returns the resizing coefficient, resizing ratio is 8192.0 / resize_coeff. -- */ --static u32 calc_resize_coeff(u32 input_size, u32 downsize_coeff, -- u32 output_size, bool allow_overshoot) --{ -- u32 downsized = input_size >> downsize_coeff; -- -- if (allow_overshoot) -- return DIV_ROUND_CLOSEST(8192 * downsized, output_size); -- else -- return 8192 * (downsized - 1) / (output_size - 1); --} -- --/* -- * Slightly modify resize coefficients per tile to hide the bilinear -- * interpolator reset at tile borders, shifting the right / bottom edge -- * by up to a half input pixel. This removes noticeable seams between -- * tiles at higher upscaling factors. -- */ --static void calc_tile_resize_coefficients(struct ipu_image_convert_ctx *ctx) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_tile *in_tile, *out_tile; -- unsigned int col, row, tile_idx; -- unsigned int last_output; -- -- for (col = 0; col < ctx->in.num_cols; col++) { -- bool closest = (col < ctx->in.num_cols - 1) && -- !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); -- u32 resized_width; -- u32 resize_coeff_h; -- u32 in_width; -- -- tile_idx = col; -- in_tile = &ctx->in.tile[tile_idx]; -- out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) -- resized_width = out_tile->height; -- else -- resized_width = out_tile->width; -- -- resize_coeff_h = calc_resize_coeff(in_tile->width, -- ctx->downsize_coeff_h, -- resized_width, closest); -- -- dev_dbg(priv->ipu->dev, "%s: column %u hscale: *8192/%u\n", -- __func__, col, resize_coeff_h); -- -- /* -- * With the horizontal scaling factor known, round up resized -- * width (output width or height) to burst size. -- */ -- resized_width = round_up(resized_width, 8); -- -- /* -- * Calculate input width from the last accessed input pixel -- * given resized width and scaling coefficients. Round up to -- * burst size. -- */ -- last_output = resized_width - 1; -- if (closest && ((last_output * resize_coeff_h) % 8192)) -- last_output++; -- in_width = round_up( -- (DIV_ROUND_UP(last_output * resize_coeff_h, 8192) + 1) -- << ctx->downsize_coeff_h, 8); -- -- for (row = 0; row < ctx->in.num_rows; row++) { -- tile_idx = row * ctx->in.num_cols + col; -- in_tile = &ctx->in.tile[tile_idx]; -- out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) -- out_tile->height = resized_width; -- else -- out_tile->width = resized_width; -- -- in_tile->width = in_width; -- } -- -- ctx->resize_coeffs_h[col] = resize_coeff_h; -- } -- -- for (row = 0; row < ctx->in.num_rows; row++) { -- bool closest = (row < ctx->in.num_rows - 1) && -- !(ctx->rot_mode & IPU_ROT_BIT_VFLIP); -- u32 resized_height; -- u32 resize_coeff_v; -- u32 in_height; -- -- tile_idx = row * ctx->in.num_cols; -- in_tile = &ctx->in.tile[tile_idx]; -- out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) -- resized_height = out_tile->width; -- else -- resized_height = out_tile->height; -- -- resize_coeff_v = calc_resize_coeff(in_tile->height, -- ctx->downsize_coeff_v, -- resized_height, closest); -- -- dev_dbg(priv->ipu->dev, "%s: row %u vscale: *8192/%u\n", -- __func__, row, resize_coeff_v); -- -- /* -- * With the vertical scaling factor known, round up resized -- * height (output width or height) to IDMAC limitations. -- */ -- resized_height = round_up(resized_height, 2); -- -- /* -- * Calculate input width from the last accessed input pixel -- * given resized height and scaling coefficients. Align to -- * IDMAC restrictions. -- */ -- last_output = resized_height - 1; -- if (closest && ((last_output * resize_coeff_v) % 8192)) -- last_output++; -- in_height = round_up( -- (DIV_ROUND_UP(last_output * resize_coeff_v, 8192) + 1) -- << ctx->downsize_coeff_v, 2); -- -- for (col = 0; col < ctx->in.num_cols; col++) { -- tile_idx = row * ctx->in.num_cols + col; -- in_tile = &ctx->in.tile[tile_idx]; -- out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) -- out_tile->width = resized_height; -- else -- out_tile->height = resized_height; -- -- in_tile->height = in_height; -- } -- -- ctx->resize_coeffs_v[row] = resize_coeff_v; -- } --} -- --/* -- * return the number of runs in given queue (pending_q or done_q) -- * for this context. hold irqlock when calling. -- */ --static int get_run_count(struct ipu_image_convert_ctx *ctx, -- struct list_head *q) --{ -- struct ipu_image_convert_run *run; -- int count = 0; -- -- lockdep_assert_held(&ctx->chan->irqlock); -- -- list_for_each_entry(run, q, list) { -- if (run->ctx == ctx) -- count++; -- } -- -- return count; --} -- --static void convert_stop(struct ipu_image_convert_run *run) --{ -- struct ipu_image_convert_ctx *ctx = run->ctx; -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- -- dev_dbg(priv->ipu->dev, "%s: task %u: stopping ctx %p run %p\n", -- __func__, chan->ic_task, ctx, run); -- -- /* disable IC tasks and the channels */ -- ipu_ic_task_disable(chan->ic); -- ipu_idmac_disable_channel(chan->in_chan); -- ipu_idmac_disable_channel(chan->out_chan); -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- ipu_idmac_disable_channel(chan->rotation_in_chan); -- ipu_idmac_disable_channel(chan->rotation_out_chan); -- ipu_idmac_unlink(chan->out_chan, chan->rotation_in_chan); -- } -- -- ipu_ic_disable(chan->ic); --} -- --static void init_idmac_channel(struct ipu_image_convert_ctx *ctx, -- struct ipuv3_channel *channel, -- struct ipu_image_convert_image *image, -- enum ipu_rotate_mode rot_mode, -- bool rot_swap_width_height, -- unsigned int tile) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- unsigned int burst_size; -- u32 width, height, stride; -- dma_addr_t addr0, addr1 = 0; -- struct ipu_image tile_image; -- unsigned int tile_idx[2]; -- -- if (image->type == IMAGE_CONVERT_OUT) { -- tile_idx[0] = ctx->out_tile_map[tile]; -- tile_idx[1] = ctx->out_tile_map[1]; -- } else { -- tile_idx[0] = tile; -- tile_idx[1] = 1; -- } -- -- if (rot_swap_width_height) { -- width = image->tile[tile_idx[0]].height; -- height = image->tile[tile_idx[0]].width; -- stride = image->tile[tile_idx[0]].rot_stride; -- addr0 = ctx->rot_intermediate[0].phys; -- if (ctx->double_buffering) -- addr1 = ctx->rot_intermediate[1].phys; -- } else { -- width = image->tile[tile_idx[0]].width; -- height = image->tile[tile_idx[0]].height; -- stride = image->stride; -- addr0 = image->base.phys0 + -- image->tile[tile_idx[0]].offset; -- if (ctx->double_buffering) -- addr1 = image->base.phys0 + -- image->tile[tile_idx[1]].offset; -- } -- -- ipu_cpmem_zero(channel); -- -- memset(&tile_image, 0, sizeof(tile_image)); -- tile_image.pix.width = tile_image.rect.width = width; -- tile_image.pix.height = tile_image.rect.height = height; -- tile_image.pix.bytesperline = stride; -- tile_image.pix.pixelformat = image->fmt->fourcc; -- tile_image.phys0 = addr0; -- tile_image.phys1 = addr1; -- if (image->fmt->planar && !rot_swap_width_height) { -- tile_image.u_offset = image->tile[tile_idx[0]].u_off; -- tile_image.v_offset = image->tile[tile_idx[0]].v_off; -- } -- -- ipu_cpmem_set_image(channel, &tile_image); -- -- if (rot_mode) -- ipu_cpmem_set_rotation(channel, rot_mode); -- -- /* -- * Skip writing U and V components to odd rows in the output -- * channels for planar 4:2:0. -- */ -- if ((channel == chan->out_chan || -- channel == chan->rotation_out_chan) && -- image->fmt->planar && image->fmt->uv_height_dec == 2) -- ipu_cpmem_skip_odd_chroma_rows(channel); -- -- if (channel == chan->rotation_in_chan || -- channel == chan->rotation_out_chan) { -- burst_size = 8; -- ipu_cpmem_set_block_mode(channel); -- } else -- burst_size = (width % 16) ? 8 : 16; -- -- ipu_cpmem_set_burstsize(channel, burst_size); -- -- ipu_ic_task_idma_init(chan->ic, channel, width, height, -- burst_size, rot_mode); -- -- /* -- * Setting a non-zero AXI ID collides with the PRG AXI snooping, so -- * only do this when there is no PRG present. -- */ -- if (!channel->ipu->prg_priv) -- ipu_cpmem_set_axi_id(channel, 1); -- -- ipu_idmac_set_double_buffer(channel, ctx->double_buffering); --} -- --static int convert_start(struct ipu_image_convert_run *run, unsigned int tile) --{ -- struct ipu_image_convert_ctx *ctx = run->ctx; -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_image *s_image = &ctx->in; -- struct ipu_image_convert_image *d_image = &ctx->out; -- unsigned int dst_tile = ctx->out_tile_map[tile]; -- unsigned int dest_width, dest_height; -- unsigned int col, row; -- u32 rsc; -- int ret; -- -- dev_dbg(priv->ipu->dev, "%s: task %u: starting ctx %p run %p tile %u -> %u\n", -- __func__, chan->ic_task, ctx, run, tile, dst_tile); -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- /* swap width/height for resizer */ -- dest_width = d_image->tile[dst_tile].height; -- dest_height = d_image->tile[dst_tile].width; -- } else { -- dest_width = d_image->tile[dst_tile].width; -- dest_height = d_image->tile[dst_tile].height; -- } -- -- row = tile / s_image->num_cols; -- col = tile % s_image->num_cols; -- -- rsc = (ctx->downsize_coeff_v << 30) | -- (ctx->resize_coeffs_v[row] << 16) | -- (ctx->downsize_coeff_h << 14) | -- (ctx->resize_coeffs_h[col]); -- -- dev_dbg(priv->ipu->dev, "%s: %ux%u -> %ux%u (rsc = 0x%x)\n", -- __func__, s_image->tile[tile].width, -- s_image->tile[tile].height, dest_width, dest_height, rsc); -- -- /* setup the IC resizer and CSC */ -- ret = ipu_ic_task_init_rsc(chan->ic, &ctx->csc, -- s_image->tile[tile].width, -- s_image->tile[tile].height, -- dest_width, -- dest_height, -- rsc); -- if (ret) { -- dev_err(priv->ipu->dev, "ipu_ic_task_init failed, %d\n", ret); -- return ret; -- } -- -- /* init the source MEM-->IC PP IDMAC channel */ -- init_idmac_channel(ctx, chan->in_chan, s_image, -- IPU_ROTATE_NONE, false, tile); -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- /* init the IC PP-->MEM IDMAC channel */ -- init_idmac_channel(ctx, chan->out_chan, d_image, -- IPU_ROTATE_NONE, true, tile); -- -- /* init the MEM-->IC PP ROT IDMAC channel */ -- init_idmac_channel(ctx, chan->rotation_in_chan, d_image, -- ctx->rot_mode, true, tile); -- -- /* init the destination IC PP ROT-->MEM IDMAC channel */ -- init_idmac_channel(ctx, chan->rotation_out_chan, d_image, -- IPU_ROTATE_NONE, false, tile); -- -- /* now link IC PP-->MEM to MEM-->IC PP ROT */ -- ipu_idmac_link(chan->out_chan, chan->rotation_in_chan); -- } else { -- /* init the destination IC PP-->MEM IDMAC channel */ -- init_idmac_channel(ctx, chan->out_chan, d_image, -- ctx->rot_mode, false, tile); -- } -- -- /* enable the IC */ -- ipu_ic_enable(chan->ic); -- -- /* set buffers ready */ -- ipu_idmac_select_buffer(chan->in_chan, 0); -- ipu_idmac_select_buffer(chan->out_chan, 0); -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) -- ipu_idmac_select_buffer(chan->rotation_out_chan, 0); -- if (ctx->double_buffering) { -- ipu_idmac_select_buffer(chan->in_chan, 1); -- ipu_idmac_select_buffer(chan->out_chan, 1); -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) -- ipu_idmac_select_buffer(chan->rotation_out_chan, 1); -- } -- -- /* enable the channels! */ -- ipu_idmac_enable_channel(chan->in_chan); -- ipu_idmac_enable_channel(chan->out_chan); -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- ipu_idmac_enable_channel(chan->rotation_in_chan); -- ipu_idmac_enable_channel(chan->rotation_out_chan); -- } -- -- ipu_ic_task_enable(chan->ic); -- -- ipu_cpmem_dump(chan->in_chan); -- ipu_cpmem_dump(chan->out_chan); -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- ipu_cpmem_dump(chan->rotation_in_chan); -- ipu_cpmem_dump(chan->rotation_out_chan); -- } -- -- ipu_dump(priv->ipu); -- -- return 0; --} -- --/* hold irqlock when calling */ --static int do_run(struct ipu_image_convert_run *run) --{ -- struct ipu_image_convert_ctx *ctx = run->ctx; -- struct ipu_image_convert_chan *chan = ctx->chan; -- -- lockdep_assert_held(&chan->irqlock); -- -- ctx->in.base.phys0 = run->in_phys; -- ctx->out.base.phys0 = run->out_phys; -- -- ctx->cur_buf_num = 0; -- ctx->next_tile = 1; -- -- /* remove run from pending_q and set as current */ -- list_del(&run->list); -- chan->current_run = run; -- -- return convert_start(run, 0); --} -- --/* hold irqlock when calling */ --static void run_next(struct ipu_image_convert_chan *chan) --{ -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_run *run, *tmp; -- int ret; -- -- lockdep_assert_held(&chan->irqlock); -- -- list_for_each_entry_safe(run, tmp, &chan->pending_q, list) { -- /* skip contexts that are aborting */ -- if (run->ctx->aborting) { -- dev_dbg(priv->ipu->dev, -- "%s: task %u: skipping aborting ctx %p run %p\n", -- __func__, chan->ic_task, run->ctx, run); -- continue; -- } -- -- ret = do_run(run); -- if (!ret) -- break; -- -- /* -- * something went wrong with start, add the run -- * to done q and continue to the next run in the -- * pending q. -- */ -- run->status = ret; -- list_add_tail(&run->list, &chan->done_q); -- chan->current_run = NULL; -- } --} -- --static void empty_done_q(struct ipu_image_convert_chan *chan) --{ -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_run *run; -- unsigned long flags; -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- while (!list_empty(&chan->done_q)) { -- run = list_entry(chan->done_q.next, -- struct ipu_image_convert_run, -- list); -- -- list_del(&run->list); -- -- dev_dbg(priv->ipu->dev, -- "%s: task %u: completing ctx %p run %p with %d\n", -- __func__, chan->ic_task, run->ctx, run, run->status); -- -- /* call the completion callback and free the run */ -- spin_unlock_irqrestore(&chan->irqlock, flags); -- run->ctx->complete(run, run->ctx->complete_context); -- spin_lock_irqsave(&chan->irqlock, flags); -- } -- -- spin_unlock_irqrestore(&chan->irqlock, flags); --} -- --/* -- * the bottom half thread clears out the done_q, calling the -- * completion handler for each. -- */ --static irqreturn_t do_bh(int irq, void *dev_id) --{ -- struct ipu_image_convert_chan *chan = dev_id; -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_ctx *ctx; -- unsigned long flags; -- -- dev_dbg(priv->ipu->dev, "%s: task %u: enter\n", __func__, -- chan->ic_task); -- -- empty_done_q(chan); -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- /* -- * the done_q is cleared out, signal any contexts -- * that are aborting that abort can complete. -- */ -- list_for_each_entry(ctx, &chan->ctx_list, list) { -- if (ctx->aborting) { -- dev_dbg(priv->ipu->dev, -- "%s: task %u: signaling abort for ctx %p\n", -- __func__, chan->ic_task, ctx); -- complete_all(&ctx->aborted); -- } -- } -- -- spin_unlock_irqrestore(&chan->irqlock, flags); -- -- dev_dbg(priv->ipu->dev, "%s: task %u: exit\n", __func__, -- chan->ic_task); -- -- return IRQ_HANDLED; --} -- --static bool ic_settings_changed(struct ipu_image_convert_ctx *ctx) --{ -- unsigned int cur_tile = ctx->next_tile - 1; -- unsigned int next_tile = ctx->next_tile; -- -- if (ctx->resize_coeffs_h[cur_tile % ctx->in.num_cols] != -- ctx->resize_coeffs_h[next_tile % ctx->in.num_cols] || -- ctx->resize_coeffs_v[cur_tile / ctx->in.num_cols] != -- ctx->resize_coeffs_v[next_tile / ctx->in.num_cols] || -- ctx->in.tile[cur_tile].width != ctx->in.tile[next_tile].width || -- ctx->in.tile[cur_tile].height != ctx->in.tile[next_tile].height || -- ctx->out.tile[cur_tile].width != ctx->out.tile[next_tile].width || -- ctx->out.tile[cur_tile].height != ctx->out.tile[next_tile].height) -- return true; -- -- return false; --} -- --/* hold irqlock when calling */ --static irqreturn_t do_irq(struct ipu_image_convert_run *run) --{ -- struct ipu_image_convert_ctx *ctx = run->ctx; -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_tile *src_tile, *dst_tile; -- struct ipu_image_convert_image *s_image = &ctx->in; -- struct ipu_image_convert_image *d_image = &ctx->out; -- struct ipuv3_channel *outch; -- unsigned int dst_idx; -- -- lockdep_assert_held(&chan->irqlock); -- -- outch = ipu_rot_mode_is_irt(ctx->rot_mode) ? -- chan->rotation_out_chan : chan->out_chan; -- -- /* -- * It is difficult to stop the channel DMA before the channels -- * enter the paused state. Without double-buffering the channels -- * are always in a paused state when the EOF irq occurs, so it -- * is safe to stop the channels now. For double-buffering we -- * just ignore the abort until the operation completes, when it -- * is safe to shut down. -- */ -- if (ctx->aborting && !ctx->double_buffering) { -- convert_stop(run); -- run->status = -EIO; -- goto done; -- } -- -- if (ctx->next_tile == ctx->num_tiles) { -- /* -- * the conversion is complete -- */ -- convert_stop(run); -- run->status = 0; -- goto done; -- } -- -- /* -- * not done, place the next tile buffers. -- */ -- if (!ctx->double_buffering) { -- if (ic_settings_changed(ctx)) { -- convert_stop(run); -- convert_start(run, ctx->next_tile); -- } else { -- src_tile = &s_image->tile[ctx->next_tile]; -- dst_idx = ctx->out_tile_map[ctx->next_tile]; -- dst_tile = &d_image->tile[dst_idx]; -- -- ipu_cpmem_set_buffer(chan->in_chan, 0, -- s_image->base.phys0 + -- src_tile->offset); -- ipu_cpmem_set_buffer(outch, 0, -- d_image->base.phys0 + -- dst_tile->offset); -- if (s_image->fmt->planar) -- ipu_cpmem_set_uv_offset(chan->in_chan, -- src_tile->u_off, -- src_tile->v_off); -- if (d_image->fmt->planar) -- ipu_cpmem_set_uv_offset(outch, -- dst_tile->u_off, -- dst_tile->v_off); -- -- ipu_idmac_select_buffer(chan->in_chan, 0); -- ipu_idmac_select_buffer(outch, 0); -- } -- } else if (ctx->next_tile < ctx->num_tiles - 1) { -- -- src_tile = &s_image->tile[ctx->next_tile + 1]; -- dst_idx = ctx->out_tile_map[ctx->next_tile + 1]; -- dst_tile = &d_image->tile[dst_idx]; -- -- ipu_cpmem_set_buffer(chan->in_chan, ctx->cur_buf_num, -- s_image->base.phys0 + src_tile->offset); -- ipu_cpmem_set_buffer(outch, ctx->cur_buf_num, -- d_image->base.phys0 + dst_tile->offset); -- -- ipu_idmac_select_buffer(chan->in_chan, ctx->cur_buf_num); -- ipu_idmac_select_buffer(outch, ctx->cur_buf_num); -- -- ctx->cur_buf_num ^= 1; -- } -- -- ctx->next_tile++; -- return IRQ_HANDLED; --done: -- list_add_tail(&run->list, &chan->done_q); -- chan->current_run = NULL; -- run_next(chan); -- return IRQ_WAKE_THREAD; --} -- --static irqreturn_t norotate_irq(int irq, void *data) --{ -- struct ipu_image_convert_chan *chan = data; -- struct ipu_image_convert_ctx *ctx; -- struct ipu_image_convert_run *run; -- unsigned long flags; -- irqreturn_t ret; -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- /* get current run and its context */ -- run = chan->current_run; -- if (!run) { -- ret = IRQ_NONE; -- goto out; -- } -- -- ctx = run->ctx; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- /* this is a rotation operation, just ignore */ -- spin_unlock_irqrestore(&chan->irqlock, flags); -- return IRQ_HANDLED; -- } -- -- ret = do_irq(run); --out: -- spin_unlock_irqrestore(&chan->irqlock, flags); -- return ret; --} -- --static irqreturn_t rotate_irq(int irq, void *data) --{ -- struct ipu_image_convert_chan *chan = data; -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_ctx *ctx; -- struct ipu_image_convert_run *run; -- unsigned long flags; -- irqreturn_t ret; -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- /* get current run and its context */ -- run = chan->current_run; -- if (!run) { -- ret = IRQ_NONE; -- goto out; -- } -- -- ctx = run->ctx; -- -- if (!ipu_rot_mode_is_irt(ctx->rot_mode)) { -- /* this was NOT a rotation operation, shouldn't happen */ -- dev_err(priv->ipu->dev, "Unexpected rotation interrupt\n"); -- spin_unlock_irqrestore(&chan->irqlock, flags); -- return IRQ_HANDLED; -- } -- -- ret = do_irq(run); --out: -- spin_unlock_irqrestore(&chan->irqlock, flags); -- return ret; --} -- --/* -- * try to force the completion of runs for this ctx. Called when -- * abort wait times out in ipu_image_convert_abort(). -- */ --static void force_abort(struct ipu_image_convert_ctx *ctx) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_run *run; -- unsigned long flags; -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- run = chan->current_run; -- if (run && run->ctx == ctx) { -- convert_stop(run); -- run->status = -EIO; -- list_add_tail(&run->list, &chan->done_q); -- chan->current_run = NULL; -- run_next(chan); -- } -- -- spin_unlock_irqrestore(&chan->irqlock, flags); -- -- empty_done_q(chan); --} -- --static void release_ipu_resources(struct ipu_image_convert_chan *chan) --{ -- if (chan->out_eof_irq >= 0) -- free_irq(chan->out_eof_irq, chan); -- if (chan->rot_out_eof_irq >= 0) -- free_irq(chan->rot_out_eof_irq, chan); -- -- if (!IS_ERR_OR_NULL(chan->in_chan)) -- ipu_idmac_put(chan->in_chan); -- if (!IS_ERR_OR_NULL(chan->out_chan)) -- ipu_idmac_put(chan->out_chan); -- if (!IS_ERR_OR_NULL(chan->rotation_in_chan)) -- ipu_idmac_put(chan->rotation_in_chan); -- if (!IS_ERR_OR_NULL(chan->rotation_out_chan)) -- ipu_idmac_put(chan->rotation_out_chan); -- if (!IS_ERR_OR_NULL(chan->ic)) -- ipu_ic_put(chan->ic); -- -- chan->in_chan = chan->out_chan = chan->rotation_in_chan = -- chan->rotation_out_chan = NULL; -- chan->out_eof_irq = chan->rot_out_eof_irq = -1; --} -- --static int get_ipu_resources(struct ipu_image_convert_chan *chan) --{ -- const struct ipu_image_convert_dma_chan *dma = chan->dma_ch; -- struct ipu_image_convert_priv *priv = chan->priv; -- int ret; -- -- /* get IC */ -- chan->ic = ipu_ic_get(priv->ipu, chan->ic_task); -- if (IS_ERR(chan->ic)) { -- dev_err(priv->ipu->dev, "could not acquire IC\n"); -- ret = PTR_ERR(chan->ic); -- goto err; -- } -- -- /* get IDMAC channels */ -- chan->in_chan = ipu_idmac_get(priv->ipu, dma->in); -- chan->out_chan = ipu_idmac_get(priv->ipu, dma->out); -- if (IS_ERR(chan->in_chan) || IS_ERR(chan->out_chan)) { -- dev_err(priv->ipu->dev, "could not acquire idmac channels\n"); -- ret = -EBUSY; -- goto err; -- } -- -- chan->rotation_in_chan = ipu_idmac_get(priv->ipu, dma->rot_in); -- chan->rotation_out_chan = ipu_idmac_get(priv->ipu, dma->rot_out); -- if (IS_ERR(chan->rotation_in_chan) || IS_ERR(chan->rotation_out_chan)) { -- dev_err(priv->ipu->dev, -- "could not acquire idmac rotation channels\n"); -- ret = -EBUSY; -- goto err; -- } -- -- /* acquire the EOF interrupts */ -- chan->out_eof_irq = ipu_idmac_channel_irq(priv->ipu, -- chan->out_chan, -- IPU_IRQ_EOF); -- -- ret = request_threaded_irq(chan->out_eof_irq, norotate_irq, do_bh, -- 0, "ipu-ic", chan); -- if (ret < 0) { -- dev_err(priv->ipu->dev, "could not acquire irq %d\n", -- chan->out_eof_irq); -- chan->out_eof_irq = -1; -- goto err; -- } -- -- chan->rot_out_eof_irq = ipu_idmac_channel_irq(priv->ipu, -- chan->rotation_out_chan, -- IPU_IRQ_EOF); -- -- ret = request_threaded_irq(chan->rot_out_eof_irq, rotate_irq, do_bh, -- 0, "ipu-ic", chan); -- if (ret < 0) { -- dev_err(priv->ipu->dev, "could not acquire irq %d\n", -- chan->rot_out_eof_irq); -- chan->rot_out_eof_irq = -1; -- goto err; -- } -- -- return 0; --err: -- release_ipu_resources(chan); -- return ret; --} -- --static int fill_image(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *ic_image, -- struct ipu_image *image, -- enum ipu_image_convert_type type) --{ -- struct ipu_image_convert_priv *priv = ctx->chan->priv; -- -- ic_image->base = *image; -- ic_image->type = type; -- -- ic_image->fmt = get_format(image->pix.pixelformat); -- if (!ic_image->fmt) { -- dev_err(priv->ipu->dev, "pixelformat not supported for %s\n", -- type == IMAGE_CONVERT_OUT ? "Output" : "Input"); -- return -EINVAL; -- } -- -- if (ic_image->fmt->planar) -- ic_image->stride = ic_image->base.pix.width; -- else -- ic_image->stride = ic_image->base.pix.bytesperline; -- -- return 0; --} -- --/* borrowed from drivers/media/v4l2-core/v4l2-common.c */ --static unsigned int clamp_align(unsigned int x, unsigned int min, -- unsigned int max, unsigned int align) --{ -- /* Bits that must be zero to be aligned */ -- unsigned int mask = ~((1 << align) - 1); -- -- /* Clamp to aligned min and max */ -- x = clamp(x, (min + ~mask) & mask, max & mask); -- -- /* Round to nearest aligned value */ -- if (align) -- x = (x + (1 << (align - 1))) & mask; -- -- return x; --} -- --/* Adjusts input/output images to IPU restrictions */ --void ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out, -- enum ipu_rotate_mode rot_mode) --{ -- const struct ipu_image_pixfmt *infmt, *outfmt; -- u32 w_align_out, h_align_out; -- u32 w_align_in, h_align_in; -- -- infmt = get_format(in->pix.pixelformat); -- outfmt = get_format(out->pix.pixelformat); -- -- /* set some default pixel formats if needed */ -- if (!infmt) { -- in->pix.pixelformat = V4L2_PIX_FMT_RGB24; -- infmt = get_format(V4L2_PIX_FMT_RGB24); -- } -- if (!outfmt) { -- out->pix.pixelformat = V4L2_PIX_FMT_RGB24; -- outfmt = get_format(V4L2_PIX_FMT_RGB24); -- } -- -- /* image converter does not handle fields */ -- in->pix.field = out->pix.field = V4L2_FIELD_NONE; -- -- /* resizer cannot downsize more than 4:1 */ -- if (ipu_rot_mode_is_irt(rot_mode)) { -- out->pix.height = max_t(__u32, out->pix.height, -- in->pix.width / 4); -- out->pix.width = max_t(__u32, out->pix.width, -- in->pix.height / 4); -- } else { -- out->pix.width = max_t(__u32, out->pix.width, -- in->pix.width / 4); -- out->pix.height = max_t(__u32, out->pix.height, -- in->pix.height / 4); -- } -- -- /* align input width/height */ -- w_align_in = ilog2(tile_width_align(IMAGE_CONVERT_IN, infmt, -- rot_mode)); -- h_align_in = ilog2(tile_height_align(IMAGE_CONVERT_IN, infmt, -- rot_mode)); -- in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W, -- w_align_in); -- in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H, -- h_align_in); -- -- /* align output width/height */ -- w_align_out = ilog2(tile_width_align(IMAGE_CONVERT_OUT, outfmt, -- rot_mode)); -- h_align_out = ilog2(tile_height_align(IMAGE_CONVERT_OUT, outfmt, -- rot_mode)); -- out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W, -- w_align_out); -- out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H, -- h_align_out); -- -- /* set input/output strides and image sizes */ -- in->pix.bytesperline = infmt->planar ? -- clamp_align(in->pix.width, 2 << w_align_in, MAX_W, -- w_align_in) : -- clamp_align((in->pix.width * infmt->bpp) >> 3, -- ((2 << w_align_in) * infmt->bpp) >> 3, -- (MAX_W * infmt->bpp) >> 3, -- w_align_in); -- in->pix.sizeimage = infmt->planar ? -- (in->pix.height * in->pix.bytesperline * infmt->bpp) >> 3 : -- in->pix.height * in->pix.bytesperline; -- out->pix.bytesperline = outfmt->planar ? out->pix.width : -- (out->pix.width * outfmt->bpp) >> 3; -- out->pix.sizeimage = outfmt->planar ? -- (out->pix.height * out->pix.bytesperline * outfmt->bpp) >> 3 : -- out->pix.height * out->pix.bytesperline; --} --EXPORT_SYMBOL_GPL(ipu_image_convert_adjust); -- --/* -- * this is used by ipu_image_convert_prepare() to verify set input and -- * output images are valid before starting the conversion. Clients can -- * also call it before calling ipu_image_convert_prepare(). -- */ --int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out, -- enum ipu_rotate_mode rot_mode) --{ -- struct ipu_image testin, testout; -- -- testin = *in; -- testout = *out; -- -- ipu_image_convert_adjust(&testin, &testout, rot_mode); -- -- if (testin.pix.width != in->pix.width || -- testin.pix.height != in->pix.height || -- testout.pix.width != out->pix.width || -- testout.pix.height != out->pix.height) -- return -EINVAL; -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_image_convert_verify); -- --/* -- * Call ipu_image_convert_prepare() to prepare for the conversion of -- * given images and rotation mode. Returns a new conversion context. -- */ --struct ipu_image_convert_ctx * --ipu_image_convert_prepare(struct ipu_soc *ipu, enum ipu_ic_task ic_task, -- struct ipu_image *in, struct ipu_image *out, -- enum ipu_rotate_mode rot_mode, -- ipu_image_convert_cb_t complete, -- void *complete_context) --{ -- struct ipu_image_convert_priv *priv = ipu->image_convert_priv; -- struct ipu_image_convert_image *s_image, *d_image; -- struct ipu_image_convert_chan *chan; -- struct ipu_image_convert_ctx *ctx; -- unsigned long flags; -- unsigned int i; -- bool get_res; -- int ret; -- -- if (!in || !out || !complete || -- (ic_task != IC_TASK_VIEWFINDER && -- ic_task != IC_TASK_POST_PROCESSOR)) -- return ERR_PTR(-EINVAL); -- -- /* verify the in/out images before continuing */ -- ret = ipu_image_convert_verify(in, out, rot_mode); -- if (ret) { -- dev_err(priv->ipu->dev, "%s: in/out formats invalid\n", -- __func__); -- return ERR_PTR(ret); -- } -- -- chan = &priv->chan[ic_task]; -- -- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); -- if (!ctx) -- return ERR_PTR(-ENOMEM); -- -- dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p\n", __func__, -- chan->ic_task, ctx); -- -- ctx->chan = chan; -- init_completion(&ctx->aborted); -- -- ctx->rot_mode = rot_mode; -- -- /* Sets ctx->in.num_rows/cols as well */ -- ret = calc_image_resize_coefficients(ctx, in, out); -- if (ret) -- goto out_free; -- -- s_image = &ctx->in; -- d_image = &ctx->out; -- -- /* set tiling and rotation */ -- if (ipu_rot_mode_is_irt(rot_mode)) { -- d_image->num_rows = s_image->num_cols; -- d_image->num_cols = s_image->num_rows; -- } else { -- d_image->num_rows = s_image->num_rows; -- d_image->num_cols = s_image->num_cols; -- } -- -- ctx->num_tiles = d_image->num_cols * d_image->num_rows; -- -- ret = fill_image(ctx, s_image, in, IMAGE_CONVERT_IN); -- if (ret) -- goto out_free; -- ret = fill_image(ctx, d_image, out, IMAGE_CONVERT_OUT); -- if (ret) -- goto out_free; -- -- calc_out_tile_map(ctx); -- -- find_seams(ctx, s_image, d_image); -- -- ret = calc_tile_dimensions(ctx, s_image); -- if (ret) -- goto out_free; -- -- ret = calc_tile_offsets(ctx, s_image); -- if (ret) -- goto out_free; -- -- calc_tile_dimensions(ctx, d_image); -- ret = calc_tile_offsets(ctx, d_image); -- if (ret) -- goto out_free; -- -- calc_tile_resize_coefficients(ctx); -- -- ret = ipu_ic_calc_csc(&ctx->csc, -- s_image->base.pix.ycbcr_enc, -- s_image->base.pix.quantization, -- ipu_pixelformat_to_colorspace(s_image->fmt->fourcc), -- d_image->base.pix.ycbcr_enc, -- d_image->base.pix.quantization, -- ipu_pixelformat_to_colorspace(d_image->fmt->fourcc)); -- if (ret) -- goto out_free; -- -- dump_format(ctx, s_image); -- dump_format(ctx, d_image); -- -- ctx->complete = complete; -- ctx->complete_context = complete_context; -- -- /* -- * Can we use double-buffering for this operation? If there is -- * only one tile (the whole image can be converted in a single -- * operation) there's no point in using double-buffering. Also, -- * the IPU's IDMAC channels allow only a single U and V plane -- * offset shared between both buffers, but these offsets change -- * for every tile, and therefore would have to be updated for -- * each buffer which is not possible. So double-buffering is -- * impossible when either the source or destination images are -- * a planar format (YUV420, YUV422P, etc.). Further, differently -- * sized tiles or different resizing coefficients per tile -- * prevent double-buffering as well. -- */ -- ctx->double_buffering = (ctx->num_tiles > 1 && -- !s_image->fmt->planar && -- !d_image->fmt->planar); -- for (i = 1; i < ctx->num_tiles; i++) { -- if (ctx->in.tile[i].width != ctx->in.tile[0].width || -- ctx->in.tile[i].height != ctx->in.tile[0].height || -- ctx->out.tile[i].width != ctx->out.tile[0].width || -- ctx->out.tile[i].height != ctx->out.tile[0].height) { -- ctx->double_buffering = false; -- break; -- } -- } -- for (i = 1; i < ctx->in.num_cols; i++) { -- if (ctx->resize_coeffs_h[i] != ctx->resize_coeffs_h[0]) { -- ctx->double_buffering = false; -- break; -- } -- } -- for (i = 1; i < ctx->in.num_rows; i++) { -- if (ctx->resize_coeffs_v[i] != ctx->resize_coeffs_v[0]) { -- ctx->double_buffering = false; -- break; -- } -- } -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- unsigned long intermediate_size = d_image->tile[0].size; -- -- for (i = 1; i < ctx->num_tiles; i++) { -- if (d_image->tile[i].size > intermediate_size) -- intermediate_size = d_image->tile[i].size; -- } -- -- ret = alloc_dma_buf(priv, &ctx->rot_intermediate[0], -- intermediate_size); -- if (ret) -- goto out_free; -- if (ctx->double_buffering) { -- ret = alloc_dma_buf(priv, -- &ctx->rot_intermediate[1], -- intermediate_size); -- if (ret) -- goto out_free_dmabuf0; -- } -- } -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- get_res = list_empty(&chan->ctx_list); -- -- list_add_tail(&ctx->list, &chan->ctx_list); -- -- spin_unlock_irqrestore(&chan->irqlock, flags); -- -- if (get_res) { -- ret = get_ipu_resources(chan); -- if (ret) -- goto out_free_dmabuf1; -- } -- -- return ctx; -- --out_free_dmabuf1: -- free_dma_buf(priv, &ctx->rot_intermediate[1]); -- spin_lock_irqsave(&chan->irqlock, flags); -- list_del(&ctx->list); -- spin_unlock_irqrestore(&chan->irqlock, flags); --out_free_dmabuf0: -- free_dma_buf(priv, &ctx->rot_intermediate[0]); --out_free: -- kfree(ctx); -- return ERR_PTR(ret); --} --EXPORT_SYMBOL_GPL(ipu_image_convert_prepare); -- --/* -- * Carry out a single image conversion run. Only the physaddr's of the input -- * and output image buffers are needed. The conversion context must have -- * been created previously with ipu_image_convert_prepare(). -- */ --int ipu_image_convert_queue(struct ipu_image_convert_run *run) --{ -- struct ipu_image_convert_chan *chan; -- struct ipu_image_convert_priv *priv; -- struct ipu_image_convert_ctx *ctx; -- unsigned long flags; -- int ret = 0; -- -- if (!run || !run->ctx || !run->in_phys || !run->out_phys) -- return -EINVAL; -- -- ctx = run->ctx; -- chan = ctx->chan; -- priv = chan->priv; -- -- dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p run %p\n", __func__, -- chan->ic_task, ctx, run); -- -- INIT_LIST_HEAD(&run->list); -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- if (ctx->aborting) { -- ret = -EIO; -- goto unlock; -- } -- -- list_add_tail(&run->list, &chan->pending_q); -- -- if (!chan->current_run) { -- ret = do_run(run); -- if (ret) -- chan->current_run = NULL; -- } --unlock: -- spin_unlock_irqrestore(&chan->irqlock, flags); -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_image_convert_queue); -- --/* Abort any active or pending conversions for this context */ --static void __ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_run *run, *active_run, *tmp; -- unsigned long flags; -- int run_count, ret; -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- /* move all remaining pending runs in this context to done_q */ -- list_for_each_entry_safe(run, tmp, &chan->pending_q, list) { -- if (run->ctx != ctx) -- continue; -- run->status = -EIO; -- list_move_tail(&run->list, &chan->done_q); -- } -- -- run_count = get_run_count(ctx, &chan->done_q); -- active_run = (chan->current_run && chan->current_run->ctx == ctx) ? -- chan->current_run : NULL; -- -- if (active_run) -- reinit_completion(&ctx->aborted); -- -- ctx->aborting = true; -- -- spin_unlock_irqrestore(&chan->irqlock, flags); -- -- if (!run_count && !active_run) { -- dev_dbg(priv->ipu->dev, -- "%s: task %u: no abort needed for ctx %p\n", -- __func__, chan->ic_task, ctx); -- return; -- } -- -- if (!active_run) { -- empty_done_q(chan); -- return; -- } -- -- dev_dbg(priv->ipu->dev, -- "%s: task %u: wait for completion: %d runs\n", -- __func__, chan->ic_task, run_count); -- -- ret = wait_for_completion_timeout(&ctx->aborted, -- msecs_to_jiffies(10000)); -- if (ret == 0) { -- dev_warn(priv->ipu->dev, "%s: timeout\n", __func__); -- force_abort(ctx); -- } --} -- --void ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx) --{ -- __ipu_image_convert_abort(ctx); -- ctx->aborting = false; --} --EXPORT_SYMBOL_GPL(ipu_image_convert_abort); -- --/* Unprepare image conversion context */ --void ipu_image_convert_unprepare(struct ipu_image_convert_ctx *ctx) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- unsigned long flags; -- bool put_res; -- -- /* make sure no runs are hanging around */ -- __ipu_image_convert_abort(ctx); -- -- dev_dbg(priv->ipu->dev, "%s: task %u: removing ctx %p\n", __func__, -- chan->ic_task, ctx); -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- list_del(&ctx->list); -- -- put_res = list_empty(&chan->ctx_list); -- -- spin_unlock_irqrestore(&chan->irqlock, flags); -- -- if (put_res) -- release_ipu_resources(chan); -- -- free_dma_buf(priv, &ctx->rot_intermediate[1]); -- free_dma_buf(priv, &ctx->rot_intermediate[0]); -- -- kfree(ctx); --} --EXPORT_SYMBOL_GPL(ipu_image_convert_unprepare); -- --/* -- * "Canned" asynchronous single image conversion. Allocates and returns -- * a new conversion run. On successful return the caller must free the -- * run and call ipu_image_convert_unprepare() after conversion completes. -- */ --struct ipu_image_convert_run * --ipu_image_convert(struct ipu_soc *ipu, enum ipu_ic_task ic_task, -- struct ipu_image *in, struct ipu_image *out, -- enum ipu_rotate_mode rot_mode, -- ipu_image_convert_cb_t complete, -- void *complete_context) --{ -- struct ipu_image_convert_ctx *ctx; -- struct ipu_image_convert_run *run; -- int ret; -- -- ctx = ipu_image_convert_prepare(ipu, ic_task, in, out, rot_mode, -- complete, complete_context); -- if (IS_ERR(ctx)) -- return ERR_CAST(ctx); -- -- run = kzalloc(sizeof(*run), GFP_KERNEL); -- if (!run) { -- ipu_image_convert_unprepare(ctx); -- return ERR_PTR(-ENOMEM); -- } -- -- run->ctx = ctx; -- run->in_phys = in->phys0; -- run->out_phys = out->phys0; -- -- ret = ipu_image_convert_queue(run); -- if (ret) { -- ipu_image_convert_unprepare(ctx); -- kfree(run); -- return ERR_PTR(ret); -- } -- -- return run; --} --EXPORT_SYMBOL_GPL(ipu_image_convert); -- --/* "Canned" synchronous single image conversion */ --static void image_convert_sync_complete(struct ipu_image_convert_run *run, -- void *data) --{ -- struct completion *comp = data; -- -- complete(comp); --} -- --int ipu_image_convert_sync(struct ipu_soc *ipu, enum ipu_ic_task ic_task, -- struct ipu_image *in, struct ipu_image *out, -- enum ipu_rotate_mode rot_mode) --{ -- struct ipu_image_convert_run *run; -- struct completion comp; -- int ret; -- -- init_completion(&comp); -- -- run = ipu_image_convert(ipu, ic_task, in, out, rot_mode, -- image_convert_sync_complete, &comp); -- if (IS_ERR(run)) -- return PTR_ERR(run); -- -- ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(10000)); -- ret = (ret == 0) ? -ETIMEDOUT : 0; -- -- ipu_image_convert_unprepare(run->ctx); -- kfree(run); -- -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_image_convert_sync); -- --int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev) --{ -- struct ipu_image_convert_priv *priv; -- int i; -- -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- -- ipu->image_convert_priv = priv; -- priv->ipu = ipu; -- -- for (i = 0; i < IC_NUM_TASKS; i++) { -- struct ipu_image_convert_chan *chan = &priv->chan[i]; -- -- chan->ic_task = i; -- chan->priv = priv; -- chan->dma_ch = &image_convert_dma_chan[i]; -- chan->out_eof_irq = -1; -- chan->rot_out_eof_irq = -1; -- -- spin_lock_init(&chan->irqlock); -- INIT_LIST_HEAD(&chan->ctx_list); -- INIT_LIST_HEAD(&chan->pending_q); -- INIT_LIST_HEAD(&chan->done_q); -- } -- -- return 0; --} -- --void ipu_image_convert_exit(struct ipu_soc *ipu) --{ --} ---- a/drivers/gpu/imx/ipu-v3/ipu-pre.c -+++ /dev/null -@@ -1,346 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (c) 2017 Lucas Stach, Pengutronix -- */ -- --#include <drm/drm_fourcc.h> --#include <linux/clk.h> --#include <linux/err.h> --#include <linux/genalloc.h> --#include <linux/module.h> --#include <linux/of.h> --#include <linux/platform_device.h> --#include <video/imx-ipu-v3.h> -- --#include "ipu-prv.h" -- --#define IPU_PRE_MAX_WIDTH 2048 --#define IPU_PRE_NUM_SCANLINES 8 -- --#define IPU_PRE_CTRL 0x000 --#define IPU_PRE_CTRL_SET 0x004 --#define IPU_PRE_CTRL_ENABLE (1 << 0) --#define IPU_PRE_CTRL_BLOCK_EN (1 << 1) --#define IPU_PRE_CTRL_BLOCK_16 (1 << 2) --#define IPU_PRE_CTRL_SDW_UPDATE (1 << 4) --#define IPU_PRE_CTRL_VFLIP (1 << 5) --#define IPU_PRE_CTRL_SO (1 << 6) --#define IPU_PRE_CTRL_INTERLACED_FIELD (1 << 7) --#define IPU_PRE_CTRL_HANDSHAKE_EN (1 << 8) --#define IPU_PRE_CTRL_HANDSHAKE_LINE_NUM(v) ((v & 0x3) << 9) --#define IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN (1 << 11) --#define IPU_PRE_CTRL_EN_REPEAT (1 << 28) --#define IPU_PRE_CTRL_TPR_REST_SEL (1 << 29) --#define IPU_PRE_CTRL_CLKGATE (1 << 30) --#define IPU_PRE_CTRL_SFTRST (1 << 31) -- --#define IPU_PRE_CUR_BUF 0x030 -- --#define IPU_PRE_NEXT_BUF 0x040 -- --#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) --#define IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(v) ((v & 0x7) << 1) --#define IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) --#define IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(v) ((v & 0x7) << 8) --#define IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS (1 << 11) --#define IPU_PRE_PREF_ENG_CTRL_FIELD_INVERSE (1 << 12) --#define IPU_PRE_PREF_ENG_CTRL_PARTIAL_UV_SWAP (1 << 14) --#define IPU_PRE_PREF_ENG_CTRL_TPR_COOR_OFFSET_EN (1 << 15) -- --#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE 0x0a0 --#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(v) ((v & 0xffff) << 0) --#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(v) ((v & 0xffff) << 16) -- --#define IPU_PRE_PREFETCH_ENG_PITCH 0x0d0 --#define IPU_PRE_PREFETCH_ENG_PITCH_Y(v) ((v & 0xffff) << 0) --#define IPU_PRE_PREFETCH_ENG_PITCH_UV(v) ((v & 0xffff) << 16) -- --#define IPU_PRE_STORE_ENG_CTRL 0x110 --#define IPU_PRE_STORE_ENG_CTRL_STORE_EN (1 << 0) --#define IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(v) ((v & 0x7) << 1) --#define IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) -- --#define IPU_PRE_STORE_ENG_STATUS 0x120 --#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_MASK 0xffff --#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_SHIFT 0 --#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK 0x3fff --#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT 16 --#define IPU_PRE_STORE_ENG_STATUS_STORE_FIFO_FULL (1 << 30) --#define IPU_PRE_STORE_ENG_STATUS_STORE_FIELD (1 << 31) -- --#define IPU_PRE_STORE_ENG_SIZE 0x130 --#define IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(v) ((v & 0xffff) << 0) --#define IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(v) ((v & 0xffff) << 16) -- --#define IPU_PRE_STORE_ENG_PITCH 0x140 --#define IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(v) ((v & 0xffff) << 0) -- --#define IPU_PRE_STORE_ENG_ADDR 0x150 -- --struct ipu_pre { -- struct list_head list; -- struct device *dev; -- -- void __iomem *regs; -- struct clk *clk_axi; -- struct gen_pool *iram; -- -- dma_addr_t buffer_paddr; -- void *buffer_virt; -- bool in_use; -- unsigned int safe_window_end; -- unsigned int last_bufaddr; --}; -- --static DEFINE_MUTEX(ipu_pre_list_mutex); --static LIST_HEAD(ipu_pre_list); --static int available_pres; -- --int ipu_pre_get_available_count(void) --{ -- return available_pres; --} -- --struct ipu_pre * --ipu_pre_lookup_by_phandle(struct device *dev, const char *name, int index) --{ -- struct device_node *pre_node = of_parse_phandle(dev->of_node, -- name, index); -- struct ipu_pre *pre; -- -- mutex_lock(&ipu_pre_list_mutex); -- list_for_each_entry(pre, &ipu_pre_list, list) { -- if (pre_node == pre->dev->of_node) { -- mutex_unlock(&ipu_pre_list_mutex); -- device_link_add(dev, pre->dev, -- DL_FLAG_AUTOREMOVE_CONSUMER); -- of_node_put(pre_node); -- return pre; -- } -- } -- mutex_unlock(&ipu_pre_list_mutex); -- -- of_node_put(pre_node); -- -- return NULL; --} -- --int ipu_pre_get(struct ipu_pre *pre) --{ -- u32 val; -- -- if (pre->in_use) -- return -EBUSY; -- -- /* first get the engine out of reset and remove clock gating */ -- writel(0, pre->regs + IPU_PRE_CTRL); -- -- /* init defaults that should be applied to all streams */ -- val = IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN | -- IPU_PRE_CTRL_HANDSHAKE_EN | -- IPU_PRE_CTRL_TPR_REST_SEL | -- IPU_PRE_CTRL_SDW_UPDATE; -- writel(val, pre->regs + IPU_PRE_CTRL); -- -- pre->in_use = true; -- return 0; --} -- --void ipu_pre_put(struct ipu_pre *pre) --{ -- writel(IPU_PRE_CTRL_SFTRST, pre->regs + IPU_PRE_CTRL); -- -- pre->in_use = false; --} -- --void ipu_pre_configure(struct ipu_pre *pre, unsigned int width, -- unsigned int height, unsigned int stride, u32 format, -- 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 */ -- 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); -- pre->last_bufaddr = bufaddr; -- -- val = IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(0) | -- IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(active_bpp) | -- IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(4) | -- IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS | -- IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN; -- writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_CTRL); -- -- val = IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(width) | -- IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(height); -- writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_INPUT_SIZE); -- -- val = IPU_PRE_PREFETCH_ENG_PITCH_Y(stride); -- writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_PITCH); -- -- val = IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(active_bpp) | -- IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(4) | -- IPU_PRE_STORE_ENG_CTRL_STORE_EN; -- writel(val, pre->regs + IPU_PRE_STORE_ENG_CTRL); -- -- val = IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(width) | -- IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(height); -- writel(val, pre->regs + IPU_PRE_STORE_ENG_SIZE); -- -- val = IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(stride); -- writel(val, pre->regs + IPU_PRE_STORE_ENG_PITCH); -- -- 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); --} -- --void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr) --{ -- unsigned long timeout = jiffies + msecs_to_jiffies(5); -- unsigned short current_yblock; -- u32 val; -- -- if (bufaddr == pre->last_bufaddr) -- return; -- -- writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF); -- pre->last_bufaddr = bufaddr; -- -- do { -- if (time_after(jiffies, timeout)) { -- dev_warn(pre->dev, "timeout waiting for PRE safe window\n"); -- return; -- } -- -- val = readl(pre->regs + IPU_PRE_STORE_ENG_STATUS); -- current_yblock = -- (val >> IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT) & -- IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK; -- } while (current_yblock == 0 || current_yblock >= pre->safe_window_end); -- -- writel(IPU_PRE_CTRL_SDW_UPDATE, pre->regs + IPU_PRE_CTRL_SET); --} -- --bool ipu_pre_update_pending(struct ipu_pre *pre) --{ -- return !!(readl_relaxed(pre->regs + IPU_PRE_CTRL) & -- IPU_PRE_CTRL_SDW_UPDATE); --} -- --u32 ipu_pre_get_baddr(struct ipu_pre *pre) --{ -- return (u32)pre->buffer_paddr; --} -- --static int ipu_pre_probe(struct platform_device *pdev) --{ -- struct device *dev = &pdev->dev; -- struct resource *res; -- struct ipu_pre *pre; -- -- pre = devm_kzalloc(dev, sizeof(*pre), GFP_KERNEL); -- if (!pre) -- return -ENOMEM; -- -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- pre->regs = devm_ioremap_resource(&pdev->dev, res); -- if (IS_ERR(pre->regs)) -- return PTR_ERR(pre->regs); -- -- pre->clk_axi = devm_clk_get(dev, "axi"); -- if (IS_ERR(pre->clk_axi)) -- return PTR_ERR(pre->clk_axi); -- -- pre->iram = of_gen_pool_get(dev->of_node, "fsl,iram", 0); -- if (!pre->iram) -- return -EPROBE_DEFER; -- -- /* -- * Allocate IRAM buffer with maximum size. This could be made dynamic, -- * but as there is no other user of this IRAM region and we can fit all -- * max sized buffers into it, there is no need yet. -- */ -- pre->buffer_virt = gen_pool_dma_alloc(pre->iram, IPU_PRE_MAX_WIDTH * -- IPU_PRE_NUM_SCANLINES * 4, -- &pre->buffer_paddr); -- if (!pre->buffer_virt) -- return -ENOMEM; -- -- clk_prepare_enable(pre->clk_axi); -- -- pre->dev = dev; -- platform_set_drvdata(pdev, pre); -- mutex_lock(&ipu_pre_list_mutex); -- list_add(&pre->list, &ipu_pre_list); -- available_pres++; -- mutex_unlock(&ipu_pre_list_mutex); -- -- return 0; --} -- --static int ipu_pre_remove(struct platform_device *pdev) --{ -- struct ipu_pre *pre = platform_get_drvdata(pdev); -- -- mutex_lock(&ipu_pre_list_mutex); -- list_del(&pre->list); -- available_pres--; -- mutex_unlock(&ipu_pre_list_mutex); -- -- clk_disable_unprepare(pre->clk_axi); -- -- if (pre->buffer_virt) -- gen_pool_free(pre->iram, (unsigned long)pre->buffer_virt, -- IPU_PRE_MAX_WIDTH * IPU_PRE_NUM_SCANLINES * 4); -- return 0; --} -- --static const struct of_device_id ipu_pre_dt_ids[] = { -- { .compatible = "fsl,imx6qp-pre", }, -- { /* sentinel */ }, --}; -- --struct platform_driver ipu_pre_drv = { -- .probe = ipu_pre_probe, -- .remove = ipu_pre_remove, -- .driver = { -- .name = "imx-ipu-pre", -- .of_match_table = ipu_pre_dt_ids, -- }, --}; ---- a/drivers/gpu/imx/ipu-v3/ipu-prg.c -+++ /dev/null -@@ -1,483 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (c) 2016-2017 Lucas Stach, Pengutronix -- */ -- --#include <drm/drm_fourcc.h> --#include <linux/clk.h> --#include <linux/err.h> --#include <linux/iopoll.h> --#include <linux/mfd/syscon.h> --#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> --#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> -- --#include "ipu-prv.h" -- --#define IPU_PRG_CTL 0x00 --#define IPU_PRG_CTL_BYPASS(i) (1 << (0 + i)) --#define IPU_PRG_CTL_SOFT_ARID_MASK 0x3 --#define IPU_PRG_CTL_SOFT_ARID_SHIFT(i) (8 + i * 2) --#define IPU_PRG_CTL_SOFT_ARID(i, v) ((v & 0x3) << (8 + 2 * i)) --#define IPU_PRG_CTL_SO(i) (1 << (16 + i)) --#define IPU_PRG_CTL_VFLIP(i) (1 << (19 + i)) --#define IPU_PRG_CTL_BLOCK_MODE(i) (1 << (22 + i)) --#define IPU_PRG_CTL_CNT_LOAD_EN(i) (1 << (25 + i)) --#define IPU_PRG_CTL_SOFTRST (1 << 30) --#define IPU_PRG_CTL_SHADOW_EN (1 << 31) -- --#define IPU_PRG_STATUS 0x04 --#define IPU_PRG_STATUS_BUFFER0_READY(i) (1 << (0 + i * 2)) --#define IPU_PRG_STATUS_BUFFER1_READY(i) (1 << (1 + i * 2)) -- --#define IPU_PRG_QOS 0x08 --#define IPU_PRG_QOS_ARID_MASK 0xf --#define IPU_PRG_QOS_ARID_SHIFT(i) (0 + i * 4) -- --#define IPU_PRG_REG_UPDATE 0x0c --#define IPU_PRG_REG_UPDATE_REG_UPDATE (1 << 0) -- --#define IPU_PRG_STRIDE(i) (0x10 + i * 0x4) --#define IPU_PRG_STRIDE_STRIDE_MASK 0x3fff -- --#define IPU_PRG_CROP_LINE 0x1c -- --#define IPU_PRG_THD 0x20 -- --#define IPU_PRG_BADDR(i) (0x24 + i * 0x4) -- --#define IPU_PRG_OFFSET(i) (0x30 + i * 0x4) -- --#define IPU_PRG_ILO(i) (0x3c + i * 0x4) -- --#define IPU_PRG_HEIGHT(i) (0x48 + i * 0x4) --#define IPU_PRG_HEIGHT_PRE_HEIGHT_MASK 0xfff --#define IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT 0 --#define IPU_PRG_HEIGHT_IPU_HEIGHT_MASK 0xfff --#define IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT 16 -- --struct ipu_prg_channel { -- bool enabled; -- int used_pre; --}; -- --struct ipu_prg { -- struct list_head list; -- struct device *dev; -- int id; -- -- void __iomem *regs; -- struct clk *clk_ipg, *clk_axi; -- struct regmap *iomuxc_gpr; -- struct ipu_pre *pres[3]; -- -- struct ipu_prg_channel chan[3]; --}; -- --static DEFINE_MUTEX(ipu_prg_list_mutex); --static LIST_HEAD(ipu_prg_list); -- --struct ipu_prg * --ipu_prg_lookup_by_phandle(struct device *dev, const char *name, int ipu_id) --{ -- struct device_node *prg_node = of_parse_phandle(dev->of_node, -- name, 0); -- struct ipu_prg *prg; -- -- mutex_lock(&ipu_prg_list_mutex); -- list_for_each_entry(prg, &ipu_prg_list, list) { -- if (prg_node == prg->dev->of_node) { -- mutex_unlock(&ipu_prg_list_mutex); -- device_link_add(dev, prg->dev, -- DL_FLAG_AUTOREMOVE_CONSUMER); -- prg->id = ipu_id; -- of_node_put(prg_node); -- return prg; -- } -- } -- mutex_unlock(&ipu_prg_list_mutex); -- -- of_node_put(prg_node); -- -- return NULL; --} -- --int ipu_prg_max_active_channels(void) --{ -- return ipu_pre_get_available_count(); --} --EXPORT_SYMBOL_GPL(ipu_prg_max_active_channels); -- --bool ipu_prg_present(struct ipu_soc *ipu) --{ -- if (ipu->prg_priv) -- return true; -- -- return false; --} --EXPORT_SYMBOL_GPL(ipu_prg_present); -- --bool ipu_prg_format_supported(struct ipu_soc *ipu, uint32_t format, -- uint64_t modifier) --{ -- const struct drm_format_info *info = drm_format_info(format); -- -- if (info->num_planes != 1) -- return false; -- -- 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; -- -- if (!prg) -- return 0; -- -- return pm_runtime_get_sync(prg->dev); --} --EXPORT_SYMBOL_GPL(ipu_prg_enable); -- --void ipu_prg_disable(struct ipu_soc *ipu) --{ -- struct ipu_prg *prg = ipu->prg_priv; -- -- if (!prg) -- return; -- -- pm_runtime_put(prg->dev); --} --EXPORT_SYMBOL_GPL(ipu_prg_disable); -- --/* -- * The channel configuartion functions below are not thread safe, as they -- * must be only called from the atomic commit path in the DRM driver, which -- * is properly serialized. -- */ --static int ipu_prg_ipu_to_prg_chan(int ipu_chan) --{ -- /* -- * This isn't clearly documented in the RM, but IPU to PRG channel -- * assignment is fixed, as only with this mapping the control signals -- * match up. -- */ -- switch (ipu_chan) { -- case IPUV3_CHANNEL_MEM_BG_SYNC: -- return 0; -- case IPUV3_CHANNEL_MEM_FG_SYNC: -- return 1; -- case IPUV3_CHANNEL_MEM_DC_SYNC: -- return 2; -- default: -- return -EINVAL; -- } --} -- --static int ipu_prg_get_pre(struct ipu_prg *prg, int prg_chan) --{ -- int i, ret; -- -- /* channel 0 is special as it is hardwired to one of the PREs */ -- if (prg_chan == 0) { -- ret = ipu_pre_get(prg->pres[0]); -- if (ret) -- goto fail; -- prg->chan[prg_chan].used_pre = 0; -- return 0; -- } -- -- for (i = 1; i < 3; i++) { -- ret = ipu_pre_get(prg->pres[i]); -- if (!ret) { -- u32 val, mux; -- int shift; -- -- prg->chan[prg_chan].used_pre = i; -- -- /* configure the PRE to PRG channel mux */ -- shift = (i == 1) ? 12 : 14; -- mux = (prg->id << 1) | (prg_chan - 1); -- regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5, -- 0x3 << shift, mux << shift); -- -- /* check other mux, must not point to same channel */ -- shift = (i == 1) ? 14 : 12; -- regmap_read(prg->iomuxc_gpr, IOMUXC_GPR5, &val); -- if (((val >> shift) & 0x3) == mux) { -- regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5, -- 0x3 << shift, -- (mux ^ 0x1) << shift); -- } -- -- return 0; -- } -- } -- --fail: -- dev_err(prg->dev, "could not get PRE for PRG chan %d", prg_chan); -- return ret; --} -- --static void ipu_prg_put_pre(struct ipu_prg *prg, int prg_chan) --{ -- struct ipu_prg_channel *chan = &prg->chan[prg_chan]; -- -- ipu_pre_put(prg->pres[chan->used_pre]); -- chan->used_pre = -1; --} -- --void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan) --{ -- int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); -- struct ipu_prg *prg = ipu_chan->ipu->prg_priv; -- struct ipu_prg_channel *chan; -- u32 val; -- -- if (prg_chan < 0) -- return; -- -- chan = &prg->chan[prg_chan]; -- if (!chan->enabled) -- return; -- -- pm_runtime_get_sync(prg->dev); -- -- val = readl(prg->regs + IPU_PRG_CTL); -- val |= IPU_PRG_CTL_BYPASS(prg_chan); -- writel(val, prg->regs + IPU_PRG_CTL); -- -- val = IPU_PRG_REG_UPDATE_REG_UPDATE; -- writel(val, prg->regs + IPU_PRG_REG_UPDATE); -- -- pm_runtime_put(prg->dev); -- -- ipu_prg_put_pre(prg, prg_chan); -- -- chan->enabled = false; --} --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, 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; -- struct ipu_prg_channel *chan; -- u32 val; -- int ret; -- -- if (prg_chan < 0) -- return prg_chan; -- -- chan = &prg->chan[prg_chan]; -- -- if (chan->enabled) { -- ipu_pre_update(prg->pres[chan->used_pre], *eba); -- return 0; -- } -- -- ret = ipu_prg_get_pre(prg, prg_chan); -- if (ret) -- return ret; -- -- ipu_pre_configure(prg->pres[chan->used_pre], -- width, height, stride, format, modifier, *eba); -- -- -- pm_runtime_get_sync(prg->dev); -- -- val = (stride - 1) & IPU_PRG_STRIDE_STRIDE_MASK; -- writel(val, prg->regs + IPU_PRG_STRIDE(prg_chan)); -- -- val = ((height & IPU_PRG_HEIGHT_PRE_HEIGHT_MASK) << -- IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT) | -- ((height & IPU_PRG_HEIGHT_IPU_HEIGHT_MASK) << -- IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT); -- writel(val, prg->regs + IPU_PRG_HEIGHT(prg_chan)); -- -- val = ipu_pre_get_baddr(prg->pres[chan->used_pre]); -- *eba = val; -- writel(val, prg->regs + IPU_PRG_BADDR(prg_chan)); -- -- val = readl(prg->regs + IPU_PRG_CTL); -- /* config AXI ID */ -- val &= ~(IPU_PRG_CTL_SOFT_ARID_MASK << -- IPU_PRG_CTL_SOFT_ARID_SHIFT(prg_chan)); -- val |= IPU_PRG_CTL_SOFT_ARID(prg_chan, axi_id); -- /* enable channel */ -- val &= ~IPU_PRG_CTL_BYPASS(prg_chan); -- writel(val, prg->regs + IPU_PRG_CTL); -- -- val = IPU_PRG_REG_UPDATE_REG_UPDATE; -- writel(val, prg->regs + IPU_PRG_REG_UPDATE); -- -- /* wait for both double buffers to be filled */ -- readl_poll_timeout(prg->regs + IPU_PRG_STATUS, val, -- (val & IPU_PRG_STATUS_BUFFER0_READY(prg_chan)) && -- (val & IPU_PRG_STATUS_BUFFER1_READY(prg_chan)), -- 5, 1000); -- -- pm_runtime_put(prg->dev); -- -- chan->enabled = true; -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_prg_channel_configure); -- --bool ipu_prg_channel_configure_pending(struct ipuv3_channel *ipu_chan) --{ -- int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); -- struct ipu_prg *prg = ipu_chan->ipu->prg_priv; -- struct ipu_prg_channel *chan; -- -- if (prg_chan < 0) -- return false; -- -- chan = &prg->chan[prg_chan]; -- WARN_ON(!chan->enabled); -- -- return ipu_pre_update_pending(prg->pres[chan->used_pre]); --} --EXPORT_SYMBOL_GPL(ipu_prg_channel_configure_pending); -- --static int ipu_prg_probe(struct platform_device *pdev) --{ -- struct device *dev = &pdev->dev; -- struct resource *res; -- struct ipu_prg *prg; -- u32 val; -- int i, ret; -- -- prg = devm_kzalloc(dev, sizeof(*prg), GFP_KERNEL); -- if (!prg) -- return -ENOMEM; -- -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- prg->regs = devm_ioremap_resource(&pdev->dev, res); -- if (IS_ERR(prg->regs)) -- return PTR_ERR(prg->regs); -- -- -- prg->clk_ipg = devm_clk_get(dev, "ipg"); -- if (IS_ERR(prg->clk_ipg)) -- return PTR_ERR(prg->clk_ipg); -- -- prg->clk_axi = devm_clk_get(dev, "axi"); -- if (IS_ERR(prg->clk_axi)) -- return PTR_ERR(prg->clk_axi); -- -- prg->iomuxc_gpr = -- syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); -- if (IS_ERR(prg->iomuxc_gpr)) -- return PTR_ERR(prg->iomuxc_gpr); -- -- for (i = 0; i < 3; i++) { -- prg->pres[i] = ipu_pre_lookup_by_phandle(dev, "fsl,pres", i); -- if (!prg->pres[i]) -- return -EPROBE_DEFER; -- } -- -- 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; -- } -- -- /* init to free running mode */ -- val = readl(prg->regs + IPU_PRG_CTL); -- val |= IPU_PRG_CTL_SHADOW_EN; -- writel(val, prg->regs + IPU_PRG_CTL); -- -- /* disable address threshold */ -- writel(0xffffffff, prg->regs + IPU_PRG_THD); -- -- pm_runtime_set_active(dev); -- pm_runtime_enable(dev); -- -- prg->dev = dev; -- platform_set_drvdata(pdev, prg); -- mutex_lock(&ipu_prg_list_mutex); -- list_add(&prg->list, &ipu_prg_list); -- mutex_unlock(&ipu_prg_list_mutex); -- -- return 0; --} -- --static int ipu_prg_remove(struct platform_device *pdev) --{ -- struct ipu_prg *prg = platform_get_drvdata(pdev); -- -- mutex_lock(&ipu_prg_list_mutex); -- list_del(&prg->list); -- mutex_unlock(&ipu_prg_list_mutex); -- -- 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 */ }, --}; -- --struct platform_driver ipu_prg_drv = { -- .probe = ipu_prg_probe, -- .remove = ipu_prg_remove, -- .driver = { -- .name = "imx-ipu-prg", -- .pm = &prg_pm_ops, -- .of_match_table = ipu_prg_dt_ids, -- }, --}; ---- a/drivers/gpu/imx/ipu-v3/ipu-prv.h -+++ /dev/null -@@ -1,274 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-or-later */ --/* -- * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#ifndef __IPU_PRV_H__ --#define __IPU_PRV_H__ -- --struct ipu_soc; -- --#include <linux/types.h> --#include <linux/device.h> --#include <linux/clk.h> --#include <linux/platform_device.h> -- --#include <video/imx-ipu-v3.h> -- --#define IPU_MCU_T_DEFAULT 8 --#define IPU_CM_IDMAC_REG_OFS 0x00008000 --#define IPU_CM_IC_REG_OFS 0x00020000 --#define IPU_CM_IRT_REG_OFS 0x00028000 --#define IPU_CM_CSI0_REG_OFS 0x00030000 --#define IPU_CM_CSI1_REG_OFS 0x00038000 --#define IPU_CM_SMFC_REG_OFS 0x00050000 --#define IPU_CM_DC_REG_OFS 0x00058000 --#define IPU_CM_DMFC_REG_OFS 0x00060000 -- --/* Register addresses */ --/* IPU Common registers */ --#define IPU_CM_REG(offset) (offset) -- --#define IPU_CONF IPU_CM_REG(0) -- --#define IPU_SRM_PRI1 IPU_CM_REG(0x00a0) --#define IPU_SRM_PRI2 IPU_CM_REG(0x00a4) --#define IPU_FS_PROC_FLOW1 IPU_CM_REG(0x00a8) --#define IPU_FS_PROC_FLOW2 IPU_CM_REG(0x00ac) --#define IPU_FS_PROC_FLOW3 IPU_CM_REG(0x00b0) --#define IPU_FS_DISP_FLOW1 IPU_CM_REG(0x00b4) --#define IPU_FS_DISP_FLOW2 IPU_CM_REG(0x00b8) --#define IPU_SKIP IPU_CM_REG(0x00bc) --#define IPU_DISP_ALT_CONF IPU_CM_REG(0x00c0) --#define IPU_DISP_GEN IPU_CM_REG(0x00c4) --#define IPU_DISP_ALT1 IPU_CM_REG(0x00c8) --#define IPU_DISP_ALT2 IPU_CM_REG(0x00cc) --#define IPU_DISP_ALT3 IPU_CM_REG(0x00d0) --#define IPU_DISP_ALT4 IPU_CM_REG(0x00d4) --#define IPU_SNOOP IPU_CM_REG(0x00d8) --#define IPU_MEM_RST IPU_CM_REG(0x00dc) --#define IPU_PM IPU_CM_REG(0x00e0) --#define IPU_GPR IPU_CM_REG(0x00e4) --#define IPU_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0150 + 4 * ((ch) / 32)) --#define IPU_ALT_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0168 + 4 * ((ch) / 32)) --#define IPU_CHA_CUR_BUF(ch) IPU_CM_REG(0x023C + 4 * ((ch) / 32)) --#define IPU_ALT_CUR_BUF0 IPU_CM_REG(0x0244) --#define IPU_ALT_CUR_BUF1 IPU_CM_REG(0x0248) --#define IPU_SRM_STAT IPU_CM_REG(0x024C) --#define IPU_PROC_TASK_STAT IPU_CM_REG(0x0250) --#define IPU_DISP_TASK_STAT IPU_CM_REG(0x0254) --#define IPU_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0268 + 4 * ((ch) / 32)) --#define IPU_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0270 + 4 * ((ch) / 32)) --#define IPU_CHA_BUF2_RDY(ch) IPU_CM_REG(0x0288 + 4 * ((ch) / 32)) --#define IPU_ALT_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0278 + 4 * ((ch) / 32)) --#define IPU_ALT_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0280 + 4 * ((ch) / 32)) -- --#define IPU_INT_CTRL(n) IPU_CM_REG(0x003C + 4 * (n)) --#define IPU_INT_STAT(n) IPU_CM_REG(0x0200 + 4 * (n)) -- --/* SRM_PRI2 */ --#define DP_S_SRM_MODE_MASK (0x3 << 3) --#define DP_S_SRM_MODE_NOW (0x3 << 3) --#define DP_S_SRM_MODE_NEXT_FRAME (0x1 << 3) -- --/* FS_PROC_FLOW1 */ --#define FS_PRPENC_ROT_SRC_SEL_MASK (0xf << 0) --#define FS_PRPENC_ROT_SRC_SEL_ENC (0x7 << 0) --#define FS_PRPVF_ROT_SRC_SEL_MASK (0xf << 8) --#define FS_PRPVF_ROT_SRC_SEL_VF (0x8 << 8) --#define FS_PP_SRC_SEL_MASK (0xf << 12) --#define FS_PP_ROT_SRC_SEL_MASK (0xf << 16) --#define FS_PP_ROT_SRC_SEL_PP (0x5 << 16) --#define FS_VDI1_SRC_SEL_MASK (0x3 << 20) --#define FS_VDI3_SRC_SEL_MASK (0x3 << 20) --#define FS_PRP_SRC_SEL_MASK (0xf << 24) --#define FS_VDI_SRC_SEL_MASK (0x3 << 28) --#define FS_VDI_SRC_SEL_CSI_DIRECT (0x1 << 28) --#define FS_VDI_SRC_SEL_VDOA (0x2 << 28) -- --/* FS_PROC_FLOW2 */ --#define FS_PRP_ENC_DEST_SEL_MASK (0xf << 0) --#define FS_PRP_ENC_DEST_SEL_IRT_ENC (0x1 << 0) --#define FS_PRPVF_DEST_SEL_MASK (0xf << 4) --#define FS_PRPVF_DEST_SEL_IRT_VF (0x1 << 4) --#define FS_PRPVF_ROT_DEST_SEL_MASK (0xf << 8) --#define FS_PP_DEST_SEL_MASK (0xf << 12) --#define FS_PP_DEST_SEL_IRT_PP (0x3 << 12) --#define FS_PP_ROT_DEST_SEL_MASK (0xf << 16) --#define FS_PRPENC_ROT_DEST_SEL_MASK (0xf << 20) --#define FS_PRP_DEST_SEL_MASK (0xf << 24) -- --#define IPU_DI0_COUNTER_RELEASE (1 << 24) --#define IPU_DI1_COUNTER_RELEASE (1 << 25) -- --#define IPU_IDMAC_REG(offset) (offset) -- --#define IDMAC_CONF IPU_IDMAC_REG(0x0000) --#define IDMAC_CHA_EN(ch) IPU_IDMAC_REG(0x0004 + 4 * ((ch) / 32)) --#define IDMAC_SEP_ALPHA IPU_IDMAC_REG(0x000c) --#define IDMAC_ALT_SEP_ALPHA IPU_IDMAC_REG(0x0010) --#define IDMAC_CHA_PRI(ch) IPU_IDMAC_REG(0x0014 + 4 * ((ch) / 32)) --#define IDMAC_WM_EN(ch) IPU_IDMAC_REG(0x001c + 4 * ((ch) / 32)) --#define IDMAC_CH_LOCK_EN_1 IPU_IDMAC_REG(0x0024) --#define IDMAC_CH_LOCK_EN_2 IPU_IDMAC_REG(0x0028) --#define IDMAC_SUB_ADDR_0 IPU_IDMAC_REG(0x002c) --#define IDMAC_SUB_ADDR_1 IPU_IDMAC_REG(0x0030) --#define IDMAC_SUB_ADDR_2 IPU_IDMAC_REG(0x0034) --#define IDMAC_BAND_EN(ch) IPU_IDMAC_REG(0x0040 + 4 * ((ch) / 32)) --#define IDMAC_CHA_BUSY(ch) IPU_IDMAC_REG(0x0100 + 4 * ((ch) / 32)) -- --#define IPU_NUM_IRQS (32 * 15) -- --enum ipu_modules { -- IPU_CONF_CSI0_EN = (1 << 0), -- IPU_CONF_CSI1_EN = (1 << 1), -- IPU_CONF_IC_EN = (1 << 2), -- IPU_CONF_ROT_EN = (1 << 3), -- IPU_CONF_ISP_EN = (1 << 4), -- IPU_CONF_DP_EN = (1 << 5), -- IPU_CONF_DI0_EN = (1 << 6), -- IPU_CONF_DI1_EN = (1 << 7), -- IPU_CONF_SMFC_EN = (1 << 8), -- IPU_CONF_DC_EN = (1 << 9), -- IPU_CONF_DMFC_EN = (1 << 10), -- -- IPU_CONF_VDI_EN = (1 << 12), -- -- IPU_CONF_IDMAC_DIS = (1 << 22), -- -- IPU_CONF_IC_DMFC_SEL = (1 << 25), -- IPU_CONF_IC_DMFC_SYNC = (1 << 26), -- IPU_CONF_VDI_DMFC_SYNC = (1 << 27), -- -- IPU_CONF_CSI0_DATA_SOURCE = (1 << 28), -- IPU_CONF_CSI1_DATA_SOURCE = (1 << 29), -- IPU_CONF_IC_INPUT = (1 << 30), -- IPU_CONF_CSI_SEL = (1 << 31), --}; -- --struct ipuv3_channel { -- unsigned int num; -- struct ipu_soc *ipu; -- struct list_head list; --}; -- --struct ipu_cpmem; --struct ipu_csi; --struct ipu_dc_priv; --struct ipu_dmfc_priv; --struct ipu_di; --struct ipu_ic_priv; --struct ipu_vdi; --struct ipu_image_convert_priv; --struct ipu_smfc_priv; --struct ipu_pre; --struct ipu_prg; -- --struct ipu_devtype; -- --struct ipu_soc { -- struct device *dev; -- const struct ipu_devtype *devtype; -- enum ipuv3_type ipu_type; -- spinlock_t lock; -- struct mutex channel_lock; -- struct list_head channels; -- -- void __iomem *cm_reg; -- void __iomem *idmac_reg; -- -- int id; -- int usecount; -- -- struct clk *clk; -- -- int irq_sync; -- int irq_err; -- struct irq_domain *domain; -- -- struct ipu_cpmem *cpmem_priv; -- struct ipu_dc_priv *dc_priv; -- struct ipu_dp_priv *dp_priv; -- struct ipu_dmfc_priv *dmfc_priv; -- struct ipu_di *di_priv[2]; -- struct ipu_csi *csi_priv[2]; -- struct ipu_ic_priv *ic_priv; -- struct ipu_vdi *vdi_priv; -- struct ipu_image_convert_priv *image_convert_priv; -- struct ipu_smfc_priv *smfc_priv; -- struct ipu_prg *prg_priv; --}; -- --static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset) --{ -- return readl(ipu->idmac_reg + offset); --} -- --static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value, -- unsigned offset) --{ -- writel(value, ipu->idmac_reg + offset); --} -- --void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync); -- --int ipu_module_enable(struct ipu_soc *ipu, u32 mask); --int ipu_module_disable(struct ipu_soc *ipu, u32 mask); -- --bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno); -- --int ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id, -- unsigned long base, u32 module, struct clk *clk_ipu); --void ipu_csi_exit(struct ipu_soc *ipu, int id); -- --int ipu_ic_init(struct ipu_soc *ipu, struct device *dev, -- unsigned long base, unsigned long tpmem_base); --void ipu_ic_exit(struct ipu_soc *ipu); -- --int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev, -- unsigned long base, u32 module); --void ipu_vdi_exit(struct ipu_soc *ipu); -- --int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev); --void ipu_image_convert_exit(struct ipu_soc *ipu); -- --int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, -- unsigned long base, u32 module, struct clk *ipu_clk); --void ipu_di_exit(struct ipu_soc *ipu, int id); -- --int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, -- struct clk *ipu_clk); --void ipu_dmfc_exit(struct ipu_soc *ipu); -- --int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); --void ipu_dp_exit(struct ipu_soc *ipu); -- --int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, -- unsigned long template_base); --void ipu_dc_exit(struct ipu_soc *ipu); -- --int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); --void ipu_cpmem_exit(struct ipu_soc *ipu); -- --int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); --void ipu_smfc_exit(struct ipu_soc *ipu); -- --struct ipu_pre *ipu_pre_lookup_by_phandle(struct device *dev, const char *name, -- int index); --int ipu_pre_get_available_count(void); --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, -- uint64_t modifier, unsigned int bufaddr); --void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr); --bool ipu_pre_update_pending(struct ipu_pre *pre); -- --struct ipu_prg *ipu_prg_lookup_by_phandle(struct device *dev, const char *name, -- int ipu_id); -- --extern struct platform_driver ipu_pre_drv; --extern struct platform_driver ipu_prg_drv; -- --#endif /* __IPU_PRV_H__ */ ---- a/drivers/gpu/imx/ipu-v3/ipu-smfc.c -+++ /dev/null -@@ -1,202 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. -- */ --#include <linux/export.h> --#include <linux/types.h> --#include <linux/init.h> --#include <linux/io.h> --#include <linux/errno.h> --#include <linux/spinlock.h> --#include <linux/delay.h> --#include <linux/clk.h> --#include <video/imx-ipu-v3.h> -- --#include "ipu-prv.h" -- --struct ipu_smfc { -- struct ipu_smfc_priv *priv; -- int chno; -- bool inuse; --}; -- --struct ipu_smfc_priv { -- void __iomem *base; -- spinlock_t lock; -- struct ipu_soc *ipu; -- struct ipu_smfc channel[4]; -- int use_count; --}; -- --/*SMFC Registers */ --#define SMFC_MAP 0x0000 --#define SMFC_WMC 0x0004 --#define SMFC_BS 0x0008 -- --int ipu_smfc_set_burstsize(struct ipu_smfc *smfc, int burstsize) --{ -- struct ipu_smfc_priv *priv = smfc->priv; -- unsigned long flags; -- u32 val, shift; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- shift = smfc->chno * 4; -- val = readl(priv->base + SMFC_BS); -- val &= ~(0xf << shift); -- val |= burstsize << shift; -- writel(val, priv->base + SMFC_BS); -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_smfc_set_burstsize); -- --int ipu_smfc_map_channel(struct ipu_smfc *smfc, int csi_id, int mipi_id) --{ -- struct ipu_smfc_priv *priv = smfc->priv; -- unsigned long flags; -- u32 val, shift; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- shift = smfc->chno * 3; -- val = readl(priv->base + SMFC_MAP); -- val &= ~(0x7 << shift); -- val |= ((csi_id << 2) | mipi_id) << shift; -- writel(val, priv->base + SMFC_MAP); -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_smfc_map_channel); -- --int ipu_smfc_set_watermark(struct ipu_smfc *smfc, u32 set_level, u32 clr_level) --{ -- struct ipu_smfc_priv *priv = smfc->priv; -- unsigned long flags; -- u32 val, shift; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- shift = smfc->chno * 6 + (smfc->chno > 1 ? 4 : 0); -- val = readl(priv->base + SMFC_WMC); -- val &= ~(0x3f << shift); -- val |= ((clr_level << 3) | set_level) << shift; -- writel(val, priv->base + SMFC_WMC); -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_smfc_set_watermark); -- --int ipu_smfc_enable(struct ipu_smfc *smfc) --{ -- struct ipu_smfc_priv *priv = smfc->priv; -- unsigned long flags; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- if (!priv->use_count) -- ipu_module_enable(priv->ipu, IPU_CONF_SMFC_EN); -- -- priv->use_count++; -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_smfc_enable); -- --int ipu_smfc_disable(struct ipu_smfc *smfc) --{ -- struct ipu_smfc_priv *priv = smfc->priv; -- unsigned long flags; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- priv->use_count--; -- -- if (!priv->use_count) -- ipu_module_disable(priv->ipu, IPU_CONF_SMFC_EN); -- -- if (priv->use_count < 0) -- priv->use_count = 0; -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_smfc_disable); -- --struct ipu_smfc *ipu_smfc_get(struct ipu_soc *ipu, unsigned int chno) --{ -- struct ipu_smfc_priv *priv = ipu->smfc_priv; -- struct ipu_smfc *smfc, *ret; -- unsigned long flags; -- -- if (chno >= 4) -- return ERR_PTR(-EINVAL); -- -- smfc = &priv->channel[chno]; -- ret = smfc; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- if (smfc->inuse) { -- ret = ERR_PTR(-EBUSY); -- goto unlock; -- } -- -- smfc->inuse = true; --unlock: -- spin_unlock_irqrestore(&priv->lock, flags); -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_smfc_get); -- --void ipu_smfc_put(struct ipu_smfc *smfc) --{ -- struct ipu_smfc_priv *priv = smfc->priv; -- unsigned long flags; -- -- spin_lock_irqsave(&priv->lock, flags); -- smfc->inuse = false; -- spin_unlock_irqrestore(&priv->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_smfc_put); -- --int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, -- unsigned long base) --{ -- struct ipu_smfc_priv *priv; -- int i; -- -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- -- ipu->smfc_priv = priv; -- spin_lock_init(&priv->lock); -- priv->ipu = ipu; -- -- priv->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!priv->base) -- return -ENOMEM; -- -- for (i = 0; i < 4; i++) { -- priv->channel[i].priv = priv; -- priv->channel[i].chno = i; -- } -- -- pr_debug("%s: ioremap 0x%08lx -> %p\n", __func__, base, priv->base); -- -- return 0; --} -- --void ipu_smfc_exit(struct ipu_soc *ipu) --{ --} ---- a/drivers/gpu/imx/ipu-v3/ipu-vdi.c -+++ /dev/null -@@ -1,234 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (C) 2012-2016 Mentor Graphics Inc. -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#include <linux/io.h> --#include "ipu-prv.h" -- --struct ipu_vdi { -- void __iomem *base; -- u32 module; -- spinlock_t lock; -- int use_count; -- struct ipu_soc *ipu; --}; -- -- --/* VDI Register Offsets */ --#define VDI_FSIZE 0x0000 --#define VDI_C 0x0004 -- --/* VDI Register Fields */ --#define VDI_C_CH_420 (0 << 1) --#define VDI_C_CH_422 (1 << 1) --#define VDI_C_MOT_SEL_MASK (0x3 << 2) --#define VDI_C_MOT_SEL_FULL (2 << 2) --#define VDI_C_MOT_SEL_LOW (1 << 2) --#define VDI_C_MOT_SEL_MED (0 << 2) --#define VDI_C_BURST_SIZE1_4 (3 << 4) --#define VDI_C_BURST_SIZE2_4 (3 << 8) --#define VDI_C_BURST_SIZE3_4 (3 << 12) --#define VDI_C_BURST_SIZE_MASK 0xF --#define VDI_C_BURST_SIZE1_OFFSET 4 --#define VDI_C_BURST_SIZE2_OFFSET 8 --#define VDI_C_BURST_SIZE3_OFFSET 12 --#define VDI_C_VWM1_SET_1 (0 << 16) --#define VDI_C_VWM1_SET_2 (1 << 16) --#define VDI_C_VWM1_CLR_2 (1 << 19) --#define VDI_C_VWM3_SET_1 (0 << 22) --#define VDI_C_VWM3_SET_2 (1 << 22) --#define VDI_C_VWM3_CLR_2 (1 << 25) --#define VDI_C_TOP_FIELD_MAN_1 (1 << 30) --#define VDI_C_TOP_FIELD_AUTO_1 (1 << 31) -- --static inline u32 ipu_vdi_read(struct ipu_vdi *vdi, unsigned int offset) --{ -- return readl(vdi->base + offset); --} -- --static inline void ipu_vdi_write(struct ipu_vdi *vdi, u32 value, -- unsigned int offset) --{ -- writel(value, vdi->base + offset); --} -- --void ipu_vdi_set_field_order(struct ipu_vdi *vdi, v4l2_std_id std, u32 field) --{ -- bool top_field_0 = false; -- unsigned long flags; -- u32 reg; -- -- switch (field) { -- case V4L2_FIELD_INTERLACED_TB: -- case V4L2_FIELD_SEQ_TB: -- case V4L2_FIELD_TOP: -- top_field_0 = true; -- break; -- case V4L2_FIELD_INTERLACED_BT: -- case V4L2_FIELD_SEQ_BT: -- case V4L2_FIELD_BOTTOM: -- top_field_0 = false; -- break; -- default: -- top_field_0 = (std & V4L2_STD_525_60) ? true : false; -- break; -- } -- -- spin_lock_irqsave(&vdi->lock, flags); -- -- reg = ipu_vdi_read(vdi, VDI_C); -- if (top_field_0) -- reg &= ~(VDI_C_TOP_FIELD_MAN_1 | VDI_C_TOP_FIELD_AUTO_1); -- else -- reg |= VDI_C_TOP_FIELD_MAN_1 | VDI_C_TOP_FIELD_AUTO_1; -- ipu_vdi_write(vdi, reg, VDI_C); -- -- spin_unlock_irqrestore(&vdi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_vdi_set_field_order); -- --void ipu_vdi_set_motion(struct ipu_vdi *vdi, enum ipu_motion_sel motion_sel) --{ -- unsigned long flags; -- u32 reg; -- -- spin_lock_irqsave(&vdi->lock, flags); -- -- reg = ipu_vdi_read(vdi, VDI_C); -- -- reg &= ~VDI_C_MOT_SEL_MASK; -- -- switch (motion_sel) { -- case MED_MOTION: -- reg |= VDI_C_MOT_SEL_MED; -- break; -- case HIGH_MOTION: -- reg |= VDI_C_MOT_SEL_FULL; -- break; -- default: -- reg |= VDI_C_MOT_SEL_LOW; -- break; -- } -- -- ipu_vdi_write(vdi, reg, VDI_C); -- -- spin_unlock_irqrestore(&vdi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_vdi_set_motion); -- --void ipu_vdi_setup(struct ipu_vdi *vdi, u32 code, int xres, int yres) --{ -- unsigned long flags; -- u32 pixel_fmt, reg; -- -- spin_lock_irqsave(&vdi->lock, flags); -- -- reg = ((yres - 1) << 16) | (xres - 1); -- ipu_vdi_write(vdi, reg, VDI_FSIZE); -- -- /* -- * Full motion, only vertical filter is used. -- * Burst size is 4 accesses -- */ -- if (code == MEDIA_BUS_FMT_UYVY8_2X8 || -- code == MEDIA_BUS_FMT_UYVY8_1X16 || -- code == MEDIA_BUS_FMT_YUYV8_2X8 || -- code == MEDIA_BUS_FMT_YUYV8_1X16) -- pixel_fmt = VDI_C_CH_422; -- else -- pixel_fmt = VDI_C_CH_420; -- -- reg = ipu_vdi_read(vdi, VDI_C); -- reg |= pixel_fmt; -- reg |= VDI_C_BURST_SIZE2_4; -- reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_CLR_2; -- reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_CLR_2; -- ipu_vdi_write(vdi, reg, VDI_C); -- -- spin_unlock_irqrestore(&vdi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_vdi_setup); -- --void ipu_vdi_unsetup(struct ipu_vdi *vdi) --{ -- unsigned long flags; -- -- spin_lock_irqsave(&vdi->lock, flags); -- ipu_vdi_write(vdi, 0, VDI_FSIZE); -- ipu_vdi_write(vdi, 0, VDI_C); -- spin_unlock_irqrestore(&vdi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_vdi_unsetup); -- --int ipu_vdi_enable(struct ipu_vdi *vdi) --{ -- unsigned long flags; -- -- spin_lock_irqsave(&vdi->lock, flags); -- -- if (!vdi->use_count) -- ipu_module_enable(vdi->ipu, vdi->module); -- -- vdi->use_count++; -- -- spin_unlock_irqrestore(&vdi->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_vdi_enable); -- --int ipu_vdi_disable(struct ipu_vdi *vdi) --{ -- unsigned long flags; -- -- spin_lock_irqsave(&vdi->lock, flags); -- -- if (vdi->use_count) { -- if (!--vdi->use_count) -- ipu_module_disable(vdi->ipu, vdi->module); -- } -- -- spin_unlock_irqrestore(&vdi->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_vdi_disable); -- --struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu) --{ -- return ipu->vdi_priv; --} --EXPORT_SYMBOL_GPL(ipu_vdi_get); -- --void ipu_vdi_put(struct ipu_vdi *vdi) --{ --} --EXPORT_SYMBOL_GPL(ipu_vdi_put); -- --int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev, -- unsigned long base, u32 module) --{ -- struct ipu_vdi *vdi; -- -- vdi = devm_kzalloc(dev, sizeof(*vdi), GFP_KERNEL); -- if (!vdi) -- return -ENOMEM; -- -- ipu->vdi_priv = vdi; -- -- spin_lock_init(&vdi->lock); -- vdi->module = module; -- vdi->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!vdi->base) -- return -ENOMEM; -- -- dev_dbg(dev, "VDI base: 0x%08lx remapped to %p\n", base, vdi->base); -- vdi->ipu = ipu; -- -- return 0; --} -- --void ipu_vdi_exit(struct ipu_soc *ipu) --{ --} ---- /dev/null -+++ b/drivers/gpu/ipu-v3/Kconfig -@@ -0,0 +1,11 @@ -+# SPDX-License-Identifier: GPL-2.0-only -+config IMX_IPUV3_CORE -+ tristate "IPUv3 core support" -+ 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 -+ Processing Unit. This option only enables IPU base support. ---- /dev/null -+++ b/drivers/gpu/ipu-v3/Makefile -@@ -0,0 +1,10 @@ -+# SPDX-License-Identifier: GPL-2.0 -+obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o -+ -+imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \ -+ ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-ic-csc.o \ -+ ipu-image-convert.o ipu-smfc.o ipu-vdi.o -+ -+ifdef CONFIG_DRM -+ imx-ipu-v3-objs += ipu-pre.o ipu-prg.o -+endif ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-common.c -@@ -0,0 +1,1565 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#include <linux/module.h> -+#include <linux/export.h> -+#include <linux/types.h> -+#include <linux/reset.h> -+#include <linux/platform_device.h> -+#include <linux/err.h> -+#include <linux/spinlock.h> -+#include <linux/delay.h> -+#include <linux/interrupt.h> -+#include <linux/io.h> -+#include <linux/clk.h> -+#include <linux/list.h> -+#include <linux/irq.h> -+#include <linux/irqchip/chained_irq.h> -+#include <linux/irqdomain.h> -+#include <linux/of_device.h> -+#include <linux/of_graph.h> -+ -+#include <drm/drm_fourcc.h> -+ -+#include <video/imx-ipu-v3.h> -+#include "ipu-prv.h" -+ -+static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset) -+{ -+ return readl(ipu->cm_reg + offset); -+} -+ -+static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset) -+{ -+ writel(value, ipu->cm_reg + offset); -+} -+ -+int ipu_get_num(struct ipu_soc *ipu) -+{ -+ return ipu->id; -+} -+EXPORT_SYMBOL_GPL(ipu_get_num); -+ -+void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync) -+{ -+ u32 val; -+ -+ val = ipu_cm_read(ipu, IPU_SRM_PRI2); -+ val &= ~DP_S_SRM_MODE_MASK; -+ val |= sync ? DP_S_SRM_MODE_NEXT_FRAME : -+ DP_S_SRM_MODE_NOW; -+ ipu_cm_write(ipu, val, IPU_SRM_PRI2); -+} -+EXPORT_SYMBOL_GPL(ipu_srm_dp_update); -+ -+enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc) -+{ -+ switch (drm_fourcc) { -+ case DRM_FORMAT_ARGB1555: -+ case DRM_FORMAT_ABGR1555: -+ case DRM_FORMAT_RGBA5551: -+ case DRM_FORMAT_BGRA5551: -+ case DRM_FORMAT_RGB565: -+ case DRM_FORMAT_BGR565: -+ case DRM_FORMAT_RGB888: -+ case DRM_FORMAT_BGR888: -+ case DRM_FORMAT_ARGB4444: -+ case DRM_FORMAT_XRGB8888: -+ case DRM_FORMAT_XBGR8888: -+ case DRM_FORMAT_RGBX8888: -+ case DRM_FORMAT_BGRX8888: -+ case DRM_FORMAT_ARGB8888: -+ case DRM_FORMAT_ABGR8888: -+ case DRM_FORMAT_RGBA8888: -+ case DRM_FORMAT_BGRA8888: -+ case DRM_FORMAT_RGB565_A8: -+ case DRM_FORMAT_BGR565_A8: -+ case DRM_FORMAT_RGB888_A8: -+ case DRM_FORMAT_BGR888_A8: -+ case DRM_FORMAT_RGBX8888_A8: -+ case DRM_FORMAT_BGRX8888_A8: -+ return IPUV3_COLORSPACE_RGB; -+ case DRM_FORMAT_YUYV: -+ case DRM_FORMAT_UYVY: -+ case DRM_FORMAT_YUV420: -+ case DRM_FORMAT_YVU420: -+ case DRM_FORMAT_YUV422: -+ case DRM_FORMAT_YVU422: -+ case DRM_FORMAT_YUV444: -+ case DRM_FORMAT_YVU444: -+ case DRM_FORMAT_NV12: -+ case DRM_FORMAT_NV21: -+ case DRM_FORMAT_NV16: -+ case DRM_FORMAT_NV61: -+ return IPUV3_COLORSPACE_YUV; -+ default: -+ return IPUV3_COLORSPACE_UNKNOWN; -+ } -+} -+EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace); -+ -+enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat) -+{ -+ switch (pixelformat) { -+ case V4L2_PIX_FMT_YUV420: -+ case V4L2_PIX_FMT_YVU420: -+ case V4L2_PIX_FMT_YUV422P: -+ case V4L2_PIX_FMT_UYVY: -+ case V4L2_PIX_FMT_YUYV: -+ case V4L2_PIX_FMT_NV12: -+ case V4L2_PIX_FMT_NV21: -+ case V4L2_PIX_FMT_NV16: -+ case V4L2_PIX_FMT_NV61: -+ return IPUV3_COLORSPACE_YUV; -+ case V4L2_PIX_FMT_RGB565: -+ case V4L2_PIX_FMT_BGR24: -+ case V4L2_PIX_FMT_RGB24: -+ case V4L2_PIX_FMT_ABGR32: -+ case V4L2_PIX_FMT_XBGR32: -+ case V4L2_PIX_FMT_BGRA32: -+ case V4L2_PIX_FMT_BGRX32: -+ case V4L2_PIX_FMT_RGBA32: -+ case V4L2_PIX_FMT_RGBX32: -+ case V4L2_PIX_FMT_ARGB32: -+ case V4L2_PIX_FMT_XRGB32: -+ return IPUV3_COLORSPACE_RGB; -+ default: -+ return IPUV3_COLORSPACE_UNKNOWN; -+ } -+} -+EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace); -+ -+bool ipu_pixelformat_is_planar(u32 pixelformat) -+{ -+ switch (pixelformat) { -+ case V4L2_PIX_FMT_YUV420: -+ case V4L2_PIX_FMT_YVU420: -+ case V4L2_PIX_FMT_YUV422P: -+ case V4L2_PIX_FMT_NV12: -+ case V4L2_PIX_FMT_NV21: -+ case V4L2_PIX_FMT_NV16: -+ case V4L2_PIX_FMT_NV61: -+ return true; -+ } -+ -+ return false; -+} -+EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar); -+ -+enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code) -+{ -+ switch (mbus_code & 0xf000) { -+ case 0x1000: -+ return IPUV3_COLORSPACE_RGB; -+ case 0x2000: -+ return IPUV3_COLORSPACE_YUV; -+ default: -+ return IPUV3_COLORSPACE_UNKNOWN; -+ } -+} -+EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace); -+ -+int ipu_stride_to_bytes(u32 pixel_stride, u32 pixelformat) -+{ -+ switch (pixelformat) { -+ case V4L2_PIX_FMT_YUV420: -+ case V4L2_PIX_FMT_YVU420: -+ case V4L2_PIX_FMT_YUV422P: -+ case V4L2_PIX_FMT_NV12: -+ case V4L2_PIX_FMT_NV21: -+ case V4L2_PIX_FMT_NV16: -+ case V4L2_PIX_FMT_NV61: -+ /* -+ * for the planar YUV formats, the stride passed to -+ * cpmem must be the stride in bytes of the Y plane. -+ * And all the planar YUV formats have an 8-bit -+ * Y component. -+ */ -+ return (8 * pixel_stride) >> 3; -+ case V4L2_PIX_FMT_RGB565: -+ case V4L2_PIX_FMT_YUYV: -+ case V4L2_PIX_FMT_UYVY: -+ return (16 * pixel_stride) >> 3; -+ case V4L2_PIX_FMT_BGR24: -+ case V4L2_PIX_FMT_RGB24: -+ return (24 * pixel_stride) >> 3; -+ case V4L2_PIX_FMT_BGR32: -+ case V4L2_PIX_FMT_RGB32: -+ case V4L2_PIX_FMT_XBGR32: -+ case V4L2_PIX_FMT_XRGB32: -+ return (32 * pixel_stride) >> 3; -+ default: -+ break; -+ } -+ -+ return -EINVAL; -+} -+EXPORT_SYMBOL_GPL(ipu_stride_to_bytes); -+ -+int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees, -+ bool hflip, bool vflip) -+{ -+ u32 r90, vf, hf; -+ -+ switch (degrees) { -+ case 0: -+ vf = hf = r90 = 0; -+ break; -+ case 90: -+ vf = hf = 0; -+ r90 = 1; -+ break; -+ case 180: -+ vf = hf = 1; -+ r90 = 0; -+ break; -+ case 270: -+ vf = hf = r90 = 1; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ hf ^= (u32)hflip; -+ vf ^= (u32)vflip; -+ -+ *mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode); -+ -+int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode, -+ bool hflip, bool vflip) -+{ -+ u32 r90, vf, hf; -+ -+ r90 = ((u32)mode >> 2) & 0x1; -+ hf = ((u32)mode >> 1) & 0x1; -+ vf = ((u32)mode >> 0) & 0x1; -+ hf ^= (u32)hflip; -+ vf ^= (u32)vflip; -+ -+ switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) { -+ case IPU_ROTATE_NONE: -+ *degrees = 0; -+ break; -+ case IPU_ROTATE_90_RIGHT: -+ *degrees = 90; -+ break; -+ case IPU_ROTATE_180: -+ *degrees = 180; -+ break; -+ case IPU_ROTATE_90_LEFT: -+ *degrees = 270; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees); -+ -+struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num) -+{ -+ struct ipuv3_channel *channel; -+ -+ dev_dbg(ipu->dev, "%s %d\n", __func__, num); -+ -+ if (num > 63) -+ return ERR_PTR(-ENODEV); -+ -+ mutex_lock(&ipu->channel_lock); -+ -+ list_for_each_entry(channel, &ipu->channels, list) { -+ if (channel->num == num) { -+ channel = ERR_PTR(-EBUSY); -+ goto out; -+ } -+ } -+ -+ channel = kzalloc(sizeof(*channel), GFP_KERNEL); -+ if (!channel) { -+ channel = ERR_PTR(-ENOMEM); -+ goto out; -+ } -+ -+ channel->num = num; -+ channel->ipu = ipu; -+ list_add(&channel->list, &ipu->channels); -+ -+out: -+ mutex_unlock(&ipu->channel_lock); -+ -+ return channel; -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_get); -+ -+void ipu_idmac_put(struct ipuv3_channel *channel) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ -+ dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num); -+ -+ mutex_lock(&ipu->channel_lock); -+ -+ list_del(&channel->list); -+ kfree(channel); -+ -+ mutex_unlock(&ipu->channel_lock); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_put); -+ -+#define idma_mask(ch) (1 << ((ch) & 0x1f)) -+ -+/* -+ * This is an undocumented feature, a write one to a channel bit in -+ * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's -+ * internal current buffer pointer so that transfers start from buffer -+ * 0 on the next channel enable (that's the theory anyway, the imx6 TRM -+ * only says these are read-only registers). This operation is required -+ * for channel linking to work correctly, for instance video capture -+ * pipelines that carry out image rotations will fail after the first -+ * streaming unless this function is called for each channel before -+ * re-enabling the channels. -+ */ -+static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned int chno = channel->num; -+ -+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno)); -+} -+ -+void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel, -+ bool doublebuffer) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned long flags; -+ u32 reg; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); -+ if (doublebuffer) -+ reg |= idma_mask(channel->num); -+ else -+ reg &= ~idma_mask(channel->num); -+ ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num)); -+ -+ __ipu_idmac_reset_current_buffer(channel); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer); -+ -+static const struct { -+ int chnum; -+ u32 reg; -+ int shift; -+} idmac_lock_en_info[] = { -+ { .chnum = 5, .reg = IDMAC_CH_LOCK_EN_1, .shift = 0, }, -+ { .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift = 2, }, -+ { .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift = 4, }, -+ { .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift = 6, }, -+ { .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift = 8, }, -+ { .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, }, -+ { .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, }, -+ { .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, }, -+ { .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, }, -+ { .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, }, -+ { .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, }, -+ { .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift = 0, }, -+ { .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift = 2, }, -+ { .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift = 4, }, -+ { .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift = 6, }, -+ { .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift = 8, }, -+ { .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, }, -+}; -+ -+int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned long flags; -+ u32 bursts, regval; -+ int i; -+ -+ switch (num_bursts) { -+ case 0: -+ case 1: -+ bursts = 0x00; /* locking disabled */ -+ break; -+ case 2: -+ bursts = 0x01; -+ break; -+ case 4: -+ bursts = 0x02; -+ break; -+ case 8: -+ bursts = 0x03; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ /* -+ * IPUv3EX / i.MX51 has a different register layout, and on IPUv3M / -+ * i.MX53 channel arbitration locking doesn't seem to work properly. -+ * Allow enabling the lock feature on IPUv3H / i.MX6 only. -+ */ -+ if (bursts && ipu->ipu_type != IPUV3H) -+ return -EINVAL; -+ -+ for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) { -+ if (channel->num == idmac_lock_en_info[i].chnum) -+ break; -+ } -+ if (i >= ARRAY_SIZE(idmac_lock_en_info)) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg); -+ regval &= ~(0x03 << idmac_lock_en_info[i].shift); -+ regval |= (bursts << idmac_lock_en_info[i].shift); -+ ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable); -+ -+int ipu_module_enable(struct ipu_soc *ipu, u32 mask) -+{ -+ unsigned long lock_flags; -+ u32 val; -+ -+ spin_lock_irqsave(&ipu->lock, lock_flags); -+ -+ val = ipu_cm_read(ipu, IPU_DISP_GEN); -+ -+ if (mask & IPU_CONF_DI0_EN) -+ val |= IPU_DI0_COUNTER_RELEASE; -+ if (mask & IPU_CONF_DI1_EN) -+ val |= IPU_DI1_COUNTER_RELEASE; -+ -+ ipu_cm_write(ipu, val, IPU_DISP_GEN); -+ -+ val = ipu_cm_read(ipu, IPU_CONF); -+ val |= mask; -+ ipu_cm_write(ipu, val, IPU_CONF); -+ -+ spin_unlock_irqrestore(&ipu->lock, lock_flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_module_enable); -+ -+int ipu_module_disable(struct ipu_soc *ipu, u32 mask) -+{ -+ unsigned long lock_flags; -+ u32 val; -+ -+ spin_lock_irqsave(&ipu->lock, lock_flags); -+ -+ val = ipu_cm_read(ipu, IPU_CONF); -+ val &= ~mask; -+ ipu_cm_write(ipu, val, IPU_CONF); -+ -+ val = ipu_cm_read(ipu, IPU_DISP_GEN); -+ -+ if (mask & IPU_CONF_DI0_EN) -+ val &= ~IPU_DI0_COUNTER_RELEASE; -+ if (mask & IPU_CONF_DI1_EN) -+ val &= ~IPU_DI1_COUNTER_RELEASE; -+ -+ ipu_cm_write(ipu, val, IPU_DISP_GEN); -+ -+ spin_unlock_irqrestore(&ipu->lock, lock_flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_module_disable); -+ -+int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned int chno = channel->num; -+ -+ return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0; -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer); -+ -+bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned long flags; -+ u32 reg = 0; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ switch (buf_num) { -+ case 0: -+ reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)); -+ break; -+ case 1: -+ reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)); -+ break; -+ case 2: -+ reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num)); -+ break; -+ } -+ spin_unlock_irqrestore(&ipu->lock, flags); -+ -+ return ((reg & idma_mask(channel->num)) != 0); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready); -+ -+void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned int chno = channel->num; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ /* Mark buffer as ready. */ -+ if (buf_num == 0) -+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); -+ else -+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer); -+ -+void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned int chno = channel->num; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */ -+ switch (buf_num) { -+ case 0: -+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); -+ break; -+ case 1: -+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); -+ break; -+ case 2: -+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno)); -+ break; -+ default: -+ break; -+ } -+ ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer); -+ -+int ipu_idmac_enable_channel(struct ipuv3_channel *channel) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ u32 val; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); -+ val |= idma_mask(channel->num); -+ ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel); -+ -+bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno) -+{ -+ return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno)); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy); -+ -+int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned long timeout; -+ -+ timeout = jiffies + msecs_to_jiffies(ms); -+ while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) & -+ idma_mask(channel->num)) { -+ if (time_after(jiffies, timeout)) -+ return -ETIMEDOUT; -+ cpu_relax(); -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); -+ -+int ipu_idmac_disable_channel(struct ipuv3_channel *channel) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ u32 val; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ /* Disable DMA channel(s) */ -+ val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); -+ val &= ~idma_mask(channel->num); -+ ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); -+ -+ __ipu_idmac_reset_current_buffer(channel); -+ -+ /* Set channel buffers NOT to be ready */ -+ ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */ -+ -+ if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) & -+ idma_mask(channel->num)) { -+ ipu_cm_write(ipu, idma_mask(channel->num), -+ IPU_CHA_BUF0_RDY(channel->num)); -+ } -+ -+ if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) & -+ idma_mask(channel->num)) { -+ ipu_cm_write(ipu, idma_mask(channel->num), -+ IPU_CHA_BUF1_RDY(channel->num)); -+ } -+ -+ ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ -+ -+ /* Reset the double buffer */ -+ val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); -+ val &= ~idma_mask(channel->num); -+ ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num)); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel); -+ -+/* -+ * The imx6 rev. D TRM says that enabling the WM feature will increase -+ * a channel's priority. Refer to Table 36-8 Calculated priority value. -+ * The sub-module that is the sink or source for the channel must enable -+ * watermark signal for this to take effect (SMFC_WM for instance). -+ */ -+void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned long flags; -+ u32 val; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num)); -+ if (enable) -+ val |= 1 << (channel->num % 32); -+ else -+ val &= ~(1 << (channel->num % 32)); -+ ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num)); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark); -+ -+static int ipu_memory_reset(struct ipu_soc *ipu) -+{ -+ unsigned long timeout; -+ -+ ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); -+ -+ timeout = jiffies + msecs_to_jiffies(1000); -+ while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { -+ if (time_after(jiffies, timeout)) -+ return -ETIME; -+ cpu_relax(); -+ } -+ -+ return 0; -+} -+ -+/* -+ * Set the source mux for the given CSI. Selects either parallel or -+ * MIPI CSI2 sources. -+ */ -+void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2) -+{ -+ unsigned long flags; -+ u32 val, mask; -+ -+ mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE : -+ IPU_CONF_CSI0_DATA_SOURCE; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ val = ipu_cm_read(ipu, IPU_CONF); -+ if (mipi_csi2) -+ val |= mask; -+ else -+ val &= ~mask; -+ ipu_cm_write(ipu, val, IPU_CONF); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux); -+ -+/* -+ * Set the source mux for the IC. Selects either CSI[01] or the VDI. -+ */ -+void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi) -+{ -+ unsigned long flags; -+ u32 val; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ val = ipu_cm_read(ipu, IPU_CONF); -+ if (vdi) -+ val |= IPU_CONF_IC_INPUT; -+ else -+ val &= ~IPU_CONF_IC_INPUT; -+ -+ if (csi_id == 1) -+ val |= IPU_CONF_CSI_SEL; -+ else -+ val &= ~IPU_CONF_CSI_SEL; -+ -+ ipu_cm_write(ipu, val, IPU_CONF); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux); -+ -+ -+/* Frame Synchronization Unit Channel Linking */ -+ -+struct fsu_link_reg_info { -+ int chno; -+ u32 reg; -+ u32 mask; -+ u32 val; -+}; -+ -+struct fsu_link_info { -+ struct fsu_link_reg_info src; -+ struct fsu_link_reg_info sink; -+}; -+ -+static const struct fsu_link_info fsu_link_info[] = { -+ { -+ .src = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2, -+ FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC }, -+ .sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1, -+ FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC }, -+ }, { -+ .src = { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2, -+ FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF }, -+ .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1, -+ FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF }, -+ }, { -+ .src = { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2, -+ FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP }, -+ .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1, -+ FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP }, -+ }, { -+ .src = { IPUV3_CHANNEL_CSI_DIRECT, 0 }, -+ .sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1, -+ FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT }, -+ }, -+}; -+ -+static const struct fsu_link_info *find_fsu_link_info(int src, int sink) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) { -+ if (src == fsu_link_info[i].src.chno && -+ sink == fsu_link_info[i].sink.chno) -+ return &fsu_link_info[i]; -+ } -+ -+ return NULL; -+} -+ -+/* -+ * Links a source channel to a sink channel in the FSU. -+ */ -+int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch) -+{ -+ const struct fsu_link_info *link; -+ u32 src_reg, sink_reg; -+ unsigned long flags; -+ -+ link = find_fsu_link_info(src_ch, sink_ch); -+ if (!link) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ if (link->src.mask) { -+ src_reg = ipu_cm_read(ipu, link->src.reg); -+ src_reg &= ~link->src.mask; -+ src_reg |= link->src.val; -+ ipu_cm_write(ipu, src_reg, link->src.reg); -+ } -+ -+ if (link->sink.mask) { -+ sink_reg = ipu_cm_read(ipu, link->sink.reg); -+ sink_reg &= ~link->sink.mask; -+ sink_reg |= link->sink.val; -+ ipu_cm_write(ipu, sink_reg, link->sink.reg); -+ } -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_fsu_link); -+ -+/* -+ * Unlinks source and sink channels in the FSU. -+ */ -+int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch) -+{ -+ const struct fsu_link_info *link; -+ u32 src_reg, sink_reg; -+ unsigned long flags; -+ -+ link = find_fsu_link_info(src_ch, sink_ch); -+ if (!link) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ if (link->src.mask) { -+ src_reg = ipu_cm_read(ipu, link->src.reg); -+ src_reg &= ~link->src.mask; -+ ipu_cm_write(ipu, src_reg, link->src.reg); -+ } -+ -+ if (link->sink.mask) { -+ sink_reg = ipu_cm_read(ipu, link->sink.reg); -+ sink_reg &= ~link->sink.mask; -+ ipu_cm_write(ipu, sink_reg, link->sink.reg); -+ } -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_fsu_unlink); -+ -+/* Link IDMAC channels in the FSU */ -+int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink) -+{ -+ return ipu_fsu_link(src->ipu, src->num, sink->num); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_link); -+ -+/* Unlink IDMAC channels in the FSU */ -+int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink) -+{ -+ return ipu_fsu_unlink(src->ipu, src->num, sink->num); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_unlink); -+ -+struct ipu_devtype { -+ const char *name; -+ unsigned long cm_ofs; -+ unsigned long cpmem_ofs; -+ unsigned long srm_ofs; -+ unsigned long tpm_ofs; -+ unsigned long csi0_ofs; -+ unsigned long csi1_ofs; -+ unsigned long ic_ofs; -+ unsigned long disp0_ofs; -+ unsigned long disp1_ofs; -+ unsigned long dc_tmpl_ofs; -+ unsigned long vdi_ofs; -+ enum ipuv3_type type; -+}; -+ -+static struct ipu_devtype ipu_type_imx51 = { -+ .name = "IPUv3EX", -+ .cm_ofs = 0x1e000000, -+ .cpmem_ofs = 0x1f000000, -+ .srm_ofs = 0x1f040000, -+ .tpm_ofs = 0x1f060000, -+ .csi0_ofs = 0x1e030000, -+ .csi1_ofs = 0x1e038000, -+ .ic_ofs = 0x1e020000, -+ .disp0_ofs = 0x1e040000, -+ .disp1_ofs = 0x1e048000, -+ .dc_tmpl_ofs = 0x1f080000, -+ .vdi_ofs = 0x1e068000, -+ .type = IPUV3EX, -+}; -+ -+static struct ipu_devtype ipu_type_imx53 = { -+ .name = "IPUv3M", -+ .cm_ofs = 0x06000000, -+ .cpmem_ofs = 0x07000000, -+ .srm_ofs = 0x07040000, -+ .tpm_ofs = 0x07060000, -+ .csi0_ofs = 0x06030000, -+ .csi1_ofs = 0x06038000, -+ .ic_ofs = 0x06020000, -+ .disp0_ofs = 0x06040000, -+ .disp1_ofs = 0x06048000, -+ .dc_tmpl_ofs = 0x07080000, -+ .vdi_ofs = 0x06068000, -+ .type = IPUV3M, -+}; -+ -+static struct ipu_devtype ipu_type_imx6q = { -+ .name = "IPUv3H", -+ .cm_ofs = 0x00200000, -+ .cpmem_ofs = 0x00300000, -+ .srm_ofs = 0x00340000, -+ .tpm_ofs = 0x00360000, -+ .csi0_ofs = 0x00230000, -+ .csi1_ofs = 0x00238000, -+ .ic_ofs = 0x00220000, -+ .disp0_ofs = 0x00240000, -+ .disp1_ofs = 0x00248000, -+ .dc_tmpl_ofs = 0x00380000, -+ .vdi_ofs = 0x00268000, -+ .type = IPUV3H, -+}; -+ -+static const struct of_device_id imx_ipu_dt_ids[] = { -+ { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, -+ { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, -+ { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, -+ { .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6q, }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); -+ -+static int ipu_submodules_init(struct ipu_soc *ipu, -+ struct platform_device *pdev, unsigned long ipu_base, -+ struct clk *ipu_clk) -+{ -+ char *unit; -+ int ret; -+ struct device *dev = &pdev->dev; -+ const struct ipu_devtype *devtype = ipu->devtype; -+ -+ ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs); -+ if (ret) { -+ unit = "cpmem"; -+ goto err_cpmem; -+ } -+ -+ ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs, -+ IPU_CONF_CSI0_EN, ipu_clk); -+ if (ret) { -+ unit = "csi0"; -+ goto err_csi_0; -+ } -+ -+ ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs, -+ IPU_CONF_CSI1_EN, ipu_clk); -+ if (ret) { -+ unit = "csi1"; -+ goto err_csi_1; -+ } -+ -+ ret = ipu_ic_init(ipu, dev, -+ ipu_base + devtype->ic_ofs, -+ ipu_base + devtype->tpm_ofs); -+ if (ret) { -+ unit = "ic"; -+ goto err_ic; -+ } -+ -+ ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs, -+ IPU_CONF_VDI_EN | IPU_CONF_ISP_EN | -+ IPU_CONF_IC_INPUT); -+ if (ret) { -+ unit = "vdi"; -+ goto err_vdi; -+ } -+ -+ ret = ipu_image_convert_init(ipu, dev); -+ if (ret) { -+ unit = "image_convert"; -+ goto err_image_convert; -+ } -+ -+ ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, -+ IPU_CONF_DI0_EN, ipu_clk); -+ if (ret) { -+ unit = "di0"; -+ goto err_di_0; -+ } -+ -+ ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs, -+ IPU_CONF_DI1_EN, ipu_clk); -+ if (ret) { -+ unit = "di1"; -+ goto err_di_1; -+ } -+ -+ ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs + -+ IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs); -+ if (ret) { -+ unit = "dc_template"; -+ goto err_dc; -+ } -+ -+ ret = ipu_dmfc_init(ipu, dev, ipu_base + -+ devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk); -+ if (ret) { -+ unit = "dmfc"; -+ goto err_dmfc; -+ } -+ -+ ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs); -+ if (ret) { -+ unit = "dp"; -+ goto err_dp; -+ } -+ -+ ret = ipu_smfc_init(ipu, dev, ipu_base + -+ devtype->cm_ofs + IPU_CM_SMFC_REG_OFS); -+ if (ret) { -+ unit = "smfc"; -+ goto err_smfc; -+ } -+ -+ return 0; -+ -+err_smfc: -+ ipu_dp_exit(ipu); -+err_dp: -+ ipu_dmfc_exit(ipu); -+err_dmfc: -+ ipu_dc_exit(ipu); -+err_dc: -+ ipu_di_exit(ipu, 1); -+err_di_1: -+ ipu_di_exit(ipu, 0); -+err_di_0: -+ ipu_image_convert_exit(ipu); -+err_image_convert: -+ ipu_vdi_exit(ipu); -+err_vdi: -+ ipu_ic_exit(ipu); -+err_ic: -+ ipu_csi_exit(ipu, 1); -+err_csi_1: -+ ipu_csi_exit(ipu, 0); -+err_csi_0: -+ ipu_cpmem_exit(ipu); -+err_cpmem: -+ dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret); -+ return ret; -+} -+ -+static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs) -+{ -+ unsigned long status; -+ int i, bit, irq; -+ -+ for (i = 0; i < num_regs; i++) { -+ -+ status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i])); -+ status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i])); -+ -+ for_each_set_bit(bit, &status, 32) { -+ irq = irq_linear_revmap(ipu->domain, -+ regs[i] * 32 + bit); -+ if (irq) -+ generic_handle_irq(irq); -+ } -+ } -+} -+ -+static void ipu_irq_handler(struct irq_desc *desc) -+{ -+ struct ipu_soc *ipu = irq_desc_get_handler_data(desc); -+ struct irq_chip *chip = irq_desc_get_chip(desc); -+ static const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14}; -+ -+ chained_irq_enter(chip, desc); -+ -+ ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); -+ -+ chained_irq_exit(chip, desc); -+} -+ -+static void ipu_err_irq_handler(struct irq_desc *desc) -+{ -+ struct ipu_soc *ipu = irq_desc_get_handler_data(desc); -+ struct irq_chip *chip = irq_desc_get_chip(desc); -+ static const int int_reg[] = { 4, 5, 8, 9}; -+ -+ chained_irq_enter(chip, desc); -+ -+ ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); -+ -+ chained_irq_exit(chip, desc); -+} -+ -+int ipu_map_irq(struct ipu_soc *ipu, int irq) -+{ -+ int virq; -+ -+ virq = irq_linear_revmap(ipu->domain, irq); -+ if (!virq) -+ virq = irq_create_mapping(ipu->domain, irq); -+ -+ return virq; -+} -+EXPORT_SYMBOL_GPL(ipu_map_irq); -+ -+int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, -+ enum ipu_channel_irq irq_type) -+{ -+ return ipu_map_irq(ipu, irq_type + channel->num); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq); -+ -+static void ipu_submodules_exit(struct ipu_soc *ipu) -+{ -+ ipu_smfc_exit(ipu); -+ ipu_dp_exit(ipu); -+ ipu_dmfc_exit(ipu); -+ ipu_dc_exit(ipu); -+ ipu_di_exit(ipu, 1); -+ ipu_di_exit(ipu, 0); -+ ipu_image_convert_exit(ipu); -+ ipu_vdi_exit(ipu); -+ ipu_ic_exit(ipu); -+ ipu_csi_exit(ipu, 1); -+ ipu_csi_exit(ipu, 0); -+ ipu_cpmem_exit(ipu); -+} -+ -+static int platform_remove_devices_fn(struct device *dev, void *unused) -+{ -+ struct platform_device *pdev = to_platform_device(dev); -+ -+ platform_device_unregister(pdev); -+ -+ return 0; -+} -+ -+static void platform_device_unregister_children(struct platform_device *pdev) -+{ -+ device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn); -+} -+ -+struct ipu_platform_reg { -+ struct ipu_client_platformdata pdata; -+ const char *name; -+}; -+ -+/* These must be in the order of the corresponding device tree port nodes */ -+static struct ipu_platform_reg client_reg[] = { -+ { -+ .pdata = { -+ .csi = 0, -+ .dma[0] = IPUV3_CHANNEL_CSI0, -+ .dma[1] = -EINVAL, -+ }, -+ .name = "imx-ipuv3-csi", -+ }, { -+ .pdata = { -+ .csi = 1, -+ .dma[0] = IPUV3_CHANNEL_CSI1, -+ .dma[1] = -EINVAL, -+ }, -+ .name = "imx-ipuv3-csi", -+ }, { -+ .pdata = { -+ .di = 0, -+ .dc = 5, -+ .dp = IPU_DP_FLOW_SYNC_BG, -+ .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC, -+ .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC, -+ }, -+ .name = "imx-ipuv3-crtc", -+ }, { -+ .pdata = { -+ .di = 1, -+ .dc = 1, -+ .dp = -EINVAL, -+ .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC, -+ .dma[1] = -EINVAL, -+ }, -+ .name = "imx-ipuv3-crtc", -+ }, -+}; -+ -+static DEFINE_MUTEX(ipu_client_id_mutex); -+static int ipu_client_id; -+ -+static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) -+{ -+ struct device *dev = ipu->dev; -+ unsigned i; -+ int id, ret; -+ -+ mutex_lock(&ipu_client_id_mutex); -+ id = ipu_client_id; -+ ipu_client_id += ARRAY_SIZE(client_reg); -+ mutex_unlock(&ipu_client_id_mutex); -+ -+ for (i = 0; i < ARRAY_SIZE(client_reg); i++) { -+ struct ipu_platform_reg *reg = &client_reg[i]; -+ struct platform_device *pdev; -+ struct device_node *of_node; -+ -+ /* Associate subdevice with the corresponding port node */ -+ of_node = of_graph_get_port_by_id(dev->of_node, i); -+ if (!of_node) { -+ dev_info(dev, -+ "no port@%d node in %pOF, not using %s%d\n", -+ i, dev->of_node, -+ (i / 2) ? "DI" : "CSI", i % 2); -+ continue; -+ } -+ -+ pdev = platform_device_alloc(reg->name, id++); -+ if (!pdev) { -+ ret = -ENOMEM; -+ goto err_register; -+ } -+ -+ pdev->dev.parent = dev; -+ -+ reg->pdata.of_node = of_node; -+ ret = platform_device_add_data(pdev, ®->pdata, -+ sizeof(reg->pdata)); -+ if (!ret) -+ ret = platform_device_add(pdev); -+ if (ret) { -+ platform_device_put(pdev); -+ goto err_register; -+ } -+ } -+ -+ return 0; -+ -+err_register: -+ platform_device_unregister_children(to_platform_device(dev)); -+ -+ return ret; -+} -+ -+ -+static int ipu_irq_init(struct ipu_soc *ipu) -+{ -+ struct irq_chip_generic *gc; -+ struct irq_chip_type *ct; -+ unsigned long unused[IPU_NUM_IRQS / 32] = { -+ 0x400100d0, 0xffe000fd, -+ 0x400100d0, 0xffe000fd, -+ 0x400100d0, 0xffe000fd, -+ 0x4077ffff, 0xffe7e1fd, -+ 0x23fffffe, 0x8880fff0, -+ 0xf98fe7d0, 0xfff81fff, -+ 0x400100d0, 0xffe000fd, -+ 0x00000000, -+ }; -+ int ret, i; -+ -+ ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS, -+ &irq_generic_chip_ops, ipu); -+ if (!ipu->domain) { -+ dev_err(ipu->dev, "failed to add irq domain\n"); -+ return -ENODEV; -+ } -+ -+ ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU", -+ handle_level_irq, 0, 0, 0); -+ if (ret < 0) { -+ dev_err(ipu->dev, "failed to alloc generic irq chips\n"); -+ irq_domain_remove(ipu->domain); -+ return ret; -+ } -+ -+ /* Mask and clear all interrupts */ -+ for (i = 0; i < IPU_NUM_IRQS; i += 32) { -+ ipu_cm_write(ipu, 0, IPU_INT_CTRL(i / 32)); -+ ipu_cm_write(ipu, ~unused[i / 32], IPU_INT_STAT(i / 32)); -+ } -+ -+ for (i = 0; i < IPU_NUM_IRQS; i += 32) { -+ gc = irq_get_domain_generic_chip(ipu->domain, i); -+ gc->reg_base = ipu->cm_reg; -+ gc->unused = unused[i / 32]; -+ ct = gc->chip_types; -+ ct->chip.irq_ack = irq_gc_ack_set_bit; -+ ct->chip.irq_mask = irq_gc_mask_clr_bit; -+ ct->chip.irq_unmask = irq_gc_mask_set_bit; -+ ct->regs.ack = IPU_INT_STAT(i / 32); -+ ct->regs.mask = IPU_INT_CTRL(i / 32); -+ } -+ -+ irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu); -+ irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler, -+ ipu); -+ -+ return 0; -+} -+ -+static void ipu_irq_exit(struct ipu_soc *ipu) -+{ -+ int i, irq; -+ -+ irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL); -+ irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL); -+ -+ /* TODO: remove irq_domain_generic_chips */ -+ -+ for (i = 0; i < IPU_NUM_IRQS; i++) { -+ irq = irq_linear_revmap(ipu->domain, i); -+ if (irq) -+ irq_dispose_mapping(irq); -+ } -+ -+ irq_domain_remove(ipu->domain); -+} -+ -+void ipu_dump(struct ipu_soc *ipu) -+{ -+ int i; -+ -+ dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_CONF)); -+ dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_CONF)); -+ dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_CHA_EN(0))); -+ dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_CHA_EN(32))); -+ dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_CHA_PRI(0))); -+ dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_CHA_PRI(32))); -+ dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_BAND_EN(0))); -+ dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_BAND_EN(32))); -+ dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0))); -+ dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32))); -+ dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_FS_PROC_FLOW1)); -+ dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_FS_PROC_FLOW2)); -+ dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_FS_PROC_FLOW3)); -+ dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_FS_DISP_FLOW1)); -+ for (i = 0; i < 15; i++) -+ dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i, -+ ipu_cm_read(ipu, IPU_INT_CTRL(i))); -+} -+EXPORT_SYMBOL_GPL(ipu_dump); -+ -+static int ipu_probe(struct platform_device *pdev) -+{ -+ struct device_node *np = pdev->dev.of_node; -+ struct ipu_soc *ipu; -+ struct resource *res; -+ unsigned long ipu_base; -+ int ret, irq_sync, irq_err; -+ const struct ipu_devtype *devtype; -+ -+ devtype = of_device_get_match_data(&pdev->dev); -+ if (!devtype) -+ return -EINVAL; -+ -+ irq_sync = platform_get_irq(pdev, 0); -+ irq_err = platform_get_irq(pdev, 1); -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ -+ dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n", -+ irq_sync, irq_err); -+ -+ if (!res || irq_sync < 0 || irq_err < 0) -+ return -ENODEV; -+ -+ ipu_base = res->start; -+ -+ ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL); -+ if (!ipu) -+ return -ENODEV; -+ -+ ipu->id = of_alias_get_id(np, "ipu"); -+ if (ipu->id < 0) -+ ipu->id = 0; -+ -+ if (of_device_is_compatible(np, "fsl,imx6qp-ipu") && -+ IS_ENABLED(CONFIG_DRM)) { -+ ipu->prg_priv = ipu_prg_lookup_by_phandle(&pdev->dev, -+ "fsl,prg", ipu->id); -+ if (!ipu->prg_priv) -+ return -EPROBE_DEFER; -+ } -+ -+ ipu->devtype = devtype; -+ ipu->ipu_type = devtype->type; -+ -+ spin_lock_init(&ipu->lock); -+ mutex_init(&ipu->channel_lock); -+ INIT_LIST_HEAD(&ipu->channels); -+ -+ dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n", -+ ipu_base + devtype->cm_ofs); -+ dev_dbg(&pdev->dev, "idmac: 0x%08lx\n", -+ ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS); -+ dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n", -+ ipu_base + devtype->cpmem_ofs); -+ dev_dbg(&pdev->dev, "csi0: 0x%08lx\n", -+ ipu_base + devtype->csi0_ofs); -+ dev_dbg(&pdev->dev, "csi1: 0x%08lx\n", -+ ipu_base + devtype->csi1_ofs); -+ dev_dbg(&pdev->dev, "ic: 0x%08lx\n", -+ ipu_base + devtype->ic_ofs); -+ dev_dbg(&pdev->dev, "disp0: 0x%08lx\n", -+ ipu_base + devtype->disp0_ofs); -+ dev_dbg(&pdev->dev, "disp1: 0x%08lx\n", -+ ipu_base + devtype->disp1_ofs); -+ dev_dbg(&pdev->dev, "srm: 0x%08lx\n", -+ ipu_base + devtype->srm_ofs); -+ dev_dbg(&pdev->dev, "tpm: 0x%08lx\n", -+ ipu_base + devtype->tpm_ofs); -+ dev_dbg(&pdev->dev, "dc: 0x%08lx\n", -+ ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS); -+ dev_dbg(&pdev->dev, "ic: 0x%08lx\n", -+ ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS); -+ dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n", -+ ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS); -+ dev_dbg(&pdev->dev, "vdi: 0x%08lx\n", -+ ipu_base + devtype->vdi_ofs); -+ -+ ipu->cm_reg = devm_ioremap(&pdev->dev, -+ ipu_base + devtype->cm_ofs, PAGE_SIZE); -+ ipu->idmac_reg = devm_ioremap(&pdev->dev, -+ ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS, -+ PAGE_SIZE); -+ -+ if (!ipu->cm_reg || !ipu->idmac_reg) -+ return -ENOMEM; -+ -+ ipu->clk = devm_clk_get(&pdev->dev, "bus"); -+ if (IS_ERR(ipu->clk)) { -+ ret = PTR_ERR(ipu->clk); -+ dev_err(&pdev->dev, "clk_get failed with %d", ret); -+ return ret; -+ } -+ -+ platform_set_drvdata(pdev, ipu); -+ -+ ret = clk_prepare_enable(ipu->clk); -+ if (ret) { -+ dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); -+ return ret; -+ } -+ -+ ipu->dev = &pdev->dev; -+ ipu->irq_sync = irq_sync; -+ ipu->irq_err = irq_err; -+ -+ ret = device_reset(&pdev->dev); -+ if (ret) { -+ dev_err(&pdev->dev, "failed to reset: %d\n", ret); -+ goto out_failed_reset; -+ } -+ ret = ipu_memory_reset(ipu); -+ if (ret) -+ goto out_failed_reset; -+ -+ ret = ipu_irq_init(ipu); -+ if (ret) -+ goto out_failed_irq; -+ -+ /* Set MCU_T to divide MCU access window into 2 */ -+ ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), -+ IPU_DISP_GEN); -+ -+ ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk); -+ if (ret) -+ goto failed_submodules_init; -+ -+ ret = ipu_add_client_devices(ipu, ipu_base); -+ if (ret) { -+ dev_err(&pdev->dev, "adding client devices failed with %d\n", -+ ret); -+ goto failed_add_clients; -+ } -+ -+ dev_info(&pdev->dev, "%s probed\n", devtype->name); -+ -+ return 0; -+ -+failed_add_clients: -+ ipu_submodules_exit(ipu); -+failed_submodules_init: -+ ipu_irq_exit(ipu); -+out_failed_irq: -+out_failed_reset: -+ clk_disable_unprepare(ipu->clk); -+ return ret; -+} -+ -+static int ipu_remove(struct platform_device *pdev) -+{ -+ struct ipu_soc *ipu = platform_get_drvdata(pdev); -+ -+ platform_device_unregister_children(pdev); -+ ipu_submodules_exit(ipu); -+ ipu_irq_exit(ipu); -+ -+ clk_disable_unprepare(ipu->clk); -+ -+ return 0; -+} -+ -+static struct platform_driver imx_ipu_driver = { -+ .driver = { -+ .name = "imx-ipuv3", -+ .of_match_table = imx_ipu_dt_ids, -+ }, -+ .probe = ipu_probe, -+ .remove = ipu_remove, -+}; -+ -+static struct platform_driver * const drivers[] = { -+#if IS_ENABLED(CONFIG_DRM) -+ &ipu_pre_drv, -+ &ipu_prg_drv, -+#endif -+ &imx_ipu_driver, -+}; -+ -+static int __init imx_ipu_init(void) -+{ -+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); -+} -+module_init(imx_ipu_init); -+ -+static void __exit imx_ipu_exit(void) -+{ -+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); -+} -+module_exit(imx_ipu_exit); -+ -+MODULE_ALIAS("platform:imx-ipuv3"); -+MODULE_DESCRIPTION("i.MX IPU v3 driver"); -+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); -+MODULE_LICENSE("GPL"); ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-cpmem.c -@@ -0,0 +1,976 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (C) 2012 Mentor Graphics Inc. -+ * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. -+ */ -+#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" -+ -+struct ipu_cpmem_word { -+ u32 data[5]; -+ u32 res[3]; -+}; -+ -+struct ipu_ch_param { -+ struct ipu_cpmem_word word[2]; -+}; -+ -+struct ipu_cpmem { -+ struct ipu_ch_param __iomem *base; -+ u32 module; -+ spinlock_t lock; -+ int use_count; -+ struct ipu_soc *ipu; -+}; -+ -+#define IPU_CPMEM_WORD(word, ofs, size) ((((word) * 160 + (ofs)) << 8) | (size)) -+ -+#define IPU_FIELD_UBO IPU_CPMEM_WORD(0, 46, 22) -+#define IPU_FIELD_VBO IPU_CPMEM_WORD(0, 68, 22) -+#define IPU_FIELD_IOX IPU_CPMEM_WORD(0, 90, 4) -+#define IPU_FIELD_RDRW IPU_CPMEM_WORD(0, 94, 1) -+#define IPU_FIELD_SO IPU_CPMEM_WORD(0, 113, 1) -+#define IPU_FIELD_SLY IPU_CPMEM_WORD(1, 102, 14) -+#define IPU_FIELD_SLUV IPU_CPMEM_WORD(1, 128, 14) -+ -+#define IPU_FIELD_XV IPU_CPMEM_WORD(0, 0, 10) -+#define IPU_FIELD_YV IPU_CPMEM_WORD(0, 10, 9) -+#define IPU_FIELD_XB IPU_CPMEM_WORD(0, 19, 13) -+#define IPU_FIELD_YB IPU_CPMEM_WORD(0, 32, 12) -+#define IPU_FIELD_NSB_B IPU_CPMEM_WORD(0, 44, 1) -+#define IPU_FIELD_CF IPU_CPMEM_WORD(0, 45, 1) -+#define IPU_FIELD_SX IPU_CPMEM_WORD(0, 46, 12) -+#define IPU_FIELD_SY IPU_CPMEM_WORD(0, 58, 11) -+#define IPU_FIELD_NS IPU_CPMEM_WORD(0, 69, 10) -+#define IPU_FIELD_SDX IPU_CPMEM_WORD(0, 79, 7) -+#define IPU_FIELD_SM IPU_CPMEM_WORD(0, 86, 10) -+#define IPU_FIELD_SCC IPU_CPMEM_WORD(0, 96, 1) -+#define IPU_FIELD_SCE IPU_CPMEM_WORD(0, 97, 1) -+#define IPU_FIELD_SDY IPU_CPMEM_WORD(0, 98, 7) -+#define IPU_FIELD_SDRX IPU_CPMEM_WORD(0, 105, 1) -+#define IPU_FIELD_SDRY IPU_CPMEM_WORD(0, 106, 1) -+#define IPU_FIELD_BPP IPU_CPMEM_WORD(0, 107, 3) -+#define IPU_FIELD_DEC_SEL IPU_CPMEM_WORD(0, 110, 2) -+#define IPU_FIELD_DIM IPU_CPMEM_WORD(0, 112, 1) -+#define IPU_FIELD_BNDM IPU_CPMEM_WORD(0, 114, 3) -+#define IPU_FIELD_BM IPU_CPMEM_WORD(0, 117, 2) -+#define IPU_FIELD_ROT IPU_CPMEM_WORD(0, 119, 1) -+#define IPU_FIELD_ROT_HF_VF IPU_CPMEM_WORD(0, 119, 3) -+#define IPU_FIELD_HF IPU_CPMEM_WORD(0, 120, 1) -+#define IPU_FIELD_VF IPU_CPMEM_WORD(0, 121, 1) -+#define IPU_FIELD_THE IPU_CPMEM_WORD(0, 122, 1) -+#define IPU_FIELD_CAP IPU_CPMEM_WORD(0, 123, 1) -+#define IPU_FIELD_CAE IPU_CPMEM_WORD(0, 124, 1) -+#define IPU_FIELD_FW IPU_CPMEM_WORD(0, 125, 13) -+#define IPU_FIELD_FH IPU_CPMEM_WORD(0, 138, 12) -+#define IPU_FIELD_EBA0 IPU_CPMEM_WORD(1, 0, 29) -+#define IPU_FIELD_EBA1 IPU_CPMEM_WORD(1, 29, 29) -+#define IPU_FIELD_ILO IPU_CPMEM_WORD(1, 58, 20) -+#define IPU_FIELD_NPB IPU_CPMEM_WORD(1, 78, 7) -+#define IPU_FIELD_PFS IPU_CPMEM_WORD(1, 85, 4) -+#define IPU_FIELD_ALU IPU_CPMEM_WORD(1, 89, 1) -+#define IPU_FIELD_ALBM IPU_CPMEM_WORD(1, 90, 3) -+#define IPU_FIELD_ID IPU_CPMEM_WORD(1, 93, 2) -+#define IPU_FIELD_TH IPU_CPMEM_WORD(1, 95, 7) -+#define IPU_FIELD_SL IPU_CPMEM_WORD(1, 102, 14) -+#define IPU_FIELD_WID0 IPU_CPMEM_WORD(1, 116, 3) -+#define IPU_FIELD_WID1 IPU_CPMEM_WORD(1, 119, 3) -+#define IPU_FIELD_WID2 IPU_CPMEM_WORD(1, 122, 3) -+#define IPU_FIELD_WID3 IPU_CPMEM_WORD(1, 125, 3) -+#define IPU_FIELD_OFS0 IPU_CPMEM_WORD(1, 128, 5) -+#define IPU_FIELD_OFS1 IPU_CPMEM_WORD(1, 133, 5) -+#define IPU_FIELD_OFS2 IPU_CPMEM_WORD(1, 138, 5) -+#define IPU_FIELD_OFS3 IPU_CPMEM_WORD(1, 143, 5) -+#define IPU_FIELD_SXYS IPU_CPMEM_WORD(1, 148, 1) -+#define IPU_FIELD_CRE IPU_CPMEM_WORD(1, 149, 1) -+#define IPU_FIELD_DEC_SEL2 IPU_CPMEM_WORD(1, 150, 1) -+ -+static inline struct ipu_ch_param __iomem * -+ipu_get_cpmem(struct ipuv3_channel *ch) -+{ -+ struct ipu_cpmem *cpmem = ch->ipu->cpmem_priv; -+ -+ return cpmem->base + ch->num; -+} -+ -+static void ipu_ch_param_write_field(struct ipuv3_channel *ch, u32 wbs, u32 v) -+{ -+ struct ipu_ch_param __iomem *base = ipu_get_cpmem(ch); -+ u32 bit = (wbs >> 8) % 160; -+ u32 size = wbs & 0xff; -+ u32 word = (wbs >> 8) / 160; -+ u32 i = bit / 32; -+ u32 ofs = bit % 32; -+ u32 mask = (1 << size) - 1; -+ u32 val; -+ -+ pr_debug("%s %d %d %d\n", __func__, word, bit , size); -+ -+ val = readl(&base->word[word].data[i]); -+ val &= ~(mask << ofs); -+ val |= v << ofs; -+ writel(val, &base->word[word].data[i]); -+ -+ if ((bit + size - 1) / 32 > i) { -+ val = readl(&base->word[word].data[i + 1]); -+ val &= ~(mask >> (ofs ? (32 - ofs) : 0)); -+ val |= v >> (ofs ? (32 - ofs) : 0); -+ writel(val, &base->word[word].data[i + 1]); -+ } -+} -+ -+static u32 ipu_ch_param_read_field(struct ipuv3_channel *ch, u32 wbs) -+{ -+ struct ipu_ch_param __iomem *base = ipu_get_cpmem(ch); -+ u32 bit = (wbs >> 8) % 160; -+ u32 size = wbs & 0xff; -+ u32 word = (wbs >> 8) / 160; -+ u32 i = bit / 32; -+ u32 ofs = bit % 32; -+ u32 mask = (1 << size) - 1; -+ u32 val = 0; -+ -+ pr_debug("%s %d %d %d\n", __func__, word, bit , size); -+ -+ val = (readl(&base->word[word].data[i]) >> ofs) & mask; -+ -+ if ((bit + size - 1) / 32 > i) { -+ u32 tmp; -+ -+ tmp = readl(&base->word[word].data[i + 1]); -+ tmp &= mask >> (ofs ? (32 - ofs) : 0); -+ val |= tmp << (ofs ? (32 - ofs) : 0); -+ } -+ -+ return val; -+} -+ -+/* -+ * The V4L2 spec defines packed RGB formats in memory byte order, which from -+ * point of view of the IPU corresponds to little-endian words with the first -+ * component in the least significant bits. -+ * The DRM pixel formats and IPU internal representation are ordered the other -+ * way around, with the first named component ordered at the most significant -+ * bits. Further, V4L2 formats are not well defined: -+ * https://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html -+ * We choose the interpretation which matches GStreamer behavior. -+ */ -+static int v4l2_pix_fmt_to_drm_fourcc(u32 pixelformat) -+{ -+ switch (pixelformat) { -+ case V4L2_PIX_FMT_RGB565: -+ /* -+ * Here we choose the 'corrected' interpretation of RGBP, a -+ * little-endian 16-bit word with the red component at the most -+ * significant bits: -+ * g[2:0]b[4:0] r[4:0]g[5:3] <=> [16:0] R:G:B -+ */ -+ return DRM_FORMAT_RGB565; -+ case V4L2_PIX_FMT_BGR24: -+ /* B G R <=> [24:0] R:G:B */ -+ return DRM_FORMAT_RGB888; -+ case V4L2_PIX_FMT_RGB24: -+ /* R G B <=> [24:0] B:G:R */ -+ return DRM_FORMAT_BGR888; -+ case V4L2_PIX_FMT_BGR32: -+ /* B G R A <=> [32:0] A:B:G:R */ -+ return DRM_FORMAT_XRGB8888; -+ case V4L2_PIX_FMT_RGB32: -+ /* R G B A <=> [32:0] A:B:G:R */ -+ return DRM_FORMAT_XBGR8888; -+ case V4L2_PIX_FMT_ABGR32: -+ /* B G R A <=> [32:0] A:R:G:B */ -+ return DRM_FORMAT_ARGB8888; -+ case V4L2_PIX_FMT_XBGR32: -+ /* B G R X <=> [32:0] X:R:G:B */ -+ return DRM_FORMAT_XRGB8888; -+ case V4L2_PIX_FMT_BGRA32: -+ /* A B G R <=> [32:0] R:G:B:A */ -+ return DRM_FORMAT_RGBA8888; -+ case V4L2_PIX_FMT_BGRX32: -+ /* X B G R <=> [32:0] R:G:B:X */ -+ return DRM_FORMAT_RGBX8888; -+ case V4L2_PIX_FMT_RGBA32: -+ /* R G B A <=> [32:0] A:B:G:R */ -+ return DRM_FORMAT_ABGR8888; -+ case V4L2_PIX_FMT_RGBX32: -+ /* R G B X <=> [32:0] X:B:G:R */ -+ return DRM_FORMAT_XBGR8888; -+ case V4L2_PIX_FMT_ARGB32: -+ /* A R G B <=> [32:0] B:G:R:A */ -+ return DRM_FORMAT_BGRA8888; -+ case V4L2_PIX_FMT_XRGB32: -+ /* X R G B <=> [32:0] B:G:R:X */ -+ return DRM_FORMAT_BGRX8888; -+ case V4L2_PIX_FMT_UYVY: -+ return DRM_FORMAT_UYVY; -+ case V4L2_PIX_FMT_YUYV: -+ return DRM_FORMAT_YUYV; -+ case V4L2_PIX_FMT_YUV420: -+ return DRM_FORMAT_YUV420; -+ case V4L2_PIX_FMT_YUV422P: -+ return DRM_FORMAT_YUV422; -+ case V4L2_PIX_FMT_YVU420: -+ return DRM_FORMAT_YVU420; -+ case V4L2_PIX_FMT_NV12: -+ return DRM_FORMAT_NV12; -+ case V4L2_PIX_FMT_NV16: -+ return DRM_FORMAT_NV16; -+ } -+ -+ return -EINVAL; -+} -+ -+void ipu_cpmem_zero(struct ipuv3_channel *ch) -+{ -+ struct ipu_ch_param __iomem *p = ipu_get_cpmem(ch); -+ void __iomem *base = p; -+ int i; -+ -+ for (i = 0; i < sizeof(*p) / sizeof(u32); i++) -+ writel(0, base + i * sizeof(u32)); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_zero); -+ -+void ipu_cpmem_set_resolution(struct ipuv3_channel *ch, int xres, int yres) -+{ -+ ipu_ch_param_write_field(ch, IPU_FIELD_FW, xres - 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_FH, yres - 1); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_resolution); -+ -+void ipu_cpmem_skip_odd_chroma_rows(struct ipuv3_channel *ch) -+{ -+ ipu_ch_param_write_field(ch, IPU_FIELD_RDRW, 1); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_skip_odd_chroma_rows); -+ -+void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride) -+{ -+ ipu_ch_param_write_field(ch, IPU_FIELD_SLY, stride - 1); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_stride); -+ -+void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch) -+{ -+ struct ipu_soc *ipu = ch->ipu; -+ u32 val; -+ -+ if (ipu->ipu_type == IPUV3EX) -+ ipu_ch_param_write_field(ch, IPU_FIELD_ID, 1); -+ -+ val = ipu_idmac_read(ipu, IDMAC_CHA_PRI(ch->num)); -+ val |= 1 << (ch->num % 32); -+ ipu_idmac_write(ipu, val, IDMAC_CHA_PRI(ch->num)); -+}; -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_high_priority); -+ -+void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf) -+{ -+ WARN_ON_ONCE(buf & 0x7); -+ -+ if (bufnum) -+ ipu_ch_param_write_field(ch, IPU_FIELD_EBA1, buf >> 3); -+ else -+ ipu_ch_param_write_field(ch, IPU_FIELD_EBA0, buf >> 3); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer); -+ -+void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) -+{ -+ WARN_ON_ONCE((u_off & 0x7) || (v_off & 0x7)); -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_off / 8); -+ ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_off / 8); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -+ -+void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, -+ u32 pixelformat) -+{ -+ u32 ilo, sly, sluv; -+ -+ if (stride < 0) { -+ stride = -stride; -+ ilo = 0x100000 - (stride / 8); -+ } else { -+ ilo = stride / 8; -+ } -+ -+ sly = (stride * 2) - 1; -+ -+ switch (pixelformat) { -+ case V4L2_PIX_FMT_YUV420: -+ case V4L2_PIX_FMT_YVU420: -+ sluv = stride / 2 - 1; -+ break; -+ case V4L2_PIX_FMT_NV12: -+ sluv = stride - 1; -+ break; -+ case V4L2_PIX_FMT_YUV422P: -+ sluv = stride - 1; -+ break; -+ case V4L2_PIX_FMT_NV16: -+ sluv = stride * 2 - 1; -+ break; -+ default: -+ sluv = 0; -+ break; -+ } -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); -+ ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); -+ if (sluv) -+ ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); -+}; -+EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); -+ -+void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id) -+{ -+ id &= 0x3; -+ ipu_ch_param_write_field(ch, IPU_FIELD_ID, id); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_axi_id); -+ -+int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch) -+{ -+ return ipu_ch_param_read_field(ch, IPU_FIELD_NPB) + 1; -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_get_burstsize); -+ -+void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize) -+{ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, burstsize - 1); -+}; -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_burstsize); -+ -+void ipu_cpmem_set_block_mode(struct ipuv3_channel *ch) -+{ -+ ipu_ch_param_write_field(ch, IPU_FIELD_BM, 1); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_block_mode); -+ -+void ipu_cpmem_set_rotation(struct ipuv3_channel *ch, -+ enum ipu_rotate_mode rot) -+{ -+ u32 temp_rot = bitrev8(rot) >> 5; -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_ROT_HF_VF, temp_rot); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_rotation); -+ -+int ipu_cpmem_set_format_rgb(struct ipuv3_channel *ch, -+ const struct ipu_rgb *rgb) -+{ -+ int bpp = 0, npb = 0, ro, go, bo, to; -+ -+ ro = rgb->bits_per_pixel - rgb->red.length - rgb->red.offset; -+ go = rgb->bits_per_pixel - rgb->green.length - rgb->green.offset; -+ bo = rgb->bits_per_pixel - rgb->blue.length - rgb->blue.offset; -+ to = rgb->bits_per_pixel - rgb->transp.length - rgb->transp.offset; -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_WID0, rgb->red.length - 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS0, ro); -+ ipu_ch_param_write_field(ch, IPU_FIELD_WID1, rgb->green.length - 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS1, go); -+ ipu_ch_param_write_field(ch, IPU_FIELD_WID2, rgb->blue.length - 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS2, bo); -+ -+ if (rgb->transp.length) { -+ ipu_ch_param_write_field(ch, IPU_FIELD_WID3, -+ rgb->transp.length - 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS3, to); -+ } else { -+ ipu_ch_param_write_field(ch, IPU_FIELD_WID3, 7); -+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS3, -+ rgb->bits_per_pixel); -+ } -+ -+ switch (rgb->bits_per_pixel) { -+ case 32: -+ bpp = 0; -+ npb = 15; -+ break; -+ case 24: -+ bpp = 1; -+ npb = 19; -+ break; -+ case 16: -+ bpp = 3; -+ npb = 31; -+ break; -+ case 8: -+ bpp = 5; -+ npb = 63; -+ break; -+ default: -+ return -EINVAL; -+ } -+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, bpp); -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, npb); -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 7); /* rgb mode */ -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_rgb); -+ -+int ipu_cpmem_set_format_passthrough(struct ipuv3_channel *ch, int width) -+{ -+ int bpp = 0, npb = 0; -+ -+ switch (width) { -+ case 32: -+ bpp = 0; -+ npb = 15; -+ break; -+ case 24: -+ bpp = 1; -+ npb = 19; -+ break; -+ case 16: -+ bpp = 3; -+ npb = 31; -+ break; -+ case 8: -+ bpp = 5; -+ npb = 63; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, bpp); -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, npb); -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 6); /* raw mode */ -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_passthrough); -+ -+void ipu_cpmem_set_yuv_interleaved(struct ipuv3_channel *ch, u32 pixel_format) -+{ -+ switch (pixel_format) { -+ case V4L2_PIX_FMT_UYVY: -+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0xA);/* pix fmt */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */ -+ break; -+ case V4L2_PIX_FMT_YUYV: -+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0x8);/* pix fmt */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */ -+ break; -+ } -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_interleaved); -+ -+void ipu_cpmem_set_yuv_planar_full(struct ipuv3_channel *ch, -+ unsigned int uv_stride, -+ unsigned int u_offset, unsigned int v_offset) -+{ -+ WARN_ON_ONCE((u_offset & 0x7) || (v_offset & 0x7)); -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, uv_stride - 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_offset / 8); -+ ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_offset / 8); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar_full); -+ -+static const struct ipu_rgb def_xrgb_32 = { -+ .red = { .offset = 16, .length = 8, }, -+ .green = { .offset = 8, .length = 8, }, -+ .blue = { .offset = 0, .length = 8, }, -+ .transp = { .offset = 24, .length = 8, }, -+ .bits_per_pixel = 32, -+}; -+ -+static const struct ipu_rgb def_xbgr_32 = { -+ .red = { .offset = 0, .length = 8, }, -+ .green = { .offset = 8, .length = 8, }, -+ .blue = { .offset = 16, .length = 8, }, -+ .transp = { .offset = 24, .length = 8, }, -+ .bits_per_pixel = 32, -+}; -+ -+static const struct ipu_rgb def_rgbx_32 = { -+ .red = { .offset = 24, .length = 8, }, -+ .green = { .offset = 16, .length = 8, }, -+ .blue = { .offset = 8, .length = 8, }, -+ .transp = { .offset = 0, .length = 8, }, -+ .bits_per_pixel = 32, -+}; -+ -+static const struct ipu_rgb def_bgrx_32 = { -+ .red = { .offset = 8, .length = 8, }, -+ .green = { .offset = 16, .length = 8, }, -+ .blue = { .offset = 24, .length = 8, }, -+ .transp = { .offset = 0, .length = 8, }, -+ .bits_per_pixel = 32, -+}; -+ -+static const struct ipu_rgb def_rgb_24 = { -+ .red = { .offset = 16, .length = 8, }, -+ .green = { .offset = 8, .length = 8, }, -+ .blue = { .offset = 0, .length = 8, }, -+ .transp = { .offset = 0, .length = 0, }, -+ .bits_per_pixel = 24, -+}; -+ -+static const struct ipu_rgb def_bgr_24 = { -+ .red = { .offset = 0, .length = 8, }, -+ .green = { .offset = 8, .length = 8, }, -+ .blue = { .offset = 16, .length = 8, }, -+ .transp = { .offset = 0, .length = 0, }, -+ .bits_per_pixel = 24, -+}; -+ -+static const struct ipu_rgb def_rgb_16 = { -+ .red = { .offset = 11, .length = 5, }, -+ .green = { .offset = 5, .length = 6, }, -+ .blue = { .offset = 0, .length = 5, }, -+ .transp = { .offset = 0, .length = 0, }, -+ .bits_per_pixel = 16, -+}; -+ -+static const struct ipu_rgb def_bgr_16 = { -+ .red = { .offset = 0, .length = 5, }, -+ .green = { .offset = 5, .length = 6, }, -+ .blue = { .offset = 11, .length = 5, }, -+ .transp = { .offset = 0, .length = 0, }, -+ .bits_per_pixel = 16, -+}; -+ -+static const struct ipu_rgb def_argb_16 = { -+ .red = { .offset = 10, .length = 5, }, -+ .green = { .offset = 5, .length = 5, }, -+ .blue = { .offset = 0, .length = 5, }, -+ .transp = { .offset = 15, .length = 1, }, -+ .bits_per_pixel = 16, -+}; -+ -+static const struct ipu_rgb def_argb_16_4444 = { -+ .red = { .offset = 8, .length = 4, }, -+ .green = { .offset = 4, .length = 4, }, -+ .blue = { .offset = 0, .length = 4, }, -+ .transp = { .offset = 12, .length = 4, }, -+ .bits_per_pixel = 16, -+}; -+ -+static const struct ipu_rgb def_abgr_16 = { -+ .red = { .offset = 0, .length = 5, }, -+ .green = { .offset = 5, .length = 5, }, -+ .blue = { .offset = 10, .length = 5, }, -+ .transp = { .offset = 15, .length = 1, }, -+ .bits_per_pixel = 16, -+}; -+ -+static const struct ipu_rgb def_rgba_16 = { -+ .red = { .offset = 11, .length = 5, }, -+ .green = { .offset = 6, .length = 5, }, -+ .blue = { .offset = 1, .length = 5, }, -+ .transp = { .offset = 0, .length = 1, }, -+ .bits_per_pixel = 16, -+}; -+ -+static const struct ipu_rgb def_bgra_16 = { -+ .red = { .offset = 1, .length = 5, }, -+ .green = { .offset = 6, .length = 5, }, -+ .blue = { .offset = 11, .length = 5, }, -+ .transp = { .offset = 0, .length = 1, }, -+ .bits_per_pixel = 16, -+}; -+ -+#define Y_OFFSET(pix, x, y) ((x) + pix->width * (y)) -+#define U_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -+ (pix->width * ((y) / 2) / 2) + (x) / 2) -+#define V_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -+ (pix->width * pix->height / 4) + \ -+ (pix->width * ((y) / 2) / 2) + (x) / 2) -+#define U2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -+ (pix->width * (y) / 2) + (x) / 2) -+#define V2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -+ (pix->width * pix->height / 2) + \ -+ (pix->width * (y) / 2) + (x) / 2) -+#define UV_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -+ (pix->width * ((y) / 2)) + (x)) -+#define UV2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -+ (pix->width * y) + (x)) -+ -+#define NUM_ALPHA_CHANNELS 7 -+ -+/* See Table 37-12. Alpha channels mapping. */ -+static int ipu_channel_albm(int ch_num) -+{ -+ switch (ch_num) { -+ case IPUV3_CHANNEL_G_MEM_IC_PRP_VF: return 0; -+ case IPUV3_CHANNEL_G_MEM_IC_PP: return 1; -+ case IPUV3_CHANNEL_MEM_FG_SYNC: return 2; -+ case IPUV3_CHANNEL_MEM_FG_ASYNC: return 3; -+ case IPUV3_CHANNEL_MEM_BG_SYNC: return 4; -+ case IPUV3_CHANNEL_MEM_BG_ASYNC: return 5; -+ case IPUV3_CHANNEL_MEM_VDI_PLANE1_COMB: return 6; -+ default: -+ return -EINVAL; -+ } -+} -+ -+static void ipu_cpmem_set_separate_alpha(struct ipuv3_channel *ch) -+{ -+ struct ipu_soc *ipu = ch->ipu; -+ int albm; -+ u32 val; -+ -+ albm = ipu_channel_albm(ch->num); -+ if (albm < 0) -+ return; -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_ALU, 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_ALBM, albm); -+ ipu_ch_param_write_field(ch, IPU_FIELD_CRE, 1); -+ -+ val = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA); -+ val |= BIT(ch->num); -+ ipu_idmac_write(ipu, val, IDMAC_SEP_ALPHA); -+} -+ -+int ipu_cpmem_set_fmt(struct ipuv3_channel *ch, u32 drm_fourcc) -+{ -+ switch (drm_fourcc) { -+ case DRM_FORMAT_YUV420: -+ case DRM_FORMAT_YVU420: -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 2); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_YUV422: -+ case DRM_FORMAT_YVU422: -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 1); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_YUV444: -+ case DRM_FORMAT_YVU444: -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_NV12: -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 4); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_NV16: -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 3); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_UYVY: -+ /* bits/pixel */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0xA); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_YUYV: -+ /* bits/pixel */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0x8); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_ABGR8888: -+ case DRM_FORMAT_XBGR8888: -+ ipu_cpmem_set_format_rgb(ch, &def_xbgr_32); -+ break; -+ case DRM_FORMAT_ARGB8888: -+ case DRM_FORMAT_XRGB8888: -+ ipu_cpmem_set_format_rgb(ch, &def_xrgb_32); -+ break; -+ case DRM_FORMAT_RGBA8888: -+ case DRM_FORMAT_RGBX8888: -+ case DRM_FORMAT_RGBX8888_A8: -+ ipu_cpmem_set_format_rgb(ch, &def_rgbx_32); -+ break; -+ case DRM_FORMAT_BGRA8888: -+ case DRM_FORMAT_BGRX8888: -+ case DRM_FORMAT_BGRX8888_A8: -+ ipu_cpmem_set_format_rgb(ch, &def_bgrx_32); -+ break; -+ case DRM_FORMAT_BGR888: -+ case DRM_FORMAT_BGR888_A8: -+ ipu_cpmem_set_format_rgb(ch, &def_bgr_24); -+ break; -+ case DRM_FORMAT_RGB888: -+ case DRM_FORMAT_RGB888_A8: -+ ipu_cpmem_set_format_rgb(ch, &def_rgb_24); -+ break; -+ case DRM_FORMAT_RGB565: -+ case DRM_FORMAT_RGB565_A8: -+ ipu_cpmem_set_format_rgb(ch, &def_rgb_16); -+ break; -+ case DRM_FORMAT_BGR565: -+ case DRM_FORMAT_BGR565_A8: -+ ipu_cpmem_set_format_rgb(ch, &def_bgr_16); -+ break; -+ case DRM_FORMAT_ARGB1555: -+ ipu_cpmem_set_format_rgb(ch, &def_argb_16); -+ break; -+ case DRM_FORMAT_ABGR1555: -+ ipu_cpmem_set_format_rgb(ch, &def_abgr_16); -+ break; -+ case DRM_FORMAT_RGBA5551: -+ ipu_cpmem_set_format_rgb(ch, &def_rgba_16); -+ break; -+ case DRM_FORMAT_BGRA5551: -+ ipu_cpmem_set_format_rgb(ch, &def_bgra_16); -+ break; -+ case DRM_FORMAT_ARGB4444: -+ ipu_cpmem_set_format_rgb(ch, &def_argb_16_4444); -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ switch (drm_fourcc) { -+ case DRM_FORMAT_RGB565_A8: -+ case DRM_FORMAT_BGR565_A8: -+ case DRM_FORMAT_RGB888_A8: -+ case DRM_FORMAT_BGR888_A8: -+ case DRM_FORMAT_RGBX8888_A8: -+ case DRM_FORMAT_BGRX8888_A8: -+ ipu_ch_param_write_field(ch, IPU_FIELD_WID3, 7); -+ ipu_cpmem_set_separate_alpha(ch); -+ break; -+ default: -+ break; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt); -+ -+int ipu_cpmem_set_image(struct ipuv3_channel *ch, struct ipu_image *image) -+{ -+ struct v4l2_pix_format *pix = &image->pix; -+ int offset, u_offset, v_offset; -+ int ret = 0; -+ -+ pr_debug("%s: resolution: %dx%d stride: %d\n", -+ __func__, pix->width, pix->height, -+ pix->bytesperline); -+ -+ ipu_cpmem_set_resolution(ch, image->rect.width, image->rect.height); -+ ipu_cpmem_set_stride(ch, pix->bytesperline); -+ -+ ipu_cpmem_set_fmt(ch, v4l2_pix_fmt_to_drm_fourcc(pix->pixelformat)); -+ -+ switch (pix->pixelformat) { -+ case V4L2_PIX_FMT_YUV420: -+ offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -+ u_offset = image->u_offset ? -+ image->u_offset : U_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ v_offset = image->v_offset ? -+ image->v_offset : V_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ -+ ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2, -+ u_offset, v_offset); -+ break; -+ case V4L2_PIX_FMT_YVU420: -+ offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -+ u_offset = image->u_offset ? -+ image->u_offset : V_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ v_offset = image->v_offset ? -+ image->v_offset : U_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ -+ ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2, -+ u_offset, v_offset); -+ break; -+ case V4L2_PIX_FMT_YUV422P: -+ offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -+ u_offset = image->u_offset ? -+ image->u_offset : U2_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ v_offset = image->v_offset ? -+ image->v_offset : V2_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ -+ ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2, -+ u_offset, v_offset); -+ break; -+ case V4L2_PIX_FMT_NV12: -+ offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -+ u_offset = image->u_offset ? -+ image->u_offset : UV_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ v_offset = image->v_offset ? image->v_offset : 0; -+ -+ ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline, -+ u_offset, v_offset); -+ break; -+ case V4L2_PIX_FMT_NV16: -+ offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -+ u_offset = image->u_offset ? -+ image->u_offset : UV2_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ v_offset = image->v_offset ? image->v_offset : 0; -+ -+ ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline, -+ u_offset, v_offset); -+ break; -+ case V4L2_PIX_FMT_UYVY: -+ case V4L2_PIX_FMT_YUYV: -+ case V4L2_PIX_FMT_RGB565: -+ offset = image->rect.left * 2 + -+ image->rect.top * pix->bytesperline; -+ break; -+ case V4L2_PIX_FMT_RGB32: -+ case V4L2_PIX_FMT_BGR32: -+ case V4L2_PIX_FMT_ABGR32: -+ case V4L2_PIX_FMT_XBGR32: -+ case V4L2_PIX_FMT_BGRA32: -+ case V4L2_PIX_FMT_BGRX32: -+ case V4L2_PIX_FMT_RGBA32: -+ case V4L2_PIX_FMT_RGBX32: -+ case V4L2_PIX_FMT_ARGB32: -+ case V4L2_PIX_FMT_XRGB32: -+ offset = image->rect.left * 4 + -+ image->rect.top * pix->bytesperline; -+ break; -+ case V4L2_PIX_FMT_RGB24: -+ case V4L2_PIX_FMT_BGR24: -+ offset = image->rect.left * 3 + -+ image->rect.top * pix->bytesperline; -+ break; -+ case V4L2_PIX_FMT_SBGGR8: -+ case V4L2_PIX_FMT_SGBRG8: -+ case V4L2_PIX_FMT_SGRBG8: -+ case V4L2_PIX_FMT_SRGGB8: -+ case V4L2_PIX_FMT_GREY: -+ offset = image->rect.left + image->rect.top * pix->bytesperline; -+ break; -+ case V4L2_PIX_FMT_SBGGR16: -+ case V4L2_PIX_FMT_SGBRG16: -+ case V4L2_PIX_FMT_SGRBG16: -+ case V4L2_PIX_FMT_SRGGB16: -+ case V4L2_PIX_FMT_Y16: -+ offset = image->rect.left * 2 + -+ image->rect.top * pix->bytesperline; -+ break; -+ default: -+ /* This should not happen */ -+ WARN_ON(1); -+ offset = 0; -+ ret = -EINVAL; -+ } -+ -+ ipu_cpmem_set_buffer(ch, 0, image->phys0 + offset); -+ ipu_cpmem_set_buffer(ch, 1, image->phys1 + offset); -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_image); -+ -+void ipu_cpmem_dump(struct ipuv3_channel *ch) -+{ -+ struct ipu_ch_param __iomem *p = ipu_get_cpmem(ch); -+ struct ipu_soc *ipu = ch->ipu; -+ int chno = ch->num; -+ -+ dev_dbg(ipu->dev, "ch %d word 0 - %08X %08X %08X %08X %08X\n", chno, -+ readl(&p->word[0].data[0]), -+ readl(&p->word[0].data[1]), -+ readl(&p->word[0].data[2]), -+ readl(&p->word[0].data[3]), -+ readl(&p->word[0].data[4])); -+ dev_dbg(ipu->dev, "ch %d word 1 - %08X %08X %08X %08X %08X\n", chno, -+ readl(&p->word[1].data[0]), -+ readl(&p->word[1].data[1]), -+ readl(&p->word[1].data[2]), -+ readl(&p->word[1].data[3]), -+ readl(&p->word[1].data[4])); -+ dev_dbg(ipu->dev, "PFS 0x%x, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_PFS)); -+ dev_dbg(ipu->dev, "BPP 0x%x, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_BPP)); -+ dev_dbg(ipu->dev, "NPB 0x%x\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_NPB)); -+ -+ dev_dbg(ipu->dev, "FW %d, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_FW)); -+ dev_dbg(ipu->dev, "FH %d, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_FH)); -+ dev_dbg(ipu->dev, "EBA0 0x%x\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_EBA0) << 3); -+ dev_dbg(ipu->dev, "EBA1 0x%x\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_EBA1) << 3); -+ dev_dbg(ipu->dev, "Stride %d\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_SL)); -+ dev_dbg(ipu->dev, "scan_order %d\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_SO)); -+ dev_dbg(ipu->dev, "uv_stride %d\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_SLUV)); -+ dev_dbg(ipu->dev, "u_offset 0x%x\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_UBO) << 3); -+ dev_dbg(ipu->dev, "v_offset 0x%x\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_VBO) << 3); -+ -+ dev_dbg(ipu->dev, "Width0 %d+1, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_WID0)); -+ dev_dbg(ipu->dev, "Width1 %d+1, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_WID1)); -+ dev_dbg(ipu->dev, "Width2 %d+1, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_WID2)); -+ dev_dbg(ipu->dev, "Width3 %d+1, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_WID3)); -+ dev_dbg(ipu->dev, "Offset0 %d, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_OFS0)); -+ dev_dbg(ipu->dev, "Offset1 %d, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_OFS1)); -+ dev_dbg(ipu->dev, "Offset2 %d, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_OFS2)); -+ dev_dbg(ipu->dev, "Offset3 %d\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_OFS3)); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_dump); -+ -+int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base) -+{ -+ struct ipu_cpmem *cpmem; -+ -+ cpmem = devm_kzalloc(dev, sizeof(*cpmem), GFP_KERNEL); -+ if (!cpmem) -+ return -ENOMEM; -+ -+ ipu->cpmem_priv = cpmem; -+ -+ spin_lock_init(&cpmem->lock); -+ cpmem->base = devm_ioremap(dev, base, SZ_128K); -+ if (!cpmem->base) -+ return -ENOMEM; -+ -+ dev_dbg(dev, "CPMEM base: 0x%08lx remapped to %p\n", -+ base, cpmem->base); -+ cpmem->ipu = ipu; -+ -+ return 0; -+} -+ -+void ipu_cpmem_exit(struct ipu_soc *ipu) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-csi.c -@@ -0,0 +1,821 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (C) 2012-2014 Mentor Graphics Inc. -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#include <linux/export.h> -+#include <linux/module.h> -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/delay.h> -+#include <linux/io.h> -+#include <linux/err.h> -+#include <linux/platform_device.h> -+#include <linux/videodev2.h> -+#include <uapi/linux/v4l2-mediabus.h> -+#include <linux/clk.h> -+#include <linux/clk-provider.h> -+#include <linux/clkdev.h> -+ -+#include "ipu-prv.h" -+ -+struct ipu_csi { -+ void __iomem *base; -+ int id; -+ u32 module; -+ struct clk *clk_ipu; /* IPU bus clock */ -+ spinlock_t lock; -+ bool inuse; -+ struct ipu_soc *ipu; -+}; -+ -+/* CSI Register Offsets */ -+#define CSI_SENS_CONF 0x0000 -+#define CSI_SENS_FRM_SIZE 0x0004 -+#define CSI_ACT_FRM_SIZE 0x0008 -+#define CSI_OUT_FRM_CTRL 0x000c -+#define CSI_TST_CTRL 0x0010 -+#define CSI_CCIR_CODE_1 0x0014 -+#define CSI_CCIR_CODE_2 0x0018 -+#define CSI_CCIR_CODE_3 0x001c -+#define CSI_MIPI_DI 0x0020 -+#define CSI_SKIP 0x0024 -+#define CSI_CPD_CTRL 0x0028 -+#define CSI_CPD_RC(n) (0x002c + ((n)*4)) -+#define CSI_CPD_RS(n) (0x004c + ((n)*4)) -+#define CSI_CPD_GRC(n) (0x005c + ((n)*4)) -+#define CSI_CPD_GRS(n) (0x007c + ((n)*4)) -+#define CSI_CPD_GBC(n) (0x008c + ((n)*4)) -+#define CSI_CPD_GBS(n) (0x00Ac + ((n)*4)) -+#define CSI_CPD_BC(n) (0x00Bc + ((n)*4)) -+#define CSI_CPD_BS(n) (0x00Dc + ((n)*4)) -+#define CSI_CPD_OFFSET1 0x00ec -+#define CSI_CPD_OFFSET2 0x00f0 -+ -+/* CSI Register Fields */ -+#define CSI_SENS_CONF_DATA_FMT_SHIFT 8 -+#define CSI_SENS_CONF_DATA_FMT_MASK 0x00000700 -+#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 0L -+#define CSI_SENS_CONF_DATA_FMT_YUV422_YUYV 1L -+#define CSI_SENS_CONF_DATA_FMT_YUV422_UYVY 2L -+#define CSI_SENS_CONF_DATA_FMT_BAYER 3L -+#define CSI_SENS_CONF_DATA_FMT_RGB565 4L -+#define CSI_SENS_CONF_DATA_FMT_RGB555 5L -+#define CSI_SENS_CONF_DATA_FMT_RGB444 6L -+#define CSI_SENS_CONF_DATA_FMT_JPEG 7L -+ -+#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0 -+#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1 -+#define CSI_SENS_CONF_DATA_POL_SHIFT 2 -+#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3 -+#define CSI_SENS_CONF_SENS_PRTCL_MASK 0x00000070 -+#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4 -+#define CSI_SENS_CONF_PACK_TIGHT_SHIFT 7 -+#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 11 -+#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15 -+#define CSI_SENS_CONF_DIVRATIO_SHIFT 16 -+ -+#define CSI_SENS_CONF_DIVRATIO_MASK 0x00ff0000 -+#define CSI_SENS_CONF_DATA_DEST_SHIFT 24 -+#define CSI_SENS_CONF_DATA_DEST_MASK 0x07000000 -+#define CSI_SENS_CONF_JPEG8_EN_SHIFT 27 -+#define CSI_SENS_CONF_JPEG_EN_SHIFT 28 -+#define CSI_SENS_CONF_FORCE_EOF_SHIFT 29 -+#define CSI_SENS_CONF_DATA_EN_POL_SHIFT 31 -+ -+#define CSI_DATA_DEST_IC 2 -+#define CSI_DATA_DEST_IDMAC 4 -+ -+#define CSI_CCIR_ERR_DET_EN 0x01000000 -+#define CSI_HORI_DOWNSIZE_EN 0x80000000 -+#define CSI_VERT_DOWNSIZE_EN 0x40000000 -+#define CSI_TEST_GEN_MODE_EN 0x01000000 -+ -+#define CSI_HSC_MASK 0x1fff0000 -+#define CSI_HSC_SHIFT 16 -+#define CSI_VSC_MASK 0x00000fff -+#define CSI_VSC_SHIFT 0 -+ -+#define CSI_TEST_GEN_R_MASK 0x000000ff -+#define CSI_TEST_GEN_R_SHIFT 0 -+#define CSI_TEST_GEN_G_MASK 0x0000ff00 -+#define CSI_TEST_GEN_G_SHIFT 8 -+#define CSI_TEST_GEN_B_MASK 0x00ff0000 -+#define CSI_TEST_GEN_B_SHIFT 16 -+ -+#define CSI_MAX_RATIO_SKIP_SMFC_MASK 0x00000007 -+#define CSI_MAX_RATIO_SKIP_SMFC_SHIFT 0 -+#define CSI_SKIP_SMFC_MASK 0x000000f8 -+#define CSI_SKIP_SMFC_SHIFT 3 -+#define CSI_ID_2_SKIP_MASK 0x00000300 -+#define CSI_ID_2_SKIP_SHIFT 8 -+ -+#define CSI_COLOR_FIRST_ROW_MASK 0x00000002 -+#define CSI_COLOR_FIRST_COMP_MASK 0x00000001 -+ -+/* MIPI CSI-2 data types */ -+#define MIPI_DT_YUV420 0x18 /* YYY.../UYVY.... */ -+#define MIPI_DT_YUV420_LEGACY 0x1a /* UYY.../VYY... */ -+#define MIPI_DT_YUV422 0x1e /* UYVY... */ -+#define MIPI_DT_RGB444 0x20 -+#define MIPI_DT_RGB555 0x21 -+#define MIPI_DT_RGB565 0x22 -+#define MIPI_DT_RGB666 0x23 -+#define MIPI_DT_RGB888 0x24 -+#define MIPI_DT_RAW6 0x28 -+#define MIPI_DT_RAW7 0x29 -+#define MIPI_DT_RAW8 0x2a -+#define MIPI_DT_RAW10 0x2b -+#define MIPI_DT_RAW12 0x2c -+#define MIPI_DT_RAW14 0x2d -+ -+/* -+ * Bitfield of CSI bus signal polarities and modes. -+ */ -+struct ipu_csi_bus_config { -+ unsigned data_width:4; -+ unsigned clk_mode:3; -+ unsigned ext_vsync:1; -+ unsigned vsync_pol:1; -+ unsigned hsync_pol:1; -+ unsigned pixclk_pol:1; -+ unsigned data_pol:1; -+ unsigned sens_clksrc:1; -+ unsigned pack_tight:1; -+ unsigned force_eof:1; -+ unsigned data_en_pol:1; -+ -+ unsigned data_fmt; -+ unsigned mipi_dt; -+}; -+ -+/* -+ * Enumeration of CSI data bus widths. -+ */ -+enum ipu_csi_data_width { -+ IPU_CSI_DATA_WIDTH_4 = 0, -+ IPU_CSI_DATA_WIDTH_8 = 1, -+ IPU_CSI_DATA_WIDTH_10 = 3, -+ IPU_CSI_DATA_WIDTH_12 = 5, -+ IPU_CSI_DATA_WIDTH_16 = 9, -+}; -+ -+/* -+ * Enumeration of CSI clock modes. -+ */ -+enum ipu_csi_clk_mode { -+ IPU_CSI_CLK_MODE_GATED_CLK, -+ IPU_CSI_CLK_MODE_NONGATED_CLK, -+ IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE, -+ IPU_CSI_CLK_MODE_CCIR656_INTERLACED, -+ IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR, -+ IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR, -+ IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR, -+ IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR, -+}; -+ -+static inline u32 ipu_csi_read(struct ipu_csi *csi, unsigned offset) -+{ -+ return readl(csi->base + offset); -+} -+ -+static inline void ipu_csi_write(struct ipu_csi *csi, u32 value, -+ unsigned offset) -+{ -+ writel(value, csi->base + offset); -+} -+ -+/* -+ * Set mclk division ratio for generating test mode mclk. Only used -+ * for test generator. -+ */ -+static int ipu_csi_set_testgen_mclk(struct ipu_csi *csi, u32 pixel_clk, -+ u32 ipu_clk) -+{ -+ u32 temp; -+ int div_ratio; -+ -+ div_ratio = (ipu_clk / pixel_clk) - 1; -+ -+ if (div_ratio > 0xFF || div_ratio < 0) { -+ dev_err(csi->ipu->dev, -+ "value of pixel_clk extends normal range\n"); -+ return -EINVAL; -+ } -+ -+ temp = ipu_csi_read(csi, CSI_SENS_CONF); -+ temp &= ~CSI_SENS_CONF_DIVRATIO_MASK; -+ ipu_csi_write(csi, temp | (div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT), -+ CSI_SENS_CONF); -+ -+ return 0; -+} -+ -+/* -+ * Find the CSI data format and data width for the given V4L2 media -+ * bus pixel format code. -+ */ -+static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, -+ enum v4l2_mbus_type mbus_type) -+{ -+ switch (mbus_code) { -+ case MEDIA_BUS_FMT_BGR565_2X8_BE: -+ case MEDIA_BUS_FMT_BGR565_2X8_LE: -+ case MEDIA_BUS_FMT_RGB565_2X8_BE: -+ case MEDIA_BUS_FMT_RGB565_2X8_LE: -+ if (mbus_type == V4L2_MBUS_CSI2_DPHY) -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565; -+ else -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -+ cfg->mipi_dt = MIPI_DT_RGB565; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE: -+ case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB444; -+ cfg->mipi_dt = MIPI_DT_RGB444; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: -+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555; -+ cfg->mipi_dt = MIPI_DT_RGB555; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ case MEDIA_BUS_FMT_BGR888_1X24: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444; -+ cfg->mipi_dt = MIPI_DT_RGB888; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_UYVY8_2X8: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY; -+ cfg->mipi_dt = MIPI_DT_YUV422; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_YUYV8_2X8: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV; -+ cfg->mipi_dt = MIPI_DT_YUV422; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_UYVY8_1X16: -+ case MEDIA_BUS_FMT_YUYV8_1X16: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -+ cfg->mipi_dt = MIPI_DT_YUV422; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_16; -+ break; -+ case MEDIA_BUS_FMT_SBGGR8_1X8: -+ case MEDIA_BUS_FMT_SGBRG8_1X8: -+ case MEDIA_BUS_FMT_SGRBG8_1X8: -+ case MEDIA_BUS_FMT_SRGGB8_1X8: -+ case MEDIA_BUS_FMT_Y8_1X8: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -+ cfg->mipi_dt = MIPI_DT_RAW8; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8: -+ case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8: -+ case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8: -+ case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8: -+ case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE: -+ case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE: -+ case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE: -+ case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -+ cfg->mipi_dt = MIPI_DT_RAW10; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_SBGGR10_1X10: -+ case MEDIA_BUS_FMT_SGBRG10_1X10: -+ case MEDIA_BUS_FMT_SGRBG10_1X10: -+ case MEDIA_BUS_FMT_SRGGB10_1X10: -+ case MEDIA_BUS_FMT_Y10_1X10: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -+ cfg->mipi_dt = MIPI_DT_RAW10; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_10; -+ break; -+ case MEDIA_BUS_FMT_SBGGR12_1X12: -+ case MEDIA_BUS_FMT_SGBRG12_1X12: -+ case MEDIA_BUS_FMT_SGRBG12_1X12: -+ case MEDIA_BUS_FMT_SRGGB12_1X12: -+ case MEDIA_BUS_FMT_Y12_1X12: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -+ cfg->mipi_dt = MIPI_DT_RAW12; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_12; -+ break; -+ case MEDIA_BUS_FMT_JPEG_1X8: -+ /* TODO */ -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_JPEG; -+ cfg->mipi_dt = MIPI_DT_RAW8; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+/* translate alternate field mode based on given standard */ -+static inline enum v4l2_field -+ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) -+{ -+ return (field != V4L2_FIELD_ALTERNATE) ? field : -+ ((std & V4L2_STD_525_60) ? -+ V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); -+} -+ -+/* -+ * Fill a CSI bus config struct from mbus_config and mbus_framefmt. -+ */ -+static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, -+ const struct v4l2_mbus_config *mbus_cfg, -+ const struct v4l2_mbus_framefmt *mbus_fmt) -+{ -+ int ret; -+ -+ memset(csicfg, 0, sizeof(*csicfg)); -+ -+ ret = mbus_code_to_bus_cfg(csicfg, mbus_fmt->code, mbus_cfg->type); -+ if (ret < 0) -+ return ret; -+ -+ switch (mbus_cfg->type) { -+ case V4L2_MBUS_PARALLEL: -+ csicfg->ext_vsync = 1; -+ csicfg->vsync_pol = (mbus_cfg->flags & -+ V4L2_MBUS_VSYNC_ACTIVE_LOW) ? 1 : 0; -+ csicfg->hsync_pol = (mbus_cfg->flags & -+ V4L2_MBUS_HSYNC_ACTIVE_LOW) ? 1 : 0; -+ csicfg->pixclk_pol = (mbus_cfg->flags & -+ V4L2_MBUS_PCLK_SAMPLE_FALLING) ? 1 : 0; -+ csicfg->clk_mode = IPU_CSI_CLK_MODE_GATED_CLK; -+ break; -+ case V4L2_MBUS_BT656: -+ csicfg->ext_vsync = 0; -+ if (V4L2_FIELD_HAS_BOTH(mbus_fmt->field) || -+ mbus_fmt->field == V4L2_FIELD_ALTERNATE) -+ csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED; -+ else -+ csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE; -+ break; -+ case V4L2_MBUS_CSI2_DPHY: -+ /* -+ * MIPI CSI-2 requires non gated clock mode, all other -+ * parameters are not applicable for MIPI CSI-2 bus. -+ */ -+ csicfg->clk_mode = IPU_CSI_CLK_MODE_NONGATED_CLK; -+ break; -+ default: -+ /* will never get here, keep compiler quiet */ -+ break; -+ } -+ -+ return 0; -+} -+ -+static int -+ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, -+ const struct v4l2_mbus_framefmt *infmt, -+ const struct v4l2_mbus_framefmt *outfmt, -+ v4l2_std_id std) -+{ -+ enum v4l2_field infield, outfield; -+ bool swap_fields; -+ -+ /* get translated field type of input and output */ -+ infield = ipu_csi_translate_field(infmt->field, std); -+ outfield = ipu_csi_translate_field(outfmt->field, std); -+ -+ /* -+ * Write the H-V-F codes the CSI will match against the -+ * incoming data for start/end of active and blanking -+ * field intervals. If input and output field types are -+ * sequential but not the same (one is SEQ_BT and the other -+ * is SEQ_TB), swap the F-bit so that the CSI will capture -+ * field 1 lines before field 0 lines. -+ */ -+ swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && -+ V4L2_FIELD_IS_SEQUENTIAL(outfield) && -+ infield != outfield); -+ -+ if (!swap_fields) { -+ /* -+ * Field0BlankEnd = 110, Field0BlankStart = 010 -+ * Field0ActiveEnd = 100, Field0ActiveStart = 000 -+ * Field1BlankEnd = 111, Field1BlankStart = 011 -+ * Field1ActiveEnd = 101, Field1ActiveStart = 001 -+ */ -+ ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, -+ CSI_CCIR_CODE_1); -+ ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); -+ } else { -+ dev_dbg(csi->ipu->dev, "capture field swap\n"); -+ -+ /* same as above but with F-bit inverted */ -+ ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, -+ CSI_CCIR_CODE_1); -+ ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); -+ } -+ -+ ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); -+ -+ return 0; -+} -+ -+ -+int ipu_csi_init_interface(struct ipu_csi *csi, -+ const struct v4l2_mbus_config *mbus_cfg, -+ const struct v4l2_mbus_framefmt *infmt, -+ const struct v4l2_mbus_framefmt *outfmt) -+{ -+ struct ipu_csi_bus_config cfg; -+ unsigned long flags; -+ u32 width, height, data = 0; -+ v4l2_std_id std; -+ int ret; -+ -+ ret = fill_csi_bus_cfg(&cfg, mbus_cfg, infmt); -+ if (ret < 0) -+ return ret; -+ -+ /* set default sensor frame width and height */ -+ width = infmt->width; -+ height = infmt->height; -+ if (infmt->field == V4L2_FIELD_ALTERNATE) -+ height *= 2; -+ -+ /* Set the CSI_SENS_CONF register remaining fields */ -+ data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT | -+ cfg.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT | -+ cfg.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT | -+ cfg.vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT | -+ cfg.hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT | -+ cfg.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT | -+ cfg.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT | -+ cfg.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT | -+ cfg.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT | -+ cfg.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT | -+ cfg.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ ipu_csi_write(csi, data, CSI_SENS_CONF); -+ -+ /* Set CCIR registers */ -+ -+ switch (cfg.clk_mode) { -+ case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: -+ ipu_csi_write(csi, 0x40030, CSI_CCIR_CODE_1); -+ ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); -+ break; -+ case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: -+ if (width == 720 && height == 480) { -+ std = V4L2_STD_NTSC; -+ height = 525; -+ } else if (width == 720 && height == 576) { -+ std = V4L2_STD_PAL; -+ height = 625; -+ } else { -+ dev_err(csi->ipu->dev, -+ "Unsupported interlaced video mode\n"); -+ ret = -EINVAL; -+ goto out_unlock; -+ } -+ -+ ret = ipu_csi_set_bt_interlaced_codes(csi, infmt, outfmt, std); -+ if (ret) -+ goto out_unlock; -+ break; -+ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: -+ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: -+ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: -+ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: -+ ipu_csi_write(csi, 0x40030 | CSI_CCIR_ERR_DET_EN, -+ CSI_CCIR_CODE_1); -+ ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); -+ break; -+ case IPU_CSI_CLK_MODE_GATED_CLK: -+ case IPU_CSI_CLK_MODE_NONGATED_CLK: -+ ipu_csi_write(csi, 0, CSI_CCIR_CODE_1); -+ break; -+ } -+ -+ /* Setup sensor frame size */ -+ ipu_csi_write(csi, (width - 1) | ((height - 1) << 16), -+ CSI_SENS_FRM_SIZE); -+ -+ dev_dbg(csi->ipu->dev, "CSI_SENS_CONF = 0x%08X\n", -+ ipu_csi_read(csi, CSI_SENS_CONF)); -+ dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n", -+ ipu_csi_read(csi, CSI_ACT_FRM_SIZE)); -+ -+out_unlock: -+ spin_unlock_irqrestore(&csi->lock, flags); -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_init_interface); -+ -+bool ipu_csi_is_interlaced(struct ipu_csi *csi) -+{ -+ unsigned long flags; -+ u32 sensor_protocol; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ sensor_protocol = -+ (ipu_csi_read(csi, CSI_SENS_CONF) & -+ CSI_SENS_CONF_SENS_PRTCL_MASK) >> -+ CSI_SENS_CONF_SENS_PRTCL_SHIFT; -+ spin_unlock_irqrestore(&csi->lock, flags); -+ -+ switch (sensor_protocol) { -+ case IPU_CSI_CLK_MODE_GATED_CLK: -+ case IPU_CSI_CLK_MODE_NONGATED_CLK: -+ case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: -+ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: -+ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: -+ return false; -+ case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: -+ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: -+ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: -+ return true; -+ default: -+ dev_err(csi->ipu->dev, -+ "CSI %d sensor protocol unsupported\n", csi->id); -+ return false; -+ } -+} -+EXPORT_SYMBOL_GPL(ipu_csi_is_interlaced); -+ -+void ipu_csi_get_window(struct ipu_csi *csi, struct v4l2_rect *w) -+{ -+ unsigned long flags; -+ u32 reg; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ reg = ipu_csi_read(csi, CSI_ACT_FRM_SIZE); -+ w->width = (reg & 0xFFFF) + 1; -+ w->height = (reg >> 16 & 0xFFFF) + 1; -+ -+ reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); -+ w->left = (reg & CSI_HSC_MASK) >> CSI_HSC_SHIFT; -+ w->top = (reg & CSI_VSC_MASK) >> CSI_VSC_SHIFT; -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_csi_get_window); -+ -+void ipu_csi_set_window(struct ipu_csi *csi, struct v4l2_rect *w) -+{ -+ unsigned long flags; -+ u32 reg; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ ipu_csi_write(csi, (w->width - 1) | ((w->height - 1) << 16), -+ CSI_ACT_FRM_SIZE); -+ -+ reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); -+ reg &= ~(CSI_HSC_MASK | CSI_VSC_MASK); -+ reg |= ((w->top << CSI_VSC_SHIFT) | (w->left << CSI_HSC_SHIFT)); -+ ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL); -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_csi_set_window); -+ -+void ipu_csi_set_downsize(struct ipu_csi *csi, bool horiz, bool vert) -+{ -+ unsigned long flags; -+ u32 reg; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); -+ reg &= ~(CSI_HORI_DOWNSIZE_EN | CSI_VERT_DOWNSIZE_EN); -+ reg |= (horiz ? CSI_HORI_DOWNSIZE_EN : 0) | -+ (vert ? CSI_VERT_DOWNSIZE_EN : 0); -+ ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL); -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_csi_set_downsize); -+ -+void ipu_csi_set_test_generator(struct ipu_csi *csi, bool active, -+ u32 r_value, u32 g_value, u32 b_value, -+ u32 pix_clk) -+{ -+ unsigned long flags; -+ u32 ipu_clk = clk_get_rate(csi->clk_ipu); -+ u32 temp; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ temp = ipu_csi_read(csi, CSI_TST_CTRL); -+ -+ if (!active) { -+ temp &= ~CSI_TEST_GEN_MODE_EN; -+ ipu_csi_write(csi, temp, CSI_TST_CTRL); -+ } else { -+ /* Set sensb_mclk div_ratio */ -+ ipu_csi_set_testgen_mclk(csi, pix_clk, ipu_clk); -+ -+ temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK | -+ CSI_TEST_GEN_B_MASK); -+ temp |= CSI_TEST_GEN_MODE_EN; -+ temp |= (r_value << CSI_TEST_GEN_R_SHIFT) | -+ (g_value << CSI_TEST_GEN_G_SHIFT) | -+ (b_value << CSI_TEST_GEN_B_SHIFT); -+ ipu_csi_write(csi, temp, CSI_TST_CTRL); -+ } -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_csi_set_test_generator); -+ -+int ipu_csi_set_mipi_datatype(struct ipu_csi *csi, u32 vc, -+ struct v4l2_mbus_framefmt *mbus_fmt) -+{ -+ struct ipu_csi_bus_config cfg; -+ unsigned long flags; -+ u32 temp; -+ int ret; -+ -+ if (vc > 3) -+ return -EINVAL; -+ -+ ret = mbus_code_to_bus_cfg(&cfg, mbus_fmt->code, V4L2_MBUS_CSI2_DPHY); -+ if (ret < 0) -+ return ret; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ temp = ipu_csi_read(csi, CSI_MIPI_DI); -+ temp &= ~(0xff << (vc * 8)); -+ temp |= (cfg.mipi_dt << (vc * 8)); -+ ipu_csi_write(csi, temp, CSI_MIPI_DI); -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_set_mipi_datatype); -+ -+int ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip, -+ u32 max_ratio, u32 id) -+{ -+ unsigned long flags; -+ u32 temp; -+ -+ if (max_ratio > 5 || id > 3) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ temp = ipu_csi_read(csi, CSI_SKIP); -+ temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK | -+ CSI_SKIP_SMFC_MASK); -+ temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) | -+ (id << CSI_ID_2_SKIP_SHIFT) | -+ (skip << CSI_SKIP_SMFC_SHIFT); -+ ipu_csi_write(csi, temp, CSI_SKIP); -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_set_skip_smfc); -+ -+int ipu_csi_set_dest(struct ipu_csi *csi, enum ipu_csi_dest csi_dest) -+{ -+ unsigned long flags; -+ u32 csi_sens_conf, dest; -+ -+ if (csi_dest == IPU_CSI_DEST_IDMAC) -+ dest = CSI_DATA_DEST_IDMAC; -+ else -+ dest = CSI_DATA_DEST_IC; /* IC or VDIC */ -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ csi_sens_conf = ipu_csi_read(csi, CSI_SENS_CONF); -+ csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK; -+ csi_sens_conf |= (dest << CSI_SENS_CONF_DATA_DEST_SHIFT); -+ ipu_csi_write(csi, csi_sens_conf, CSI_SENS_CONF); -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_set_dest); -+ -+int ipu_csi_enable(struct ipu_csi *csi) -+{ -+ ipu_module_enable(csi->ipu, csi->module); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_enable); -+ -+int ipu_csi_disable(struct ipu_csi *csi) -+{ -+ ipu_module_disable(csi->ipu, csi->module); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_disable); -+ -+struct ipu_csi *ipu_csi_get(struct ipu_soc *ipu, int id) -+{ -+ unsigned long flags; -+ struct ipu_csi *csi, *ret; -+ -+ if (id > 1) -+ return ERR_PTR(-EINVAL); -+ -+ csi = ipu->csi_priv[id]; -+ ret = csi; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ if (csi->inuse) { -+ ret = ERR_PTR(-EBUSY); -+ goto unlock; -+ } -+ -+ csi->inuse = true; -+unlock: -+ spin_unlock_irqrestore(&csi->lock, flags); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_get); -+ -+void ipu_csi_put(struct ipu_csi *csi) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ csi->inuse = false; -+ spin_unlock_irqrestore(&csi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_csi_put); -+ -+int ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id, -+ unsigned long base, u32 module, struct clk *clk_ipu) -+{ -+ struct ipu_csi *csi; -+ -+ if (id > 1) -+ return -ENODEV; -+ -+ csi = devm_kzalloc(dev, sizeof(*csi), GFP_KERNEL); -+ if (!csi) -+ return -ENOMEM; -+ -+ ipu->csi_priv[id] = csi; -+ -+ spin_lock_init(&csi->lock); -+ csi->module = module; -+ csi->id = id; -+ csi->clk_ipu = clk_ipu; -+ csi->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!csi->base) -+ return -ENOMEM; -+ -+ dev_dbg(dev, "CSI%d base: 0x%08lx remapped to %p\n", -+ id, base, csi->base); -+ csi->ipu = ipu; -+ -+ return 0; -+} -+ -+void ipu_csi_exit(struct ipu_soc *ipu, int id) -+{ -+} -+ -+void ipu_csi_dump(struct ipu_csi *csi) -+{ -+ dev_dbg(csi->ipu->dev, "CSI_SENS_CONF: %08x\n", -+ ipu_csi_read(csi, CSI_SENS_CONF)); -+ dev_dbg(csi->ipu->dev, "CSI_SENS_FRM_SIZE: %08x\n", -+ ipu_csi_read(csi, CSI_SENS_FRM_SIZE)); -+ dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE: %08x\n", -+ ipu_csi_read(csi, CSI_ACT_FRM_SIZE)); -+ dev_dbg(csi->ipu->dev, "CSI_OUT_FRM_CTRL: %08x\n", -+ ipu_csi_read(csi, CSI_OUT_FRM_CTRL)); -+ dev_dbg(csi->ipu->dev, "CSI_TST_CTRL: %08x\n", -+ ipu_csi_read(csi, CSI_TST_CTRL)); -+ dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_1: %08x\n", -+ ipu_csi_read(csi, CSI_CCIR_CODE_1)); -+ dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_2: %08x\n", -+ ipu_csi_read(csi, CSI_CCIR_CODE_2)); -+ dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_3: %08x\n", -+ ipu_csi_read(csi, CSI_CCIR_CODE_3)); -+ dev_dbg(csi->ipu->dev, "CSI_MIPI_DI: %08x\n", -+ ipu_csi_read(csi, CSI_MIPI_DI)); -+ dev_dbg(csi->ipu->dev, "CSI_SKIP: %08x\n", -+ ipu_csi_read(csi, CSI_SKIP)); -+} -+EXPORT_SYMBOL_GPL(ipu_csi_dump); ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-dc.c -@@ -0,0 +1,420 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+ -+#include <linux/export.h> -+#include <linux/module.h> -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/delay.h> -+#include <linux/interrupt.h> -+#include <linux/io.h> -+ -+#include <video/imx-ipu-v3.h> -+#include "ipu-prv.h" -+ -+#define DC_MAP_CONF_PTR(n) (0x108 + ((n) & ~0x1) * 2) -+#define DC_MAP_CONF_VAL(n) (0x144 + ((n) & ~0x1) * 2) -+ -+#define DC_EVT_NF 0 -+#define DC_EVT_NL 1 -+#define DC_EVT_EOF 2 -+#define DC_EVT_NFIELD 3 -+#define DC_EVT_EOL 4 -+#define DC_EVT_EOFIELD 5 -+#define DC_EVT_NEW_ADDR 6 -+#define DC_EVT_NEW_CHAN 7 -+#define DC_EVT_NEW_DATA 8 -+ -+#define DC_EVT_NEW_ADDR_W_0 0 -+#define DC_EVT_NEW_ADDR_W_1 1 -+#define DC_EVT_NEW_CHAN_W_0 2 -+#define DC_EVT_NEW_CHAN_W_1 3 -+#define DC_EVT_NEW_DATA_W_0 4 -+#define DC_EVT_NEW_DATA_W_1 5 -+#define DC_EVT_NEW_ADDR_R_0 6 -+#define DC_EVT_NEW_ADDR_R_1 7 -+#define DC_EVT_NEW_CHAN_R_0 8 -+#define DC_EVT_NEW_CHAN_R_1 9 -+#define DC_EVT_NEW_DATA_R_0 10 -+#define DC_EVT_NEW_DATA_R_1 11 -+ -+#define DC_WR_CH_CONF 0x0 -+#define DC_WR_CH_ADDR 0x4 -+#define DC_RL_CH(evt) (8 + ((evt) & ~0x1) * 2) -+ -+#define DC_GEN 0xd4 -+#define DC_DISP_CONF1(disp) (0xd8 + (disp) * 4) -+#define DC_DISP_CONF2(disp) (0xe8 + (disp) * 4) -+#define DC_STAT 0x1c8 -+ -+#define WROD(lf) (0x18 | ((lf) << 1)) -+#define WRG 0x01 -+#define WCLK 0xc9 -+ -+#define SYNC_WAVE 0 -+#define NULL_WAVE (-1) -+ -+#define DC_GEN_SYNC_1_6_SYNC (2 << 1) -+#define DC_GEN_SYNC_PRIORITY_1 (1 << 7) -+ -+#define DC_WR_CH_CONF_WORD_SIZE_8 (0 << 0) -+#define DC_WR_CH_CONF_WORD_SIZE_16 (1 << 0) -+#define DC_WR_CH_CONF_WORD_SIZE_24 (2 << 0) -+#define DC_WR_CH_CONF_WORD_SIZE_32 (3 << 0) -+#define DC_WR_CH_CONF_DISP_ID_PARALLEL(i) (((i) & 0x1) << 3) -+#define DC_WR_CH_CONF_DISP_ID_SERIAL (2 << 3) -+#define DC_WR_CH_CONF_DISP_ID_ASYNC (3 << 4) -+#define DC_WR_CH_CONF_FIELD_MODE (1 << 9) -+#define DC_WR_CH_CONF_PROG_TYPE_NORMAL (4 << 5) -+#define DC_WR_CH_CONF_PROG_TYPE_MASK (7 << 5) -+#define DC_WR_CH_CONF_PROG_DI_ID (1 << 2) -+#define DC_WR_CH_CONF_PROG_DISP_ID(i) (((i) & 0x1) << 3) -+ -+#define IPU_DC_NUM_CHANNELS 10 -+ -+struct ipu_dc_priv; -+ -+enum ipu_dc_map { -+ IPU_DC_MAP_RGB24, -+ IPU_DC_MAP_RGB565, -+ IPU_DC_MAP_GBR24, /* TVEv2 */ -+ IPU_DC_MAP_BGR666, -+ IPU_DC_MAP_LVDS666, -+ IPU_DC_MAP_BGR24, -+}; -+ -+struct ipu_dc { -+ /* The display interface number assigned to this dc channel */ -+ unsigned int di; -+ void __iomem *base; -+ struct ipu_dc_priv *priv; -+ int chno; -+ bool in_use; -+}; -+ -+struct ipu_dc_priv { -+ void __iomem *dc_reg; -+ void __iomem *dc_tmpl_reg; -+ struct ipu_soc *ipu; -+ struct device *dev; -+ struct ipu_dc channels[IPU_DC_NUM_CHANNELS]; -+ struct mutex mutex; -+ struct completion comp; -+ int use_count; -+}; -+ -+static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority) -+{ -+ u32 reg; -+ -+ reg = readl(dc->base + DC_RL_CH(event)); -+ reg &= ~(0xffff << (16 * (event & 0x1))); -+ reg |= ((addr << 8) | priority) << (16 * (event & 0x1)); -+ writel(reg, dc->base + DC_RL_CH(event)); -+} -+ -+static void dc_write_tmpl(struct ipu_dc *dc, int word, u32 opcode, u32 operand, -+ int map, int wave, int glue, int sync, int stop) -+{ -+ struct ipu_dc_priv *priv = dc->priv; -+ u32 reg1, reg2; -+ -+ if (opcode == WCLK) { -+ reg1 = (operand << 20) & 0xfff00000; -+ reg2 = operand >> 12 | opcode << 1 | stop << 9; -+ } else if (opcode == WRG) { -+ reg1 = sync | glue << 4 | ++wave << 11 | ((operand << 15) & 0xffff8000); -+ reg2 = operand >> 17 | opcode << 7 | stop << 9; -+ } else { -+ reg1 = sync | glue << 4 | ++wave << 11 | ++map << 15 | ((operand << 20) & 0xfff00000); -+ reg2 = operand >> 12 | opcode << 4 | stop << 9; -+ } -+ writel(reg1, priv->dc_tmpl_reg + word * 8); -+ writel(reg2, priv->dc_tmpl_reg + word * 8 + 4); -+} -+ -+static int ipu_bus_format_to_map(u32 fmt) -+{ -+ switch (fmt) { -+ default: -+ WARN_ON(1); -+ /* fall-through */ -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ return IPU_DC_MAP_RGB24; -+ case MEDIA_BUS_FMT_RGB565_1X16: -+ return IPU_DC_MAP_RGB565; -+ case MEDIA_BUS_FMT_GBR888_1X24: -+ return IPU_DC_MAP_GBR24; -+ case MEDIA_BUS_FMT_RGB666_1X18: -+ return IPU_DC_MAP_BGR666; -+ case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: -+ return IPU_DC_MAP_LVDS666; -+ case MEDIA_BUS_FMT_BGR888_1X24: -+ return IPU_DC_MAP_BGR24; -+ } -+} -+ -+int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, -+ u32 bus_format, u32 width) -+{ -+ struct ipu_dc_priv *priv = dc->priv; -+ int addr, sync; -+ u32 reg = 0; -+ int map; -+ -+ dc->di = ipu_di_get_num(di); -+ -+ map = ipu_bus_format_to_map(bus_format); -+ -+ /* -+ * In interlaced mode we need more counters to create the asymmetric -+ * per-field VSYNC signals. The pixel active signal synchronising DC -+ * to DI moves to signal generator #6 (see ipu-di.c). In progressive -+ * mode counter #5 is used. -+ */ -+ sync = interlaced ? 6 : 5; -+ -+ /* Reserve 5 microcode template words for each DI */ -+ if (dc->di) -+ addr = 5; -+ else -+ addr = 0; -+ -+ if (interlaced) { -+ dc_link_event(dc, DC_EVT_NL, addr, 3); -+ dc_link_event(dc, DC_EVT_EOL, addr, 2); -+ dc_link_event(dc, DC_EVT_NEW_DATA, addr, 1); -+ -+ /* Init template microcode */ -+ dc_write_tmpl(dc, addr, WROD(0), 0, map, SYNC_WAVE, 0, sync, 1); -+ } else { -+ dc_link_event(dc, DC_EVT_NL, addr + 2, 3); -+ dc_link_event(dc, DC_EVT_EOL, addr + 3, 2); -+ dc_link_event(dc, DC_EVT_NEW_DATA, addr + 1, 1); -+ -+ /* Init template microcode */ -+ dc_write_tmpl(dc, addr + 2, WROD(0), 0, map, SYNC_WAVE, 8, sync, 1); -+ dc_write_tmpl(dc, addr + 3, WROD(0), 0, map, SYNC_WAVE, 4, sync, 0); -+ dc_write_tmpl(dc, addr + 4, WRG, 0, map, NULL_WAVE, 0, 0, 1); -+ dc_write_tmpl(dc, addr + 1, WROD(0), 0, map, SYNC_WAVE, 0, sync, 1); -+ } -+ -+ dc_link_event(dc, DC_EVT_NF, 0, 0); -+ dc_link_event(dc, DC_EVT_NFIELD, 0, 0); -+ dc_link_event(dc, DC_EVT_EOF, 0, 0); -+ dc_link_event(dc, DC_EVT_EOFIELD, 0, 0); -+ dc_link_event(dc, DC_EVT_NEW_CHAN, 0, 0); -+ dc_link_event(dc, DC_EVT_NEW_ADDR, 0, 0); -+ -+ reg = readl(dc->base + DC_WR_CH_CONF); -+ if (interlaced) -+ reg |= DC_WR_CH_CONF_FIELD_MODE; -+ else -+ reg &= ~DC_WR_CH_CONF_FIELD_MODE; -+ writel(reg, dc->base + DC_WR_CH_CONF); -+ -+ writel(0x0, dc->base + DC_WR_CH_ADDR); -+ writel(width, priv->dc_reg + DC_DISP_CONF2(dc->di)); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dc_init_sync); -+ -+void ipu_dc_enable(struct ipu_soc *ipu) -+{ -+ struct ipu_dc_priv *priv = ipu->dc_priv; -+ -+ mutex_lock(&priv->mutex); -+ -+ if (!priv->use_count) -+ ipu_module_enable(priv->ipu, IPU_CONF_DC_EN); -+ -+ priv->use_count++; -+ -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dc_enable); -+ -+void ipu_dc_enable_channel(struct ipu_dc *dc) -+{ -+ u32 reg; -+ -+ reg = readl(dc->base + DC_WR_CH_CONF); -+ reg |= DC_WR_CH_CONF_PROG_TYPE_NORMAL; -+ writel(reg, dc->base + DC_WR_CH_CONF); -+} -+EXPORT_SYMBOL_GPL(ipu_dc_enable_channel); -+ -+void ipu_dc_disable_channel(struct ipu_dc *dc) -+{ -+ u32 val; -+ -+ val = readl(dc->base + DC_WR_CH_CONF); -+ val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; -+ writel(val, dc->base + DC_WR_CH_CONF); -+} -+EXPORT_SYMBOL_GPL(ipu_dc_disable_channel); -+ -+void ipu_dc_disable(struct ipu_soc *ipu) -+{ -+ struct ipu_dc_priv *priv = ipu->dc_priv; -+ -+ mutex_lock(&priv->mutex); -+ -+ priv->use_count--; -+ if (!priv->use_count) -+ ipu_module_disable(priv->ipu, IPU_CONF_DC_EN); -+ -+ if (priv->use_count < 0) -+ priv->use_count = 0; -+ -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dc_disable); -+ -+static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map, -+ int byte_num, int offset, int mask) -+{ -+ int ptr = map * 3 + byte_num; -+ u32 reg; -+ -+ reg = readl(priv->dc_reg + DC_MAP_CONF_VAL(ptr)); -+ reg &= ~(0xffff << (16 * (ptr & 0x1))); -+ reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1)); -+ writel(reg, priv->dc_reg + DC_MAP_CONF_VAL(ptr)); -+ -+ reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map)); -+ reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num))); -+ reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num)); -+ writel(reg, priv->dc_reg + DC_MAP_CONF_PTR(map)); -+} -+ -+static void ipu_dc_map_clear(struct ipu_dc_priv *priv, int map) -+{ -+ u32 reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map)); -+ -+ writel(reg & ~(0xffff << (16 * (map & 0x1))), -+ priv->dc_reg + DC_MAP_CONF_PTR(map)); -+} -+ -+struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel) -+{ -+ struct ipu_dc_priv *priv = ipu->dc_priv; -+ struct ipu_dc *dc; -+ -+ if (channel >= IPU_DC_NUM_CHANNELS) -+ return ERR_PTR(-ENODEV); -+ -+ dc = &priv->channels[channel]; -+ -+ mutex_lock(&priv->mutex); -+ -+ if (dc->in_use) { -+ mutex_unlock(&priv->mutex); -+ return ERR_PTR(-EBUSY); -+ } -+ -+ dc->in_use = true; -+ -+ mutex_unlock(&priv->mutex); -+ -+ return dc; -+} -+EXPORT_SYMBOL_GPL(ipu_dc_get); -+ -+void ipu_dc_put(struct ipu_dc *dc) -+{ -+ struct ipu_dc_priv *priv = dc->priv; -+ -+ mutex_lock(&priv->mutex); -+ dc->in_use = false; -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dc_put); -+ -+int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, -+ unsigned long base, unsigned long template_base) -+{ -+ struct ipu_dc_priv *priv; -+ static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c, -+ 0x78, 0, 0x94, 0xb4}; -+ int i; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ mutex_init(&priv->mutex); -+ -+ priv->dev = dev; -+ priv->ipu = ipu; -+ priv->dc_reg = devm_ioremap(dev, base, PAGE_SIZE); -+ priv->dc_tmpl_reg = devm_ioremap(dev, template_base, PAGE_SIZE); -+ if (!priv->dc_reg || !priv->dc_tmpl_reg) -+ return -ENOMEM; -+ -+ for (i = 0; i < IPU_DC_NUM_CHANNELS; i++) { -+ priv->channels[i].chno = i; -+ priv->channels[i].priv = priv; -+ priv->channels[i].base = priv->dc_reg + channel_offsets[i]; -+ } -+ -+ writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) | -+ DC_WR_CH_CONF_PROG_DI_ID, -+ priv->channels[1].base + DC_WR_CH_CONF); -+ writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(0), -+ priv->channels[5].base + DC_WR_CH_CONF); -+ -+ writel(DC_GEN_SYNC_1_6_SYNC | DC_GEN_SYNC_PRIORITY_1, -+ priv->dc_reg + DC_GEN); -+ -+ ipu->dc_priv = priv; -+ -+ dev_dbg(dev, "DC base: 0x%08lx template base: 0x%08lx\n", -+ base, template_base); -+ -+ /* rgb24 */ -+ ipu_dc_map_clear(priv, IPU_DC_MAP_RGB24); -+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 0, 7, 0xff); /* blue */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 1, 15, 0xff); /* green */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 2, 23, 0xff); /* red */ -+ -+ /* rgb565 */ -+ ipu_dc_map_clear(priv, IPU_DC_MAP_RGB565); -+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 0, 4, 0xf8); /* blue */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 1, 10, 0xfc); /* green */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 2, 15, 0xf8); /* red */ -+ -+ /* gbr24 */ -+ ipu_dc_map_clear(priv, IPU_DC_MAP_GBR24); -+ ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 2, 15, 0xff); /* green */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 1, 7, 0xff); /* blue */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 0, 23, 0xff); /* red */ -+ -+ /* bgr666 */ -+ ipu_dc_map_clear(priv, IPU_DC_MAP_BGR666); -+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 0, 5, 0xfc); /* blue */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 1, 11, 0xfc); /* green */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 2, 17, 0xfc); /* red */ -+ -+ /* lvds666 */ -+ ipu_dc_map_clear(priv, IPU_DC_MAP_LVDS666); -+ ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 0, 5, 0xfc); /* blue */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 1, 13, 0xfc); /* green */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 2, 21, 0xfc); /* red */ -+ -+ /* bgr24 */ -+ ipu_dc_map_clear(priv, IPU_DC_MAP_BGR24); -+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 2, 7, 0xff); /* red */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 1, 15, 0xff); /* green */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 0, 23, 0xff); /* blue */ -+ -+ return 0; -+} -+ -+void ipu_dc_exit(struct ipu_soc *ipu) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-di.c -@@ -0,0 +1,745 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#include <linux/export.h> -+#include <linux/module.h> -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/io.h> -+#include <linux/err.h> -+#include <linux/platform_device.h> -+ -+#include <video/imx-ipu-v3.h> -+#include "ipu-prv.h" -+ -+struct ipu_di { -+ void __iomem *base; -+ int id; -+ u32 module; -+ struct clk *clk_di; /* display input clock */ -+ struct clk *clk_ipu; /* IPU bus clock */ -+ struct clk *clk_di_pixel; /* resulting pixel clock */ -+ bool inuse; -+ struct ipu_soc *ipu; -+}; -+ -+static DEFINE_MUTEX(di_mutex); -+ -+struct di_sync_config { -+ int run_count; -+ int run_src; -+ int offset_count; -+ int offset_src; -+ int repeat_count; -+ int cnt_clr_src; -+ int cnt_polarity_gen_en; -+ int cnt_polarity_clr_src; -+ int cnt_polarity_trigger_src; -+ int cnt_up; -+ int cnt_down; -+}; -+ -+enum di_pins { -+ DI_PIN11 = 0, -+ DI_PIN12 = 1, -+ DI_PIN13 = 2, -+ DI_PIN14 = 3, -+ DI_PIN15 = 4, -+ DI_PIN16 = 5, -+ DI_PIN17 = 6, -+ DI_PIN_CS = 7, -+ -+ DI_PIN_SER_CLK = 0, -+ DI_PIN_SER_RS = 1, -+}; -+ -+enum di_sync_wave { -+ DI_SYNC_NONE = 0, -+ DI_SYNC_CLK = 1, -+ DI_SYNC_INT_HSYNC = 2, -+ DI_SYNC_HSYNC = 3, -+ DI_SYNC_VSYNC = 4, -+ DI_SYNC_DE = 6, -+ -+ DI_SYNC_CNT1 = 2, /* counter >= 2 only */ -+ DI_SYNC_CNT4 = 5, /* counter >= 5 only */ -+ DI_SYNC_CNT5 = 6, /* counter >= 6 only */ -+}; -+ -+#define SYNC_WAVE 0 -+ -+#define DI_GENERAL 0x0000 -+#define DI_BS_CLKGEN0 0x0004 -+#define DI_BS_CLKGEN1 0x0008 -+#define DI_SW_GEN0(gen) (0x000c + 4 * ((gen) - 1)) -+#define DI_SW_GEN1(gen) (0x0030 + 4 * ((gen) - 1)) -+#define DI_STP_REP(gen) (0x0148 + 4 * (((gen) - 1)/2)) -+#define DI_SYNC_AS_GEN 0x0054 -+#define DI_DW_GEN(gen) (0x0058 + 4 * (gen)) -+#define DI_DW_SET(gen, set) (0x0088 + 4 * ((gen) + 0xc * (set))) -+#define DI_SER_CONF 0x015c -+#define DI_SSC 0x0160 -+#define DI_POL 0x0164 -+#define DI_AW0 0x0168 -+#define DI_AW1 0x016c -+#define DI_SCR_CONF 0x0170 -+#define DI_STAT 0x0174 -+ -+#define DI_SW_GEN0_RUN_COUNT(x) ((x) << 19) -+#define DI_SW_GEN0_RUN_SRC(x) ((x) << 16) -+#define DI_SW_GEN0_OFFSET_COUNT(x) ((x) << 3) -+#define DI_SW_GEN0_OFFSET_SRC(x) ((x) << 0) -+ -+#define DI_SW_GEN1_CNT_POL_GEN_EN(x) ((x) << 29) -+#define DI_SW_GEN1_CNT_CLR_SRC(x) ((x) << 25) -+#define DI_SW_GEN1_CNT_POL_TRIGGER_SRC(x) ((x) << 12) -+#define DI_SW_GEN1_CNT_POL_CLR_SRC(x) ((x) << 9) -+#define DI_SW_GEN1_CNT_DOWN(x) ((x) << 16) -+#define DI_SW_GEN1_CNT_UP(x) (x) -+#define DI_SW_GEN1_AUTO_RELOAD (0x10000000) -+ -+#define DI_DW_GEN_ACCESS_SIZE_OFFSET 24 -+#define DI_DW_GEN_COMPONENT_SIZE_OFFSET 16 -+ -+#define DI_GEN_POLARITY_1 (1 << 0) -+#define DI_GEN_POLARITY_2 (1 << 1) -+#define DI_GEN_POLARITY_3 (1 << 2) -+#define DI_GEN_POLARITY_4 (1 << 3) -+#define DI_GEN_POLARITY_5 (1 << 4) -+#define DI_GEN_POLARITY_6 (1 << 5) -+#define DI_GEN_POLARITY_7 (1 << 6) -+#define DI_GEN_POLARITY_8 (1 << 7) -+#define DI_GEN_POLARITY_DISP_CLK (1 << 17) -+#define DI_GEN_DI_CLK_EXT (1 << 20) -+#define DI_GEN_DI_VSYNC_EXT (1 << 21) -+ -+#define DI_POL_DRDY_DATA_POLARITY (1 << 7) -+#define DI_POL_DRDY_POLARITY_15 (1 << 4) -+ -+#define DI_VSYNC_SEL_OFFSET 13 -+ -+static inline u32 ipu_di_read(struct ipu_di *di, unsigned offset) -+{ -+ return readl(di->base + offset); -+} -+ -+static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset) -+{ -+ writel(value, di->base + offset); -+} -+ -+static void ipu_di_data_wave_config(struct ipu_di *di, -+ int wave_gen, -+ int access_size, int component_size) -+{ -+ u32 reg; -+ reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) | -+ (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET); -+ ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); -+} -+ -+static void ipu_di_data_pin_config(struct ipu_di *di, int wave_gen, int di_pin, -+ int set, int up, int down) -+{ -+ u32 reg; -+ -+ reg = ipu_di_read(di, DI_DW_GEN(wave_gen)); -+ reg &= ~(0x3 << (di_pin * 2)); -+ reg |= set << (di_pin * 2); -+ ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); -+ -+ ipu_di_write(di, (down << 16) | up, DI_DW_SET(wave_gen, set)); -+} -+ -+static void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config, -+ int start, int count) -+{ -+ u32 reg; -+ int i; -+ -+ for (i = 0; i < count; i++) { -+ struct di_sync_config *c = &config[i]; -+ int wave_gen = start + i + 1; -+ -+ if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) || -+ (c->repeat_count >= 0x1000) || -+ (c->cnt_up >= 0x400) || -+ (c->cnt_down >= 0x400)) { -+ dev_err(di->ipu->dev, "DI%d counters out of range.\n", -+ di->id); -+ return; -+ } -+ -+ reg = DI_SW_GEN0_RUN_COUNT(c->run_count) | -+ DI_SW_GEN0_RUN_SRC(c->run_src) | -+ DI_SW_GEN0_OFFSET_COUNT(c->offset_count) | -+ DI_SW_GEN0_OFFSET_SRC(c->offset_src); -+ ipu_di_write(di, reg, DI_SW_GEN0(wave_gen)); -+ -+ reg = DI_SW_GEN1_CNT_POL_GEN_EN(c->cnt_polarity_gen_en) | -+ DI_SW_GEN1_CNT_CLR_SRC(c->cnt_clr_src) | -+ DI_SW_GEN1_CNT_POL_TRIGGER_SRC( -+ c->cnt_polarity_trigger_src) | -+ DI_SW_GEN1_CNT_POL_CLR_SRC(c->cnt_polarity_clr_src) | -+ DI_SW_GEN1_CNT_DOWN(c->cnt_down) | -+ DI_SW_GEN1_CNT_UP(c->cnt_up); -+ -+ /* Enable auto reload */ -+ if (c->repeat_count == 0) -+ reg |= DI_SW_GEN1_AUTO_RELOAD; -+ -+ ipu_di_write(di, reg, DI_SW_GEN1(wave_gen)); -+ -+ reg = ipu_di_read(di, DI_STP_REP(wave_gen)); -+ reg &= ~(0xffff << (16 * ((wave_gen - 1) & 0x1))); -+ reg |= c->repeat_count << (16 * ((wave_gen - 1) & 0x1)); -+ ipu_di_write(di, reg, DI_STP_REP(wave_gen)); -+ } -+} -+ -+static void ipu_di_sync_config_interlaced(struct ipu_di *di, -+ struct ipu_di_signal_cfg *sig) -+{ -+ u32 h_total = sig->mode.hactive + sig->mode.hsync_len + -+ sig->mode.hback_porch + sig->mode.hfront_porch; -+ u32 v_total = sig->mode.vactive + sig->mode.vsync_len + -+ sig->mode.vback_porch + sig->mode.vfront_porch; -+ struct di_sync_config cfg[] = { -+ { -+ /* 1: internal VSYNC for each frame */ -+ .run_count = v_total * 2 - 1, -+ .run_src = 3, /* == counter 7 */ -+ }, { -+ /* PIN2: HSYNC waveform */ -+ .run_count = h_total - 1, -+ .run_src = DI_SYNC_CLK, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_CLK, -+ .cnt_down = sig->mode.hsync_len * 2, -+ }, { -+ /* PIN3: VSYNC waveform */ -+ .run_count = v_total - 1, -+ .run_src = 4, /* == counter 7 */ -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = 4, /* == counter 7 */ -+ .cnt_down = sig->mode.vsync_len * 2, -+ .cnt_clr_src = DI_SYNC_CNT1, -+ }, { -+ /* 4: Field */ -+ .run_count = v_total / 2, -+ .run_src = DI_SYNC_HSYNC, -+ .offset_count = h_total / 2, -+ .offset_src = DI_SYNC_CLK, -+ .repeat_count = 2, -+ .cnt_clr_src = DI_SYNC_CNT1, -+ }, { -+ /* 5: Active lines */ -+ .run_src = DI_SYNC_HSYNC, -+ .offset_count = (sig->mode.vsync_len + -+ sig->mode.vback_porch) / 2, -+ .offset_src = DI_SYNC_HSYNC, -+ .repeat_count = sig->mode.vactive / 2, -+ .cnt_clr_src = DI_SYNC_CNT4, -+ }, { -+ /* 6: Active pixel, referenced by DC */ -+ .run_src = DI_SYNC_CLK, -+ .offset_count = sig->mode.hsync_len + -+ sig->mode.hback_porch, -+ .offset_src = DI_SYNC_CLK, -+ .repeat_count = sig->mode.hactive, -+ .cnt_clr_src = DI_SYNC_CNT5, -+ }, { -+ /* 7: Half line HSYNC */ -+ .run_count = h_total / 2 - 1, -+ .run_src = DI_SYNC_CLK, -+ } -+ }; -+ -+ ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); -+ -+ ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF); -+} -+ -+static void ipu_di_sync_config_noninterlaced(struct ipu_di *di, -+ struct ipu_di_signal_cfg *sig, int div) -+{ -+ u32 h_total = sig->mode.hactive + sig->mode.hsync_len + -+ sig->mode.hback_porch + sig->mode.hfront_porch; -+ u32 v_total = sig->mode.vactive + sig->mode.vsync_len + -+ sig->mode.vback_porch + sig->mode.vfront_porch; -+ struct di_sync_config cfg[] = { -+ { -+ /* 1: INT_HSYNC */ -+ .run_count = h_total - 1, -+ .run_src = DI_SYNC_CLK, -+ } , { -+ /* PIN2: HSYNC */ -+ .run_count = h_total - 1, -+ .run_src = DI_SYNC_CLK, -+ .offset_count = div * sig->v_to_h_sync, -+ .offset_src = DI_SYNC_CLK, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_CLK, -+ .cnt_down = sig->mode.hsync_len * 2, -+ } , { -+ /* PIN3: VSYNC */ -+ .run_count = v_total - 1, -+ .run_src = DI_SYNC_INT_HSYNC, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, -+ .cnt_down = sig->mode.vsync_len * 2, -+ } , { -+ /* 4: Line Active */ -+ .run_src = DI_SYNC_HSYNC, -+ .offset_count = sig->mode.vsync_len + -+ sig->mode.vback_porch, -+ .offset_src = DI_SYNC_HSYNC, -+ .repeat_count = sig->mode.vactive, -+ .cnt_clr_src = DI_SYNC_VSYNC, -+ } , { -+ /* 5: Pixel Active, referenced by DC */ -+ .run_src = DI_SYNC_CLK, -+ .offset_count = sig->mode.hsync_len + -+ sig->mode.hback_porch, -+ .offset_src = DI_SYNC_CLK, -+ .repeat_count = sig->mode.hactive, -+ .cnt_clr_src = 5, /* Line Active */ -+ } , { -+ /* unused */ -+ } , { -+ /* unused */ -+ } , { -+ /* unused */ -+ } , { -+ /* unused */ -+ }, -+ }; -+ /* can't use #7 and #8 for line active and pixel active counters */ -+ struct di_sync_config cfg_vga[] = { -+ { -+ /* 1: INT_HSYNC */ -+ .run_count = h_total - 1, -+ .run_src = DI_SYNC_CLK, -+ } , { -+ /* 2: VSYNC */ -+ .run_count = v_total - 1, -+ .run_src = DI_SYNC_INT_HSYNC, -+ } , { -+ /* 3: Line Active */ -+ .run_src = DI_SYNC_INT_HSYNC, -+ .offset_count = sig->mode.vsync_len + -+ sig->mode.vback_porch, -+ .offset_src = DI_SYNC_INT_HSYNC, -+ .repeat_count = sig->mode.vactive, -+ .cnt_clr_src = 3 /* VSYNC */, -+ } , { -+ /* PIN4: HSYNC for VGA via TVEv2 on TQ MBa53 */ -+ .run_count = h_total - 1, -+ .run_src = DI_SYNC_CLK, -+ .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ -+ .offset_src = DI_SYNC_CLK, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_CLK, -+ .cnt_down = sig->mode.hsync_len * 2, -+ } , { -+ /* 5: Pixel Active signal to DC */ -+ .run_src = DI_SYNC_CLK, -+ .offset_count = sig->mode.hsync_len + -+ sig->mode.hback_porch, -+ .offset_src = DI_SYNC_CLK, -+ .repeat_count = sig->mode.hactive, -+ .cnt_clr_src = 4, /* Line Active */ -+ } , { -+ /* PIN6: VSYNC for VGA via TVEv2 on TQ MBa53 */ -+ .run_count = v_total - 1, -+ .run_src = DI_SYNC_INT_HSYNC, -+ .offset_count = 1, /* magic value from Freescale TVE driver */ -+ .offset_src = DI_SYNC_INT_HSYNC, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, -+ .cnt_down = sig->mode.vsync_len * 2, -+ } , { -+ /* PIN4: HSYNC for VGA via TVEv2 on i.MX53-QSB */ -+ .run_count = h_total - 1, -+ .run_src = DI_SYNC_CLK, -+ .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ -+ .offset_src = DI_SYNC_CLK, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_CLK, -+ .cnt_down = sig->mode.hsync_len * 2, -+ } , { -+ /* PIN6: VSYNC for VGA via TVEv2 on i.MX53-QSB */ -+ .run_count = v_total - 1, -+ .run_src = DI_SYNC_INT_HSYNC, -+ .offset_count = 1, /* magic value from Freescale TVE driver */ -+ .offset_src = DI_SYNC_INT_HSYNC, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, -+ .cnt_down = sig->mode.vsync_len * 2, -+ } , { -+ /* unused */ -+ }, -+ }; -+ -+ ipu_di_write(di, v_total - 1, DI_SCR_CONF); -+ if (sig->hsync_pin == 2 && sig->vsync_pin == 3) -+ ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); -+ else -+ ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga)); -+} -+ -+static void ipu_di_config_clock(struct ipu_di *di, -+ const struct ipu_di_signal_cfg *sig) -+{ -+ struct clk *clk; -+ unsigned clkgen0; -+ uint32_t val; -+ -+ if (sig->clkflags & IPU_DI_CLKMODE_EXT) { -+ /* -+ * CLKMODE_EXT means we must use the DI clock: this is -+ * needed for things like LVDS which needs to feed the -+ * DI and LDB with the same pixel clock. -+ */ -+ clk = di->clk_di; -+ -+ if (sig->clkflags & IPU_DI_CLKMODE_SYNC) { -+ /* -+ * CLKMODE_SYNC means that we want the DI to be -+ * clocked at the same rate as the parent clock. -+ * This is needed (eg) for LDB which needs to be -+ * fed with the same pixel clock. We assume that -+ * the LDB clock has already been set correctly. -+ */ -+ clkgen0 = 1 << 4; -+ } else { -+ /* -+ * We can use the divider. We should really have -+ * a flag here indicating whether the bridge can -+ * cope with a fractional divider or not. For the -+ * time being, let's go for simplicitly and -+ * reliability. -+ */ -+ unsigned long in_rate; -+ unsigned div; -+ -+ clk_set_rate(clk, sig->mode.pixelclock); -+ -+ in_rate = clk_get_rate(clk); -+ div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock); -+ div = clamp(div, 1U, 255U); -+ -+ clkgen0 = div << 4; -+ } -+ } else { -+ /* -+ * For other interfaces, we can arbitarily select between -+ * the DI specific clock and the internal IPU clock. See -+ * DI_GENERAL bit 20. We select the IPU clock if it can -+ * give us a clock rate within 1% of the requested frequency, -+ * otherwise we use the DI clock. -+ */ -+ unsigned long rate, clkrate; -+ unsigned div, error; -+ -+ clkrate = clk_get_rate(di->clk_ipu); -+ div = DIV_ROUND_CLOSEST(clkrate, sig->mode.pixelclock); -+ div = clamp(div, 1U, 255U); -+ rate = clkrate / div; -+ -+ error = rate / (sig->mode.pixelclock / 1000); -+ -+ dev_dbg(di->ipu->dev, " IPU clock can give %lu with divider %u, error %d.%u%%\n", -+ rate, div, (signed)(error - 1000) / 10, error % 10); -+ -+ /* Allow a 1% error */ -+ if (error < 1010 && error >= 990) { -+ clk = di->clk_ipu; -+ -+ clkgen0 = div << 4; -+ } else { -+ unsigned long in_rate; -+ unsigned div; -+ -+ clk = di->clk_di; -+ -+ clk_set_rate(clk, sig->mode.pixelclock); -+ -+ in_rate = clk_get_rate(clk); -+ div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock); -+ div = clamp(div, 1U, 255U); -+ -+ clkgen0 = div << 4; -+ } -+ } -+ -+ di->clk_di_pixel = clk; -+ -+ /* Set the divider */ -+ ipu_di_write(di, clkgen0, DI_BS_CLKGEN0); -+ -+ /* -+ * Set the high/low periods. Bits 24:16 give us the falling edge, -+ * and bits 8:0 give the rising edge. LSB is fraction, and is -+ * based on the divider above. We want a 50% duty cycle, so set -+ * the falling edge to be half the divider. -+ */ -+ ipu_di_write(di, (clkgen0 >> 4) << 16, DI_BS_CLKGEN1); -+ -+ /* Finally select the input clock */ -+ val = ipu_di_read(di, DI_GENERAL) & ~DI_GEN_DI_CLK_EXT; -+ if (clk == di->clk_di) -+ val |= DI_GEN_DI_CLK_EXT; -+ ipu_di_write(di, val, DI_GENERAL); -+ -+ dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, %luHz\n", -+ sig->mode.pixelclock, -+ clk_get_rate(di->clk_ipu), -+ clk_get_rate(di->clk_di), -+ clk == di->clk_di ? "DI" : "IPU", -+ clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4)); -+} -+ -+/* -+ * This function is called to adjust a video mode to IPU restrictions. -+ * It is meant to be called from drm crtc mode_fixup() methods. -+ */ -+int ipu_di_adjust_videomode(struct ipu_di *di, struct videomode *mode) -+{ -+ u32 diff; -+ -+ if (mode->vfront_porch >= 2) -+ return 0; -+ -+ diff = 2 - mode->vfront_porch; -+ -+ if (mode->vback_porch >= diff) { -+ mode->vfront_porch = 2; -+ mode->vback_porch -= diff; -+ } else if (mode->vsync_len > diff) { -+ mode->vfront_porch = 2; -+ mode->vsync_len = mode->vsync_len - diff; -+ } else { -+ dev_warn(di->ipu->dev, "failed to adjust videomode\n"); -+ return -EINVAL; -+ } -+ -+ dev_dbg(di->ipu->dev, "videomode adapted for IPU restrictions\n"); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_di_adjust_videomode); -+ -+static u32 ipu_di_gen_polarity(int pin) -+{ -+ switch (pin) { -+ case 1: -+ return DI_GEN_POLARITY_1; -+ case 2: -+ return DI_GEN_POLARITY_2; -+ case 3: -+ return DI_GEN_POLARITY_3; -+ case 4: -+ return DI_GEN_POLARITY_4; -+ case 5: -+ return DI_GEN_POLARITY_5; -+ case 6: -+ return DI_GEN_POLARITY_6; -+ case 7: -+ return DI_GEN_POLARITY_7; -+ case 8: -+ return DI_GEN_POLARITY_8; -+ } -+ return 0; -+} -+ -+int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) -+{ -+ u32 reg; -+ u32 di_gen, vsync_cnt; -+ u32 div; -+ -+ dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n", -+ di->id, sig->mode.hactive, sig->mode.vactive); -+ -+ dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n", -+ clk_get_rate(di->clk_ipu), -+ clk_get_rate(di->clk_di), -+ sig->mode.pixelclock); -+ -+ mutex_lock(&di_mutex); -+ -+ ipu_di_config_clock(di, sig); -+ -+ div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff; -+ div = div / 16; /* Now divider is integer portion */ -+ -+ /* Setup pixel clock timing */ -+ /* Down time is half of period */ -+ ipu_di_write(di, (div << 16), DI_BS_CLKGEN1); -+ -+ ipu_di_data_wave_config(di, SYNC_WAVE, div - 1, div - 1); -+ ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div * 2); -+ -+ di_gen = ipu_di_read(di, DI_GENERAL) & DI_GEN_DI_CLK_EXT; -+ di_gen |= DI_GEN_DI_VSYNC_EXT; -+ -+ if (sig->mode.flags & DISPLAY_FLAGS_INTERLACED) { -+ ipu_di_sync_config_interlaced(di, sig); -+ -+ /* set y_sel = 1 */ -+ di_gen |= 0x10000000; -+ -+ vsync_cnt = 3; -+ } else { -+ ipu_di_sync_config_noninterlaced(di, sig, div); -+ -+ vsync_cnt = 3; -+ if (di->id == 1) -+ /* -+ * TODO: change only for TVEv2, parallel display -+ * uses pin 2 / 3 -+ */ -+ if (!(sig->hsync_pin == 2 && sig->vsync_pin == 3)) -+ vsync_cnt = 6; -+ } -+ -+ if (sig->mode.flags & DISPLAY_FLAGS_HSYNC_HIGH) -+ di_gen |= ipu_di_gen_polarity(sig->hsync_pin); -+ if (sig->mode.flags & DISPLAY_FLAGS_VSYNC_HIGH) -+ di_gen |= ipu_di_gen_polarity(sig->vsync_pin); -+ -+ if (sig->clk_pol) -+ di_gen |= DI_GEN_POLARITY_DISP_CLK; -+ -+ ipu_di_write(di, di_gen, DI_GENERAL); -+ -+ ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002, -+ DI_SYNC_AS_GEN); -+ -+ reg = ipu_di_read(di, DI_POL); -+ reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15); -+ -+ if (sig->enable_pol) -+ reg |= DI_POL_DRDY_POLARITY_15; -+ if (sig->data_pol) -+ reg |= DI_POL_DRDY_DATA_POLARITY; -+ -+ ipu_di_write(di, reg, DI_POL); -+ -+ mutex_unlock(&di_mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel); -+ -+int ipu_di_enable(struct ipu_di *di) -+{ -+ int ret; -+ -+ WARN_ON(IS_ERR(di->clk_di_pixel)); -+ -+ ret = clk_prepare_enable(di->clk_di_pixel); -+ if (ret) -+ return ret; -+ -+ ipu_module_enable(di->ipu, di->module); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_di_enable); -+ -+int ipu_di_disable(struct ipu_di *di) -+{ -+ WARN_ON(IS_ERR(di->clk_di_pixel)); -+ -+ ipu_module_disable(di->ipu, di->module); -+ -+ clk_disable_unprepare(di->clk_di_pixel); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_di_disable); -+ -+int ipu_di_get_num(struct ipu_di *di) -+{ -+ return di->id; -+} -+EXPORT_SYMBOL_GPL(ipu_di_get_num); -+ -+static DEFINE_MUTEX(ipu_di_lock); -+ -+struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp) -+{ -+ struct ipu_di *di; -+ -+ if (disp > 1) -+ return ERR_PTR(-EINVAL); -+ -+ di = ipu->di_priv[disp]; -+ -+ mutex_lock(&ipu_di_lock); -+ -+ if (di->inuse) { -+ di = ERR_PTR(-EBUSY); -+ goto out; -+ } -+ -+ di->inuse = true; -+out: -+ mutex_unlock(&ipu_di_lock); -+ -+ return di; -+} -+EXPORT_SYMBOL_GPL(ipu_di_get); -+ -+void ipu_di_put(struct ipu_di *di) -+{ -+ mutex_lock(&ipu_di_lock); -+ -+ di->inuse = false; -+ -+ mutex_unlock(&ipu_di_lock); -+} -+EXPORT_SYMBOL_GPL(ipu_di_put); -+ -+int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, -+ unsigned long base, -+ u32 module, struct clk *clk_ipu) -+{ -+ struct ipu_di *di; -+ -+ if (id > 1) -+ return -ENODEV; -+ -+ di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL); -+ if (!di) -+ return -ENOMEM; -+ -+ ipu->di_priv[id] = di; -+ -+ di->clk_di = devm_clk_get(dev, id ? "di1" : "di0"); -+ if (IS_ERR(di->clk_di)) -+ return PTR_ERR(di->clk_di); -+ -+ di->module = module; -+ di->id = id; -+ di->clk_ipu = clk_ipu; -+ di->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!di->base) -+ return -ENOMEM; -+ -+ ipu_di_write(di, 0x10, DI_BS_CLKGEN0); -+ -+ dev_dbg(dev, "DI%d base: 0x%08lx remapped to %p\n", -+ id, base, di->base); -+ di->inuse = false; -+ di->ipu = ipu; -+ -+ return 0; -+} -+ -+void ipu_di_exit(struct ipu_soc *ipu, int id) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-dmfc.c -@@ -0,0 +1,214 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#include <linux/export.h> -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/io.h> -+ -+#include <video/imx-ipu-v3.h> -+#include "ipu-prv.h" -+ -+#define DMFC_RD_CHAN 0x0000 -+#define DMFC_WR_CHAN 0x0004 -+#define DMFC_WR_CHAN_DEF 0x0008 -+#define DMFC_DP_CHAN 0x000c -+#define DMFC_DP_CHAN_DEF 0x0010 -+#define DMFC_GENERAL1 0x0014 -+#define DMFC_GENERAL2 0x0018 -+#define DMFC_IC_CTRL 0x001c -+#define DMFC_WR_CHAN_ALT 0x0020 -+#define DMFC_WR_CHAN_DEF_ALT 0x0024 -+#define DMFC_DP_CHAN_ALT 0x0028 -+#define DMFC_DP_CHAN_DEF_ALT 0x002c -+#define DMFC_GENERAL1_ALT 0x0030 -+#define DMFC_STAT 0x0034 -+ -+#define DMFC_WR_CHAN_1_28 0 -+#define DMFC_WR_CHAN_2_41 8 -+#define DMFC_WR_CHAN_1C_42 16 -+#define DMFC_WR_CHAN_2C_43 24 -+ -+#define DMFC_DP_CHAN_5B_23 0 -+#define DMFC_DP_CHAN_5F_27 8 -+#define DMFC_DP_CHAN_6B_24 16 -+#define DMFC_DP_CHAN_6F_29 24 -+ -+struct dmfc_channel_data { -+ int ipu_channel; -+ unsigned long channel_reg; -+ unsigned long shift; -+ unsigned eot_shift; -+ unsigned max_fifo_lines; -+}; -+ -+static const struct dmfc_channel_data dmfcdata[] = { -+ { -+ .ipu_channel = IPUV3_CHANNEL_MEM_BG_SYNC, -+ .channel_reg = DMFC_DP_CHAN, -+ .shift = DMFC_DP_CHAN_5B_23, -+ .eot_shift = 20, -+ .max_fifo_lines = 3, -+ }, { -+ .ipu_channel = 24, -+ .channel_reg = DMFC_DP_CHAN, -+ .shift = DMFC_DP_CHAN_6B_24, -+ .eot_shift = 22, -+ .max_fifo_lines = 1, -+ }, { -+ .ipu_channel = IPUV3_CHANNEL_MEM_FG_SYNC, -+ .channel_reg = DMFC_DP_CHAN, -+ .shift = DMFC_DP_CHAN_5F_27, -+ .eot_shift = 21, -+ .max_fifo_lines = 2, -+ }, { -+ .ipu_channel = IPUV3_CHANNEL_MEM_DC_SYNC, -+ .channel_reg = DMFC_WR_CHAN, -+ .shift = DMFC_WR_CHAN_1_28, -+ .eot_shift = 16, -+ .max_fifo_lines = 2, -+ }, { -+ .ipu_channel = 29, -+ .channel_reg = DMFC_DP_CHAN, -+ .shift = DMFC_DP_CHAN_6F_29, -+ .eot_shift = 23, -+ .max_fifo_lines = 1, -+ }, -+}; -+ -+#define DMFC_NUM_CHANNELS ARRAY_SIZE(dmfcdata) -+ -+struct ipu_dmfc_priv; -+ -+struct dmfc_channel { -+ unsigned slots; -+ struct ipu_soc *ipu; -+ struct ipu_dmfc_priv *priv; -+ const struct dmfc_channel_data *data; -+}; -+ -+struct ipu_dmfc_priv { -+ struct ipu_soc *ipu; -+ struct device *dev; -+ struct dmfc_channel channels[DMFC_NUM_CHANNELS]; -+ struct mutex mutex; -+ void __iomem *base; -+ int use_count; -+}; -+ -+int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc) -+{ -+ struct ipu_dmfc_priv *priv = dmfc->priv; -+ mutex_lock(&priv->mutex); -+ -+ if (!priv->use_count) -+ ipu_module_enable(priv->ipu, IPU_CONF_DMFC_EN); -+ -+ priv->use_count++; -+ -+ mutex_unlock(&priv->mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel); -+ -+void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc) -+{ -+ struct ipu_dmfc_priv *priv = dmfc->priv; -+ -+ mutex_lock(&priv->mutex); -+ -+ priv->use_count--; -+ -+ if (!priv->use_count) -+ ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN); -+ -+ if (priv->use_count < 0) -+ priv->use_count = 0; -+ -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dmfc_disable_channel); -+ -+void ipu_dmfc_config_wait4eot(struct dmfc_channel *dmfc, int width) -+{ -+ struct ipu_dmfc_priv *priv = dmfc->priv; -+ u32 dmfc_gen1; -+ -+ mutex_lock(&priv->mutex); -+ -+ dmfc_gen1 = readl(priv->base + DMFC_GENERAL1); -+ -+ if ((dmfc->slots * 64 * 4) / width > dmfc->data->max_fifo_lines) -+ dmfc_gen1 |= 1 << dmfc->data->eot_shift; -+ else -+ dmfc_gen1 &= ~(1 << dmfc->data->eot_shift); -+ -+ writel(dmfc_gen1, priv->base + DMFC_GENERAL1); -+ -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dmfc_config_wait4eot); -+ -+struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipu_channel) -+{ -+ struct ipu_dmfc_priv *priv = ipu->dmfc_priv; -+ int i; -+ -+ for (i = 0; i < DMFC_NUM_CHANNELS; i++) -+ if (dmfcdata[i].ipu_channel == ipu_channel) -+ return &priv->channels[i]; -+ return ERR_PTR(-ENODEV); -+} -+EXPORT_SYMBOL_GPL(ipu_dmfc_get); -+ -+void ipu_dmfc_put(struct dmfc_channel *dmfc) -+{ -+} -+EXPORT_SYMBOL_GPL(ipu_dmfc_put); -+ -+int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, -+ struct clk *ipu_clk) -+{ -+ struct ipu_dmfc_priv *priv; -+ int i; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ priv->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!priv->base) -+ return -ENOMEM; -+ -+ priv->dev = dev; -+ priv->ipu = ipu; -+ mutex_init(&priv->mutex); -+ -+ ipu->dmfc_priv = priv; -+ -+ for (i = 0; i < DMFC_NUM_CHANNELS; i++) { -+ priv->channels[i].priv = priv; -+ priv->channels[i].ipu = ipu; -+ priv->channels[i].data = &dmfcdata[i]; -+ -+ if (dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_BG_SYNC || -+ dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_FG_SYNC || -+ dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_DC_SYNC) -+ priv->channels[i].slots = 2; -+ } -+ -+ writel(0x00000050, priv->base + DMFC_WR_CHAN); -+ writel(0x00005654, priv->base + DMFC_DP_CHAN); -+ writel(0x202020f6, priv->base + DMFC_WR_CHAN_DEF); -+ writel(0x2020f6f6, priv->base + DMFC_DP_CHAN_DEF); -+ writel(0x00000003, priv->base + DMFC_GENERAL1); -+ -+ return 0; -+} -+ -+void ipu_dmfc_exit(struct ipu_soc *ipu) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-dp.c -@@ -0,0 +1,357 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#include <linux/export.h> -+#include <linux/kernel.h> -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/io.h> -+#include <linux/err.h> -+ -+#include <video/imx-ipu-v3.h> -+#include "ipu-prv.h" -+ -+#define DP_SYNC 0 -+#define DP_ASYNC0 0x60 -+#define DP_ASYNC1 0xBC -+ -+#define DP_COM_CONF 0x0 -+#define DP_GRAPH_WIND_CTRL 0x0004 -+#define DP_FG_POS 0x0008 -+#define DP_CSC_A_0 0x0044 -+#define DP_CSC_A_1 0x0048 -+#define DP_CSC_A_2 0x004C -+#define DP_CSC_A_3 0x0050 -+#define DP_CSC_0 0x0054 -+#define DP_CSC_1 0x0058 -+ -+#define DP_COM_CONF_FG_EN (1 << 0) -+#define DP_COM_CONF_GWSEL (1 << 1) -+#define DP_COM_CONF_GWAM (1 << 2) -+#define DP_COM_CONF_GWCKE (1 << 3) -+#define DP_COM_CONF_CSC_DEF_MASK (3 << 8) -+#define DP_COM_CONF_CSC_DEF_OFFSET 8 -+#define DP_COM_CONF_CSC_DEF_FG (3 << 8) -+#define DP_COM_CONF_CSC_DEF_BG (2 << 8) -+#define DP_COM_CONF_CSC_DEF_BOTH (1 << 8) -+ -+#define IPUV3_NUM_FLOWS 3 -+ -+struct ipu_dp_priv; -+ -+struct ipu_dp { -+ u32 flow; -+ bool in_use; -+ bool foreground; -+ enum ipu_color_space in_cs; -+}; -+ -+struct ipu_flow { -+ struct ipu_dp foreground; -+ struct ipu_dp background; -+ enum ipu_color_space out_cs; -+ void __iomem *base; -+ struct ipu_dp_priv *priv; -+}; -+ -+struct ipu_dp_priv { -+ struct ipu_soc *ipu; -+ struct device *dev; -+ void __iomem *base; -+ struct ipu_flow flow[IPUV3_NUM_FLOWS]; -+ struct mutex mutex; -+ int use_count; -+}; -+ -+static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1}; -+ -+static inline struct ipu_flow *to_flow(struct ipu_dp *dp) -+{ -+ if (dp->foreground) -+ return container_of(dp, struct ipu_flow, foreground); -+ else -+ return container_of(dp, struct ipu_flow, background); -+} -+ -+int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, -+ u8 alpha, bool bg_chan) -+{ -+ struct ipu_flow *flow = to_flow(dp); -+ struct ipu_dp_priv *priv = flow->priv; -+ u32 reg; -+ -+ mutex_lock(&priv->mutex); -+ -+ reg = readl(flow->base + DP_COM_CONF); -+ if (bg_chan) -+ reg &= ~DP_COM_CONF_GWSEL; -+ else -+ reg |= DP_COM_CONF_GWSEL; -+ writel(reg, flow->base + DP_COM_CONF); -+ -+ if (enable) { -+ reg = readl(flow->base + DP_GRAPH_WIND_CTRL) & 0x00FFFFFFL; -+ writel(reg | ((u32) alpha << 24), -+ flow->base + DP_GRAPH_WIND_CTRL); -+ -+ reg = readl(flow->base + DP_COM_CONF); -+ writel(reg | DP_COM_CONF_GWAM, flow->base + DP_COM_CONF); -+ } else { -+ reg = readl(flow->base + DP_COM_CONF); -+ writel(reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF); -+ } -+ -+ ipu_srm_dp_update(priv->ipu, true); -+ -+ mutex_unlock(&priv->mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha); -+ -+int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos) -+{ -+ struct ipu_flow *flow = to_flow(dp); -+ struct ipu_dp_priv *priv = flow->priv; -+ -+ writel((x_pos << 16) | y_pos, flow->base + DP_FG_POS); -+ -+ ipu_srm_dp_update(priv->ipu, true); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos); -+ -+static void ipu_dp_csc_init(struct ipu_flow *flow, -+ enum ipu_color_space in, -+ enum ipu_color_space out, -+ u32 place) -+{ -+ u32 reg; -+ -+ reg = readl(flow->base + DP_COM_CONF); -+ reg &= ~DP_COM_CONF_CSC_DEF_MASK; -+ -+ if (in == out) { -+ writel(reg, flow->base + DP_COM_CONF); -+ return; -+ } -+ -+ if (in == IPUV3_COLORSPACE_RGB && out == IPUV3_COLORSPACE_YUV) { -+ writel(0x099 | (0x12d << 16), flow->base + DP_CSC_A_0); -+ writel(0x03a | (0x3a9 << 16), flow->base + DP_CSC_A_1); -+ writel(0x356 | (0x100 << 16), flow->base + DP_CSC_A_2); -+ writel(0x100 | (0x329 << 16), flow->base + DP_CSC_A_3); -+ writel(0x3d6 | (0x0000 << 16) | (2 << 30), -+ flow->base + DP_CSC_0); -+ writel(0x200 | (2 << 14) | (0x200 << 16) | (2 << 30), -+ flow->base + DP_CSC_1); -+ } else { -+ writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0); -+ writel(0x0cc | (0x095 << 16), flow->base + DP_CSC_A_1); -+ writel(0x3ce | (0x398 << 16), flow->base + DP_CSC_A_2); -+ writel(0x095 | (0x0ff << 16), flow->base + DP_CSC_A_3); -+ writel(0x000 | (0x3e42 << 16) | (1 << 30), -+ flow->base + DP_CSC_0); -+ writel(0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30), -+ flow->base + DP_CSC_1); -+ } -+ -+ reg |= place; -+ -+ writel(reg, flow->base + DP_COM_CONF); -+} -+ -+int ipu_dp_setup_channel(struct ipu_dp *dp, -+ enum ipu_color_space in, -+ enum ipu_color_space out) -+{ -+ struct ipu_flow *flow = to_flow(dp); -+ struct ipu_dp_priv *priv = flow->priv; -+ -+ mutex_lock(&priv->mutex); -+ -+ dp->in_cs = in; -+ -+ if (!dp->foreground) -+ flow->out_cs = out; -+ -+ if (flow->foreground.in_cs == flow->background.in_cs) { -+ /* -+ * foreground and background are of same colorspace, put -+ * colorspace converter after combining unit. -+ */ -+ ipu_dp_csc_init(flow, flow->foreground.in_cs, flow->out_cs, -+ DP_COM_CONF_CSC_DEF_BOTH); -+ } else { -+ if (flow->foreground.in_cs == IPUV3_COLORSPACE_UNKNOWN || -+ flow->foreground.in_cs == flow->out_cs) -+ /* -+ * foreground identical to output, apply color -+ * conversion on background -+ */ -+ ipu_dp_csc_init(flow, flow->background.in_cs, -+ flow->out_cs, DP_COM_CONF_CSC_DEF_BG); -+ else -+ ipu_dp_csc_init(flow, flow->foreground.in_cs, -+ flow->out_cs, DP_COM_CONF_CSC_DEF_FG); -+ } -+ -+ ipu_srm_dp_update(priv->ipu, true); -+ -+ mutex_unlock(&priv->mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_setup_channel); -+ -+int ipu_dp_enable(struct ipu_soc *ipu) -+{ -+ struct ipu_dp_priv *priv = ipu->dp_priv; -+ -+ mutex_lock(&priv->mutex); -+ -+ if (!priv->use_count) -+ ipu_module_enable(priv->ipu, IPU_CONF_DP_EN); -+ -+ priv->use_count++; -+ -+ mutex_unlock(&priv->mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_enable); -+ -+int ipu_dp_enable_channel(struct ipu_dp *dp) -+{ -+ struct ipu_flow *flow = to_flow(dp); -+ struct ipu_dp_priv *priv = flow->priv; -+ u32 reg; -+ -+ if (!dp->foreground) -+ return 0; -+ -+ mutex_lock(&priv->mutex); -+ -+ reg = readl(flow->base + DP_COM_CONF); -+ reg |= DP_COM_CONF_FG_EN; -+ writel(reg, flow->base + DP_COM_CONF); -+ -+ ipu_srm_dp_update(priv->ipu, true); -+ -+ mutex_unlock(&priv->mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_enable_channel); -+ -+void ipu_dp_disable_channel(struct ipu_dp *dp, bool sync) -+{ -+ struct ipu_flow *flow = to_flow(dp); -+ struct ipu_dp_priv *priv = flow->priv; -+ u32 reg, csc; -+ -+ dp->in_cs = IPUV3_COLORSPACE_UNKNOWN; -+ -+ if (!dp->foreground) -+ return; -+ -+ mutex_lock(&priv->mutex); -+ -+ reg = readl(flow->base + DP_COM_CONF); -+ csc = reg & DP_COM_CONF_CSC_DEF_MASK; -+ reg &= ~DP_COM_CONF_CSC_DEF_MASK; -+ if (csc == DP_COM_CONF_CSC_DEF_BOTH || csc == DP_COM_CONF_CSC_DEF_BG) -+ reg |= DP_COM_CONF_CSC_DEF_BG; -+ -+ reg &= ~DP_COM_CONF_FG_EN; -+ writel(reg, flow->base + DP_COM_CONF); -+ -+ writel(0, flow->base + DP_FG_POS); -+ ipu_srm_dp_update(priv->ipu, sync); -+ -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dp_disable_channel); -+ -+void ipu_dp_disable(struct ipu_soc *ipu) -+{ -+ struct ipu_dp_priv *priv = ipu->dp_priv; -+ -+ mutex_lock(&priv->mutex); -+ -+ priv->use_count--; -+ -+ if (!priv->use_count) -+ ipu_module_disable(priv->ipu, IPU_CONF_DP_EN); -+ -+ if (priv->use_count < 0) -+ priv->use_count = 0; -+ -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dp_disable); -+ -+struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow) -+{ -+ struct ipu_dp_priv *priv = ipu->dp_priv; -+ struct ipu_dp *dp; -+ -+ if ((flow >> 1) >= IPUV3_NUM_FLOWS) -+ return ERR_PTR(-EINVAL); -+ -+ if (flow & 1) -+ dp = &priv->flow[flow >> 1].foreground; -+ else -+ dp = &priv->flow[flow >> 1].background; -+ -+ if (dp->in_use) -+ return ERR_PTR(-EBUSY); -+ -+ dp->in_use = true; -+ -+ return dp; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_get); -+ -+void ipu_dp_put(struct ipu_dp *dp) -+{ -+ dp->in_use = false; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_put); -+ -+int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base) -+{ -+ struct ipu_dp_priv *priv; -+ int i; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ priv->dev = dev; -+ priv->ipu = ipu; -+ -+ ipu->dp_priv = priv; -+ -+ priv->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!priv->base) -+ return -ENOMEM; -+ -+ mutex_init(&priv->mutex); -+ -+ for (i = 0; i < IPUV3_NUM_FLOWS; i++) { -+ priv->flow[i].background.in_cs = IPUV3_COLORSPACE_UNKNOWN; -+ priv->flow[i].foreground.in_cs = IPUV3_COLORSPACE_UNKNOWN; -+ priv->flow[i].foreground.foreground = true; -+ priv->flow[i].base = priv->base + ipu_dp_flow_base[i]; -+ priv->flow[i].priv = priv; -+ } -+ -+ return 0; -+} -+ -+void ipu_dp_exit(struct ipu_soc *ipu) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-ic.c -@@ -0,0 +1,761 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (C) 2012-2014 Mentor Graphics Inc. -+ * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. -+ */ -+ -+#include <linux/types.h> -+#include <linux/init.h> -+#include <linux/errno.h> -+#include <linux/spinlock.h> -+#include <linux/bitrev.h> -+#include <linux/io.h> -+#include <linux/err.h> -+#include <linux/sizes.h> -+#include "ipu-prv.h" -+ -+/* IC Register Offsets */ -+#define IC_CONF 0x0000 -+#define IC_PRP_ENC_RSC 0x0004 -+#define IC_PRP_VF_RSC 0x0008 -+#define IC_PP_RSC 0x000C -+#define IC_CMBP_1 0x0010 -+#define IC_CMBP_2 0x0014 -+#define IC_IDMAC_1 0x0018 -+#define IC_IDMAC_2 0x001C -+#define IC_IDMAC_3 0x0020 -+#define IC_IDMAC_4 0x0024 -+ -+/* IC Register Fields */ -+#define IC_CONF_PRPENC_EN (1 << 0) -+#define IC_CONF_PRPENC_CSC1 (1 << 1) -+#define IC_CONF_PRPENC_ROT_EN (1 << 2) -+#define IC_CONF_PRPVF_EN (1 << 8) -+#define IC_CONF_PRPVF_CSC1 (1 << 9) -+#define IC_CONF_PRPVF_CSC2 (1 << 10) -+#define IC_CONF_PRPVF_CMB (1 << 11) -+#define IC_CONF_PRPVF_ROT_EN (1 << 12) -+#define IC_CONF_PP_EN (1 << 16) -+#define IC_CONF_PP_CSC1 (1 << 17) -+#define IC_CONF_PP_CSC2 (1 << 18) -+#define IC_CONF_PP_CMB (1 << 19) -+#define IC_CONF_PP_ROT_EN (1 << 20) -+#define IC_CONF_IC_GLB_LOC_A (1 << 28) -+#define IC_CONF_KEY_COLOR_EN (1 << 29) -+#define IC_CONF_RWS_EN (1 << 30) -+#define IC_CONF_CSI_MEM_WR_EN (1 << 31) -+ -+#define IC_IDMAC_1_CB0_BURST_16 (1 << 0) -+#define IC_IDMAC_1_CB1_BURST_16 (1 << 1) -+#define IC_IDMAC_1_CB2_BURST_16 (1 << 2) -+#define IC_IDMAC_1_CB3_BURST_16 (1 << 3) -+#define IC_IDMAC_1_CB4_BURST_16 (1 << 4) -+#define IC_IDMAC_1_CB5_BURST_16 (1 << 5) -+#define IC_IDMAC_1_CB6_BURST_16 (1 << 6) -+#define IC_IDMAC_1_CB7_BURST_16 (1 << 7) -+#define IC_IDMAC_1_PRPENC_ROT_MASK (0x7 << 11) -+#define IC_IDMAC_1_PRPENC_ROT_OFFSET 11 -+#define IC_IDMAC_1_PRPVF_ROT_MASK (0x7 << 14) -+#define IC_IDMAC_1_PRPVF_ROT_OFFSET 14 -+#define IC_IDMAC_1_PP_ROT_MASK (0x7 << 17) -+#define IC_IDMAC_1_PP_ROT_OFFSET 17 -+#define IC_IDMAC_1_PP_FLIP_RS (1 << 22) -+#define IC_IDMAC_1_PRPVF_FLIP_RS (1 << 21) -+#define IC_IDMAC_1_PRPENC_FLIP_RS (1 << 20) -+ -+#define IC_IDMAC_2_PRPENC_HEIGHT_MASK (0x3ff << 0) -+#define IC_IDMAC_2_PRPENC_HEIGHT_OFFSET 0 -+#define IC_IDMAC_2_PRPVF_HEIGHT_MASK (0x3ff << 10) -+#define IC_IDMAC_2_PRPVF_HEIGHT_OFFSET 10 -+#define IC_IDMAC_2_PP_HEIGHT_MASK (0x3ff << 20) -+#define IC_IDMAC_2_PP_HEIGHT_OFFSET 20 -+ -+#define IC_IDMAC_3_PRPENC_WIDTH_MASK (0x3ff << 0) -+#define IC_IDMAC_3_PRPENC_WIDTH_OFFSET 0 -+#define IC_IDMAC_3_PRPVF_WIDTH_MASK (0x3ff << 10) -+#define IC_IDMAC_3_PRPVF_WIDTH_OFFSET 10 -+#define IC_IDMAC_3_PP_WIDTH_MASK (0x3ff << 20) -+#define IC_IDMAC_3_PP_WIDTH_OFFSET 20 -+ -+struct ic_task_regoffs { -+ u32 rsc; -+ u32 tpmem_csc[2]; -+}; -+ -+struct ic_task_bitfields { -+ u32 ic_conf_en; -+ u32 ic_conf_rot_en; -+ u32 ic_conf_cmb_en; -+ u32 ic_conf_csc1_en; -+ u32 ic_conf_csc2_en; -+ u32 ic_cmb_galpha_bit; -+}; -+ -+static const struct ic_task_regoffs ic_task_reg[IC_NUM_TASKS] = { -+ [IC_TASK_ENCODER] = { -+ .rsc = IC_PRP_ENC_RSC, -+ .tpmem_csc = {0x2008, 0}, -+ }, -+ [IC_TASK_VIEWFINDER] = { -+ .rsc = IC_PRP_VF_RSC, -+ .tpmem_csc = {0x4028, 0x4040}, -+ }, -+ [IC_TASK_POST_PROCESSOR] = { -+ .rsc = IC_PP_RSC, -+ .tpmem_csc = {0x6060, 0x6078}, -+ }, -+}; -+ -+static const struct ic_task_bitfields ic_task_bit[IC_NUM_TASKS] = { -+ [IC_TASK_ENCODER] = { -+ .ic_conf_en = IC_CONF_PRPENC_EN, -+ .ic_conf_rot_en = IC_CONF_PRPENC_ROT_EN, -+ .ic_conf_cmb_en = 0, /* NA */ -+ .ic_conf_csc1_en = IC_CONF_PRPENC_CSC1, -+ .ic_conf_csc2_en = 0, /* NA */ -+ .ic_cmb_galpha_bit = 0, /* NA */ -+ }, -+ [IC_TASK_VIEWFINDER] = { -+ .ic_conf_en = IC_CONF_PRPVF_EN, -+ .ic_conf_rot_en = IC_CONF_PRPVF_ROT_EN, -+ .ic_conf_cmb_en = IC_CONF_PRPVF_CMB, -+ .ic_conf_csc1_en = IC_CONF_PRPVF_CSC1, -+ .ic_conf_csc2_en = IC_CONF_PRPVF_CSC2, -+ .ic_cmb_galpha_bit = 0, -+ }, -+ [IC_TASK_POST_PROCESSOR] = { -+ .ic_conf_en = IC_CONF_PP_EN, -+ .ic_conf_rot_en = IC_CONF_PP_ROT_EN, -+ .ic_conf_cmb_en = IC_CONF_PP_CMB, -+ .ic_conf_csc1_en = IC_CONF_PP_CSC1, -+ .ic_conf_csc2_en = IC_CONF_PP_CSC2, -+ .ic_cmb_galpha_bit = 8, -+ }, -+}; -+ -+struct ipu_ic_priv; -+ -+struct ipu_ic { -+ enum ipu_ic_task task; -+ const struct ic_task_regoffs *reg; -+ const struct ic_task_bitfields *bit; -+ -+ struct ipu_ic_colorspace in_cs; -+ struct ipu_ic_colorspace g_in_cs; -+ struct ipu_ic_colorspace out_cs; -+ -+ bool graphics; -+ bool rotation; -+ bool in_use; -+ -+ struct ipu_ic_priv *priv; -+}; -+ -+struct ipu_ic_priv { -+ void __iomem *base; -+ void __iomem *tpmem_base; -+ spinlock_t lock; -+ struct ipu_soc *ipu; -+ int use_count; -+ int irt_use_count; -+ struct ipu_ic task[IC_NUM_TASKS]; -+}; -+ -+static inline u32 ipu_ic_read(struct ipu_ic *ic, unsigned offset) -+{ -+ return readl(ic->priv->base + offset); -+} -+ -+static inline void ipu_ic_write(struct ipu_ic *ic, u32 value, unsigned offset) -+{ -+ writel(value, ic->priv->base + offset); -+} -+ -+static int init_csc(struct ipu_ic *ic, -+ const struct ipu_ic_csc *csc, -+ int csc_index) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ u32 __iomem *base; -+ const u16 (*c)[3]; -+ const u16 *a; -+ u32 param; -+ -+ base = (u32 __iomem *) -+ (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); -+ -+ /* Cast to unsigned */ -+ c = (const u16 (*)[3])csc->params.coeff; -+ a = (const u16 *)csc->params.offset; -+ -+ param = ((a[0] & 0x1f) << 27) | ((c[0][0] & 0x1ff) << 18) | -+ ((c[1][1] & 0x1ff) << 9) | (c[2][2] & 0x1ff); -+ writel(param, base++); -+ -+ param = ((a[0] & 0x1fe0) >> 5) | (csc->params.scale << 8) | -+ (csc->params.sat << 10); -+ writel(param, base++); -+ -+ param = ((a[1] & 0x1f) << 27) | ((c[0][1] & 0x1ff) << 18) | -+ ((c[1][0] & 0x1ff) << 9) | (c[2][0] & 0x1ff); -+ writel(param, base++); -+ -+ param = ((a[1] & 0x1fe0) >> 5); -+ writel(param, base++); -+ -+ param = ((a[2] & 0x1f) << 27) | ((c[0][2] & 0x1ff) << 18) | -+ ((c[1][2] & 0x1ff) << 9) | (c[2][1] & 0x1ff); -+ writel(param, base++); -+ -+ param = ((a[2] & 0x1fe0) >> 5); -+ writel(param, base++); -+ -+ return 0; -+} -+ -+static int calc_resize_coeffs(struct ipu_ic *ic, -+ u32 in_size, u32 out_size, -+ u32 *resize_coeff, -+ u32 *downsize_coeff) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ struct ipu_soc *ipu = priv->ipu; -+ u32 temp_size, temp_downsize; -+ -+ /* -+ * Input size cannot be more than 4096, and output size cannot -+ * be more than 1024 -+ */ -+ if (in_size > 4096) { -+ dev_err(ipu->dev, "Unsupported resize (in_size > 4096)\n"); -+ return -EINVAL; -+ } -+ if (out_size > 1024) { -+ dev_err(ipu->dev, "Unsupported resize (out_size > 1024)\n"); -+ return -EINVAL; -+ } -+ -+ /* Cannot downsize more than 4:1 */ -+ if ((out_size << 2) < in_size) { -+ dev_err(ipu->dev, "Unsupported downsize\n"); -+ return -EINVAL; -+ } -+ -+ /* Compute downsizing coefficient */ -+ temp_downsize = 0; -+ temp_size = in_size; -+ while (((temp_size > 1024) || (temp_size >= out_size * 2)) && -+ (temp_downsize < 2)) { -+ temp_size >>= 1; -+ temp_downsize++; -+ } -+ *downsize_coeff = temp_downsize; -+ -+ /* -+ * compute resizing coefficient using the following equation: -+ * resize_coeff = M * (SI - 1) / (SO - 1) -+ * where M = 2^13, SI = input size, SO = output size -+ */ -+ *resize_coeff = (8192L * (temp_size - 1)) / (out_size - 1); -+ if (*resize_coeff >= 16384L) { -+ dev_err(ipu->dev, "Warning! Overflow on resize coeff.\n"); -+ *resize_coeff = 0x3FFF; -+ } -+ -+ return 0; -+} -+ -+void ipu_ic_task_enable(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ unsigned long flags; -+ u32 ic_conf; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ ic_conf = ipu_ic_read(ic, IC_CONF); -+ -+ ic_conf |= ic->bit->ic_conf_en; -+ -+ if (ic->rotation) -+ ic_conf |= ic->bit->ic_conf_rot_en; -+ -+ if (ic->in_cs.cs != ic->out_cs.cs) -+ ic_conf |= ic->bit->ic_conf_csc1_en; -+ -+ if (ic->graphics) { -+ ic_conf |= ic->bit->ic_conf_cmb_en; -+ ic_conf |= ic->bit->ic_conf_csc1_en; -+ -+ if (ic->g_in_cs.cs != ic->out_cs.cs) -+ ic_conf |= ic->bit->ic_conf_csc2_en; -+ } -+ -+ ipu_ic_write(ic, ic_conf, IC_CONF); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_ic_task_enable); -+ -+void ipu_ic_task_disable(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ unsigned long flags; -+ u32 ic_conf; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ ic_conf = ipu_ic_read(ic, IC_CONF); -+ -+ ic_conf &= ~(ic->bit->ic_conf_en | -+ ic->bit->ic_conf_csc1_en | -+ ic->bit->ic_conf_rot_en); -+ if (ic->bit->ic_conf_csc2_en) -+ ic_conf &= ~ic->bit->ic_conf_csc2_en; -+ if (ic->bit->ic_conf_cmb_en) -+ ic_conf &= ~ic->bit->ic_conf_cmb_en; -+ -+ ipu_ic_write(ic, ic_conf, IC_CONF); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_ic_task_disable); -+ -+int ipu_ic_task_graphics_init(struct ipu_ic *ic, -+ const struct ipu_ic_colorspace *g_in_cs, -+ bool galpha_en, u32 galpha, -+ bool colorkey_en, u32 colorkey) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ struct ipu_ic_csc csc2; -+ unsigned long flags; -+ u32 reg, ic_conf; -+ int ret = 0; -+ -+ if (ic->task == IC_TASK_ENCODER) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ ic_conf = ipu_ic_read(ic, IC_CONF); -+ -+ if (!(ic_conf & ic->bit->ic_conf_csc1_en)) { -+ struct ipu_ic_csc csc1; -+ -+ ret = ipu_ic_calc_csc(&csc1, -+ V4L2_YCBCR_ENC_601, -+ V4L2_QUANTIZATION_FULL_RANGE, -+ IPUV3_COLORSPACE_RGB, -+ V4L2_YCBCR_ENC_601, -+ V4L2_QUANTIZATION_FULL_RANGE, -+ IPUV3_COLORSPACE_RGB); -+ if (ret) -+ goto unlock; -+ -+ /* need transparent CSC1 conversion */ -+ ret = init_csc(ic, &csc1, 0); -+ if (ret) -+ goto unlock; -+ } -+ -+ ic->g_in_cs = *g_in_cs; -+ csc2.in_cs = ic->g_in_cs; -+ csc2.out_cs = ic->out_cs; -+ -+ ret = __ipu_ic_calc_csc(&csc2); -+ if (ret) -+ goto unlock; -+ -+ ret = init_csc(ic, &csc2, 1); -+ if (ret) -+ goto unlock; -+ -+ if (galpha_en) { -+ ic_conf |= IC_CONF_IC_GLB_LOC_A; -+ reg = ipu_ic_read(ic, IC_CMBP_1); -+ reg &= ~(0xff << ic->bit->ic_cmb_galpha_bit); -+ reg |= (galpha << ic->bit->ic_cmb_galpha_bit); -+ ipu_ic_write(ic, reg, IC_CMBP_1); -+ } else -+ ic_conf &= ~IC_CONF_IC_GLB_LOC_A; -+ -+ if (colorkey_en) { -+ ic_conf |= IC_CONF_KEY_COLOR_EN; -+ ipu_ic_write(ic, colorkey, IC_CMBP_2); -+ } else -+ ic_conf &= ~IC_CONF_KEY_COLOR_EN; -+ -+ ipu_ic_write(ic, ic_conf, IC_CONF); -+ -+ ic->graphics = true; -+unlock: -+ spin_unlock_irqrestore(&priv->lock, flags); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_ic_task_graphics_init); -+ -+int ipu_ic_task_init_rsc(struct ipu_ic *ic, -+ const struct ipu_ic_csc *csc, -+ int in_width, int in_height, -+ int out_width, int out_height, -+ u32 rsc) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ u32 downsize_coeff, resize_coeff; -+ unsigned long flags; -+ int ret = 0; -+ -+ if (!rsc) { -+ /* Setup vertical resizing */ -+ -+ ret = calc_resize_coeffs(ic, in_height, out_height, -+ &resize_coeff, &downsize_coeff); -+ if (ret) -+ return ret; -+ -+ rsc = (downsize_coeff << 30) | (resize_coeff << 16); -+ -+ /* Setup horizontal resizing */ -+ ret = calc_resize_coeffs(ic, in_width, out_width, -+ &resize_coeff, &downsize_coeff); -+ if (ret) -+ return ret; -+ -+ rsc |= (downsize_coeff << 14) | resize_coeff; -+ } -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ ipu_ic_write(ic, rsc, ic->reg->rsc); -+ -+ /* Setup color space conversion */ -+ ic->in_cs = csc->in_cs; -+ ic->out_cs = csc->out_cs; -+ -+ ret = init_csc(ic, csc, 0); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ return ret; -+} -+ -+int ipu_ic_task_init(struct ipu_ic *ic, -+ const struct ipu_ic_csc *csc, -+ int in_width, int in_height, -+ int out_width, int out_height) -+{ -+ return ipu_ic_task_init_rsc(ic, csc, -+ in_width, in_height, -+ out_width, out_height, 0); -+} -+EXPORT_SYMBOL_GPL(ipu_ic_task_init); -+ -+int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel, -+ u32 width, u32 height, int burst_size, -+ enum ipu_rotate_mode rot) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ struct ipu_soc *ipu = priv->ipu; -+ u32 ic_idmac_1, ic_idmac_2, ic_idmac_3; -+ u32 temp_rot = bitrev8(rot) >> 5; -+ bool need_hor_flip = false; -+ unsigned long flags; -+ int ret = 0; -+ -+ if ((burst_size != 8) && (burst_size != 16)) { -+ dev_err(ipu->dev, "Illegal burst length for IC\n"); -+ return -EINVAL; -+ } -+ -+ width--; -+ height--; -+ -+ if (temp_rot & 0x2) /* Need horizontal flip */ -+ need_hor_flip = true; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ ic_idmac_1 = ipu_ic_read(ic, IC_IDMAC_1); -+ ic_idmac_2 = ipu_ic_read(ic, IC_IDMAC_2); -+ ic_idmac_3 = ipu_ic_read(ic, IC_IDMAC_3); -+ -+ switch (channel->num) { -+ case IPUV3_CHANNEL_IC_PP_MEM: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB2_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB2_BURST_16; -+ -+ if (need_hor_flip) -+ ic_idmac_1 |= IC_IDMAC_1_PP_FLIP_RS; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_PP_FLIP_RS; -+ -+ ic_idmac_2 &= ~IC_IDMAC_2_PP_HEIGHT_MASK; -+ ic_idmac_2 |= height << IC_IDMAC_2_PP_HEIGHT_OFFSET; -+ -+ ic_idmac_3 &= ~IC_IDMAC_3_PP_WIDTH_MASK; -+ ic_idmac_3 |= width << IC_IDMAC_3_PP_WIDTH_OFFSET; -+ break; -+ case IPUV3_CHANNEL_MEM_IC_PP: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB5_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB5_BURST_16; -+ break; -+ case IPUV3_CHANNEL_MEM_ROT_PP: -+ ic_idmac_1 &= ~IC_IDMAC_1_PP_ROT_MASK; -+ ic_idmac_1 |= temp_rot << IC_IDMAC_1_PP_ROT_OFFSET; -+ break; -+ case IPUV3_CHANNEL_MEM_IC_PRP_VF: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB6_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB6_BURST_16; -+ break; -+ case IPUV3_CHANNEL_IC_PRP_ENC_MEM: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB0_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB0_BURST_16; -+ -+ if (need_hor_flip) -+ ic_idmac_1 |= IC_IDMAC_1_PRPENC_FLIP_RS; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_FLIP_RS; -+ -+ ic_idmac_2 &= ~IC_IDMAC_2_PRPENC_HEIGHT_MASK; -+ ic_idmac_2 |= height << IC_IDMAC_2_PRPENC_HEIGHT_OFFSET; -+ -+ ic_idmac_3 &= ~IC_IDMAC_3_PRPENC_WIDTH_MASK; -+ ic_idmac_3 |= width << IC_IDMAC_3_PRPENC_WIDTH_OFFSET; -+ break; -+ case IPUV3_CHANNEL_MEM_ROT_ENC: -+ ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_ROT_MASK; -+ ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPENC_ROT_OFFSET; -+ break; -+ case IPUV3_CHANNEL_IC_PRP_VF_MEM: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB1_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB1_BURST_16; -+ -+ if (need_hor_flip) -+ ic_idmac_1 |= IC_IDMAC_1_PRPVF_FLIP_RS; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_FLIP_RS; -+ -+ ic_idmac_2 &= ~IC_IDMAC_2_PRPVF_HEIGHT_MASK; -+ ic_idmac_2 |= height << IC_IDMAC_2_PRPVF_HEIGHT_OFFSET; -+ -+ ic_idmac_3 &= ~IC_IDMAC_3_PRPVF_WIDTH_MASK; -+ ic_idmac_3 |= width << IC_IDMAC_3_PRPVF_WIDTH_OFFSET; -+ break; -+ case IPUV3_CHANNEL_MEM_ROT_VF: -+ ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_ROT_MASK; -+ ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPVF_ROT_OFFSET; -+ break; -+ case IPUV3_CHANNEL_G_MEM_IC_PRP_VF: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB3_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB3_BURST_16; -+ break; -+ case IPUV3_CHANNEL_G_MEM_IC_PP: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB4_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB4_BURST_16; -+ break; -+ case IPUV3_CHANNEL_VDI_MEM_IC_VF: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB7_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB7_BURST_16; -+ break; -+ default: -+ goto unlock; -+ } -+ -+ ipu_ic_write(ic, ic_idmac_1, IC_IDMAC_1); -+ ipu_ic_write(ic, ic_idmac_2, IC_IDMAC_2); -+ ipu_ic_write(ic, ic_idmac_3, IC_IDMAC_3); -+ -+ if (ipu_rot_mode_is_irt(rot)) -+ ic->rotation = true; -+ -+unlock: -+ spin_unlock_irqrestore(&priv->lock, flags); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init); -+ -+static void ipu_irt_enable(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ -+ if (!priv->irt_use_count) -+ ipu_module_enable(priv->ipu, IPU_CONF_ROT_EN); -+ -+ priv->irt_use_count++; -+} -+ -+static void ipu_irt_disable(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ -+ if (priv->irt_use_count) { -+ if (!--priv->irt_use_count) -+ ipu_module_disable(priv->ipu, IPU_CONF_ROT_EN); -+ } -+} -+ -+int ipu_ic_enable(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ if (!priv->use_count) -+ ipu_module_enable(priv->ipu, IPU_CONF_IC_EN); -+ -+ priv->use_count++; -+ -+ if (ic->rotation) -+ ipu_irt_enable(ic); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_ic_enable); -+ -+int ipu_ic_disable(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ priv->use_count--; -+ -+ if (!priv->use_count) -+ ipu_module_disable(priv->ipu, IPU_CONF_IC_EN); -+ -+ if (priv->use_count < 0) -+ priv->use_count = 0; -+ -+ if (ic->rotation) -+ ipu_irt_disable(ic); -+ -+ ic->rotation = ic->graphics = false; -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_ic_disable); -+ -+struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task) -+{ -+ struct ipu_ic_priv *priv = ipu->ic_priv; -+ unsigned long flags; -+ struct ipu_ic *ic, *ret; -+ -+ if (task >= IC_NUM_TASKS) -+ return ERR_PTR(-EINVAL); -+ -+ ic = &priv->task[task]; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ if (ic->in_use) { -+ ret = ERR_PTR(-EBUSY); -+ goto unlock; -+ } -+ -+ ic->in_use = true; -+ ret = ic; -+ -+unlock: -+ spin_unlock_irqrestore(&priv->lock, flags); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_ic_get); -+ -+void ipu_ic_put(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ ic->in_use = false; -+ spin_unlock_irqrestore(&priv->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_ic_put); -+ -+int ipu_ic_init(struct ipu_soc *ipu, struct device *dev, -+ unsigned long base, unsigned long tpmem_base) -+{ -+ struct ipu_ic_priv *priv; -+ int i; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ ipu->ic_priv = priv; -+ -+ spin_lock_init(&priv->lock); -+ priv->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!priv->base) -+ return -ENOMEM; -+ priv->tpmem_base = devm_ioremap(dev, tpmem_base, SZ_64K); -+ if (!priv->tpmem_base) -+ return -ENOMEM; -+ -+ dev_dbg(dev, "IC base: 0x%08lx remapped to %p\n", base, priv->base); -+ -+ priv->ipu = ipu; -+ -+ for (i = 0; i < IC_NUM_TASKS; i++) { -+ priv->task[i].task = i; -+ priv->task[i].priv = priv; -+ priv->task[i].reg = &ic_task_reg[i]; -+ priv->task[i].bit = &ic_task_bit[i]; -+ } -+ -+ return 0; -+} -+ -+void ipu_ic_exit(struct ipu_soc *ipu) -+{ -+} -+ -+void ipu_ic_dump(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ struct ipu_soc *ipu = priv->ipu; -+ -+ dev_dbg(ipu->dev, "IC_CONF = \t0x%08X\n", -+ ipu_ic_read(ic, IC_CONF)); -+ dev_dbg(ipu->dev, "IC_PRP_ENC_RSC = \t0x%08X\n", -+ ipu_ic_read(ic, IC_PRP_ENC_RSC)); -+ dev_dbg(ipu->dev, "IC_PRP_VF_RSC = \t0x%08X\n", -+ ipu_ic_read(ic, IC_PRP_VF_RSC)); -+ dev_dbg(ipu->dev, "IC_PP_RSC = \t0x%08X\n", -+ ipu_ic_read(ic, IC_PP_RSC)); -+ dev_dbg(ipu->dev, "IC_CMBP_1 = \t0x%08X\n", -+ ipu_ic_read(ic, IC_CMBP_1)); -+ dev_dbg(ipu->dev, "IC_CMBP_2 = \t0x%08X\n", -+ ipu_ic_read(ic, IC_CMBP_2)); -+ dev_dbg(ipu->dev, "IC_IDMAC_1 = \t0x%08X\n", -+ ipu_ic_read(ic, IC_IDMAC_1)); -+ dev_dbg(ipu->dev, "IC_IDMAC_2 = \t0x%08X\n", -+ ipu_ic_read(ic, IC_IDMAC_2)); -+ dev_dbg(ipu->dev, "IC_IDMAC_3 = \t0x%08X\n", -+ ipu_ic_read(ic, IC_IDMAC_3)); -+ dev_dbg(ipu->dev, "IC_IDMAC_4 = \t0x%08X\n", -+ ipu_ic_read(ic, IC_IDMAC_4)); -+} -+EXPORT_SYMBOL_GPL(ipu_ic_dump); ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-image-convert.c -@@ -0,0 +1,2475 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (C) 2012-2016 Mentor Graphics Inc. -+ * -+ * Queued image conversion support, with tiling and rotation. -+ */ -+ -+#include <linux/interrupt.h> -+#include <linux/dma-mapping.h> -+#include <video/imx-ipu-image-convert.h> -+#include "ipu-prv.h" -+ -+/* -+ * The IC Resizer has a restriction that the output frame from the -+ * resizer must be 1024 or less in both width (pixels) and height -+ * (lines). -+ * -+ * The image converter attempts to split up a conversion when -+ * the desired output (converted) frame resolution exceeds the -+ * IC resizer limit of 1024 in either dimension. -+ * -+ * If either dimension of the output frame exceeds the limit, the -+ * dimension is split into 1, 2, or 4 equal stripes, for a maximum -+ * of 4*4 or 16 tiles. A conversion is then carried out for each -+ * tile (but taking care to pass the full frame stride length to -+ * the DMA channel's parameter memory!). IDMA double-buffering is used -+ * to convert each tile back-to-back when possible (see note below -+ * when double_buffering boolean is set). -+ * -+ * Note that the input frame must be split up into the same number -+ * of tiles as the output frame: -+ * -+ * +---------+-----+ -+ * +-----+---+ | A | B | -+ * | A | B | | | | -+ * +-----+---+ --> +---------+-----+ -+ * | C | D | | C | D | -+ * +-----+---+ | | | -+ * +---------+-----+ -+ * -+ * Clockwise 90° rotations are handled by first rescaling into a -+ * reusable temporary tile buffer and then rotating with the 8x8 -+ * block rotator, writing to the correct destination: -+ * -+ * +-----+-----+ -+ * | | | -+ * +-----+---+ +---------+ | C | A | -+ * | A | B | | A,B, | | | | | -+ * +-----+---+ --> | C,D | | --> | | | -+ * | C | D | +---------+ +-----+-----+ -+ * +-----+---+ | D | B | -+ * | | | -+ * +-----+-----+ -+ * -+ * If the 8x8 block rotator is used, horizontal or vertical flipping -+ * is done during the rotation step, otherwise flipping is done -+ * during the scaling step. -+ * With rotation or flipping, tile order changes between input and -+ * output image. Tiles are numbered row major from top left to bottom -+ * right for both input and output image. -+ */ -+ -+#define MAX_STRIPES_W 4 -+#define MAX_STRIPES_H 4 -+#define MAX_TILES (MAX_STRIPES_W * MAX_STRIPES_H) -+ -+#define MIN_W 16 -+#define MIN_H 8 -+#define MAX_W 4096 -+#define MAX_H 4096 -+ -+enum ipu_image_convert_type { -+ IMAGE_CONVERT_IN = 0, -+ IMAGE_CONVERT_OUT, -+}; -+ -+struct ipu_image_convert_dma_buf { -+ void *virt; -+ dma_addr_t phys; -+ unsigned long len; -+}; -+ -+struct ipu_image_convert_dma_chan { -+ int in; -+ int out; -+ int rot_in; -+ int rot_out; -+ int vdi_in_p; -+ int vdi_in; -+ int vdi_in_n; -+}; -+ -+/* dimensions of one tile */ -+struct ipu_image_tile { -+ u32 width; -+ u32 height; -+ u32 left; -+ u32 top; -+ /* size and strides are in bytes */ -+ u32 size; -+ u32 stride; -+ u32 rot_stride; -+ /* start Y or packed offset of this tile */ -+ u32 offset; -+ /* offset from start to tile in U plane, for planar formats */ -+ u32 u_off; -+ /* offset from start to tile in V plane, for planar formats */ -+ u32 v_off; -+}; -+ -+struct ipu_image_convert_image { -+ struct ipu_image base; -+ enum ipu_image_convert_type type; -+ -+ const struct ipu_image_pixfmt *fmt; -+ unsigned int stride; -+ -+ /* # of rows (horizontal stripes) if dest height is > 1024 */ -+ unsigned int num_rows; -+ /* # of columns (vertical stripes) if dest width is > 1024 */ -+ unsigned int num_cols; -+ -+ struct ipu_image_tile tile[MAX_TILES]; -+}; -+ -+struct ipu_image_pixfmt { -+ u32 fourcc; /* V4L2 fourcc */ -+ int bpp; /* total bpp */ -+ int uv_width_dec; /* decimation in width for U/V planes */ -+ int uv_height_dec; /* decimation in height for U/V planes */ -+ bool planar; /* planar format */ -+ bool uv_swapped; /* U and V planes are swapped */ -+ bool uv_packed; /* partial planar (U and V in same plane) */ -+}; -+ -+struct ipu_image_convert_ctx; -+struct ipu_image_convert_chan; -+struct ipu_image_convert_priv; -+ -+struct ipu_image_convert_ctx { -+ struct ipu_image_convert_chan *chan; -+ -+ ipu_image_convert_cb_t complete; -+ void *complete_context; -+ -+ /* Source/destination image data and rotation mode */ -+ struct ipu_image_convert_image in; -+ struct ipu_image_convert_image out; -+ struct ipu_ic_csc csc; -+ enum ipu_rotate_mode rot_mode; -+ u32 downsize_coeff_h; -+ u32 downsize_coeff_v; -+ u32 image_resize_coeff_h; -+ u32 image_resize_coeff_v; -+ u32 resize_coeffs_h[MAX_STRIPES_W]; -+ u32 resize_coeffs_v[MAX_STRIPES_H]; -+ -+ /* intermediate buffer for rotation */ -+ struct ipu_image_convert_dma_buf rot_intermediate[2]; -+ -+ /* current buffer number for double buffering */ -+ int cur_buf_num; -+ -+ bool aborting; -+ struct completion aborted; -+ -+ /* can we use double-buffering for this conversion operation? */ -+ bool double_buffering; -+ /* num_rows * num_cols */ -+ unsigned int num_tiles; -+ /* next tile to process */ -+ unsigned int next_tile; -+ /* where to place converted tile in dest image */ -+ unsigned int out_tile_map[MAX_TILES]; -+ -+ struct list_head list; -+}; -+ -+struct ipu_image_convert_chan { -+ struct ipu_image_convert_priv *priv; -+ -+ enum ipu_ic_task ic_task; -+ const struct ipu_image_convert_dma_chan *dma_ch; -+ -+ struct ipu_ic *ic; -+ struct ipuv3_channel *in_chan; -+ struct ipuv3_channel *out_chan; -+ struct ipuv3_channel *rotation_in_chan; -+ struct ipuv3_channel *rotation_out_chan; -+ -+ /* the IPU end-of-frame irqs */ -+ int out_eof_irq; -+ int rot_out_eof_irq; -+ -+ spinlock_t irqlock; -+ -+ /* list of convert contexts */ -+ struct list_head ctx_list; -+ /* queue of conversion runs */ -+ struct list_head pending_q; -+ /* queue of completed runs */ -+ struct list_head done_q; -+ -+ /* the current conversion run */ -+ struct ipu_image_convert_run *current_run; -+}; -+ -+struct ipu_image_convert_priv { -+ struct ipu_image_convert_chan chan[IC_NUM_TASKS]; -+ struct ipu_soc *ipu; -+}; -+ -+static const struct ipu_image_convert_dma_chan -+image_convert_dma_chan[IC_NUM_TASKS] = { -+ [IC_TASK_VIEWFINDER] = { -+ .in = IPUV3_CHANNEL_MEM_IC_PRP_VF, -+ .out = IPUV3_CHANNEL_IC_PRP_VF_MEM, -+ .rot_in = IPUV3_CHANNEL_MEM_ROT_VF, -+ .rot_out = IPUV3_CHANNEL_ROT_VF_MEM, -+ .vdi_in_p = IPUV3_CHANNEL_MEM_VDI_PREV, -+ .vdi_in = IPUV3_CHANNEL_MEM_VDI_CUR, -+ .vdi_in_n = IPUV3_CHANNEL_MEM_VDI_NEXT, -+ }, -+ [IC_TASK_POST_PROCESSOR] = { -+ .in = IPUV3_CHANNEL_MEM_IC_PP, -+ .out = IPUV3_CHANNEL_IC_PP_MEM, -+ .rot_in = IPUV3_CHANNEL_MEM_ROT_PP, -+ .rot_out = IPUV3_CHANNEL_ROT_PP_MEM, -+ }, -+}; -+ -+static const struct ipu_image_pixfmt image_convert_formats[] = { -+ { -+ .fourcc = V4L2_PIX_FMT_RGB565, -+ .bpp = 16, -+ }, { -+ .fourcc = V4L2_PIX_FMT_RGB24, -+ .bpp = 24, -+ }, { -+ .fourcc = V4L2_PIX_FMT_BGR24, -+ .bpp = 24, -+ }, { -+ .fourcc = V4L2_PIX_FMT_RGB32, -+ .bpp = 32, -+ }, { -+ .fourcc = V4L2_PIX_FMT_BGR32, -+ .bpp = 32, -+ }, { -+ .fourcc = V4L2_PIX_FMT_XRGB32, -+ .bpp = 32, -+ }, { -+ .fourcc = V4L2_PIX_FMT_XBGR32, -+ .bpp = 32, -+ }, { -+ .fourcc = V4L2_PIX_FMT_BGRX32, -+ .bpp = 32, -+ }, { -+ .fourcc = V4L2_PIX_FMT_RGBX32, -+ .bpp = 32, -+ }, { -+ .fourcc = V4L2_PIX_FMT_YUYV, -+ .bpp = 16, -+ .uv_width_dec = 2, -+ .uv_height_dec = 1, -+ }, { -+ .fourcc = V4L2_PIX_FMT_UYVY, -+ .bpp = 16, -+ .uv_width_dec = 2, -+ .uv_height_dec = 1, -+ }, { -+ .fourcc = V4L2_PIX_FMT_YUV420, -+ .bpp = 12, -+ .planar = true, -+ .uv_width_dec = 2, -+ .uv_height_dec = 2, -+ }, { -+ .fourcc = V4L2_PIX_FMT_YVU420, -+ .bpp = 12, -+ .planar = true, -+ .uv_width_dec = 2, -+ .uv_height_dec = 2, -+ .uv_swapped = true, -+ }, { -+ .fourcc = V4L2_PIX_FMT_NV12, -+ .bpp = 12, -+ .planar = true, -+ .uv_width_dec = 2, -+ .uv_height_dec = 2, -+ .uv_packed = true, -+ }, { -+ .fourcc = V4L2_PIX_FMT_YUV422P, -+ .bpp = 16, -+ .planar = true, -+ .uv_width_dec = 2, -+ .uv_height_dec = 1, -+ }, { -+ .fourcc = V4L2_PIX_FMT_NV16, -+ .bpp = 16, -+ .planar = true, -+ .uv_width_dec = 2, -+ .uv_height_dec = 1, -+ .uv_packed = true, -+ }, -+}; -+ -+static const struct ipu_image_pixfmt *get_format(u32 fourcc) -+{ -+ const struct ipu_image_pixfmt *ret = NULL; -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(image_convert_formats); i++) { -+ if (image_convert_formats[i].fourcc == fourcc) { -+ ret = &image_convert_formats[i]; -+ break; -+ } -+ } -+ -+ return ret; -+} -+ -+static void dump_format(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *ic_image) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ -+ dev_dbg(priv->ipu->dev, -+ "task %u: ctx %p: %s format: %dx%d (%dx%d tiles), %c%c%c%c\n", -+ chan->ic_task, ctx, -+ ic_image->type == IMAGE_CONVERT_OUT ? "Output" : "Input", -+ ic_image->base.pix.width, ic_image->base.pix.height, -+ ic_image->num_cols, ic_image->num_rows, -+ ic_image->fmt->fourcc & 0xff, -+ (ic_image->fmt->fourcc >> 8) & 0xff, -+ (ic_image->fmt->fourcc >> 16) & 0xff, -+ (ic_image->fmt->fourcc >> 24) & 0xff); -+} -+ -+int ipu_image_convert_enum_format(int index, u32 *fourcc) -+{ -+ const struct ipu_image_pixfmt *fmt; -+ -+ if (index >= (int)ARRAY_SIZE(image_convert_formats)) -+ return -EINVAL; -+ -+ /* Format found */ -+ fmt = &image_convert_formats[index]; -+ *fourcc = fmt->fourcc; -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_enum_format); -+ -+static void free_dma_buf(struct ipu_image_convert_priv *priv, -+ struct ipu_image_convert_dma_buf *buf) -+{ -+ if (buf->virt) -+ dma_free_coherent(priv->ipu->dev, -+ buf->len, buf->virt, buf->phys); -+ buf->virt = NULL; -+ buf->phys = 0; -+} -+ -+static int alloc_dma_buf(struct ipu_image_convert_priv *priv, -+ struct ipu_image_convert_dma_buf *buf, -+ int size) -+{ -+ buf->len = PAGE_ALIGN(size); -+ buf->virt = dma_alloc_coherent(priv->ipu->dev, buf->len, &buf->phys, -+ GFP_DMA | GFP_KERNEL); -+ if (!buf->virt) { -+ dev_err(priv->ipu->dev, "failed to alloc dma buffer\n"); -+ return -ENOMEM; -+ } -+ -+ return 0; -+} -+ -+static inline int num_stripes(int dim) -+{ -+ return (dim - 1) / 1024 + 1; -+} -+ -+/* -+ * Calculate downsizing coefficients, which are the same for all tiles, -+ * and initial bilinear resizing coefficients, which are used to find the -+ * best seam positions. -+ * Also determine the number of tiles necessary to guarantee that no tile -+ * is larger than 1024 pixels in either dimension at the output and between -+ * IC downsizing and main processing sections. -+ */ -+static int calc_image_resize_coefficients(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image *in, -+ struct ipu_image *out) -+{ -+ u32 downsized_width = in->rect.width; -+ u32 downsized_height = in->rect.height; -+ u32 downsize_coeff_v = 0; -+ u32 downsize_coeff_h = 0; -+ u32 resized_width = out->rect.width; -+ u32 resized_height = out->rect.height; -+ u32 resize_coeff_h; -+ u32 resize_coeff_v; -+ u32 cols; -+ u32 rows; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ resized_width = out->rect.height; -+ resized_height = out->rect.width; -+ } -+ -+ /* Do not let invalid input lead to an endless loop below */ -+ if (WARN_ON(resized_width == 0 || resized_height == 0)) -+ return -EINVAL; -+ -+ while (downsized_width >= resized_width * 2) { -+ downsized_width >>= 1; -+ downsize_coeff_h++; -+ } -+ -+ while (downsized_height >= resized_height * 2) { -+ downsized_height >>= 1; -+ downsize_coeff_v++; -+ } -+ -+ /* -+ * Calculate the bilinear resizing coefficients that could be used if -+ * we were converting with a single tile. The bottom right output pixel -+ * should sample as close as possible to the bottom right input pixel -+ * out of the decimator, but not overshoot it: -+ */ -+ resize_coeff_h = 8192 * (downsized_width - 1) / (resized_width - 1); -+ resize_coeff_v = 8192 * (downsized_height - 1) / (resized_height - 1); -+ -+ /* -+ * Both the output of the IC downsizing section before being passed to -+ * the IC main processing section and the final output of the IC main -+ * processing section must be <= 1024 pixels in both dimensions. -+ */ -+ cols = num_stripes(max_t(u32, downsized_width, resized_width)); -+ rows = num_stripes(max_t(u32, downsized_height, resized_height)); -+ -+ dev_dbg(ctx->chan->priv->ipu->dev, -+ "%s: hscale: >>%u, *8192/%u vscale: >>%u, *8192/%u, %ux%u tiles\n", -+ __func__, downsize_coeff_h, resize_coeff_h, downsize_coeff_v, -+ resize_coeff_v, cols, rows); -+ -+ if (downsize_coeff_h > 2 || downsize_coeff_v > 2 || -+ resize_coeff_h > 0x3fff || resize_coeff_v > 0x3fff) -+ return -EINVAL; -+ -+ ctx->downsize_coeff_h = downsize_coeff_h; -+ ctx->downsize_coeff_v = downsize_coeff_v; -+ ctx->image_resize_coeff_h = resize_coeff_h; -+ ctx->image_resize_coeff_v = resize_coeff_v; -+ ctx->in.num_cols = cols; -+ ctx->in.num_rows = rows; -+ -+ return 0; -+} -+ -+#define round_closest(x, y) round_down((x) + (y)/2, (y)) -+ -+/* -+ * Find the best aligned seam position for the given column / row index. -+ * Rotation and image offsets are out of scope. -+ * -+ * @index: column / row index, used to calculate valid interval -+ * @in_edge: input right / bottom edge -+ * @out_edge: output right / bottom edge -+ * @in_align: input alignment, either horizontal 8-byte line start address -+ * alignment, or pixel alignment due to image format -+ * @out_align: output alignment, either horizontal 8-byte line start address -+ * alignment, or pixel alignment due to image format or rotator -+ * block size -+ * @in_burst: horizontal input burst size in case of horizontal flip -+ * @out_burst: horizontal output burst size or rotator block size -+ * @downsize_coeff: downsizing section coefficient -+ * @resize_coeff: main processing section resizing coefficient -+ * @_in_seam: aligned input seam position return value -+ * @_out_seam: aligned output seam position return value -+ */ -+static void find_best_seam(struct ipu_image_convert_ctx *ctx, -+ unsigned int index, -+ unsigned int in_edge, -+ unsigned int out_edge, -+ unsigned int in_align, -+ unsigned int out_align, -+ unsigned int in_burst, -+ unsigned int out_burst, -+ unsigned int downsize_coeff, -+ unsigned int resize_coeff, -+ u32 *_in_seam, -+ u32 *_out_seam) -+{ -+ struct device *dev = ctx->chan->priv->ipu->dev; -+ unsigned int out_pos; -+ /* Input / output seam position candidates */ -+ unsigned int out_seam = 0; -+ unsigned int in_seam = 0; -+ unsigned int min_diff = UINT_MAX; -+ unsigned int out_start; -+ unsigned int out_end; -+ unsigned int in_start; -+ unsigned int in_end; -+ -+ /* Start within 1024 pixels of the right / bottom edge */ -+ out_start = max_t(int, index * out_align, out_edge - 1024); -+ /* End before having to add more columns to the left / rows above */ -+ out_end = min_t(unsigned int, out_edge, index * 1024 + 1); -+ -+ /* -+ * Limit input seam position to make sure that the downsized input tile -+ * to the right or bottom does not exceed 1024 pixels. -+ */ -+ in_start = max_t(int, index * in_align, -+ in_edge - (1024 << downsize_coeff)); -+ in_end = min_t(unsigned int, in_edge, -+ index * (1024 << downsize_coeff) + 1); -+ -+ /* -+ * Output tiles must start at a multiple of 8 bytes horizontally and -+ * possibly at an even line horizontally depending on the pixel format. -+ * Only consider output aligned positions for the seam. -+ */ -+ out_start = round_up(out_start, out_align); -+ for (out_pos = out_start; out_pos < out_end; out_pos += out_align) { -+ unsigned int in_pos; -+ unsigned int in_pos_aligned; -+ unsigned int in_pos_rounded; -+ unsigned int abs_diff; -+ -+ /* -+ * Tiles in the right row / bottom column may not be allowed to -+ * overshoot horizontally / vertically. out_burst may be the -+ * actual DMA burst size, or the rotator block size. -+ */ -+ if ((out_burst > 1) && (out_edge - out_pos) % out_burst) -+ continue; -+ -+ /* -+ * Input sample position, corresponding to out_pos, 19.13 fixed -+ * point. -+ */ -+ in_pos = (out_pos * resize_coeff) << downsize_coeff; -+ /* -+ * The closest input sample position that we could actually -+ * start the input tile at, 19.13 fixed point. -+ */ -+ in_pos_aligned = round_closest(in_pos, 8192U * in_align); -+ /* Convert 19.13 fixed point to integer */ -+ in_pos_rounded = in_pos_aligned / 8192U; -+ -+ if (in_pos_rounded < in_start) -+ continue; -+ if (in_pos_rounded >= in_end) -+ break; -+ -+ if ((in_burst > 1) && -+ (in_edge - in_pos_rounded) % in_burst) -+ continue; -+ -+ if (in_pos < in_pos_aligned) -+ abs_diff = in_pos_aligned - in_pos; -+ else -+ abs_diff = in_pos - in_pos_aligned; -+ -+ if (abs_diff < min_diff) { -+ in_seam = in_pos_rounded; -+ out_seam = out_pos; -+ min_diff = abs_diff; -+ } -+ } -+ -+ *_out_seam = out_seam; -+ *_in_seam = in_seam; -+ -+ dev_dbg(dev, "%s: out_seam %u(%u) in [%u, %u], in_seam %u(%u) in [%u, %u] diff %u.%03u\n", -+ __func__, out_seam, out_align, out_start, out_end, -+ in_seam, in_align, in_start, in_end, min_diff / 8192, -+ DIV_ROUND_CLOSEST(min_diff % 8192 * 1000, 8192)); -+} -+ -+/* -+ * Tile left edges are required to be aligned to multiples of 8 bytes -+ * by the IDMAC. -+ */ -+static inline u32 tile_left_align(const struct ipu_image_pixfmt *fmt) -+{ -+ if (fmt->planar) -+ return fmt->uv_packed ? 8 : 8 * fmt->uv_width_dec; -+ else -+ return fmt->bpp == 32 ? 2 : fmt->bpp == 16 ? 4 : 8; -+} -+ -+/* -+ * Tile top edge alignment is only limited by chroma subsampling. -+ */ -+static inline u32 tile_top_align(const struct ipu_image_pixfmt *fmt) -+{ -+ return fmt->uv_height_dec > 1 ? 2 : 1; -+} -+ -+static inline u32 tile_width_align(enum ipu_image_convert_type type, -+ const struct ipu_image_pixfmt *fmt, -+ enum ipu_rotate_mode rot_mode) -+{ -+ if (type == IMAGE_CONVERT_IN) { -+ /* -+ * The IC burst reads 8 pixels at a time. Reading beyond the -+ * end of the line is usually acceptable. Those pixels are -+ * ignored, unless the IC has to write the scaled line in -+ * reverse. -+ */ -+ return (!ipu_rot_mode_is_irt(rot_mode) && -+ (rot_mode & IPU_ROT_BIT_HFLIP)) ? 8 : 2; -+ } -+ -+ /* -+ * Align to 16x16 pixel blocks for planar 4:2:0 chroma subsampled -+ * formats to guarantee 8-byte aligned line start addresses in the -+ * chroma planes when IRT is used. Align to 8x8 pixel IRT block size -+ * for all other formats. -+ */ -+ return (ipu_rot_mode_is_irt(rot_mode) && -+ fmt->planar && !fmt->uv_packed) ? -+ 8 * fmt->uv_width_dec : 8; -+} -+ -+static inline u32 tile_height_align(enum ipu_image_convert_type type, -+ const struct ipu_image_pixfmt *fmt, -+ enum ipu_rotate_mode rot_mode) -+{ -+ if (type == IMAGE_CONVERT_IN || !ipu_rot_mode_is_irt(rot_mode)) -+ return 2; -+ -+ /* -+ * Align to 16x16 pixel blocks for planar 4:2:0 chroma subsampled -+ * formats to guarantee 8-byte aligned line start addresses in the -+ * chroma planes when IRT is used. Align to 8x8 pixel IRT block size -+ * for all other formats. -+ */ -+ return (fmt->planar && !fmt->uv_packed) ? 8 * fmt->uv_width_dec : 8; -+} -+ -+/* -+ * Fill in left position and width and for all tiles in an input column, and -+ * for all corresponding output tiles. If the 90° rotator is used, the output -+ * tiles are in a row, and output tile top position and height are set. -+ */ -+static void fill_tile_column(struct ipu_image_convert_ctx *ctx, -+ unsigned int col, -+ struct ipu_image_convert_image *in, -+ unsigned int in_left, unsigned int in_width, -+ struct ipu_image_convert_image *out, -+ unsigned int out_left, unsigned int out_width) -+{ -+ unsigned int row, tile_idx; -+ struct ipu_image_tile *in_tile, *out_tile; -+ -+ for (row = 0; row < in->num_rows; row++) { -+ tile_idx = in->num_cols * row + col; -+ in_tile = &in->tile[tile_idx]; -+ out_tile = &out->tile[ctx->out_tile_map[tile_idx]]; -+ -+ in_tile->left = in_left; -+ in_tile->width = in_width; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ out_tile->top = out_left; -+ out_tile->height = out_width; -+ } else { -+ out_tile->left = out_left; -+ out_tile->width = out_width; -+ } -+ } -+} -+ -+/* -+ * Fill in top position and height and for all tiles in an input row, and -+ * for all corresponding output tiles. If the 90° rotator is used, the output -+ * tiles are in a column, and output tile left position and width are set. -+ */ -+static void fill_tile_row(struct ipu_image_convert_ctx *ctx, unsigned int row, -+ struct ipu_image_convert_image *in, -+ unsigned int in_top, unsigned int in_height, -+ struct ipu_image_convert_image *out, -+ unsigned int out_top, unsigned int out_height) -+{ -+ unsigned int col, tile_idx; -+ struct ipu_image_tile *in_tile, *out_tile; -+ -+ for (col = 0; col < in->num_cols; col++) { -+ tile_idx = in->num_cols * row + col; -+ in_tile = &in->tile[tile_idx]; -+ out_tile = &out->tile[ctx->out_tile_map[tile_idx]]; -+ -+ in_tile->top = in_top; -+ in_tile->height = in_height; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ out_tile->left = out_top; -+ out_tile->width = out_height; -+ } else { -+ out_tile->top = out_top; -+ out_tile->height = out_height; -+ } -+ } -+} -+ -+/* -+ * Find the best horizontal and vertical seam positions to split into tiles. -+ * Minimize the fractional part of the input sampling position for the -+ * top / left pixels of each tile. -+ */ -+static void find_seams(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *in, -+ struct ipu_image_convert_image *out) -+{ -+ struct device *dev = ctx->chan->priv->ipu->dev; -+ unsigned int resized_width = out->base.rect.width; -+ unsigned int resized_height = out->base.rect.height; -+ unsigned int col; -+ unsigned int row; -+ unsigned int in_left_align = tile_left_align(in->fmt); -+ unsigned int in_top_align = tile_top_align(in->fmt); -+ unsigned int out_left_align = tile_left_align(out->fmt); -+ unsigned int out_top_align = tile_top_align(out->fmt); -+ unsigned int out_width_align = tile_width_align(out->type, out->fmt, -+ ctx->rot_mode); -+ unsigned int out_height_align = tile_height_align(out->type, out->fmt, -+ ctx->rot_mode); -+ unsigned int in_right = in->base.rect.width; -+ unsigned int in_bottom = in->base.rect.height; -+ unsigned int out_right = out->base.rect.width; -+ unsigned int out_bottom = out->base.rect.height; -+ unsigned int flipped_out_left; -+ unsigned int flipped_out_top; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ /* Switch width/height and align top left to IRT block size */ -+ resized_width = out->base.rect.height; -+ resized_height = out->base.rect.width; -+ out_left_align = out_height_align; -+ out_top_align = out_width_align; -+ out_width_align = out_left_align; -+ out_height_align = out_top_align; -+ out_right = out->base.rect.height; -+ out_bottom = out->base.rect.width; -+ } -+ -+ for (col = in->num_cols - 1; col > 0; col--) { -+ bool allow_in_overshoot = ipu_rot_mode_is_irt(ctx->rot_mode) || -+ !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); -+ bool allow_out_overshoot = (col < in->num_cols - 1) && -+ !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); -+ unsigned int in_left; -+ unsigned int out_left; -+ -+ /* -+ * Align input width to burst length if the scaling step flips -+ * horizontally. -+ */ -+ -+ find_best_seam(ctx, col, -+ in_right, out_right, -+ in_left_align, out_left_align, -+ allow_in_overshoot ? 1 : 8 /* burst length */, -+ allow_out_overshoot ? 1 : out_width_align, -+ ctx->downsize_coeff_h, ctx->image_resize_coeff_h, -+ &in_left, &out_left); -+ -+ if (ctx->rot_mode & IPU_ROT_BIT_HFLIP) -+ flipped_out_left = resized_width - out_right; -+ else -+ flipped_out_left = out_left; -+ -+ fill_tile_column(ctx, col, in, in_left, in_right - in_left, -+ out, flipped_out_left, out_right - out_left); -+ -+ dev_dbg(dev, "%s: col %u: %u, %u -> %u, %u\n", __func__, col, -+ in_left, in_right - in_left, -+ flipped_out_left, out_right - out_left); -+ -+ in_right = in_left; -+ out_right = out_left; -+ } -+ -+ flipped_out_left = (ctx->rot_mode & IPU_ROT_BIT_HFLIP) ? -+ resized_width - out_right : 0; -+ -+ fill_tile_column(ctx, 0, in, 0, in_right, -+ out, flipped_out_left, out_right); -+ -+ dev_dbg(dev, "%s: col 0: 0, %u -> %u, %u\n", __func__, -+ in_right, flipped_out_left, out_right); -+ -+ for (row = in->num_rows - 1; row > 0; row--) { -+ bool allow_overshoot = row < in->num_rows - 1; -+ unsigned int in_top; -+ unsigned int out_top; -+ -+ find_best_seam(ctx, row, -+ in_bottom, out_bottom, -+ in_top_align, out_top_align, -+ 1, allow_overshoot ? 1 : out_height_align, -+ ctx->downsize_coeff_v, ctx->image_resize_coeff_v, -+ &in_top, &out_top); -+ -+ if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^ -+ ipu_rot_mode_is_irt(ctx->rot_mode)) -+ flipped_out_top = resized_height - out_bottom; -+ else -+ flipped_out_top = out_top; -+ -+ fill_tile_row(ctx, row, in, in_top, in_bottom - in_top, -+ out, flipped_out_top, out_bottom - out_top); -+ -+ dev_dbg(dev, "%s: row %u: %u, %u -> %u, %u\n", __func__, row, -+ in_top, in_bottom - in_top, -+ flipped_out_top, out_bottom - out_top); -+ -+ in_bottom = in_top; -+ out_bottom = out_top; -+ } -+ -+ if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^ -+ ipu_rot_mode_is_irt(ctx->rot_mode)) -+ flipped_out_top = resized_height - out_bottom; -+ else -+ flipped_out_top = 0; -+ -+ fill_tile_row(ctx, 0, in, 0, in_bottom, -+ out, flipped_out_top, out_bottom); -+ -+ dev_dbg(dev, "%s: row 0: 0, %u -> %u, %u\n", __func__, -+ in_bottom, flipped_out_top, out_bottom); -+} -+ -+static int calc_tile_dimensions(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *image) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ unsigned int max_width = 1024; -+ unsigned int max_height = 1024; -+ unsigned int i; -+ -+ if (image->type == IMAGE_CONVERT_IN) { -+ /* Up to 4096x4096 input tile size */ -+ max_width <<= ctx->downsize_coeff_h; -+ max_height <<= ctx->downsize_coeff_v; -+ } -+ -+ for (i = 0; i < ctx->num_tiles; i++) { -+ struct ipu_image_tile *tile; -+ const unsigned int row = i / image->num_cols; -+ const unsigned int col = i % image->num_cols; -+ -+ if (image->type == IMAGE_CONVERT_OUT) -+ tile = &image->tile[ctx->out_tile_map[i]]; -+ else -+ tile = &image->tile[i]; -+ -+ tile->size = ((tile->height * image->fmt->bpp) >> 3) * -+ tile->width; -+ -+ if (image->fmt->planar) { -+ tile->stride = tile->width; -+ tile->rot_stride = tile->height; -+ } else { -+ tile->stride = -+ (image->fmt->bpp * tile->width) >> 3; -+ tile->rot_stride = -+ (image->fmt->bpp * tile->height) >> 3; -+ } -+ -+ dev_dbg(priv->ipu->dev, -+ "task %u: ctx %p: %s@[%u,%u]: %ux%u@%u,%u\n", -+ chan->ic_task, ctx, -+ image->type == IMAGE_CONVERT_IN ? "Input" : "Output", -+ row, col, -+ tile->width, tile->height, tile->left, tile->top); -+ -+ if (!tile->width || tile->width > max_width || -+ !tile->height || tile->height > max_height) { -+ dev_err(priv->ipu->dev, "invalid %s tile size: %ux%u\n", -+ image->type == IMAGE_CONVERT_IN ? "input" : -+ "output", tile->width, tile->height); -+ return -EINVAL; -+ } -+ } -+ -+ return 0; -+} -+ -+/* -+ * Use the rotation transformation to find the tile coordinates -+ * (row, col) of a tile in the destination frame that corresponds -+ * to the given tile coordinates of a source frame. The destination -+ * coordinate is then converted to a tile index. -+ */ -+static int transform_tile_index(struct ipu_image_convert_ctx *ctx, -+ int src_row, int src_col) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_image *s_image = &ctx->in; -+ struct ipu_image_convert_image *d_image = &ctx->out; -+ int dst_row, dst_col; -+ -+ /* with no rotation it's a 1:1 mapping */ -+ if (ctx->rot_mode == IPU_ROTATE_NONE) -+ return src_row * s_image->num_cols + src_col; -+ -+ /* -+ * before doing the transform, first we have to translate -+ * source row,col for an origin in the center of s_image -+ */ -+ src_row = src_row * 2 - (s_image->num_rows - 1); -+ src_col = src_col * 2 - (s_image->num_cols - 1); -+ -+ /* do the rotation transform */ -+ if (ctx->rot_mode & IPU_ROT_BIT_90) { -+ dst_col = -src_row; -+ dst_row = src_col; -+ } else { -+ dst_col = src_col; -+ dst_row = src_row; -+ } -+ -+ /* apply flip */ -+ if (ctx->rot_mode & IPU_ROT_BIT_HFLIP) -+ dst_col = -dst_col; -+ if (ctx->rot_mode & IPU_ROT_BIT_VFLIP) -+ dst_row = -dst_row; -+ -+ dev_dbg(priv->ipu->dev, "task %u: ctx %p: [%d,%d] --> [%d,%d]\n", -+ chan->ic_task, ctx, src_col, src_row, dst_col, dst_row); -+ -+ /* -+ * finally translate dest row,col using an origin in upper -+ * left of d_image -+ */ -+ dst_row += d_image->num_rows - 1; -+ dst_col += d_image->num_cols - 1; -+ dst_row /= 2; -+ dst_col /= 2; -+ -+ return dst_row * d_image->num_cols + dst_col; -+} -+ -+/* -+ * Fill the out_tile_map[] with transformed destination tile indeces. -+ */ -+static void calc_out_tile_map(struct ipu_image_convert_ctx *ctx) -+{ -+ struct ipu_image_convert_image *s_image = &ctx->in; -+ unsigned int row, col, tile = 0; -+ -+ for (row = 0; row < s_image->num_rows; row++) { -+ for (col = 0; col < s_image->num_cols; col++) { -+ ctx->out_tile_map[tile] = -+ transform_tile_index(ctx, row, col); -+ tile++; -+ } -+ } -+} -+ -+static int calc_tile_offsets_planar(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *image) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ const struct ipu_image_pixfmt *fmt = image->fmt; -+ unsigned int row, col, tile = 0; -+ u32 H, top, y_stride, uv_stride; -+ u32 uv_row_off, uv_col_off, uv_off, u_off, v_off, tmp; -+ u32 y_row_off, y_col_off, y_off; -+ u32 y_size, uv_size; -+ -+ /* setup some convenience vars */ -+ H = image->base.pix.height; -+ -+ y_stride = image->stride; -+ uv_stride = y_stride / fmt->uv_width_dec; -+ if (fmt->uv_packed) -+ uv_stride *= 2; -+ -+ y_size = H * y_stride; -+ uv_size = y_size / (fmt->uv_width_dec * fmt->uv_height_dec); -+ -+ for (row = 0; row < image->num_rows; row++) { -+ top = image->tile[tile].top; -+ y_row_off = top * y_stride; -+ uv_row_off = (top * uv_stride) / fmt->uv_height_dec; -+ -+ for (col = 0; col < image->num_cols; col++) { -+ y_col_off = image->tile[tile].left; -+ uv_col_off = y_col_off / fmt->uv_width_dec; -+ if (fmt->uv_packed) -+ uv_col_off *= 2; -+ -+ y_off = y_row_off + y_col_off; -+ uv_off = uv_row_off + uv_col_off; -+ -+ u_off = y_size - y_off + uv_off; -+ v_off = (fmt->uv_packed) ? 0 : u_off + uv_size; -+ if (fmt->uv_swapped) { -+ tmp = u_off; -+ u_off = v_off; -+ v_off = tmp; -+ } -+ -+ image->tile[tile].offset = y_off; -+ image->tile[tile].u_off = u_off; -+ image->tile[tile++].v_off = v_off; -+ -+ if ((y_off & 0x7) || (u_off & 0x7) || (v_off & 0x7)) { -+ dev_err(priv->ipu->dev, -+ "task %u: ctx %p: %s@[%d,%d]: " -+ "y_off %08x, u_off %08x, v_off %08x\n", -+ chan->ic_task, ctx, -+ image->type == IMAGE_CONVERT_IN ? -+ "Input" : "Output", row, col, -+ y_off, u_off, v_off); -+ return -EINVAL; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+static int calc_tile_offsets_packed(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *image) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ const struct ipu_image_pixfmt *fmt = image->fmt; -+ unsigned int row, col, tile = 0; -+ u32 bpp, stride, offset; -+ u32 row_off, col_off; -+ -+ /* setup some convenience vars */ -+ stride = image->stride; -+ bpp = fmt->bpp; -+ -+ for (row = 0; row < image->num_rows; row++) { -+ row_off = image->tile[tile].top * stride; -+ -+ for (col = 0; col < image->num_cols; col++) { -+ col_off = (image->tile[tile].left * bpp) >> 3; -+ -+ offset = row_off + col_off; -+ -+ image->tile[tile].offset = offset; -+ image->tile[tile].u_off = 0; -+ image->tile[tile++].v_off = 0; -+ -+ if (offset & 0x7) { -+ dev_err(priv->ipu->dev, -+ "task %u: ctx %p: %s@[%d,%d]: " -+ "phys %08x\n", -+ chan->ic_task, ctx, -+ image->type == IMAGE_CONVERT_IN ? -+ "Input" : "Output", row, col, -+ row_off + col_off); -+ return -EINVAL; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+static int calc_tile_offsets(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *image) -+{ -+ if (image->fmt->planar) -+ return calc_tile_offsets_planar(ctx, image); -+ -+ return calc_tile_offsets_packed(ctx, image); -+} -+ -+/* -+ * Calculate the resizing ratio for the IC main processing section given input -+ * size, fixed downsizing coefficient, and output size. -+ * Either round to closest for the next tile's first pixel to minimize seams -+ * and distortion (for all but right column / bottom row), or round down to -+ * avoid sampling beyond the edges of the input image for this tile's last -+ * pixel. -+ * Returns the resizing coefficient, resizing ratio is 8192.0 / resize_coeff. -+ */ -+static u32 calc_resize_coeff(u32 input_size, u32 downsize_coeff, -+ u32 output_size, bool allow_overshoot) -+{ -+ u32 downsized = input_size >> downsize_coeff; -+ -+ if (allow_overshoot) -+ return DIV_ROUND_CLOSEST(8192 * downsized, output_size); -+ else -+ return 8192 * (downsized - 1) / (output_size - 1); -+} -+ -+/* -+ * Slightly modify resize coefficients per tile to hide the bilinear -+ * interpolator reset at tile borders, shifting the right / bottom edge -+ * by up to a half input pixel. This removes noticeable seams between -+ * tiles at higher upscaling factors. -+ */ -+static void calc_tile_resize_coefficients(struct ipu_image_convert_ctx *ctx) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_tile *in_tile, *out_tile; -+ unsigned int col, row, tile_idx; -+ unsigned int last_output; -+ -+ for (col = 0; col < ctx->in.num_cols; col++) { -+ bool closest = (col < ctx->in.num_cols - 1) && -+ !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); -+ u32 resized_width; -+ u32 resize_coeff_h; -+ u32 in_width; -+ -+ tile_idx = col; -+ in_tile = &ctx->in.tile[tile_idx]; -+ out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) -+ resized_width = out_tile->height; -+ else -+ resized_width = out_tile->width; -+ -+ resize_coeff_h = calc_resize_coeff(in_tile->width, -+ ctx->downsize_coeff_h, -+ resized_width, closest); -+ -+ dev_dbg(priv->ipu->dev, "%s: column %u hscale: *8192/%u\n", -+ __func__, col, resize_coeff_h); -+ -+ /* -+ * With the horizontal scaling factor known, round up resized -+ * width (output width or height) to burst size. -+ */ -+ resized_width = round_up(resized_width, 8); -+ -+ /* -+ * Calculate input width from the last accessed input pixel -+ * given resized width and scaling coefficients. Round up to -+ * burst size. -+ */ -+ last_output = resized_width - 1; -+ if (closest && ((last_output * resize_coeff_h) % 8192)) -+ last_output++; -+ in_width = round_up( -+ (DIV_ROUND_UP(last_output * resize_coeff_h, 8192) + 1) -+ << ctx->downsize_coeff_h, 8); -+ -+ for (row = 0; row < ctx->in.num_rows; row++) { -+ tile_idx = row * ctx->in.num_cols + col; -+ in_tile = &ctx->in.tile[tile_idx]; -+ out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) -+ out_tile->height = resized_width; -+ else -+ out_tile->width = resized_width; -+ -+ in_tile->width = in_width; -+ } -+ -+ ctx->resize_coeffs_h[col] = resize_coeff_h; -+ } -+ -+ for (row = 0; row < ctx->in.num_rows; row++) { -+ bool closest = (row < ctx->in.num_rows - 1) && -+ !(ctx->rot_mode & IPU_ROT_BIT_VFLIP); -+ u32 resized_height; -+ u32 resize_coeff_v; -+ u32 in_height; -+ -+ tile_idx = row * ctx->in.num_cols; -+ in_tile = &ctx->in.tile[tile_idx]; -+ out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) -+ resized_height = out_tile->width; -+ else -+ resized_height = out_tile->height; -+ -+ resize_coeff_v = calc_resize_coeff(in_tile->height, -+ ctx->downsize_coeff_v, -+ resized_height, closest); -+ -+ dev_dbg(priv->ipu->dev, "%s: row %u vscale: *8192/%u\n", -+ __func__, row, resize_coeff_v); -+ -+ /* -+ * With the vertical scaling factor known, round up resized -+ * height (output width or height) to IDMAC limitations. -+ */ -+ resized_height = round_up(resized_height, 2); -+ -+ /* -+ * Calculate input width from the last accessed input pixel -+ * given resized height and scaling coefficients. Align to -+ * IDMAC restrictions. -+ */ -+ last_output = resized_height - 1; -+ if (closest && ((last_output * resize_coeff_v) % 8192)) -+ last_output++; -+ in_height = round_up( -+ (DIV_ROUND_UP(last_output * resize_coeff_v, 8192) + 1) -+ << ctx->downsize_coeff_v, 2); -+ -+ for (col = 0; col < ctx->in.num_cols; col++) { -+ tile_idx = row * ctx->in.num_cols + col; -+ in_tile = &ctx->in.tile[tile_idx]; -+ out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) -+ out_tile->width = resized_height; -+ else -+ out_tile->height = resized_height; -+ -+ in_tile->height = in_height; -+ } -+ -+ ctx->resize_coeffs_v[row] = resize_coeff_v; -+ } -+} -+ -+/* -+ * return the number of runs in given queue (pending_q or done_q) -+ * for this context. hold irqlock when calling. -+ */ -+static int get_run_count(struct ipu_image_convert_ctx *ctx, -+ struct list_head *q) -+{ -+ struct ipu_image_convert_run *run; -+ int count = 0; -+ -+ lockdep_assert_held(&ctx->chan->irqlock); -+ -+ list_for_each_entry(run, q, list) { -+ if (run->ctx == ctx) -+ count++; -+ } -+ -+ return count; -+} -+ -+static void convert_stop(struct ipu_image_convert_run *run) -+{ -+ struct ipu_image_convert_ctx *ctx = run->ctx; -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: stopping ctx %p run %p\n", -+ __func__, chan->ic_task, ctx, run); -+ -+ /* disable IC tasks and the channels */ -+ ipu_ic_task_disable(chan->ic); -+ ipu_idmac_disable_channel(chan->in_chan); -+ ipu_idmac_disable_channel(chan->out_chan); -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ ipu_idmac_disable_channel(chan->rotation_in_chan); -+ ipu_idmac_disable_channel(chan->rotation_out_chan); -+ ipu_idmac_unlink(chan->out_chan, chan->rotation_in_chan); -+ } -+ -+ ipu_ic_disable(chan->ic); -+} -+ -+static void init_idmac_channel(struct ipu_image_convert_ctx *ctx, -+ struct ipuv3_channel *channel, -+ struct ipu_image_convert_image *image, -+ enum ipu_rotate_mode rot_mode, -+ bool rot_swap_width_height, -+ unsigned int tile) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ unsigned int burst_size; -+ u32 width, height, stride; -+ dma_addr_t addr0, addr1 = 0; -+ struct ipu_image tile_image; -+ unsigned int tile_idx[2]; -+ -+ if (image->type == IMAGE_CONVERT_OUT) { -+ tile_idx[0] = ctx->out_tile_map[tile]; -+ tile_idx[1] = ctx->out_tile_map[1]; -+ } else { -+ tile_idx[0] = tile; -+ tile_idx[1] = 1; -+ } -+ -+ if (rot_swap_width_height) { -+ width = image->tile[tile_idx[0]].height; -+ height = image->tile[tile_idx[0]].width; -+ stride = image->tile[tile_idx[0]].rot_stride; -+ addr0 = ctx->rot_intermediate[0].phys; -+ if (ctx->double_buffering) -+ addr1 = ctx->rot_intermediate[1].phys; -+ } else { -+ width = image->tile[tile_idx[0]].width; -+ height = image->tile[tile_idx[0]].height; -+ stride = image->stride; -+ addr0 = image->base.phys0 + -+ image->tile[tile_idx[0]].offset; -+ if (ctx->double_buffering) -+ addr1 = image->base.phys0 + -+ image->tile[tile_idx[1]].offset; -+ } -+ -+ ipu_cpmem_zero(channel); -+ -+ memset(&tile_image, 0, sizeof(tile_image)); -+ tile_image.pix.width = tile_image.rect.width = width; -+ tile_image.pix.height = tile_image.rect.height = height; -+ tile_image.pix.bytesperline = stride; -+ tile_image.pix.pixelformat = image->fmt->fourcc; -+ tile_image.phys0 = addr0; -+ tile_image.phys1 = addr1; -+ if (image->fmt->planar && !rot_swap_width_height) { -+ tile_image.u_offset = image->tile[tile_idx[0]].u_off; -+ tile_image.v_offset = image->tile[tile_idx[0]].v_off; -+ } -+ -+ ipu_cpmem_set_image(channel, &tile_image); -+ -+ if (rot_mode) -+ ipu_cpmem_set_rotation(channel, rot_mode); -+ -+ /* -+ * Skip writing U and V components to odd rows in the output -+ * channels for planar 4:2:0. -+ */ -+ if ((channel == chan->out_chan || -+ channel == chan->rotation_out_chan) && -+ image->fmt->planar && image->fmt->uv_height_dec == 2) -+ ipu_cpmem_skip_odd_chroma_rows(channel); -+ -+ if (channel == chan->rotation_in_chan || -+ channel == chan->rotation_out_chan) { -+ burst_size = 8; -+ ipu_cpmem_set_block_mode(channel); -+ } else -+ burst_size = (width % 16) ? 8 : 16; -+ -+ ipu_cpmem_set_burstsize(channel, burst_size); -+ -+ ipu_ic_task_idma_init(chan->ic, channel, width, height, -+ burst_size, rot_mode); -+ -+ /* -+ * Setting a non-zero AXI ID collides with the PRG AXI snooping, so -+ * only do this when there is no PRG present. -+ */ -+ if (!channel->ipu->prg_priv) -+ ipu_cpmem_set_axi_id(channel, 1); -+ -+ ipu_idmac_set_double_buffer(channel, ctx->double_buffering); -+} -+ -+static int convert_start(struct ipu_image_convert_run *run, unsigned int tile) -+{ -+ struct ipu_image_convert_ctx *ctx = run->ctx; -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_image *s_image = &ctx->in; -+ struct ipu_image_convert_image *d_image = &ctx->out; -+ unsigned int dst_tile = ctx->out_tile_map[tile]; -+ unsigned int dest_width, dest_height; -+ unsigned int col, row; -+ u32 rsc; -+ int ret; -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: starting ctx %p run %p tile %u -> %u\n", -+ __func__, chan->ic_task, ctx, run, tile, dst_tile); -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ /* swap width/height for resizer */ -+ dest_width = d_image->tile[dst_tile].height; -+ dest_height = d_image->tile[dst_tile].width; -+ } else { -+ dest_width = d_image->tile[dst_tile].width; -+ dest_height = d_image->tile[dst_tile].height; -+ } -+ -+ row = tile / s_image->num_cols; -+ col = tile % s_image->num_cols; -+ -+ rsc = (ctx->downsize_coeff_v << 30) | -+ (ctx->resize_coeffs_v[row] << 16) | -+ (ctx->downsize_coeff_h << 14) | -+ (ctx->resize_coeffs_h[col]); -+ -+ dev_dbg(priv->ipu->dev, "%s: %ux%u -> %ux%u (rsc = 0x%x)\n", -+ __func__, s_image->tile[tile].width, -+ s_image->tile[tile].height, dest_width, dest_height, rsc); -+ -+ /* setup the IC resizer and CSC */ -+ ret = ipu_ic_task_init_rsc(chan->ic, &ctx->csc, -+ s_image->tile[tile].width, -+ s_image->tile[tile].height, -+ dest_width, -+ dest_height, -+ rsc); -+ if (ret) { -+ dev_err(priv->ipu->dev, "ipu_ic_task_init failed, %d\n", ret); -+ return ret; -+ } -+ -+ /* init the source MEM-->IC PP IDMAC channel */ -+ init_idmac_channel(ctx, chan->in_chan, s_image, -+ IPU_ROTATE_NONE, false, tile); -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ /* init the IC PP-->MEM IDMAC channel */ -+ init_idmac_channel(ctx, chan->out_chan, d_image, -+ IPU_ROTATE_NONE, true, tile); -+ -+ /* init the MEM-->IC PP ROT IDMAC channel */ -+ init_idmac_channel(ctx, chan->rotation_in_chan, d_image, -+ ctx->rot_mode, true, tile); -+ -+ /* init the destination IC PP ROT-->MEM IDMAC channel */ -+ init_idmac_channel(ctx, chan->rotation_out_chan, d_image, -+ IPU_ROTATE_NONE, false, tile); -+ -+ /* now link IC PP-->MEM to MEM-->IC PP ROT */ -+ ipu_idmac_link(chan->out_chan, chan->rotation_in_chan); -+ } else { -+ /* init the destination IC PP-->MEM IDMAC channel */ -+ init_idmac_channel(ctx, chan->out_chan, d_image, -+ ctx->rot_mode, false, tile); -+ } -+ -+ /* enable the IC */ -+ ipu_ic_enable(chan->ic); -+ -+ /* set buffers ready */ -+ ipu_idmac_select_buffer(chan->in_chan, 0); -+ ipu_idmac_select_buffer(chan->out_chan, 0); -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) -+ ipu_idmac_select_buffer(chan->rotation_out_chan, 0); -+ if (ctx->double_buffering) { -+ ipu_idmac_select_buffer(chan->in_chan, 1); -+ ipu_idmac_select_buffer(chan->out_chan, 1); -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) -+ ipu_idmac_select_buffer(chan->rotation_out_chan, 1); -+ } -+ -+ /* enable the channels! */ -+ ipu_idmac_enable_channel(chan->in_chan); -+ ipu_idmac_enable_channel(chan->out_chan); -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ ipu_idmac_enable_channel(chan->rotation_in_chan); -+ ipu_idmac_enable_channel(chan->rotation_out_chan); -+ } -+ -+ ipu_ic_task_enable(chan->ic); -+ -+ ipu_cpmem_dump(chan->in_chan); -+ ipu_cpmem_dump(chan->out_chan); -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ ipu_cpmem_dump(chan->rotation_in_chan); -+ ipu_cpmem_dump(chan->rotation_out_chan); -+ } -+ -+ ipu_dump(priv->ipu); -+ -+ return 0; -+} -+ -+/* hold irqlock when calling */ -+static int do_run(struct ipu_image_convert_run *run) -+{ -+ struct ipu_image_convert_ctx *ctx = run->ctx; -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ -+ lockdep_assert_held(&chan->irqlock); -+ -+ ctx->in.base.phys0 = run->in_phys; -+ ctx->out.base.phys0 = run->out_phys; -+ -+ ctx->cur_buf_num = 0; -+ ctx->next_tile = 1; -+ -+ /* remove run from pending_q and set as current */ -+ list_del(&run->list); -+ chan->current_run = run; -+ -+ return convert_start(run, 0); -+} -+ -+/* hold irqlock when calling */ -+static void run_next(struct ipu_image_convert_chan *chan) -+{ -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_run *run, *tmp; -+ int ret; -+ -+ lockdep_assert_held(&chan->irqlock); -+ -+ list_for_each_entry_safe(run, tmp, &chan->pending_q, list) { -+ /* skip contexts that are aborting */ -+ if (run->ctx->aborting) { -+ dev_dbg(priv->ipu->dev, -+ "%s: task %u: skipping aborting ctx %p run %p\n", -+ __func__, chan->ic_task, run->ctx, run); -+ continue; -+ } -+ -+ ret = do_run(run); -+ if (!ret) -+ break; -+ -+ /* -+ * something went wrong with start, add the run -+ * to done q and continue to the next run in the -+ * pending q. -+ */ -+ run->status = ret; -+ list_add_tail(&run->list, &chan->done_q); -+ chan->current_run = NULL; -+ } -+} -+ -+static void empty_done_q(struct ipu_image_convert_chan *chan) -+{ -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_run *run; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ while (!list_empty(&chan->done_q)) { -+ run = list_entry(chan->done_q.next, -+ struct ipu_image_convert_run, -+ list); -+ -+ list_del(&run->list); -+ -+ dev_dbg(priv->ipu->dev, -+ "%s: task %u: completing ctx %p run %p with %d\n", -+ __func__, chan->ic_task, run->ctx, run, run->status); -+ -+ /* call the completion callback and free the run */ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ run->ctx->complete(run, run->ctx->complete_context); -+ spin_lock_irqsave(&chan->irqlock, flags); -+ } -+ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+} -+ -+/* -+ * the bottom half thread clears out the done_q, calling the -+ * completion handler for each. -+ */ -+static irqreturn_t do_bh(int irq, void *dev_id) -+{ -+ struct ipu_image_convert_chan *chan = dev_id; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_ctx *ctx; -+ unsigned long flags; -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: enter\n", __func__, -+ chan->ic_task); -+ -+ empty_done_q(chan); -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ /* -+ * the done_q is cleared out, signal any contexts -+ * that are aborting that abort can complete. -+ */ -+ list_for_each_entry(ctx, &chan->ctx_list, list) { -+ if (ctx->aborting) { -+ dev_dbg(priv->ipu->dev, -+ "%s: task %u: signaling abort for ctx %p\n", -+ __func__, chan->ic_task, ctx); -+ complete_all(&ctx->aborted); -+ } -+ } -+ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: exit\n", __func__, -+ chan->ic_task); -+ -+ return IRQ_HANDLED; -+} -+ -+static bool ic_settings_changed(struct ipu_image_convert_ctx *ctx) -+{ -+ unsigned int cur_tile = ctx->next_tile - 1; -+ unsigned int next_tile = ctx->next_tile; -+ -+ if (ctx->resize_coeffs_h[cur_tile % ctx->in.num_cols] != -+ ctx->resize_coeffs_h[next_tile % ctx->in.num_cols] || -+ ctx->resize_coeffs_v[cur_tile / ctx->in.num_cols] != -+ ctx->resize_coeffs_v[next_tile / ctx->in.num_cols] || -+ ctx->in.tile[cur_tile].width != ctx->in.tile[next_tile].width || -+ ctx->in.tile[cur_tile].height != ctx->in.tile[next_tile].height || -+ ctx->out.tile[cur_tile].width != ctx->out.tile[next_tile].width || -+ ctx->out.tile[cur_tile].height != ctx->out.tile[next_tile].height) -+ return true; -+ -+ return false; -+} -+ -+/* hold irqlock when calling */ -+static irqreturn_t do_irq(struct ipu_image_convert_run *run) -+{ -+ struct ipu_image_convert_ctx *ctx = run->ctx; -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_tile *src_tile, *dst_tile; -+ struct ipu_image_convert_image *s_image = &ctx->in; -+ struct ipu_image_convert_image *d_image = &ctx->out; -+ struct ipuv3_channel *outch; -+ unsigned int dst_idx; -+ -+ lockdep_assert_held(&chan->irqlock); -+ -+ outch = ipu_rot_mode_is_irt(ctx->rot_mode) ? -+ chan->rotation_out_chan : chan->out_chan; -+ -+ /* -+ * It is difficult to stop the channel DMA before the channels -+ * enter the paused state. Without double-buffering the channels -+ * are always in a paused state when the EOF irq occurs, so it -+ * is safe to stop the channels now. For double-buffering we -+ * just ignore the abort until the operation completes, when it -+ * is safe to shut down. -+ */ -+ if (ctx->aborting && !ctx->double_buffering) { -+ convert_stop(run); -+ run->status = -EIO; -+ goto done; -+ } -+ -+ if (ctx->next_tile == ctx->num_tiles) { -+ /* -+ * the conversion is complete -+ */ -+ convert_stop(run); -+ run->status = 0; -+ goto done; -+ } -+ -+ /* -+ * not done, place the next tile buffers. -+ */ -+ if (!ctx->double_buffering) { -+ if (ic_settings_changed(ctx)) { -+ convert_stop(run); -+ convert_start(run, ctx->next_tile); -+ } else { -+ src_tile = &s_image->tile[ctx->next_tile]; -+ dst_idx = ctx->out_tile_map[ctx->next_tile]; -+ dst_tile = &d_image->tile[dst_idx]; -+ -+ ipu_cpmem_set_buffer(chan->in_chan, 0, -+ s_image->base.phys0 + -+ src_tile->offset); -+ ipu_cpmem_set_buffer(outch, 0, -+ d_image->base.phys0 + -+ dst_tile->offset); -+ if (s_image->fmt->planar) -+ ipu_cpmem_set_uv_offset(chan->in_chan, -+ src_tile->u_off, -+ src_tile->v_off); -+ if (d_image->fmt->planar) -+ ipu_cpmem_set_uv_offset(outch, -+ dst_tile->u_off, -+ dst_tile->v_off); -+ -+ ipu_idmac_select_buffer(chan->in_chan, 0); -+ ipu_idmac_select_buffer(outch, 0); -+ } -+ } else if (ctx->next_tile < ctx->num_tiles - 1) { -+ -+ src_tile = &s_image->tile[ctx->next_tile + 1]; -+ dst_idx = ctx->out_tile_map[ctx->next_tile + 1]; -+ dst_tile = &d_image->tile[dst_idx]; -+ -+ ipu_cpmem_set_buffer(chan->in_chan, ctx->cur_buf_num, -+ s_image->base.phys0 + src_tile->offset); -+ ipu_cpmem_set_buffer(outch, ctx->cur_buf_num, -+ d_image->base.phys0 + dst_tile->offset); -+ -+ ipu_idmac_select_buffer(chan->in_chan, ctx->cur_buf_num); -+ ipu_idmac_select_buffer(outch, ctx->cur_buf_num); -+ -+ ctx->cur_buf_num ^= 1; -+ } -+ -+ ctx->next_tile++; -+ return IRQ_HANDLED; -+done: -+ list_add_tail(&run->list, &chan->done_q); -+ chan->current_run = NULL; -+ run_next(chan); -+ return IRQ_WAKE_THREAD; -+} -+ -+static irqreturn_t norotate_irq(int irq, void *data) -+{ -+ struct ipu_image_convert_chan *chan = data; -+ struct ipu_image_convert_ctx *ctx; -+ struct ipu_image_convert_run *run; -+ unsigned long flags; -+ irqreturn_t ret; -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ /* get current run and its context */ -+ run = chan->current_run; -+ if (!run) { -+ ret = IRQ_NONE; -+ goto out; -+ } -+ -+ ctx = run->ctx; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ /* this is a rotation operation, just ignore */ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ return IRQ_HANDLED; -+ } -+ -+ ret = do_irq(run); -+out: -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ return ret; -+} -+ -+static irqreturn_t rotate_irq(int irq, void *data) -+{ -+ struct ipu_image_convert_chan *chan = data; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_ctx *ctx; -+ struct ipu_image_convert_run *run; -+ unsigned long flags; -+ irqreturn_t ret; -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ /* get current run and its context */ -+ run = chan->current_run; -+ if (!run) { -+ ret = IRQ_NONE; -+ goto out; -+ } -+ -+ ctx = run->ctx; -+ -+ if (!ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ /* this was NOT a rotation operation, shouldn't happen */ -+ dev_err(priv->ipu->dev, "Unexpected rotation interrupt\n"); -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ return IRQ_HANDLED; -+ } -+ -+ ret = do_irq(run); -+out: -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ return ret; -+} -+ -+/* -+ * try to force the completion of runs for this ctx. Called when -+ * abort wait times out in ipu_image_convert_abort(). -+ */ -+static void force_abort(struct ipu_image_convert_ctx *ctx) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_run *run; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ run = chan->current_run; -+ if (run && run->ctx == ctx) { -+ convert_stop(run); -+ run->status = -EIO; -+ list_add_tail(&run->list, &chan->done_q); -+ chan->current_run = NULL; -+ run_next(chan); -+ } -+ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ -+ empty_done_q(chan); -+} -+ -+static void release_ipu_resources(struct ipu_image_convert_chan *chan) -+{ -+ if (chan->out_eof_irq >= 0) -+ free_irq(chan->out_eof_irq, chan); -+ if (chan->rot_out_eof_irq >= 0) -+ free_irq(chan->rot_out_eof_irq, chan); -+ -+ if (!IS_ERR_OR_NULL(chan->in_chan)) -+ ipu_idmac_put(chan->in_chan); -+ if (!IS_ERR_OR_NULL(chan->out_chan)) -+ ipu_idmac_put(chan->out_chan); -+ if (!IS_ERR_OR_NULL(chan->rotation_in_chan)) -+ ipu_idmac_put(chan->rotation_in_chan); -+ if (!IS_ERR_OR_NULL(chan->rotation_out_chan)) -+ ipu_idmac_put(chan->rotation_out_chan); -+ if (!IS_ERR_OR_NULL(chan->ic)) -+ ipu_ic_put(chan->ic); -+ -+ chan->in_chan = chan->out_chan = chan->rotation_in_chan = -+ chan->rotation_out_chan = NULL; -+ chan->out_eof_irq = chan->rot_out_eof_irq = -1; -+} -+ -+static int get_ipu_resources(struct ipu_image_convert_chan *chan) -+{ -+ const struct ipu_image_convert_dma_chan *dma = chan->dma_ch; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ int ret; -+ -+ /* get IC */ -+ chan->ic = ipu_ic_get(priv->ipu, chan->ic_task); -+ if (IS_ERR(chan->ic)) { -+ dev_err(priv->ipu->dev, "could not acquire IC\n"); -+ ret = PTR_ERR(chan->ic); -+ goto err; -+ } -+ -+ /* get IDMAC channels */ -+ chan->in_chan = ipu_idmac_get(priv->ipu, dma->in); -+ chan->out_chan = ipu_idmac_get(priv->ipu, dma->out); -+ if (IS_ERR(chan->in_chan) || IS_ERR(chan->out_chan)) { -+ dev_err(priv->ipu->dev, "could not acquire idmac channels\n"); -+ ret = -EBUSY; -+ goto err; -+ } -+ -+ chan->rotation_in_chan = ipu_idmac_get(priv->ipu, dma->rot_in); -+ chan->rotation_out_chan = ipu_idmac_get(priv->ipu, dma->rot_out); -+ if (IS_ERR(chan->rotation_in_chan) || IS_ERR(chan->rotation_out_chan)) { -+ dev_err(priv->ipu->dev, -+ "could not acquire idmac rotation channels\n"); -+ ret = -EBUSY; -+ goto err; -+ } -+ -+ /* acquire the EOF interrupts */ -+ chan->out_eof_irq = ipu_idmac_channel_irq(priv->ipu, -+ chan->out_chan, -+ IPU_IRQ_EOF); -+ -+ ret = request_threaded_irq(chan->out_eof_irq, norotate_irq, do_bh, -+ 0, "ipu-ic", chan); -+ if (ret < 0) { -+ dev_err(priv->ipu->dev, "could not acquire irq %d\n", -+ chan->out_eof_irq); -+ chan->out_eof_irq = -1; -+ goto err; -+ } -+ -+ chan->rot_out_eof_irq = ipu_idmac_channel_irq(priv->ipu, -+ chan->rotation_out_chan, -+ IPU_IRQ_EOF); -+ -+ ret = request_threaded_irq(chan->rot_out_eof_irq, rotate_irq, do_bh, -+ 0, "ipu-ic", chan); -+ if (ret < 0) { -+ dev_err(priv->ipu->dev, "could not acquire irq %d\n", -+ chan->rot_out_eof_irq); -+ chan->rot_out_eof_irq = -1; -+ goto err; -+ } -+ -+ return 0; -+err: -+ release_ipu_resources(chan); -+ return ret; -+} -+ -+static int fill_image(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *ic_image, -+ struct ipu_image *image, -+ enum ipu_image_convert_type type) -+{ -+ struct ipu_image_convert_priv *priv = ctx->chan->priv; -+ -+ ic_image->base = *image; -+ ic_image->type = type; -+ -+ ic_image->fmt = get_format(image->pix.pixelformat); -+ if (!ic_image->fmt) { -+ dev_err(priv->ipu->dev, "pixelformat not supported for %s\n", -+ type == IMAGE_CONVERT_OUT ? "Output" : "Input"); -+ return -EINVAL; -+ } -+ -+ if (ic_image->fmt->planar) -+ ic_image->stride = ic_image->base.pix.width; -+ else -+ ic_image->stride = ic_image->base.pix.bytesperline; -+ -+ return 0; -+} -+ -+/* borrowed from drivers/media/v4l2-core/v4l2-common.c */ -+static unsigned int clamp_align(unsigned int x, unsigned int min, -+ unsigned int max, unsigned int align) -+{ -+ /* Bits that must be zero to be aligned */ -+ unsigned int mask = ~((1 << align) - 1); -+ -+ /* Clamp to aligned min and max */ -+ x = clamp(x, (min + ~mask) & mask, max & mask); -+ -+ /* Round to nearest aligned value */ -+ if (align) -+ x = (x + (1 << (align - 1))) & mask; -+ -+ return x; -+} -+ -+/* Adjusts input/output images to IPU restrictions */ -+void ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out, -+ enum ipu_rotate_mode rot_mode) -+{ -+ const struct ipu_image_pixfmt *infmt, *outfmt; -+ u32 w_align_out, h_align_out; -+ u32 w_align_in, h_align_in; -+ -+ infmt = get_format(in->pix.pixelformat); -+ outfmt = get_format(out->pix.pixelformat); -+ -+ /* set some default pixel formats if needed */ -+ if (!infmt) { -+ in->pix.pixelformat = V4L2_PIX_FMT_RGB24; -+ infmt = get_format(V4L2_PIX_FMT_RGB24); -+ } -+ if (!outfmt) { -+ out->pix.pixelformat = V4L2_PIX_FMT_RGB24; -+ outfmt = get_format(V4L2_PIX_FMT_RGB24); -+ } -+ -+ /* image converter does not handle fields */ -+ in->pix.field = out->pix.field = V4L2_FIELD_NONE; -+ -+ /* resizer cannot downsize more than 4:1 */ -+ if (ipu_rot_mode_is_irt(rot_mode)) { -+ out->pix.height = max_t(__u32, out->pix.height, -+ in->pix.width / 4); -+ out->pix.width = max_t(__u32, out->pix.width, -+ in->pix.height / 4); -+ } else { -+ out->pix.width = max_t(__u32, out->pix.width, -+ in->pix.width / 4); -+ out->pix.height = max_t(__u32, out->pix.height, -+ in->pix.height / 4); -+ } -+ -+ /* align input width/height */ -+ w_align_in = ilog2(tile_width_align(IMAGE_CONVERT_IN, infmt, -+ rot_mode)); -+ h_align_in = ilog2(tile_height_align(IMAGE_CONVERT_IN, infmt, -+ rot_mode)); -+ in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W, -+ w_align_in); -+ in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H, -+ h_align_in); -+ -+ /* align output width/height */ -+ w_align_out = ilog2(tile_width_align(IMAGE_CONVERT_OUT, outfmt, -+ rot_mode)); -+ h_align_out = ilog2(tile_height_align(IMAGE_CONVERT_OUT, outfmt, -+ rot_mode)); -+ out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W, -+ w_align_out); -+ out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H, -+ h_align_out); -+ -+ /* set input/output strides and image sizes */ -+ in->pix.bytesperline = infmt->planar ? -+ clamp_align(in->pix.width, 2 << w_align_in, MAX_W, -+ w_align_in) : -+ clamp_align((in->pix.width * infmt->bpp) >> 3, -+ ((2 << w_align_in) * infmt->bpp) >> 3, -+ (MAX_W * infmt->bpp) >> 3, -+ w_align_in); -+ in->pix.sizeimage = infmt->planar ? -+ (in->pix.height * in->pix.bytesperline * infmt->bpp) >> 3 : -+ in->pix.height * in->pix.bytesperline; -+ out->pix.bytesperline = outfmt->planar ? out->pix.width : -+ (out->pix.width * outfmt->bpp) >> 3; -+ out->pix.sizeimage = outfmt->planar ? -+ (out->pix.height * out->pix.bytesperline * outfmt->bpp) >> 3 : -+ out->pix.height * out->pix.bytesperline; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_adjust); -+ -+/* -+ * this is used by ipu_image_convert_prepare() to verify set input and -+ * output images are valid before starting the conversion. Clients can -+ * also call it before calling ipu_image_convert_prepare(). -+ */ -+int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out, -+ enum ipu_rotate_mode rot_mode) -+{ -+ struct ipu_image testin, testout; -+ -+ testin = *in; -+ testout = *out; -+ -+ ipu_image_convert_adjust(&testin, &testout, rot_mode); -+ -+ if (testin.pix.width != in->pix.width || -+ testin.pix.height != in->pix.height || -+ testout.pix.width != out->pix.width || -+ testout.pix.height != out->pix.height) -+ return -EINVAL; -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_verify); -+ -+/* -+ * Call ipu_image_convert_prepare() to prepare for the conversion of -+ * given images and rotation mode. Returns a new conversion context. -+ */ -+struct ipu_image_convert_ctx * -+ipu_image_convert_prepare(struct ipu_soc *ipu, enum ipu_ic_task ic_task, -+ struct ipu_image *in, struct ipu_image *out, -+ enum ipu_rotate_mode rot_mode, -+ ipu_image_convert_cb_t complete, -+ void *complete_context) -+{ -+ struct ipu_image_convert_priv *priv = ipu->image_convert_priv; -+ struct ipu_image_convert_image *s_image, *d_image; -+ struct ipu_image_convert_chan *chan; -+ struct ipu_image_convert_ctx *ctx; -+ unsigned long flags; -+ unsigned int i; -+ bool get_res; -+ int ret; -+ -+ if (!in || !out || !complete || -+ (ic_task != IC_TASK_VIEWFINDER && -+ ic_task != IC_TASK_POST_PROCESSOR)) -+ return ERR_PTR(-EINVAL); -+ -+ /* verify the in/out images before continuing */ -+ ret = ipu_image_convert_verify(in, out, rot_mode); -+ if (ret) { -+ dev_err(priv->ipu->dev, "%s: in/out formats invalid\n", -+ __func__); -+ return ERR_PTR(ret); -+ } -+ -+ chan = &priv->chan[ic_task]; -+ -+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); -+ if (!ctx) -+ return ERR_PTR(-ENOMEM); -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p\n", __func__, -+ chan->ic_task, ctx); -+ -+ ctx->chan = chan; -+ init_completion(&ctx->aborted); -+ -+ ctx->rot_mode = rot_mode; -+ -+ /* Sets ctx->in.num_rows/cols as well */ -+ ret = calc_image_resize_coefficients(ctx, in, out); -+ if (ret) -+ goto out_free; -+ -+ s_image = &ctx->in; -+ d_image = &ctx->out; -+ -+ /* set tiling and rotation */ -+ if (ipu_rot_mode_is_irt(rot_mode)) { -+ d_image->num_rows = s_image->num_cols; -+ d_image->num_cols = s_image->num_rows; -+ } else { -+ d_image->num_rows = s_image->num_rows; -+ d_image->num_cols = s_image->num_cols; -+ } -+ -+ ctx->num_tiles = d_image->num_cols * d_image->num_rows; -+ -+ ret = fill_image(ctx, s_image, in, IMAGE_CONVERT_IN); -+ if (ret) -+ goto out_free; -+ ret = fill_image(ctx, d_image, out, IMAGE_CONVERT_OUT); -+ if (ret) -+ goto out_free; -+ -+ calc_out_tile_map(ctx); -+ -+ find_seams(ctx, s_image, d_image); -+ -+ ret = calc_tile_dimensions(ctx, s_image); -+ if (ret) -+ goto out_free; -+ -+ ret = calc_tile_offsets(ctx, s_image); -+ if (ret) -+ goto out_free; -+ -+ calc_tile_dimensions(ctx, d_image); -+ ret = calc_tile_offsets(ctx, d_image); -+ if (ret) -+ goto out_free; -+ -+ calc_tile_resize_coefficients(ctx); -+ -+ ret = ipu_ic_calc_csc(&ctx->csc, -+ s_image->base.pix.ycbcr_enc, -+ s_image->base.pix.quantization, -+ ipu_pixelformat_to_colorspace(s_image->fmt->fourcc), -+ d_image->base.pix.ycbcr_enc, -+ d_image->base.pix.quantization, -+ ipu_pixelformat_to_colorspace(d_image->fmt->fourcc)); -+ if (ret) -+ goto out_free; -+ -+ dump_format(ctx, s_image); -+ dump_format(ctx, d_image); -+ -+ ctx->complete = complete; -+ ctx->complete_context = complete_context; -+ -+ /* -+ * Can we use double-buffering for this operation? If there is -+ * only one tile (the whole image can be converted in a single -+ * operation) there's no point in using double-buffering. Also, -+ * the IPU's IDMAC channels allow only a single U and V plane -+ * offset shared between both buffers, but these offsets change -+ * for every tile, and therefore would have to be updated for -+ * each buffer which is not possible. So double-buffering is -+ * impossible when either the source or destination images are -+ * a planar format (YUV420, YUV422P, etc.). Further, differently -+ * sized tiles or different resizing coefficients per tile -+ * prevent double-buffering as well. -+ */ -+ ctx->double_buffering = (ctx->num_tiles > 1 && -+ !s_image->fmt->planar && -+ !d_image->fmt->planar); -+ for (i = 1; i < ctx->num_tiles; i++) { -+ if (ctx->in.tile[i].width != ctx->in.tile[0].width || -+ ctx->in.tile[i].height != ctx->in.tile[0].height || -+ ctx->out.tile[i].width != ctx->out.tile[0].width || -+ ctx->out.tile[i].height != ctx->out.tile[0].height) { -+ ctx->double_buffering = false; -+ break; -+ } -+ } -+ for (i = 1; i < ctx->in.num_cols; i++) { -+ if (ctx->resize_coeffs_h[i] != ctx->resize_coeffs_h[0]) { -+ ctx->double_buffering = false; -+ break; -+ } -+ } -+ for (i = 1; i < ctx->in.num_rows; i++) { -+ if (ctx->resize_coeffs_v[i] != ctx->resize_coeffs_v[0]) { -+ ctx->double_buffering = false; -+ break; -+ } -+ } -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ unsigned long intermediate_size = d_image->tile[0].size; -+ -+ for (i = 1; i < ctx->num_tiles; i++) { -+ if (d_image->tile[i].size > intermediate_size) -+ intermediate_size = d_image->tile[i].size; -+ } -+ -+ ret = alloc_dma_buf(priv, &ctx->rot_intermediate[0], -+ intermediate_size); -+ if (ret) -+ goto out_free; -+ if (ctx->double_buffering) { -+ ret = alloc_dma_buf(priv, -+ &ctx->rot_intermediate[1], -+ intermediate_size); -+ if (ret) -+ goto out_free_dmabuf0; -+ } -+ } -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ get_res = list_empty(&chan->ctx_list); -+ -+ list_add_tail(&ctx->list, &chan->ctx_list); -+ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ -+ if (get_res) { -+ ret = get_ipu_resources(chan); -+ if (ret) -+ goto out_free_dmabuf1; -+ } -+ -+ return ctx; -+ -+out_free_dmabuf1: -+ free_dma_buf(priv, &ctx->rot_intermediate[1]); -+ spin_lock_irqsave(&chan->irqlock, flags); -+ list_del(&ctx->list); -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+out_free_dmabuf0: -+ free_dma_buf(priv, &ctx->rot_intermediate[0]); -+out_free: -+ kfree(ctx); -+ return ERR_PTR(ret); -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_prepare); -+ -+/* -+ * Carry out a single image conversion run. Only the physaddr's of the input -+ * and output image buffers are needed. The conversion context must have -+ * been created previously with ipu_image_convert_prepare(). -+ */ -+int ipu_image_convert_queue(struct ipu_image_convert_run *run) -+{ -+ struct ipu_image_convert_chan *chan; -+ struct ipu_image_convert_priv *priv; -+ struct ipu_image_convert_ctx *ctx; -+ unsigned long flags; -+ int ret = 0; -+ -+ if (!run || !run->ctx || !run->in_phys || !run->out_phys) -+ return -EINVAL; -+ -+ ctx = run->ctx; -+ chan = ctx->chan; -+ priv = chan->priv; -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p run %p\n", __func__, -+ chan->ic_task, ctx, run); -+ -+ INIT_LIST_HEAD(&run->list); -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ if (ctx->aborting) { -+ ret = -EIO; -+ goto unlock; -+ } -+ -+ list_add_tail(&run->list, &chan->pending_q); -+ -+ if (!chan->current_run) { -+ ret = do_run(run); -+ if (ret) -+ chan->current_run = NULL; -+ } -+unlock: -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_queue); -+ -+/* Abort any active or pending conversions for this context */ -+static void __ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_run *run, *active_run, *tmp; -+ unsigned long flags; -+ int run_count, ret; -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ /* move all remaining pending runs in this context to done_q */ -+ list_for_each_entry_safe(run, tmp, &chan->pending_q, list) { -+ if (run->ctx != ctx) -+ continue; -+ run->status = -EIO; -+ list_move_tail(&run->list, &chan->done_q); -+ } -+ -+ run_count = get_run_count(ctx, &chan->done_q); -+ active_run = (chan->current_run && chan->current_run->ctx == ctx) ? -+ chan->current_run : NULL; -+ -+ if (active_run) -+ reinit_completion(&ctx->aborted); -+ -+ ctx->aborting = true; -+ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ -+ if (!run_count && !active_run) { -+ dev_dbg(priv->ipu->dev, -+ "%s: task %u: no abort needed for ctx %p\n", -+ __func__, chan->ic_task, ctx); -+ return; -+ } -+ -+ if (!active_run) { -+ empty_done_q(chan); -+ return; -+ } -+ -+ dev_dbg(priv->ipu->dev, -+ "%s: task %u: wait for completion: %d runs\n", -+ __func__, chan->ic_task, run_count); -+ -+ ret = wait_for_completion_timeout(&ctx->aborted, -+ msecs_to_jiffies(10000)); -+ if (ret == 0) { -+ dev_warn(priv->ipu->dev, "%s: timeout\n", __func__); -+ force_abort(ctx); -+ } -+} -+ -+void ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx) -+{ -+ __ipu_image_convert_abort(ctx); -+ ctx->aborting = false; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_abort); -+ -+/* Unprepare image conversion context */ -+void ipu_image_convert_unprepare(struct ipu_image_convert_ctx *ctx) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ unsigned long flags; -+ bool put_res; -+ -+ /* make sure no runs are hanging around */ -+ __ipu_image_convert_abort(ctx); -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: removing ctx %p\n", __func__, -+ chan->ic_task, ctx); -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ list_del(&ctx->list); -+ -+ put_res = list_empty(&chan->ctx_list); -+ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ -+ if (put_res) -+ release_ipu_resources(chan); -+ -+ free_dma_buf(priv, &ctx->rot_intermediate[1]); -+ free_dma_buf(priv, &ctx->rot_intermediate[0]); -+ -+ kfree(ctx); -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_unprepare); -+ -+/* -+ * "Canned" asynchronous single image conversion. Allocates and returns -+ * a new conversion run. On successful return the caller must free the -+ * run and call ipu_image_convert_unprepare() after conversion completes. -+ */ -+struct ipu_image_convert_run * -+ipu_image_convert(struct ipu_soc *ipu, enum ipu_ic_task ic_task, -+ struct ipu_image *in, struct ipu_image *out, -+ enum ipu_rotate_mode rot_mode, -+ ipu_image_convert_cb_t complete, -+ void *complete_context) -+{ -+ struct ipu_image_convert_ctx *ctx; -+ struct ipu_image_convert_run *run; -+ int ret; -+ -+ ctx = ipu_image_convert_prepare(ipu, ic_task, in, out, rot_mode, -+ complete, complete_context); -+ if (IS_ERR(ctx)) -+ return ERR_CAST(ctx); -+ -+ run = kzalloc(sizeof(*run), GFP_KERNEL); -+ if (!run) { -+ ipu_image_convert_unprepare(ctx); -+ return ERR_PTR(-ENOMEM); -+ } -+ -+ run->ctx = ctx; -+ run->in_phys = in->phys0; -+ run->out_phys = out->phys0; -+ -+ ret = ipu_image_convert_queue(run); -+ if (ret) { -+ ipu_image_convert_unprepare(ctx); -+ kfree(run); -+ return ERR_PTR(ret); -+ } -+ -+ return run; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert); -+ -+/* "Canned" synchronous single image conversion */ -+static void image_convert_sync_complete(struct ipu_image_convert_run *run, -+ void *data) -+{ -+ struct completion *comp = data; -+ -+ complete(comp); -+} -+ -+int ipu_image_convert_sync(struct ipu_soc *ipu, enum ipu_ic_task ic_task, -+ struct ipu_image *in, struct ipu_image *out, -+ enum ipu_rotate_mode rot_mode) -+{ -+ struct ipu_image_convert_run *run; -+ struct completion comp; -+ int ret; -+ -+ init_completion(&comp); -+ -+ run = ipu_image_convert(ipu, ic_task, in, out, rot_mode, -+ image_convert_sync_complete, &comp); -+ if (IS_ERR(run)) -+ return PTR_ERR(run); -+ -+ ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(10000)); -+ ret = (ret == 0) ? -ETIMEDOUT : 0; -+ -+ ipu_image_convert_unprepare(run->ctx); -+ kfree(run); -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_sync); -+ -+int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev) -+{ -+ struct ipu_image_convert_priv *priv; -+ int i; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ ipu->image_convert_priv = priv; -+ priv->ipu = ipu; -+ -+ for (i = 0; i < IC_NUM_TASKS; i++) { -+ struct ipu_image_convert_chan *chan = &priv->chan[i]; -+ -+ chan->ic_task = i; -+ chan->priv = priv; -+ chan->dma_ch = &image_convert_dma_chan[i]; -+ chan->out_eof_irq = -1; -+ chan->rot_out_eof_irq = -1; -+ -+ spin_lock_init(&chan->irqlock); -+ INIT_LIST_HEAD(&chan->ctx_list); -+ INIT_LIST_HEAD(&chan->pending_q); -+ INIT_LIST_HEAD(&chan->done_q); -+ } -+ -+ return 0; -+} -+ -+void ipu_image_convert_exit(struct ipu_soc *ipu) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-pre.c -@@ -0,0 +1,346 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (c) 2017 Lucas Stach, Pengutronix -+ */ -+ -+#include <drm/drm_fourcc.h> -+#include <linux/clk.h> -+#include <linux/err.h> -+#include <linux/genalloc.h> -+#include <linux/module.h> -+#include <linux/of.h> -+#include <linux/platform_device.h> -+#include <video/imx-ipu-v3.h> -+ -+#include "ipu-prv.h" -+ -+#define IPU_PRE_MAX_WIDTH 2048 -+#define IPU_PRE_NUM_SCANLINES 8 -+ -+#define IPU_PRE_CTRL 0x000 -+#define IPU_PRE_CTRL_SET 0x004 -+#define IPU_PRE_CTRL_ENABLE (1 << 0) -+#define IPU_PRE_CTRL_BLOCK_EN (1 << 1) -+#define IPU_PRE_CTRL_BLOCK_16 (1 << 2) -+#define IPU_PRE_CTRL_SDW_UPDATE (1 << 4) -+#define IPU_PRE_CTRL_VFLIP (1 << 5) -+#define IPU_PRE_CTRL_SO (1 << 6) -+#define IPU_PRE_CTRL_INTERLACED_FIELD (1 << 7) -+#define IPU_PRE_CTRL_HANDSHAKE_EN (1 << 8) -+#define IPU_PRE_CTRL_HANDSHAKE_LINE_NUM(v) ((v & 0x3) << 9) -+#define IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN (1 << 11) -+#define IPU_PRE_CTRL_EN_REPEAT (1 << 28) -+#define IPU_PRE_CTRL_TPR_REST_SEL (1 << 29) -+#define IPU_PRE_CTRL_CLKGATE (1 << 30) -+#define IPU_PRE_CTRL_SFTRST (1 << 31) -+ -+#define IPU_PRE_CUR_BUF 0x030 -+ -+#define IPU_PRE_NEXT_BUF 0x040 -+ -+#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) -+#define IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(v) ((v & 0x7) << 1) -+#define IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) -+#define IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(v) ((v & 0x7) << 8) -+#define IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS (1 << 11) -+#define IPU_PRE_PREF_ENG_CTRL_FIELD_INVERSE (1 << 12) -+#define IPU_PRE_PREF_ENG_CTRL_PARTIAL_UV_SWAP (1 << 14) -+#define IPU_PRE_PREF_ENG_CTRL_TPR_COOR_OFFSET_EN (1 << 15) -+ -+#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE 0x0a0 -+#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(v) ((v & 0xffff) << 0) -+#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(v) ((v & 0xffff) << 16) -+ -+#define IPU_PRE_PREFETCH_ENG_PITCH 0x0d0 -+#define IPU_PRE_PREFETCH_ENG_PITCH_Y(v) ((v & 0xffff) << 0) -+#define IPU_PRE_PREFETCH_ENG_PITCH_UV(v) ((v & 0xffff) << 16) -+ -+#define IPU_PRE_STORE_ENG_CTRL 0x110 -+#define IPU_PRE_STORE_ENG_CTRL_STORE_EN (1 << 0) -+#define IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(v) ((v & 0x7) << 1) -+#define IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) -+ -+#define IPU_PRE_STORE_ENG_STATUS 0x120 -+#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_MASK 0xffff -+#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_SHIFT 0 -+#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK 0x3fff -+#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT 16 -+#define IPU_PRE_STORE_ENG_STATUS_STORE_FIFO_FULL (1 << 30) -+#define IPU_PRE_STORE_ENG_STATUS_STORE_FIELD (1 << 31) -+ -+#define IPU_PRE_STORE_ENG_SIZE 0x130 -+#define IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(v) ((v & 0xffff) << 0) -+#define IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(v) ((v & 0xffff) << 16) -+ -+#define IPU_PRE_STORE_ENG_PITCH 0x140 -+#define IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(v) ((v & 0xffff) << 0) -+ -+#define IPU_PRE_STORE_ENG_ADDR 0x150 -+ -+struct ipu_pre { -+ struct list_head list; -+ struct device *dev; -+ -+ void __iomem *regs; -+ struct clk *clk_axi; -+ struct gen_pool *iram; -+ -+ dma_addr_t buffer_paddr; -+ void *buffer_virt; -+ bool in_use; -+ unsigned int safe_window_end; -+ unsigned int last_bufaddr; -+}; -+ -+static DEFINE_MUTEX(ipu_pre_list_mutex); -+static LIST_HEAD(ipu_pre_list); -+static int available_pres; -+ -+int ipu_pre_get_available_count(void) -+{ -+ return available_pres; -+} -+ -+struct ipu_pre * -+ipu_pre_lookup_by_phandle(struct device *dev, const char *name, int index) -+{ -+ struct device_node *pre_node = of_parse_phandle(dev->of_node, -+ name, index); -+ struct ipu_pre *pre; -+ -+ mutex_lock(&ipu_pre_list_mutex); -+ list_for_each_entry(pre, &ipu_pre_list, list) { -+ if (pre_node == pre->dev->of_node) { -+ mutex_unlock(&ipu_pre_list_mutex); -+ device_link_add(dev, pre->dev, -+ DL_FLAG_AUTOREMOVE_CONSUMER); -+ of_node_put(pre_node); -+ return pre; -+ } -+ } -+ mutex_unlock(&ipu_pre_list_mutex); -+ -+ of_node_put(pre_node); -+ -+ return NULL; -+} -+ -+int ipu_pre_get(struct ipu_pre *pre) -+{ -+ u32 val; -+ -+ if (pre->in_use) -+ return -EBUSY; -+ -+ /* first get the engine out of reset and remove clock gating */ -+ writel(0, pre->regs + IPU_PRE_CTRL); -+ -+ /* init defaults that should be applied to all streams */ -+ val = IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN | -+ IPU_PRE_CTRL_HANDSHAKE_EN | -+ IPU_PRE_CTRL_TPR_REST_SEL | -+ IPU_PRE_CTRL_SDW_UPDATE; -+ writel(val, pre->regs + IPU_PRE_CTRL); -+ -+ pre->in_use = true; -+ return 0; -+} -+ -+void ipu_pre_put(struct ipu_pre *pre) -+{ -+ writel(IPU_PRE_CTRL_SFTRST, pre->regs + IPU_PRE_CTRL); -+ -+ pre->in_use = false; -+} -+ -+void ipu_pre_configure(struct ipu_pre *pre, unsigned int width, -+ unsigned int height, unsigned int stride, u32 format, -+ 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 */ -+ 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); -+ pre->last_bufaddr = bufaddr; -+ -+ val = IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(0) | -+ IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(active_bpp) | -+ IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(4) | -+ IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS | -+ IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN; -+ writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_CTRL); -+ -+ val = IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(width) | -+ IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(height); -+ writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_INPUT_SIZE); -+ -+ val = IPU_PRE_PREFETCH_ENG_PITCH_Y(stride); -+ writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_PITCH); -+ -+ val = IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(active_bpp) | -+ IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(4) | -+ IPU_PRE_STORE_ENG_CTRL_STORE_EN; -+ writel(val, pre->regs + IPU_PRE_STORE_ENG_CTRL); -+ -+ val = IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(width) | -+ IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(height); -+ writel(val, pre->regs + IPU_PRE_STORE_ENG_SIZE); -+ -+ val = IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(stride); -+ writel(val, pre->regs + IPU_PRE_STORE_ENG_PITCH); -+ -+ 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); -+} -+ -+void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr) -+{ -+ unsigned long timeout = jiffies + msecs_to_jiffies(5); -+ unsigned short current_yblock; -+ u32 val; -+ -+ if (bufaddr == pre->last_bufaddr) -+ return; -+ -+ writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF); -+ pre->last_bufaddr = bufaddr; -+ -+ do { -+ if (time_after(jiffies, timeout)) { -+ dev_warn(pre->dev, "timeout waiting for PRE safe window\n"); -+ return; -+ } -+ -+ val = readl(pre->regs + IPU_PRE_STORE_ENG_STATUS); -+ current_yblock = -+ (val >> IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT) & -+ IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK; -+ } while (current_yblock == 0 || current_yblock >= pre->safe_window_end); -+ -+ writel(IPU_PRE_CTRL_SDW_UPDATE, pre->regs + IPU_PRE_CTRL_SET); -+} -+ -+bool ipu_pre_update_pending(struct ipu_pre *pre) -+{ -+ return !!(readl_relaxed(pre->regs + IPU_PRE_CTRL) & -+ IPU_PRE_CTRL_SDW_UPDATE); -+} -+ -+u32 ipu_pre_get_baddr(struct ipu_pre *pre) -+{ -+ return (u32)pre->buffer_paddr; -+} -+ -+static int ipu_pre_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct resource *res; -+ struct ipu_pre *pre; -+ -+ pre = devm_kzalloc(dev, sizeof(*pre), GFP_KERNEL); -+ if (!pre) -+ return -ENOMEM; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ pre->regs = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(pre->regs)) -+ return PTR_ERR(pre->regs); -+ -+ pre->clk_axi = devm_clk_get(dev, "axi"); -+ if (IS_ERR(pre->clk_axi)) -+ return PTR_ERR(pre->clk_axi); -+ -+ pre->iram = of_gen_pool_get(dev->of_node, "fsl,iram", 0); -+ if (!pre->iram) -+ return -EPROBE_DEFER; -+ -+ /* -+ * Allocate IRAM buffer with maximum size. This could be made dynamic, -+ * but as there is no other user of this IRAM region and we can fit all -+ * max sized buffers into it, there is no need yet. -+ */ -+ pre->buffer_virt = gen_pool_dma_alloc(pre->iram, IPU_PRE_MAX_WIDTH * -+ IPU_PRE_NUM_SCANLINES * 4, -+ &pre->buffer_paddr); -+ if (!pre->buffer_virt) -+ return -ENOMEM; -+ -+ clk_prepare_enable(pre->clk_axi); -+ -+ pre->dev = dev; -+ platform_set_drvdata(pdev, pre); -+ mutex_lock(&ipu_pre_list_mutex); -+ list_add(&pre->list, &ipu_pre_list); -+ available_pres++; -+ mutex_unlock(&ipu_pre_list_mutex); -+ -+ return 0; -+} -+ -+static int ipu_pre_remove(struct platform_device *pdev) -+{ -+ struct ipu_pre *pre = platform_get_drvdata(pdev); -+ -+ mutex_lock(&ipu_pre_list_mutex); -+ list_del(&pre->list); -+ available_pres--; -+ mutex_unlock(&ipu_pre_list_mutex); -+ -+ clk_disable_unprepare(pre->clk_axi); -+ -+ if (pre->buffer_virt) -+ gen_pool_free(pre->iram, (unsigned long)pre->buffer_virt, -+ IPU_PRE_MAX_WIDTH * IPU_PRE_NUM_SCANLINES * 4); -+ return 0; -+} -+ -+static const struct of_device_id ipu_pre_dt_ids[] = { -+ { .compatible = "fsl,imx6qp-pre", }, -+ { /* sentinel */ }, -+}; -+ -+struct platform_driver ipu_pre_drv = { -+ .probe = ipu_pre_probe, -+ .remove = ipu_pre_remove, -+ .driver = { -+ .name = "imx-ipu-pre", -+ .of_match_table = ipu_pre_dt_ids, -+ }, -+}; ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-prg.c -@@ -0,0 +1,483 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (c) 2016-2017 Lucas Stach, Pengutronix -+ */ -+ -+#include <drm/drm_fourcc.h> -+#include <linux/clk.h> -+#include <linux/err.h> -+#include <linux/iopoll.h> -+#include <linux/mfd/syscon.h> -+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> -+#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> -+ -+#include "ipu-prv.h" -+ -+#define IPU_PRG_CTL 0x00 -+#define IPU_PRG_CTL_BYPASS(i) (1 << (0 + i)) -+#define IPU_PRG_CTL_SOFT_ARID_MASK 0x3 -+#define IPU_PRG_CTL_SOFT_ARID_SHIFT(i) (8 + i * 2) -+#define IPU_PRG_CTL_SOFT_ARID(i, v) ((v & 0x3) << (8 + 2 * i)) -+#define IPU_PRG_CTL_SO(i) (1 << (16 + i)) -+#define IPU_PRG_CTL_VFLIP(i) (1 << (19 + i)) -+#define IPU_PRG_CTL_BLOCK_MODE(i) (1 << (22 + i)) -+#define IPU_PRG_CTL_CNT_LOAD_EN(i) (1 << (25 + i)) -+#define IPU_PRG_CTL_SOFTRST (1 << 30) -+#define IPU_PRG_CTL_SHADOW_EN (1 << 31) -+ -+#define IPU_PRG_STATUS 0x04 -+#define IPU_PRG_STATUS_BUFFER0_READY(i) (1 << (0 + i * 2)) -+#define IPU_PRG_STATUS_BUFFER1_READY(i) (1 << (1 + i * 2)) -+ -+#define IPU_PRG_QOS 0x08 -+#define IPU_PRG_QOS_ARID_MASK 0xf -+#define IPU_PRG_QOS_ARID_SHIFT(i) (0 + i * 4) -+ -+#define IPU_PRG_REG_UPDATE 0x0c -+#define IPU_PRG_REG_UPDATE_REG_UPDATE (1 << 0) -+ -+#define IPU_PRG_STRIDE(i) (0x10 + i * 0x4) -+#define IPU_PRG_STRIDE_STRIDE_MASK 0x3fff -+ -+#define IPU_PRG_CROP_LINE 0x1c -+ -+#define IPU_PRG_THD 0x20 -+ -+#define IPU_PRG_BADDR(i) (0x24 + i * 0x4) -+ -+#define IPU_PRG_OFFSET(i) (0x30 + i * 0x4) -+ -+#define IPU_PRG_ILO(i) (0x3c + i * 0x4) -+ -+#define IPU_PRG_HEIGHT(i) (0x48 + i * 0x4) -+#define IPU_PRG_HEIGHT_PRE_HEIGHT_MASK 0xfff -+#define IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT 0 -+#define IPU_PRG_HEIGHT_IPU_HEIGHT_MASK 0xfff -+#define IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT 16 -+ -+struct ipu_prg_channel { -+ bool enabled; -+ int used_pre; -+}; -+ -+struct ipu_prg { -+ struct list_head list; -+ struct device *dev; -+ int id; -+ -+ void __iomem *regs; -+ struct clk *clk_ipg, *clk_axi; -+ struct regmap *iomuxc_gpr; -+ struct ipu_pre *pres[3]; -+ -+ struct ipu_prg_channel chan[3]; -+}; -+ -+static DEFINE_MUTEX(ipu_prg_list_mutex); -+static LIST_HEAD(ipu_prg_list); -+ -+struct ipu_prg * -+ipu_prg_lookup_by_phandle(struct device *dev, const char *name, int ipu_id) -+{ -+ struct device_node *prg_node = of_parse_phandle(dev->of_node, -+ name, 0); -+ struct ipu_prg *prg; -+ -+ mutex_lock(&ipu_prg_list_mutex); -+ list_for_each_entry(prg, &ipu_prg_list, list) { -+ if (prg_node == prg->dev->of_node) { -+ mutex_unlock(&ipu_prg_list_mutex); -+ device_link_add(dev, prg->dev, -+ DL_FLAG_AUTOREMOVE_CONSUMER); -+ prg->id = ipu_id; -+ of_node_put(prg_node); -+ return prg; -+ } -+ } -+ mutex_unlock(&ipu_prg_list_mutex); -+ -+ of_node_put(prg_node); -+ -+ return NULL; -+} -+ -+int ipu_prg_max_active_channels(void) -+{ -+ return ipu_pre_get_available_count(); -+} -+EXPORT_SYMBOL_GPL(ipu_prg_max_active_channels); -+ -+bool ipu_prg_present(struct ipu_soc *ipu) -+{ -+ if (ipu->prg_priv) -+ return true; -+ -+ return false; -+} -+EXPORT_SYMBOL_GPL(ipu_prg_present); -+ -+bool ipu_prg_format_supported(struct ipu_soc *ipu, uint32_t format, -+ uint64_t modifier) -+{ -+ const struct drm_format_info *info = drm_format_info(format); -+ -+ if (info->num_planes != 1) -+ return false; -+ -+ 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; -+ -+ if (!prg) -+ return 0; -+ -+ return pm_runtime_get_sync(prg->dev); -+} -+EXPORT_SYMBOL_GPL(ipu_prg_enable); -+ -+void ipu_prg_disable(struct ipu_soc *ipu) -+{ -+ struct ipu_prg *prg = ipu->prg_priv; -+ -+ if (!prg) -+ return; -+ -+ pm_runtime_put(prg->dev); -+} -+EXPORT_SYMBOL_GPL(ipu_prg_disable); -+ -+/* -+ * The channel configuartion functions below are not thread safe, as they -+ * must be only called from the atomic commit path in the DRM driver, which -+ * is properly serialized. -+ */ -+static int ipu_prg_ipu_to_prg_chan(int ipu_chan) -+{ -+ /* -+ * This isn't clearly documented in the RM, but IPU to PRG channel -+ * assignment is fixed, as only with this mapping the control signals -+ * match up. -+ */ -+ switch (ipu_chan) { -+ case IPUV3_CHANNEL_MEM_BG_SYNC: -+ return 0; -+ case IPUV3_CHANNEL_MEM_FG_SYNC: -+ return 1; -+ case IPUV3_CHANNEL_MEM_DC_SYNC: -+ return 2; -+ default: -+ return -EINVAL; -+ } -+} -+ -+static int ipu_prg_get_pre(struct ipu_prg *prg, int prg_chan) -+{ -+ int i, ret; -+ -+ /* channel 0 is special as it is hardwired to one of the PREs */ -+ if (prg_chan == 0) { -+ ret = ipu_pre_get(prg->pres[0]); -+ if (ret) -+ goto fail; -+ prg->chan[prg_chan].used_pre = 0; -+ return 0; -+ } -+ -+ for (i = 1; i < 3; i++) { -+ ret = ipu_pre_get(prg->pres[i]); -+ if (!ret) { -+ u32 val, mux; -+ int shift; -+ -+ prg->chan[prg_chan].used_pre = i; -+ -+ /* configure the PRE to PRG channel mux */ -+ shift = (i == 1) ? 12 : 14; -+ mux = (prg->id << 1) | (prg_chan - 1); -+ regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5, -+ 0x3 << shift, mux << shift); -+ -+ /* check other mux, must not point to same channel */ -+ shift = (i == 1) ? 14 : 12; -+ regmap_read(prg->iomuxc_gpr, IOMUXC_GPR5, &val); -+ if (((val >> shift) & 0x3) == mux) { -+ regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5, -+ 0x3 << shift, -+ (mux ^ 0x1) << shift); -+ } -+ -+ return 0; -+ } -+ } -+ -+fail: -+ dev_err(prg->dev, "could not get PRE for PRG chan %d", prg_chan); -+ return ret; -+} -+ -+static void ipu_prg_put_pre(struct ipu_prg *prg, int prg_chan) -+{ -+ struct ipu_prg_channel *chan = &prg->chan[prg_chan]; -+ -+ ipu_pre_put(prg->pres[chan->used_pre]); -+ chan->used_pre = -1; -+} -+ -+void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan) -+{ -+ int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); -+ struct ipu_prg *prg = ipu_chan->ipu->prg_priv; -+ struct ipu_prg_channel *chan; -+ u32 val; -+ -+ if (prg_chan < 0) -+ return; -+ -+ chan = &prg->chan[prg_chan]; -+ if (!chan->enabled) -+ return; -+ -+ pm_runtime_get_sync(prg->dev); -+ -+ val = readl(prg->regs + IPU_PRG_CTL); -+ val |= IPU_PRG_CTL_BYPASS(prg_chan); -+ writel(val, prg->regs + IPU_PRG_CTL); -+ -+ val = IPU_PRG_REG_UPDATE_REG_UPDATE; -+ writel(val, prg->regs + IPU_PRG_REG_UPDATE); -+ -+ pm_runtime_put(prg->dev); -+ -+ ipu_prg_put_pre(prg, prg_chan); -+ -+ chan->enabled = false; -+} -+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, 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; -+ struct ipu_prg_channel *chan; -+ u32 val; -+ int ret; -+ -+ if (prg_chan < 0) -+ return prg_chan; -+ -+ chan = &prg->chan[prg_chan]; -+ -+ if (chan->enabled) { -+ ipu_pre_update(prg->pres[chan->used_pre], *eba); -+ return 0; -+ } -+ -+ ret = ipu_prg_get_pre(prg, prg_chan); -+ if (ret) -+ return ret; -+ -+ ipu_pre_configure(prg->pres[chan->used_pre], -+ width, height, stride, format, modifier, *eba); -+ -+ -+ pm_runtime_get_sync(prg->dev); -+ -+ val = (stride - 1) & IPU_PRG_STRIDE_STRIDE_MASK; -+ writel(val, prg->regs + IPU_PRG_STRIDE(prg_chan)); -+ -+ val = ((height & IPU_PRG_HEIGHT_PRE_HEIGHT_MASK) << -+ IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT) | -+ ((height & IPU_PRG_HEIGHT_IPU_HEIGHT_MASK) << -+ IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT); -+ writel(val, prg->regs + IPU_PRG_HEIGHT(prg_chan)); -+ -+ val = ipu_pre_get_baddr(prg->pres[chan->used_pre]); -+ *eba = val; -+ writel(val, prg->regs + IPU_PRG_BADDR(prg_chan)); -+ -+ val = readl(prg->regs + IPU_PRG_CTL); -+ /* config AXI ID */ -+ val &= ~(IPU_PRG_CTL_SOFT_ARID_MASK << -+ IPU_PRG_CTL_SOFT_ARID_SHIFT(prg_chan)); -+ val |= IPU_PRG_CTL_SOFT_ARID(prg_chan, axi_id); -+ /* enable channel */ -+ val &= ~IPU_PRG_CTL_BYPASS(prg_chan); -+ writel(val, prg->regs + IPU_PRG_CTL); -+ -+ val = IPU_PRG_REG_UPDATE_REG_UPDATE; -+ writel(val, prg->regs + IPU_PRG_REG_UPDATE); -+ -+ /* wait for both double buffers to be filled */ -+ readl_poll_timeout(prg->regs + IPU_PRG_STATUS, val, -+ (val & IPU_PRG_STATUS_BUFFER0_READY(prg_chan)) && -+ (val & IPU_PRG_STATUS_BUFFER1_READY(prg_chan)), -+ 5, 1000); -+ -+ pm_runtime_put(prg->dev); -+ -+ chan->enabled = true; -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_prg_channel_configure); -+ -+bool ipu_prg_channel_configure_pending(struct ipuv3_channel *ipu_chan) -+{ -+ int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); -+ struct ipu_prg *prg = ipu_chan->ipu->prg_priv; -+ struct ipu_prg_channel *chan; -+ -+ if (prg_chan < 0) -+ return false; -+ -+ chan = &prg->chan[prg_chan]; -+ WARN_ON(!chan->enabled); -+ -+ return ipu_pre_update_pending(prg->pres[chan->used_pre]); -+} -+EXPORT_SYMBOL_GPL(ipu_prg_channel_configure_pending); -+ -+static int ipu_prg_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct resource *res; -+ struct ipu_prg *prg; -+ u32 val; -+ int i, ret; -+ -+ prg = devm_kzalloc(dev, sizeof(*prg), GFP_KERNEL); -+ if (!prg) -+ return -ENOMEM; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ prg->regs = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(prg->regs)) -+ return PTR_ERR(prg->regs); -+ -+ -+ prg->clk_ipg = devm_clk_get(dev, "ipg"); -+ if (IS_ERR(prg->clk_ipg)) -+ return PTR_ERR(prg->clk_ipg); -+ -+ prg->clk_axi = devm_clk_get(dev, "axi"); -+ if (IS_ERR(prg->clk_axi)) -+ return PTR_ERR(prg->clk_axi); -+ -+ prg->iomuxc_gpr = -+ syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); -+ if (IS_ERR(prg->iomuxc_gpr)) -+ return PTR_ERR(prg->iomuxc_gpr); -+ -+ for (i = 0; i < 3; i++) { -+ prg->pres[i] = ipu_pre_lookup_by_phandle(dev, "fsl,pres", i); -+ if (!prg->pres[i]) -+ return -EPROBE_DEFER; -+ } -+ -+ 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; -+ } -+ -+ /* init to free running mode */ -+ val = readl(prg->regs + IPU_PRG_CTL); -+ val |= IPU_PRG_CTL_SHADOW_EN; -+ writel(val, prg->regs + IPU_PRG_CTL); -+ -+ /* disable address threshold */ -+ writel(0xffffffff, prg->regs + IPU_PRG_THD); -+ -+ pm_runtime_set_active(dev); -+ pm_runtime_enable(dev); -+ -+ prg->dev = dev; -+ platform_set_drvdata(pdev, prg); -+ mutex_lock(&ipu_prg_list_mutex); -+ list_add(&prg->list, &ipu_prg_list); -+ mutex_unlock(&ipu_prg_list_mutex); -+ -+ return 0; -+} -+ -+static int ipu_prg_remove(struct platform_device *pdev) -+{ -+ struct ipu_prg *prg = platform_get_drvdata(pdev); -+ -+ mutex_lock(&ipu_prg_list_mutex); -+ list_del(&prg->list); -+ mutex_unlock(&ipu_prg_list_mutex); -+ -+ 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 */ }, -+}; -+ -+struct platform_driver ipu_prg_drv = { -+ .probe = ipu_prg_probe, -+ .remove = ipu_prg_remove, -+ .driver = { -+ .name = "imx-ipu-prg", -+ .pm = &prg_pm_ops, -+ .of_match_table = ipu_prg_dt_ids, -+ }, -+}; ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-prv.h -@@ -0,0 +1,274 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+/* -+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#ifndef __IPU_PRV_H__ -+#define __IPU_PRV_H__ -+ -+struct ipu_soc; -+ -+#include <linux/types.h> -+#include <linux/device.h> -+#include <linux/clk.h> -+#include <linux/platform_device.h> -+ -+#include <video/imx-ipu-v3.h> -+ -+#define IPU_MCU_T_DEFAULT 8 -+#define IPU_CM_IDMAC_REG_OFS 0x00008000 -+#define IPU_CM_IC_REG_OFS 0x00020000 -+#define IPU_CM_IRT_REG_OFS 0x00028000 -+#define IPU_CM_CSI0_REG_OFS 0x00030000 -+#define IPU_CM_CSI1_REG_OFS 0x00038000 -+#define IPU_CM_SMFC_REG_OFS 0x00050000 -+#define IPU_CM_DC_REG_OFS 0x00058000 -+#define IPU_CM_DMFC_REG_OFS 0x00060000 -+ -+/* Register addresses */ -+/* IPU Common registers */ -+#define IPU_CM_REG(offset) (offset) -+ -+#define IPU_CONF IPU_CM_REG(0) -+ -+#define IPU_SRM_PRI1 IPU_CM_REG(0x00a0) -+#define IPU_SRM_PRI2 IPU_CM_REG(0x00a4) -+#define IPU_FS_PROC_FLOW1 IPU_CM_REG(0x00a8) -+#define IPU_FS_PROC_FLOW2 IPU_CM_REG(0x00ac) -+#define IPU_FS_PROC_FLOW3 IPU_CM_REG(0x00b0) -+#define IPU_FS_DISP_FLOW1 IPU_CM_REG(0x00b4) -+#define IPU_FS_DISP_FLOW2 IPU_CM_REG(0x00b8) -+#define IPU_SKIP IPU_CM_REG(0x00bc) -+#define IPU_DISP_ALT_CONF IPU_CM_REG(0x00c0) -+#define IPU_DISP_GEN IPU_CM_REG(0x00c4) -+#define IPU_DISP_ALT1 IPU_CM_REG(0x00c8) -+#define IPU_DISP_ALT2 IPU_CM_REG(0x00cc) -+#define IPU_DISP_ALT3 IPU_CM_REG(0x00d0) -+#define IPU_DISP_ALT4 IPU_CM_REG(0x00d4) -+#define IPU_SNOOP IPU_CM_REG(0x00d8) -+#define IPU_MEM_RST IPU_CM_REG(0x00dc) -+#define IPU_PM IPU_CM_REG(0x00e0) -+#define IPU_GPR IPU_CM_REG(0x00e4) -+#define IPU_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0150 + 4 * ((ch) / 32)) -+#define IPU_ALT_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0168 + 4 * ((ch) / 32)) -+#define IPU_CHA_CUR_BUF(ch) IPU_CM_REG(0x023C + 4 * ((ch) / 32)) -+#define IPU_ALT_CUR_BUF0 IPU_CM_REG(0x0244) -+#define IPU_ALT_CUR_BUF1 IPU_CM_REG(0x0248) -+#define IPU_SRM_STAT IPU_CM_REG(0x024C) -+#define IPU_PROC_TASK_STAT IPU_CM_REG(0x0250) -+#define IPU_DISP_TASK_STAT IPU_CM_REG(0x0254) -+#define IPU_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0268 + 4 * ((ch) / 32)) -+#define IPU_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0270 + 4 * ((ch) / 32)) -+#define IPU_CHA_BUF2_RDY(ch) IPU_CM_REG(0x0288 + 4 * ((ch) / 32)) -+#define IPU_ALT_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0278 + 4 * ((ch) / 32)) -+#define IPU_ALT_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0280 + 4 * ((ch) / 32)) -+ -+#define IPU_INT_CTRL(n) IPU_CM_REG(0x003C + 4 * (n)) -+#define IPU_INT_STAT(n) IPU_CM_REG(0x0200 + 4 * (n)) -+ -+/* SRM_PRI2 */ -+#define DP_S_SRM_MODE_MASK (0x3 << 3) -+#define DP_S_SRM_MODE_NOW (0x3 << 3) -+#define DP_S_SRM_MODE_NEXT_FRAME (0x1 << 3) -+ -+/* FS_PROC_FLOW1 */ -+#define FS_PRPENC_ROT_SRC_SEL_MASK (0xf << 0) -+#define FS_PRPENC_ROT_SRC_SEL_ENC (0x7 << 0) -+#define FS_PRPVF_ROT_SRC_SEL_MASK (0xf << 8) -+#define FS_PRPVF_ROT_SRC_SEL_VF (0x8 << 8) -+#define FS_PP_SRC_SEL_MASK (0xf << 12) -+#define FS_PP_ROT_SRC_SEL_MASK (0xf << 16) -+#define FS_PP_ROT_SRC_SEL_PP (0x5 << 16) -+#define FS_VDI1_SRC_SEL_MASK (0x3 << 20) -+#define FS_VDI3_SRC_SEL_MASK (0x3 << 20) -+#define FS_PRP_SRC_SEL_MASK (0xf << 24) -+#define FS_VDI_SRC_SEL_MASK (0x3 << 28) -+#define FS_VDI_SRC_SEL_CSI_DIRECT (0x1 << 28) -+#define FS_VDI_SRC_SEL_VDOA (0x2 << 28) -+ -+/* FS_PROC_FLOW2 */ -+#define FS_PRP_ENC_DEST_SEL_MASK (0xf << 0) -+#define FS_PRP_ENC_DEST_SEL_IRT_ENC (0x1 << 0) -+#define FS_PRPVF_DEST_SEL_MASK (0xf << 4) -+#define FS_PRPVF_DEST_SEL_IRT_VF (0x1 << 4) -+#define FS_PRPVF_ROT_DEST_SEL_MASK (0xf << 8) -+#define FS_PP_DEST_SEL_MASK (0xf << 12) -+#define FS_PP_DEST_SEL_IRT_PP (0x3 << 12) -+#define FS_PP_ROT_DEST_SEL_MASK (0xf << 16) -+#define FS_PRPENC_ROT_DEST_SEL_MASK (0xf << 20) -+#define FS_PRP_DEST_SEL_MASK (0xf << 24) -+ -+#define IPU_DI0_COUNTER_RELEASE (1 << 24) -+#define IPU_DI1_COUNTER_RELEASE (1 << 25) -+ -+#define IPU_IDMAC_REG(offset) (offset) -+ -+#define IDMAC_CONF IPU_IDMAC_REG(0x0000) -+#define IDMAC_CHA_EN(ch) IPU_IDMAC_REG(0x0004 + 4 * ((ch) / 32)) -+#define IDMAC_SEP_ALPHA IPU_IDMAC_REG(0x000c) -+#define IDMAC_ALT_SEP_ALPHA IPU_IDMAC_REG(0x0010) -+#define IDMAC_CHA_PRI(ch) IPU_IDMAC_REG(0x0014 + 4 * ((ch) / 32)) -+#define IDMAC_WM_EN(ch) IPU_IDMAC_REG(0x001c + 4 * ((ch) / 32)) -+#define IDMAC_CH_LOCK_EN_1 IPU_IDMAC_REG(0x0024) -+#define IDMAC_CH_LOCK_EN_2 IPU_IDMAC_REG(0x0028) -+#define IDMAC_SUB_ADDR_0 IPU_IDMAC_REG(0x002c) -+#define IDMAC_SUB_ADDR_1 IPU_IDMAC_REG(0x0030) -+#define IDMAC_SUB_ADDR_2 IPU_IDMAC_REG(0x0034) -+#define IDMAC_BAND_EN(ch) IPU_IDMAC_REG(0x0040 + 4 * ((ch) / 32)) -+#define IDMAC_CHA_BUSY(ch) IPU_IDMAC_REG(0x0100 + 4 * ((ch) / 32)) -+ -+#define IPU_NUM_IRQS (32 * 15) -+ -+enum ipu_modules { -+ IPU_CONF_CSI0_EN = (1 << 0), -+ IPU_CONF_CSI1_EN = (1 << 1), -+ IPU_CONF_IC_EN = (1 << 2), -+ IPU_CONF_ROT_EN = (1 << 3), -+ IPU_CONF_ISP_EN = (1 << 4), -+ IPU_CONF_DP_EN = (1 << 5), -+ IPU_CONF_DI0_EN = (1 << 6), -+ IPU_CONF_DI1_EN = (1 << 7), -+ IPU_CONF_SMFC_EN = (1 << 8), -+ IPU_CONF_DC_EN = (1 << 9), -+ IPU_CONF_DMFC_EN = (1 << 10), -+ -+ IPU_CONF_VDI_EN = (1 << 12), -+ -+ IPU_CONF_IDMAC_DIS = (1 << 22), -+ -+ IPU_CONF_IC_DMFC_SEL = (1 << 25), -+ IPU_CONF_IC_DMFC_SYNC = (1 << 26), -+ IPU_CONF_VDI_DMFC_SYNC = (1 << 27), -+ -+ IPU_CONF_CSI0_DATA_SOURCE = (1 << 28), -+ IPU_CONF_CSI1_DATA_SOURCE = (1 << 29), -+ IPU_CONF_IC_INPUT = (1 << 30), -+ IPU_CONF_CSI_SEL = (1 << 31), -+}; -+ -+struct ipuv3_channel { -+ unsigned int num; -+ struct ipu_soc *ipu; -+ struct list_head list; -+}; -+ -+struct ipu_cpmem; -+struct ipu_csi; -+struct ipu_dc_priv; -+struct ipu_dmfc_priv; -+struct ipu_di; -+struct ipu_ic_priv; -+struct ipu_vdi; -+struct ipu_image_convert_priv; -+struct ipu_smfc_priv; -+struct ipu_pre; -+struct ipu_prg; -+ -+struct ipu_devtype; -+ -+struct ipu_soc { -+ struct device *dev; -+ const struct ipu_devtype *devtype; -+ enum ipuv3_type ipu_type; -+ spinlock_t lock; -+ struct mutex channel_lock; -+ struct list_head channels; -+ -+ void __iomem *cm_reg; -+ void __iomem *idmac_reg; -+ -+ int id; -+ int usecount; -+ -+ struct clk *clk; -+ -+ int irq_sync; -+ int irq_err; -+ struct irq_domain *domain; -+ -+ struct ipu_cpmem *cpmem_priv; -+ struct ipu_dc_priv *dc_priv; -+ struct ipu_dp_priv *dp_priv; -+ struct ipu_dmfc_priv *dmfc_priv; -+ struct ipu_di *di_priv[2]; -+ struct ipu_csi *csi_priv[2]; -+ struct ipu_ic_priv *ic_priv; -+ struct ipu_vdi *vdi_priv; -+ struct ipu_image_convert_priv *image_convert_priv; -+ struct ipu_smfc_priv *smfc_priv; -+ struct ipu_prg *prg_priv; -+}; -+ -+static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset) -+{ -+ return readl(ipu->idmac_reg + offset); -+} -+ -+static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value, -+ unsigned offset) -+{ -+ writel(value, ipu->idmac_reg + offset); -+} -+ -+void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync); -+ -+int ipu_module_enable(struct ipu_soc *ipu, u32 mask); -+int ipu_module_disable(struct ipu_soc *ipu, u32 mask); -+ -+bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno); -+ -+int ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id, -+ unsigned long base, u32 module, struct clk *clk_ipu); -+void ipu_csi_exit(struct ipu_soc *ipu, int id); -+ -+int ipu_ic_init(struct ipu_soc *ipu, struct device *dev, -+ unsigned long base, unsigned long tpmem_base); -+void ipu_ic_exit(struct ipu_soc *ipu); -+ -+int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev, -+ unsigned long base, u32 module); -+void ipu_vdi_exit(struct ipu_soc *ipu); -+ -+int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev); -+void ipu_image_convert_exit(struct ipu_soc *ipu); -+ -+int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, -+ unsigned long base, u32 module, struct clk *ipu_clk); -+void ipu_di_exit(struct ipu_soc *ipu, int id); -+ -+int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, -+ struct clk *ipu_clk); -+void ipu_dmfc_exit(struct ipu_soc *ipu); -+ -+int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); -+void ipu_dp_exit(struct ipu_soc *ipu); -+ -+int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, -+ unsigned long template_base); -+void ipu_dc_exit(struct ipu_soc *ipu); -+ -+int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); -+void ipu_cpmem_exit(struct ipu_soc *ipu); -+ -+int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); -+void ipu_smfc_exit(struct ipu_soc *ipu); -+ -+struct ipu_pre *ipu_pre_lookup_by_phandle(struct device *dev, const char *name, -+ int index); -+int ipu_pre_get_available_count(void); -+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, -+ uint64_t modifier, unsigned int bufaddr); -+void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr); -+bool ipu_pre_update_pending(struct ipu_pre *pre); -+ -+struct ipu_prg *ipu_prg_lookup_by_phandle(struct device *dev, const char *name, -+ int ipu_id); -+ -+extern struct platform_driver ipu_pre_drv; -+extern struct platform_driver ipu_prg_drv; -+ -+#endif /* __IPU_PRV_H__ */ ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-smfc.c -@@ -0,0 +1,202 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. -+ */ -+#include <linux/export.h> -+#include <linux/types.h> -+#include <linux/init.h> -+#include <linux/io.h> -+#include <linux/errno.h> -+#include <linux/spinlock.h> -+#include <linux/delay.h> -+#include <linux/clk.h> -+#include <video/imx-ipu-v3.h> -+ -+#include "ipu-prv.h" -+ -+struct ipu_smfc { -+ struct ipu_smfc_priv *priv; -+ int chno; -+ bool inuse; -+}; -+ -+struct ipu_smfc_priv { -+ void __iomem *base; -+ spinlock_t lock; -+ struct ipu_soc *ipu; -+ struct ipu_smfc channel[4]; -+ int use_count; -+}; -+ -+/*SMFC Registers */ -+#define SMFC_MAP 0x0000 -+#define SMFC_WMC 0x0004 -+#define SMFC_BS 0x0008 -+ -+int ipu_smfc_set_burstsize(struct ipu_smfc *smfc, int burstsize) -+{ -+ struct ipu_smfc_priv *priv = smfc->priv; -+ unsigned long flags; -+ u32 val, shift; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ shift = smfc->chno * 4; -+ val = readl(priv->base + SMFC_BS); -+ val &= ~(0xf << shift); -+ val |= burstsize << shift; -+ writel(val, priv->base + SMFC_BS); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_set_burstsize); -+ -+int ipu_smfc_map_channel(struct ipu_smfc *smfc, int csi_id, int mipi_id) -+{ -+ struct ipu_smfc_priv *priv = smfc->priv; -+ unsigned long flags; -+ u32 val, shift; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ shift = smfc->chno * 3; -+ val = readl(priv->base + SMFC_MAP); -+ val &= ~(0x7 << shift); -+ val |= ((csi_id << 2) | mipi_id) << shift; -+ writel(val, priv->base + SMFC_MAP); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_map_channel); -+ -+int ipu_smfc_set_watermark(struct ipu_smfc *smfc, u32 set_level, u32 clr_level) -+{ -+ struct ipu_smfc_priv *priv = smfc->priv; -+ unsigned long flags; -+ u32 val, shift; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ shift = smfc->chno * 6 + (smfc->chno > 1 ? 4 : 0); -+ val = readl(priv->base + SMFC_WMC); -+ val &= ~(0x3f << shift); -+ val |= ((clr_level << 3) | set_level) << shift; -+ writel(val, priv->base + SMFC_WMC); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_set_watermark); -+ -+int ipu_smfc_enable(struct ipu_smfc *smfc) -+{ -+ struct ipu_smfc_priv *priv = smfc->priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ if (!priv->use_count) -+ ipu_module_enable(priv->ipu, IPU_CONF_SMFC_EN); -+ -+ priv->use_count++; -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_enable); -+ -+int ipu_smfc_disable(struct ipu_smfc *smfc) -+{ -+ struct ipu_smfc_priv *priv = smfc->priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ priv->use_count--; -+ -+ if (!priv->use_count) -+ ipu_module_disable(priv->ipu, IPU_CONF_SMFC_EN); -+ -+ if (priv->use_count < 0) -+ priv->use_count = 0; -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_disable); -+ -+struct ipu_smfc *ipu_smfc_get(struct ipu_soc *ipu, unsigned int chno) -+{ -+ struct ipu_smfc_priv *priv = ipu->smfc_priv; -+ struct ipu_smfc *smfc, *ret; -+ unsigned long flags; -+ -+ if (chno >= 4) -+ return ERR_PTR(-EINVAL); -+ -+ smfc = &priv->channel[chno]; -+ ret = smfc; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ if (smfc->inuse) { -+ ret = ERR_PTR(-EBUSY); -+ goto unlock; -+ } -+ -+ smfc->inuse = true; -+unlock: -+ spin_unlock_irqrestore(&priv->lock, flags); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_get); -+ -+void ipu_smfc_put(struct ipu_smfc *smfc) -+{ -+ struct ipu_smfc_priv *priv = smfc->priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ smfc->inuse = false; -+ spin_unlock_irqrestore(&priv->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_put); -+ -+int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, -+ unsigned long base) -+{ -+ struct ipu_smfc_priv *priv; -+ int i; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ ipu->smfc_priv = priv; -+ spin_lock_init(&priv->lock); -+ priv->ipu = ipu; -+ -+ priv->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!priv->base) -+ return -ENOMEM; -+ -+ for (i = 0; i < 4; i++) { -+ priv->channel[i].priv = priv; -+ priv->channel[i].chno = i; -+ } -+ -+ pr_debug("%s: ioremap 0x%08lx -> %p\n", __func__, base, priv->base); -+ -+ return 0; -+} -+ -+void ipu_smfc_exit(struct ipu_soc *ipu) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-vdi.c -@@ -0,0 +1,234 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (C) 2012-2016 Mentor Graphics Inc. -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#include <linux/io.h> -+#include "ipu-prv.h" -+ -+struct ipu_vdi { -+ void __iomem *base; -+ u32 module; -+ spinlock_t lock; -+ int use_count; -+ struct ipu_soc *ipu; -+}; -+ -+ -+/* VDI Register Offsets */ -+#define VDI_FSIZE 0x0000 -+#define VDI_C 0x0004 -+ -+/* VDI Register Fields */ -+#define VDI_C_CH_420 (0 << 1) -+#define VDI_C_CH_422 (1 << 1) -+#define VDI_C_MOT_SEL_MASK (0x3 << 2) -+#define VDI_C_MOT_SEL_FULL (2 << 2) -+#define VDI_C_MOT_SEL_LOW (1 << 2) -+#define VDI_C_MOT_SEL_MED (0 << 2) -+#define VDI_C_BURST_SIZE1_4 (3 << 4) -+#define VDI_C_BURST_SIZE2_4 (3 << 8) -+#define VDI_C_BURST_SIZE3_4 (3 << 12) -+#define VDI_C_BURST_SIZE_MASK 0xF -+#define VDI_C_BURST_SIZE1_OFFSET 4 -+#define VDI_C_BURST_SIZE2_OFFSET 8 -+#define VDI_C_BURST_SIZE3_OFFSET 12 -+#define VDI_C_VWM1_SET_1 (0 << 16) -+#define VDI_C_VWM1_SET_2 (1 << 16) -+#define VDI_C_VWM1_CLR_2 (1 << 19) -+#define VDI_C_VWM3_SET_1 (0 << 22) -+#define VDI_C_VWM3_SET_2 (1 << 22) -+#define VDI_C_VWM3_CLR_2 (1 << 25) -+#define VDI_C_TOP_FIELD_MAN_1 (1 << 30) -+#define VDI_C_TOP_FIELD_AUTO_1 (1 << 31) -+ -+static inline u32 ipu_vdi_read(struct ipu_vdi *vdi, unsigned int offset) -+{ -+ return readl(vdi->base + offset); -+} -+ -+static inline void ipu_vdi_write(struct ipu_vdi *vdi, u32 value, -+ unsigned int offset) -+{ -+ writel(value, vdi->base + offset); -+} -+ -+void ipu_vdi_set_field_order(struct ipu_vdi *vdi, v4l2_std_id std, u32 field) -+{ -+ bool top_field_0 = false; -+ unsigned long flags; -+ u32 reg; -+ -+ switch (field) { -+ case V4L2_FIELD_INTERLACED_TB: -+ case V4L2_FIELD_SEQ_TB: -+ case V4L2_FIELD_TOP: -+ top_field_0 = true; -+ break; -+ case V4L2_FIELD_INTERLACED_BT: -+ case V4L2_FIELD_SEQ_BT: -+ case V4L2_FIELD_BOTTOM: -+ top_field_0 = false; -+ break; -+ default: -+ top_field_0 = (std & V4L2_STD_525_60) ? true : false; -+ break; -+ } -+ -+ spin_lock_irqsave(&vdi->lock, flags); -+ -+ reg = ipu_vdi_read(vdi, VDI_C); -+ if (top_field_0) -+ reg &= ~(VDI_C_TOP_FIELD_MAN_1 | VDI_C_TOP_FIELD_AUTO_1); -+ else -+ reg |= VDI_C_TOP_FIELD_MAN_1 | VDI_C_TOP_FIELD_AUTO_1; -+ ipu_vdi_write(vdi, reg, VDI_C); -+ -+ spin_unlock_irqrestore(&vdi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_set_field_order); -+ -+void ipu_vdi_set_motion(struct ipu_vdi *vdi, enum ipu_motion_sel motion_sel) -+{ -+ unsigned long flags; -+ u32 reg; -+ -+ spin_lock_irqsave(&vdi->lock, flags); -+ -+ reg = ipu_vdi_read(vdi, VDI_C); -+ -+ reg &= ~VDI_C_MOT_SEL_MASK; -+ -+ switch (motion_sel) { -+ case MED_MOTION: -+ reg |= VDI_C_MOT_SEL_MED; -+ break; -+ case HIGH_MOTION: -+ reg |= VDI_C_MOT_SEL_FULL; -+ break; -+ default: -+ reg |= VDI_C_MOT_SEL_LOW; -+ break; -+ } -+ -+ ipu_vdi_write(vdi, reg, VDI_C); -+ -+ spin_unlock_irqrestore(&vdi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_set_motion); -+ -+void ipu_vdi_setup(struct ipu_vdi *vdi, u32 code, int xres, int yres) -+{ -+ unsigned long flags; -+ u32 pixel_fmt, reg; -+ -+ spin_lock_irqsave(&vdi->lock, flags); -+ -+ reg = ((yres - 1) << 16) | (xres - 1); -+ ipu_vdi_write(vdi, reg, VDI_FSIZE); -+ -+ /* -+ * Full motion, only vertical filter is used. -+ * Burst size is 4 accesses -+ */ -+ if (code == MEDIA_BUS_FMT_UYVY8_2X8 || -+ code == MEDIA_BUS_FMT_UYVY8_1X16 || -+ code == MEDIA_BUS_FMT_YUYV8_2X8 || -+ code == MEDIA_BUS_FMT_YUYV8_1X16) -+ pixel_fmt = VDI_C_CH_422; -+ else -+ pixel_fmt = VDI_C_CH_420; -+ -+ reg = ipu_vdi_read(vdi, VDI_C); -+ reg |= pixel_fmt; -+ reg |= VDI_C_BURST_SIZE2_4; -+ reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_CLR_2; -+ reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_CLR_2; -+ ipu_vdi_write(vdi, reg, VDI_C); -+ -+ spin_unlock_irqrestore(&vdi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_setup); -+ -+void ipu_vdi_unsetup(struct ipu_vdi *vdi) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&vdi->lock, flags); -+ ipu_vdi_write(vdi, 0, VDI_FSIZE); -+ ipu_vdi_write(vdi, 0, VDI_C); -+ spin_unlock_irqrestore(&vdi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_unsetup); -+ -+int ipu_vdi_enable(struct ipu_vdi *vdi) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&vdi->lock, flags); -+ -+ if (!vdi->use_count) -+ ipu_module_enable(vdi->ipu, vdi->module); -+ -+ vdi->use_count++; -+ -+ spin_unlock_irqrestore(&vdi->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_enable); -+ -+int ipu_vdi_disable(struct ipu_vdi *vdi) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&vdi->lock, flags); -+ -+ if (vdi->use_count) { -+ if (!--vdi->use_count) -+ ipu_module_disable(vdi->ipu, vdi->module); -+ } -+ -+ spin_unlock_irqrestore(&vdi->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_disable); -+ -+struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu) -+{ -+ return ipu->vdi_priv; -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_get); -+ -+void ipu_vdi_put(struct ipu_vdi *vdi) -+{ -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_put); -+ -+int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev, -+ unsigned long base, u32 module) -+{ -+ struct ipu_vdi *vdi; -+ -+ vdi = devm_kzalloc(dev, sizeof(*vdi), GFP_KERNEL); -+ if (!vdi) -+ return -ENOMEM; -+ -+ ipu->vdi_priv = vdi; -+ -+ spin_lock_init(&vdi->lock); -+ vdi->module = module; -+ vdi->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!vdi->base) -+ return -ENOMEM; -+ -+ dev_dbg(dev, "VDI base: 0x%08lx remapped to %p\n", base, vdi->base); -+ vdi->ipu = ipu; -+ -+ return 0; -+} -+ -+void ipu_vdi_exit(struct ipu_soc *ipu) -+{ -+} ---- a/drivers/video/Kconfig -+++ b/drivers/video/Kconfig -@@ -15,7 +15,7 @@ source "drivers/char/agp/Kconfig" - source "drivers/gpu/vga/Kconfig" - - source "drivers/gpu/host1x/Kconfig" --source "drivers/gpu/imx/Kconfig" -+source "drivers/gpu/ipu-v3/Kconfig" - - source "drivers/gpu/drm/Kconfig" - |