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: 7739b9004dSPhilipp Zabel return IPUV3_COLORSPACE_YUV; 7839b9004dSPhilipp Zabel default: 7939b9004dSPhilipp Zabel return IPUV3_COLORSPACE_UNKNOWN; 8039b9004dSPhilipp Zabel } 8139b9004dSPhilipp Zabel } 8239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace); 8339b9004dSPhilipp Zabel 8439b9004dSPhilipp Zabel enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat) 8539b9004dSPhilipp Zabel { 8639b9004dSPhilipp Zabel switch (pixelformat) { 8739b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUV420: 8839b9004dSPhilipp Zabel case V4L2_PIX_FMT_YVU420: 8939b9004dSPhilipp Zabel case V4L2_PIX_FMT_UYVY: 9039b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUYV: 9139b9004dSPhilipp Zabel return IPUV3_COLORSPACE_YUV; 9239b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB32: 9339b9004dSPhilipp Zabel case V4L2_PIX_FMT_BGR32: 9439b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB24: 9539b9004dSPhilipp Zabel case V4L2_PIX_FMT_BGR24: 9639b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB565: 9739b9004dSPhilipp Zabel return IPUV3_COLORSPACE_RGB; 9839b9004dSPhilipp Zabel default: 9939b9004dSPhilipp Zabel return IPUV3_COLORSPACE_UNKNOWN; 10039b9004dSPhilipp Zabel } 10139b9004dSPhilipp Zabel } 10239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace); 10339b9004dSPhilipp Zabel 1044cea940dSSteve Longerbeam bool ipu_pixelformat_is_planar(u32 pixelformat) 1054cea940dSSteve Longerbeam { 1064cea940dSSteve Longerbeam switch (pixelformat) { 1074cea940dSSteve Longerbeam case V4L2_PIX_FMT_YUV420: 1084cea940dSSteve Longerbeam case V4L2_PIX_FMT_YVU420: 1094cea940dSSteve Longerbeam return true; 1104cea940dSSteve Longerbeam } 1114cea940dSSteve Longerbeam 1124cea940dSSteve Longerbeam return false; 1134cea940dSSteve Longerbeam } 1144cea940dSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar); 1154cea940dSSteve Longerbeam 116ae0e9708SSteve Longerbeam enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code) 117ae0e9708SSteve Longerbeam { 118ae0e9708SSteve Longerbeam switch (mbus_code & 0xf000) { 119ae0e9708SSteve Longerbeam case 0x1000: 120ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_RGB; 121ae0e9708SSteve Longerbeam case 0x2000: 122ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_YUV; 123ae0e9708SSteve Longerbeam default: 124ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_UNKNOWN; 125ae0e9708SSteve Longerbeam } 126ae0e9708SSteve Longerbeam } 127ae0e9708SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace); 128ae0e9708SSteve Longerbeam 129f835f386SSteve Longerbeam int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees, 130f835f386SSteve Longerbeam bool hflip, bool vflip) 131f835f386SSteve Longerbeam { 132f835f386SSteve Longerbeam u32 r90, vf, hf; 133f835f386SSteve Longerbeam 134f835f386SSteve Longerbeam switch (degrees) { 135f835f386SSteve Longerbeam case 0: 136f835f386SSteve Longerbeam vf = hf = r90 = 0; 137f835f386SSteve Longerbeam break; 138f835f386SSteve Longerbeam case 90: 139f835f386SSteve Longerbeam vf = hf = 0; 140f835f386SSteve Longerbeam r90 = 1; 141f835f386SSteve Longerbeam break; 142f835f386SSteve Longerbeam case 180: 143f835f386SSteve Longerbeam vf = hf = 1; 144f835f386SSteve Longerbeam r90 = 0; 145f835f386SSteve Longerbeam break; 146f835f386SSteve Longerbeam case 270: 147f835f386SSteve Longerbeam vf = hf = r90 = 1; 148f835f386SSteve Longerbeam break; 149f835f386SSteve Longerbeam default: 150f835f386SSteve Longerbeam return -EINVAL; 151f835f386SSteve Longerbeam } 152f835f386SSteve Longerbeam 153f835f386SSteve Longerbeam hf ^= (u32)hflip; 154f835f386SSteve Longerbeam vf ^= (u32)vflip; 155f835f386SSteve Longerbeam 156f835f386SSteve Longerbeam *mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf); 157f835f386SSteve Longerbeam return 0; 158f835f386SSteve Longerbeam } 159f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode); 160f835f386SSteve Longerbeam 161f835f386SSteve Longerbeam int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode, 162f835f386SSteve Longerbeam bool hflip, bool vflip) 163f835f386SSteve Longerbeam { 164f835f386SSteve Longerbeam u32 r90, vf, hf; 165f835f386SSteve Longerbeam 166f835f386SSteve Longerbeam r90 = ((u32)mode >> 2) & 0x1; 167f835f386SSteve Longerbeam hf = ((u32)mode >> 1) & 0x1; 168f835f386SSteve Longerbeam vf = ((u32)mode >> 0) & 0x1; 169f835f386SSteve Longerbeam hf ^= (u32)hflip; 170f835f386SSteve Longerbeam vf ^= (u32)vflip; 171f835f386SSteve Longerbeam 172f835f386SSteve Longerbeam switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) { 173f835f386SSteve Longerbeam case IPU_ROTATE_NONE: 174f835f386SSteve Longerbeam *degrees = 0; 175f835f386SSteve Longerbeam break; 176f835f386SSteve Longerbeam case IPU_ROTATE_90_RIGHT: 177f835f386SSteve Longerbeam *degrees = 90; 178f835f386SSteve Longerbeam break; 179f835f386SSteve Longerbeam case IPU_ROTATE_180: 180f835f386SSteve Longerbeam *degrees = 180; 181f835f386SSteve Longerbeam break; 182f835f386SSteve Longerbeam case IPU_ROTATE_90_LEFT: 183f835f386SSteve Longerbeam *degrees = 270; 184f835f386SSteve Longerbeam break; 185f835f386SSteve Longerbeam default: 186f835f386SSteve Longerbeam return -EINVAL; 187f835f386SSteve Longerbeam } 188f835f386SSteve Longerbeam 189f835f386SSteve Longerbeam return 0; 190f835f386SSteve Longerbeam } 191f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees); 192f835f386SSteve Longerbeam 19339b9004dSPhilipp Zabel struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num) 19439b9004dSPhilipp Zabel { 19539b9004dSPhilipp Zabel struct ipuv3_channel *channel; 19639b9004dSPhilipp Zabel 19739b9004dSPhilipp Zabel dev_dbg(ipu->dev, "%s %d\n", __func__, num); 19839b9004dSPhilipp Zabel 19939b9004dSPhilipp Zabel if (num > 63) 20039b9004dSPhilipp Zabel return ERR_PTR(-ENODEV); 20139b9004dSPhilipp Zabel 20239b9004dSPhilipp Zabel mutex_lock(&ipu->channel_lock); 20339b9004dSPhilipp Zabel 20439b9004dSPhilipp Zabel channel = &ipu->channel[num]; 20539b9004dSPhilipp Zabel 20639b9004dSPhilipp Zabel if (channel->busy) { 20739b9004dSPhilipp Zabel channel = ERR_PTR(-EBUSY); 20839b9004dSPhilipp Zabel goto out; 20939b9004dSPhilipp Zabel } 21039b9004dSPhilipp Zabel 21139b9004dSPhilipp Zabel channel->busy = true; 21239b9004dSPhilipp Zabel channel->num = num; 21339b9004dSPhilipp Zabel 21439b9004dSPhilipp Zabel out: 21539b9004dSPhilipp Zabel mutex_unlock(&ipu->channel_lock); 21639b9004dSPhilipp Zabel 21739b9004dSPhilipp Zabel return channel; 21839b9004dSPhilipp Zabel } 21939b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get); 22039b9004dSPhilipp Zabel 22139b9004dSPhilipp Zabel void ipu_idmac_put(struct ipuv3_channel *channel) 22239b9004dSPhilipp Zabel { 22339b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 22439b9004dSPhilipp Zabel 22539b9004dSPhilipp Zabel dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num); 22639b9004dSPhilipp Zabel 22739b9004dSPhilipp Zabel mutex_lock(&ipu->channel_lock); 22839b9004dSPhilipp Zabel 22939b9004dSPhilipp Zabel channel->busy = false; 23039b9004dSPhilipp Zabel 23139b9004dSPhilipp Zabel mutex_unlock(&ipu->channel_lock); 23239b9004dSPhilipp Zabel } 23339b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_put); 23439b9004dSPhilipp Zabel 235aa52f578SSteve Longerbeam #define idma_mask(ch) (1 << ((ch) & 0x1f)) 23639b9004dSPhilipp Zabel 23739b9004dSPhilipp Zabel void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel, 23839b9004dSPhilipp Zabel bool doublebuffer) 23939b9004dSPhilipp Zabel { 24039b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 24139b9004dSPhilipp Zabel unsigned long flags; 24239b9004dSPhilipp Zabel u32 reg; 24339b9004dSPhilipp Zabel 24439b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 24539b9004dSPhilipp Zabel 24639b9004dSPhilipp Zabel reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); 24739b9004dSPhilipp Zabel if (doublebuffer) 24839b9004dSPhilipp Zabel reg |= idma_mask(channel->num); 24939b9004dSPhilipp Zabel else 25039b9004dSPhilipp Zabel reg &= ~idma_mask(channel->num); 25139b9004dSPhilipp Zabel ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num)); 25239b9004dSPhilipp Zabel 25339b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 25439b9004dSPhilipp Zabel } 25539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer); 25639b9004dSPhilipp Zabel 25739b9004dSPhilipp Zabel int ipu_module_enable(struct ipu_soc *ipu, u32 mask) 25839b9004dSPhilipp Zabel { 25939b9004dSPhilipp Zabel unsigned long lock_flags; 26039b9004dSPhilipp Zabel u32 val; 26139b9004dSPhilipp Zabel 26239b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, lock_flags); 26339b9004dSPhilipp Zabel 26439b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_DISP_GEN); 26539b9004dSPhilipp Zabel 26639b9004dSPhilipp Zabel if (mask & IPU_CONF_DI0_EN) 26739b9004dSPhilipp Zabel val |= IPU_DI0_COUNTER_RELEASE; 26839b9004dSPhilipp Zabel if (mask & IPU_CONF_DI1_EN) 26939b9004dSPhilipp Zabel val |= IPU_DI1_COUNTER_RELEASE; 27039b9004dSPhilipp Zabel 27139b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_DISP_GEN); 27239b9004dSPhilipp Zabel 27339b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CONF); 27439b9004dSPhilipp Zabel val |= mask; 27539b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CONF); 27639b9004dSPhilipp Zabel 27739b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, lock_flags); 27839b9004dSPhilipp Zabel 27939b9004dSPhilipp Zabel return 0; 28039b9004dSPhilipp Zabel } 28139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_enable); 28239b9004dSPhilipp Zabel 28339b9004dSPhilipp Zabel int ipu_module_disable(struct ipu_soc *ipu, u32 mask) 28439b9004dSPhilipp Zabel { 28539b9004dSPhilipp Zabel unsigned long lock_flags; 28639b9004dSPhilipp Zabel u32 val; 28739b9004dSPhilipp Zabel 28839b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, lock_flags); 28939b9004dSPhilipp Zabel 29039b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CONF); 29139b9004dSPhilipp Zabel val &= ~mask; 29239b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CONF); 29339b9004dSPhilipp Zabel 29439b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_DISP_GEN); 29539b9004dSPhilipp Zabel 29639b9004dSPhilipp Zabel if (mask & IPU_CONF_DI0_EN) 29739b9004dSPhilipp Zabel val &= ~IPU_DI0_COUNTER_RELEASE; 29839b9004dSPhilipp Zabel if (mask & IPU_CONF_DI1_EN) 29939b9004dSPhilipp Zabel val &= ~IPU_DI1_COUNTER_RELEASE; 30039b9004dSPhilipp Zabel 30139b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_DISP_GEN); 30239b9004dSPhilipp Zabel 30339b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, lock_flags); 30439b9004dSPhilipp Zabel 30539b9004dSPhilipp Zabel return 0; 30639b9004dSPhilipp Zabel } 30739b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_disable); 30839b9004dSPhilipp Zabel 309e9046097SPhilipp Zabel int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel) 310e9046097SPhilipp Zabel { 311e9046097SPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 312e9046097SPhilipp Zabel unsigned int chno = channel->num; 313e9046097SPhilipp Zabel 314e9046097SPhilipp Zabel return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0; 315e9046097SPhilipp Zabel } 316e9046097SPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer); 317e9046097SPhilipp Zabel 318aa52f578SSteve Longerbeam bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num) 319aa52f578SSteve Longerbeam { 320aa52f578SSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 321aa52f578SSteve Longerbeam unsigned long flags; 322aa52f578SSteve Longerbeam u32 reg = 0; 323aa52f578SSteve Longerbeam 324aa52f578SSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 325aa52f578SSteve Longerbeam switch (buf_num) { 326aa52f578SSteve Longerbeam case 0: 327aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)); 328aa52f578SSteve Longerbeam break; 329aa52f578SSteve Longerbeam case 1: 330aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)); 331aa52f578SSteve Longerbeam break; 332aa52f578SSteve Longerbeam case 2: 333aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num)); 334aa52f578SSteve Longerbeam break; 335aa52f578SSteve Longerbeam } 336aa52f578SSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 337aa52f578SSteve Longerbeam 338aa52f578SSteve Longerbeam return ((reg & idma_mask(channel->num)) != 0); 339aa52f578SSteve Longerbeam } 340aa52f578SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready); 341aa52f578SSteve Longerbeam 34239b9004dSPhilipp Zabel void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num) 34339b9004dSPhilipp Zabel { 34439b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 34539b9004dSPhilipp Zabel unsigned int chno = channel->num; 34639b9004dSPhilipp Zabel unsigned long flags; 34739b9004dSPhilipp Zabel 34839b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 34939b9004dSPhilipp Zabel 35039b9004dSPhilipp Zabel /* Mark buffer as ready. */ 35139b9004dSPhilipp Zabel if (buf_num == 0) 35239b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); 35339b9004dSPhilipp Zabel else 35439b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); 35539b9004dSPhilipp Zabel 35639b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 35739b9004dSPhilipp Zabel } 35839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer); 35939b9004dSPhilipp Zabel 360bce6f087SSteve Longerbeam void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num) 361bce6f087SSteve Longerbeam { 362bce6f087SSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 363bce6f087SSteve Longerbeam unsigned int chno = channel->num; 364bce6f087SSteve Longerbeam unsigned long flags; 365bce6f087SSteve Longerbeam 366bce6f087SSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 367bce6f087SSteve Longerbeam 368bce6f087SSteve Longerbeam ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */ 369bce6f087SSteve Longerbeam switch (buf_num) { 370bce6f087SSteve Longerbeam case 0: 371bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); 372bce6f087SSteve Longerbeam break; 373bce6f087SSteve Longerbeam case 1: 374bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); 375bce6f087SSteve Longerbeam break; 376bce6f087SSteve Longerbeam case 2: 377bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno)); 378bce6f087SSteve Longerbeam break; 379bce6f087SSteve Longerbeam default: 380bce6f087SSteve Longerbeam break; 381bce6f087SSteve Longerbeam } 382bce6f087SSteve Longerbeam ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ 383bce6f087SSteve Longerbeam 384bce6f087SSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 385bce6f087SSteve Longerbeam } 386bce6f087SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer); 387bce6f087SSteve Longerbeam 38839b9004dSPhilipp Zabel int ipu_idmac_enable_channel(struct ipuv3_channel *channel) 38939b9004dSPhilipp Zabel { 39039b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 39139b9004dSPhilipp Zabel u32 val; 39239b9004dSPhilipp Zabel unsigned long flags; 39339b9004dSPhilipp Zabel 39439b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 39539b9004dSPhilipp Zabel 39639b9004dSPhilipp Zabel val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); 39739b9004dSPhilipp Zabel val |= idma_mask(channel->num); 39839b9004dSPhilipp Zabel ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); 39939b9004dSPhilipp Zabel 40039b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 40139b9004dSPhilipp Zabel 40239b9004dSPhilipp Zabel return 0; 40339b9004dSPhilipp Zabel } 40439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel); 40539b9004dSPhilipp Zabel 406682b7c1cSLinus Torvalds bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno) 407682b7c1cSLinus Torvalds { 408682b7c1cSLinus Torvalds return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno)); 409682b7c1cSLinus Torvalds } 410682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy); 411682b7c1cSLinus Torvalds 41239b9004dSPhilipp Zabel int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) 41339b9004dSPhilipp Zabel { 41439b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 41539b9004dSPhilipp Zabel unsigned long timeout; 41639b9004dSPhilipp Zabel 41739b9004dSPhilipp Zabel timeout = jiffies + msecs_to_jiffies(ms); 41839b9004dSPhilipp Zabel while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) & 41939b9004dSPhilipp Zabel idma_mask(channel->num)) { 42039b9004dSPhilipp Zabel if (time_after(jiffies, timeout)) 42139b9004dSPhilipp Zabel return -ETIMEDOUT; 42239b9004dSPhilipp Zabel cpu_relax(); 42339b9004dSPhilipp Zabel } 42439b9004dSPhilipp Zabel 42539b9004dSPhilipp Zabel return 0; 42639b9004dSPhilipp Zabel } 42739b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); 42839b9004dSPhilipp Zabel 429682b7c1cSLinus Torvalds int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms) 430682b7c1cSLinus Torvalds { 431682b7c1cSLinus Torvalds unsigned long timeout; 432682b7c1cSLinus Torvalds 433682b7c1cSLinus Torvalds timeout = jiffies + msecs_to_jiffies(ms); 434682b7c1cSLinus Torvalds ipu_cm_write(ipu, BIT(irq % 32), IPU_INT_STAT(irq / 32)); 435682b7c1cSLinus Torvalds while (!(ipu_cm_read(ipu, IPU_INT_STAT(irq / 32) & BIT(irq % 32)))) { 436682b7c1cSLinus Torvalds if (time_after(jiffies, timeout)) 437682b7c1cSLinus Torvalds return -ETIMEDOUT; 438682b7c1cSLinus Torvalds cpu_relax(); 439682b7c1cSLinus Torvalds } 440682b7c1cSLinus Torvalds 441682b7c1cSLinus Torvalds return 0; 442682b7c1cSLinus Torvalds } 443682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_wait_interrupt); 444682b7c1cSLinus Torvalds 44539b9004dSPhilipp Zabel int ipu_idmac_disable_channel(struct ipuv3_channel *channel) 44639b9004dSPhilipp Zabel { 44739b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 44839b9004dSPhilipp Zabel u32 val; 44939b9004dSPhilipp Zabel unsigned long flags; 45039b9004dSPhilipp Zabel 45139b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 45239b9004dSPhilipp Zabel 45339b9004dSPhilipp Zabel /* Disable DMA channel(s) */ 45439b9004dSPhilipp Zabel val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); 45539b9004dSPhilipp Zabel val &= ~idma_mask(channel->num); 45639b9004dSPhilipp Zabel ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); 45739b9004dSPhilipp Zabel 45839b9004dSPhilipp Zabel /* Set channel buffers NOT to be ready */ 45939b9004dSPhilipp Zabel ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */ 46039b9004dSPhilipp Zabel 46139b9004dSPhilipp Zabel if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) & 46239b9004dSPhilipp Zabel idma_mask(channel->num)) { 46339b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(channel->num), 46439b9004dSPhilipp Zabel IPU_CHA_BUF0_RDY(channel->num)); 46539b9004dSPhilipp Zabel } 46639b9004dSPhilipp Zabel 46739b9004dSPhilipp Zabel if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) & 46839b9004dSPhilipp Zabel idma_mask(channel->num)) { 46939b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(channel->num), 47039b9004dSPhilipp Zabel IPU_CHA_BUF1_RDY(channel->num)); 47139b9004dSPhilipp Zabel } 47239b9004dSPhilipp Zabel 47339b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ 47439b9004dSPhilipp Zabel 47539b9004dSPhilipp Zabel /* Reset the double buffer */ 47639b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); 47739b9004dSPhilipp Zabel val &= ~idma_mask(channel->num); 47839b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num)); 47939b9004dSPhilipp Zabel 48039b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 48139b9004dSPhilipp Zabel 48239b9004dSPhilipp Zabel return 0; 48339b9004dSPhilipp Zabel } 48439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel); 48539b9004dSPhilipp Zabel 48639b9004dSPhilipp Zabel static int ipu_memory_reset(struct ipu_soc *ipu) 48739b9004dSPhilipp Zabel { 48839b9004dSPhilipp Zabel unsigned long timeout; 48939b9004dSPhilipp Zabel 49039b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); 49139b9004dSPhilipp Zabel 49239b9004dSPhilipp Zabel timeout = jiffies + msecs_to_jiffies(1000); 49339b9004dSPhilipp Zabel while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { 49439b9004dSPhilipp Zabel if (time_after(jiffies, timeout)) 49539b9004dSPhilipp Zabel return -ETIME; 49639b9004dSPhilipp Zabel cpu_relax(); 49739b9004dSPhilipp Zabel } 49839b9004dSPhilipp Zabel 49939b9004dSPhilipp Zabel return 0; 50039b9004dSPhilipp Zabel } 50139b9004dSPhilipp Zabel 502ba07975fSSteve Longerbeam /* 503ba07975fSSteve Longerbeam * Set the source mux for the given CSI. Selects either parallel or 504ba07975fSSteve Longerbeam * MIPI CSI2 sources. 505ba07975fSSteve Longerbeam */ 506ba07975fSSteve Longerbeam void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2) 507ba07975fSSteve Longerbeam { 508ba07975fSSteve Longerbeam unsigned long flags; 509ba07975fSSteve Longerbeam u32 val, mask; 510ba07975fSSteve Longerbeam 511ba07975fSSteve Longerbeam mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE : 512ba07975fSSteve Longerbeam IPU_CONF_CSI0_DATA_SOURCE; 513ba07975fSSteve Longerbeam 514ba07975fSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 515ba07975fSSteve Longerbeam 516ba07975fSSteve Longerbeam val = ipu_cm_read(ipu, IPU_CONF); 517ba07975fSSteve Longerbeam if (mipi_csi2) 518ba07975fSSteve Longerbeam val |= mask; 519ba07975fSSteve Longerbeam else 520ba07975fSSteve Longerbeam val &= ~mask; 521ba07975fSSteve Longerbeam ipu_cm_write(ipu, val, IPU_CONF); 522ba07975fSSteve Longerbeam 523ba07975fSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 524ba07975fSSteve Longerbeam } 525ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux); 526ba07975fSSteve Longerbeam 527ba07975fSSteve Longerbeam /* 528ba07975fSSteve Longerbeam * Set the source mux for the IC. Selects either CSI[01] or the VDI. 529ba07975fSSteve Longerbeam */ 530ba07975fSSteve Longerbeam void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi) 531ba07975fSSteve Longerbeam { 532ba07975fSSteve Longerbeam unsigned long flags; 533ba07975fSSteve Longerbeam u32 val; 534ba07975fSSteve Longerbeam 535ba07975fSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 536ba07975fSSteve Longerbeam 537ba07975fSSteve Longerbeam val = ipu_cm_read(ipu, IPU_CONF); 538ba07975fSSteve Longerbeam if (vdi) { 539ba07975fSSteve Longerbeam val |= IPU_CONF_IC_INPUT; 540ba07975fSSteve Longerbeam } else { 541ba07975fSSteve Longerbeam val &= ~IPU_CONF_IC_INPUT; 542ba07975fSSteve Longerbeam if (csi_id == 1) 543ba07975fSSteve Longerbeam val |= IPU_CONF_CSI_SEL; 544ba07975fSSteve Longerbeam else 545ba07975fSSteve Longerbeam val &= ~IPU_CONF_CSI_SEL; 546ba07975fSSteve Longerbeam } 547ba07975fSSteve Longerbeam ipu_cm_write(ipu, val, IPU_CONF); 548ba07975fSSteve Longerbeam 549ba07975fSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 550ba07975fSSteve Longerbeam } 551ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux); 552ba07975fSSteve Longerbeam 55339b9004dSPhilipp Zabel struct ipu_devtype { 55439b9004dSPhilipp Zabel const char *name; 55539b9004dSPhilipp Zabel unsigned long cm_ofs; 55639b9004dSPhilipp Zabel unsigned long cpmem_ofs; 55739b9004dSPhilipp Zabel unsigned long srm_ofs; 55839b9004dSPhilipp Zabel unsigned long tpm_ofs; 5592ffd48f2SSteve Longerbeam unsigned long csi0_ofs; 5602ffd48f2SSteve Longerbeam unsigned long csi1_ofs; 5611aa8ea0dSSteve Longerbeam unsigned long ic_ofs; 56239b9004dSPhilipp Zabel unsigned long disp0_ofs; 56339b9004dSPhilipp Zabel unsigned long disp1_ofs; 56439b9004dSPhilipp Zabel unsigned long dc_tmpl_ofs; 56539b9004dSPhilipp Zabel unsigned long vdi_ofs; 56639b9004dSPhilipp Zabel enum ipuv3_type type; 56739b9004dSPhilipp Zabel }; 56839b9004dSPhilipp Zabel 56939b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx51 = { 57039b9004dSPhilipp Zabel .name = "IPUv3EX", 57139b9004dSPhilipp Zabel .cm_ofs = 0x1e000000, 57239b9004dSPhilipp Zabel .cpmem_ofs = 0x1f000000, 57339b9004dSPhilipp Zabel .srm_ofs = 0x1f040000, 57439b9004dSPhilipp Zabel .tpm_ofs = 0x1f060000, 5752ffd48f2SSteve Longerbeam .csi0_ofs = 0x1f030000, 5762ffd48f2SSteve Longerbeam .csi1_ofs = 0x1f038000, 5771aa8ea0dSSteve Longerbeam .ic_ofs = 0x1f020000, 57839b9004dSPhilipp Zabel .disp0_ofs = 0x1e040000, 57939b9004dSPhilipp Zabel .disp1_ofs = 0x1e048000, 58039b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x1f080000, 58139b9004dSPhilipp Zabel .vdi_ofs = 0x1e068000, 58239b9004dSPhilipp Zabel .type = IPUV3EX, 58339b9004dSPhilipp Zabel }; 58439b9004dSPhilipp Zabel 58539b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx53 = { 58639b9004dSPhilipp Zabel .name = "IPUv3M", 58739b9004dSPhilipp Zabel .cm_ofs = 0x06000000, 58839b9004dSPhilipp Zabel .cpmem_ofs = 0x07000000, 58939b9004dSPhilipp Zabel .srm_ofs = 0x07040000, 59039b9004dSPhilipp Zabel .tpm_ofs = 0x07060000, 5912ffd48f2SSteve Longerbeam .csi0_ofs = 0x07030000, 5922ffd48f2SSteve Longerbeam .csi1_ofs = 0x07038000, 5931aa8ea0dSSteve Longerbeam .ic_ofs = 0x07020000, 59439b9004dSPhilipp Zabel .disp0_ofs = 0x06040000, 59539b9004dSPhilipp Zabel .disp1_ofs = 0x06048000, 59639b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x07080000, 59739b9004dSPhilipp Zabel .vdi_ofs = 0x06068000, 59839b9004dSPhilipp Zabel .type = IPUV3M, 59939b9004dSPhilipp Zabel }; 60039b9004dSPhilipp Zabel 60139b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx6q = { 60239b9004dSPhilipp Zabel .name = "IPUv3H", 60339b9004dSPhilipp Zabel .cm_ofs = 0x00200000, 60439b9004dSPhilipp Zabel .cpmem_ofs = 0x00300000, 60539b9004dSPhilipp Zabel .srm_ofs = 0x00340000, 60639b9004dSPhilipp Zabel .tpm_ofs = 0x00360000, 6072ffd48f2SSteve Longerbeam .csi0_ofs = 0x00230000, 6082ffd48f2SSteve Longerbeam .csi1_ofs = 0x00238000, 6091aa8ea0dSSteve Longerbeam .ic_ofs = 0x00220000, 61039b9004dSPhilipp Zabel .disp0_ofs = 0x00240000, 61139b9004dSPhilipp Zabel .disp1_ofs = 0x00248000, 61239b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x00380000, 61339b9004dSPhilipp Zabel .vdi_ofs = 0x00268000, 61439b9004dSPhilipp Zabel .type = IPUV3H, 61539b9004dSPhilipp Zabel }; 61639b9004dSPhilipp Zabel 61739b9004dSPhilipp Zabel static const struct of_device_id imx_ipu_dt_ids[] = { 61839b9004dSPhilipp Zabel { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, 61939b9004dSPhilipp Zabel { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, 62039b9004dSPhilipp Zabel { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, 62139b9004dSPhilipp Zabel { /* sentinel */ } 62239b9004dSPhilipp Zabel }; 62339b9004dSPhilipp Zabel MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); 62439b9004dSPhilipp Zabel 62539b9004dSPhilipp Zabel static int ipu_submodules_init(struct ipu_soc *ipu, 62639b9004dSPhilipp Zabel struct platform_device *pdev, unsigned long ipu_base, 62739b9004dSPhilipp Zabel struct clk *ipu_clk) 62839b9004dSPhilipp Zabel { 62939b9004dSPhilipp Zabel char *unit; 63039b9004dSPhilipp Zabel int ret; 63139b9004dSPhilipp Zabel struct device *dev = &pdev->dev; 63239b9004dSPhilipp Zabel const struct ipu_devtype *devtype = ipu->devtype; 63339b9004dSPhilipp Zabel 6347d2691daSSteve Longerbeam ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs); 6357d2691daSSteve Longerbeam if (ret) { 6367d2691daSSteve Longerbeam unit = "cpmem"; 6377d2691daSSteve Longerbeam goto err_cpmem; 6387d2691daSSteve Longerbeam } 6397d2691daSSteve Longerbeam 6402ffd48f2SSteve Longerbeam ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs, 6412ffd48f2SSteve Longerbeam IPU_CONF_CSI0_EN, ipu_clk); 6422ffd48f2SSteve Longerbeam if (ret) { 6432ffd48f2SSteve Longerbeam unit = "csi0"; 6442ffd48f2SSteve Longerbeam goto err_csi_0; 6452ffd48f2SSteve Longerbeam } 6462ffd48f2SSteve Longerbeam 6472ffd48f2SSteve Longerbeam ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs, 6482ffd48f2SSteve Longerbeam IPU_CONF_CSI1_EN, ipu_clk); 6492ffd48f2SSteve Longerbeam if (ret) { 6502ffd48f2SSteve Longerbeam unit = "csi1"; 6512ffd48f2SSteve Longerbeam goto err_csi_1; 6522ffd48f2SSteve Longerbeam } 6532ffd48f2SSteve Longerbeam 6541aa8ea0dSSteve Longerbeam ret = ipu_ic_init(ipu, dev, 6551aa8ea0dSSteve Longerbeam ipu_base + devtype->ic_ofs, 6561aa8ea0dSSteve Longerbeam ipu_base + devtype->tpm_ofs); 6571aa8ea0dSSteve Longerbeam if (ret) { 6581aa8ea0dSSteve Longerbeam unit = "ic"; 6591aa8ea0dSSteve Longerbeam goto err_ic; 6601aa8ea0dSSteve Longerbeam } 6611aa8ea0dSSteve Longerbeam 66239b9004dSPhilipp Zabel ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, 66339b9004dSPhilipp Zabel IPU_CONF_DI0_EN, ipu_clk); 66439b9004dSPhilipp Zabel if (ret) { 66539b9004dSPhilipp Zabel unit = "di0"; 66639b9004dSPhilipp Zabel goto err_di_0; 66739b9004dSPhilipp Zabel } 66839b9004dSPhilipp Zabel 66939b9004dSPhilipp Zabel ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs, 67039b9004dSPhilipp Zabel IPU_CONF_DI1_EN, ipu_clk); 67139b9004dSPhilipp Zabel if (ret) { 67239b9004dSPhilipp Zabel unit = "di1"; 67339b9004dSPhilipp Zabel goto err_di_1; 67439b9004dSPhilipp Zabel } 67539b9004dSPhilipp Zabel 67639b9004dSPhilipp Zabel ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs + 67739b9004dSPhilipp Zabel IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs); 67839b9004dSPhilipp Zabel if (ret) { 67939b9004dSPhilipp Zabel unit = "dc_template"; 68039b9004dSPhilipp Zabel goto err_dc; 68139b9004dSPhilipp Zabel } 68239b9004dSPhilipp Zabel 68339b9004dSPhilipp Zabel ret = ipu_dmfc_init(ipu, dev, ipu_base + 68439b9004dSPhilipp Zabel devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk); 68539b9004dSPhilipp Zabel if (ret) { 68639b9004dSPhilipp Zabel unit = "dmfc"; 68739b9004dSPhilipp Zabel goto err_dmfc; 68839b9004dSPhilipp Zabel } 68939b9004dSPhilipp Zabel 69039b9004dSPhilipp Zabel ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs); 69139b9004dSPhilipp Zabel if (ret) { 69239b9004dSPhilipp Zabel unit = "dp"; 69339b9004dSPhilipp Zabel goto err_dp; 69439b9004dSPhilipp Zabel } 69539b9004dSPhilipp Zabel 69635de925fSPhilipp Zabel ret = ipu_smfc_init(ipu, dev, ipu_base + 69735de925fSPhilipp Zabel devtype->cm_ofs + IPU_CM_SMFC_REG_OFS); 69835de925fSPhilipp Zabel if (ret) { 69935de925fSPhilipp Zabel unit = "smfc"; 70035de925fSPhilipp Zabel goto err_smfc; 70135de925fSPhilipp Zabel } 70235de925fSPhilipp Zabel 70339b9004dSPhilipp Zabel return 0; 70439b9004dSPhilipp Zabel 70535de925fSPhilipp Zabel err_smfc: 70635de925fSPhilipp Zabel ipu_dp_exit(ipu); 70739b9004dSPhilipp Zabel err_dp: 70839b9004dSPhilipp Zabel ipu_dmfc_exit(ipu); 70939b9004dSPhilipp Zabel err_dmfc: 71039b9004dSPhilipp Zabel ipu_dc_exit(ipu); 71139b9004dSPhilipp Zabel err_dc: 71239b9004dSPhilipp Zabel ipu_di_exit(ipu, 1); 71339b9004dSPhilipp Zabel err_di_1: 71439b9004dSPhilipp Zabel ipu_di_exit(ipu, 0); 71539b9004dSPhilipp Zabel err_di_0: 7161aa8ea0dSSteve Longerbeam ipu_ic_exit(ipu); 7171aa8ea0dSSteve Longerbeam err_ic: 7182ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 1); 7192ffd48f2SSteve Longerbeam err_csi_1: 7202ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 0); 7212ffd48f2SSteve Longerbeam err_csi_0: 7227d2691daSSteve Longerbeam ipu_cpmem_exit(ipu); 7237d2691daSSteve Longerbeam err_cpmem: 72439b9004dSPhilipp Zabel dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret); 72539b9004dSPhilipp Zabel return ret; 72639b9004dSPhilipp Zabel } 72739b9004dSPhilipp Zabel 72839b9004dSPhilipp Zabel static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs) 72939b9004dSPhilipp Zabel { 73039b9004dSPhilipp Zabel unsigned long status; 73139b9004dSPhilipp Zabel int i, bit, irq; 73239b9004dSPhilipp Zabel 73339b9004dSPhilipp Zabel for (i = 0; i < num_regs; i++) { 73439b9004dSPhilipp Zabel 73539b9004dSPhilipp Zabel status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i])); 73639b9004dSPhilipp Zabel status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i])); 73739b9004dSPhilipp Zabel 73839b9004dSPhilipp Zabel for_each_set_bit(bit, &status, 32) { 739682b7c1cSLinus Torvalds irq = irq_linear_revmap(ipu->domain, 740682b7c1cSLinus Torvalds regs[i] * 32 + bit); 74139b9004dSPhilipp Zabel if (irq) 74239b9004dSPhilipp Zabel generic_handle_irq(irq); 74339b9004dSPhilipp Zabel } 74439b9004dSPhilipp Zabel } 74539b9004dSPhilipp Zabel } 74639b9004dSPhilipp Zabel 74739b9004dSPhilipp Zabel static void ipu_irq_handler(unsigned int irq, struct irq_desc *desc) 74839b9004dSPhilipp Zabel { 74939b9004dSPhilipp Zabel struct ipu_soc *ipu = irq_desc_get_handler_data(desc); 75039b9004dSPhilipp Zabel const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14}; 75139b9004dSPhilipp Zabel struct irq_chip *chip = irq_get_chip(irq); 75239b9004dSPhilipp Zabel 75339b9004dSPhilipp Zabel chained_irq_enter(chip, desc); 75439b9004dSPhilipp Zabel 75539b9004dSPhilipp Zabel ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); 75639b9004dSPhilipp Zabel 75739b9004dSPhilipp Zabel chained_irq_exit(chip, desc); 75839b9004dSPhilipp Zabel } 75939b9004dSPhilipp Zabel 76039b9004dSPhilipp Zabel static void ipu_err_irq_handler(unsigned int irq, struct irq_desc *desc) 76139b9004dSPhilipp Zabel { 76239b9004dSPhilipp Zabel struct ipu_soc *ipu = irq_desc_get_handler_data(desc); 76339b9004dSPhilipp Zabel const int int_reg[] = { 4, 5, 8, 9}; 76439b9004dSPhilipp Zabel struct irq_chip *chip = irq_get_chip(irq); 76539b9004dSPhilipp Zabel 76639b9004dSPhilipp Zabel chained_irq_enter(chip, desc); 76739b9004dSPhilipp Zabel 76839b9004dSPhilipp Zabel ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); 76939b9004dSPhilipp Zabel 77039b9004dSPhilipp Zabel chained_irq_exit(chip, desc); 77139b9004dSPhilipp Zabel } 77239b9004dSPhilipp Zabel 773682b7c1cSLinus Torvalds int ipu_map_irq(struct ipu_soc *ipu, int irq) 774682b7c1cSLinus Torvalds { 775682b7c1cSLinus Torvalds int virq; 776682b7c1cSLinus Torvalds 777682b7c1cSLinus Torvalds virq = irq_linear_revmap(ipu->domain, irq); 778682b7c1cSLinus Torvalds if (!virq) 779682b7c1cSLinus Torvalds virq = irq_create_mapping(ipu->domain, irq); 780682b7c1cSLinus Torvalds 781682b7c1cSLinus Torvalds return virq; 782682b7c1cSLinus Torvalds } 783682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_map_irq); 784682b7c1cSLinus Torvalds 78539b9004dSPhilipp Zabel int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, 78639b9004dSPhilipp Zabel enum ipu_channel_irq irq_type) 78739b9004dSPhilipp Zabel { 788682b7c1cSLinus Torvalds return ipu_map_irq(ipu, irq_type + channel->num); 78939b9004dSPhilipp Zabel } 79039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq); 79139b9004dSPhilipp Zabel 79239b9004dSPhilipp Zabel static void ipu_submodules_exit(struct ipu_soc *ipu) 79339b9004dSPhilipp Zabel { 79435de925fSPhilipp Zabel ipu_smfc_exit(ipu); 79539b9004dSPhilipp Zabel ipu_dp_exit(ipu); 79639b9004dSPhilipp Zabel ipu_dmfc_exit(ipu); 79739b9004dSPhilipp Zabel ipu_dc_exit(ipu); 79839b9004dSPhilipp Zabel ipu_di_exit(ipu, 1); 79939b9004dSPhilipp Zabel ipu_di_exit(ipu, 0); 8001aa8ea0dSSteve Longerbeam ipu_ic_exit(ipu); 8012ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 1); 8022ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 0); 8037d2691daSSteve Longerbeam ipu_cpmem_exit(ipu); 80439b9004dSPhilipp Zabel } 80539b9004dSPhilipp Zabel 80639b9004dSPhilipp Zabel static int platform_remove_devices_fn(struct device *dev, void *unused) 80739b9004dSPhilipp Zabel { 80839b9004dSPhilipp Zabel struct platform_device *pdev = to_platform_device(dev); 80939b9004dSPhilipp Zabel 81039b9004dSPhilipp Zabel platform_device_unregister(pdev); 81139b9004dSPhilipp Zabel 81239b9004dSPhilipp Zabel return 0; 81339b9004dSPhilipp Zabel } 81439b9004dSPhilipp Zabel 81539b9004dSPhilipp Zabel static void platform_device_unregister_children(struct platform_device *pdev) 81639b9004dSPhilipp Zabel { 81739b9004dSPhilipp Zabel device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn); 81839b9004dSPhilipp Zabel } 81939b9004dSPhilipp Zabel 82039b9004dSPhilipp Zabel struct ipu_platform_reg { 82139b9004dSPhilipp Zabel struct ipu_client_platformdata pdata; 82239b9004dSPhilipp Zabel const char *name; 823d6ca8ca7SPhilipp Zabel int reg_offset; 82439b9004dSPhilipp Zabel }; 82539b9004dSPhilipp Zabel 82639b9004dSPhilipp Zabel static const struct ipu_platform_reg client_reg[] = { 82739b9004dSPhilipp Zabel { 82839b9004dSPhilipp Zabel .pdata = { 82939b9004dSPhilipp Zabel .di = 0, 83039b9004dSPhilipp Zabel .dc = 5, 83139b9004dSPhilipp Zabel .dp = IPU_DP_FLOW_SYNC_BG, 83239b9004dSPhilipp Zabel .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC, 83339b9004dSPhilipp Zabel .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC, 83439b9004dSPhilipp Zabel }, 83539b9004dSPhilipp Zabel .name = "imx-ipuv3-crtc", 83639b9004dSPhilipp Zabel }, { 83739b9004dSPhilipp Zabel .pdata = { 83839b9004dSPhilipp Zabel .di = 1, 83939b9004dSPhilipp Zabel .dc = 1, 84039b9004dSPhilipp Zabel .dp = -EINVAL, 84139b9004dSPhilipp Zabel .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC, 84239b9004dSPhilipp Zabel .dma[1] = -EINVAL, 84339b9004dSPhilipp Zabel }, 84439b9004dSPhilipp Zabel .name = "imx-ipuv3-crtc", 845d6ca8ca7SPhilipp Zabel }, { 846d6ca8ca7SPhilipp Zabel .pdata = { 847d6ca8ca7SPhilipp Zabel .csi = 0, 848d6ca8ca7SPhilipp Zabel .dma[0] = IPUV3_CHANNEL_CSI0, 849d6ca8ca7SPhilipp Zabel .dma[1] = -EINVAL, 850d6ca8ca7SPhilipp Zabel }, 851d6ca8ca7SPhilipp Zabel .reg_offset = IPU_CM_CSI0_REG_OFS, 852d6ca8ca7SPhilipp Zabel .name = "imx-ipuv3-camera", 853d6ca8ca7SPhilipp Zabel }, { 854d6ca8ca7SPhilipp Zabel .pdata = { 855d6ca8ca7SPhilipp Zabel .csi = 1, 856d6ca8ca7SPhilipp Zabel .dma[0] = IPUV3_CHANNEL_CSI1, 857d6ca8ca7SPhilipp Zabel .dma[1] = -EINVAL, 858d6ca8ca7SPhilipp Zabel }, 859d6ca8ca7SPhilipp Zabel .reg_offset = IPU_CM_CSI1_REG_OFS, 860d6ca8ca7SPhilipp Zabel .name = "imx-ipuv3-camera", 86139b9004dSPhilipp Zabel }, 86239b9004dSPhilipp Zabel }; 86339b9004dSPhilipp Zabel 86439b9004dSPhilipp Zabel static DEFINE_MUTEX(ipu_client_id_mutex); 86539b9004dSPhilipp Zabel static int ipu_client_id; 86639b9004dSPhilipp Zabel 867d6ca8ca7SPhilipp Zabel static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) 86839b9004dSPhilipp Zabel { 86939b9004dSPhilipp Zabel struct device *dev = ipu->dev; 87039b9004dSPhilipp Zabel unsigned i; 87139b9004dSPhilipp Zabel int id, ret; 87239b9004dSPhilipp Zabel 87339b9004dSPhilipp Zabel mutex_lock(&ipu_client_id_mutex); 87439b9004dSPhilipp Zabel id = ipu_client_id; 87539b9004dSPhilipp Zabel ipu_client_id += ARRAY_SIZE(client_reg); 87639b9004dSPhilipp Zabel mutex_unlock(&ipu_client_id_mutex); 87739b9004dSPhilipp Zabel 87839b9004dSPhilipp Zabel for (i = 0; i < ARRAY_SIZE(client_reg); i++) { 87939b9004dSPhilipp Zabel const struct ipu_platform_reg *reg = &client_reg[i]; 88039b9004dSPhilipp Zabel struct platform_device *pdev; 881d6ca8ca7SPhilipp Zabel struct resource res; 88239b9004dSPhilipp Zabel 883d6ca8ca7SPhilipp Zabel if (reg->reg_offset) { 884d6ca8ca7SPhilipp Zabel memset(&res, 0, sizeof(res)); 885d6ca8ca7SPhilipp Zabel res.flags = IORESOURCE_MEM; 886d6ca8ca7SPhilipp Zabel res.start = ipu_base + ipu->devtype->cm_ofs + reg->reg_offset; 887d6ca8ca7SPhilipp Zabel res.end = res.start + PAGE_SIZE - 1; 888d6ca8ca7SPhilipp Zabel pdev = platform_device_register_resndata(dev, reg->name, 889d6ca8ca7SPhilipp Zabel id++, &res, 1, ®->pdata, sizeof(reg->pdata)); 890d6ca8ca7SPhilipp Zabel } else { 89139b9004dSPhilipp Zabel pdev = platform_device_register_data(dev, reg->name, 89239b9004dSPhilipp Zabel id++, ®->pdata, sizeof(reg->pdata)); 893d6ca8ca7SPhilipp Zabel } 89439b9004dSPhilipp Zabel 89539b9004dSPhilipp Zabel if (IS_ERR(pdev)) 89639b9004dSPhilipp Zabel goto err_register; 89739b9004dSPhilipp Zabel } 89839b9004dSPhilipp Zabel 89939b9004dSPhilipp Zabel return 0; 90039b9004dSPhilipp Zabel 90139b9004dSPhilipp Zabel err_register: 90239b9004dSPhilipp Zabel platform_device_unregister_children(to_platform_device(dev)); 90339b9004dSPhilipp Zabel 90439b9004dSPhilipp Zabel return ret; 90539b9004dSPhilipp Zabel } 90639b9004dSPhilipp Zabel 90739b9004dSPhilipp Zabel 90839b9004dSPhilipp Zabel static int ipu_irq_init(struct ipu_soc *ipu) 90939b9004dSPhilipp Zabel { 91039b9004dSPhilipp Zabel struct irq_chip_generic *gc; 91139b9004dSPhilipp Zabel struct irq_chip_type *ct; 91239b9004dSPhilipp Zabel unsigned long unused[IPU_NUM_IRQS / 32] = { 91339b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 91439b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 91539b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 91639b9004dSPhilipp Zabel 0x4077ffff, 0xffe7e1fd, 91739b9004dSPhilipp Zabel 0x23fffffe, 0x8880fff0, 91839b9004dSPhilipp Zabel 0xf98fe7d0, 0xfff81fff, 91939b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 92039b9004dSPhilipp Zabel 0x00000000, 92139b9004dSPhilipp Zabel }; 92239b9004dSPhilipp Zabel int ret, i; 92339b9004dSPhilipp Zabel 92439b9004dSPhilipp Zabel ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS, 92539b9004dSPhilipp Zabel &irq_generic_chip_ops, ipu); 92639b9004dSPhilipp Zabel if (!ipu->domain) { 92739b9004dSPhilipp Zabel dev_err(ipu->dev, "failed to add irq domain\n"); 92839b9004dSPhilipp Zabel return -ENODEV; 92939b9004dSPhilipp Zabel } 93039b9004dSPhilipp Zabel 93139b9004dSPhilipp Zabel ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU", 932682b7c1cSLinus Torvalds handle_level_irq, 0, 933682b7c1cSLinus Torvalds IRQF_VALID, 0); 93439b9004dSPhilipp Zabel if (ret < 0) { 93539b9004dSPhilipp Zabel dev_err(ipu->dev, "failed to alloc generic irq chips\n"); 93639b9004dSPhilipp Zabel irq_domain_remove(ipu->domain); 93739b9004dSPhilipp Zabel return ret; 93839b9004dSPhilipp Zabel } 93939b9004dSPhilipp Zabel 94039b9004dSPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i += 32) { 94139b9004dSPhilipp Zabel gc = irq_get_domain_generic_chip(ipu->domain, i); 94239b9004dSPhilipp Zabel gc->reg_base = ipu->cm_reg; 94339b9004dSPhilipp Zabel gc->unused = unused[i / 32]; 94439b9004dSPhilipp Zabel ct = gc->chip_types; 94539b9004dSPhilipp Zabel ct->chip.irq_ack = irq_gc_ack_set_bit; 94639b9004dSPhilipp Zabel ct->chip.irq_mask = irq_gc_mask_clr_bit; 94739b9004dSPhilipp Zabel ct->chip.irq_unmask = irq_gc_mask_set_bit; 94839b9004dSPhilipp Zabel ct->regs.ack = IPU_INT_STAT(i / 32); 94939b9004dSPhilipp Zabel ct->regs.mask = IPU_INT_CTRL(i / 32); 95039b9004dSPhilipp Zabel } 95139b9004dSPhilipp Zabel 95239b9004dSPhilipp Zabel irq_set_chained_handler(ipu->irq_sync, ipu_irq_handler); 95339b9004dSPhilipp Zabel irq_set_handler_data(ipu->irq_sync, ipu); 95439b9004dSPhilipp Zabel irq_set_chained_handler(ipu->irq_err, ipu_err_irq_handler); 95539b9004dSPhilipp Zabel irq_set_handler_data(ipu->irq_err, ipu); 95639b9004dSPhilipp Zabel 95739b9004dSPhilipp Zabel return 0; 95839b9004dSPhilipp Zabel } 95939b9004dSPhilipp Zabel 96039b9004dSPhilipp Zabel static void ipu_irq_exit(struct ipu_soc *ipu) 96139b9004dSPhilipp Zabel { 96239b9004dSPhilipp Zabel int i, irq; 96339b9004dSPhilipp Zabel 96439b9004dSPhilipp Zabel irq_set_chained_handler(ipu->irq_err, NULL); 96539b9004dSPhilipp Zabel irq_set_handler_data(ipu->irq_err, NULL); 96639b9004dSPhilipp Zabel irq_set_chained_handler(ipu->irq_sync, NULL); 96739b9004dSPhilipp Zabel irq_set_handler_data(ipu->irq_sync, NULL); 96839b9004dSPhilipp Zabel 96939b9004dSPhilipp Zabel /* TODO: remove irq_domain_generic_chips */ 97039b9004dSPhilipp Zabel 97139b9004dSPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i++) { 97239b9004dSPhilipp Zabel irq = irq_linear_revmap(ipu->domain, i); 97339b9004dSPhilipp Zabel if (irq) 97439b9004dSPhilipp Zabel irq_dispose_mapping(irq); 97539b9004dSPhilipp Zabel } 97639b9004dSPhilipp Zabel 97739b9004dSPhilipp Zabel irq_domain_remove(ipu->domain); 97839b9004dSPhilipp Zabel } 97939b9004dSPhilipp Zabel 98039b9004dSPhilipp Zabel static int ipu_probe(struct platform_device *pdev) 98139b9004dSPhilipp Zabel { 98239b9004dSPhilipp Zabel const struct of_device_id *of_id = 98339b9004dSPhilipp Zabel of_match_device(imx_ipu_dt_ids, &pdev->dev); 98439b9004dSPhilipp Zabel struct ipu_soc *ipu; 98539b9004dSPhilipp Zabel struct resource *res; 98639b9004dSPhilipp Zabel unsigned long ipu_base; 98739b9004dSPhilipp Zabel int i, ret, irq_sync, irq_err; 98839b9004dSPhilipp Zabel const struct ipu_devtype *devtype; 98939b9004dSPhilipp Zabel 99039b9004dSPhilipp Zabel devtype = of_id->data; 99139b9004dSPhilipp Zabel 99239b9004dSPhilipp Zabel irq_sync = platform_get_irq(pdev, 0); 99339b9004dSPhilipp Zabel irq_err = platform_get_irq(pdev, 1); 99439b9004dSPhilipp Zabel res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 99539b9004dSPhilipp Zabel 99639b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n", 99739b9004dSPhilipp Zabel irq_sync, irq_err); 99839b9004dSPhilipp Zabel 99939b9004dSPhilipp Zabel if (!res || irq_sync < 0 || irq_err < 0) 100039b9004dSPhilipp Zabel return -ENODEV; 100139b9004dSPhilipp Zabel 100239b9004dSPhilipp Zabel ipu_base = res->start; 100339b9004dSPhilipp Zabel 100439b9004dSPhilipp Zabel ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL); 100539b9004dSPhilipp Zabel if (!ipu) 100639b9004dSPhilipp Zabel return -ENODEV; 100739b9004dSPhilipp Zabel 100839b9004dSPhilipp Zabel for (i = 0; i < 64; i++) 100939b9004dSPhilipp Zabel ipu->channel[i].ipu = ipu; 101039b9004dSPhilipp Zabel ipu->devtype = devtype; 101139b9004dSPhilipp Zabel ipu->ipu_type = devtype->type; 101239b9004dSPhilipp Zabel 101339b9004dSPhilipp Zabel spin_lock_init(&ipu->lock); 101439b9004dSPhilipp Zabel mutex_init(&ipu->channel_lock); 101539b9004dSPhilipp Zabel 101639b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n", 101739b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs); 101839b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "idmac: 0x%08lx\n", 101939b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS); 102039b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n", 102139b9004dSPhilipp Zabel ipu_base + devtype->cpmem_ofs); 10222ffd48f2SSteve Longerbeam dev_dbg(&pdev->dev, "csi0: 0x%08lx\n", 10232ffd48f2SSteve Longerbeam ipu_base + devtype->csi0_ofs); 10242ffd48f2SSteve Longerbeam dev_dbg(&pdev->dev, "csi1: 0x%08lx\n", 10252ffd48f2SSteve Longerbeam ipu_base + devtype->csi1_ofs); 10261aa8ea0dSSteve Longerbeam dev_dbg(&pdev->dev, "ic: 0x%08lx\n", 10271aa8ea0dSSteve Longerbeam ipu_base + devtype->ic_ofs); 102839b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "disp0: 0x%08lx\n", 102939b9004dSPhilipp Zabel ipu_base + devtype->disp0_ofs); 103039b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "disp1: 0x%08lx\n", 103139b9004dSPhilipp Zabel ipu_base + devtype->disp1_ofs); 103239b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "srm: 0x%08lx\n", 103339b9004dSPhilipp Zabel ipu_base + devtype->srm_ofs); 103439b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "tpm: 0x%08lx\n", 103539b9004dSPhilipp Zabel ipu_base + devtype->tpm_ofs); 103639b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "dc: 0x%08lx\n", 103739b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS); 103839b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "ic: 0x%08lx\n", 103939b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS); 104039b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n", 104139b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS); 104239b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "vdi: 0x%08lx\n", 104339b9004dSPhilipp Zabel ipu_base + devtype->vdi_ofs); 104439b9004dSPhilipp Zabel 104539b9004dSPhilipp Zabel ipu->cm_reg = devm_ioremap(&pdev->dev, 104639b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs, PAGE_SIZE); 104739b9004dSPhilipp Zabel ipu->idmac_reg = devm_ioremap(&pdev->dev, 104839b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS, 104939b9004dSPhilipp Zabel PAGE_SIZE); 105039b9004dSPhilipp Zabel 10517d2691daSSteve Longerbeam if (!ipu->cm_reg || !ipu->idmac_reg) 105239b9004dSPhilipp Zabel return -ENOMEM; 105339b9004dSPhilipp Zabel 105439b9004dSPhilipp Zabel ipu->clk = devm_clk_get(&pdev->dev, "bus"); 105539b9004dSPhilipp Zabel if (IS_ERR(ipu->clk)) { 105639b9004dSPhilipp Zabel ret = PTR_ERR(ipu->clk); 105739b9004dSPhilipp Zabel dev_err(&pdev->dev, "clk_get failed with %d", ret); 105839b9004dSPhilipp Zabel return ret; 105939b9004dSPhilipp Zabel } 106039b9004dSPhilipp Zabel 106139b9004dSPhilipp Zabel platform_set_drvdata(pdev, ipu); 106239b9004dSPhilipp Zabel 106339b9004dSPhilipp Zabel ret = clk_prepare_enable(ipu->clk); 106439b9004dSPhilipp Zabel if (ret) { 106539b9004dSPhilipp Zabel dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); 106639b9004dSPhilipp Zabel return ret; 106739b9004dSPhilipp Zabel } 106839b9004dSPhilipp Zabel 106939b9004dSPhilipp Zabel ipu->dev = &pdev->dev; 107039b9004dSPhilipp Zabel ipu->irq_sync = irq_sync; 107139b9004dSPhilipp Zabel ipu->irq_err = irq_err; 107239b9004dSPhilipp Zabel 107339b9004dSPhilipp Zabel ret = ipu_irq_init(ipu); 107439b9004dSPhilipp Zabel if (ret) 107539b9004dSPhilipp Zabel goto out_failed_irq; 107639b9004dSPhilipp Zabel 107739b9004dSPhilipp Zabel ret = device_reset(&pdev->dev); 107839b9004dSPhilipp Zabel if (ret) { 107939b9004dSPhilipp Zabel dev_err(&pdev->dev, "failed to reset: %d\n", ret); 108039b9004dSPhilipp Zabel goto out_failed_reset; 108139b9004dSPhilipp Zabel } 108239b9004dSPhilipp Zabel ret = ipu_memory_reset(ipu); 108339b9004dSPhilipp Zabel if (ret) 108439b9004dSPhilipp Zabel goto out_failed_reset; 108539b9004dSPhilipp Zabel 108639b9004dSPhilipp Zabel /* Set MCU_T to divide MCU access window into 2 */ 108739b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), 108839b9004dSPhilipp Zabel IPU_DISP_GEN); 108939b9004dSPhilipp Zabel 109039b9004dSPhilipp Zabel ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk); 109139b9004dSPhilipp Zabel if (ret) 109239b9004dSPhilipp Zabel goto failed_submodules_init; 109339b9004dSPhilipp Zabel 1094d6ca8ca7SPhilipp Zabel ret = ipu_add_client_devices(ipu, ipu_base); 109539b9004dSPhilipp Zabel if (ret) { 109639b9004dSPhilipp Zabel dev_err(&pdev->dev, "adding client devices failed with %d\n", 109739b9004dSPhilipp Zabel ret); 109839b9004dSPhilipp Zabel goto failed_add_clients; 109939b9004dSPhilipp Zabel } 110039b9004dSPhilipp Zabel 110139b9004dSPhilipp Zabel dev_info(&pdev->dev, "%s probed\n", devtype->name); 110239b9004dSPhilipp Zabel 110339b9004dSPhilipp Zabel return 0; 110439b9004dSPhilipp Zabel 110539b9004dSPhilipp Zabel failed_add_clients: 110639b9004dSPhilipp Zabel ipu_submodules_exit(ipu); 110739b9004dSPhilipp Zabel failed_submodules_init: 110839b9004dSPhilipp Zabel out_failed_reset: 110939b9004dSPhilipp Zabel ipu_irq_exit(ipu); 111039b9004dSPhilipp Zabel out_failed_irq: 111139b9004dSPhilipp Zabel clk_disable_unprepare(ipu->clk); 111239b9004dSPhilipp Zabel return ret; 111339b9004dSPhilipp Zabel } 111439b9004dSPhilipp Zabel 111539b9004dSPhilipp Zabel static int ipu_remove(struct platform_device *pdev) 111639b9004dSPhilipp Zabel { 111739b9004dSPhilipp Zabel struct ipu_soc *ipu = platform_get_drvdata(pdev); 111839b9004dSPhilipp Zabel 111939b9004dSPhilipp Zabel platform_device_unregister_children(pdev); 112039b9004dSPhilipp Zabel ipu_submodules_exit(ipu); 112139b9004dSPhilipp Zabel ipu_irq_exit(ipu); 112239b9004dSPhilipp Zabel 112339b9004dSPhilipp Zabel clk_disable_unprepare(ipu->clk); 112439b9004dSPhilipp Zabel 112539b9004dSPhilipp Zabel return 0; 112639b9004dSPhilipp Zabel } 112739b9004dSPhilipp Zabel 112839b9004dSPhilipp Zabel static struct platform_driver imx_ipu_driver = { 112939b9004dSPhilipp Zabel .driver = { 113039b9004dSPhilipp Zabel .name = "imx-ipuv3", 113139b9004dSPhilipp Zabel .of_match_table = imx_ipu_dt_ids, 113239b9004dSPhilipp Zabel }, 113339b9004dSPhilipp Zabel .probe = ipu_probe, 113439b9004dSPhilipp Zabel .remove = ipu_remove, 113539b9004dSPhilipp Zabel }; 113639b9004dSPhilipp Zabel 113739b9004dSPhilipp Zabel module_platform_driver(imx_ipu_driver); 113839b9004dSPhilipp Zabel 113939b9004dSPhilipp Zabel MODULE_ALIAS("platform:imx-ipuv3"); 114039b9004dSPhilipp Zabel MODULE_DESCRIPTION("i.MX IPU v3 driver"); 114139b9004dSPhilipp Zabel MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); 114239b9004dSPhilipp Zabel MODULE_LICENSE("GPL"); 1143