139b9004dSPhilipp Zabel /* 239b9004dSPhilipp Zabel * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> 339b9004dSPhilipp Zabel * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. 439b9004dSPhilipp Zabel * 539b9004dSPhilipp Zabel * This program is free software; you can redistribute it and/or modify it 639b9004dSPhilipp Zabel * under the terms of the GNU General Public License as published by the 739b9004dSPhilipp Zabel * Free Software Foundation; either version 2 of the License, or (at your 839b9004dSPhilipp Zabel * option) any later version. 939b9004dSPhilipp Zabel * 1039b9004dSPhilipp Zabel * This program is distributed in the hope that it will be useful, but 1139b9004dSPhilipp Zabel * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 1239b9004dSPhilipp Zabel * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1339b9004dSPhilipp Zabel * for more details. 1439b9004dSPhilipp Zabel */ 1539b9004dSPhilipp Zabel #include <linux/module.h> 1639b9004dSPhilipp Zabel #include <linux/export.h> 1739b9004dSPhilipp Zabel #include <linux/types.h> 1839b9004dSPhilipp Zabel #include <linux/reset.h> 1939b9004dSPhilipp Zabel #include <linux/platform_device.h> 2039b9004dSPhilipp Zabel #include <linux/err.h> 2139b9004dSPhilipp Zabel #include <linux/spinlock.h> 2239b9004dSPhilipp Zabel #include <linux/delay.h> 2339b9004dSPhilipp Zabel #include <linux/interrupt.h> 2439b9004dSPhilipp Zabel #include <linux/io.h> 2539b9004dSPhilipp Zabel #include <linux/clk.h> 2639b9004dSPhilipp Zabel #include <linux/list.h> 2739b9004dSPhilipp Zabel #include <linux/irq.h> 2839b9004dSPhilipp Zabel #include <linux/irqchip/chained_irq.h> 2939b9004dSPhilipp Zabel #include <linux/irqdomain.h> 3039b9004dSPhilipp Zabel #include <linux/of_device.h> 31304e6be6SPhilipp Zabel #include <linux/of_graph.h> 3239b9004dSPhilipp Zabel 3339b9004dSPhilipp Zabel #include <drm/drm_fourcc.h> 3439b9004dSPhilipp Zabel 3539b9004dSPhilipp Zabel #include <video/imx-ipu-v3.h> 3639b9004dSPhilipp Zabel #include "ipu-prv.h" 3739b9004dSPhilipp Zabel 3839b9004dSPhilipp Zabel static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset) 3939b9004dSPhilipp Zabel { 4039b9004dSPhilipp Zabel return readl(ipu->cm_reg + offset); 4139b9004dSPhilipp Zabel } 4239b9004dSPhilipp Zabel 4339b9004dSPhilipp Zabel static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset) 4439b9004dSPhilipp Zabel { 4539b9004dSPhilipp Zabel writel(value, ipu->cm_reg + offset); 4639b9004dSPhilipp Zabel } 4739b9004dSPhilipp Zabel 48572a7615SSteve Longerbeam int ipu_get_num(struct ipu_soc *ipu) 49572a7615SSteve Longerbeam { 50572a7615SSteve Longerbeam return ipu->id; 51572a7615SSteve Longerbeam } 52572a7615SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_get_num); 53572a7615SSteve Longerbeam 54f9bb7acbSPhilipp Zabel void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync) 5539b9004dSPhilipp Zabel { 5639b9004dSPhilipp Zabel u32 val; 5739b9004dSPhilipp Zabel 5839b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_SRM_PRI2); 59f9bb7acbSPhilipp Zabel val &= ~DP_S_SRM_MODE_MASK; 60f9bb7acbSPhilipp Zabel val |= sync ? DP_S_SRM_MODE_NEXT_FRAME : 61f9bb7acbSPhilipp Zabel DP_S_SRM_MODE_NOW; 6239b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_SRM_PRI2); 6339b9004dSPhilipp Zabel } 64f9bb7acbSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_srm_dp_update); 6539b9004dSPhilipp Zabel 6639b9004dSPhilipp Zabel enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc) 6739b9004dSPhilipp Zabel { 6839b9004dSPhilipp Zabel switch (drm_fourcc) { 690cb8b757SPhilipp Zabel case DRM_FORMAT_ARGB1555: 700cb8b757SPhilipp Zabel case DRM_FORMAT_ABGR1555: 710cb8b757SPhilipp Zabel case DRM_FORMAT_RGBA5551: 720cb8b757SPhilipp Zabel case DRM_FORMAT_BGRA5551: 7339b9004dSPhilipp Zabel case DRM_FORMAT_RGB565: 7439b9004dSPhilipp Zabel case DRM_FORMAT_BGR565: 7539b9004dSPhilipp Zabel case DRM_FORMAT_RGB888: 7639b9004dSPhilipp Zabel case DRM_FORMAT_BGR888: 777d2e8a20SLucas Stach case DRM_FORMAT_ARGB4444: 7839b9004dSPhilipp Zabel case DRM_FORMAT_XRGB8888: 7939b9004dSPhilipp Zabel case DRM_FORMAT_XBGR8888: 8039b9004dSPhilipp Zabel case DRM_FORMAT_RGBX8888: 8139b9004dSPhilipp Zabel case DRM_FORMAT_BGRX8888: 8239b9004dSPhilipp Zabel case DRM_FORMAT_ARGB8888: 8339b9004dSPhilipp Zabel case DRM_FORMAT_ABGR8888: 8439b9004dSPhilipp Zabel case DRM_FORMAT_RGBA8888: 8539b9004dSPhilipp Zabel case DRM_FORMAT_BGRA8888: 86e72db3b1SPhilipp Zabel case DRM_FORMAT_RGB565_A8: 87e72db3b1SPhilipp Zabel case DRM_FORMAT_BGR565_A8: 88e72db3b1SPhilipp Zabel case DRM_FORMAT_RGB888_A8: 89e72db3b1SPhilipp Zabel case DRM_FORMAT_BGR888_A8: 90e72db3b1SPhilipp Zabel case DRM_FORMAT_RGBX8888_A8: 91e72db3b1SPhilipp Zabel case DRM_FORMAT_BGRX8888_A8: 9239b9004dSPhilipp Zabel return IPUV3_COLORSPACE_RGB; 9339b9004dSPhilipp Zabel case DRM_FORMAT_YUYV: 9439b9004dSPhilipp Zabel case DRM_FORMAT_UYVY: 9539b9004dSPhilipp Zabel case DRM_FORMAT_YUV420: 9639b9004dSPhilipp Zabel case DRM_FORMAT_YVU420: 979a34cef0SSteve Longerbeam case DRM_FORMAT_YUV422: 989a34cef0SSteve Longerbeam case DRM_FORMAT_YVU422: 99c9d508c2SPhilipp Zabel case DRM_FORMAT_YUV444: 100c9d508c2SPhilipp Zabel case DRM_FORMAT_YVU444: 1019a34cef0SSteve Longerbeam case DRM_FORMAT_NV12: 1029a34cef0SSteve Longerbeam case DRM_FORMAT_NV21: 1039a34cef0SSteve Longerbeam case DRM_FORMAT_NV16: 1049a34cef0SSteve Longerbeam case DRM_FORMAT_NV61: 10539b9004dSPhilipp Zabel return IPUV3_COLORSPACE_YUV; 10639b9004dSPhilipp Zabel default: 10739b9004dSPhilipp Zabel return IPUV3_COLORSPACE_UNKNOWN; 10839b9004dSPhilipp Zabel } 10939b9004dSPhilipp Zabel } 11039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace); 11139b9004dSPhilipp Zabel 11239b9004dSPhilipp Zabel enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat) 11339b9004dSPhilipp Zabel { 11439b9004dSPhilipp Zabel switch (pixelformat) { 11539b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUV420: 11639b9004dSPhilipp Zabel case V4L2_PIX_FMT_YVU420: 1179a34cef0SSteve Longerbeam case V4L2_PIX_FMT_YUV422P: 11839b9004dSPhilipp Zabel case V4L2_PIX_FMT_UYVY: 11939b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUYV: 1209a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV12: 1219a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV21: 1229a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV16: 1239a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV61: 12439b9004dSPhilipp Zabel return IPUV3_COLORSPACE_YUV; 1255c41bb60SPhilipp Zabel case V4L2_PIX_FMT_XRGB32: 1265c41bb60SPhilipp Zabel case V4L2_PIX_FMT_XBGR32: 12739b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB32: 12839b9004dSPhilipp Zabel case V4L2_PIX_FMT_BGR32: 12939b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB24: 13039b9004dSPhilipp Zabel case V4L2_PIX_FMT_BGR24: 13139b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB565: 13239b9004dSPhilipp Zabel return IPUV3_COLORSPACE_RGB; 13339b9004dSPhilipp Zabel default: 13439b9004dSPhilipp Zabel return IPUV3_COLORSPACE_UNKNOWN; 13539b9004dSPhilipp Zabel } 13639b9004dSPhilipp Zabel } 13739b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace); 13839b9004dSPhilipp Zabel 1394cea940dSSteve Longerbeam bool ipu_pixelformat_is_planar(u32 pixelformat) 1404cea940dSSteve Longerbeam { 1414cea940dSSteve Longerbeam switch (pixelformat) { 1424cea940dSSteve Longerbeam case V4L2_PIX_FMT_YUV420: 1434cea940dSSteve Longerbeam case V4L2_PIX_FMT_YVU420: 1449a34cef0SSteve Longerbeam case V4L2_PIX_FMT_YUV422P: 1459a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV12: 1469a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV21: 1479a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV16: 1489a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV61: 1494cea940dSSteve Longerbeam return true; 1504cea940dSSteve Longerbeam } 1514cea940dSSteve Longerbeam 1524cea940dSSteve Longerbeam return false; 1534cea940dSSteve Longerbeam } 1544cea940dSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar); 1554cea940dSSteve Longerbeam 156ae0e9708SSteve Longerbeam enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code) 157ae0e9708SSteve Longerbeam { 158ae0e9708SSteve Longerbeam switch (mbus_code & 0xf000) { 159ae0e9708SSteve Longerbeam case 0x1000: 160ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_RGB; 161ae0e9708SSteve Longerbeam case 0x2000: 162ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_YUV; 163ae0e9708SSteve Longerbeam default: 164ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_UNKNOWN; 165ae0e9708SSteve Longerbeam } 166ae0e9708SSteve Longerbeam } 167ae0e9708SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace); 168ae0e9708SSteve Longerbeam 1696930afdcSSteve Longerbeam int ipu_stride_to_bytes(u32 pixel_stride, u32 pixelformat) 1706930afdcSSteve Longerbeam { 1716930afdcSSteve Longerbeam switch (pixelformat) { 1726930afdcSSteve Longerbeam case V4L2_PIX_FMT_YUV420: 1736930afdcSSteve Longerbeam case V4L2_PIX_FMT_YVU420: 1749a34cef0SSteve Longerbeam case V4L2_PIX_FMT_YUV422P: 1759a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV12: 1769a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV21: 1779a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV16: 1789a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV61: 1796930afdcSSteve Longerbeam /* 1806930afdcSSteve Longerbeam * for the planar YUV formats, the stride passed to 1816930afdcSSteve Longerbeam * cpmem must be the stride in bytes of the Y plane. 1826930afdcSSteve Longerbeam * And all the planar YUV formats have an 8-bit 1836930afdcSSteve Longerbeam * Y component. 1846930afdcSSteve Longerbeam */ 1856930afdcSSteve Longerbeam return (8 * pixel_stride) >> 3; 1866930afdcSSteve Longerbeam case V4L2_PIX_FMT_RGB565: 1876930afdcSSteve Longerbeam case V4L2_PIX_FMT_YUYV: 1886930afdcSSteve Longerbeam case V4L2_PIX_FMT_UYVY: 1896930afdcSSteve Longerbeam return (16 * pixel_stride) >> 3; 1906930afdcSSteve Longerbeam case V4L2_PIX_FMT_BGR24: 1916930afdcSSteve Longerbeam case V4L2_PIX_FMT_RGB24: 1926930afdcSSteve Longerbeam return (24 * pixel_stride) >> 3; 1936930afdcSSteve Longerbeam case V4L2_PIX_FMT_BGR32: 1946930afdcSSteve Longerbeam case V4L2_PIX_FMT_RGB32: 1955c41bb60SPhilipp Zabel case V4L2_PIX_FMT_XBGR32: 1965c41bb60SPhilipp Zabel case V4L2_PIX_FMT_XRGB32: 1976930afdcSSteve Longerbeam return (32 * pixel_stride) >> 3; 1986930afdcSSteve Longerbeam default: 1996930afdcSSteve Longerbeam break; 2006930afdcSSteve Longerbeam } 2016930afdcSSteve Longerbeam 2026930afdcSSteve Longerbeam return -EINVAL; 2036930afdcSSteve Longerbeam } 2046930afdcSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_stride_to_bytes); 2056930afdcSSteve Longerbeam 206f835f386SSteve Longerbeam int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees, 207f835f386SSteve Longerbeam bool hflip, bool vflip) 208f835f386SSteve Longerbeam { 209f835f386SSteve Longerbeam u32 r90, vf, hf; 210f835f386SSteve Longerbeam 211f835f386SSteve Longerbeam switch (degrees) { 212f835f386SSteve Longerbeam case 0: 213f835f386SSteve Longerbeam vf = hf = r90 = 0; 214f835f386SSteve Longerbeam break; 215f835f386SSteve Longerbeam case 90: 216f835f386SSteve Longerbeam vf = hf = 0; 217f835f386SSteve Longerbeam r90 = 1; 218f835f386SSteve Longerbeam break; 219f835f386SSteve Longerbeam case 180: 220f835f386SSteve Longerbeam vf = hf = 1; 221f835f386SSteve Longerbeam r90 = 0; 222f835f386SSteve Longerbeam break; 223f835f386SSteve Longerbeam case 270: 224f835f386SSteve Longerbeam vf = hf = r90 = 1; 225f835f386SSteve Longerbeam break; 226f835f386SSteve Longerbeam default: 227f835f386SSteve Longerbeam return -EINVAL; 228f835f386SSteve Longerbeam } 229f835f386SSteve Longerbeam 230f835f386SSteve Longerbeam hf ^= (u32)hflip; 231f835f386SSteve Longerbeam vf ^= (u32)vflip; 232f835f386SSteve Longerbeam 233f835f386SSteve Longerbeam *mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf); 234f835f386SSteve Longerbeam return 0; 235f835f386SSteve Longerbeam } 236f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode); 237f835f386SSteve Longerbeam 238f835f386SSteve Longerbeam int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode, 239f835f386SSteve Longerbeam bool hflip, bool vflip) 240f835f386SSteve Longerbeam { 241f835f386SSteve Longerbeam u32 r90, vf, hf; 242f835f386SSteve Longerbeam 243f835f386SSteve Longerbeam r90 = ((u32)mode >> 2) & 0x1; 244f835f386SSteve Longerbeam hf = ((u32)mode >> 1) & 0x1; 245f835f386SSteve Longerbeam vf = ((u32)mode >> 0) & 0x1; 246f835f386SSteve Longerbeam hf ^= (u32)hflip; 247f835f386SSteve Longerbeam vf ^= (u32)vflip; 248f835f386SSteve Longerbeam 249f835f386SSteve Longerbeam switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) { 250f835f386SSteve Longerbeam case IPU_ROTATE_NONE: 251f835f386SSteve Longerbeam *degrees = 0; 252f835f386SSteve Longerbeam break; 253f835f386SSteve Longerbeam case IPU_ROTATE_90_RIGHT: 254f835f386SSteve Longerbeam *degrees = 90; 255f835f386SSteve Longerbeam break; 256f835f386SSteve Longerbeam case IPU_ROTATE_180: 257f835f386SSteve Longerbeam *degrees = 180; 258f835f386SSteve Longerbeam break; 259f835f386SSteve Longerbeam case IPU_ROTATE_90_LEFT: 260f835f386SSteve Longerbeam *degrees = 270; 261f835f386SSteve Longerbeam break; 262f835f386SSteve Longerbeam default: 263f835f386SSteve Longerbeam return -EINVAL; 264f835f386SSteve Longerbeam } 265f835f386SSteve Longerbeam 266f835f386SSteve Longerbeam return 0; 267f835f386SSteve Longerbeam } 268f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees); 269f835f386SSteve Longerbeam 27039b9004dSPhilipp Zabel struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num) 27139b9004dSPhilipp Zabel { 27239b9004dSPhilipp Zabel struct ipuv3_channel *channel; 27339b9004dSPhilipp Zabel 27439b9004dSPhilipp Zabel dev_dbg(ipu->dev, "%s %d\n", __func__, num); 27539b9004dSPhilipp Zabel 27639b9004dSPhilipp Zabel if (num > 63) 27739b9004dSPhilipp Zabel return ERR_PTR(-ENODEV); 27839b9004dSPhilipp Zabel 27939b9004dSPhilipp Zabel mutex_lock(&ipu->channel_lock); 28039b9004dSPhilipp Zabel 28193adc8b5SPhilipp Zabel list_for_each_entry(channel, &ipu->channels, list) { 28293adc8b5SPhilipp Zabel if (channel->num == num) { 28339b9004dSPhilipp Zabel channel = ERR_PTR(-EBUSY); 28439b9004dSPhilipp Zabel goto out; 28539b9004dSPhilipp Zabel } 28693adc8b5SPhilipp Zabel } 28739b9004dSPhilipp Zabel 28893adc8b5SPhilipp Zabel channel = kzalloc(sizeof(*channel), GFP_KERNEL); 28993adc8b5SPhilipp Zabel if (!channel) { 29093adc8b5SPhilipp Zabel channel = ERR_PTR(-ENOMEM); 29193adc8b5SPhilipp Zabel goto out; 29293adc8b5SPhilipp Zabel } 29393adc8b5SPhilipp Zabel 29439b9004dSPhilipp Zabel channel->num = num; 29593adc8b5SPhilipp Zabel channel->ipu = ipu; 29693adc8b5SPhilipp Zabel list_add(&channel->list, &ipu->channels); 29739b9004dSPhilipp Zabel 29839b9004dSPhilipp Zabel out: 29939b9004dSPhilipp Zabel mutex_unlock(&ipu->channel_lock); 30039b9004dSPhilipp Zabel 30139b9004dSPhilipp Zabel return channel; 30239b9004dSPhilipp Zabel } 30339b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get); 30439b9004dSPhilipp Zabel 30539b9004dSPhilipp Zabel void ipu_idmac_put(struct ipuv3_channel *channel) 30639b9004dSPhilipp Zabel { 30739b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 30839b9004dSPhilipp Zabel 30939b9004dSPhilipp Zabel dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num); 31039b9004dSPhilipp Zabel 31139b9004dSPhilipp Zabel mutex_lock(&ipu->channel_lock); 31239b9004dSPhilipp Zabel 31393adc8b5SPhilipp Zabel list_del(&channel->list); 31493adc8b5SPhilipp Zabel kfree(channel); 31539b9004dSPhilipp Zabel 31639b9004dSPhilipp Zabel mutex_unlock(&ipu->channel_lock); 31739b9004dSPhilipp Zabel } 31839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_put); 31939b9004dSPhilipp Zabel 320aa52f578SSteve Longerbeam #define idma_mask(ch) (1 << ((ch) & 0x1f)) 32139b9004dSPhilipp Zabel 322e7268c69SSteve Longerbeam /* 323e7268c69SSteve Longerbeam * This is an undocumented feature, a write one to a channel bit in 324e7268c69SSteve Longerbeam * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's 325e7268c69SSteve Longerbeam * internal current buffer pointer so that transfers start from buffer 326e7268c69SSteve Longerbeam * 0 on the next channel enable (that's the theory anyway, the imx6 TRM 327e7268c69SSteve Longerbeam * only says these are read-only registers). This operation is required 328e7268c69SSteve Longerbeam * for channel linking to work correctly, for instance video capture 329e7268c69SSteve Longerbeam * pipelines that carry out image rotations will fail after the first 330e7268c69SSteve Longerbeam * streaming unless this function is called for each channel before 331e7268c69SSteve Longerbeam * re-enabling the channels. 332e7268c69SSteve Longerbeam */ 333e7268c69SSteve Longerbeam static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel) 334e7268c69SSteve Longerbeam { 335e7268c69SSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 336e7268c69SSteve Longerbeam unsigned int chno = channel->num; 337e7268c69SSteve Longerbeam 338e7268c69SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno)); 339e7268c69SSteve Longerbeam } 340e7268c69SSteve Longerbeam 34139b9004dSPhilipp Zabel void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel, 34239b9004dSPhilipp Zabel bool doublebuffer) 34339b9004dSPhilipp Zabel { 34439b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 34539b9004dSPhilipp Zabel unsigned long flags; 34639b9004dSPhilipp Zabel u32 reg; 34739b9004dSPhilipp Zabel 34839b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 34939b9004dSPhilipp Zabel 35039b9004dSPhilipp Zabel reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); 35139b9004dSPhilipp Zabel if (doublebuffer) 35239b9004dSPhilipp Zabel reg |= idma_mask(channel->num); 35339b9004dSPhilipp Zabel else 35439b9004dSPhilipp Zabel reg &= ~idma_mask(channel->num); 35539b9004dSPhilipp Zabel ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num)); 35639b9004dSPhilipp Zabel 357e7268c69SSteve Longerbeam __ipu_idmac_reset_current_buffer(channel); 358e7268c69SSteve Longerbeam 35939b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 36039b9004dSPhilipp Zabel } 36139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer); 36239b9004dSPhilipp Zabel 3634fd1a07aSSteve Longerbeam static const struct { 3644fd1a07aSSteve Longerbeam int chnum; 3654fd1a07aSSteve Longerbeam u32 reg; 3664fd1a07aSSteve Longerbeam int shift; 3674fd1a07aSSteve Longerbeam } idmac_lock_en_info[] = { 3684fd1a07aSSteve Longerbeam { .chnum = 5, .reg = IDMAC_CH_LOCK_EN_1, .shift = 0, }, 3694fd1a07aSSteve Longerbeam { .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift = 2, }, 3704fd1a07aSSteve Longerbeam { .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift = 4, }, 3714fd1a07aSSteve Longerbeam { .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift = 6, }, 3724fd1a07aSSteve Longerbeam { .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift = 8, }, 3734fd1a07aSSteve Longerbeam { .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, }, 3744fd1a07aSSteve Longerbeam { .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, }, 3754fd1a07aSSteve Longerbeam { .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, }, 3764fd1a07aSSteve Longerbeam { .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, }, 3774fd1a07aSSteve Longerbeam { .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, }, 3784fd1a07aSSteve Longerbeam { .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, }, 3794fd1a07aSSteve Longerbeam { .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift = 0, }, 3804fd1a07aSSteve Longerbeam { .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift = 2, }, 3814fd1a07aSSteve Longerbeam { .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift = 4, }, 3824fd1a07aSSteve Longerbeam { .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift = 6, }, 3834fd1a07aSSteve Longerbeam { .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift = 8, }, 3844fd1a07aSSteve Longerbeam { .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, }, 3854fd1a07aSSteve Longerbeam }; 3864fd1a07aSSteve Longerbeam 3874fd1a07aSSteve Longerbeam int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts) 3884fd1a07aSSteve Longerbeam { 3894fd1a07aSSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 3904fd1a07aSSteve Longerbeam unsigned long flags; 3914fd1a07aSSteve Longerbeam u32 bursts, regval; 3924fd1a07aSSteve Longerbeam int i; 3934fd1a07aSSteve Longerbeam 3944fd1a07aSSteve Longerbeam switch (num_bursts) { 3954fd1a07aSSteve Longerbeam case 0: 3964fd1a07aSSteve Longerbeam case 1: 3974fd1a07aSSteve Longerbeam bursts = 0x00; /* locking disabled */ 3984fd1a07aSSteve Longerbeam break; 3994fd1a07aSSteve Longerbeam case 2: 4004fd1a07aSSteve Longerbeam bursts = 0x01; 4014fd1a07aSSteve Longerbeam break; 4024fd1a07aSSteve Longerbeam case 4: 4034fd1a07aSSteve Longerbeam bursts = 0x02; 4044fd1a07aSSteve Longerbeam break; 4054fd1a07aSSteve Longerbeam case 8: 4064fd1a07aSSteve Longerbeam bursts = 0x03; 4074fd1a07aSSteve Longerbeam break; 4084fd1a07aSSteve Longerbeam default: 4094fd1a07aSSteve Longerbeam return -EINVAL; 4104fd1a07aSSteve Longerbeam } 4114fd1a07aSSteve Longerbeam 412cda77556SPhilipp Zabel /* 413cda77556SPhilipp Zabel * IPUv3EX / i.MX51 has a different register layout, and on IPUv3M / 414cda77556SPhilipp Zabel * i.MX53 channel arbitration locking doesn't seem to work properly. 415cda77556SPhilipp Zabel * Allow enabling the lock feature on IPUv3H / i.MX6 only. 416cda77556SPhilipp Zabel */ 417cda77556SPhilipp Zabel if (bursts && ipu->ipu_type != IPUV3H) 418cda77556SPhilipp Zabel return -EINVAL; 419cda77556SPhilipp Zabel 4204fd1a07aSSteve Longerbeam for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) { 4214fd1a07aSSteve Longerbeam if (channel->num == idmac_lock_en_info[i].chnum) 4224fd1a07aSSteve Longerbeam break; 4234fd1a07aSSteve Longerbeam } 4244fd1a07aSSteve Longerbeam if (i >= ARRAY_SIZE(idmac_lock_en_info)) 4254fd1a07aSSteve Longerbeam return -EINVAL; 4264fd1a07aSSteve Longerbeam 4274fd1a07aSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 4284fd1a07aSSteve Longerbeam 4294fd1a07aSSteve Longerbeam regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg); 4304fd1a07aSSteve Longerbeam regval &= ~(0x03 << idmac_lock_en_info[i].shift); 4314fd1a07aSSteve Longerbeam regval |= (bursts << idmac_lock_en_info[i].shift); 4324fd1a07aSSteve Longerbeam ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg); 4334fd1a07aSSteve Longerbeam 4344fd1a07aSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 4354fd1a07aSSteve Longerbeam 4364fd1a07aSSteve Longerbeam return 0; 4374fd1a07aSSteve Longerbeam } 4384fd1a07aSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable); 4394fd1a07aSSteve Longerbeam 44039b9004dSPhilipp Zabel int ipu_module_enable(struct ipu_soc *ipu, u32 mask) 44139b9004dSPhilipp Zabel { 44239b9004dSPhilipp Zabel unsigned long lock_flags; 44339b9004dSPhilipp Zabel u32 val; 44439b9004dSPhilipp Zabel 44539b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, lock_flags); 44639b9004dSPhilipp Zabel 44739b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_DISP_GEN); 44839b9004dSPhilipp Zabel 44939b9004dSPhilipp Zabel if (mask & IPU_CONF_DI0_EN) 45039b9004dSPhilipp Zabel val |= IPU_DI0_COUNTER_RELEASE; 45139b9004dSPhilipp Zabel if (mask & IPU_CONF_DI1_EN) 45239b9004dSPhilipp Zabel val |= IPU_DI1_COUNTER_RELEASE; 45339b9004dSPhilipp Zabel 45439b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_DISP_GEN); 45539b9004dSPhilipp Zabel 45639b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CONF); 45739b9004dSPhilipp Zabel val |= mask; 45839b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CONF); 45939b9004dSPhilipp Zabel 46039b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, lock_flags); 46139b9004dSPhilipp Zabel 46239b9004dSPhilipp Zabel return 0; 46339b9004dSPhilipp Zabel } 46439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_enable); 46539b9004dSPhilipp Zabel 46639b9004dSPhilipp Zabel int ipu_module_disable(struct ipu_soc *ipu, u32 mask) 46739b9004dSPhilipp Zabel { 46839b9004dSPhilipp Zabel unsigned long lock_flags; 46939b9004dSPhilipp Zabel u32 val; 47039b9004dSPhilipp Zabel 47139b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, lock_flags); 47239b9004dSPhilipp Zabel 47339b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CONF); 47439b9004dSPhilipp Zabel val &= ~mask; 47539b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CONF); 47639b9004dSPhilipp Zabel 47739b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_DISP_GEN); 47839b9004dSPhilipp Zabel 47939b9004dSPhilipp Zabel if (mask & IPU_CONF_DI0_EN) 48039b9004dSPhilipp Zabel val &= ~IPU_DI0_COUNTER_RELEASE; 48139b9004dSPhilipp Zabel if (mask & IPU_CONF_DI1_EN) 48239b9004dSPhilipp Zabel val &= ~IPU_DI1_COUNTER_RELEASE; 48339b9004dSPhilipp Zabel 48439b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_DISP_GEN); 48539b9004dSPhilipp Zabel 48639b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, lock_flags); 48739b9004dSPhilipp Zabel 48839b9004dSPhilipp Zabel return 0; 48939b9004dSPhilipp Zabel } 49039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_disable); 49139b9004dSPhilipp Zabel 492e9046097SPhilipp Zabel int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel) 493e9046097SPhilipp Zabel { 494e9046097SPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 495e9046097SPhilipp Zabel unsigned int chno = channel->num; 496e9046097SPhilipp Zabel 497e9046097SPhilipp Zabel return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0; 498e9046097SPhilipp Zabel } 499e9046097SPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer); 500e9046097SPhilipp Zabel 501aa52f578SSteve Longerbeam bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num) 502aa52f578SSteve Longerbeam { 503aa52f578SSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 504aa52f578SSteve Longerbeam unsigned long flags; 505aa52f578SSteve Longerbeam u32 reg = 0; 506aa52f578SSteve Longerbeam 507aa52f578SSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 508aa52f578SSteve Longerbeam switch (buf_num) { 509aa52f578SSteve Longerbeam case 0: 510aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)); 511aa52f578SSteve Longerbeam break; 512aa52f578SSteve Longerbeam case 1: 513aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)); 514aa52f578SSteve Longerbeam break; 515aa52f578SSteve Longerbeam case 2: 516aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num)); 517aa52f578SSteve Longerbeam break; 518aa52f578SSteve Longerbeam } 519aa52f578SSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 520aa52f578SSteve Longerbeam 521aa52f578SSteve Longerbeam return ((reg & idma_mask(channel->num)) != 0); 522aa52f578SSteve Longerbeam } 523aa52f578SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready); 524aa52f578SSteve Longerbeam 52539b9004dSPhilipp Zabel void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num) 52639b9004dSPhilipp Zabel { 52739b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 52839b9004dSPhilipp Zabel unsigned int chno = channel->num; 52939b9004dSPhilipp Zabel unsigned long flags; 53039b9004dSPhilipp Zabel 53139b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 53239b9004dSPhilipp Zabel 53339b9004dSPhilipp Zabel /* Mark buffer as ready. */ 53439b9004dSPhilipp Zabel if (buf_num == 0) 53539b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); 53639b9004dSPhilipp Zabel else 53739b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); 53839b9004dSPhilipp Zabel 53939b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 54039b9004dSPhilipp Zabel } 54139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer); 54239b9004dSPhilipp Zabel 543bce6f087SSteve Longerbeam void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num) 544bce6f087SSteve Longerbeam { 545bce6f087SSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 546bce6f087SSteve Longerbeam unsigned int chno = channel->num; 547bce6f087SSteve Longerbeam unsigned long flags; 548bce6f087SSteve Longerbeam 549bce6f087SSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 550bce6f087SSteve Longerbeam 551bce6f087SSteve Longerbeam ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */ 552bce6f087SSteve Longerbeam switch (buf_num) { 553bce6f087SSteve Longerbeam case 0: 554bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); 555bce6f087SSteve Longerbeam break; 556bce6f087SSteve Longerbeam case 1: 557bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); 558bce6f087SSteve Longerbeam break; 559bce6f087SSteve Longerbeam case 2: 560bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno)); 561bce6f087SSteve Longerbeam break; 562bce6f087SSteve Longerbeam default: 563bce6f087SSteve Longerbeam break; 564bce6f087SSteve Longerbeam } 565bce6f087SSteve Longerbeam ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ 566bce6f087SSteve Longerbeam 567bce6f087SSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 568bce6f087SSteve Longerbeam } 569bce6f087SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer); 570bce6f087SSteve Longerbeam 57139b9004dSPhilipp Zabel int ipu_idmac_enable_channel(struct ipuv3_channel *channel) 57239b9004dSPhilipp Zabel { 57339b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 57439b9004dSPhilipp Zabel u32 val; 57539b9004dSPhilipp Zabel unsigned long flags; 57639b9004dSPhilipp Zabel 57739b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 57839b9004dSPhilipp Zabel 57939b9004dSPhilipp Zabel val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); 58039b9004dSPhilipp Zabel val |= idma_mask(channel->num); 58139b9004dSPhilipp Zabel ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); 58239b9004dSPhilipp Zabel 58339b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 58439b9004dSPhilipp Zabel 58539b9004dSPhilipp Zabel return 0; 58639b9004dSPhilipp Zabel } 58739b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel); 58839b9004dSPhilipp Zabel 589682b7c1cSLinus Torvalds bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno) 590682b7c1cSLinus Torvalds { 591682b7c1cSLinus Torvalds return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno)); 592682b7c1cSLinus Torvalds } 593682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy); 594682b7c1cSLinus Torvalds 59539b9004dSPhilipp Zabel int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) 59639b9004dSPhilipp Zabel { 59739b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 59839b9004dSPhilipp Zabel unsigned long timeout; 59939b9004dSPhilipp Zabel 60039b9004dSPhilipp Zabel timeout = jiffies + msecs_to_jiffies(ms); 60139b9004dSPhilipp Zabel while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) & 60239b9004dSPhilipp Zabel idma_mask(channel->num)) { 60339b9004dSPhilipp Zabel if (time_after(jiffies, timeout)) 60439b9004dSPhilipp Zabel return -ETIMEDOUT; 60539b9004dSPhilipp Zabel cpu_relax(); 60639b9004dSPhilipp Zabel } 60739b9004dSPhilipp Zabel 60839b9004dSPhilipp Zabel return 0; 60939b9004dSPhilipp Zabel } 61039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); 61139b9004dSPhilipp Zabel 61239b9004dSPhilipp Zabel int ipu_idmac_disable_channel(struct ipuv3_channel *channel) 61339b9004dSPhilipp Zabel { 61439b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 61539b9004dSPhilipp Zabel u32 val; 61639b9004dSPhilipp Zabel unsigned long flags; 61739b9004dSPhilipp Zabel 61839b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 61939b9004dSPhilipp Zabel 62039b9004dSPhilipp Zabel /* Disable DMA channel(s) */ 62139b9004dSPhilipp Zabel val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); 62239b9004dSPhilipp Zabel val &= ~idma_mask(channel->num); 62339b9004dSPhilipp Zabel ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); 62439b9004dSPhilipp Zabel 625e7268c69SSteve Longerbeam __ipu_idmac_reset_current_buffer(channel); 626e7268c69SSteve Longerbeam 62739b9004dSPhilipp Zabel /* Set channel buffers NOT to be ready */ 62839b9004dSPhilipp Zabel ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */ 62939b9004dSPhilipp Zabel 63039b9004dSPhilipp Zabel if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) & 63139b9004dSPhilipp Zabel idma_mask(channel->num)) { 63239b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(channel->num), 63339b9004dSPhilipp Zabel IPU_CHA_BUF0_RDY(channel->num)); 63439b9004dSPhilipp Zabel } 63539b9004dSPhilipp Zabel 63639b9004dSPhilipp Zabel if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) & 63739b9004dSPhilipp Zabel idma_mask(channel->num)) { 63839b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(channel->num), 63939b9004dSPhilipp Zabel IPU_CHA_BUF1_RDY(channel->num)); 64039b9004dSPhilipp Zabel } 64139b9004dSPhilipp Zabel 64239b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ 64339b9004dSPhilipp Zabel 64439b9004dSPhilipp Zabel /* Reset the double buffer */ 64539b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); 64639b9004dSPhilipp Zabel val &= ~idma_mask(channel->num); 64739b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num)); 64839b9004dSPhilipp Zabel 64939b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 65039b9004dSPhilipp Zabel 65139b9004dSPhilipp Zabel return 0; 65239b9004dSPhilipp Zabel } 65339b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel); 65439b9004dSPhilipp Zabel 6552bcf577eSSteve Longerbeam /* 6562bcf577eSSteve Longerbeam * The imx6 rev. D TRM says that enabling the WM feature will increase 6572bcf577eSSteve Longerbeam * a channel's priority. Refer to Table 36-8 Calculated priority value. 6582bcf577eSSteve Longerbeam * The sub-module that is the sink or source for the channel must enable 6592bcf577eSSteve Longerbeam * watermark signal for this to take effect (SMFC_WM for instance). 6602bcf577eSSteve Longerbeam */ 6612bcf577eSSteve Longerbeam void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable) 6622bcf577eSSteve Longerbeam { 6632bcf577eSSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 6642bcf577eSSteve Longerbeam unsigned long flags; 6652bcf577eSSteve Longerbeam u32 val; 6662bcf577eSSteve Longerbeam 6672bcf577eSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 6682bcf577eSSteve Longerbeam 6692bcf577eSSteve Longerbeam val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num)); 6702bcf577eSSteve Longerbeam if (enable) 6712bcf577eSSteve Longerbeam val |= 1 << (channel->num % 32); 6722bcf577eSSteve Longerbeam else 6732bcf577eSSteve Longerbeam val &= ~(1 << (channel->num % 32)); 6742bcf577eSSteve Longerbeam ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num)); 6752bcf577eSSteve Longerbeam 6762bcf577eSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 6772bcf577eSSteve Longerbeam } 6782bcf577eSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark); 6792bcf577eSSteve Longerbeam 68039b9004dSPhilipp Zabel static int ipu_memory_reset(struct ipu_soc *ipu) 68139b9004dSPhilipp Zabel { 68239b9004dSPhilipp Zabel unsigned long timeout; 68339b9004dSPhilipp Zabel 68439b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); 68539b9004dSPhilipp Zabel 68639b9004dSPhilipp Zabel timeout = jiffies + msecs_to_jiffies(1000); 68739b9004dSPhilipp Zabel while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { 68839b9004dSPhilipp Zabel if (time_after(jiffies, timeout)) 68939b9004dSPhilipp Zabel return -ETIME; 69039b9004dSPhilipp Zabel cpu_relax(); 69139b9004dSPhilipp Zabel } 69239b9004dSPhilipp Zabel 69339b9004dSPhilipp Zabel return 0; 69439b9004dSPhilipp Zabel } 69539b9004dSPhilipp Zabel 696ba07975fSSteve Longerbeam /* 697ba07975fSSteve Longerbeam * Set the source mux for the given CSI. Selects either parallel or 698ba07975fSSteve Longerbeam * MIPI CSI2 sources. 699ba07975fSSteve Longerbeam */ 700ba07975fSSteve Longerbeam void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2) 701ba07975fSSteve Longerbeam { 702ba07975fSSteve Longerbeam unsigned long flags; 703ba07975fSSteve Longerbeam u32 val, mask; 704ba07975fSSteve Longerbeam 705ba07975fSSteve Longerbeam mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE : 706ba07975fSSteve Longerbeam IPU_CONF_CSI0_DATA_SOURCE; 707ba07975fSSteve Longerbeam 708ba07975fSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 709ba07975fSSteve Longerbeam 710ba07975fSSteve Longerbeam val = ipu_cm_read(ipu, IPU_CONF); 711ba07975fSSteve Longerbeam if (mipi_csi2) 712ba07975fSSteve Longerbeam val |= mask; 713ba07975fSSteve Longerbeam else 714ba07975fSSteve Longerbeam val &= ~mask; 715ba07975fSSteve Longerbeam ipu_cm_write(ipu, val, IPU_CONF); 716ba07975fSSteve Longerbeam 717ba07975fSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 718ba07975fSSteve Longerbeam } 719ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux); 720ba07975fSSteve Longerbeam 721ba07975fSSteve Longerbeam /* 722ba07975fSSteve Longerbeam * Set the source mux for the IC. Selects either CSI[01] or the VDI. 723ba07975fSSteve Longerbeam */ 724ba07975fSSteve Longerbeam void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi) 725ba07975fSSteve Longerbeam { 726ba07975fSSteve Longerbeam unsigned long flags; 727ba07975fSSteve Longerbeam u32 val; 728ba07975fSSteve Longerbeam 729ba07975fSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 730ba07975fSSteve Longerbeam 731ba07975fSSteve Longerbeam val = ipu_cm_read(ipu, IPU_CONF); 732b7dfee24SMarek Vasut if (vdi) 733ba07975fSSteve Longerbeam val |= IPU_CONF_IC_INPUT; 734b7dfee24SMarek Vasut else 735ba07975fSSteve Longerbeam val &= ~IPU_CONF_IC_INPUT; 736b7dfee24SMarek Vasut 737ba07975fSSteve Longerbeam if (csi_id == 1) 738ba07975fSSteve Longerbeam val |= IPU_CONF_CSI_SEL; 739ba07975fSSteve Longerbeam else 740ba07975fSSteve Longerbeam val &= ~IPU_CONF_CSI_SEL; 741b7dfee24SMarek Vasut 742ba07975fSSteve Longerbeam ipu_cm_write(ipu, val, IPU_CONF); 743ba07975fSSteve Longerbeam 744ba07975fSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 745ba07975fSSteve Longerbeam } 746ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux); 747ba07975fSSteve Longerbeam 748ac4708faSSteve Longerbeam 749ac4708faSSteve Longerbeam /* Frame Synchronization Unit Channel Linking */ 750ac4708faSSteve Longerbeam 751ac4708faSSteve Longerbeam struct fsu_link_reg_info { 752ac4708faSSteve Longerbeam int chno; 753ac4708faSSteve Longerbeam u32 reg; 754ac4708faSSteve Longerbeam u32 mask; 755ac4708faSSteve Longerbeam u32 val; 756ac4708faSSteve Longerbeam }; 757ac4708faSSteve Longerbeam 758ac4708faSSteve Longerbeam struct fsu_link_info { 759ac4708faSSteve Longerbeam struct fsu_link_reg_info src; 760ac4708faSSteve Longerbeam struct fsu_link_reg_info sink; 761ac4708faSSteve Longerbeam }; 762ac4708faSSteve Longerbeam 763ac4708faSSteve Longerbeam static const struct fsu_link_info fsu_link_info[] = { 764ac4708faSSteve Longerbeam { 765ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2, 766ac4708faSSteve Longerbeam FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC }, 767ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1, 768ac4708faSSteve Longerbeam FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC }, 769ac4708faSSteve Longerbeam }, { 770ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2, 771ac4708faSSteve Longerbeam FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF }, 772ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1, 773ac4708faSSteve Longerbeam FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF }, 774ac4708faSSteve Longerbeam }, { 775ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2, 776ac4708faSSteve Longerbeam FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP }, 777ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1, 778ac4708faSSteve Longerbeam FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP }, 779ac4708faSSteve Longerbeam }, { 780ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_CSI_DIRECT, 0 }, 781ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1, 782ac4708faSSteve Longerbeam FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT }, 783ac4708faSSteve Longerbeam }, 784ac4708faSSteve Longerbeam }; 785ac4708faSSteve Longerbeam 786ac4708faSSteve Longerbeam static const struct fsu_link_info *find_fsu_link_info(int src, int sink) 787ac4708faSSteve Longerbeam { 788ac4708faSSteve Longerbeam int i; 789ac4708faSSteve Longerbeam 790ac4708faSSteve Longerbeam for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) { 791ac4708faSSteve Longerbeam if (src == fsu_link_info[i].src.chno && 792ac4708faSSteve Longerbeam sink == fsu_link_info[i].sink.chno) 793ac4708faSSteve Longerbeam return &fsu_link_info[i]; 794ac4708faSSteve Longerbeam } 795ac4708faSSteve Longerbeam 796ac4708faSSteve Longerbeam return NULL; 797ac4708faSSteve Longerbeam } 798ac4708faSSteve Longerbeam 799ac4708faSSteve Longerbeam /* 800ac4708faSSteve Longerbeam * Links a source channel to a sink channel in the FSU. 801ac4708faSSteve Longerbeam */ 802ac4708faSSteve Longerbeam int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch) 803ac4708faSSteve Longerbeam { 804ac4708faSSteve Longerbeam const struct fsu_link_info *link; 805ac4708faSSteve Longerbeam u32 src_reg, sink_reg; 806ac4708faSSteve Longerbeam unsigned long flags; 807ac4708faSSteve Longerbeam 808ac4708faSSteve Longerbeam link = find_fsu_link_info(src_ch, sink_ch); 809ac4708faSSteve Longerbeam if (!link) 810ac4708faSSteve Longerbeam return -EINVAL; 811ac4708faSSteve Longerbeam 812ac4708faSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 813ac4708faSSteve Longerbeam 814ac4708faSSteve Longerbeam if (link->src.mask) { 815ac4708faSSteve Longerbeam src_reg = ipu_cm_read(ipu, link->src.reg); 816ac4708faSSteve Longerbeam src_reg &= ~link->src.mask; 817ac4708faSSteve Longerbeam src_reg |= link->src.val; 818ac4708faSSteve Longerbeam ipu_cm_write(ipu, src_reg, link->src.reg); 819ac4708faSSteve Longerbeam } 820ac4708faSSteve Longerbeam 821ac4708faSSteve Longerbeam if (link->sink.mask) { 822ac4708faSSteve Longerbeam sink_reg = ipu_cm_read(ipu, link->sink.reg); 823ac4708faSSteve Longerbeam sink_reg &= ~link->sink.mask; 824ac4708faSSteve Longerbeam sink_reg |= link->sink.val; 825ac4708faSSteve Longerbeam ipu_cm_write(ipu, sink_reg, link->sink.reg); 826ac4708faSSteve Longerbeam } 827ac4708faSSteve Longerbeam 828ac4708faSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 829ac4708faSSteve Longerbeam return 0; 830ac4708faSSteve Longerbeam } 831ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_link); 832ac4708faSSteve Longerbeam 833ac4708faSSteve Longerbeam /* 834ac4708faSSteve Longerbeam * Unlinks source and sink channels in the FSU. 835ac4708faSSteve Longerbeam */ 836ac4708faSSteve Longerbeam int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch) 837ac4708faSSteve Longerbeam { 838ac4708faSSteve Longerbeam const struct fsu_link_info *link; 839ac4708faSSteve Longerbeam u32 src_reg, sink_reg; 840ac4708faSSteve Longerbeam unsigned long flags; 841ac4708faSSteve Longerbeam 842ac4708faSSteve Longerbeam link = find_fsu_link_info(src_ch, sink_ch); 843ac4708faSSteve Longerbeam if (!link) 844ac4708faSSteve Longerbeam return -EINVAL; 845ac4708faSSteve Longerbeam 846ac4708faSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 847ac4708faSSteve Longerbeam 848ac4708faSSteve Longerbeam if (link->src.mask) { 849ac4708faSSteve Longerbeam src_reg = ipu_cm_read(ipu, link->src.reg); 850ac4708faSSteve Longerbeam src_reg &= ~link->src.mask; 851ac4708faSSteve Longerbeam ipu_cm_write(ipu, src_reg, link->src.reg); 852ac4708faSSteve Longerbeam } 853ac4708faSSteve Longerbeam 854ac4708faSSteve Longerbeam if (link->sink.mask) { 855ac4708faSSteve Longerbeam sink_reg = ipu_cm_read(ipu, link->sink.reg); 856ac4708faSSteve Longerbeam sink_reg &= ~link->sink.mask; 857ac4708faSSteve Longerbeam ipu_cm_write(ipu, sink_reg, link->sink.reg); 858ac4708faSSteve Longerbeam } 859ac4708faSSteve Longerbeam 860ac4708faSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 861ac4708faSSteve Longerbeam return 0; 862ac4708faSSteve Longerbeam } 863ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_unlink); 864ac4708faSSteve Longerbeam 865ac4708faSSteve Longerbeam /* Link IDMAC channels in the FSU */ 866ac4708faSSteve Longerbeam int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink) 867ac4708faSSteve Longerbeam { 868ac4708faSSteve Longerbeam return ipu_fsu_link(src->ipu, src->num, sink->num); 869ac4708faSSteve Longerbeam } 870ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_link); 871ac4708faSSteve Longerbeam 872ac4708faSSteve Longerbeam /* Unlink IDMAC channels in the FSU */ 873ac4708faSSteve Longerbeam int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink) 874ac4708faSSteve Longerbeam { 875ac4708faSSteve Longerbeam return ipu_fsu_unlink(src->ipu, src->num, sink->num); 876ac4708faSSteve Longerbeam } 877ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_unlink); 878ac4708faSSteve Longerbeam 87939b9004dSPhilipp Zabel struct ipu_devtype { 88039b9004dSPhilipp Zabel const char *name; 88139b9004dSPhilipp Zabel unsigned long cm_ofs; 88239b9004dSPhilipp Zabel unsigned long cpmem_ofs; 88339b9004dSPhilipp Zabel unsigned long srm_ofs; 88439b9004dSPhilipp Zabel unsigned long tpm_ofs; 8852ffd48f2SSteve Longerbeam unsigned long csi0_ofs; 8862ffd48f2SSteve Longerbeam unsigned long csi1_ofs; 8871aa8ea0dSSteve Longerbeam unsigned long ic_ofs; 88839b9004dSPhilipp Zabel unsigned long disp0_ofs; 88939b9004dSPhilipp Zabel unsigned long disp1_ofs; 89039b9004dSPhilipp Zabel unsigned long dc_tmpl_ofs; 89139b9004dSPhilipp Zabel unsigned long vdi_ofs; 89239b9004dSPhilipp Zabel enum ipuv3_type type; 89339b9004dSPhilipp Zabel }; 89439b9004dSPhilipp Zabel 89539b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx51 = { 89639b9004dSPhilipp Zabel .name = "IPUv3EX", 89739b9004dSPhilipp Zabel .cm_ofs = 0x1e000000, 89839b9004dSPhilipp Zabel .cpmem_ofs = 0x1f000000, 89939b9004dSPhilipp Zabel .srm_ofs = 0x1f040000, 90039b9004dSPhilipp Zabel .tpm_ofs = 0x1f060000, 9012ffd48f2SSteve Longerbeam .csi0_ofs = 0x1f030000, 9022ffd48f2SSteve Longerbeam .csi1_ofs = 0x1f038000, 903a49e7c0dSPhilipp Zabel .ic_ofs = 0x1e020000, 90439b9004dSPhilipp Zabel .disp0_ofs = 0x1e040000, 90539b9004dSPhilipp Zabel .disp1_ofs = 0x1e048000, 90639b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x1f080000, 90739b9004dSPhilipp Zabel .vdi_ofs = 0x1e068000, 90839b9004dSPhilipp Zabel .type = IPUV3EX, 90939b9004dSPhilipp Zabel }; 91039b9004dSPhilipp Zabel 91139b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx53 = { 91239b9004dSPhilipp Zabel .name = "IPUv3M", 91339b9004dSPhilipp Zabel .cm_ofs = 0x06000000, 91439b9004dSPhilipp Zabel .cpmem_ofs = 0x07000000, 91539b9004dSPhilipp Zabel .srm_ofs = 0x07040000, 91639b9004dSPhilipp Zabel .tpm_ofs = 0x07060000, 9172ffd48f2SSteve Longerbeam .csi0_ofs = 0x07030000, 9182ffd48f2SSteve Longerbeam .csi1_ofs = 0x07038000, 919a49e7c0dSPhilipp Zabel .ic_ofs = 0x06020000, 92039b9004dSPhilipp Zabel .disp0_ofs = 0x06040000, 92139b9004dSPhilipp Zabel .disp1_ofs = 0x06048000, 92239b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x07080000, 92339b9004dSPhilipp Zabel .vdi_ofs = 0x06068000, 92439b9004dSPhilipp Zabel .type = IPUV3M, 92539b9004dSPhilipp Zabel }; 92639b9004dSPhilipp Zabel 92739b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx6q = { 92839b9004dSPhilipp Zabel .name = "IPUv3H", 92939b9004dSPhilipp Zabel .cm_ofs = 0x00200000, 93039b9004dSPhilipp Zabel .cpmem_ofs = 0x00300000, 93139b9004dSPhilipp Zabel .srm_ofs = 0x00340000, 93239b9004dSPhilipp Zabel .tpm_ofs = 0x00360000, 9332ffd48f2SSteve Longerbeam .csi0_ofs = 0x00230000, 9342ffd48f2SSteve Longerbeam .csi1_ofs = 0x00238000, 9351aa8ea0dSSteve Longerbeam .ic_ofs = 0x00220000, 93639b9004dSPhilipp Zabel .disp0_ofs = 0x00240000, 93739b9004dSPhilipp Zabel .disp1_ofs = 0x00248000, 93839b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x00380000, 93939b9004dSPhilipp Zabel .vdi_ofs = 0x00268000, 94039b9004dSPhilipp Zabel .type = IPUV3H, 94139b9004dSPhilipp Zabel }; 94239b9004dSPhilipp Zabel 94339b9004dSPhilipp Zabel static const struct of_device_id imx_ipu_dt_ids[] = { 94439b9004dSPhilipp Zabel { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, 94539b9004dSPhilipp Zabel { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, 94639b9004dSPhilipp Zabel { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, 94792681fe7SLucas Stach { .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6q, }, 94839b9004dSPhilipp Zabel { /* sentinel */ } 94939b9004dSPhilipp Zabel }; 95039b9004dSPhilipp Zabel MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); 95139b9004dSPhilipp Zabel 95239b9004dSPhilipp Zabel static int ipu_submodules_init(struct ipu_soc *ipu, 95339b9004dSPhilipp Zabel struct platform_device *pdev, unsigned long ipu_base, 95439b9004dSPhilipp Zabel struct clk *ipu_clk) 95539b9004dSPhilipp Zabel { 95639b9004dSPhilipp Zabel char *unit; 95739b9004dSPhilipp Zabel int ret; 95839b9004dSPhilipp Zabel struct device *dev = &pdev->dev; 95939b9004dSPhilipp Zabel const struct ipu_devtype *devtype = ipu->devtype; 96039b9004dSPhilipp Zabel 9617d2691daSSteve Longerbeam ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs); 9627d2691daSSteve Longerbeam if (ret) { 9637d2691daSSteve Longerbeam unit = "cpmem"; 9647d2691daSSteve Longerbeam goto err_cpmem; 9657d2691daSSteve Longerbeam } 9667d2691daSSteve Longerbeam 9672ffd48f2SSteve Longerbeam ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs, 9682ffd48f2SSteve Longerbeam IPU_CONF_CSI0_EN, ipu_clk); 9692ffd48f2SSteve Longerbeam if (ret) { 9702ffd48f2SSteve Longerbeam unit = "csi0"; 9712ffd48f2SSteve Longerbeam goto err_csi_0; 9722ffd48f2SSteve Longerbeam } 9732ffd48f2SSteve Longerbeam 9742ffd48f2SSteve Longerbeam ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs, 9752ffd48f2SSteve Longerbeam IPU_CONF_CSI1_EN, ipu_clk); 9762ffd48f2SSteve Longerbeam if (ret) { 9772ffd48f2SSteve Longerbeam unit = "csi1"; 9782ffd48f2SSteve Longerbeam goto err_csi_1; 9792ffd48f2SSteve Longerbeam } 9802ffd48f2SSteve Longerbeam 9811aa8ea0dSSteve Longerbeam ret = ipu_ic_init(ipu, dev, 9821aa8ea0dSSteve Longerbeam ipu_base + devtype->ic_ofs, 9831aa8ea0dSSteve Longerbeam ipu_base + devtype->tpm_ofs); 9841aa8ea0dSSteve Longerbeam if (ret) { 9851aa8ea0dSSteve Longerbeam unit = "ic"; 9861aa8ea0dSSteve Longerbeam goto err_ic; 9871aa8ea0dSSteve Longerbeam } 9881aa8ea0dSSteve Longerbeam 9892d2ead45SSteve Longerbeam ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs, 9902d2ead45SSteve Longerbeam IPU_CONF_VDI_EN | IPU_CONF_ISP_EN | 9912d2ead45SSteve Longerbeam IPU_CONF_IC_INPUT); 9922d2ead45SSteve Longerbeam if (ret) { 9932d2ead45SSteve Longerbeam unit = "vdi"; 9942d2ead45SSteve Longerbeam goto err_vdi; 9952d2ead45SSteve Longerbeam } 9962d2ead45SSteve Longerbeam 997cd98e85aSSteve Longerbeam ret = ipu_image_convert_init(ipu, dev); 998cd98e85aSSteve Longerbeam if (ret) { 999cd98e85aSSteve Longerbeam unit = "image_convert"; 1000cd98e85aSSteve Longerbeam goto err_image_convert; 1001cd98e85aSSteve Longerbeam } 1002cd98e85aSSteve Longerbeam 100339b9004dSPhilipp Zabel ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, 100439b9004dSPhilipp Zabel IPU_CONF_DI0_EN, ipu_clk); 100539b9004dSPhilipp Zabel if (ret) { 100639b9004dSPhilipp Zabel unit = "di0"; 100739b9004dSPhilipp Zabel goto err_di_0; 100839b9004dSPhilipp Zabel } 100939b9004dSPhilipp Zabel 101039b9004dSPhilipp Zabel ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs, 101139b9004dSPhilipp Zabel IPU_CONF_DI1_EN, ipu_clk); 101239b9004dSPhilipp Zabel if (ret) { 101339b9004dSPhilipp Zabel unit = "di1"; 101439b9004dSPhilipp Zabel goto err_di_1; 101539b9004dSPhilipp Zabel } 101639b9004dSPhilipp Zabel 101739b9004dSPhilipp Zabel ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs + 101839b9004dSPhilipp Zabel IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs); 101939b9004dSPhilipp Zabel if (ret) { 102039b9004dSPhilipp Zabel unit = "dc_template"; 102139b9004dSPhilipp Zabel goto err_dc; 102239b9004dSPhilipp Zabel } 102339b9004dSPhilipp Zabel 102439b9004dSPhilipp Zabel ret = ipu_dmfc_init(ipu, dev, ipu_base + 102539b9004dSPhilipp Zabel devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk); 102639b9004dSPhilipp Zabel if (ret) { 102739b9004dSPhilipp Zabel unit = "dmfc"; 102839b9004dSPhilipp Zabel goto err_dmfc; 102939b9004dSPhilipp Zabel } 103039b9004dSPhilipp Zabel 103139b9004dSPhilipp Zabel ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs); 103239b9004dSPhilipp Zabel if (ret) { 103339b9004dSPhilipp Zabel unit = "dp"; 103439b9004dSPhilipp Zabel goto err_dp; 103539b9004dSPhilipp Zabel } 103639b9004dSPhilipp Zabel 103735de925fSPhilipp Zabel ret = ipu_smfc_init(ipu, dev, ipu_base + 103835de925fSPhilipp Zabel devtype->cm_ofs + IPU_CM_SMFC_REG_OFS); 103935de925fSPhilipp Zabel if (ret) { 104035de925fSPhilipp Zabel unit = "smfc"; 104135de925fSPhilipp Zabel goto err_smfc; 104235de925fSPhilipp Zabel } 104335de925fSPhilipp Zabel 104439b9004dSPhilipp Zabel return 0; 104539b9004dSPhilipp Zabel 104635de925fSPhilipp Zabel err_smfc: 104735de925fSPhilipp Zabel ipu_dp_exit(ipu); 104839b9004dSPhilipp Zabel err_dp: 104939b9004dSPhilipp Zabel ipu_dmfc_exit(ipu); 105039b9004dSPhilipp Zabel err_dmfc: 105139b9004dSPhilipp Zabel ipu_dc_exit(ipu); 105239b9004dSPhilipp Zabel err_dc: 105339b9004dSPhilipp Zabel ipu_di_exit(ipu, 1); 105439b9004dSPhilipp Zabel err_di_1: 105539b9004dSPhilipp Zabel ipu_di_exit(ipu, 0); 105639b9004dSPhilipp Zabel err_di_0: 1057cd98e85aSSteve Longerbeam ipu_image_convert_exit(ipu); 1058cd98e85aSSteve Longerbeam err_image_convert: 10592d2ead45SSteve Longerbeam ipu_vdi_exit(ipu); 10602d2ead45SSteve Longerbeam err_vdi: 10611aa8ea0dSSteve Longerbeam ipu_ic_exit(ipu); 10621aa8ea0dSSteve Longerbeam err_ic: 10632ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 1); 10642ffd48f2SSteve Longerbeam err_csi_1: 10652ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 0); 10662ffd48f2SSteve Longerbeam err_csi_0: 10677d2691daSSteve Longerbeam ipu_cpmem_exit(ipu); 10687d2691daSSteve Longerbeam err_cpmem: 106939b9004dSPhilipp Zabel dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret); 107039b9004dSPhilipp Zabel return ret; 107139b9004dSPhilipp Zabel } 107239b9004dSPhilipp Zabel 107339b9004dSPhilipp Zabel static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs) 107439b9004dSPhilipp Zabel { 107539b9004dSPhilipp Zabel unsigned long status; 107639b9004dSPhilipp Zabel int i, bit, irq; 107739b9004dSPhilipp Zabel 107839b9004dSPhilipp Zabel for (i = 0; i < num_regs; i++) { 107939b9004dSPhilipp Zabel 108039b9004dSPhilipp Zabel status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i])); 108139b9004dSPhilipp Zabel status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i])); 108239b9004dSPhilipp Zabel 108339b9004dSPhilipp Zabel for_each_set_bit(bit, &status, 32) { 1084682b7c1cSLinus Torvalds irq = irq_linear_revmap(ipu->domain, 1085682b7c1cSLinus Torvalds regs[i] * 32 + bit); 108639b9004dSPhilipp Zabel if (irq) 108739b9004dSPhilipp Zabel generic_handle_irq(irq); 108839b9004dSPhilipp Zabel } 108939b9004dSPhilipp Zabel } 109039b9004dSPhilipp Zabel } 109139b9004dSPhilipp Zabel 1092bd0b9ac4SThomas Gleixner static void ipu_irq_handler(struct irq_desc *desc) 109339b9004dSPhilipp Zabel { 109439b9004dSPhilipp Zabel struct ipu_soc *ipu = irq_desc_get_handler_data(desc); 10954d9efdfcSJiang Liu struct irq_chip *chip = irq_desc_get_chip(desc); 1096ac66b834SColin Ian King static const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14}; 109739b9004dSPhilipp Zabel 109839b9004dSPhilipp Zabel chained_irq_enter(chip, desc); 109939b9004dSPhilipp Zabel 110039b9004dSPhilipp Zabel ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); 110139b9004dSPhilipp Zabel 110239b9004dSPhilipp Zabel chained_irq_exit(chip, desc); 110339b9004dSPhilipp Zabel } 110439b9004dSPhilipp Zabel 1105bd0b9ac4SThomas Gleixner static void ipu_err_irq_handler(struct irq_desc *desc) 110639b9004dSPhilipp Zabel { 110739b9004dSPhilipp Zabel struct ipu_soc *ipu = irq_desc_get_handler_data(desc); 11084d9efdfcSJiang Liu struct irq_chip *chip = irq_desc_get_chip(desc); 1109ac66b834SColin Ian King static const int int_reg[] = { 4, 5, 8, 9}; 111039b9004dSPhilipp Zabel 111139b9004dSPhilipp Zabel chained_irq_enter(chip, desc); 111239b9004dSPhilipp Zabel 111339b9004dSPhilipp Zabel ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); 111439b9004dSPhilipp Zabel 111539b9004dSPhilipp Zabel chained_irq_exit(chip, desc); 111639b9004dSPhilipp Zabel } 111739b9004dSPhilipp Zabel 1118682b7c1cSLinus Torvalds int ipu_map_irq(struct ipu_soc *ipu, int irq) 1119682b7c1cSLinus Torvalds { 1120682b7c1cSLinus Torvalds int virq; 1121682b7c1cSLinus Torvalds 1122682b7c1cSLinus Torvalds virq = irq_linear_revmap(ipu->domain, irq); 1123682b7c1cSLinus Torvalds if (!virq) 1124682b7c1cSLinus Torvalds virq = irq_create_mapping(ipu->domain, irq); 1125682b7c1cSLinus Torvalds 1126682b7c1cSLinus Torvalds return virq; 1127682b7c1cSLinus Torvalds } 1128682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_map_irq); 1129682b7c1cSLinus Torvalds 113039b9004dSPhilipp Zabel int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, 113139b9004dSPhilipp Zabel enum ipu_channel_irq irq_type) 113239b9004dSPhilipp Zabel { 1133682b7c1cSLinus Torvalds return ipu_map_irq(ipu, irq_type + channel->num); 113439b9004dSPhilipp Zabel } 113539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq); 113639b9004dSPhilipp Zabel 113739b9004dSPhilipp Zabel static void ipu_submodules_exit(struct ipu_soc *ipu) 113839b9004dSPhilipp Zabel { 113935de925fSPhilipp Zabel ipu_smfc_exit(ipu); 114039b9004dSPhilipp Zabel ipu_dp_exit(ipu); 114139b9004dSPhilipp Zabel ipu_dmfc_exit(ipu); 114239b9004dSPhilipp Zabel ipu_dc_exit(ipu); 114339b9004dSPhilipp Zabel ipu_di_exit(ipu, 1); 114439b9004dSPhilipp Zabel ipu_di_exit(ipu, 0); 1145cd98e85aSSteve Longerbeam ipu_image_convert_exit(ipu); 11462d2ead45SSteve Longerbeam ipu_vdi_exit(ipu); 11471aa8ea0dSSteve Longerbeam ipu_ic_exit(ipu); 11482ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 1); 11492ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 0); 11507d2691daSSteve Longerbeam ipu_cpmem_exit(ipu); 115139b9004dSPhilipp Zabel } 115239b9004dSPhilipp Zabel 115339b9004dSPhilipp Zabel static int platform_remove_devices_fn(struct device *dev, void *unused) 115439b9004dSPhilipp Zabel { 115539b9004dSPhilipp Zabel struct platform_device *pdev = to_platform_device(dev); 115639b9004dSPhilipp Zabel 115739b9004dSPhilipp Zabel platform_device_unregister(pdev); 115839b9004dSPhilipp Zabel 115939b9004dSPhilipp Zabel return 0; 116039b9004dSPhilipp Zabel } 116139b9004dSPhilipp Zabel 116239b9004dSPhilipp Zabel static void platform_device_unregister_children(struct platform_device *pdev) 116339b9004dSPhilipp Zabel { 116439b9004dSPhilipp Zabel device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn); 116539b9004dSPhilipp Zabel } 116639b9004dSPhilipp Zabel 116739b9004dSPhilipp Zabel struct ipu_platform_reg { 116839b9004dSPhilipp Zabel struct ipu_client_platformdata pdata; 116939b9004dSPhilipp Zabel const char *name; 117039b9004dSPhilipp Zabel }; 117139b9004dSPhilipp Zabel 1172304e6be6SPhilipp Zabel /* These must be in the order of the corresponding device tree port nodes */ 1173310944d1SPhilipp Zabel static struct ipu_platform_reg client_reg[] = { 117439b9004dSPhilipp Zabel { 117539b9004dSPhilipp Zabel .pdata = { 1176304e6be6SPhilipp Zabel .csi = 0, 1177304e6be6SPhilipp Zabel .dma[0] = IPUV3_CHANNEL_CSI0, 1178304e6be6SPhilipp Zabel .dma[1] = -EINVAL, 1179304e6be6SPhilipp Zabel }, 118088287ec3SSteve Longerbeam .name = "imx-ipuv3-csi", 1181304e6be6SPhilipp Zabel }, { 1182304e6be6SPhilipp Zabel .pdata = { 1183304e6be6SPhilipp Zabel .csi = 1, 1184304e6be6SPhilipp Zabel .dma[0] = IPUV3_CHANNEL_CSI1, 1185304e6be6SPhilipp Zabel .dma[1] = -EINVAL, 1186304e6be6SPhilipp Zabel }, 118788287ec3SSteve Longerbeam .name = "imx-ipuv3-csi", 1188304e6be6SPhilipp Zabel }, { 1189304e6be6SPhilipp Zabel .pdata = { 119039b9004dSPhilipp Zabel .di = 0, 119139b9004dSPhilipp Zabel .dc = 5, 119239b9004dSPhilipp Zabel .dp = IPU_DP_FLOW_SYNC_BG, 119339b9004dSPhilipp Zabel .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC, 119439b9004dSPhilipp Zabel .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC, 119539b9004dSPhilipp Zabel }, 119639b9004dSPhilipp Zabel .name = "imx-ipuv3-crtc", 119739b9004dSPhilipp Zabel }, { 119839b9004dSPhilipp Zabel .pdata = { 119939b9004dSPhilipp Zabel .di = 1, 120039b9004dSPhilipp Zabel .dc = 1, 120139b9004dSPhilipp Zabel .dp = -EINVAL, 120239b9004dSPhilipp Zabel .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC, 120339b9004dSPhilipp Zabel .dma[1] = -EINVAL, 120439b9004dSPhilipp Zabel }, 120539b9004dSPhilipp Zabel .name = "imx-ipuv3-crtc", 120639b9004dSPhilipp Zabel }, 120739b9004dSPhilipp Zabel }; 120839b9004dSPhilipp Zabel 120939b9004dSPhilipp Zabel static DEFINE_MUTEX(ipu_client_id_mutex); 121039b9004dSPhilipp Zabel static int ipu_client_id; 121139b9004dSPhilipp Zabel 1212d6ca8ca7SPhilipp Zabel static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) 121339b9004dSPhilipp Zabel { 121439b9004dSPhilipp Zabel struct device *dev = ipu->dev; 121539b9004dSPhilipp Zabel unsigned i; 121639b9004dSPhilipp Zabel int id, ret; 121739b9004dSPhilipp Zabel 121839b9004dSPhilipp Zabel mutex_lock(&ipu_client_id_mutex); 121939b9004dSPhilipp Zabel id = ipu_client_id; 122039b9004dSPhilipp Zabel ipu_client_id += ARRAY_SIZE(client_reg); 122139b9004dSPhilipp Zabel mutex_unlock(&ipu_client_id_mutex); 122239b9004dSPhilipp Zabel 122339b9004dSPhilipp Zabel for (i = 0; i < ARRAY_SIZE(client_reg); i++) { 1224310944d1SPhilipp Zabel struct ipu_platform_reg *reg = &client_reg[i]; 122539b9004dSPhilipp Zabel struct platform_device *pdev; 122617e05217SPhilipp Zabel struct device_node *of_node; 122717e05217SPhilipp Zabel 122817e05217SPhilipp Zabel /* Associate subdevice with the corresponding port node */ 122917e05217SPhilipp Zabel of_node = of_graph_get_port_by_id(dev->of_node, i); 123017e05217SPhilipp Zabel if (!of_node) { 123117e05217SPhilipp Zabel dev_info(dev, 12324bf99144SRob Herring "no port@%d node in %pOF, not using %s%d\n", 12334bf99144SRob Herring i, dev->of_node, 123417e05217SPhilipp Zabel (i / 2) ? "DI" : "CSI", i % 2); 123517e05217SPhilipp Zabel continue; 123617e05217SPhilipp Zabel } 123739b9004dSPhilipp Zabel 1238304e6be6SPhilipp Zabel pdev = platform_device_alloc(reg->name, id++); 1239304e6be6SPhilipp Zabel if (!pdev) { 1240304e6be6SPhilipp Zabel ret = -ENOMEM; 1241304e6be6SPhilipp Zabel goto err_register; 1242304e6be6SPhilipp Zabel } 124339b9004dSPhilipp Zabel 1244304e6be6SPhilipp Zabel pdev->dev.parent = dev; 1245304e6be6SPhilipp Zabel 1246310944d1SPhilipp Zabel reg->pdata.of_node = of_node; 1247304e6be6SPhilipp Zabel ret = platform_device_add_data(pdev, ®->pdata, 1248304e6be6SPhilipp Zabel sizeof(reg->pdata)); 1249304e6be6SPhilipp Zabel if (!ret) 1250304e6be6SPhilipp Zabel ret = platform_device_add(pdev); 1251304e6be6SPhilipp Zabel if (ret) { 1252304e6be6SPhilipp Zabel platform_device_put(pdev); 125339b9004dSPhilipp Zabel goto err_register; 125439b9004dSPhilipp Zabel } 1255e4946cdcSAxel Lin } 125639b9004dSPhilipp Zabel 125739b9004dSPhilipp Zabel return 0; 125839b9004dSPhilipp Zabel 125939b9004dSPhilipp Zabel err_register: 126039b9004dSPhilipp Zabel platform_device_unregister_children(to_platform_device(dev)); 126139b9004dSPhilipp Zabel 126239b9004dSPhilipp Zabel return ret; 126339b9004dSPhilipp Zabel } 126439b9004dSPhilipp Zabel 126539b9004dSPhilipp Zabel 126639b9004dSPhilipp Zabel static int ipu_irq_init(struct ipu_soc *ipu) 126739b9004dSPhilipp Zabel { 126839b9004dSPhilipp Zabel struct irq_chip_generic *gc; 126939b9004dSPhilipp Zabel struct irq_chip_type *ct; 127039b9004dSPhilipp Zabel unsigned long unused[IPU_NUM_IRQS / 32] = { 127139b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 127239b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 127339b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 127439b9004dSPhilipp Zabel 0x4077ffff, 0xffe7e1fd, 127539b9004dSPhilipp Zabel 0x23fffffe, 0x8880fff0, 127639b9004dSPhilipp Zabel 0xf98fe7d0, 0xfff81fff, 127739b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 127839b9004dSPhilipp Zabel 0x00000000, 127939b9004dSPhilipp Zabel }; 128039b9004dSPhilipp Zabel int ret, i; 128139b9004dSPhilipp Zabel 128239b9004dSPhilipp Zabel ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS, 128339b9004dSPhilipp Zabel &irq_generic_chip_ops, ipu); 128439b9004dSPhilipp Zabel if (!ipu->domain) { 128539b9004dSPhilipp Zabel dev_err(ipu->dev, "failed to add irq domain\n"); 128639b9004dSPhilipp Zabel return -ENODEV; 128739b9004dSPhilipp Zabel } 128839b9004dSPhilipp Zabel 128939b9004dSPhilipp Zabel ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU", 1290ca0141deSRob Herring handle_level_irq, 0, 0, 0); 129139b9004dSPhilipp Zabel if (ret < 0) { 129239b9004dSPhilipp Zabel dev_err(ipu->dev, "failed to alloc generic irq chips\n"); 129339b9004dSPhilipp Zabel irq_domain_remove(ipu->domain); 129439b9004dSPhilipp Zabel return ret; 129539b9004dSPhilipp Zabel } 129639b9004dSPhilipp Zabel 1297a92d8145SPhilipp Zabel /* Mask and clear all interrupts */ 1298a92d8145SPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i += 32) { 1299510e6426SRussell King ipu_cm_write(ipu, 0, IPU_INT_CTRL(i / 32)); 1300a92d8145SPhilipp Zabel ipu_cm_write(ipu, ~unused[i / 32], IPU_INT_STAT(i / 32)); 1301a92d8145SPhilipp Zabel } 1302510e6426SRussell King 130339b9004dSPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i += 32) { 130439b9004dSPhilipp Zabel gc = irq_get_domain_generic_chip(ipu->domain, i); 130539b9004dSPhilipp Zabel gc->reg_base = ipu->cm_reg; 130639b9004dSPhilipp Zabel gc->unused = unused[i / 32]; 130739b9004dSPhilipp Zabel ct = gc->chip_types; 130839b9004dSPhilipp Zabel ct->chip.irq_ack = irq_gc_ack_set_bit; 130939b9004dSPhilipp Zabel ct->chip.irq_mask = irq_gc_mask_clr_bit; 131039b9004dSPhilipp Zabel ct->chip.irq_unmask = irq_gc_mask_set_bit; 131139b9004dSPhilipp Zabel ct->regs.ack = IPU_INT_STAT(i / 32); 131239b9004dSPhilipp Zabel ct->regs.mask = IPU_INT_CTRL(i / 32); 131339b9004dSPhilipp Zabel } 131439b9004dSPhilipp Zabel 131586f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu); 131686f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler, 131786f5e733SRussell King ipu); 131839b9004dSPhilipp Zabel 131939b9004dSPhilipp Zabel return 0; 132039b9004dSPhilipp Zabel } 132139b9004dSPhilipp Zabel 132239b9004dSPhilipp Zabel static void ipu_irq_exit(struct ipu_soc *ipu) 132339b9004dSPhilipp Zabel { 132439b9004dSPhilipp Zabel int i, irq; 132539b9004dSPhilipp Zabel 132686f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL); 132786f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL); 132839b9004dSPhilipp Zabel 132939b9004dSPhilipp Zabel /* TODO: remove irq_domain_generic_chips */ 133039b9004dSPhilipp Zabel 133139b9004dSPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i++) { 133239b9004dSPhilipp Zabel irq = irq_linear_revmap(ipu->domain, i); 133339b9004dSPhilipp Zabel if (irq) 133439b9004dSPhilipp Zabel irq_dispose_mapping(irq); 133539b9004dSPhilipp Zabel } 133639b9004dSPhilipp Zabel 133739b9004dSPhilipp Zabel irq_domain_remove(ipu->domain); 133839b9004dSPhilipp Zabel } 133939b9004dSPhilipp Zabel 13403feb049fSSteve Longerbeam void ipu_dump(struct ipu_soc *ipu) 13413feb049fSSteve Longerbeam { 13423feb049fSSteve Longerbeam int i; 13433feb049fSSteve Longerbeam 13443feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n", 13453feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_CONF)); 13463feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n", 13473feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CONF)); 13483feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n", 13493feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_EN(0))); 13503feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n", 13513feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_EN(32))); 13523feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n", 13533feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_PRI(0))); 13543feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n", 13553feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_PRI(32))); 13563feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n", 13573feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_BAND_EN(0))); 13583feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n", 13593feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_BAND_EN(32))); 13603feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n", 13613feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0))); 13623feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n", 13633feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32))); 13643feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n", 13653feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_PROC_FLOW1)); 13663feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n", 13673feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_PROC_FLOW2)); 13683feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n", 13693feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_PROC_FLOW3)); 13703feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n", 13713feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_DISP_FLOW1)); 13723feb049fSSteve Longerbeam for (i = 0; i < 15; i++) 13733feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i, 13743feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_INT_CTRL(i))); 13753feb049fSSteve Longerbeam } 13763feb049fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_dump); 13773feb049fSSteve Longerbeam 137839b9004dSPhilipp Zabel static int ipu_probe(struct platform_device *pdev) 137939b9004dSPhilipp Zabel { 1380572a7615SSteve Longerbeam struct device_node *np = pdev->dev.of_node; 138139b9004dSPhilipp Zabel struct ipu_soc *ipu; 138239b9004dSPhilipp Zabel struct resource *res; 138339b9004dSPhilipp Zabel unsigned long ipu_base; 138493adc8b5SPhilipp Zabel int ret, irq_sync, irq_err; 138539b9004dSPhilipp Zabel const struct ipu_devtype *devtype; 138639b9004dSPhilipp Zabel 1387e92e4478SLABBE Corentin devtype = of_device_get_match_data(&pdev->dev); 1388e92e4478SLABBE Corentin if (!devtype) 1389e92e4478SLABBE Corentin return -EINVAL; 139039b9004dSPhilipp Zabel 139139b9004dSPhilipp Zabel irq_sync = platform_get_irq(pdev, 0); 139239b9004dSPhilipp Zabel irq_err = platform_get_irq(pdev, 1); 139339b9004dSPhilipp Zabel res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 139439b9004dSPhilipp Zabel 139539b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n", 139639b9004dSPhilipp Zabel irq_sync, irq_err); 139739b9004dSPhilipp Zabel 139839b9004dSPhilipp Zabel if (!res || irq_sync < 0 || irq_err < 0) 139939b9004dSPhilipp Zabel return -ENODEV; 140039b9004dSPhilipp Zabel 140139b9004dSPhilipp Zabel ipu_base = res->start; 140239b9004dSPhilipp Zabel 140339b9004dSPhilipp Zabel ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL); 140439b9004dSPhilipp Zabel if (!ipu) 140539b9004dSPhilipp Zabel return -ENODEV; 140639b9004dSPhilipp Zabel 140792681fe7SLucas Stach ipu->id = of_alias_get_id(np, "ipu"); 140892681fe7SLucas Stach 140930310c83SLucas Stach if (of_device_is_compatible(np, "fsl,imx6qp-ipu") && 141030310c83SLucas Stach IS_ENABLED(CONFIG_DRM)) { 141192681fe7SLucas Stach ipu->prg_priv = ipu_prg_lookup_by_phandle(&pdev->dev, 141292681fe7SLucas Stach "fsl,prg", ipu->id); 141392681fe7SLucas Stach if (!ipu->prg_priv) 141492681fe7SLucas Stach return -EPROBE_DEFER; 141592681fe7SLucas Stach } 141692681fe7SLucas Stach 141739b9004dSPhilipp Zabel ipu->devtype = devtype; 141839b9004dSPhilipp Zabel ipu->ipu_type = devtype->type; 141939b9004dSPhilipp Zabel 142039b9004dSPhilipp Zabel spin_lock_init(&ipu->lock); 142139b9004dSPhilipp Zabel mutex_init(&ipu->channel_lock); 142293adc8b5SPhilipp Zabel INIT_LIST_HEAD(&ipu->channels); 142339b9004dSPhilipp Zabel 142439b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n", 142539b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs); 142639b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "idmac: 0x%08lx\n", 142739b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS); 142839b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n", 142939b9004dSPhilipp Zabel ipu_base + devtype->cpmem_ofs); 14302ffd48f2SSteve Longerbeam dev_dbg(&pdev->dev, "csi0: 0x%08lx\n", 14312ffd48f2SSteve Longerbeam ipu_base + devtype->csi0_ofs); 14322ffd48f2SSteve Longerbeam dev_dbg(&pdev->dev, "csi1: 0x%08lx\n", 14332ffd48f2SSteve Longerbeam ipu_base + devtype->csi1_ofs); 14341aa8ea0dSSteve Longerbeam dev_dbg(&pdev->dev, "ic: 0x%08lx\n", 14351aa8ea0dSSteve Longerbeam ipu_base + devtype->ic_ofs); 143639b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "disp0: 0x%08lx\n", 143739b9004dSPhilipp Zabel ipu_base + devtype->disp0_ofs); 143839b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "disp1: 0x%08lx\n", 143939b9004dSPhilipp Zabel ipu_base + devtype->disp1_ofs); 144039b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "srm: 0x%08lx\n", 144139b9004dSPhilipp Zabel ipu_base + devtype->srm_ofs); 144239b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "tpm: 0x%08lx\n", 144339b9004dSPhilipp Zabel ipu_base + devtype->tpm_ofs); 144439b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "dc: 0x%08lx\n", 144539b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS); 144639b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "ic: 0x%08lx\n", 144739b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS); 144839b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n", 144939b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS); 145039b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "vdi: 0x%08lx\n", 145139b9004dSPhilipp Zabel ipu_base + devtype->vdi_ofs); 145239b9004dSPhilipp Zabel 145339b9004dSPhilipp Zabel ipu->cm_reg = devm_ioremap(&pdev->dev, 145439b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs, PAGE_SIZE); 145539b9004dSPhilipp Zabel ipu->idmac_reg = devm_ioremap(&pdev->dev, 145639b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS, 145739b9004dSPhilipp Zabel PAGE_SIZE); 145839b9004dSPhilipp Zabel 14597d2691daSSteve Longerbeam if (!ipu->cm_reg || !ipu->idmac_reg) 146039b9004dSPhilipp Zabel return -ENOMEM; 146139b9004dSPhilipp Zabel 146239b9004dSPhilipp Zabel ipu->clk = devm_clk_get(&pdev->dev, "bus"); 146339b9004dSPhilipp Zabel if (IS_ERR(ipu->clk)) { 146439b9004dSPhilipp Zabel ret = PTR_ERR(ipu->clk); 146539b9004dSPhilipp Zabel dev_err(&pdev->dev, "clk_get failed with %d", ret); 146639b9004dSPhilipp Zabel return ret; 146739b9004dSPhilipp Zabel } 146839b9004dSPhilipp Zabel 146939b9004dSPhilipp Zabel platform_set_drvdata(pdev, ipu); 147039b9004dSPhilipp Zabel 147139b9004dSPhilipp Zabel ret = clk_prepare_enable(ipu->clk); 147239b9004dSPhilipp Zabel if (ret) { 147339b9004dSPhilipp Zabel dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); 147439b9004dSPhilipp Zabel return ret; 147539b9004dSPhilipp Zabel } 147639b9004dSPhilipp Zabel 147739b9004dSPhilipp Zabel ipu->dev = &pdev->dev; 147839b9004dSPhilipp Zabel ipu->irq_sync = irq_sync; 147939b9004dSPhilipp Zabel ipu->irq_err = irq_err; 148039b9004dSPhilipp Zabel 148139b9004dSPhilipp Zabel ret = device_reset(&pdev->dev); 148239b9004dSPhilipp Zabel if (ret) { 148339b9004dSPhilipp Zabel dev_err(&pdev->dev, "failed to reset: %d\n", ret); 148439b9004dSPhilipp Zabel goto out_failed_reset; 148539b9004dSPhilipp Zabel } 148639b9004dSPhilipp Zabel ret = ipu_memory_reset(ipu); 148739b9004dSPhilipp Zabel if (ret) 148839b9004dSPhilipp Zabel goto out_failed_reset; 148939b9004dSPhilipp Zabel 1490596a65d1SDavid Jander ret = ipu_irq_init(ipu); 1491596a65d1SDavid Jander if (ret) 1492596a65d1SDavid Jander goto out_failed_irq; 1493596a65d1SDavid Jander 149439b9004dSPhilipp Zabel /* Set MCU_T to divide MCU access window into 2 */ 149539b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), 149639b9004dSPhilipp Zabel IPU_DISP_GEN); 149739b9004dSPhilipp Zabel 149839b9004dSPhilipp Zabel ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk); 149939b9004dSPhilipp Zabel if (ret) 150039b9004dSPhilipp Zabel goto failed_submodules_init; 150139b9004dSPhilipp Zabel 1502d6ca8ca7SPhilipp Zabel ret = ipu_add_client_devices(ipu, ipu_base); 150339b9004dSPhilipp Zabel if (ret) { 150439b9004dSPhilipp Zabel dev_err(&pdev->dev, "adding client devices failed with %d\n", 150539b9004dSPhilipp Zabel ret); 150639b9004dSPhilipp Zabel goto failed_add_clients; 150739b9004dSPhilipp Zabel } 150839b9004dSPhilipp Zabel 150939b9004dSPhilipp Zabel dev_info(&pdev->dev, "%s probed\n", devtype->name); 151039b9004dSPhilipp Zabel 151139b9004dSPhilipp Zabel return 0; 151239b9004dSPhilipp Zabel 151339b9004dSPhilipp Zabel failed_add_clients: 151439b9004dSPhilipp Zabel ipu_submodules_exit(ipu); 151539b9004dSPhilipp Zabel failed_submodules_init: 151639b9004dSPhilipp Zabel ipu_irq_exit(ipu); 151739b9004dSPhilipp Zabel out_failed_irq: 1518596a65d1SDavid Jander out_failed_reset: 151939b9004dSPhilipp Zabel clk_disable_unprepare(ipu->clk); 152039b9004dSPhilipp Zabel return ret; 152139b9004dSPhilipp Zabel } 152239b9004dSPhilipp Zabel 152339b9004dSPhilipp Zabel static int ipu_remove(struct platform_device *pdev) 152439b9004dSPhilipp Zabel { 152539b9004dSPhilipp Zabel struct ipu_soc *ipu = platform_get_drvdata(pdev); 152639b9004dSPhilipp Zabel 152739b9004dSPhilipp Zabel platform_device_unregister_children(pdev); 152839b9004dSPhilipp Zabel ipu_submodules_exit(ipu); 152939b9004dSPhilipp Zabel ipu_irq_exit(ipu); 153039b9004dSPhilipp Zabel 153139b9004dSPhilipp Zabel clk_disable_unprepare(ipu->clk); 153239b9004dSPhilipp Zabel 153339b9004dSPhilipp Zabel return 0; 153439b9004dSPhilipp Zabel } 153539b9004dSPhilipp Zabel 153639b9004dSPhilipp Zabel static struct platform_driver imx_ipu_driver = { 153739b9004dSPhilipp Zabel .driver = { 153839b9004dSPhilipp Zabel .name = "imx-ipuv3", 153939b9004dSPhilipp Zabel .of_match_table = imx_ipu_dt_ids, 154039b9004dSPhilipp Zabel }, 154139b9004dSPhilipp Zabel .probe = ipu_probe, 154239b9004dSPhilipp Zabel .remove = ipu_remove, 154339b9004dSPhilipp Zabel }; 154439b9004dSPhilipp Zabel 1545d2a34232SLucas Stach static struct platform_driver * const drivers[] = { 154630310c83SLucas Stach #if IS_ENABLED(CONFIG_DRM) 1547d2a34232SLucas Stach &ipu_pre_drv, 1548ea9c2605SLucas Stach &ipu_prg_drv, 154930310c83SLucas Stach #endif 1550d2a34232SLucas Stach &imx_ipu_driver, 1551d2a34232SLucas Stach }; 1552d2a34232SLucas Stach 1553d2a34232SLucas Stach static int __init imx_ipu_init(void) 1554d2a34232SLucas Stach { 1555d2a34232SLucas Stach return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); 1556d2a34232SLucas Stach } 1557d2a34232SLucas Stach module_init(imx_ipu_init); 1558d2a34232SLucas Stach 1559d2a34232SLucas Stach static void __exit imx_ipu_exit(void) 1560d2a34232SLucas Stach { 1561d2a34232SLucas Stach platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); 1562d2a34232SLucas Stach } 1563d2a34232SLucas Stach module_exit(imx_ipu_exit); 156439b9004dSPhilipp Zabel 156539b9004dSPhilipp Zabel MODULE_ALIAS("platform:imx-ipuv3"); 156639b9004dSPhilipp Zabel MODULE_DESCRIPTION("i.MX IPU v3 driver"); 156739b9004dSPhilipp Zabel MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); 156839b9004dSPhilipp Zabel MODULE_LICENSE("GPL"); 1569