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> 3139b9004dSPhilipp Zabel 3239b9004dSPhilipp Zabel #include <drm/drm_fourcc.h> 3339b9004dSPhilipp Zabel 3439b9004dSPhilipp Zabel #include <video/imx-ipu-v3.h> 3539b9004dSPhilipp Zabel #include "ipu-prv.h" 3639b9004dSPhilipp Zabel 3739b9004dSPhilipp Zabel static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset) 3839b9004dSPhilipp Zabel { 3939b9004dSPhilipp Zabel return readl(ipu->cm_reg + offset); 4039b9004dSPhilipp Zabel } 4139b9004dSPhilipp Zabel 4239b9004dSPhilipp Zabel static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset) 4339b9004dSPhilipp Zabel { 4439b9004dSPhilipp Zabel writel(value, ipu->cm_reg + offset); 4539b9004dSPhilipp Zabel } 4639b9004dSPhilipp Zabel 4739b9004dSPhilipp Zabel void ipu_srm_dp_sync_update(struct ipu_soc *ipu) 4839b9004dSPhilipp Zabel { 4939b9004dSPhilipp Zabel u32 val; 5039b9004dSPhilipp Zabel 5139b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_SRM_PRI2); 5239b9004dSPhilipp Zabel val |= 0x8; 5339b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_SRM_PRI2); 5439b9004dSPhilipp Zabel } 5539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_srm_dp_sync_update); 5639b9004dSPhilipp Zabel 5739b9004dSPhilipp Zabel enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc) 5839b9004dSPhilipp Zabel { 5939b9004dSPhilipp Zabel switch (drm_fourcc) { 6039b9004dSPhilipp Zabel case DRM_FORMAT_RGB565: 6139b9004dSPhilipp Zabel case DRM_FORMAT_BGR565: 6239b9004dSPhilipp Zabel case DRM_FORMAT_RGB888: 6339b9004dSPhilipp Zabel case DRM_FORMAT_BGR888: 6439b9004dSPhilipp Zabel case DRM_FORMAT_XRGB8888: 6539b9004dSPhilipp Zabel case DRM_FORMAT_XBGR8888: 6639b9004dSPhilipp Zabel case DRM_FORMAT_RGBX8888: 6739b9004dSPhilipp Zabel case DRM_FORMAT_BGRX8888: 6839b9004dSPhilipp Zabel case DRM_FORMAT_ARGB8888: 6939b9004dSPhilipp Zabel case DRM_FORMAT_ABGR8888: 7039b9004dSPhilipp Zabel case DRM_FORMAT_RGBA8888: 7139b9004dSPhilipp Zabel case DRM_FORMAT_BGRA8888: 7239b9004dSPhilipp Zabel return IPUV3_COLORSPACE_RGB; 7339b9004dSPhilipp Zabel case DRM_FORMAT_YUYV: 7439b9004dSPhilipp Zabel case DRM_FORMAT_UYVY: 7539b9004dSPhilipp Zabel case DRM_FORMAT_YUV420: 7639b9004dSPhilipp Zabel case DRM_FORMAT_YVU420: 779a34cef0SSteve Longerbeam case DRM_FORMAT_YUV422: 789a34cef0SSteve Longerbeam case DRM_FORMAT_YVU422: 799a34cef0SSteve Longerbeam case DRM_FORMAT_NV12: 809a34cef0SSteve Longerbeam case DRM_FORMAT_NV21: 819a34cef0SSteve Longerbeam case DRM_FORMAT_NV16: 829a34cef0SSteve Longerbeam case DRM_FORMAT_NV61: 8339b9004dSPhilipp Zabel return IPUV3_COLORSPACE_YUV; 8439b9004dSPhilipp Zabel default: 8539b9004dSPhilipp Zabel return IPUV3_COLORSPACE_UNKNOWN; 8639b9004dSPhilipp Zabel } 8739b9004dSPhilipp Zabel } 8839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace); 8939b9004dSPhilipp Zabel 9039b9004dSPhilipp Zabel enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat) 9139b9004dSPhilipp Zabel { 9239b9004dSPhilipp Zabel switch (pixelformat) { 9339b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUV420: 9439b9004dSPhilipp Zabel case V4L2_PIX_FMT_YVU420: 959a34cef0SSteve Longerbeam case V4L2_PIX_FMT_YUV422P: 9639b9004dSPhilipp Zabel case V4L2_PIX_FMT_UYVY: 9739b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUYV: 989a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV12: 999a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV21: 1009a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV16: 1019a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV61: 10239b9004dSPhilipp Zabel return IPUV3_COLORSPACE_YUV; 10339b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB32: 10439b9004dSPhilipp Zabel case V4L2_PIX_FMT_BGR32: 10539b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB24: 10639b9004dSPhilipp Zabel case V4L2_PIX_FMT_BGR24: 10739b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB565: 10839b9004dSPhilipp Zabel return IPUV3_COLORSPACE_RGB; 10939b9004dSPhilipp Zabel default: 11039b9004dSPhilipp Zabel return IPUV3_COLORSPACE_UNKNOWN; 11139b9004dSPhilipp Zabel } 11239b9004dSPhilipp Zabel } 11339b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace); 11439b9004dSPhilipp Zabel 1154cea940dSSteve Longerbeam bool ipu_pixelformat_is_planar(u32 pixelformat) 1164cea940dSSteve Longerbeam { 1174cea940dSSteve Longerbeam switch (pixelformat) { 1184cea940dSSteve Longerbeam case V4L2_PIX_FMT_YUV420: 1194cea940dSSteve Longerbeam case V4L2_PIX_FMT_YVU420: 1209a34cef0SSteve Longerbeam case V4L2_PIX_FMT_YUV422P: 1219a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV12: 1229a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV21: 1239a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV16: 1249a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV61: 1254cea940dSSteve Longerbeam return true; 1264cea940dSSteve Longerbeam } 1274cea940dSSteve Longerbeam 1284cea940dSSteve Longerbeam return false; 1294cea940dSSteve Longerbeam } 1304cea940dSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar); 1314cea940dSSteve Longerbeam 132ae0e9708SSteve Longerbeam enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code) 133ae0e9708SSteve Longerbeam { 134ae0e9708SSteve Longerbeam switch (mbus_code & 0xf000) { 135ae0e9708SSteve Longerbeam case 0x1000: 136ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_RGB; 137ae0e9708SSteve Longerbeam case 0x2000: 138ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_YUV; 139ae0e9708SSteve Longerbeam default: 140ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_UNKNOWN; 141ae0e9708SSteve Longerbeam } 142ae0e9708SSteve Longerbeam } 143ae0e9708SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace); 144ae0e9708SSteve Longerbeam 1456930afdcSSteve Longerbeam int ipu_stride_to_bytes(u32 pixel_stride, u32 pixelformat) 1466930afdcSSteve Longerbeam { 1476930afdcSSteve Longerbeam switch (pixelformat) { 1486930afdcSSteve Longerbeam case V4L2_PIX_FMT_YUV420: 1496930afdcSSteve Longerbeam case V4L2_PIX_FMT_YVU420: 1509a34cef0SSteve Longerbeam case V4L2_PIX_FMT_YUV422P: 1519a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV12: 1529a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV21: 1539a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV16: 1549a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV61: 1556930afdcSSteve Longerbeam /* 1566930afdcSSteve Longerbeam * for the planar YUV formats, the stride passed to 1576930afdcSSteve Longerbeam * cpmem must be the stride in bytes of the Y plane. 1586930afdcSSteve Longerbeam * And all the planar YUV formats have an 8-bit 1596930afdcSSteve Longerbeam * Y component. 1606930afdcSSteve Longerbeam */ 1616930afdcSSteve Longerbeam return (8 * pixel_stride) >> 3; 1626930afdcSSteve Longerbeam case V4L2_PIX_FMT_RGB565: 1636930afdcSSteve Longerbeam case V4L2_PIX_FMT_YUYV: 1646930afdcSSteve Longerbeam case V4L2_PIX_FMT_UYVY: 1656930afdcSSteve Longerbeam return (16 * pixel_stride) >> 3; 1666930afdcSSteve Longerbeam case V4L2_PIX_FMT_BGR24: 1676930afdcSSteve Longerbeam case V4L2_PIX_FMT_RGB24: 1686930afdcSSteve Longerbeam return (24 * pixel_stride) >> 3; 1696930afdcSSteve Longerbeam case V4L2_PIX_FMT_BGR32: 1706930afdcSSteve Longerbeam case V4L2_PIX_FMT_RGB32: 1716930afdcSSteve Longerbeam return (32 * pixel_stride) >> 3; 1726930afdcSSteve Longerbeam default: 1736930afdcSSteve Longerbeam break; 1746930afdcSSteve Longerbeam } 1756930afdcSSteve Longerbeam 1766930afdcSSteve Longerbeam return -EINVAL; 1776930afdcSSteve Longerbeam } 1786930afdcSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_stride_to_bytes); 1796930afdcSSteve Longerbeam 180f835f386SSteve Longerbeam int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees, 181f835f386SSteve Longerbeam bool hflip, bool vflip) 182f835f386SSteve Longerbeam { 183f835f386SSteve Longerbeam u32 r90, vf, hf; 184f835f386SSteve Longerbeam 185f835f386SSteve Longerbeam switch (degrees) { 186f835f386SSteve Longerbeam case 0: 187f835f386SSteve Longerbeam vf = hf = r90 = 0; 188f835f386SSteve Longerbeam break; 189f835f386SSteve Longerbeam case 90: 190f835f386SSteve Longerbeam vf = hf = 0; 191f835f386SSteve Longerbeam r90 = 1; 192f835f386SSteve Longerbeam break; 193f835f386SSteve Longerbeam case 180: 194f835f386SSteve Longerbeam vf = hf = 1; 195f835f386SSteve Longerbeam r90 = 0; 196f835f386SSteve Longerbeam break; 197f835f386SSteve Longerbeam case 270: 198f835f386SSteve Longerbeam vf = hf = r90 = 1; 199f835f386SSteve Longerbeam break; 200f835f386SSteve Longerbeam default: 201f835f386SSteve Longerbeam return -EINVAL; 202f835f386SSteve Longerbeam } 203f835f386SSteve Longerbeam 204f835f386SSteve Longerbeam hf ^= (u32)hflip; 205f835f386SSteve Longerbeam vf ^= (u32)vflip; 206f835f386SSteve Longerbeam 207f835f386SSteve Longerbeam *mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf); 208f835f386SSteve Longerbeam return 0; 209f835f386SSteve Longerbeam } 210f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode); 211f835f386SSteve Longerbeam 212f835f386SSteve Longerbeam int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode, 213f835f386SSteve Longerbeam bool hflip, bool vflip) 214f835f386SSteve Longerbeam { 215f835f386SSteve Longerbeam u32 r90, vf, hf; 216f835f386SSteve Longerbeam 217f835f386SSteve Longerbeam r90 = ((u32)mode >> 2) & 0x1; 218f835f386SSteve Longerbeam hf = ((u32)mode >> 1) & 0x1; 219f835f386SSteve Longerbeam vf = ((u32)mode >> 0) & 0x1; 220f835f386SSteve Longerbeam hf ^= (u32)hflip; 221f835f386SSteve Longerbeam vf ^= (u32)vflip; 222f835f386SSteve Longerbeam 223f835f386SSteve Longerbeam switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) { 224f835f386SSteve Longerbeam case IPU_ROTATE_NONE: 225f835f386SSteve Longerbeam *degrees = 0; 226f835f386SSteve Longerbeam break; 227f835f386SSteve Longerbeam case IPU_ROTATE_90_RIGHT: 228f835f386SSteve Longerbeam *degrees = 90; 229f835f386SSteve Longerbeam break; 230f835f386SSteve Longerbeam case IPU_ROTATE_180: 231f835f386SSteve Longerbeam *degrees = 180; 232f835f386SSteve Longerbeam break; 233f835f386SSteve Longerbeam case IPU_ROTATE_90_LEFT: 234f835f386SSteve Longerbeam *degrees = 270; 235f835f386SSteve Longerbeam break; 236f835f386SSteve Longerbeam default: 237f835f386SSteve Longerbeam return -EINVAL; 238f835f386SSteve Longerbeam } 239f835f386SSteve Longerbeam 240f835f386SSteve Longerbeam return 0; 241f835f386SSteve Longerbeam } 242f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees); 243f835f386SSteve Longerbeam 24439b9004dSPhilipp Zabel struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num) 24539b9004dSPhilipp Zabel { 24639b9004dSPhilipp Zabel struct ipuv3_channel *channel; 24739b9004dSPhilipp Zabel 24839b9004dSPhilipp Zabel dev_dbg(ipu->dev, "%s %d\n", __func__, num); 24939b9004dSPhilipp Zabel 25039b9004dSPhilipp Zabel if (num > 63) 25139b9004dSPhilipp Zabel return ERR_PTR(-ENODEV); 25239b9004dSPhilipp Zabel 25339b9004dSPhilipp Zabel mutex_lock(&ipu->channel_lock); 25439b9004dSPhilipp Zabel 25539b9004dSPhilipp Zabel channel = &ipu->channel[num]; 25639b9004dSPhilipp Zabel 25739b9004dSPhilipp Zabel if (channel->busy) { 25839b9004dSPhilipp Zabel channel = ERR_PTR(-EBUSY); 25939b9004dSPhilipp Zabel goto out; 26039b9004dSPhilipp Zabel } 26139b9004dSPhilipp Zabel 26239b9004dSPhilipp Zabel channel->busy = true; 26339b9004dSPhilipp Zabel channel->num = num; 26439b9004dSPhilipp Zabel 26539b9004dSPhilipp Zabel out: 26639b9004dSPhilipp Zabel mutex_unlock(&ipu->channel_lock); 26739b9004dSPhilipp Zabel 26839b9004dSPhilipp Zabel return channel; 26939b9004dSPhilipp Zabel } 27039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get); 27139b9004dSPhilipp Zabel 27239b9004dSPhilipp Zabel void ipu_idmac_put(struct ipuv3_channel *channel) 27339b9004dSPhilipp Zabel { 27439b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 27539b9004dSPhilipp Zabel 27639b9004dSPhilipp Zabel dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num); 27739b9004dSPhilipp Zabel 27839b9004dSPhilipp Zabel mutex_lock(&ipu->channel_lock); 27939b9004dSPhilipp Zabel 28039b9004dSPhilipp Zabel channel->busy = false; 28139b9004dSPhilipp Zabel 28239b9004dSPhilipp Zabel mutex_unlock(&ipu->channel_lock); 28339b9004dSPhilipp Zabel } 28439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_put); 28539b9004dSPhilipp Zabel 286aa52f578SSteve Longerbeam #define idma_mask(ch) (1 << ((ch) & 0x1f)) 28739b9004dSPhilipp Zabel 288e7268c69SSteve Longerbeam /* 289e7268c69SSteve Longerbeam * This is an undocumented feature, a write one to a channel bit in 290e7268c69SSteve Longerbeam * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's 291e7268c69SSteve Longerbeam * internal current buffer pointer so that transfers start from buffer 292e7268c69SSteve Longerbeam * 0 on the next channel enable (that's the theory anyway, the imx6 TRM 293e7268c69SSteve Longerbeam * only says these are read-only registers). This operation is required 294e7268c69SSteve Longerbeam * for channel linking to work correctly, for instance video capture 295e7268c69SSteve Longerbeam * pipelines that carry out image rotations will fail after the first 296e7268c69SSteve Longerbeam * streaming unless this function is called for each channel before 297e7268c69SSteve Longerbeam * re-enabling the channels. 298e7268c69SSteve Longerbeam */ 299e7268c69SSteve Longerbeam static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel) 300e7268c69SSteve Longerbeam { 301e7268c69SSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 302e7268c69SSteve Longerbeam unsigned int chno = channel->num; 303e7268c69SSteve Longerbeam 304e7268c69SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno)); 305e7268c69SSteve Longerbeam } 306e7268c69SSteve Longerbeam 30739b9004dSPhilipp Zabel void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel, 30839b9004dSPhilipp Zabel bool doublebuffer) 30939b9004dSPhilipp Zabel { 31039b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 31139b9004dSPhilipp Zabel unsigned long flags; 31239b9004dSPhilipp Zabel u32 reg; 31339b9004dSPhilipp Zabel 31439b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 31539b9004dSPhilipp Zabel 31639b9004dSPhilipp Zabel reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); 31739b9004dSPhilipp Zabel if (doublebuffer) 31839b9004dSPhilipp Zabel reg |= idma_mask(channel->num); 31939b9004dSPhilipp Zabel else 32039b9004dSPhilipp Zabel reg &= ~idma_mask(channel->num); 32139b9004dSPhilipp Zabel ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num)); 32239b9004dSPhilipp Zabel 323e7268c69SSteve Longerbeam __ipu_idmac_reset_current_buffer(channel); 324e7268c69SSteve Longerbeam 32539b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 32639b9004dSPhilipp Zabel } 32739b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer); 32839b9004dSPhilipp Zabel 3294fd1a07aSSteve Longerbeam static const struct { 3304fd1a07aSSteve Longerbeam int chnum; 3314fd1a07aSSteve Longerbeam u32 reg; 3324fd1a07aSSteve Longerbeam int shift; 3334fd1a07aSSteve Longerbeam } idmac_lock_en_info[] = { 3344fd1a07aSSteve Longerbeam { .chnum = 5, .reg = IDMAC_CH_LOCK_EN_1, .shift = 0, }, 3354fd1a07aSSteve Longerbeam { .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift = 2, }, 3364fd1a07aSSteve Longerbeam { .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift = 4, }, 3374fd1a07aSSteve Longerbeam { .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift = 6, }, 3384fd1a07aSSteve Longerbeam { .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift = 8, }, 3394fd1a07aSSteve Longerbeam { .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, }, 3404fd1a07aSSteve Longerbeam { .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, }, 3414fd1a07aSSteve Longerbeam { .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, }, 3424fd1a07aSSteve Longerbeam { .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, }, 3434fd1a07aSSteve Longerbeam { .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, }, 3444fd1a07aSSteve Longerbeam { .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, }, 3454fd1a07aSSteve Longerbeam { .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift = 0, }, 3464fd1a07aSSteve Longerbeam { .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift = 2, }, 3474fd1a07aSSteve Longerbeam { .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift = 4, }, 3484fd1a07aSSteve Longerbeam { .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift = 6, }, 3494fd1a07aSSteve Longerbeam { .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift = 8, }, 3504fd1a07aSSteve Longerbeam { .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, }, 3514fd1a07aSSteve Longerbeam }; 3524fd1a07aSSteve Longerbeam 3534fd1a07aSSteve Longerbeam int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts) 3544fd1a07aSSteve Longerbeam { 3554fd1a07aSSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 3564fd1a07aSSteve Longerbeam unsigned long flags; 3574fd1a07aSSteve Longerbeam u32 bursts, regval; 3584fd1a07aSSteve Longerbeam int i; 3594fd1a07aSSteve Longerbeam 3604fd1a07aSSteve Longerbeam switch (num_bursts) { 3614fd1a07aSSteve Longerbeam case 0: 3624fd1a07aSSteve Longerbeam case 1: 3634fd1a07aSSteve Longerbeam bursts = 0x00; /* locking disabled */ 3644fd1a07aSSteve Longerbeam break; 3654fd1a07aSSteve Longerbeam case 2: 3664fd1a07aSSteve Longerbeam bursts = 0x01; 3674fd1a07aSSteve Longerbeam break; 3684fd1a07aSSteve Longerbeam case 4: 3694fd1a07aSSteve Longerbeam bursts = 0x02; 3704fd1a07aSSteve Longerbeam break; 3714fd1a07aSSteve Longerbeam case 8: 3724fd1a07aSSteve Longerbeam bursts = 0x03; 3734fd1a07aSSteve Longerbeam break; 3744fd1a07aSSteve Longerbeam default: 3754fd1a07aSSteve Longerbeam return -EINVAL; 3764fd1a07aSSteve Longerbeam } 3774fd1a07aSSteve Longerbeam 3784fd1a07aSSteve Longerbeam for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) { 3794fd1a07aSSteve Longerbeam if (channel->num == idmac_lock_en_info[i].chnum) 3804fd1a07aSSteve Longerbeam break; 3814fd1a07aSSteve Longerbeam } 3824fd1a07aSSteve Longerbeam if (i >= ARRAY_SIZE(idmac_lock_en_info)) 3834fd1a07aSSteve Longerbeam return -EINVAL; 3844fd1a07aSSteve Longerbeam 3854fd1a07aSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 3864fd1a07aSSteve Longerbeam 3874fd1a07aSSteve Longerbeam regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg); 3884fd1a07aSSteve Longerbeam regval &= ~(0x03 << idmac_lock_en_info[i].shift); 3894fd1a07aSSteve Longerbeam regval |= (bursts << idmac_lock_en_info[i].shift); 3904fd1a07aSSteve Longerbeam ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg); 3914fd1a07aSSteve Longerbeam 3924fd1a07aSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 3934fd1a07aSSteve Longerbeam 3944fd1a07aSSteve Longerbeam return 0; 3954fd1a07aSSteve Longerbeam } 3964fd1a07aSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable); 3974fd1a07aSSteve Longerbeam 39839b9004dSPhilipp Zabel int ipu_module_enable(struct ipu_soc *ipu, u32 mask) 39939b9004dSPhilipp Zabel { 40039b9004dSPhilipp Zabel unsigned long lock_flags; 40139b9004dSPhilipp Zabel u32 val; 40239b9004dSPhilipp Zabel 40339b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, lock_flags); 40439b9004dSPhilipp Zabel 40539b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_DISP_GEN); 40639b9004dSPhilipp Zabel 40739b9004dSPhilipp Zabel if (mask & IPU_CONF_DI0_EN) 40839b9004dSPhilipp Zabel val |= IPU_DI0_COUNTER_RELEASE; 40939b9004dSPhilipp Zabel if (mask & IPU_CONF_DI1_EN) 41039b9004dSPhilipp Zabel val |= IPU_DI1_COUNTER_RELEASE; 41139b9004dSPhilipp Zabel 41239b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_DISP_GEN); 41339b9004dSPhilipp Zabel 41439b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CONF); 41539b9004dSPhilipp Zabel val |= mask; 41639b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CONF); 41739b9004dSPhilipp Zabel 41839b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, lock_flags); 41939b9004dSPhilipp Zabel 42039b9004dSPhilipp Zabel return 0; 42139b9004dSPhilipp Zabel } 42239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_enable); 42339b9004dSPhilipp Zabel 42439b9004dSPhilipp Zabel int ipu_module_disable(struct ipu_soc *ipu, u32 mask) 42539b9004dSPhilipp Zabel { 42639b9004dSPhilipp Zabel unsigned long lock_flags; 42739b9004dSPhilipp Zabel u32 val; 42839b9004dSPhilipp Zabel 42939b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, lock_flags); 43039b9004dSPhilipp Zabel 43139b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CONF); 43239b9004dSPhilipp Zabel val &= ~mask; 43339b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CONF); 43439b9004dSPhilipp Zabel 43539b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_DISP_GEN); 43639b9004dSPhilipp Zabel 43739b9004dSPhilipp Zabel if (mask & IPU_CONF_DI0_EN) 43839b9004dSPhilipp Zabel val &= ~IPU_DI0_COUNTER_RELEASE; 43939b9004dSPhilipp Zabel if (mask & IPU_CONF_DI1_EN) 44039b9004dSPhilipp Zabel val &= ~IPU_DI1_COUNTER_RELEASE; 44139b9004dSPhilipp Zabel 44239b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_DISP_GEN); 44339b9004dSPhilipp Zabel 44439b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, lock_flags); 44539b9004dSPhilipp Zabel 44639b9004dSPhilipp Zabel return 0; 44739b9004dSPhilipp Zabel } 44839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_disable); 44939b9004dSPhilipp Zabel 450e9046097SPhilipp Zabel int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel) 451e9046097SPhilipp Zabel { 452e9046097SPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 453e9046097SPhilipp Zabel unsigned int chno = channel->num; 454e9046097SPhilipp Zabel 455e9046097SPhilipp Zabel return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0; 456e9046097SPhilipp Zabel } 457e9046097SPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer); 458e9046097SPhilipp Zabel 459aa52f578SSteve Longerbeam bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num) 460aa52f578SSteve Longerbeam { 461aa52f578SSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 462aa52f578SSteve Longerbeam unsigned long flags; 463aa52f578SSteve Longerbeam u32 reg = 0; 464aa52f578SSteve Longerbeam 465aa52f578SSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 466aa52f578SSteve Longerbeam switch (buf_num) { 467aa52f578SSteve Longerbeam case 0: 468aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)); 469aa52f578SSteve Longerbeam break; 470aa52f578SSteve Longerbeam case 1: 471aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)); 472aa52f578SSteve Longerbeam break; 473aa52f578SSteve Longerbeam case 2: 474aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num)); 475aa52f578SSteve Longerbeam break; 476aa52f578SSteve Longerbeam } 477aa52f578SSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 478aa52f578SSteve Longerbeam 479aa52f578SSteve Longerbeam return ((reg & idma_mask(channel->num)) != 0); 480aa52f578SSteve Longerbeam } 481aa52f578SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready); 482aa52f578SSteve Longerbeam 48339b9004dSPhilipp Zabel void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num) 48439b9004dSPhilipp Zabel { 48539b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 48639b9004dSPhilipp Zabel unsigned int chno = channel->num; 48739b9004dSPhilipp Zabel unsigned long flags; 48839b9004dSPhilipp Zabel 48939b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 49039b9004dSPhilipp Zabel 49139b9004dSPhilipp Zabel /* Mark buffer as ready. */ 49239b9004dSPhilipp Zabel if (buf_num == 0) 49339b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); 49439b9004dSPhilipp Zabel else 49539b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); 49639b9004dSPhilipp Zabel 49739b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 49839b9004dSPhilipp Zabel } 49939b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer); 50039b9004dSPhilipp Zabel 501bce6f087SSteve Longerbeam void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num) 502bce6f087SSteve Longerbeam { 503bce6f087SSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 504bce6f087SSteve Longerbeam unsigned int chno = channel->num; 505bce6f087SSteve Longerbeam unsigned long flags; 506bce6f087SSteve Longerbeam 507bce6f087SSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 508bce6f087SSteve Longerbeam 509bce6f087SSteve Longerbeam ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */ 510bce6f087SSteve Longerbeam switch (buf_num) { 511bce6f087SSteve Longerbeam case 0: 512bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); 513bce6f087SSteve Longerbeam break; 514bce6f087SSteve Longerbeam case 1: 515bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); 516bce6f087SSteve Longerbeam break; 517bce6f087SSteve Longerbeam case 2: 518bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno)); 519bce6f087SSteve Longerbeam break; 520bce6f087SSteve Longerbeam default: 521bce6f087SSteve Longerbeam break; 522bce6f087SSteve Longerbeam } 523bce6f087SSteve Longerbeam ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ 524bce6f087SSteve Longerbeam 525bce6f087SSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 526bce6f087SSteve Longerbeam } 527bce6f087SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer); 528bce6f087SSteve Longerbeam 52939b9004dSPhilipp Zabel int ipu_idmac_enable_channel(struct ipuv3_channel *channel) 53039b9004dSPhilipp Zabel { 53139b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 53239b9004dSPhilipp Zabel u32 val; 53339b9004dSPhilipp Zabel unsigned long flags; 53439b9004dSPhilipp Zabel 53539b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 53639b9004dSPhilipp Zabel 53739b9004dSPhilipp Zabel val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); 53839b9004dSPhilipp Zabel val |= idma_mask(channel->num); 53939b9004dSPhilipp Zabel ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); 54039b9004dSPhilipp Zabel 54139b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 54239b9004dSPhilipp Zabel 54339b9004dSPhilipp Zabel return 0; 54439b9004dSPhilipp Zabel } 54539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel); 54639b9004dSPhilipp Zabel 547682b7c1cSLinus Torvalds bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno) 548682b7c1cSLinus Torvalds { 549682b7c1cSLinus Torvalds return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno)); 550682b7c1cSLinus Torvalds } 551682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy); 552682b7c1cSLinus Torvalds 55339b9004dSPhilipp Zabel int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) 55439b9004dSPhilipp Zabel { 55539b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 55639b9004dSPhilipp Zabel unsigned long timeout; 55739b9004dSPhilipp Zabel 55839b9004dSPhilipp Zabel timeout = jiffies + msecs_to_jiffies(ms); 55939b9004dSPhilipp Zabel while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) & 56039b9004dSPhilipp Zabel idma_mask(channel->num)) { 56139b9004dSPhilipp Zabel if (time_after(jiffies, timeout)) 56239b9004dSPhilipp Zabel return -ETIMEDOUT; 56339b9004dSPhilipp Zabel cpu_relax(); 56439b9004dSPhilipp Zabel } 56539b9004dSPhilipp Zabel 56639b9004dSPhilipp Zabel return 0; 56739b9004dSPhilipp Zabel } 56839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); 56939b9004dSPhilipp Zabel 570682b7c1cSLinus Torvalds int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms) 571682b7c1cSLinus Torvalds { 572682b7c1cSLinus Torvalds unsigned long timeout; 573682b7c1cSLinus Torvalds 574682b7c1cSLinus Torvalds timeout = jiffies + msecs_to_jiffies(ms); 575682b7c1cSLinus Torvalds ipu_cm_write(ipu, BIT(irq % 32), IPU_INT_STAT(irq / 32)); 576682b7c1cSLinus Torvalds while (!(ipu_cm_read(ipu, IPU_INT_STAT(irq / 32) & BIT(irq % 32)))) { 577682b7c1cSLinus Torvalds if (time_after(jiffies, timeout)) 578682b7c1cSLinus Torvalds return -ETIMEDOUT; 579682b7c1cSLinus Torvalds cpu_relax(); 580682b7c1cSLinus Torvalds } 581682b7c1cSLinus Torvalds 582682b7c1cSLinus Torvalds return 0; 583682b7c1cSLinus Torvalds } 584682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_wait_interrupt); 585682b7c1cSLinus Torvalds 58639b9004dSPhilipp Zabel int ipu_idmac_disable_channel(struct ipuv3_channel *channel) 58739b9004dSPhilipp Zabel { 58839b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 58939b9004dSPhilipp Zabel u32 val; 59039b9004dSPhilipp Zabel unsigned long flags; 59139b9004dSPhilipp Zabel 59239b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 59339b9004dSPhilipp Zabel 59439b9004dSPhilipp Zabel /* Disable DMA channel(s) */ 59539b9004dSPhilipp Zabel val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); 59639b9004dSPhilipp Zabel val &= ~idma_mask(channel->num); 59739b9004dSPhilipp Zabel ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); 59839b9004dSPhilipp Zabel 599e7268c69SSteve Longerbeam __ipu_idmac_reset_current_buffer(channel); 600e7268c69SSteve Longerbeam 60139b9004dSPhilipp Zabel /* Set channel buffers NOT to be ready */ 60239b9004dSPhilipp Zabel ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */ 60339b9004dSPhilipp Zabel 60439b9004dSPhilipp Zabel if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) & 60539b9004dSPhilipp Zabel idma_mask(channel->num)) { 60639b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(channel->num), 60739b9004dSPhilipp Zabel IPU_CHA_BUF0_RDY(channel->num)); 60839b9004dSPhilipp Zabel } 60939b9004dSPhilipp Zabel 61039b9004dSPhilipp Zabel if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) & 61139b9004dSPhilipp Zabel idma_mask(channel->num)) { 61239b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(channel->num), 61339b9004dSPhilipp Zabel IPU_CHA_BUF1_RDY(channel->num)); 61439b9004dSPhilipp Zabel } 61539b9004dSPhilipp Zabel 61639b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ 61739b9004dSPhilipp Zabel 61839b9004dSPhilipp Zabel /* Reset the double buffer */ 61939b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); 62039b9004dSPhilipp Zabel val &= ~idma_mask(channel->num); 62139b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num)); 62239b9004dSPhilipp Zabel 62339b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 62439b9004dSPhilipp Zabel 62539b9004dSPhilipp Zabel return 0; 62639b9004dSPhilipp Zabel } 62739b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel); 62839b9004dSPhilipp Zabel 6292bcf577eSSteve Longerbeam /* 6302bcf577eSSteve Longerbeam * The imx6 rev. D TRM says that enabling the WM feature will increase 6312bcf577eSSteve Longerbeam * a channel's priority. Refer to Table 36-8 Calculated priority value. 6322bcf577eSSteve Longerbeam * The sub-module that is the sink or source for the channel must enable 6332bcf577eSSteve Longerbeam * watermark signal for this to take effect (SMFC_WM for instance). 6342bcf577eSSteve Longerbeam */ 6352bcf577eSSteve Longerbeam void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable) 6362bcf577eSSteve Longerbeam { 6372bcf577eSSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 6382bcf577eSSteve Longerbeam unsigned long flags; 6392bcf577eSSteve Longerbeam u32 val; 6402bcf577eSSteve Longerbeam 6412bcf577eSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 6422bcf577eSSteve Longerbeam 6432bcf577eSSteve Longerbeam val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num)); 6442bcf577eSSteve Longerbeam if (enable) 6452bcf577eSSteve Longerbeam val |= 1 << (channel->num % 32); 6462bcf577eSSteve Longerbeam else 6472bcf577eSSteve Longerbeam val &= ~(1 << (channel->num % 32)); 6482bcf577eSSteve Longerbeam ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num)); 6492bcf577eSSteve Longerbeam 6502bcf577eSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 6512bcf577eSSteve Longerbeam } 6522bcf577eSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark); 6532bcf577eSSteve Longerbeam 65439b9004dSPhilipp Zabel static int ipu_memory_reset(struct ipu_soc *ipu) 65539b9004dSPhilipp Zabel { 65639b9004dSPhilipp Zabel unsigned long timeout; 65739b9004dSPhilipp Zabel 65839b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); 65939b9004dSPhilipp Zabel 66039b9004dSPhilipp Zabel timeout = jiffies + msecs_to_jiffies(1000); 66139b9004dSPhilipp Zabel while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { 66239b9004dSPhilipp Zabel if (time_after(jiffies, timeout)) 66339b9004dSPhilipp Zabel return -ETIME; 66439b9004dSPhilipp Zabel cpu_relax(); 66539b9004dSPhilipp Zabel } 66639b9004dSPhilipp Zabel 66739b9004dSPhilipp Zabel return 0; 66839b9004dSPhilipp Zabel } 66939b9004dSPhilipp Zabel 670ba07975fSSteve Longerbeam /* 671ba07975fSSteve Longerbeam * Set the source mux for the given CSI. Selects either parallel or 672ba07975fSSteve Longerbeam * MIPI CSI2 sources. 673ba07975fSSteve Longerbeam */ 674ba07975fSSteve Longerbeam void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2) 675ba07975fSSteve Longerbeam { 676ba07975fSSteve Longerbeam unsigned long flags; 677ba07975fSSteve Longerbeam u32 val, mask; 678ba07975fSSteve Longerbeam 679ba07975fSSteve Longerbeam mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE : 680ba07975fSSteve Longerbeam IPU_CONF_CSI0_DATA_SOURCE; 681ba07975fSSteve Longerbeam 682ba07975fSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 683ba07975fSSteve Longerbeam 684ba07975fSSteve Longerbeam val = ipu_cm_read(ipu, IPU_CONF); 685ba07975fSSteve Longerbeam if (mipi_csi2) 686ba07975fSSteve Longerbeam val |= mask; 687ba07975fSSteve Longerbeam else 688ba07975fSSteve Longerbeam val &= ~mask; 689ba07975fSSteve Longerbeam ipu_cm_write(ipu, val, IPU_CONF); 690ba07975fSSteve Longerbeam 691ba07975fSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 692ba07975fSSteve Longerbeam } 693ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux); 694ba07975fSSteve Longerbeam 695ba07975fSSteve Longerbeam /* 696ba07975fSSteve Longerbeam * Set the source mux for the IC. Selects either CSI[01] or the VDI. 697ba07975fSSteve Longerbeam */ 698ba07975fSSteve Longerbeam void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi) 699ba07975fSSteve Longerbeam { 700ba07975fSSteve Longerbeam unsigned long flags; 701ba07975fSSteve Longerbeam u32 val; 702ba07975fSSteve Longerbeam 703ba07975fSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 704ba07975fSSteve Longerbeam 705ba07975fSSteve Longerbeam val = ipu_cm_read(ipu, IPU_CONF); 706ba07975fSSteve Longerbeam if (vdi) { 707ba07975fSSteve Longerbeam val |= IPU_CONF_IC_INPUT; 708ba07975fSSteve Longerbeam } else { 709ba07975fSSteve Longerbeam val &= ~IPU_CONF_IC_INPUT; 710ba07975fSSteve Longerbeam if (csi_id == 1) 711ba07975fSSteve Longerbeam val |= IPU_CONF_CSI_SEL; 712ba07975fSSteve Longerbeam else 713ba07975fSSteve Longerbeam val &= ~IPU_CONF_CSI_SEL; 714ba07975fSSteve Longerbeam } 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_ic_src_mux); 720ba07975fSSteve Longerbeam 72139b9004dSPhilipp Zabel struct ipu_devtype { 72239b9004dSPhilipp Zabel const char *name; 72339b9004dSPhilipp Zabel unsigned long cm_ofs; 72439b9004dSPhilipp Zabel unsigned long cpmem_ofs; 72539b9004dSPhilipp Zabel unsigned long srm_ofs; 72639b9004dSPhilipp Zabel unsigned long tpm_ofs; 7272ffd48f2SSteve Longerbeam unsigned long csi0_ofs; 7282ffd48f2SSteve Longerbeam unsigned long csi1_ofs; 7291aa8ea0dSSteve Longerbeam unsigned long ic_ofs; 73039b9004dSPhilipp Zabel unsigned long disp0_ofs; 73139b9004dSPhilipp Zabel unsigned long disp1_ofs; 73239b9004dSPhilipp Zabel unsigned long dc_tmpl_ofs; 73339b9004dSPhilipp Zabel unsigned long vdi_ofs; 73439b9004dSPhilipp Zabel enum ipuv3_type type; 73539b9004dSPhilipp Zabel }; 73639b9004dSPhilipp Zabel 73739b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx51 = { 73839b9004dSPhilipp Zabel .name = "IPUv3EX", 73939b9004dSPhilipp Zabel .cm_ofs = 0x1e000000, 74039b9004dSPhilipp Zabel .cpmem_ofs = 0x1f000000, 74139b9004dSPhilipp Zabel .srm_ofs = 0x1f040000, 74239b9004dSPhilipp Zabel .tpm_ofs = 0x1f060000, 7432ffd48f2SSteve Longerbeam .csi0_ofs = 0x1f030000, 7442ffd48f2SSteve Longerbeam .csi1_ofs = 0x1f038000, 7451aa8ea0dSSteve Longerbeam .ic_ofs = 0x1f020000, 74639b9004dSPhilipp Zabel .disp0_ofs = 0x1e040000, 74739b9004dSPhilipp Zabel .disp1_ofs = 0x1e048000, 74839b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x1f080000, 74939b9004dSPhilipp Zabel .vdi_ofs = 0x1e068000, 75039b9004dSPhilipp Zabel .type = IPUV3EX, 75139b9004dSPhilipp Zabel }; 75239b9004dSPhilipp Zabel 75339b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx53 = { 75439b9004dSPhilipp Zabel .name = "IPUv3M", 75539b9004dSPhilipp Zabel .cm_ofs = 0x06000000, 75639b9004dSPhilipp Zabel .cpmem_ofs = 0x07000000, 75739b9004dSPhilipp Zabel .srm_ofs = 0x07040000, 75839b9004dSPhilipp Zabel .tpm_ofs = 0x07060000, 7592ffd48f2SSteve Longerbeam .csi0_ofs = 0x07030000, 7602ffd48f2SSteve Longerbeam .csi1_ofs = 0x07038000, 7611aa8ea0dSSteve Longerbeam .ic_ofs = 0x07020000, 76239b9004dSPhilipp Zabel .disp0_ofs = 0x06040000, 76339b9004dSPhilipp Zabel .disp1_ofs = 0x06048000, 76439b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x07080000, 76539b9004dSPhilipp Zabel .vdi_ofs = 0x06068000, 76639b9004dSPhilipp Zabel .type = IPUV3M, 76739b9004dSPhilipp Zabel }; 76839b9004dSPhilipp Zabel 76939b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx6q = { 77039b9004dSPhilipp Zabel .name = "IPUv3H", 77139b9004dSPhilipp Zabel .cm_ofs = 0x00200000, 77239b9004dSPhilipp Zabel .cpmem_ofs = 0x00300000, 77339b9004dSPhilipp Zabel .srm_ofs = 0x00340000, 77439b9004dSPhilipp Zabel .tpm_ofs = 0x00360000, 7752ffd48f2SSteve Longerbeam .csi0_ofs = 0x00230000, 7762ffd48f2SSteve Longerbeam .csi1_ofs = 0x00238000, 7771aa8ea0dSSteve Longerbeam .ic_ofs = 0x00220000, 77839b9004dSPhilipp Zabel .disp0_ofs = 0x00240000, 77939b9004dSPhilipp Zabel .disp1_ofs = 0x00248000, 78039b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x00380000, 78139b9004dSPhilipp Zabel .vdi_ofs = 0x00268000, 78239b9004dSPhilipp Zabel .type = IPUV3H, 78339b9004dSPhilipp Zabel }; 78439b9004dSPhilipp Zabel 78539b9004dSPhilipp Zabel static const struct of_device_id imx_ipu_dt_ids[] = { 78639b9004dSPhilipp Zabel { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, 78739b9004dSPhilipp Zabel { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, 78839b9004dSPhilipp Zabel { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, 78939b9004dSPhilipp Zabel { /* sentinel */ } 79039b9004dSPhilipp Zabel }; 79139b9004dSPhilipp Zabel MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); 79239b9004dSPhilipp Zabel 79339b9004dSPhilipp Zabel static int ipu_submodules_init(struct ipu_soc *ipu, 79439b9004dSPhilipp Zabel struct platform_device *pdev, unsigned long ipu_base, 79539b9004dSPhilipp Zabel struct clk *ipu_clk) 79639b9004dSPhilipp Zabel { 79739b9004dSPhilipp Zabel char *unit; 79839b9004dSPhilipp Zabel int ret; 79939b9004dSPhilipp Zabel struct device *dev = &pdev->dev; 80039b9004dSPhilipp Zabel const struct ipu_devtype *devtype = ipu->devtype; 80139b9004dSPhilipp Zabel 8027d2691daSSteve Longerbeam ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs); 8037d2691daSSteve Longerbeam if (ret) { 8047d2691daSSteve Longerbeam unit = "cpmem"; 8057d2691daSSteve Longerbeam goto err_cpmem; 8067d2691daSSteve Longerbeam } 8077d2691daSSteve Longerbeam 8082ffd48f2SSteve Longerbeam ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs, 8092ffd48f2SSteve Longerbeam IPU_CONF_CSI0_EN, ipu_clk); 8102ffd48f2SSteve Longerbeam if (ret) { 8112ffd48f2SSteve Longerbeam unit = "csi0"; 8122ffd48f2SSteve Longerbeam goto err_csi_0; 8132ffd48f2SSteve Longerbeam } 8142ffd48f2SSteve Longerbeam 8152ffd48f2SSteve Longerbeam ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs, 8162ffd48f2SSteve Longerbeam IPU_CONF_CSI1_EN, ipu_clk); 8172ffd48f2SSteve Longerbeam if (ret) { 8182ffd48f2SSteve Longerbeam unit = "csi1"; 8192ffd48f2SSteve Longerbeam goto err_csi_1; 8202ffd48f2SSteve Longerbeam } 8212ffd48f2SSteve Longerbeam 8221aa8ea0dSSteve Longerbeam ret = ipu_ic_init(ipu, dev, 8231aa8ea0dSSteve Longerbeam ipu_base + devtype->ic_ofs, 8241aa8ea0dSSteve Longerbeam ipu_base + devtype->tpm_ofs); 8251aa8ea0dSSteve Longerbeam if (ret) { 8261aa8ea0dSSteve Longerbeam unit = "ic"; 8271aa8ea0dSSteve Longerbeam goto err_ic; 8281aa8ea0dSSteve Longerbeam } 8291aa8ea0dSSteve Longerbeam 83039b9004dSPhilipp Zabel ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, 83139b9004dSPhilipp Zabel IPU_CONF_DI0_EN, ipu_clk); 83239b9004dSPhilipp Zabel if (ret) { 83339b9004dSPhilipp Zabel unit = "di0"; 83439b9004dSPhilipp Zabel goto err_di_0; 83539b9004dSPhilipp Zabel } 83639b9004dSPhilipp Zabel 83739b9004dSPhilipp Zabel ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs, 83839b9004dSPhilipp Zabel IPU_CONF_DI1_EN, ipu_clk); 83939b9004dSPhilipp Zabel if (ret) { 84039b9004dSPhilipp Zabel unit = "di1"; 84139b9004dSPhilipp Zabel goto err_di_1; 84239b9004dSPhilipp Zabel } 84339b9004dSPhilipp Zabel 84439b9004dSPhilipp Zabel ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs + 84539b9004dSPhilipp Zabel IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs); 84639b9004dSPhilipp Zabel if (ret) { 84739b9004dSPhilipp Zabel unit = "dc_template"; 84839b9004dSPhilipp Zabel goto err_dc; 84939b9004dSPhilipp Zabel } 85039b9004dSPhilipp Zabel 85139b9004dSPhilipp Zabel ret = ipu_dmfc_init(ipu, dev, ipu_base + 85239b9004dSPhilipp Zabel devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk); 85339b9004dSPhilipp Zabel if (ret) { 85439b9004dSPhilipp Zabel unit = "dmfc"; 85539b9004dSPhilipp Zabel goto err_dmfc; 85639b9004dSPhilipp Zabel } 85739b9004dSPhilipp Zabel 85839b9004dSPhilipp Zabel ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs); 85939b9004dSPhilipp Zabel if (ret) { 86039b9004dSPhilipp Zabel unit = "dp"; 86139b9004dSPhilipp Zabel goto err_dp; 86239b9004dSPhilipp Zabel } 86339b9004dSPhilipp Zabel 86435de925fSPhilipp Zabel ret = ipu_smfc_init(ipu, dev, ipu_base + 86535de925fSPhilipp Zabel devtype->cm_ofs + IPU_CM_SMFC_REG_OFS); 86635de925fSPhilipp Zabel if (ret) { 86735de925fSPhilipp Zabel unit = "smfc"; 86835de925fSPhilipp Zabel goto err_smfc; 86935de925fSPhilipp Zabel } 87035de925fSPhilipp Zabel 87139b9004dSPhilipp Zabel return 0; 87239b9004dSPhilipp Zabel 87335de925fSPhilipp Zabel err_smfc: 87435de925fSPhilipp Zabel ipu_dp_exit(ipu); 87539b9004dSPhilipp Zabel err_dp: 87639b9004dSPhilipp Zabel ipu_dmfc_exit(ipu); 87739b9004dSPhilipp Zabel err_dmfc: 87839b9004dSPhilipp Zabel ipu_dc_exit(ipu); 87939b9004dSPhilipp Zabel err_dc: 88039b9004dSPhilipp Zabel ipu_di_exit(ipu, 1); 88139b9004dSPhilipp Zabel err_di_1: 88239b9004dSPhilipp Zabel ipu_di_exit(ipu, 0); 88339b9004dSPhilipp Zabel err_di_0: 8841aa8ea0dSSteve Longerbeam ipu_ic_exit(ipu); 8851aa8ea0dSSteve Longerbeam err_ic: 8862ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 1); 8872ffd48f2SSteve Longerbeam err_csi_1: 8882ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 0); 8892ffd48f2SSteve Longerbeam err_csi_0: 8907d2691daSSteve Longerbeam ipu_cpmem_exit(ipu); 8917d2691daSSteve Longerbeam err_cpmem: 89239b9004dSPhilipp Zabel dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret); 89339b9004dSPhilipp Zabel return ret; 89439b9004dSPhilipp Zabel } 89539b9004dSPhilipp Zabel 89639b9004dSPhilipp Zabel static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs) 89739b9004dSPhilipp Zabel { 89839b9004dSPhilipp Zabel unsigned long status; 89939b9004dSPhilipp Zabel int i, bit, irq; 90039b9004dSPhilipp Zabel 90139b9004dSPhilipp Zabel for (i = 0; i < num_regs; i++) { 90239b9004dSPhilipp Zabel 90339b9004dSPhilipp Zabel status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i])); 90439b9004dSPhilipp Zabel status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i])); 90539b9004dSPhilipp Zabel 90639b9004dSPhilipp Zabel for_each_set_bit(bit, &status, 32) { 907682b7c1cSLinus Torvalds irq = irq_linear_revmap(ipu->domain, 908682b7c1cSLinus Torvalds regs[i] * 32 + bit); 90939b9004dSPhilipp Zabel if (irq) 91039b9004dSPhilipp Zabel generic_handle_irq(irq); 91139b9004dSPhilipp Zabel } 91239b9004dSPhilipp Zabel } 91339b9004dSPhilipp Zabel } 91439b9004dSPhilipp Zabel 91539b9004dSPhilipp Zabel static void ipu_irq_handler(unsigned int irq, struct irq_desc *desc) 91639b9004dSPhilipp Zabel { 91739b9004dSPhilipp Zabel struct ipu_soc *ipu = irq_desc_get_handler_data(desc); 91839b9004dSPhilipp Zabel const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14}; 91939b9004dSPhilipp Zabel struct irq_chip *chip = irq_get_chip(irq); 92039b9004dSPhilipp Zabel 92139b9004dSPhilipp Zabel chained_irq_enter(chip, desc); 92239b9004dSPhilipp Zabel 92339b9004dSPhilipp Zabel ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); 92439b9004dSPhilipp Zabel 92539b9004dSPhilipp Zabel chained_irq_exit(chip, desc); 92639b9004dSPhilipp Zabel } 92739b9004dSPhilipp Zabel 92839b9004dSPhilipp Zabel static void ipu_err_irq_handler(unsigned int irq, struct irq_desc *desc) 92939b9004dSPhilipp Zabel { 93039b9004dSPhilipp Zabel struct ipu_soc *ipu = irq_desc_get_handler_data(desc); 93139b9004dSPhilipp Zabel const int int_reg[] = { 4, 5, 8, 9}; 93239b9004dSPhilipp Zabel struct irq_chip *chip = irq_get_chip(irq); 93339b9004dSPhilipp Zabel 93439b9004dSPhilipp Zabel chained_irq_enter(chip, desc); 93539b9004dSPhilipp Zabel 93639b9004dSPhilipp Zabel ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); 93739b9004dSPhilipp Zabel 93839b9004dSPhilipp Zabel chained_irq_exit(chip, desc); 93939b9004dSPhilipp Zabel } 94039b9004dSPhilipp Zabel 941682b7c1cSLinus Torvalds int ipu_map_irq(struct ipu_soc *ipu, int irq) 942682b7c1cSLinus Torvalds { 943682b7c1cSLinus Torvalds int virq; 944682b7c1cSLinus Torvalds 945682b7c1cSLinus Torvalds virq = irq_linear_revmap(ipu->domain, irq); 946682b7c1cSLinus Torvalds if (!virq) 947682b7c1cSLinus Torvalds virq = irq_create_mapping(ipu->domain, irq); 948682b7c1cSLinus Torvalds 949682b7c1cSLinus Torvalds return virq; 950682b7c1cSLinus Torvalds } 951682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_map_irq); 952682b7c1cSLinus Torvalds 95339b9004dSPhilipp Zabel int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, 95439b9004dSPhilipp Zabel enum ipu_channel_irq irq_type) 95539b9004dSPhilipp Zabel { 956682b7c1cSLinus Torvalds return ipu_map_irq(ipu, irq_type + channel->num); 95739b9004dSPhilipp Zabel } 95839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq); 95939b9004dSPhilipp Zabel 96039b9004dSPhilipp Zabel static void ipu_submodules_exit(struct ipu_soc *ipu) 96139b9004dSPhilipp Zabel { 96235de925fSPhilipp Zabel ipu_smfc_exit(ipu); 96339b9004dSPhilipp Zabel ipu_dp_exit(ipu); 96439b9004dSPhilipp Zabel ipu_dmfc_exit(ipu); 96539b9004dSPhilipp Zabel ipu_dc_exit(ipu); 96639b9004dSPhilipp Zabel ipu_di_exit(ipu, 1); 96739b9004dSPhilipp Zabel ipu_di_exit(ipu, 0); 9681aa8ea0dSSteve Longerbeam ipu_ic_exit(ipu); 9692ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 1); 9702ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 0); 9717d2691daSSteve Longerbeam ipu_cpmem_exit(ipu); 97239b9004dSPhilipp Zabel } 97339b9004dSPhilipp Zabel 97439b9004dSPhilipp Zabel static int platform_remove_devices_fn(struct device *dev, void *unused) 97539b9004dSPhilipp Zabel { 97639b9004dSPhilipp Zabel struct platform_device *pdev = to_platform_device(dev); 97739b9004dSPhilipp Zabel 97839b9004dSPhilipp Zabel platform_device_unregister(pdev); 97939b9004dSPhilipp Zabel 98039b9004dSPhilipp Zabel return 0; 98139b9004dSPhilipp Zabel } 98239b9004dSPhilipp Zabel 98339b9004dSPhilipp Zabel static void platform_device_unregister_children(struct platform_device *pdev) 98439b9004dSPhilipp Zabel { 98539b9004dSPhilipp Zabel device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn); 98639b9004dSPhilipp Zabel } 98739b9004dSPhilipp Zabel 98839b9004dSPhilipp Zabel struct ipu_platform_reg { 98939b9004dSPhilipp Zabel struct ipu_client_platformdata pdata; 99039b9004dSPhilipp Zabel const char *name; 991d6ca8ca7SPhilipp Zabel int reg_offset; 99239b9004dSPhilipp Zabel }; 99339b9004dSPhilipp Zabel 99439b9004dSPhilipp Zabel static const struct ipu_platform_reg client_reg[] = { 99539b9004dSPhilipp Zabel { 99639b9004dSPhilipp Zabel .pdata = { 99739b9004dSPhilipp Zabel .di = 0, 99839b9004dSPhilipp Zabel .dc = 5, 99939b9004dSPhilipp Zabel .dp = IPU_DP_FLOW_SYNC_BG, 100039b9004dSPhilipp Zabel .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC, 100139b9004dSPhilipp Zabel .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC, 100239b9004dSPhilipp Zabel }, 100339b9004dSPhilipp Zabel .name = "imx-ipuv3-crtc", 100439b9004dSPhilipp Zabel }, { 100539b9004dSPhilipp Zabel .pdata = { 100639b9004dSPhilipp Zabel .di = 1, 100739b9004dSPhilipp Zabel .dc = 1, 100839b9004dSPhilipp Zabel .dp = -EINVAL, 100939b9004dSPhilipp Zabel .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC, 101039b9004dSPhilipp Zabel .dma[1] = -EINVAL, 101139b9004dSPhilipp Zabel }, 101239b9004dSPhilipp Zabel .name = "imx-ipuv3-crtc", 1013d6ca8ca7SPhilipp Zabel }, { 1014d6ca8ca7SPhilipp Zabel .pdata = { 1015d6ca8ca7SPhilipp Zabel .csi = 0, 1016d6ca8ca7SPhilipp Zabel .dma[0] = IPUV3_CHANNEL_CSI0, 1017d6ca8ca7SPhilipp Zabel .dma[1] = -EINVAL, 1018d6ca8ca7SPhilipp Zabel }, 1019d6ca8ca7SPhilipp Zabel .reg_offset = IPU_CM_CSI0_REG_OFS, 1020d6ca8ca7SPhilipp Zabel .name = "imx-ipuv3-camera", 1021d6ca8ca7SPhilipp Zabel }, { 1022d6ca8ca7SPhilipp Zabel .pdata = { 1023d6ca8ca7SPhilipp Zabel .csi = 1, 1024d6ca8ca7SPhilipp Zabel .dma[0] = IPUV3_CHANNEL_CSI1, 1025d6ca8ca7SPhilipp Zabel .dma[1] = -EINVAL, 1026d6ca8ca7SPhilipp Zabel }, 1027d6ca8ca7SPhilipp Zabel .reg_offset = IPU_CM_CSI1_REG_OFS, 1028d6ca8ca7SPhilipp Zabel .name = "imx-ipuv3-camera", 102939b9004dSPhilipp Zabel }, 103039b9004dSPhilipp Zabel }; 103139b9004dSPhilipp Zabel 103239b9004dSPhilipp Zabel static DEFINE_MUTEX(ipu_client_id_mutex); 103339b9004dSPhilipp Zabel static int ipu_client_id; 103439b9004dSPhilipp Zabel 1035d6ca8ca7SPhilipp Zabel static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) 103639b9004dSPhilipp Zabel { 103739b9004dSPhilipp Zabel struct device *dev = ipu->dev; 103839b9004dSPhilipp Zabel unsigned i; 103939b9004dSPhilipp Zabel int id, ret; 104039b9004dSPhilipp Zabel 104139b9004dSPhilipp Zabel mutex_lock(&ipu_client_id_mutex); 104239b9004dSPhilipp Zabel id = ipu_client_id; 104339b9004dSPhilipp Zabel ipu_client_id += ARRAY_SIZE(client_reg); 104439b9004dSPhilipp Zabel mutex_unlock(&ipu_client_id_mutex); 104539b9004dSPhilipp Zabel 104639b9004dSPhilipp Zabel for (i = 0; i < ARRAY_SIZE(client_reg); i++) { 104739b9004dSPhilipp Zabel const struct ipu_platform_reg *reg = &client_reg[i]; 104839b9004dSPhilipp Zabel struct platform_device *pdev; 1049d6ca8ca7SPhilipp Zabel struct resource res; 105039b9004dSPhilipp Zabel 1051d6ca8ca7SPhilipp Zabel if (reg->reg_offset) { 1052d6ca8ca7SPhilipp Zabel memset(&res, 0, sizeof(res)); 1053d6ca8ca7SPhilipp Zabel res.flags = IORESOURCE_MEM; 1054d6ca8ca7SPhilipp Zabel res.start = ipu_base + ipu->devtype->cm_ofs + reg->reg_offset; 1055d6ca8ca7SPhilipp Zabel res.end = res.start + PAGE_SIZE - 1; 1056d6ca8ca7SPhilipp Zabel pdev = platform_device_register_resndata(dev, reg->name, 1057d6ca8ca7SPhilipp Zabel id++, &res, 1, ®->pdata, sizeof(reg->pdata)); 1058d6ca8ca7SPhilipp Zabel } else { 105939b9004dSPhilipp Zabel pdev = platform_device_register_data(dev, reg->name, 106039b9004dSPhilipp Zabel id++, ®->pdata, sizeof(reg->pdata)); 1061d6ca8ca7SPhilipp Zabel } 106239b9004dSPhilipp Zabel 106339b9004dSPhilipp Zabel if (IS_ERR(pdev)) 106439b9004dSPhilipp Zabel goto err_register; 106539b9004dSPhilipp Zabel } 106639b9004dSPhilipp Zabel 106739b9004dSPhilipp Zabel return 0; 106839b9004dSPhilipp Zabel 106939b9004dSPhilipp Zabel err_register: 107039b9004dSPhilipp Zabel platform_device_unregister_children(to_platform_device(dev)); 107139b9004dSPhilipp Zabel 107239b9004dSPhilipp Zabel return ret; 107339b9004dSPhilipp Zabel } 107439b9004dSPhilipp Zabel 107539b9004dSPhilipp Zabel 107639b9004dSPhilipp Zabel static int ipu_irq_init(struct ipu_soc *ipu) 107739b9004dSPhilipp Zabel { 107839b9004dSPhilipp Zabel struct irq_chip_generic *gc; 107939b9004dSPhilipp Zabel struct irq_chip_type *ct; 108039b9004dSPhilipp Zabel unsigned long unused[IPU_NUM_IRQS / 32] = { 108139b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 108239b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 108339b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 108439b9004dSPhilipp Zabel 0x4077ffff, 0xffe7e1fd, 108539b9004dSPhilipp Zabel 0x23fffffe, 0x8880fff0, 108639b9004dSPhilipp Zabel 0xf98fe7d0, 0xfff81fff, 108739b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 108839b9004dSPhilipp Zabel 0x00000000, 108939b9004dSPhilipp Zabel }; 109039b9004dSPhilipp Zabel int ret, i; 109139b9004dSPhilipp Zabel 109239b9004dSPhilipp Zabel ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS, 109339b9004dSPhilipp Zabel &irq_generic_chip_ops, ipu); 109439b9004dSPhilipp Zabel if (!ipu->domain) { 109539b9004dSPhilipp Zabel dev_err(ipu->dev, "failed to add irq domain\n"); 109639b9004dSPhilipp Zabel return -ENODEV; 109739b9004dSPhilipp Zabel } 109839b9004dSPhilipp Zabel 109939b9004dSPhilipp Zabel ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU", 1100682b7c1cSLinus Torvalds handle_level_irq, 0, 1101682b7c1cSLinus Torvalds IRQF_VALID, 0); 110239b9004dSPhilipp Zabel if (ret < 0) { 110339b9004dSPhilipp Zabel dev_err(ipu->dev, "failed to alloc generic irq chips\n"); 110439b9004dSPhilipp Zabel irq_domain_remove(ipu->domain); 110539b9004dSPhilipp Zabel return ret; 110639b9004dSPhilipp Zabel } 110739b9004dSPhilipp Zabel 110839b9004dSPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i += 32) { 110939b9004dSPhilipp Zabel gc = irq_get_domain_generic_chip(ipu->domain, i); 111039b9004dSPhilipp Zabel gc->reg_base = ipu->cm_reg; 111139b9004dSPhilipp Zabel gc->unused = unused[i / 32]; 111239b9004dSPhilipp Zabel ct = gc->chip_types; 111339b9004dSPhilipp Zabel ct->chip.irq_ack = irq_gc_ack_set_bit; 111439b9004dSPhilipp Zabel ct->chip.irq_mask = irq_gc_mask_clr_bit; 111539b9004dSPhilipp Zabel ct->chip.irq_unmask = irq_gc_mask_set_bit; 111639b9004dSPhilipp Zabel ct->regs.ack = IPU_INT_STAT(i / 32); 111739b9004dSPhilipp Zabel ct->regs.mask = IPU_INT_CTRL(i / 32); 111839b9004dSPhilipp Zabel } 111939b9004dSPhilipp Zabel 112039b9004dSPhilipp Zabel irq_set_chained_handler(ipu->irq_sync, ipu_irq_handler); 112139b9004dSPhilipp Zabel irq_set_handler_data(ipu->irq_sync, ipu); 112239b9004dSPhilipp Zabel irq_set_chained_handler(ipu->irq_err, ipu_err_irq_handler); 112339b9004dSPhilipp Zabel irq_set_handler_data(ipu->irq_err, ipu); 112439b9004dSPhilipp Zabel 112539b9004dSPhilipp Zabel return 0; 112639b9004dSPhilipp Zabel } 112739b9004dSPhilipp Zabel 112839b9004dSPhilipp Zabel static void ipu_irq_exit(struct ipu_soc *ipu) 112939b9004dSPhilipp Zabel { 113039b9004dSPhilipp Zabel int i, irq; 113139b9004dSPhilipp Zabel 113239b9004dSPhilipp Zabel irq_set_chained_handler(ipu->irq_err, NULL); 113339b9004dSPhilipp Zabel irq_set_handler_data(ipu->irq_err, NULL); 113439b9004dSPhilipp Zabel irq_set_chained_handler(ipu->irq_sync, NULL); 113539b9004dSPhilipp Zabel irq_set_handler_data(ipu->irq_sync, NULL); 113639b9004dSPhilipp Zabel 113739b9004dSPhilipp Zabel /* TODO: remove irq_domain_generic_chips */ 113839b9004dSPhilipp Zabel 113939b9004dSPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i++) { 114039b9004dSPhilipp Zabel irq = irq_linear_revmap(ipu->domain, i); 114139b9004dSPhilipp Zabel if (irq) 114239b9004dSPhilipp Zabel irq_dispose_mapping(irq); 114339b9004dSPhilipp Zabel } 114439b9004dSPhilipp Zabel 114539b9004dSPhilipp Zabel irq_domain_remove(ipu->domain); 114639b9004dSPhilipp Zabel } 114739b9004dSPhilipp Zabel 11483feb049fSSteve Longerbeam void ipu_dump(struct ipu_soc *ipu) 11493feb049fSSteve Longerbeam { 11503feb049fSSteve Longerbeam int i; 11513feb049fSSteve Longerbeam 11523feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n", 11533feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_CONF)); 11543feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n", 11553feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CONF)); 11563feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n", 11573feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_EN(0))); 11583feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n", 11593feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_EN(32))); 11603feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n", 11613feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_PRI(0))); 11623feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n", 11633feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_PRI(32))); 11643feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n", 11653feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_BAND_EN(0))); 11663feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n", 11673feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_BAND_EN(32))); 11683feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n", 11693feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0))); 11703feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n", 11713feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32))); 11723feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n", 11733feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_PROC_FLOW1)); 11743feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n", 11753feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_PROC_FLOW2)); 11763feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n", 11773feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_PROC_FLOW3)); 11783feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n", 11793feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_DISP_FLOW1)); 11803feb049fSSteve Longerbeam for (i = 0; i < 15; i++) 11813feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i, 11823feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_INT_CTRL(i))); 11833feb049fSSteve Longerbeam } 11843feb049fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_dump); 11853feb049fSSteve Longerbeam 118639b9004dSPhilipp Zabel static int ipu_probe(struct platform_device *pdev) 118739b9004dSPhilipp Zabel { 118839b9004dSPhilipp Zabel const struct of_device_id *of_id = 118939b9004dSPhilipp Zabel of_match_device(imx_ipu_dt_ids, &pdev->dev); 119039b9004dSPhilipp Zabel struct ipu_soc *ipu; 119139b9004dSPhilipp Zabel struct resource *res; 119239b9004dSPhilipp Zabel unsigned long ipu_base; 119339b9004dSPhilipp Zabel int i, ret, irq_sync, irq_err; 119439b9004dSPhilipp Zabel const struct ipu_devtype *devtype; 119539b9004dSPhilipp Zabel 119639b9004dSPhilipp Zabel devtype = of_id->data; 119739b9004dSPhilipp Zabel 119839b9004dSPhilipp Zabel irq_sync = platform_get_irq(pdev, 0); 119939b9004dSPhilipp Zabel irq_err = platform_get_irq(pdev, 1); 120039b9004dSPhilipp Zabel res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 120139b9004dSPhilipp Zabel 120239b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n", 120339b9004dSPhilipp Zabel irq_sync, irq_err); 120439b9004dSPhilipp Zabel 120539b9004dSPhilipp Zabel if (!res || irq_sync < 0 || irq_err < 0) 120639b9004dSPhilipp Zabel return -ENODEV; 120739b9004dSPhilipp Zabel 120839b9004dSPhilipp Zabel ipu_base = res->start; 120939b9004dSPhilipp Zabel 121039b9004dSPhilipp Zabel ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL); 121139b9004dSPhilipp Zabel if (!ipu) 121239b9004dSPhilipp Zabel return -ENODEV; 121339b9004dSPhilipp Zabel 121439b9004dSPhilipp Zabel for (i = 0; i < 64; i++) 121539b9004dSPhilipp Zabel ipu->channel[i].ipu = ipu; 121639b9004dSPhilipp Zabel ipu->devtype = devtype; 121739b9004dSPhilipp Zabel ipu->ipu_type = devtype->type; 121839b9004dSPhilipp Zabel 121939b9004dSPhilipp Zabel spin_lock_init(&ipu->lock); 122039b9004dSPhilipp Zabel mutex_init(&ipu->channel_lock); 122139b9004dSPhilipp Zabel 122239b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n", 122339b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs); 122439b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "idmac: 0x%08lx\n", 122539b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS); 122639b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n", 122739b9004dSPhilipp Zabel ipu_base + devtype->cpmem_ofs); 12282ffd48f2SSteve Longerbeam dev_dbg(&pdev->dev, "csi0: 0x%08lx\n", 12292ffd48f2SSteve Longerbeam ipu_base + devtype->csi0_ofs); 12302ffd48f2SSteve Longerbeam dev_dbg(&pdev->dev, "csi1: 0x%08lx\n", 12312ffd48f2SSteve Longerbeam ipu_base + devtype->csi1_ofs); 12321aa8ea0dSSteve Longerbeam dev_dbg(&pdev->dev, "ic: 0x%08lx\n", 12331aa8ea0dSSteve Longerbeam ipu_base + devtype->ic_ofs); 123439b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "disp0: 0x%08lx\n", 123539b9004dSPhilipp Zabel ipu_base + devtype->disp0_ofs); 123639b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "disp1: 0x%08lx\n", 123739b9004dSPhilipp Zabel ipu_base + devtype->disp1_ofs); 123839b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "srm: 0x%08lx\n", 123939b9004dSPhilipp Zabel ipu_base + devtype->srm_ofs); 124039b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "tpm: 0x%08lx\n", 124139b9004dSPhilipp Zabel ipu_base + devtype->tpm_ofs); 124239b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "dc: 0x%08lx\n", 124339b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS); 124439b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "ic: 0x%08lx\n", 124539b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS); 124639b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n", 124739b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS); 124839b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "vdi: 0x%08lx\n", 124939b9004dSPhilipp Zabel ipu_base + devtype->vdi_ofs); 125039b9004dSPhilipp Zabel 125139b9004dSPhilipp Zabel ipu->cm_reg = devm_ioremap(&pdev->dev, 125239b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs, PAGE_SIZE); 125339b9004dSPhilipp Zabel ipu->idmac_reg = devm_ioremap(&pdev->dev, 125439b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS, 125539b9004dSPhilipp Zabel PAGE_SIZE); 125639b9004dSPhilipp Zabel 12577d2691daSSteve Longerbeam if (!ipu->cm_reg || !ipu->idmac_reg) 125839b9004dSPhilipp Zabel return -ENOMEM; 125939b9004dSPhilipp Zabel 126039b9004dSPhilipp Zabel ipu->clk = devm_clk_get(&pdev->dev, "bus"); 126139b9004dSPhilipp Zabel if (IS_ERR(ipu->clk)) { 126239b9004dSPhilipp Zabel ret = PTR_ERR(ipu->clk); 126339b9004dSPhilipp Zabel dev_err(&pdev->dev, "clk_get failed with %d", ret); 126439b9004dSPhilipp Zabel return ret; 126539b9004dSPhilipp Zabel } 126639b9004dSPhilipp Zabel 126739b9004dSPhilipp Zabel platform_set_drvdata(pdev, ipu); 126839b9004dSPhilipp Zabel 126939b9004dSPhilipp Zabel ret = clk_prepare_enable(ipu->clk); 127039b9004dSPhilipp Zabel if (ret) { 127139b9004dSPhilipp Zabel dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); 127239b9004dSPhilipp Zabel return ret; 127339b9004dSPhilipp Zabel } 127439b9004dSPhilipp Zabel 127539b9004dSPhilipp Zabel ipu->dev = &pdev->dev; 127639b9004dSPhilipp Zabel ipu->irq_sync = irq_sync; 127739b9004dSPhilipp Zabel ipu->irq_err = irq_err; 127839b9004dSPhilipp Zabel 127939b9004dSPhilipp Zabel ret = ipu_irq_init(ipu); 128039b9004dSPhilipp Zabel if (ret) 128139b9004dSPhilipp Zabel goto out_failed_irq; 128239b9004dSPhilipp Zabel 128339b9004dSPhilipp Zabel ret = device_reset(&pdev->dev); 128439b9004dSPhilipp Zabel if (ret) { 128539b9004dSPhilipp Zabel dev_err(&pdev->dev, "failed to reset: %d\n", ret); 128639b9004dSPhilipp Zabel goto out_failed_reset; 128739b9004dSPhilipp Zabel } 128839b9004dSPhilipp Zabel ret = ipu_memory_reset(ipu); 128939b9004dSPhilipp Zabel if (ret) 129039b9004dSPhilipp Zabel goto out_failed_reset; 129139b9004dSPhilipp Zabel 129239b9004dSPhilipp Zabel /* Set MCU_T to divide MCU access window into 2 */ 129339b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), 129439b9004dSPhilipp Zabel IPU_DISP_GEN); 129539b9004dSPhilipp Zabel 129639b9004dSPhilipp Zabel ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk); 129739b9004dSPhilipp Zabel if (ret) 129839b9004dSPhilipp Zabel goto failed_submodules_init; 129939b9004dSPhilipp Zabel 1300d6ca8ca7SPhilipp Zabel ret = ipu_add_client_devices(ipu, ipu_base); 130139b9004dSPhilipp Zabel if (ret) { 130239b9004dSPhilipp Zabel dev_err(&pdev->dev, "adding client devices failed with %d\n", 130339b9004dSPhilipp Zabel ret); 130439b9004dSPhilipp Zabel goto failed_add_clients; 130539b9004dSPhilipp Zabel } 130639b9004dSPhilipp Zabel 130739b9004dSPhilipp Zabel dev_info(&pdev->dev, "%s probed\n", devtype->name); 130839b9004dSPhilipp Zabel 130939b9004dSPhilipp Zabel return 0; 131039b9004dSPhilipp Zabel 131139b9004dSPhilipp Zabel failed_add_clients: 131239b9004dSPhilipp Zabel ipu_submodules_exit(ipu); 131339b9004dSPhilipp Zabel failed_submodules_init: 131439b9004dSPhilipp Zabel out_failed_reset: 131539b9004dSPhilipp Zabel ipu_irq_exit(ipu); 131639b9004dSPhilipp Zabel out_failed_irq: 131739b9004dSPhilipp Zabel clk_disable_unprepare(ipu->clk); 131839b9004dSPhilipp Zabel return ret; 131939b9004dSPhilipp Zabel } 132039b9004dSPhilipp Zabel 132139b9004dSPhilipp Zabel static int ipu_remove(struct platform_device *pdev) 132239b9004dSPhilipp Zabel { 132339b9004dSPhilipp Zabel struct ipu_soc *ipu = platform_get_drvdata(pdev); 132439b9004dSPhilipp Zabel 132539b9004dSPhilipp Zabel platform_device_unregister_children(pdev); 132639b9004dSPhilipp Zabel ipu_submodules_exit(ipu); 132739b9004dSPhilipp Zabel ipu_irq_exit(ipu); 132839b9004dSPhilipp Zabel 132939b9004dSPhilipp Zabel clk_disable_unprepare(ipu->clk); 133039b9004dSPhilipp Zabel 133139b9004dSPhilipp Zabel return 0; 133239b9004dSPhilipp Zabel } 133339b9004dSPhilipp Zabel 133439b9004dSPhilipp Zabel static struct platform_driver imx_ipu_driver = { 133539b9004dSPhilipp Zabel .driver = { 133639b9004dSPhilipp Zabel .name = "imx-ipuv3", 133739b9004dSPhilipp Zabel .of_match_table = imx_ipu_dt_ids, 133839b9004dSPhilipp Zabel }, 133939b9004dSPhilipp Zabel .probe = ipu_probe, 134039b9004dSPhilipp Zabel .remove = ipu_remove, 134139b9004dSPhilipp Zabel }; 134239b9004dSPhilipp Zabel 134339b9004dSPhilipp Zabel module_platform_driver(imx_ipu_driver); 134439b9004dSPhilipp Zabel 134539b9004dSPhilipp Zabel MODULE_ALIAS("platform:imx-ipuv3"); 134639b9004dSPhilipp Zabel MODULE_DESCRIPTION("i.MX IPU v3 driver"); 134739b9004dSPhilipp Zabel MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); 134839b9004dSPhilipp Zabel MODULE_LICENSE("GPL"); 1349