1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 239b9004dSPhilipp Zabel /* 339b9004dSPhilipp Zabel * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> 439b9004dSPhilipp Zabel * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. 539b9004dSPhilipp Zabel */ 639b9004dSPhilipp Zabel #include <linux/module.h> 739b9004dSPhilipp Zabel #include <linux/export.h> 839b9004dSPhilipp Zabel #include <linux/types.h> 939b9004dSPhilipp Zabel #include <linux/reset.h> 1039b9004dSPhilipp Zabel #include <linux/platform_device.h> 1139b9004dSPhilipp Zabel #include <linux/err.h> 1239b9004dSPhilipp Zabel #include <linux/spinlock.h> 1339b9004dSPhilipp Zabel #include <linux/delay.h> 1439b9004dSPhilipp Zabel #include <linux/interrupt.h> 1539b9004dSPhilipp Zabel #include <linux/io.h> 1639b9004dSPhilipp Zabel #include <linux/clk.h> 1739b9004dSPhilipp Zabel #include <linux/list.h> 1839b9004dSPhilipp Zabel #include <linux/irq.h> 1939b9004dSPhilipp Zabel #include <linux/irqchip/chained_irq.h> 2039b9004dSPhilipp Zabel #include <linux/irqdomain.h> 2139b9004dSPhilipp Zabel #include <linux/of_device.h> 22304e6be6SPhilipp Zabel #include <linux/of_graph.h> 2339b9004dSPhilipp Zabel 2439b9004dSPhilipp Zabel #include <drm/drm_fourcc.h> 2539b9004dSPhilipp Zabel 2639b9004dSPhilipp Zabel #include <video/imx-ipu-v3.h> 2739b9004dSPhilipp Zabel #include "ipu-prv.h" 2839b9004dSPhilipp Zabel 2939b9004dSPhilipp Zabel static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset) 3039b9004dSPhilipp Zabel { 3139b9004dSPhilipp Zabel return readl(ipu->cm_reg + offset); 3239b9004dSPhilipp Zabel } 3339b9004dSPhilipp Zabel 3439b9004dSPhilipp Zabel static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset) 3539b9004dSPhilipp Zabel { 3639b9004dSPhilipp Zabel writel(value, ipu->cm_reg + offset); 3739b9004dSPhilipp Zabel } 3839b9004dSPhilipp Zabel 39572a7615SSteve Longerbeam int ipu_get_num(struct ipu_soc *ipu) 40572a7615SSteve Longerbeam { 41572a7615SSteve Longerbeam return ipu->id; 42572a7615SSteve Longerbeam } 43572a7615SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_get_num); 44572a7615SSteve Longerbeam 45f9bb7acbSPhilipp Zabel void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync) 4639b9004dSPhilipp Zabel { 4739b9004dSPhilipp Zabel u32 val; 4839b9004dSPhilipp Zabel 4939b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_SRM_PRI2); 50f9bb7acbSPhilipp Zabel val &= ~DP_S_SRM_MODE_MASK; 51f9bb7acbSPhilipp Zabel val |= sync ? DP_S_SRM_MODE_NEXT_FRAME : 52f9bb7acbSPhilipp Zabel DP_S_SRM_MODE_NOW; 5339b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_SRM_PRI2); 5439b9004dSPhilipp Zabel } 55f9bb7acbSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_srm_dp_update); 5639b9004dSPhilipp Zabel 5739b9004dSPhilipp Zabel enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc) 5839b9004dSPhilipp Zabel { 5939b9004dSPhilipp Zabel switch (drm_fourcc) { 600cb8b757SPhilipp Zabel case DRM_FORMAT_ARGB1555: 610cb8b757SPhilipp Zabel case DRM_FORMAT_ABGR1555: 620cb8b757SPhilipp Zabel case DRM_FORMAT_RGBA5551: 630cb8b757SPhilipp Zabel case DRM_FORMAT_BGRA5551: 6439b9004dSPhilipp Zabel case DRM_FORMAT_RGB565: 6539b9004dSPhilipp Zabel case DRM_FORMAT_BGR565: 6639b9004dSPhilipp Zabel case DRM_FORMAT_RGB888: 6739b9004dSPhilipp Zabel case DRM_FORMAT_BGR888: 687d2e8a20SLucas Stach case DRM_FORMAT_ARGB4444: 6939b9004dSPhilipp Zabel case DRM_FORMAT_XRGB8888: 7039b9004dSPhilipp Zabel case DRM_FORMAT_XBGR8888: 7139b9004dSPhilipp Zabel case DRM_FORMAT_RGBX8888: 7239b9004dSPhilipp Zabel case DRM_FORMAT_BGRX8888: 7339b9004dSPhilipp Zabel case DRM_FORMAT_ARGB8888: 7439b9004dSPhilipp Zabel case DRM_FORMAT_ABGR8888: 7539b9004dSPhilipp Zabel case DRM_FORMAT_RGBA8888: 7639b9004dSPhilipp Zabel case DRM_FORMAT_BGRA8888: 77e72db3b1SPhilipp Zabel case DRM_FORMAT_RGB565_A8: 78e72db3b1SPhilipp Zabel case DRM_FORMAT_BGR565_A8: 79e72db3b1SPhilipp Zabel case DRM_FORMAT_RGB888_A8: 80e72db3b1SPhilipp Zabel case DRM_FORMAT_BGR888_A8: 81e72db3b1SPhilipp Zabel case DRM_FORMAT_RGBX8888_A8: 82e72db3b1SPhilipp Zabel case DRM_FORMAT_BGRX8888_A8: 8339b9004dSPhilipp Zabel return IPUV3_COLORSPACE_RGB; 8439b9004dSPhilipp Zabel case DRM_FORMAT_YUYV: 8539b9004dSPhilipp Zabel case DRM_FORMAT_UYVY: 8639b9004dSPhilipp Zabel case DRM_FORMAT_YUV420: 8739b9004dSPhilipp Zabel case DRM_FORMAT_YVU420: 889a34cef0SSteve Longerbeam case DRM_FORMAT_YUV422: 899a34cef0SSteve Longerbeam case DRM_FORMAT_YVU422: 90c9d508c2SPhilipp Zabel case DRM_FORMAT_YUV444: 91c9d508c2SPhilipp Zabel case DRM_FORMAT_YVU444: 929a34cef0SSteve Longerbeam case DRM_FORMAT_NV12: 939a34cef0SSteve Longerbeam case DRM_FORMAT_NV21: 949a34cef0SSteve Longerbeam case DRM_FORMAT_NV16: 959a34cef0SSteve Longerbeam case DRM_FORMAT_NV61: 9639b9004dSPhilipp Zabel return IPUV3_COLORSPACE_YUV; 9739b9004dSPhilipp Zabel default: 9839b9004dSPhilipp Zabel return IPUV3_COLORSPACE_UNKNOWN; 9939b9004dSPhilipp Zabel } 10039b9004dSPhilipp Zabel } 10139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace); 10239b9004dSPhilipp Zabel 10339b9004dSPhilipp Zabel enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat) 10439b9004dSPhilipp Zabel { 10539b9004dSPhilipp Zabel switch (pixelformat) { 10639b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUV420: 10739b9004dSPhilipp Zabel case V4L2_PIX_FMT_YVU420: 1089a34cef0SSteve Longerbeam case V4L2_PIX_FMT_YUV422P: 10939b9004dSPhilipp Zabel case V4L2_PIX_FMT_UYVY: 11039b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUYV: 1119a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV12: 1129a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV21: 1139a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV16: 1149a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV61: 11539b9004dSPhilipp Zabel return IPUV3_COLORSPACE_YUV; 11639b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB565: 117a5995717SPhilipp Zabel case V4L2_PIX_FMT_BGR24: 118a5995717SPhilipp Zabel case V4L2_PIX_FMT_RGB24: 119a5995717SPhilipp Zabel case V4L2_PIX_FMT_ABGR32: 120a5995717SPhilipp Zabel case V4L2_PIX_FMT_XBGR32: 121a5995717SPhilipp Zabel case V4L2_PIX_FMT_BGRA32: 122a5995717SPhilipp Zabel case V4L2_PIX_FMT_BGRX32: 123a5995717SPhilipp Zabel case V4L2_PIX_FMT_RGBA32: 124a5995717SPhilipp Zabel case V4L2_PIX_FMT_RGBX32: 125a5995717SPhilipp Zabel case V4L2_PIX_FMT_ARGB32: 126a5995717SPhilipp Zabel case V4L2_PIX_FMT_XRGB32: 12739b9004dSPhilipp Zabel return IPUV3_COLORSPACE_RGB; 12839b9004dSPhilipp Zabel default: 12939b9004dSPhilipp Zabel return IPUV3_COLORSPACE_UNKNOWN; 13039b9004dSPhilipp Zabel } 13139b9004dSPhilipp Zabel } 13239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace); 13339b9004dSPhilipp Zabel 1344cea940dSSteve Longerbeam bool ipu_pixelformat_is_planar(u32 pixelformat) 1354cea940dSSteve Longerbeam { 1364cea940dSSteve Longerbeam switch (pixelformat) { 1374cea940dSSteve Longerbeam case V4L2_PIX_FMT_YUV420: 1384cea940dSSteve Longerbeam case V4L2_PIX_FMT_YVU420: 1399a34cef0SSteve Longerbeam case V4L2_PIX_FMT_YUV422P: 1409a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV12: 1419a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV21: 1429a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV16: 1439a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV61: 1444cea940dSSteve Longerbeam return true; 1454cea940dSSteve Longerbeam } 1464cea940dSSteve Longerbeam 1474cea940dSSteve Longerbeam return false; 1484cea940dSSteve Longerbeam } 1494cea940dSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar); 1504cea940dSSteve Longerbeam 151ae0e9708SSteve Longerbeam enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code) 152ae0e9708SSteve Longerbeam { 153ae0e9708SSteve Longerbeam switch (mbus_code & 0xf000) { 154ae0e9708SSteve Longerbeam case 0x1000: 155ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_RGB; 156ae0e9708SSteve Longerbeam case 0x2000: 157ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_YUV; 158ae0e9708SSteve Longerbeam default: 159ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_UNKNOWN; 160ae0e9708SSteve Longerbeam } 161ae0e9708SSteve Longerbeam } 162ae0e9708SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace); 163ae0e9708SSteve Longerbeam 1646930afdcSSteve Longerbeam int ipu_stride_to_bytes(u32 pixel_stride, u32 pixelformat) 1656930afdcSSteve Longerbeam { 1666930afdcSSteve Longerbeam switch (pixelformat) { 1676930afdcSSteve Longerbeam case V4L2_PIX_FMT_YUV420: 1686930afdcSSteve Longerbeam case V4L2_PIX_FMT_YVU420: 1699a34cef0SSteve Longerbeam case V4L2_PIX_FMT_YUV422P: 1709a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV12: 1719a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV21: 1729a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV16: 1739a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV61: 1746930afdcSSteve Longerbeam /* 1756930afdcSSteve Longerbeam * for the planar YUV formats, the stride passed to 1766930afdcSSteve Longerbeam * cpmem must be the stride in bytes of the Y plane. 1776930afdcSSteve Longerbeam * And all the planar YUV formats have an 8-bit 1786930afdcSSteve Longerbeam * Y component. 1796930afdcSSteve Longerbeam */ 1806930afdcSSteve Longerbeam return (8 * pixel_stride) >> 3; 1816930afdcSSteve Longerbeam case V4L2_PIX_FMT_RGB565: 1826930afdcSSteve Longerbeam case V4L2_PIX_FMT_YUYV: 1836930afdcSSteve Longerbeam case V4L2_PIX_FMT_UYVY: 1846930afdcSSteve Longerbeam return (16 * pixel_stride) >> 3; 1856930afdcSSteve Longerbeam case V4L2_PIX_FMT_BGR24: 1866930afdcSSteve Longerbeam case V4L2_PIX_FMT_RGB24: 1876930afdcSSteve Longerbeam return (24 * pixel_stride) >> 3; 1886930afdcSSteve Longerbeam case V4L2_PIX_FMT_BGR32: 1896930afdcSSteve Longerbeam case V4L2_PIX_FMT_RGB32: 1905c41bb60SPhilipp Zabel case V4L2_PIX_FMT_XBGR32: 1915c41bb60SPhilipp Zabel case V4L2_PIX_FMT_XRGB32: 1926930afdcSSteve Longerbeam return (32 * pixel_stride) >> 3; 1936930afdcSSteve Longerbeam default: 1946930afdcSSteve Longerbeam break; 1956930afdcSSteve Longerbeam } 1966930afdcSSteve Longerbeam 1976930afdcSSteve Longerbeam return -EINVAL; 1986930afdcSSteve Longerbeam } 1996930afdcSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_stride_to_bytes); 2006930afdcSSteve Longerbeam 201f835f386SSteve Longerbeam int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees, 202f835f386SSteve Longerbeam bool hflip, bool vflip) 203f835f386SSteve Longerbeam { 204f835f386SSteve Longerbeam u32 r90, vf, hf; 205f835f386SSteve Longerbeam 206f835f386SSteve Longerbeam switch (degrees) { 207f835f386SSteve Longerbeam case 0: 208f835f386SSteve Longerbeam vf = hf = r90 = 0; 209f835f386SSteve Longerbeam break; 210f835f386SSteve Longerbeam case 90: 211f835f386SSteve Longerbeam vf = hf = 0; 212f835f386SSteve Longerbeam r90 = 1; 213f835f386SSteve Longerbeam break; 214f835f386SSteve Longerbeam case 180: 215f835f386SSteve Longerbeam vf = hf = 1; 216f835f386SSteve Longerbeam r90 = 0; 217f835f386SSteve Longerbeam break; 218f835f386SSteve Longerbeam case 270: 219f835f386SSteve Longerbeam vf = hf = r90 = 1; 220f835f386SSteve Longerbeam break; 221f835f386SSteve Longerbeam default: 222f835f386SSteve Longerbeam return -EINVAL; 223f835f386SSteve Longerbeam } 224f835f386SSteve Longerbeam 225f835f386SSteve Longerbeam hf ^= (u32)hflip; 226f835f386SSteve Longerbeam vf ^= (u32)vflip; 227f835f386SSteve Longerbeam 228f835f386SSteve Longerbeam *mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf); 229f835f386SSteve Longerbeam return 0; 230f835f386SSteve Longerbeam } 231f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode); 232f835f386SSteve Longerbeam 233f835f386SSteve Longerbeam int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode, 234f835f386SSteve Longerbeam bool hflip, bool vflip) 235f835f386SSteve Longerbeam { 236f835f386SSteve Longerbeam u32 r90, vf, hf; 237f835f386SSteve Longerbeam 238f835f386SSteve Longerbeam r90 = ((u32)mode >> 2) & 0x1; 239f835f386SSteve Longerbeam hf = ((u32)mode >> 1) & 0x1; 240f835f386SSteve Longerbeam vf = ((u32)mode >> 0) & 0x1; 241f835f386SSteve Longerbeam hf ^= (u32)hflip; 242f835f386SSteve Longerbeam vf ^= (u32)vflip; 243f835f386SSteve Longerbeam 244f835f386SSteve Longerbeam switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) { 245f835f386SSteve Longerbeam case IPU_ROTATE_NONE: 246f835f386SSteve Longerbeam *degrees = 0; 247f835f386SSteve Longerbeam break; 248f835f386SSteve Longerbeam case IPU_ROTATE_90_RIGHT: 249f835f386SSteve Longerbeam *degrees = 90; 250f835f386SSteve Longerbeam break; 251f835f386SSteve Longerbeam case IPU_ROTATE_180: 252f835f386SSteve Longerbeam *degrees = 180; 253f835f386SSteve Longerbeam break; 254f835f386SSteve Longerbeam case IPU_ROTATE_90_LEFT: 255f835f386SSteve Longerbeam *degrees = 270; 256f835f386SSteve Longerbeam break; 257f835f386SSteve Longerbeam default: 258f835f386SSteve Longerbeam return -EINVAL; 259f835f386SSteve Longerbeam } 260f835f386SSteve Longerbeam 261f835f386SSteve Longerbeam return 0; 262f835f386SSteve Longerbeam } 263f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees); 264f835f386SSteve Longerbeam 26539b9004dSPhilipp Zabel struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num) 26639b9004dSPhilipp Zabel { 26739b9004dSPhilipp Zabel struct ipuv3_channel *channel; 26839b9004dSPhilipp Zabel 26939b9004dSPhilipp Zabel dev_dbg(ipu->dev, "%s %d\n", __func__, num); 27039b9004dSPhilipp Zabel 27139b9004dSPhilipp Zabel if (num > 63) 27239b9004dSPhilipp Zabel return ERR_PTR(-ENODEV); 27339b9004dSPhilipp Zabel 27439b9004dSPhilipp Zabel mutex_lock(&ipu->channel_lock); 27539b9004dSPhilipp Zabel 27693adc8b5SPhilipp Zabel list_for_each_entry(channel, &ipu->channels, list) { 27793adc8b5SPhilipp Zabel if (channel->num == num) { 27839b9004dSPhilipp Zabel channel = ERR_PTR(-EBUSY); 27939b9004dSPhilipp Zabel goto out; 28039b9004dSPhilipp Zabel } 28193adc8b5SPhilipp Zabel } 28239b9004dSPhilipp Zabel 28393adc8b5SPhilipp Zabel channel = kzalloc(sizeof(*channel), GFP_KERNEL); 28493adc8b5SPhilipp Zabel if (!channel) { 28593adc8b5SPhilipp Zabel channel = ERR_PTR(-ENOMEM); 28693adc8b5SPhilipp Zabel goto out; 28793adc8b5SPhilipp Zabel } 28893adc8b5SPhilipp Zabel 28939b9004dSPhilipp Zabel channel->num = num; 29093adc8b5SPhilipp Zabel channel->ipu = ipu; 29193adc8b5SPhilipp Zabel list_add(&channel->list, &ipu->channels); 29239b9004dSPhilipp Zabel 29339b9004dSPhilipp Zabel out: 29439b9004dSPhilipp Zabel mutex_unlock(&ipu->channel_lock); 29539b9004dSPhilipp Zabel 29639b9004dSPhilipp Zabel return channel; 29739b9004dSPhilipp Zabel } 29839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get); 29939b9004dSPhilipp Zabel 30039b9004dSPhilipp Zabel void ipu_idmac_put(struct ipuv3_channel *channel) 30139b9004dSPhilipp Zabel { 30239b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 30339b9004dSPhilipp Zabel 30439b9004dSPhilipp Zabel dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num); 30539b9004dSPhilipp Zabel 30639b9004dSPhilipp Zabel mutex_lock(&ipu->channel_lock); 30739b9004dSPhilipp Zabel 30893adc8b5SPhilipp Zabel list_del(&channel->list); 30993adc8b5SPhilipp Zabel kfree(channel); 31039b9004dSPhilipp Zabel 31139b9004dSPhilipp Zabel mutex_unlock(&ipu->channel_lock); 31239b9004dSPhilipp Zabel } 31339b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_put); 31439b9004dSPhilipp Zabel 315aa52f578SSteve Longerbeam #define idma_mask(ch) (1 << ((ch) & 0x1f)) 31639b9004dSPhilipp Zabel 317e7268c69SSteve Longerbeam /* 318e7268c69SSteve Longerbeam * This is an undocumented feature, a write one to a channel bit in 319e7268c69SSteve Longerbeam * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's 320e7268c69SSteve Longerbeam * internal current buffer pointer so that transfers start from buffer 321e7268c69SSteve Longerbeam * 0 on the next channel enable (that's the theory anyway, the imx6 TRM 322e7268c69SSteve Longerbeam * only says these are read-only registers). This operation is required 323e7268c69SSteve Longerbeam * for channel linking to work correctly, for instance video capture 324e7268c69SSteve Longerbeam * pipelines that carry out image rotations will fail after the first 325e7268c69SSteve Longerbeam * streaming unless this function is called for each channel before 326e7268c69SSteve Longerbeam * re-enabling the channels. 327e7268c69SSteve Longerbeam */ 328e7268c69SSteve Longerbeam static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel) 329e7268c69SSteve Longerbeam { 330e7268c69SSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 331e7268c69SSteve Longerbeam unsigned int chno = channel->num; 332e7268c69SSteve Longerbeam 333e7268c69SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno)); 334e7268c69SSteve Longerbeam } 335e7268c69SSteve Longerbeam 33639b9004dSPhilipp Zabel void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel, 33739b9004dSPhilipp Zabel bool doublebuffer) 33839b9004dSPhilipp Zabel { 33939b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 34039b9004dSPhilipp Zabel unsigned long flags; 34139b9004dSPhilipp Zabel u32 reg; 34239b9004dSPhilipp Zabel 34339b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 34439b9004dSPhilipp Zabel 34539b9004dSPhilipp Zabel reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); 34639b9004dSPhilipp Zabel if (doublebuffer) 34739b9004dSPhilipp Zabel reg |= idma_mask(channel->num); 34839b9004dSPhilipp Zabel else 34939b9004dSPhilipp Zabel reg &= ~idma_mask(channel->num); 35039b9004dSPhilipp Zabel ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num)); 35139b9004dSPhilipp Zabel 352e7268c69SSteve Longerbeam __ipu_idmac_reset_current_buffer(channel); 353e7268c69SSteve Longerbeam 35439b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 35539b9004dSPhilipp Zabel } 35639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer); 35739b9004dSPhilipp Zabel 3584fd1a07aSSteve Longerbeam static const struct { 3594fd1a07aSSteve Longerbeam int chnum; 3604fd1a07aSSteve Longerbeam u32 reg; 3614fd1a07aSSteve Longerbeam int shift; 3624fd1a07aSSteve Longerbeam } idmac_lock_en_info[] = { 3634fd1a07aSSteve Longerbeam { .chnum = 5, .reg = IDMAC_CH_LOCK_EN_1, .shift = 0, }, 3644fd1a07aSSteve Longerbeam { .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift = 2, }, 3654fd1a07aSSteve Longerbeam { .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift = 4, }, 3664fd1a07aSSteve Longerbeam { .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift = 6, }, 3674fd1a07aSSteve Longerbeam { .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift = 8, }, 3684fd1a07aSSteve Longerbeam { .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, }, 3694fd1a07aSSteve Longerbeam { .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, }, 3704fd1a07aSSteve Longerbeam { .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, }, 3714fd1a07aSSteve Longerbeam { .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, }, 3724fd1a07aSSteve Longerbeam { .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, }, 3734fd1a07aSSteve Longerbeam { .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, }, 3744fd1a07aSSteve Longerbeam { .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift = 0, }, 3754fd1a07aSSteve Longerbeam { .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift = 2, }, 3764fd1a07aSSteve Longerbeam { .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift = 4, }, 3774fd1a07aSSteve Longerbeam { .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift = 6, }, 3784fd1a07aSSteve Longerbeam { .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift = 8, }, 3794fd1a07aSSteve Longerbeam { .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, }, 3804fd1a07aSSteve Longerbeam }; 3814fd1a07aSSteve Longerbeam 3824fd1a07aSSteve Longerbeam int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts) 3834fd1a07aSSteve Longerbeam { 3844fd1a07aSSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 3854fd1a07aSSteve Longerbeam unsigned long flags; 3864fd1a07aSSteve Longerbeam u32 bursts, regval; 3874fd1a07aSSteve Longerbeam int i; 3884fd1a07aSSteve Longerbeam 3894fd1a07aSSteve Longerbeam switch (num_bursts) { 3904fd1a07aSSteve Longerbeam case 0: 3914fd1a07aSSteve Longerbeam case 1: 3924fd1a07aSSteve Longerbeam bursts = 0x00; /* locking disabled */ 3934fd1a07aSSteve Longerbeam break; 3944fd1a07aSSteve Longerbeam case 2: 3954fd1a07aSSteve Longerbeam bursts = 0x01; 3964fd1a07aSSteve Longerbeam break; 3974fd1a07aSSteve Longerbeam case 4: 3984fd1a07aSSteve Longerbeam bursts = 0x02; 3994fd1a07aSSteve Longerbeam break; 4004fd1a07aSSteve Longerbeam case 8: 4014fd1a07aSSteve Longerbeam bursts = 0x03; 4024fd1a07aSSteve Longerbeam break; 4034fd1a07aSSteve Longerbeam default: 4044fd1a07aSSteve Longerbeam return -EINVAL; 4054fd1a07aSSteve Longerbeam } 4064fd1a07aSSteve Longerbeam 407cda77556SPhilipp Zabel /* 408cda77556SPhilipp Zabel * IPUv3EX / i.MX51 has a different register layout, and on IPUv3M / 409cda77556SPhilipp Zabel * i.MX53 channel arbitration locking doesn't seem to work properly. 410cda77556SPhilipp Zabel * Allow enabling the lock feature on IPUv3H / i.MX6 only. 411cda77556SPhilipp Zabel */ 412cda77556SPhilipp Zabel if (bursts && ipu->ipu_type != IPUV3H) 413cda77556SPhilipp Zabel return -EINVAL; 414cda77556SPhilipp Zabel 4154fd1a07aSSteve Longerbeam for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) { 4164fd1a07aSSteve Longerbeam if (channel->num == idmac_lock_en_info[i].chnum) 4174fd1a07aSSteve Longerbeam break; 4184fd1a07aSSteve Longerbeam } 4194fd1a07aSSteve Longerbeam if (i >= ARRAY_SIZE(idmac_lock_en_info)) 4204fd1a07aSSteve Longerbeam return -EINVAL; 4214fd1a07aSSteve Longerbeam 4224fd1a07aSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 4234fd1a07aSSteve Longerbeam 4244fd1a07aSSteve Longerbeam regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg); 4254fd1a07aSSteve Longerbeam regval &= ~(0x03 << idmac_lock_en_info[i].shift); 4264fd1a07aSSteve Longerbeam regval |= (bursts << idmac_lock_en_info[i].shift); 4274fd1a07aSSteve Longerbeam ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg); 4284fd1a07aSSteve Longerbeam 4294fd1a07aSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 4304fd1a07aSSteve Longerbeam 4314fd1a07aSSteve Longerbeam return 0; 4324fd1a07aSSteve Longerbeam } 4334fd1a07aSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable); 4344fd1a07aSSteve Longerbeam 43539b9004dSPhilipp Zabel int ipu_module_enable(struct ipu_soc *ipu, u32 mask) 43639b9004dSPhilipp Zabel { 43739b9004dSPhilipp Zabel unsigned long lock_flags; 43839b9004dSPhilipp Zabel u32 val; 43939b9004dSPhilipp Zabel 44039b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, lock_flags); 44139b9004dSPhilipp Zabel 44239b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_DISP_GEN); 44339b9004dSPhilipp Zabel 44439b9004dSPhilipp Zabel if (mask & IPU_CONF_DI0_EN) 44539b9004dSPhilipp Zabel val |= IPU_DI0_COUNTER_RELEASE; 44639b9004dSPhilipp Zabel if (mask & IPU_CONF_DI1_EN) 44739b9004dSPhilipp Zabel val |= IPU_DI1_COUNTER_RELEASE; 44839b9004dSPhilipp Zabel 44939b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_DISP_GEN); 45039b9004dSPhilipp Zabel 45139b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CONF); 45239b9004dSPhilipp Zabel val |= mask; 45339b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CONF); 45439b9004dSPhilipp Zabel 45539b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, lock_flags); 45639b9004dSPhilipp Zabel 45739b9004dSPhilipp Zabel return 0; 45839b9004dSPhilipp Zabel } 45939b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_enable); 46039b9004dSPhilipp Zabel 46139b9004dSPhilipp Zabel int ipu_module_disable(struct ipu_soc *ipu, u32 mask) 46239b9004dSPhilipp Zabel { 46339b9004dSPhilipp Zabel unsigned long lock_flags; 46439b9004dSPhilipp Zabel u32 val; 46539b9004dSPhilipp Zabel 46639b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, lock_flags); 46739b9004dSPhilipp Zabel 46839b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CONF); 46939b9004dSPhilipp Zabel val &= ~mask; 47039b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CONF); 47139b9004dSPhilipp Zabel 47239b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_DISP_GEN); 47339b9004dSPhilipp Zabel 47439b9004dSPhilipp Zabel if (mask & IPU_CONF_DI0_EN) 47539b9004dSPhilipp Zabel val &= ~IPU_DI0_COUNTER_RELEASE; 47639b9004dSPhilipp Zabel if (mask & IPU_CONF_DI1_EN) 47739b9004dSPhilipp Zabel val &= ~IPU_DI1_COUNTER_RELEASE; 47839b9004dSPhilipp Zabel 47939b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_DISP_GEN); 48039b9004dSPhilipp Zabel 48139b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, lock_flags); 48239b9004dSPhilipp Zabel 48339b9004dSPhilipp Zabel return 0; 48439b9004dSPhilipp Zabel } 48539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_disable); 48639b9004dSPhilipp Zabel 487e9046097SPhilipp Zabel int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel) 488e9046097SPhilipp Zabel { 489e9046097SPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 490e9046097SPhilipp Zabel unsigned int chno = channel->num; 491e9046097SPhilipp Zabel 492e9046097SPhilipp Zabel return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0; 493e9046097SPhilipp Zabel } 494e9046097SPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer); 495e9046097SPhilipp Zabel 496aa52f578SSteve Longerbeam bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num) 497aa52f578SSteve Longerbeam { 498aa52f578SSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 499aa52f578SSteve Longerbeam unsigned long flags; 500aa52f578SSteve Longerbeam u32 reg = 0; 501aa52f578SSteve Longerbeam 502aa52f578SSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 503aa52f578SSteve Longerbeam switch (buf_num) { 504aa52f578SSteve Longerbeam case 0: 505aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)); 506aa52f578SSteve Longerbeam break; 507aa52f578SSteve Longerbeam case 1: 508aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)); 509aa52f578SSteve Longerbeam break; 510aa52f578SSteve Longerbeam case 2: 511aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num)); 512aa52f578SSteve Longerbeam break; 513aa52f578SSteve Longerbeam } 514aa52f578SSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 515aa52f578SSteve Longerbeam 516aa52f578SSteve Longerbeam return ((reg & idma_mask(channel->num)) != 0); 517aa52f578SSteve Longerbeam } 518aa52f578SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready); 519aa52f578SSteve Longerbeam 52039b9004dSPhilipp Zabel void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num) 52139b9004dSPhilipp Zabel { 52239b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 52339b9004dSPhilipp Zabel unsigned int chno = channel->num; 52439b9004dSPhilipp Zabel unsigned long flags; 52539b9004dSPhilipp Zabel 52639b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 52739b9004dSPhilipp Zabel 52839b9004dSPhilipp Zabel /* Mark buffer as ready. */ 52939b9004dSPhilipp Zabel if (buf_num == 0) 53039b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); 53139b9004dSPhilipp Zabel else 53239b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); 53339b9004dSPhilipp Zabel 53439b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 53539b9004dSPhilipp Zabel } 53639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer); 53739b9004dSPhilipp Zabel 538bce6f087SSteve Longerbeam void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num) 539bce6f087SSteve Longerbeam { 540bce6f087SSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 541bce6f087SSteve Longerbeam unsigned int chno = channel->num; 542bce6f087SSteve Longerbeam unsigned long flags; 543bce6f087SSteve Longerbeam 544bce6f087SSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 545bce6f087SSteve Longerbeam 546bce6f087SSteve Longerbeam ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */ 547bce6f087SSteve Longerbeam switch (buf_num) { 548bce6f087SSteve Longerbeam case 0: 549bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); 550bce6f087SSteve Longerbeam break; 551bce6f087SSteve Longerbeam case 1: 552bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); 553bce6f087SSteve Longerbeam break; 554bce6f087SSteve Longerbeam case 2: 555bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno)); 556bce6f087SSteve Longerbeam break; 557bce6f087SSteve Longerbeam default: 558bce6f087SSteve Longerbeam break; 559bce6f087SSteve Longerbeam } 560bce6f087SSteve Longerbeam ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ 561bce6f087SSteve Longerbeam 562bce6f087SSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 563bce6f087SSteve Longerbeam } 564bce6f087SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer); 565bce6f087SSteve Longerbeam 56639b9004dSPhilipp Zabel int ipu_idmac_enable_channel(struct ipuv3_channel *channel) 56739b9004dSPhilipp Zabel { 56839b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 56939b9004dSPhilipp Zabel u32 val; 57039b9004dSPhilipp Zabel unsigned long flags; 57139b9004dSPhilipp Zabel 57239b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 57339b9004dSPhilipp Zabel 57439b9004dSPhilipp Zabel val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); 57539b9004dSPhilipp Zabel val |= idma_mask(channel->num); 57639b9004dSPhilipp Zabel ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); 57739b9004dSPhilipp Zabel 57839b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 57939b9004dSPhilipp Zabel 58039b9004dSPhilipp Zabel return 0; 58139b9004dSPhilipp Zabel } 58239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel); 58339b9004dSPhilipp Zabel 584682b7c1cSLinus Torvalds bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno) 585682b7c1cSLinus Torvalds { 586682b7c1cSLinus Torvalds return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno)); 587682b7c1cSLinus Torvalds } 588682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy); 589682b7c1cSLinus Torvalds 59039b9004dSPhilipp Zabel int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) 59139b9004dSPhilipp Zabel { 59239b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 59339b9004dSPhilipp Zabel unsigned long timeout; 59439b9004dSPhilipp Zabel 59539b9004dSPhilipp Zabel timeout = jiffies + msecs_to_jiffies(ms); 59639b9004dSPhilipp Zabel while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) & 59739b9004dSPhilipp Zabel idma_mask(channel->num)) { 59839b9004dSPhilipp Zabel if (time_after(jiffies, timeout)) 59939b9004dSPhilipp Zabel return -ETIMEDOUT; 60039b9004dSPhilipp Zabel cpu_relax(); 60139b9004dSPhilipp Zabel } 60239b9004dSPhilipp Zabel 60339b9004dSPhilipp Zabel return 0; 60439b9004dSPhilipp Zabel } 60539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); 60639b9004dSPhilipp Zabel 60739b9004dSPhilipp Zabel int ipu_idmac_disable_channel(struct ipuv3_channel *channel) 60839b9004dSPhilipp Zabel { 60939b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 61039b9004dSPhilipp Zabel u32 val; 61139b9004dSPhilipp Zabel unsigned long flags; 61239b9004dSPhilipp Zabel 61339b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 61439b9004dSPhilipp Zabel 61539b9004dSPhilipp Zabel /* Disable DMA channel(s) */ 61639b9004dSPhilipp Zabel val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); 61739b9004dSPhilipp Zabel val &= ~idma_mask(channel->num); 61839b9004dSPhilipp Zabel ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); 61939b9004dSPhilipp Zabel 620e7268c69SSteve Longerbeam __ipu_idmac_reset_current_buffer(channel); 621e7268c69SSteve Longerbeam 62239b9004dSPhilipp Zabel /* Set channel buffers NOT to be ready */ 62339b9004dSPhilipp Zabel ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */ 62439b9004dSPhilipp Zabel 62539b9004dSPhilipp Zabel if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) & 62639b9004dSPhilipp Zabel idma_mask(channel->num)) { 62739b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(channel->num), 62839b9004dSPhilipp Zabel IPU_CHA_BUF0_RDY(channel->num)); 62939b9004dSPhilipp Zabel } 63039b9004dSPhilipp Zabel 63139b9004dSPhilipp Zabel if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) & 63239b9004dSPhilipp Zabel idma_mask(channel->num)) { 63339b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(channel->num), 63439b9004dSPhilipp Zabel IPU_CHA_BUF1_RDY(channel->num)); 63539b9004dSPhilipp Zabel } 63639b9004dSPhilipp Zabel 63739b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ 63839b9004dSPhilipp Zabel 63939b9004dSPhilipp Zabel /* Reset the double buffer */ 64039b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); 64139b9004dSPhilipp Zabel val &= ~idma_mask(channel->num); 64239b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num)); 64339b9004dSPhilipp Zabel 64439b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 64539b9004dSPhilipp Zabel 64639b9004dSPhilipp Zabel return 0; 64739b9004dSPhilipp Zabel } 64839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel); 64939b9004dSPhilipp Zabel 6502bcf577eSSteve Longerbeam /* 6512bcf577eSSteve Longerbeam * The imx6 rev. D TRM says that enabling the WM feature will increase 6522bcf577eSSteve Longerbeam * a channel's priority. Refer to Table 36-8 Calculated priority value. 6532bcf577eSSteve Longerbeam * The sub-module that is the sink or source for the channel must enable 6542bcf577eSSteve Longerbeam * watermark signal for this to take effect (SMFC_WM for instance). 6552bcf577eSSteve Longerbeam */ 6562bcf577eSSteve Longerbeam void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable) 6572bcf577eSSteve Longerbeam { 6582bcf577eSSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 6592bcf577eSSteve Longerbeam unsigned long flags; 6602bcf577eSSteve Longerbeam u32 val; 6612bcf577eSSteve Longerbeam 6622bcf577eSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 6632bcf577eSSteve Longerbeam 6642bcf577eSSteve Longerbeam val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num)); 6652bcf577eSSteve Longerbeam if (enable) 6662bcf577eSSteve Longerbeam val |= 1 << (channel->num % 32); 6672bcf577eSSteve Longerbeam else 6682bcf577eSSteve Longerbeam val &= ~(1 << (channel->num % 32)); 6692bcf577eSSteve Longerbeam ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num)); 6702bcf577eSSteve Longerbeam 6712bcf577eSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 6722bcf577eSSteve Longerbeam } 6732bcf577eSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark); 6742bcf577eSSteve Longerbeam 67539b9004dSPhilipp Zabel static int ipu_memory_reset(struct ipu_soc *ipu) 67639b9004dSPhilipp Zabel { 67739b9004dSPhilipp Zabel unsigned long timeout; 67839b9004dSPhilipp Zabel 67939b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); 68039b9004dSPhilipp Zabel 68139b9004dSPhilipp Zabel timeout = jiffies + msecs_to_jiffies(1000); 68239b9004dSPhilipp Zabel while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { 68339b9004dSPhilipp Zabel if (time_after(jiffies, timeout)) 68439b9004dSPhilipp Zabel return -ETIME; 68539b9004dSPhilipp Zabel cpu_relax(); 68639b9004dSPhilipp Zabel } 68739b9004dSPhilipp Zabel 68839b9004dSPhilipp Zabel return 0; 68939b9004dSPhilipp Zabel } 69039b9004dSPhilipp Zabel 691ba07975fSSteve Longerbeam /* 692ba07975fSSteve Longerbeam * Set the source mux for the given CSI. Selects either parallel or 693ba07975fSSteve Longerbeam * MIPI CSI2 sources. 694ba07975fSSteve Longerbeam */ 695ba07975fSSteve Longerbeam void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2) 696ba07975fSSteve Longerbeam { 697ba07975fSSteve Longerbeam unsigned long flags; 698ba07975fSSteve Longerbeam u32 val, mask; 699ba07975fSSteve Longerbeam 700ba07975fSSteve Longerbeam mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE : 701ba07975fSSteve Longerbeam IPU_CONF_CSI0_DATA_SOURCE; 702ba07975fSSteve Longerbeam 703ba07975fSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 704ba07975fSSteve Longerbeam 705ba07975fSSteve Longerbeam val = ipu_cm_read(ipu, IPU_CONF); 706ba07975fSSteve Longerbeam if (mipi_csi2) 707ba07975fSSteve Longerbeam val |= mask; 708ba07975fSSteve Longerbeam else 709ba07975fSSteve Longerbeam val &= ~mask; 710ba07975fSSteve Longerbeam ipu_cm_write(ipu, val, IPU_CONF); 711ba07975fSSteve Longerbeam 712ba07975fSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 713ba07975fSSteve Longerbeam } 714ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux); 715ba07975fSSteve Longerbeam 716ba07975fSSteve Longerbeam /* 717ba07975fSSteve Longerbeam * Set the source mux for the IC. Selects either CSI[01] or the VDI. 718ba07975fSSteve Longerbeam */ 719ba07975fSSteve Longerbeam void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi) 720ba07975fSSteve Longerbeam { 721ba07975fSSteve Longerbeam unsigned long flags; 722ba07975fSSteve Longerbeam u32 val; 723ba07975fSSteve Longerbeam 724ba07975fSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 725ba07975fSSteve Longerbeam 726ba07975fSSteve Longerbeam val = ipu_cm_read(ipu, IPU_CONF); 727b7dfee24SMarek Vasut if (vdi) 728ba07975fSSteve Longerbeam val |= IPU_CONF_IC_INPUT; 729b7dfee24SMarek Vasut else 730ba07975fSSteve Longerbeam val &= ~IPU_CONF_IC_INPUT; 731b7dfee24SMarek Vasut 732ba07975fSSteve Longerbeam if (csi_id == 1) 733ba07975fSSteve Longerbeam val |= IPU_CONF_CSI_SEL; 734ba07975fSSteve Longerbeam else 735ba07975fSSteve Longerbeam val &= ~IPU_CONF_CSI_SEL; 736b7dfee24SMarek Vasut 737ba07975fSSteve Longerbeam ipu_cm_write(ipu, val, IPU_CONF); 738ba07975fSSteve Longerbeam 739ba07975fSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 740ba07975fSSteve Longerbeam } 741ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux); 742ba07975fSSteve Longerbeam 743ac4708faSSteve Longerbeam 744ac4708faSSteve Longerbeam /* Frame Synchronization Unit Channel Linking */ 745ac4708faSSteve Longerbeam 746ac4708faSSteve Longerbeam struct fsu_link_reg_info { 747ac4708faSSteve Longerbeam int chno; 748ac4708faSSteve Longerbeam u32 reg; 749ac4708faSSteve Longerbeam u32 mask; 750ac4708faSSteve Longerbeam u32 val; 751ac4708faSSteve Longerbeam }; 752ac4708faSSteve Longerbeam 753ac4708faSSteve Longerbeam struct fsu_link_info { 754ac4708faSSteve Longerbeam struct fsu_link_reg_info src; 755ac4708faSSteve Longerbeam struct fsu_link_reg_info sink; 756ac4708faSSteve Longerbeam }; 757ac4708faSSteve Longerbeam 758ac4708faSSteve Longerbeam static const struct fsu_link_info fsu_link_info[] = { 759ac4708faSSteve Longerbeam { 760ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2, 761ac4708faSSteve Longerbeam FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC }, 762ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1, 763ac4708faSSteve Longerbeam FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC }, 764ac4708faSSteve Longerbeam }, { 765ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2, 766ac4708faSSteve Longerbeam FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF }, 767ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1, 768ac4708faSSteve Longerbeam FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF }, 769ac4708faSSteve Longerbeam }, { 770ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2, 771ac4708faSSteve Longerbeam FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP }, 772ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1, 773ac4708faSSteve Longerbeam FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP }, 774ac4708faSSteve Longerbeam }, { 775ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_CSI_DIRECT, 0 }, 776ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1, 777ac4708faSSteve Longerbeam FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT }, 778ac4708faSSteve Longerbeam }, 779ac4708faSSteve Longerbeam }; 780ac4708faSSteve Longerbeam 781ac4708faSSteve Longerbeam static const struct fsu_link_info *find_fsu_link_info(int src, int sink) 782ac4708faSSteve Longerbeam { 783ac4708faSSteve Longerbeam int i; 784ac4708faSSteve Longerbeam 785ac4708faSSteve Longerbeam for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) { 786ac4708faSSteve Longerbeam if (src == fsu_link_info[i].src.chno && 787ac4708faSSteve Longerbeam sink == fsu_link_info[i].sink.chno) 788ac4708faSSteve Longerbeam return &fsu_link_info[i]; 789ac4708faSSteve Longerbeam } 790ac4708faSSteve Longerbeam 791ac4708faSSteve Longerbeam return NULL; 792ac4708faSSteve Longerbeam } 793ac4708faSSteve Longerbeam 794ac4708faSSteve Longerbeam /* 795ac4708faSSteve Longerbeam * Links a source channel to a sink channel in the FSU. 796ac4708faSSteve Longerbeam */ 797ac4708faSSteve Longerbeam int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch) 798ac4708faSSteve Longerbeam { 799ac4708faSSteve Longerbeam const struct fsu_link_info *link; 800ac4708faSSteve Longerbeam u32 src_reg, sink_reg; 801ac4708faSSteve Longerbeam unsigned long flags; 802ac4708faSSteve Longerbeam 803ac4708faSSteve Longerbeam link = find_fsu_link_info(src_ch, sink_ch); 804ac4708faSSteve Longerbeam if (!link) 805ac4708faSSteve Longerbeam return -EINVAL; 806ac4708faSSteve Longerbeam 807ac4708faSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 808ac4708faSSteve Longerbeam 809ac4708faSSteve Longerbeam if (link->src.mask) { 810ac4708faSSteve Longerbeam src_reg = ipu_cm_read(ipu, link->src.reg); 811ac4708faSSteve Longerbeam src_reg &= ~link->src.mask; 812ac4708faSSteve Longerbeam src_reg |= link->src.val; 813ac4708faSSteve Longerbeam ipu_cm_write(ipu, src_reg, link->src.reg); 814ac4708faSSteve Longerbeam } 815ac4708faSSteve Longerbeam 816ac4708faSSteve Longerbeam if (link->sink.mask) { 817ac4708faSSteve Longerbeam sink_reg = ipu_cm_read(ipu, link->sink.reg); 818ac4708faSSteve Longerbeam sink_reg &= ~link->sink.mask; 819ac4708faSSteve Longerbeam sink_reg |= link->sink.val; 820ac4708faSSteve Longerbeam ipu_cm_write(ipu, sink_reg, link->sink.reg); 821ac4708faSSteve Longerbeam } 822ac4708faSSteve Longerbeam 823ac4708faSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 824ac4708faSSteve Longerbeam return 0; 825ac4708faSSteve Longerbeam } 826ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_link); 827ac4708faSSteve Longerbeam 828ac4708faSSteve Longerbeam /* 829ac4708faSSteve Longerbeam * Unlinks source and sink channels in the FSU. 830ac4708faSSteve Longerbeam */ 831ac4708faSSteve Longerbeam int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch) 832ac4708faSSteve Longerbeam { 833ac4708faSSteve Longerbeam const struct fsu_link_info *link; 834ac4708faSSteve Longerbeam u32 src_reg, sink_reg; 835ac4708faSSteve Longerbeam unsigned long flags; 836ac4708faSSteve Longerbeam 837ac4708faSSteve Longerbeam link = find_fsu_link_info(src_ch, sink_ch); 838ac4708faSSteve Longerbeam if (!link) 839ac4708faSSteve Longerbeam return -EINVAL; 840ac4708faSSteve Longerbeam 841ac4708faSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 842ac4708faSSteve Longerbeam 843ac4708faSSteve Longerbeam if (link->src.mask) { 844ac4708faSSteve Longerbeam src_reg = ipu_cm_read(ipu, link->src.reg); 845ac4708faSSteve Longerbeam src_reg &= ~link->src.mask; 846ac4708faSSteve Longerbeam ipu_cm_write(ipu, src_reg, link->src.reg); 847ac4708faSSteve Longerbeam } 848ac4708faSSteve Longerbeam 849ac4708faSSteve Longerbeam if (link->sink.mask) { 850ac4708faSSteve Longerbeam sink_reg = ipu_cm_read(ipu, link->sink.reg); 851ac4708faSSteve Longerbeam sink_reg &= ~link->sink.mask; 852ac4708faSSteve Longerbeam ipu_cm_write(ipu, sink_reg, link->sink.reg); 853ac4708faSSteve Longerbeam } 854ac4708faSSteve Longerbeam 855ac4708faSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 856ac4708faSSteve Longerbeam return 0; 857ac4708faSSteve Longerbeam } 858ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_unlink); 859ac4708faSSteve Longerbeam 860ac4708faSSteve Longerbeam /* Link IDMAC channels in the FSU */ 861ac4708faSSteve Longerbeam int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink) 862ac4708faSSteve Longerbeam { 863ac4708faSSteve Longerbeam return ipu_fsu_link(src->ipu, src->num, sink->num); 864ac4708faSSteve Longerbeam } 865ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_link); 866ac4708faSSteve Longerbeam 867ac4708faSSteve Longerbeam /* Unlink IDMAC channels in the FSU */ 868ac4708faSSteve Longerbeam int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink) 869ac4708faSSteve Longerbeam { 870ac4708faSSteve Longerbeam return ipu_fsu_unlink(src->ipu, src->num, sink->num); 871ac4708faSSteve Longerbeam } 872ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_unlink); 873ac4708faSSteve Longerbeam 87439b9004dSPhilipp Zabel struct ipu_devtype { 87539b9004dSPhilipp Zabel const char *name; 87639b9004dSPhilipp Zabel unsigned long cm_ofs; 87739b9004dSPhilipp Zabel unsigned long cpmem_ofs; 87839b9004dSPhilipp Zabel unsigned long srm_ofs; 87939b9004dSPhilipp Zabel unsigned long tpm_ofs; 8802ffd48f2SSteve Longerbeam unsigned long csi0_ofs; 8812ffd48f2SSteve Longerbeam unsigned long csi1_ofs; 8821aa8ea0dSSteve Longerbeam unsigned long ic_ofs; 88339b9004dSPhilipp Zabel unsigned long disp0_ofs; 88439b9004dSPhilipp Zabel unsigned long disp1_ofs; 88539b9004dSPhilipp Zabel unsigned long dc_tmpl_ofs; 88639b9004dSPhilipp Zabel unsigned long vdi_ofs; 88739b9004dSPhilipp Zabel enum ipuv3_type type; 88839b9004dSPhilipp Zabel }; 88939b9004dSPhilipp Zabel 89039b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx51 = { 89139b9004dSPhilipp Zabel .name = "IPUv3EX", 89239b9004dSPhilipp Zabel .cm_ofs = 0x1e000000, 89339b9004dSPhilipp Zabel .cpmem_ofs = 0x1f000000, 89439b9004dSPhilipp Zabel .srm_ofs = 0x1f040000, 89539b9004dSPhilipp Zabel .tpm_ofs = 0x1f060000, 8962c0408ddSAlexander Shiyan .csi0_ofs = 0x1e030000, 8972c0408ddSAlexander Shiyan .csi1_ofs = 0x1e038000, 898a49e7c0dSPhilipp Zabel .ic_ofs = 0x1e020000, 89939b9004dSPhilipp Zabel .disp0_ofs = 0x1e040000, 90039b9004dSPhilipp Zabel .disp1_ofs = 0x1e048000, 90139b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x1f080000, 90239b9004dSPhilipp Zabel .vdi_ofs = 0x1e068000, 90339b9004dSPhilipp Zabel .type = IPUV3EX, 90439b9004dSPhilipp Zabel }; 90539b9004dSPhilipp Zabel 90639b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx53 = { 90739b9004dSPhilipp Zabel .name = "IPUv3M", 90839b9004dSPhilipp Zabel .cm_ofs = 0x06000000, 90939b9004dSPhilipp Zabel .cpmem_ofs = 0x07000000, 91039b9004dSPhilipp Zabel .srm_ofs = 0x07040000, 91139b9004dSPhilipp Zabel .tpm_ofs = 0x07060000, 912bb867d21SSteve Longerbeam .csi0_ofs = 0x06030000, 913bb867d21SSteve Longerbeam .csi1_ofs = 0x06038000, 914a49e7c0dSPhilipp Zabel .ic_ofs = 0x06020000, 91539b9004dSPhilipp Zabel .disp0_ofs = 0x06040000, 91639b9004dSPhilipp Zabel .disp1_ofs = 0x06048000, 91739b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x07080000, 91839b9004dSPhilipp Zabel .vdi_ofs = 0x06068000, 91939b9004dSPhilipp Zabel .type = IPUV3M, 92039b9004dSPhilipp Zabel }; 92139b9004dSPhilipp Zabel 92239b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx6q = { 92339b9004dSPhilipp Zabel .name = "IPUv3H", 92439b9004dSPhilipp Zabel .cm_ofs = 0x00200000, 92539b9004dSPhilipp Zabel .cpmem_ofs = 0x00300000, 92639b9004dSPhilipp Zabel .srm_ofs = 0x00340000, 92739b9004dSPhilipp Zabel .tpm_ofs = 0x00360000, 9282ffd48f2SSteve Longerbeam .csi0_ofs = 0x00230000, 9292ffd48f2SSteve Longerbeam .csi1_ofs = 0x00238000, 9301aa8ea0dSSteve Longerbeam .ic_ofs = 0x00220000, 93139b9004dSPhilipp Zabel .disp0_ofs = 0x00240000, 93239b9004dSPhilipp Zabel .disp1_ofs = 0x00248000, 93339b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x00380000, 93439b9004dSPhilipp Zabel .vdi_ofs = 0x00268000, 93539b9004dSPhilipp Zabel .type = IPUV3H, 93639b9004dSPhilipp Zabel }; 93739b9004dSPhilipp Zabel 93839b9004dSPhilipp Zabel static const struct of_device_id imx_ipu_dt_ids[] = { 93939b9004dSPhilipp Zabel { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, 94039b9004dSPhilipp Zabel { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, 94139b9004dSPhilipp Zabel { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, 94292681fe7SLucas Stach { .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6q, }, 94339b9004dSPhilipp Zabel { /* sentinel */ } 94439b9004dSPhilipp Zabel }; 94539b9004dSPhilipp Zabel MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); 94639b9004dSPhilipp Zabel 94739b9004dSPhilipp Zabel static int ipu_submodules_init(struct ipu_soc *ipu, 94839b9004dSPhilipp Zabel struct platform_device *pdev, unsigned long ipu_base, 94939b9004dSPhilipp Zabel struct clk *ipu_clk) 95039b9004dSPhilipp Zabel { 95139b9004dSPhilipp Zabel char *unit; 95239b9004dSPhilipp Zabel int ret; 95339b9004dSPhilipp Zabel struct device *dev = &pdev->dev; 95439b9004dSPhilipp Zabel const struct ipu_devtype *devtype = ipu->devtype; 95539b9004dSPhilipp Zabel 9567d2691daSSteve Longerbeam ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs); 9577d2691daSSteve Longerbeam if (ret) { 9587d2691daSSteve Longerbeam unit = "cpmem"; 9597d2691daSSteve Longerbeam goto err_cpmem; 9607d2691daSSteve Longerbeam } 9617d2691daSSteve Longerbeam 9622ffd48f2SSteve Longerbeam ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs, 9632ffd48f2SSteve Longerbeam IPU_CONF_CSI0_EN, ipu_clk); 9642ffd48f2SSteve Longerbeam if (ret) { 9652ffd48f2SSteve Longerbeam unit = "csi0"; 9662ffd48f2SSteve Longerbeam goto err_csi_0; 9672ffd48f2SSteve Longerbeam } 9682ffd48f2SSteve Longerbeam 9692ffd48f2SSteve Longerbeam ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs, 9702ffd48f2SSteve Longerbeam IPU_CONF_CSI1_EN, ipu_clk); 9712ffd48f2SSteve Longerbeam if (ret) { 9722ffd48f2SSteve Longerbeam unit = "csi1"; 9732ffd48f2SSteve Longerbeam goto err_csi_1; 9742ffd48f2SSteve Longerbeam } 9752ffd48f2SSteve Longerbeam 9761aa8ea0dSSteve Longerbeam ret = ipu_ic_init(ipu, dev, 9771aa8ea0dSSteve Longerbeam ipu_base + devtype->ic_ofs, 9781aa8ea0dSSteve Longerbeam ipu_base + devtype->tpm_ofs); 9791aa8ea0dSSteve Longerbeam if (ret) { 9801aa8ea0dSSteve Longerbeam unit = "ic"; 9811aa8ea0dSSteve Longerbeam goto err_ic; 9821aa8ea0dSSteve Longerbeam } 9831aa8ea0dSSteve Longerbeam 9842d2ead45SSteve Longerbeam ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs, 9852d2ead45SSteve Longerbeam IPU_CONF_VDI_EN | IPU_CONF_ISP_EN | 9862d2ead45SSteve Longerbeam IPU_CONF_IC_INPUT); 9872d2ead45SSteve Longerbeam if (ret) { 9882d2ead45SSteve Longerbeam unit = "vdi"; 9892d2ead45SSteve Longerbeam goto err_vdi; 9902d2ead45SSteve Longerbeam } 9912d2ead45SSteve Longerbeam 992cd98e85aSSteve Longerbeam ret = ipu_image_convert_init(ipu, dev); 993cd98e85aSSteve Longerbeam if (ret) { 994cd98e85aSSteve Longerbeam unit = "image_convert"; 995cd98e85aSSteve Longerbeam goto err_image_convert; 996cd98e85aSSteve Longerbeam } 997cd98e85aSSteve Longerbeam 99839b9004dSPhilipp Zabel ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, 99939b9004dSPhilipp Zabel IPU_CONF_DI0_EN, ipu_clk); 100039b9004dSPhilipp Zabel if (ret) { 100139b9004dSPhilipp Zabel unit = "di0"; 100239b9004dSPhilipp Zabel goto err_di_0; 100339b9004dSPhilipp Zabel } 100439b9004dSPhilipp Zabel 100539b9004dSPhilipp Zabel ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs, 100639b9004dSPhilipp Zabel IPU_CONF_DI1_EN, ipu_clk); 100739b9004dSPhilipp Zabel if (ret) { 100839b9004dSPhilipp Zabel unit = "di1"; 100939b9004dSPhilipp Zabel goto err_di_1; 101039b9004dSPhilipp Zabel } 101139b9004dSPhilipp Zabel 101239b9004dSPhilipp Zabel ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs + 101339b9004dSPhilipp Zabel IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs); 101439b9004dSPhilipp Zabel if (ret) { 101539b9004dSPhilipp Zabel unit = "dc_template"; 101639b9004dSPhilipp Zabel goto err_dc; 101739b9004dSPhilipp Zabel } 101839b9004dSPhilipp Zabel 101939b9004dSPhilipp Zabel ret = ipu_dmfc_init(ipu, dev, ipu_base + 102039b9004dSPhilipp Zabel devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk); 102139b9004dSPhilipp Zabel if (ret) { 102239b9004dSPhilipp Zabel unit = "dmfc"; 102339b9004dSPhilipp Zabel goto err_dmfc; 102439b9004dSPhilipp Zabel } 102539b9004dSPhilipp Zabel 102639b9004dSPhilipp Zabel ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs); 102739b9004dSPhilipp Zabel if (ret) { 102839b9004dSPhilipp Zabel unit = "dp"; 102939b9004dSPhilipp Zabel goto err_dp; 103039b9004dSPhilipp Zabel } 103139b9004dSPhilipp Zabel 103235de925fSPhilipp Zabel ret = ipu_smfc_init(ipu, dev, ipu_base + 103335de925fSPhilipp Zabel devtype->cm_ofs + IPU_CM_SMFC_REG_OFS); 103435de925fSPhilipp Zabel if (ret) { 103535de925fSPhilipp Zabel unit = "smfc"; 103635de925fSPhilipp Zabel goto err_smfc; 103735de925fSPhilipp Zabel } 103835de925fSPhilipp Zabel 103939b9004dSPhilipp Zabel return 0; 104039b9004dSPhilipp Zabel 104135de925fSPhilipp Zabel err_smfc: 104235de925fSPhilipp Zabel ipu_dp_exit(ipu); 104339b9004dSPhilipp Zabel err_dp: 104439b9004dSPhilipp Zabel ipu_dmfc_exit(ipu); 104539b9004dSPhilipp Zabel err_dmfc: 104639b9004dSPhilipp Zabel ipu_dc_exit(ipu); 104739b9004dSPhilipp Zabel err_dc: 104839b9004dSPhilipp Zabel ipu_di_exit(ipu, 1); 104939b9004dSPhilipp Zabel err_di_1: 105039b9004dSPhilipp Zabel ipu_di_exit(ipu, 0); 105139b9004dSPhilipp Zabel err_di_0: 1052cd98e85aSSteve Longerbeam ipu_image_convert_exit(ipu); 1053cd98e85aSSteve Longerbeam err_image_convert: 10542d2ead45SSteve Longerbeam ipu_vdi_exit(ipu); 10552d2ead45SSteve Longerbeam err_vdi: 10561aa8ea0dSSteve Longerbeam ipu_ic_exit(ipu); 10571aa8ea0dSSteve Longerbeam err_ic: 10582ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 1); 10592ffd48f2SSteve Longerbeam err_csi_1: 10602ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 0); 10612ffd48f2SSteve Longerbeam err_csi_0: 10627d2691daSSteve Longerbeam ipu_cpmem_exit(ipu); 10637d2691daSSteve Longerbeam err_cpmem: 106439b9004dSPhilipp Zabel dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret); 106539b9004dSPhilipp Zabel return ret; 106639b9004dSPhilipp Zabel } 106739b9004dSPhilipp Zabel 106839b9004dSPhilipp Zabel static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs) 106939b9004dSPhilipp Zabel { 107039b9004dSPhilipp Zabel unsigned long status; 107139b9004dSPhilipp Zabel int i, bit, irq; 107239b9004dSPhilipp Zabel 107339b9004dSPhilipp Zabel for (i = 0; i < num_regs; i++) { 107439b9004dSPhilipp Zabel 107539b9004dSPhilipp Zabel status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i])); 107639b9004dSPhilipp Zabel status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i])); 107739b9004dSPhilipp Zabel 107839b9004dSPhilipp Zabel for_each_set_bit(bit, &status, 32) { 1079682b7c1cSLinus Torvalds irq = irq_linear_revmap(ipu->domain, 1080682b7c1cSLinus Torvalds regs[i] * 32 + bit); 108139b9004dSPhilipp Zabel if (irq) 108239b9004dSPhilipp Zabel generic_handle_irq(irq); 108339b9004dSPhilipp Zabel } 108439b9004dSPhilipp Zabel } 108539b9004dSPhilipp Zabel } 108639b9004dSPhilipp Zabel 1087bd0b9ac4SThomas Gleixner static void ipu_irq_handler(struct irq_desc *desc) 108839b9004dSPhilipp Zabel { 108939b9004dSPhilipp Zabel struct ipu_soc *ipu = irq_desc_get_handler_data(desc); 10904d9efdfcSJiang Liu struct irq_chip *chip = irq_desc_get_chip(desc); 1091ac66b834SColin Ian King static const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14}; 109239b9004dSPhilipp Zabel 109339b9004dSPhilipp Zabel chained_irq_enter(chip, desc); 109439b9004dSPhilipp Zabel 109539b9004dSPhilipp Zabel ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); 109639b9004dSPhilipp Zabel 109739b9004dSPhilipp Zabel chained_irq_exit(chip, desc); 109839b9004dSPhilipp Zabel } 109939b9004dSPhilipp Zabel 1100bd0b9ac4SThomas Gleixner static void ipu_err_irq_handler(struct irq_desc *desc) 110139b9004dSPhilipp Zabel { 110239b9004dSPhilipp Zabel struct ipu_soc *ipu = irq_desc_get_handler_data(desc); 11034d9efdfcSJiang Liu struct irq_chip *chip = irq_desc_get_chip(desc); 1104ac66b834SColin Ian King static const int int_reg[] = { 4, 5, 8, 9}; 110539b9004dSPhilipp Zabel 110639b9004dSPhilipp Zabel chained_irq_enter(chip, desc); 110739b9004dSPhilipp Zabel 110839b9004dSPhilipp Zabel ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); 110939b9004dSPhilipp Zabel 111039b9004dSPhilipp Zabel chained_irq_exit(chip, desc); 111139b9004dSPhilipp Zabel } 111239b9004dSPhilipp Zabel 1113682b7c1cSLinus Torvalds int ipu_map_irq(struct ipu_soc *ipu, int irq) 1114682b7c1cSLinus Torvalds { 1115682b7c1cSLinus Torvalds int virq; 1116682b7c1cSLinus Torvalds 1117682b7c1cSLinus Torvalds virq = irq_linear_revmap(ipu->domain, irq); 1118682b7c1cSLinus Torvalds if (!virq) 1119682b7c1cSLinus Torvalds virq = irq_create_mapping(ipu->domain, irq); 1120682b7c1cSLinus Torvalds 1121682b7c1cSLinus Torvalds return virq; 1122682b7c1cSLinus Torvalds } 1123682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_map_irq); 1124682b7c1cSLinus Torvalds 112539b9004dSPhilipp Zabel int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, 112639b9004dSPhilipp Zabel enum ipu_channel_irq irq_type) 112739b9004dSPhilipp Zabel { 1128682b7c1cSLinus Torvalds return ipu_map_irq(ipu, irq_type + channel->num); 112939b9004dSPhilipp Zabel } 113039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq); 113139b9004dSPhilipp Zabel 113239b9004dSPhilipp Zabel static void ipu_submodules_exit(struct ipu_soc *ipu) 113339b9004dSPhilipp Zabel { 113435de925fSPhilipp Zabel ipu_smfc_exit(ipu); 113539b9004dSPhilipp Zabel ipu_dp_exit(ipu); 113639b9004dSPhilipp Zabel ipu_dmfc_exit(ipu); 113739b9004dSPhilipp Zabel ipu_dc_exit(ipu); 113839b9004dSPhilipp Zabel ipu_di_exit(ipu, 1); 113939b9004dSPhilipp Zabel ipu_di_exit(ipu, 0); 1140cd98e85aSSteve Longerbeam ipu_image_convert_exit(ipu); 11412d2ead45SSteve Longerbeam ipu_vdi_exit(ipu); 11421aa8ea0dSSteve Longerbeam ipu_ic_exit(ipu); 11432ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 1); 11442ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 0); 11457d2691daSSteve Longerbeam ipu_cpmem_exit(ipu); 114639b9004dSPhilipp Zabel } 114739b9004dSPhilipp Zabel 114839b9004dSPhilipp Zabel static int platform_remove_devices_fn(struct device *dev, void *unused) 114939b9004dSPhilipp Zabel { 115039b9004dSPhilipp Zabel struct platform_device *pdev = to_platform_device(dev); 115139b9004dSPhilipp Zabel 115239b9004dSPhilipp Zabel platform_device_unregister(pdev); 115339b9004dSPhilipp Zabel 115439b9004dSPhilipp Zabel return 0; 115539b9004dSPhilipp Zabel } 115639b9004dSPhilipp Zabel 115739b9004dSPhilipp Zabel static void platform_device_unregister_children(struct platform_device *pdev) 115839b9004dSPhilipp Zabel { 115939b9004dSPhilipp Zabel device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn); 116039b9004dSPhilipp Zabel } 116139b9004dSPhilipp Zabel 116239b9004dSPhilipp Zabel struct ipu_platform_reg { 116339b9004dSPhilipp Zabel struct ipu_client_platformdata pdata; 116439b9004dSPhilipp Zabel const char *name; 116539b9004dSPhilipp Zabel }; 116639b9004dSPhilipp Zabel 1167304e6be6SPhilipp Zabel /* These must be in the order of the corresponding device tree port nodes */ 1168310944d1SPhilipp Zabel static struct ipu_platform_reg client_reg[] = { 116939b9004dSPhilipp Zabel { 117039b9004dSPhilipp Zabel .pdata = { 1171304e6be6SPhilipp Zabel .csi = 0, 1172304e6be6SPhilipp Zabel .dma[0] = IPUV3_CHANNEL_CSI0, 1173304e6be6SPhilipp Zabel .dma[1] = -EINVAL, 1174304e6be6SPhilipp Zabel }, 117588287ec3SSteve Longerbeam .name = "imx-ipuv3-csi", 1176304e6be6SPhilipp Zabel }, { 1177304e6be6SPhilipp Zabel .pdata = { 1178304e6be6SPhilipp Zabel .csi = 1, 1179304e6be6SPhilipp Zabel .dma[0] = IPUV3_CHANNEL_CSI1, 1180304e6be6SPhilipp Zabel .dma[1] = -EINVAL, 1181304e6be6SPhilipp Zabel }, 118288287ec3SSteve Longerbeam .name = "imx-ipuv3-csi", 1183304e6be6SPhilipp Zabel }, { 1184304e6be6SPhilipp Zabel .pdata = { 118539b9004dSPhilipp Zabel .di = 0, 118639b9004dSPhilipp Zabel .dc = 5, 118739b9004dSPhilipp Zabel .dp = IPU_DP_FLOW_SYNC_BG, 118839b9004dSPhilipp Zabel .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC, 118939b9004dSPhilipp Zabel .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC, 119039b9004dSPhilipp Zabel }, 119139b9004dSPhilipp Zabel .name = "imx-ipuv3-crtc", 119239b9004dSPhilipp Zabel }, { 119339b9004dSPhilipp Zabel .pdata = { 119439b9004dSPhilipp Zabel .di = 1, 119539b9004dSPhilipp Zabel .dc = 1, 119639b9004dSPhilipp Zabel .dp = -EINVAL, 119739b9004dSPhilipp Zabel .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC, 119839b9004dSPhilipp Zabel .dma[1] = -EINVAL, 119939b9004dSPhilipp Zabel }, 120039b9004dSPhilipp Zabel .name = "imx-ipuv3-crtc", 120139b9004dSPhilipp Zabel }, 120239b9004dSPhilipp Zabel }; 120339b9004dSPhilipp Zabel 120439b9004dSPhilipp Zabel static DEFINE_MUTEX(ipu_client_id_mutex); 120539b9004dSPhilipp Zabel static int ipu_client_id; 120639b9004dSPhilipp Zabel 1207d6ca8ca7SPhilipp Zabel static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) 120839b9004dSPhilipp Zabel { 120939b9004dSPhilipp Zabel struct device *dev = ipu->dev; 121039b9004dSPhilipp Zabel unsigned i; 121139b9004dSPhilipp Zabel int id, ret; 121239b9004dSPhilipp Zabel 121339b9004dSPhilipp Zabel mutex_lock(&ipu_client_id_mutex); 121439b9004dSPhilipp Zabel id = ipu_client_id; 121539b9004dSPhilipp Zabel ipu_client_id += ARRAY_SIZE(client_reg); 121639b9004dSPhilipp Zabel mutex_unlock(&ipu_client_id_mutex); 121739b9004dSPhilipp Zabel 121839b9004dSPhilipp Zabel for (i = 0; i < ARRAY_SIZE(client_reg); i++) { 1219310944d1SPhilipp Zabel struct ipu_platform_reg *reg = &client_reg[i]; 122039b9004dSPhilipp Zabel struct platform_device *pdev; 122117e05217SPhilipp Zabel struct device_node *of_node; 122217e05217SPhilipp Zabel 122317e05217SPhilipp Zabel /* Associate subdevice with the corresponding port node */ 122417e05217SPhilipp Zabel of_node = of_graph_get_port_by_id(dev->of_node, i); 122517e05217SPhilipp Zabel if (!of_node) { 122617e05217SPhilipp Zabel dev_info(dev, 12274bf99144SRob Herring "no port@%d node in %pOF, not using %s%d\n", 12284bf99144SRob Herring i, dev->of_node, 122917e05217SPhilipp Zabel (i / 2) ? "DI" : "CSI", i % 2); 123017e05217SPhilipp Zabel continue; 123117e05217SPhilipp Zabel } 123239b9004dSPhilipp Zabel 1233304e6be6SPhilipp Zabel pdev = platform_device_alloc(reg->name, id++); 1234304e6be6SPhilipp Zabel if (!pdev) { 1235304e6be6SPhilipp Zabel ret = -ENOMEM; 1236304e6be6SPhilipp Zabel goto err_register; 1237304e6be6SPhilipp Zabel } 123839b9004dSPhilipp Zabel 1239304e6be6SPhilipp Zabel pdev->dev.parent = dev; 1240304e6be6SPhilipp Zabel 1241310944d1SPhilipp Zabel reg->pdata.of_node = of_node; 1242304e6be6SPhilipp Zabel ret = platform_device_add_data(pdev, ®->pdata, 1243304e6be6SPhilipp Zabel sizeof(reg->pdata)); 1244304e6be6SPhilipp Zabel if (!ret) 1245304e6be6SPhilipp Zabel ret = platform_device_add(pdev); 1246304e6be6SPhilipp Zabel if (ret) { 1247304e6be6SPhilipp Zabel platform_device_put(pdev); 124839b9004dSPhilipp Zabel goto err_register; 124939b9004dSPhilipp Zabel } 1250e4946cdcSAxel Lin } 125139b9004dSPhilipp Zabel 125239b9004dSPhilipp Zabel return 0; 125339b9004dSPhilipp Zabel 125439b9004dSPhilipp Zabel err_register: 125539b9004dSPhilipp Zabel platform_device_unregister_children(to_platform_device(dev)); 125639b9004dSPhilipp Zabel 125739b9004dSPhilipp Zabel return ret; 125839b9004dSPhilipp Zabel } 125939b9004dSPhilipp Zabel 126039b9004dSPhilipp Zabel 126139b9004dSPhilipp Zabel static int ipu_irq_init(struct ipu_soc *ipu) 126239b9004dSPhilipp Zabel { 126339b9004dSPhilipp Zabel struct irq_chip_generic *gc; 126439b9004dSPhilipp Zabel struct irq_chip_type *ct; 126539b9004dSPhilipp Zabel unsigned long unused[IPU_NUM_IRQS / 32] = { 126639b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 126739b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 126839b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 126939b9004dSPhilipp Zabel 0x4077ffff, 0xffe7e1fd, 127039b9004dSPhilipp Zabel 0x23fffffe, 0x8880fff0, 127139b9004dSPhilipp Zabel 0xf98fe7d0, 0xfff81fff, 127239b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 127339b9004dSPhilipp Zabel 0x00000000, 127439b9004dSPhilipp Zabel }; 127539b9004dSPhilipp Zabel int ret, i; 127639b9004dSPhilipp Zabel 127739b9004dSPhilipp Zabel ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS, 127839b9004dSPhilipp Zabel &irq_generic_chip_ops, ipu); 127939b9004dSPhilipp Zabel if (!ipu->domain) { 128039b9004dSPhilipp Zabel dev_err(ipu->dev, "failed to add irq domain\n"); 128139b9004dSPhilipp Zabel return -ENODEV; 128239b9004dSPhilipp Zabel } 128339b9004dSPhilipp Zabel 128439b9004dSPhilipp Zabel ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU", 1285ca0141deSRob Herring handle_level_irq, 0, 0, 0); 128639b9004dSPhilipp Zabel if (ret < 0) { 128739b9004dSPhilipp Zabel dev_err(ipu->dev, "failed to alloc generic irq chips\n"); 128839b9004dSPhilipp Zabel irq_domain_remove(ipu->domain); 128939b9004dSPhilipp Zabel return ret; 129039b9004dSPhilipp Zabel } 129139b9004dSPhilipp Zabel 1292a92d8145SPhilipp Zabel /* Mask and clear all interrupts */ 1293a92d8145SPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i += 32) { 1294510e6426SRussell King ipu_cm_write(ipu, 0, IPU_INT_CTRL(i / 32)); 1295a92d8145SPhilipp Zabel ipu_cm_write(ipu, ~unused[i / 32], IPU_INT_STAT(i / 32)); 1296a92d8145SPhilipp Zabel } 1297510e6426SRussell King 129839b9004dSPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i += 32) { 129939b9004dSPhilipp Zabel gc = irq_get_domain_generic_chip(ipu->domain, i); 130039b9004dSPhilipp Zabel gc->reg_base = ipu->cm_reg; 130139b9004dSPhilipp Zabel gc->unused = unused[i / 32]; 130239b9004dSPhilipp Zabel ct = gc->chip_types; 130339b9004dSPhilipp Zabel ct->chip.irq_ack = irq_gc_ack_set_bit; 130439b9004dSPhilipp Zabel ct->chip.irq_mask = irq_gc_mask_clr_bit; 130539b9004dSPhilipp Zabel ct->chip.irq_unmask = irq_gc_mask_set_bit; 130639b9004dSPhilipp Zabel ct->regs.ack = IPU_INT_STAT(i / 32); 130739b9004dSPhilipp Zabel ct->regs.mask = IPU_INT_CTRL(i / 32); 130839b9004dSPhilipp Zabel } 130939b9004dSPhilipp Zabel 131086f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu); 131186f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler, 131286f5e733SRussell King ipu); 131339b9004dSPhilipp Zabel 131439b9004dSPhilipp Zabel return 0; 131539b9004dSPhilipp Zabel } 131639b9004dSPhilipp Zabel 131739b9004dSPhilipp Zabel static void ipu_irq_exit(struct ipu_soc *ipu) 131839b9004dSPhilipp Zabel { 131939b9004dSPhilipp Zabel int i, irq; 132039b9004dSPhilipp Zabel 132186f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL); 132286f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL); 132339b9004dSPhilipp Zabel 132439b9004dSPhilipp Zabel /* TODO: remove irq_domain_generic_chips */ 132539b9004dSPhilipp Zabel 132639b9004dSPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i++) { 132739b9004dSPhilipp Zabel irq = irq_linear_revmap(ipu->domain, i); 132839b9004dSPhilipp Zabel if (irq) 132939b9004dSPhilipp Zabel irq_dispose_mapping(irq); 133039b9004dSPhilipp Zabel } 133139b9004dSPhilipp Zabel 133239b9004dSPhilipp Zabel irq_domain_remove(ipu->domain); 133339b9004dSPhilipp Zabel } 133439b9004dSPhilipp Zabel 13353feb049fSSteve Longerbeam void ipu_dump(struct ipu_soc *ipu) 13363feb049fSSteve Longerbeam { 13373feb049fSSteve Longerbeam int i; 13383feb049fSSteve Longerbeam 13393feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n", 13403feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_CONF)); 13413feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n", 13423feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CONF)); 13433feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n", 13443feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_EN(0))); 13453feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n", 13463feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_EN(32))); 13473feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n", 13483feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_PRI(0))); 13493feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n", 13503feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_PRI(32))); 13513feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n", 13523feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_BAND_EN(0))); 13533feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n", 13543feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_BAND_EN(32))); 13553feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n", 13563feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0))); 13573feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n", 13583feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32))); 13593feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n", 13603feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_PROC_FLOW1)); 13613feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n", 13623feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_PROC_FLOW2)); 13633feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n", 13643feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_PROC_FLOW3)); 13653feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n", 13663feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_DISP_FLOW1)); 13673feb049fSSteve Longerbeam for (i = 0; i < 15; i++) 13683feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i, 13693feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_INT_CTRL(i))); 13703feb049fSSteve Longerbeam } 13713feb049fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_dump); 13723feb049fSSteve Longerbeam 137339b9004dSPhilipp Zabel static int ipu_probe(struct platform_device *pdev) 137439b9004dSPhilipp Zabel { 1375572a7615SSteve Longerbeam struct device_node *np = pdev->dev.of_node; 137639b9004dSPhilipp Zabel struct ipu_soc *ipu; 137739b9004dSPhilipp Zabel struct resource *res; 137839b9004dSPhilipp Zabel unsigned long ipu_base; 137993adc8b5SPhilipp Zabel int ret, irq_sync, irq_err; 138039b9004dSPhilipp Zabel const struct ipu_devtype *devtype; 138139b9004dSPhilipp Zabel 1382e92e4478SLABBE Corentin devtype = of_device_get_match_data(&pdev->dev); 1383e92e4478SLABBE Corentin if (!devtype) 1384e92e4478SLABBE Corentin return -EINVAL; 138539b9004dSPhilipp Zabel 138639b9004dSPhilipp Zabel irq_sync = platform_get_irq(pdev, 0); 138739b9004dSPhilipp Zabel irq_err = platform_get_irq(pdev, 1); 138839b9004dSPhilipp Zabel res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 138939b9004dSPhilipp Zabel 139039b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n", 139139b9004dSPhilipp Zabel irq_sync, irq_err); 139239b9004dSPhilipp Zabel 139339b9004dSPhilipp Zabel if (!res || irq_sync < 0 || irq_err < 0) 139439b9004dSPhilipp Zabel return -ENODEV; 139539b9004dSPhilipp Zabel 139639b9004dSPhilipp Zabel ipu_base = res->start; 139739b9004dSPhilipp Zabel 139839b9004dSPhilipp Zabel ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL); 139939b9004dSPhilipp Zabel if (!ipu) 140039b9004dSPhilipp Zabel return -ENODEV; 140139b9004dSPhilipp Zabel 140292681fe7SLucas Stach ipu->id = of_alias_get_id(np, "ipu"); 14032d87e6c1SPhilipp Zabel if (ipu->id < 0) 14042d87e6c1SPhilipp Zabel ipu->id = 0; 140592681fe7SLucas Stach 140630310c83SLucas Stach if (of_device_is_compatible(np, "fsl,imx6qp-ipu") && 140730310c83SLucas Stach IS_ENABLED(CONFIG_DRM)) { 140892681fe7SLucas Stach ipu->prg_priv = ipu_prg_lookup_by_phandle(&pdev->dev, 140992681fe7SLucas Stach "fsl,prg", ipu->id); 141092681fe7SLucas Stach if (!ipu->prg_priv) 141192681fe7SLucas Stach return -EPROBE_DEFER; 141292681fe7SLucas Stach } 141392681fe7SLucas Stach 141439b9004dSPhilipp Zabel ipu->devtype = devtype; 141539b9004dSPhilipp Zabel ipu->ipu_type = devtype->type; 141639b9004dSPhilipp Zabel 141739b9004dSPhilipp Zabel spin_lock_init(&ipu->lock); 141839b9004dSPhilipp Zabel mutex_init(&ipu->channel_lock); 141993adc8b5SPhilipp Zabel INIT_LIST_HEAD(&ipu->channels); 142039b9004dSPhilipp Zabel 142139b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n", 142239b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs); 142339b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "idmac: 0x%08lx\n", 142439b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS); 142539b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n", 142639b9004dSPhilipp Zabel ipu_base + devtype->cpmem_ofs); 14272ffd48f2SSteve Longerbeam dev_dbg(&pdev->dev, "csi0: 0x%08lx\n", 14282ffd48f2SSteve Longerbeam ipu_base + devtype->csi0_ofs); 14292ffd48f2SSteve Longerbeam dev_dbg(&pdev->dev, "csi1: 0x%08lx\n", 14302ffd48f2SSteve Longerbeam ipu_base + devtype->csi1_ofs); 14311aa8ea0dSSteve Longerbeam dev_dbg(&pdev->dev, "ic: 0x%08lx\n", 14321aa8ea0dSSteve Longerbeam ipu_base + devtype->ic_ofs); 143339b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "disp0: 0x%08lx\n", 143439b9004dSPhilipp Zabel ipu_base + devtype->disp0_ofs); 143539b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "disp1: 0x%08lx\n", 143639b9004dSPhilipp Zabel ipu_base + devtype->disp1_ofs); 143739b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "srm: 0x%08lx\n", 143839b9004dSPhilipp Zabel ipu_base + devtype->srm_ofs); 143939b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "tpm: 0x%08lx\n", 144039b9004dSPhilipp Zabel ipu_base + devtype->tpm_ofs); 144139b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "dc: 0x%08lx\n", 144239b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS); 144339b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "ic: 0x%08lx\n", 144439b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS); 144539b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n", 144639b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS); 144739b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "vdi: 0x%08lx\n", 144839b9004dSPhilipp Zabel ipu_base + devtype->vdi_ofs); 144939b9004dSPhilipp Zabel 145039b9004dSPhilipp Zabel ipu->cm_reg = devm_ioremap(&pdev->dev, 145139b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs, PAGE_SIZE); 145239b9004dSPhilipp Zabel ipu->idmac_reg = devm_ioremap(&pdev->dev, 145339b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS, 145439b9004dSPhilipp Zabel PAGE_SIZE); 145539b9004dSPhilipp Zabel 14567d2691daSSteve Longerbeam if (!ipu->cm_reg || !ipu->idmac_reg) 145739b9004dSPhilipp Zabel return -ENOMEM; 145839b9004dSPhilipp Zabel 145939b9004dSPhilipp Zabel ipu->clk = devm_clk_get(&pdev->dev, "bus"); 146039b9004dSPhilipp Zabel if (IS_ERR(ipu->clk)) { 146139b9004dSPhilipp Zabel ret = PTR_ERR(ipu->clk); 146239b9004dSPhilipp Zabel dev_err(&pdev->dev, "clk_get failed with %d", ret); 146339b9004dSPhilipp Zabel return ret; 146439b9004dSPhilipp Zabel } 146539b9004dSPhilipp Zabel 146639b9004dSPhilipp Zabel platform_set_drvdata(pdev, ipu); 146739b9004dSPhilipp Zabel 146839b9004dSPhilipp Zabel ret = clk_prepare_enable(ipu->clk); 146939b9004dSPhilipp Zabel if (ret) { 147039b9004dSPhilipp Zabel dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); 147139b9004dSPhilipp Zabel return ret; 147239b9004dSPhilipp Zabel } 147339b9004dSPhilipp Zabel 147439b9004dSPhilipp Zabel ipu->dev = &pdev->dev; 147539b9004dSPhilipp Zabel ipu->irq_sync = irq_sync; 147639b9004dSPhilipp Zabel ipu->irq_err = irq_err; 147739b9004dSPhilipp Zabel 147839b9004dSPhilipp Zabel ret = device_reset(&pdev->dev); 147939b9004dSPhilipp Zabel if (ret) { 148039b9004dSPhilipp Zabel dev_err(&pdev->dev, "failed to reset: %d\n", ret); 148139b9004dSPhilipp Zabel goto out_failed_reset; 148239b9004dSPhilipp Zabel } 148339b9004dSPhilipp Zabel ret = ipu_memory_reset(ipu); 148439b9004dSPhilipp Zabel if (ret) 148539b9004dSPhilipp Zabel goto out_failed_reset; 148639b9004dSPhilipp Zabel 1487596a65d1SDavid Jander ret = ipu_irq_init(ipu); 1488596a65d1SDavid Jander if (ret) 1489596a65d1SDavid Jander goto out_failed_irq; 1490596a65d1SDavid Jander 149139b9004dSPhilipp Zabel /* Set MCU_T to divide MCU access window into 2 */ 149239b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), 149339b9004dSPhilipp Zabel IPU_DISP_GEN); 149439b9004dSPhilipp Zabel 149539b9004dSPhilipp Zabel ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk); 149639b9004dSPhilipp Zabel if (ret) 149739b9004dSPhilipp Zabel goto failed_submodules_init; 149839b9004dSPhilipp Zabel 1499d6ca8ca7SPhilipp Zabel ret = ipu_add_client_devices(ipu, ipu_base); 150039b9004dSPhilipp Zabel if (ret) { 150139b9004dSPhilipp Zabel dev_err(&pdev->dev, "adding client devices failed with %d\n", 150239b9004dSPhilipp Zabel ret); 150339b9004dSPhilipp Zabel goto failed_add_clients; 150439b9004dSPhilipp Zabel } 150539b9004dSPhilipp Zabel 150639b9004dSPhilipp Zabel dev_info(&pdev->dev, "%s probed\n", devtype->name); 150739b9004dSPhilipp Zabel 150839b9004dSPhilipp Zabel return 0; 150939b9004dSPhilipp Zabel 151039b9004dSPhilipp Zabel failed_add_clients: 151139b9004dSPhilipp Zabel ipu_submodules_exit(ipu); 151239b9004dSPhilipp Zabel failed_submodules_init: 151339b9004dSPhilipp Zabel ipu_irq_exit(ipu); 151439b9004dSPhilipp Zabel out_failed_irq: 1515596a65d1SDavid Jander out_failed_reset: 151639b9004dSPhilipp Zabel clk_disable_unprepare(ipu->clk); 151739b9004dSPhilipp Zabel return ret; 151839b9004dSPhilipp Zabel } 151939b9004dSPhilipp Zabel 152039b9004dSPhilipp Zabel static int ipu_remove(struct platform_device *pdev) 152139b9004dSPhilipp Zabel { 152239b9004dSPhilipp Zabel struct ipu_soc *ipu = platform_get_drvdata(pdev); 152339b9004dSPhilipp Zabel 152439b9004dSPhilipp Zabel platform_device_unregister_children(pdev); 152539b9004dSPhilipp Zabel ipu_submodules_exit(ipu); 152639b9004dSPhilipp Zabel ipu_irq_exit(ipu); 152739b9004dSPhilipp Zabel 152839b9004dSPhilipp Zabel clk_disable_unprepare(ipu->clk); 152939b9004dSPhilipp Zabel 153039b9004dSPhilipp Zabel return 0; 153139b9004dSPhilipp Zabel } 153239b9004dSPhilipp Zabel 153339b9004dSPhilipp Zabel static struct platform_driver imx_ipu_driver = { 153439b9004dSPhilipp Zabel .driver = { 153539b9004dSPhilipp Zabel .name = "imx-ipuv3", 153639b9004dSPhilipp Zabel .of_match_table = imx_ipu_dt_ids, 153739b9004dSPhilipp Zabel }, 153839b9004dSPhilipp Zabel .probe = ipu_probe, 153939b9004dSPhilipp Zabel .remove = ipu_remove, 154039b9004dSPhilipp Zabel }; 154139b9004dSPhilipp Zabel 1542d2a34232SLucas Stach static struct platform_driver * const drivers[] = { 154330310c83SLucas Stach #if IS_ENABLED(CONFIG_DRM) 1544d2a34232SLucas Stach &ipu_pre_drv, 1545ea9c2605SLucas Stach &ipu_prg_drv, 154630310c83SLucas Stach #endif 1547d2a34232SLucas Stach &imx_ipu_driver, 1548d2a34232SLucas Stach }; 1549d2a34232SLucas Stach 1550d2a34232SLucas Stach static int __init imx_ipu_init(void) 1551d2a34232SLucas Stach { 1552d2a34232SLucas Stach return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); 1553d2a34232SLucas Stach } 1554d2a34232SLucas Stach module_init(imx_ipu_init); 1555d2a34232SLucas Stach 1556d2a34232SLucas Stach static void __exit imx_ipu_exit(void) 1557d2a34232SLucas Stach { 1558d2a34232SLucas Stach platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); 1559d2a34232SLucas Stach } 1560d2a34232SLucas Stach module_exit(imx_ipu_exit); 156139b9004dSPhilipp Zabel 156239b9004dSPhilipp Zabel MODULE_ALIAS("platform:imx-ipuv3"); 156339b9004dSPhilipp Zabel MODULE_DESCRIPTION("i.MX IPU v3 driver"); 156439b9004dSPhilipp Zabel MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); 156539b9004dSPhilipp Zabel MODULE_LICENSE("GPL"); 1566