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 104ae0e9708SSteve Longerbeam enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code) 105ae0e9708SSteve Longerbeam { 106ae0e9708SSteve Longerbeam switch (mbus_code & 0xf000) { 107ae0e9708SSteve Longerbeam case 0x1000: 108ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_RGB; 109ae0e9708SSteve Longerbeam case 0x2000: 110ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_YUV; 111ae0e9708SSteve Longerbeam default: 112ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_UNKNOWN; 113ae0e9708SSteve Longerbeam } 114ae0e9708SSteve Longerbeam } 115ae0e9708SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace); 116ae0e9708SSteve Longerbeam 117f835f386SSteve Longerbeam int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees, 118f835f386SSteve Longerbeam bool hflip, bool vflip) 119f835f386SSteve Longerbeam { 120f835f386SSteve Longerbeam u32 r90, vf, hf; 121f835f386SSteve Longerbeam 122f835f386SSteve Longerbeam switch (degrees) { 123f835f386SSteve Longerbeam case 0: 124f835f386SSteve Longerbeam vf = hf = r90 = 0; 125f835f386SSteve Longerbeam break; 126f835f386SSteve Longerbeam case 90: 127f835f386SSteve Longerbeam vf = hf = 0; 128f835f386SSteve Longerbeam r90 = 1; 129f835f386SSteve Longerbeam break; 130f835f386SSteve Longerbeam case 180: 131f835f386SSteve Longerbeam vf = hf = 1; 132f835f386SSteve Longerbeam r90 = 0; 133f835f386SSteve Longerbeam break; 134f835f386SSteve Longerbeam case 270: 135f835f386SSteve Longerbeam vf = hf = r90 = 1; 136f835f386SSteve Longerbeam break; 137f835f386SSteve Longerbeam default: 138f835f386SSteve Longerbeam return -EINVAL; 139f835f386SSteve Longerbeam } 140f835f386SSteve Longerbeam 141f835f386SSteve Longerbeam hf ^= (u32)hflip; 142f835f386SSteve Longerbeam vf ^= (u32)vflip; 143f835f386SSteve Longerbeam 144f835f386SSteve Longerbeam *mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf); 145f835f386SSteve Longerbeam return 0; 146f835f386SSteve Longerbeam } 147f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode); 148f835f386SSteve Longerbeam 149f835f386SSteve Longerbeam int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode, 150f835f386SSteve Longerbeam bool hflip, bool vflip) 151f835f386SSteve Longerbeam { 152f835f386SSteve Longerbeam u32 r90, vf, hf; 153f835f386SSteve Longerbeam 154f835f386SSteve Longerbeam r90 = ((u32)mode >> 2) & 0x1; 155f835f386SSteve Longerbeam hf = ((u32)mode >> 1) & 0x1; 156f835f386SSteve Longerbeam vf = ((u32)mode >> 0) & 0x1; 157f835f386SSteve Longerbeam hf ^= (u32)hflip; 158f835f386SSteve Longerbeam vf ^= (u32)vflip; 159f835f386SSteve Longerbeam 160f835f386SSteve Longerbeam switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) { 161f835f386SSteve Longerbeam case IPU_ROTATE_NONE: 162f835f386SSteve Longerbeam *degrees = 0; 163f835f386SSteve Longerbeam break; 164f835f386SSteve Longerbeam case IPU_ROTATE_90_RIGHT: 165f835f386SSteve Longerbeam *degrees = 90; 166f835f386SSteve Longerbeam break; 167f835f386SSteve Longerbeam case IPU_ROTATE_180: 168f835f386SSteve Longerbeam *degrees = 180; 169f835f386SSteve Longerbeam break; 170f835f386SSteve Longerbeam case IPU_ROTATE_90_LEFT: 171f835f386SSteve Longerbeam *degrees = 270; 172f835f386SSteve Longerbeam break; 173f835f386SSteve Longerbeam default: 174f835f386SSteve Longerbeam return -EINVAL; 175f835f386SSteve Longerbeam } 176f835f386SSteve Longerbeam 177f835f386SSteve Longerbeam return 0; 178f835f386SSteve Longerbeam } 179f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees); 180f835f386SSteve Longerbeam 18139b9004dSPhilipp Zabel struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num) 18239b9004dSPhilipp Zabel { 18339b9004dSPhilipp Zabel struct ipuv3_channel *channel; 18439b9004dSPhilipp Zabel 18539b9004dSPhilipp Zabel dev_dbg(ipu->dev, "%s %d\n", __func__, num); 18639b9004dSPhilipp Zabel 18739b9004dSPhilipp Zabel if (num > 63) 18839b9004dSPhilipp Zabel return ERR_PTR(-ENODEV); 18939b9004dSPhilipp Zabel 19039b9004dSPhilipp Zabel mutex_lock(&ipu->channel_lock); 19139b9004dSPhilipp Zabel 19239b9004dSPhilipp Zabel channel = &ipu->channel[num]; 19339b9004dSPhilipp Zabel 19439b9004dSPhilipp Zabel if (channel->busy) { 19539b9004dSPhilipp Zabel channel = ERR_PTR(-EBUSY); 19639b9004dSPhilipp Zabel goto out; 19739b9004dSPhilipp Zabel } 19839b9004dSPhilipp Zabel 19939b9004dSPhilipp Zabel channel->busy = true; 20039b9004dSPhilipp Zabel channel->num = num; 20139b9004dSPhilipp Zabel 20239b9004dSPhilipp Zabel out: 20339b9004dSPhilipp Zabel mutex_unlock(&ipu->channel_lock); 20439b9004dSPhilipp Zabel 20539b9004dSPhilipp Zabel return channel; 20639b9004dSPhilipp Zabel } 20739b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get); 20839b9004dSPhilipp Zabel 20939b9004dSPhilipp Zabel void ipu_idmac_put(struct ipuv3_channel *channel) 21039b9004dSPhilipp Zabel { 21139b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 21239b9004dSPhilipp Zabel 21339b9004dSPhilipp Zabel dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num); 21439b9004dSPhilipp Zabel 21539b9004dSPhilipp Zabel mutex_lock(&ipu->channel_lock); 21639b9004dSPhilipp Zabel 21739b9004dSPhilipp Zabel channel->busy = false; 21839b9004dSPhilipp Zabel 21939b9004dSPhilipp Zabel mutex_unlock(&ipu->channel_lock); 22039b9004dSPhilipp Zabel } 22139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_put); 22239b9004dSPhilipp Zabel 22339b9004dSPhilipp Zabel #define idma_mask(ch) (1 << (ch & 0x1f)) 22439b9004dSPhilipp Zabel 22539b9004dSPhilipp Zabel void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel, 22639b9004dSPhilipp Zabel bool doublebuffer) 22739b9004dSPhilipp Zabel { 22839b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 22939b9004dSPhilipp Zabel unsigned long flags; 23039b9004dSPhilipp Zabel u32 reg; 23139b9004dSPhilipp Zabel 23239b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 23339b9004dSPhilipp Zabel 23439b9004dSPhilipp Zabel reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); 23539b9004dSPhilipp Zabel if (doublebuffer) 23639b9004dSPhilipp Zabel reg |= idma_mask(channel->num); 23739b9004dSPhilipp Zabel else 23839b9004dSPhilipp Zabel reg &= ~idma_mask(channel->num); 23939b9004dSPhilipp Zabel ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num)); 24039b9004dSPhilipp Zabel 24139b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 24239b9004dSPhilipp Zabel } 24339b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer); 24439b9004dSPhilipp Zabel 24539b9004dSPhilipp Zabel int ipu_module_enable(struct ipu_soc *ipu, u32 mask) 24639b9004dSPhilipp Zabel { 24739b9004dSPhilipp Zabel unsigned long lock_flags; 24839b9004dSPhilipp Zabel u32 val; 24939b9004dSPhilipp Zabel 25039b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, lock_flags); 25139b9004dSPhilipp Zabel 25239b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_DISP_GEN); 25339b9004dSPhilipp Zabel 25439b9004dSPhilipp Zabel if (mask & IPU_CONF_DI0_EN) 25539b9004dSPhilipp Zabel val |= IPU_DI0_COUNTER_RELEASE; 25639b9004dSPhilipp Zabel if (mask & IPU_CONF_DI1_EN) 25739b9004dSPhilipp Zabel val |= IPU_DI1_COUNTER_RELEASE; 25839b9004dSPhilipp Zabel 25939b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_DISP_GEN); 26039b9004dSPhilipp Zabel 26139b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CONF); 26239b9004dSPhilipp Zabel val |= mask; 26339b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CONF); 26439b9004dSPhilipp Zabel 26539b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, lock_flags); 26639b9004dSPhilipp Zabel 26739b9004dSPhilipp Zabel return 0; 26839b9004dSPhilipp Zabel } 26939b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_enable); 27039b9004dSPhilipp Zabel 27139b9004dSPhilipp Zabel int ipu_module_disable(struct ipu_soc *ipu, u32 mask) 27239b9004dSPhilipp Zabel { 27339b9004dSPhilipp Zabel unsigned long lock_flags; 27439b9004dSPhilipp Zabel u32 val; 27539b9004dSPhilipp Zabel 27639b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, lock_flags); 27739b9004dSPhilipp Zabel 27839b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CONF); 27939b9004dSPhilipp Zabel val &= ~mask; 28039b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CONF); 28139b9004dSPhilipp Zabel 28239b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_DISP_GEN); 28339b9004dSPhilipp Zabel 28439b9004dSPhilipp Zabel if (mask & IPU_CONF_DI0_EN) 28539b9004dSPhilipp Zabel val &= ~IPU_DI0_COUNTER_RELEASE; 28639b9004dSPhilipp Zabel if (mask & IPU_CONF_DI1_EN) 28739b9004dSPhilipp Zabel val &= ~IPU_DI1_COUNTER_RELEASE; 28839b9004dSPhilipp Zabel 28939b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_DISP_GEN); 29039b9004dSPhilipp Zabel 29139b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, lock_flags); 29239b9004dSPhilipp Zabel 29339b9004dSPhilipp Zabel return 0; 29439b9004dSPhilipp Zabel } 29539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_disable); 29639b9004dSPhilipp Zabel 297e9046097SPhilipp Zabel int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel) 298e9046097SPhilipp Zabel { 299e9046097SPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 300e9046097SPhilipp Zabel unsigned int chno = channel->num; 301e9046097SPhilipp Zabel 302e9046097SPhilipp Zabel return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0; 303e9046097SPhilipp Zabel } 304e9046097SPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer); 305e9046097SPhilipp Zabel 30639b9004dSPhilipp Zabel void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num) 30739b9004dSPhilipp Zabel { 30839b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 30939b9004dSPhilipp Zabel unsigned int chno = channel->num; 31039b9004dSPhilipp Zabel unsigned long flags; 31139b9004dSPhilipp Zabel 31239b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 31339b9004dSPhilipp Zabel 31439b9004dSPhilipp Zabel /* Mark buffer as ready. */ 31539b9004dSPhilipp Zabel if (buf_num == 0) 31639b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); 31739b9004dSPhilipp Zabel else 31839b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); 31939b9004dSPhilipp Zabel 32039b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 32139b9004dSPhilipp Zabel } 32239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer); 32339b9004dSPhilipp Zabel 32439b9004dSPhilipp Zabel int ipu_idmac_enable_channel(struct ipuv3_channel *channel) 32539b9004dSPhilipp Zabel { 32639b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 32739b9004dSPhilipp Zabel u32 val; 32839b9004dSPhilipp Zabel unsigned long flags; 32939b9004dSPhilipp Zabel 33039b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 33139b9004dSPhilipp Zabel 33239b9004dSPhilipp Zabel val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); 33339b9004dSPhilipp Zabel val |= idma_mask(channel->num); 33439b9004dSPhilipp Zabel ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); 33539b9004dSPhilipp Zabel 33639b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 33739b9004dSPhilipp Zabel 33839b9004dSPhilipp Zabel return 0; 33939b9004dSPhilipp Zabel } 34039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel); 34139b9004dSPhilipp Zabel 342682b7c1cSLinus Torvalds bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno) 343682b7c1cSLinus Torvalds { 344682b7c1cSLinus Torvalds return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno)); 345682b7c1cSLinus Torvalds } 346682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy); 347682b7c1cSLinus Torvalds 34839b9004dSPhilipp Zabel int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) 34939b9004dSPhilipp Zabel { 35039b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 35139b9004dSPhilipp Zabel unsigned long timeout; 35239b9004dSPhilipp Zabel 35339b9004dSPhilipp Zabel timeout = jiffies + msecs_to_jiffies(ms); 35439b9004dSPhilipp Zabel while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) & 35539b9004dSPhilipp Zabel idma_mask(channel->num)) { 35639b9004dSPhilipp Zabel if (time_after(jiffies, timeout)) 35739b9004dSPhilipp Zabel return -ETIMEDOUT; 35839b9004dSPhilipp Zabel cpu_relax(); 35939b9004dSPhilipp Zabel } 36039b9004dSPhilipp Zabel 36139b9004dSPhilipp Zabel return 0; 36239b9004dSPhilipp Zabel } 36339b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); 36439b9004dSPhilipp Zabel 365682b7c1cSLinus Torvalds int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms) 366682b7c1cSLinus Torvalds { 367682b7c1cSLinus Torvalds unsigned long timeout; 368682b7c1cSLinus Torvalds 369682b7c1cSLinus Torvalds timeout = jiffies + msecs_to_jiffies(ms); 370682b7c1cSLinus Torvalds ipu_cm_write(ipu, BIT(irq % 32), IPU_INT_STAT(irq / 32)); 371682b7c1cSLinus Torvalds while (!(ipu_cm_read(ipu, IPU_INT_STAT(irq / 32) & BIT(irq % 32)))) { 372682b7c1cSLinus Torvalds if (time_after(jiffies, timeout)) 373682b7c1cSLinus Torvalds return -ETIMEDOUT; 374682b7c1cSLinus Torvalds cpu_relax(); 375682b7c1cSLinus Torvalds } 376682b7c1cSLinus Torvalds 377682b7c1cSLinus Torvalds return 0; 378682b7c1cSLinus Torvalds } 379682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_wait_interrupt); 380682b7c1cSLinus Torvalds 38139b9004dSPhilipp Zabel int ipu_idmac_disable_channel(struct ipuv3_channel *channel) 38239b9004dSPhilipp Zabel { 38339b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 38439b9004dSPhilipp Zabel u32 val; 38539b9004dSPhilipp Zabel unsigned long flags; 38639b9004dSPhilipp Zabel 38739b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 38839b9004dSPhilipp Zabel 38939b9004dSPhilipp Zabel /* Disable DMA channel(s) */ 39039b9004dSPhilipp Zabel val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); 39139b9004dSPhilipp Zabel val &= ~idma_mask(channel->num); 39239b9004dSPhilipp Zabel ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); 39339b9004dSPhilipp Zabel 39439b9004dSPhilipp Zabel /* Set channel buffers NOT to be ready */ 39539b9004dSPhilipp Zabel ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */ 39639b9004dSPhilipp Zabel 39739b9004dSPhilipp Zabel if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) & 39839b9004dSPhilipp Zabel idma_mask(channel->num)) { 39939b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(channel->num), 40039b9004dSPhilipp Zabel IPU_CHA_BUF0_RDY(channel->num)); 40139b9004dSPhilipp Zabel } 40239b9004dSPhilipp Zabel 40339b9004dSPhilipp Zabel if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) & 40439b9004dSPhilipp Zabel idma_mask(channel->num)) { 40539b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(channel->num), 40639b9004dSPhilipp Zabel IPU_CHA_BUF1_RDY(channel->num)); 40739b9004dSPhilipp Zabel } 40839b9004dSPhilipp Zabel 40939b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ 41039b9004dSPhilipp Zabel 41139b9004dSPhilipp Zabel /* Reset the double buffer */ 41239b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); 41339b9004dSPhilipp Zabel val &= ~idma_mask(channel->num); 41439b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num)); 41539b9004dSPhilipp Zabel 41639b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 41739b9004dSPhilipp Zabel 41839b9004dSPhilipp Zabel return 0; 41939b9004dSPhilipp Zabel } 42039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel); 42139b9004dSPhilipp Zabel 42239b9004dSPhilipp Zabel static int ipu_memory_reset(struct ipu_soc *ipu) 42339b9004dSPhilipp Zabel { 42439b9004dSPhilipp Zabel unsigned long timeout; 42539b9004dSPhilipp Zabel 42639b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); 42739b9004dSPhilipp Zabel 42839b9004dSPhilipp Zabel timeout = jiffies + msecs_to_jiffies(1000); 42939b9004dSPhilipp Zabel while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { 43039b9004dSPhilipp Zabel if (time_after(jiffies, timeout)) 43139b9004dSPhilipp Zabel return -ETIME; 43239b9004dSPhilipp Zabel cpu_relax(); 43339b9004dSPhilipp Zabel } 43439b9004dSPhilipp Zabel 43539b9004dSPhilipp Zabel return 0; 43639b9004dSPhilipp Zabel } 43739b9004dSPhilipp Zabel 438ba07975fSSteve Longerbeam /* 439ba07975fSSteve Longerbeam * Set the source mux for the given CSI. Selects either parallel or 440ba07975fSSteve Longerbeam * MIPI CSI2 sources. 441ba07975fSSteve Longerbeam */ 442ba07975fSSteve Longerbeam void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2) 443ba07975fSSteve Longerbeam { 444ba07975fSSteve Longerbeam unsigned long flags; 445ba07975fSSteve Longerbeam u32 val, mask; 446ba07975fSSteve Longerbeam 447ba07975fSSteve Longerbeam mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE : 448ba07975fSSteve Longerbeam IPU_CONF_CSI0_DATA_SOURCE; 449ba07975fSSteve Longerbeam 450ba07975fSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 451ba07975fSSteve Longerbeam 452ba07975fSSteve Longerbeam val = ipu_cm_read(ipu, IPU_CONF); 453ba07975fSSteve Longerbeam if (mipi_csi2) 454ba07975fSSteve Longerbeam val |= mask; 455ba07975fSSteve Longerbeam else 456ba07975fSSteve Longerbeam val &= ~mask; 457ba07975fSSteve Longerbeam ipu_cm_write(ipu, val, IPU_CONF); 458ba07975fSSteve Longerbeam 459ba07975fSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 460ba07975fSSteve Longerbeam } 461ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux); 462ba07975fSSteve Longerbeam 463ba07975fSSteve Longerbeam /* 464ba07975fSSteve Longerbeam * Set the source mux for the IC. Selects either CSI[01] or the VDI. 465ba07975fSSteve Longerbeam */ 466ba07975fSSteve Longerbeam void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi) 467ba07975fSSteve Longerbeam { 468ba07975fSSteve Longerbeam unsigned long flags; 469ba07975fSSteve Longerbeam u32 val; 470ba07975fSSteve Longerbeam 471ba07975fSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 472ba07975fSSteve Longerbeam 473ba07975fSSteve Longerbeam val = ipu_cm_read(ipu, IPU_CONF); 474ba07975fSSteve Longerbeam if (vdi) { 475ba07975fSSteve Longerbeam val |= IPU_CONF_IC_INPUT; 476ba07975fSSteve Longerbeam } else { 477ba07975fSSteve Longerbeam val &= ~IPU_CONF_IC_INPUT; 478ba07975fSSteve Longerbeam if (csi_id == 1) 479ba07975fSSteve Longerbeam val |= IPU_CONF_CSI_SEL; 480ba07975fSSteve Longerbeam else 481ba07975fSSteve Longerbeam val &= ~IPU_CONF_CSI_SEL; 482ba07975fSSteve Longerbeam } 483ba07975fSSteve Longerbeam ipu_cm_write(ipu, val, IPU_CONF); 484ba07975fSSteve Longerbeam 485ba07975fSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 486ba07975fSSteve Longerbeam } 487ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux); 488ba07975fSSteve Longerbeam 48939b9004dSPhilipp Zabel struct ipu_devtype { 49039b9004dSPhilipp Zabel const char *name; 49139b9004dSPhilipp Zabel unsigned long cm_ofs; 49239b9004dSPhilipp Zabel unsigned long cpmem_ofs; 49339b9004dSPhilipp Zabel unsigned long srm_ofs; 49439b9004dSPhilipp Zabel unsigned long tpm_ofs; 4952ffd48f2SSteve Longerbeam unsigned long csi0_ofs; 4962ffd48f2SSteve Longerbeam unsigned long csi1_ofs; 4971aa8ea0dSSteve Longerbeam unsigned long ic_ofs; 49839b9004dSPhilipp Zabel unsigned long disp0_ofs; 49939b9004dSPhilipp Zabel unsigned long disp1_ofs; 50039b9004dSPhilipp Zabel unsigned long dc_tmpl_ofs; 50139b9004dSPhilipp Zabel unsigned long vdi_ofs; 50239b9004dSPhilipp Zabel enum ipuv3_type type; 50339b9004dSPhilipp Zabel }; 50439b9004dSPhilipp Zabel 50539b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx51 = { 50639b9004dSPhilipp Zabel .name = "IPUv3EX", 50739b9004dSPhilipp Zabel .cm_ofs = 0x1e000000, 50839b9004dSPhilipp Zabel .cpmem_ofs = 0x1f000000, 50939b9004dSPhilipp Zabel .srm_ofs = 0x1f040000, 51039b9004dSPhilipp Zabel .tpm_ofs = 0x1f060000, 5112ffd48f2SSteve Longerbeam .csi0_ofs = 0x1f030000, 5122ffd48f2SSteve Longerbeam .csi1_ofs = 0x1f038000, 5131aa8ea0dSSteve Longerbeam .ic_ofs = 0x1f020000, 51439b9004dSPhilipp Zabel .disp0_ofs = 0x1e040000, 51539b9004dSPhilipp Zabel .disp1_ofs = 0x1e048000, 51639b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x1f080000, 51739b9004dSPhilipp Zabel .vdi_ofs = 0x1e068000, 51839b9004dSPhilipp Zabel .type = IPUV3EX, 51939b9004dSPhilipp Zabel }; 52039b9004dSPhilipp Zabel 52139b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx53 = { 52239b9004dSPhilipp Zabel .name = "IPUv3M", 52339b9004dSPhilipp Zabel .cm_ofs = 0x06000000, 52439b9004dSPhilipp Zabel .cpmem_ofs = 0x07000000, 52539b9004dSPhilipp Zabel .srm_ofs = 0x07040000, 52639b9004dSPhilipp Zabel .tpm_ofs = 0x07060000, 5272ffd48f2SSteve Longerbeam .csi0_ofs = 0x07030000, 5282ffd48f2SSteve Longerbeam .csi1_ofs = 0x07038000, 5291aa8ea0dSSteve Longerbeam .ic_ofs = 0x07020000, 53039b9004dSPhilipp Zabel .disp0_ofs = 0x06040000, 53139b9004dSPhilipp Zabel .disp1_ofs = 0x06048000, 53239b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x07080000, 53339b9004dSPhilipp Zabel .vdi_ofs = 0x06068000, 53439b9004dSPhilipp Zabel .type = IPUV3M, 53539b9004dSPhilipp Zabel }; 53639b9004dSPhilipp Zabel 53739b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx6q = { 53839b9004dSPhilipp Zabel .name = "IPUv3H", 53939b9004dSPhilipp Zabel .cm_ofs = 0x00200000, 54039b9004dSPhilipp Zabel .cpmem_ofs = 0x00300000, 54139b9004dSPhilipp Zabel .srm_ofs = 0x00340000, 54239b9004dSPhilipp Zabel .tpm_ofs = 0x00360000, 5432ffd48f2SSteve Longerbeam .csi0_ofs = 0x00230000, 5442ffd48f2SSteve Longerbeam .csi1_ofs = 0x00238000, 5451aa8ea0dSSteve Longerbeam .ic_ofs = 0x00220000, 54639b9004dSPhilipp Zabel .disp0_ofs = 0x00240000, 54739b9004dSPhilipp Zabel .disp1_ofs = 0x00248000, 54839b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x00380000, 54939b9004dSPhilipp Zabel .vdi_ofs = 0x00268000, 55039b9004dSPhilipp Zabel .type = IPUV3H, 55139b9004dSPhilipp Zabel }; 55239b9004dSPhilipp Zabel 55339b9004dSPhilipp Zabel static const struct of_device_id imx_ipu_dt_ids[] = { 55439b9004dSPhilipp Zabel { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, 55539b9004dSPhilipp Zabel { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, 55639b9004dSPhilipp Zabel { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, 55739b9004dSPhilipp Zabel { /* sentinel */ } 55839b9004dSPhilipp Zabel }; 55939b9004dSPhilipp Zabel MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); 56039b9004dSPhilipp Zabel 56139b9004dSPhilipp Zabel static int ipu_submodules_init(struct ipu_soc *ipu, 56239b9004dSPhilipp Zabel struct platform_device *pdev, unsigned long ipu_base, 56339b9004dSPhilipp Zabel struct clk *ipu_clk) 56439b9004dSPhilipp Zabel { 56539b9004dSPhilipp Zabel char *unit; 56639b9004dSPhilipp Zabel int ret; 56739b9004dSPhilipp Zabel struct device *dev = &pdev->dev; 56839b9004dSPhilipp Zabel const struct ipu_devtype *devtype = ipu->devtype; 56939b9004dSPhilipp Zabel 5707d2691daSSteve Longerbeam ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs); 5717d2691daSSteve Longerbeam if (ret) { 5727d2691daSSteve Longerbeam unit = "cpmem"; 5737d2691daSSteve Longerbeam goto err_cpmem; 5747d2691daSSteve Longerbeam } 5757d2691daSSteve Longerbeam 5762ffd48f2SSteve Longerbeam ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs, 5772ffd48f2SSteve Longerbeam IPU_CONF_CSI0_EN, ipu_clk); 5782ffd48f2SSteve Longerbeam if (ret) { 5792ffd48f2SSteve Longerbeam unit = "csi0"; 5802ffd48f2SSteve Longerbeam goto err_csi_0; 5812ffd48f2SSteve Longerbeam } 5822ffd48f2SSteve Longerbeam 5832ffd48f2SSteve Longerbeam ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs, 5842ffd48f2SSteve Longerbeam IPU_CONF_CSI1_EN, ipu_clk); 5852ffd48f2SSteve Longerbeam if (ret) { 5862ffd48f2SSteve Longerbeam unit = "csi1"; 5872ffd48f2SSteve Longerbeam goto err_csi_1; 5882ffd48f2SSteve Longerbeam } 5892ffd48f2SSteve Longerbeam 5901aa8ea0dSSteve Longerbeam ret = ipu_ic_init(ipu, dev, 5911aa8ea0dSSteve Longerbeam ipu_base + devtype->ic_ofs, 5921aa8ea0dSSteve Longerbeam ipu_base + devtype->tpm_ofs); 5931aa8ea0dSSteve Longerbeam if (ret) { 5941aa8ea0dSSteve Longerbeam unit = "ic"; 5951aa8ea0dSSteve Longerbeam goto err_ic; 5961aa8ea0dSSteve Longerbeam } 5971aa8ea0dSSteve Longerbeam 59839b9004dSPhilipp Zabel ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, 59939b9004dSPhilipp Zabel IPU_CONF_DI0_EN, ipu_clk); 60039b9004dSPhilipp Zabel if (ret) { 60139b9004dSPhilipp Zabel unit = "di0"; 60239b9004dSPhilipp Zabel goto err_di_0; 60339b9004dSPhilipp Zabel } 60439b9004dSPhilipp Zabel 60539b9004dSPhilipp Zabel ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs, 60639b9004dSPhilipp Zabel IPU_CONF_DI1_EN, ipu_clk); 60739b9004dSPhilipp Zabel if (ret) { 60839b9004dSPhilipp Zabel unit = "di1"; 60939b9004dSPhilipp Zabel goto err_di_1; 61039b9004dSPhilipp Zabel } 61139b9004dSPhilipp Zabel 61239b9004dSPhilipp Zabel ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs + 61339b9004dSPhilipp Zabel IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs); 61439b9004dSPhilipp Zabel if (ret) { 61539b9004dSPhilipp Zabel unit = "dc_template"; 61639b9004dSPhilipp Zabel goto err_dc; 61739b9004dSPhilipp Zabel } 61839b9004dSPhilipp Zabel 61939b9004dSPhilipp Zabel ret = ipu_dmfc_init(ipu, dev, ipu_base + 62039b9004dSPhilipp Zabel devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk); 62139b9004dSPhilipp Zabel if (ret) { 62239b9004dSPhilipp Zabel unit = "dmfc"; 62339b9004dSPhilipp Zabel goto err_dmfc; 62439b9004dSPhilipp Zabel } 62539b9004dSPhilipp Zabel 62639b9004dSPhilipp Zabel ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs); 62739b9004dSPhilipp Zabel if (ret) { 62839b9004dSPhilipp Zabel unit = "dp"; 62939b9004dSPhilipp Zabel goto err_dp; 63039b9004dSPhilipp Zabel } 63139b9004dSPhilipp Zabel 63235de925fSPhilipp Zabel ret = ipu_smfc_init(ipu, dev, ipu_base + 63335de925fSPhilipp Zabel devtype->cm_ofs + IPU_CM_SMFC_REG_OFS); 63435de925fSPhilipp Zabel if (ret) { 63535de925fSPhilipp Zabel unit = "smfc"; 63635de925fSPhilipp Zabel goto err_smfc; 63735de925fSPhilipp Zabel } 63835de925fSPhilipp Zabel 63939b9004dSPhilipp Zabel return 0; 64039b9004dSPhilipp Zabel 64135de925fSPhilipp Zabel err_smfc: 64235de925fSPhilipp Zabel ipu_dp_exit(ipu); 64339b9004dSPhilipp Zabel err_dp: 64439b9004dSPhilipp Zabel ipu_dmfc_exit(ipu); 64539b9004dSPhilipp Zabel err_dmfc: 64639b9004dSPhilipp Zabel ipu_dc_exit(ipu); 64739b9004dSPhilipp Zabel err_dc: 64839b9004dSPhilipp Zabel ipu_di_exit(ipu, 1); 64939b9004dSPhilipp Zabel err_di_1: 65039b9004dSPhilipp Zabel ipu_di_exit(ipu, 0); 65139b9004dSPhilipp Zabel err_di_0: 6521aa8ea0dSSteve Longerbeam ipu_ic_exit(ipu); 6531aa8ea0dSSteve Longerbeam err_ic: 6542ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 1); 6552ffd48f2SSteve Longerbeam err_csi_1: 6562ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 0); 6572ffd48f2SSteve Longerbeam err_csi_0: 6587d2691daSSteve Longerbeam ipu_cpmem_exit(ipu); 6597d2691daSSteve Longerbeam err_cpmem: 66039b9004dSPhilipp Zabel dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret); 66139b9004dSPhilipp Zabel return ret; 66239b9004dSPhilipp Zabel } 66339b9004dSPhilipp Zabel 66439b9004dSPhilipp Zabel static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs) 66539b9004dSPhilipp Zabel { 66639b9004dSPhilipp Zabel unsigned long status; 66739b9004dSPhilipp Zabel int i, bit, irq; 66839b9004dSPhilipp Zabel 66939b9004dSPhilipp Zabel for (i = 0; i < num_regs; i++) { 67039b9004dSPhilipp Zabel 67139b9004dSPhilipp Zabel status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i])); 67239b9004dSPhilipp Zabel status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i])); 67339b9004dSPhilipp Zabel 67439b9004dSPhilipp Zabel for_each_set_bit(bit, &status, 32) { 675682b7c1cSLinus Torvalds irq = irq_linear_revmap(ipu->domain, 676682b7c1cSLinus Torvalds regs[i] * 32 + bit); 67739b9004dSPhilipp Zabel if (irq) 67839b9004dSPhilipp Zabel generic_handle_irq(irq); 67939b9004dSPhilipp Zabel } 68039b9004dSPhilipp Zabel } 68139b9004dSPhilipp Zabel } 68239b9004dSPhilipp Zabel 68339b9004dSPhilipp Zabel static void ipu_irq_handler(unsigned int irq, struct irq_desc *desc) 68439b9004dSPhilipp Zabel { 68539b9004dSPhilipp Zabel struct ipu_soc *ipu = irq_desc_get_handler_data(desc); 68639b9004dSPhilipp Zabel const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14}; 68739b9004dSPhilipp Zabel struct irq_chip *chip = irq_get_chip(irq); 68839b9004dSPhilipp Zabel 68939b9004dSPhilipp Zabel chained_irq_enter(chip, desc); 69039b9004dSPhilipp Zabel 69139b9004dSPhilipp Zabel ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); 69239b9004dSPhilipp Zabel 69339b9004dSPhilipp Zabel chained_irq_exit(chip, desc); 69439b9004dSPhilipp Zabel } 69539b9004dSPhilipp Zabel 69639b9004dSPhilipp Zabel static void ipu_err_irq_handler(unsigned int irq, struct irq_desc *desc) 69739b9004dSPhilipp Zabel { 69839b9004dSPhilipp Zabel struct ipu_soc *ipu = irq_desc_get_handler_data(desc); 69939b9004dSPhilipp Zabel const int int_reg[] = { 4, 5, 8, 9}; 70039b9004dSPhilipp Zabel struct irq_chip *chip = irq_get_chip(irq); 70139b9004dSPhilipp Zabel 70239b9004dSPhilipp Zabel chained_irq_enter(chip, desc); 70339b9004dSPhilipp Zabel 70439b9004dSPhilipp Zabel ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); 70539b9004dSPhilipp Zabel 70639b9004dSPhilipp Zabel chained_irq_exit(chip, desc); 70739b9004dSPhilipp Zabel } 70839b9004dSPhilipp Zabel 709682b7c1cSLinus Torvalds int ipu_map_irq(struct ipu_soc *ipu, int irq) 710682b7c1cSLinus Torvalds { 711682b7c1cSLinus Torvalds int virq; 712682b7c1cSLinus Torvalds 713682b7c1cSLinus Torvalds virq = irq_linear_revmap(ipu->domain, irq); 714682b7c1cSLinus Torvalds if (!virq) 715682b7c1cSLinus Torvalds virq = irq_create_mapping(ipu->domain, irq); 716682b7c1cSLinus Torvalds 717682b7c1cSLinus Torvalds return virq; 718682b7c1cSLinus Torvalds } 719682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_map_irq); 720682b7c1cSLinus Torvalds 72139b9004dSPhilipp Zabel int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, 72239b9004dSPhilipp Zabel enum ipu_channel_irq irq_type) 72339b9004dSPhilipp Zabel { 724682b7c1cSLinus Torvalds return ipu_map_irq(ipu, irq_type + channel->num); 72539b9004dSPhilipp Zabel } 72639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq); 72739b9004dSPhilipp Zabel 72839b9004dSPhilipp Zabel static void ipu_submodules_exit(struct ipu_soc *ipu) 72939b9004dSPhilipp Zabel { 73035de925fSPhilipp Zabel ipu_smfc_exit(ipu); 73139b9004dSPhilipp Zabel ipu_dp_exit(ipu); 73239b9004dSPhilipp Zabel ipu_dmfc_exit(ipu); 73339b9004dSPhilipp Zabel ipu_dc_exit(ipu); 73439b9004dSPhilipp Zabel ipu_di_exit(ipu, 1); 73539b9004dSPhilipp Zabel ipu_di_exit(ipu, 0); 7361aa8ea0dSSteve Longerbeam ipu_ic_exit(ipu); 7372ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 1); 7382ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 0); 7397d2691daSSteve Longerbeam ipu_cpmem_exit(ipu); 74039b9004dSPhilipp Zabel } 74139b9004dSPhilipp Zabel 74239b9004dSPhilipp Zabel static int platform_remove_devices_fn(struct device *dev, void *unused) 74339b9004dSPhilipp Zabel { 74439b9004dSPhilipp Zabel struct platform_device *pdev = to_platform_device(dev); 74539b9004dSPhilipp Zabel 74639b9004dSPhilipp Zabel platform_device_unregister(pdev); 74739b9004dSPhilipp Zabel 74839b9004dSPhilipp Zabel return 0; 74939b9004dSPhilipp Zabel } 75039b9004dSPhilipp Zabel 75139b9004dSPhilipp Zabel static void platform_device_unregister_children(struct platform_device *pdev) 75239b9004dSPhilipp Zabel { 75339b9004dSPhilipp Zabel device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn); 75439b9004dSPhilipp Zabel } 75539b9004dSPhilipp Zabel 75639b9004dSPhilipp Zabel struct ipu_platform_reg { 75739b9004dSPhilipp Zabel struct ipu_client_platformdata pdata; 75839b9004dSPhilipp Zabel const char *name; 759d6ca8ca7SPhilipp Zabel int reg_offset; 76039b9004dSPhilipp Zabel }; 76139b9004dSPhilipp Zabel 76239b9004dSPhilipp Zabel static const struct ipu_platform_reg client_reg[] = { 76339b9004dSPhilipp Zabel { 76439b9004dSPhilipp Zabel .pdata = { 76539b9004dSPhilipp Zabel .di = 0, 76639b9004dSPhilipp Zabel .dc = 5, 76739b9004dSPhilipp Zabel .dp = IPU_DP_FLOW_SYNC_BG, 76839b9004dSPhilipp Zabel .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC, 76939b9004dSPhilipp Zabel .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC, 77039b9004dSPhilipp Zabel }, 77139b9004dSPhilipp Zabel .name = "imx-ipuv3-crtc", 77239b9004dSPhilipp Zabel }, { 77339b9004dSPhilipp Zabel .pdata = { 77439b9004dSPhilipp Zabel .di = 1, 77539b9004dSPhilipp Zabel .dc = 1, 77639b9004dSPhilipp Zabel .dp = -EINVAL, 77739b9004dSPhilipp Zabel .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC, 77839b9004dSPhilipp Zabel .dma[1] = -EINVAL, 77939b9004dSPhilipp Zabel }, 78039b9004dSPhilipp Zabel .name = "imx-ipuv3-crtc", 781d6ca8ca7SPhilipp Zabel }, { 782d6ca8ca7SPhilipp Zabel .pdata = { 783d6ca8ca7SPhilipp Zabel .csi = 0, 784d6ca8ca7SPhilipp Zabel .dma[0] = IPUV3_CHANNEL_CSI0, 785d6ca8ca7SPhilipp Zabel .dma[1] = -EINVAL, 786d6ca8ca7SPhilipp Zabel }, 787d6ca8ca7SPhilipp Zabel .reg_offset = IPU_CM_CSI0_REG_OFS, 788d6ca8ca7SPhilipp Zabel .name = "imx-ipuv3-camera", 789d6ca8ca7SPhilipp Zabel }, { 790d6ca8ca7SPhilipp Zabel .pdata = { 791d6ca8ca7SPhilipp Zabel .csi = 1, 792d6ca8ca7SPhilipp Zabel .dma[0] = IPUV3_CHANNEL_CSI1, 793d6ca8ca7SPhilipp Zabel .dma[1] = -EINVAL, 794d6ca8ca7SPhilipp Zabel }, 795d6ca8ca7SPhilipp Zabel .reg_offset = IPU_CM_CSI1_REG_OFS, 796d6ca8ca7SPhilipp Zabel .name = "imx-ipuv3-camera", 79739b9004dSPhilipp Zabel }, 79839b9004dSPhilipp Zabel }; 79939b9004dSPhilipp Zabel 80039b9004dSPhilipp Zabel static DEFINE_MUTEX(ipu_client_id_mutex); 80139b9004dSPhilipp Zabel static int ipu_client_id; 80239b9004dSPhilipp Zabel 803d6ca8ca7SPhilipp Zabel static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) 80439b9004dSPhilipp Zabel { 80539b9004dSPhilipp Zabel struct device *dev = ipu->dev; 80639b9004dSPhilipp Zabel unsigned i; 80739b9004dSPhilipp Zabel int id, ret; 80839b9004dSPhilipp Zabel 80939b9004dSPhilipp Zabel mutex_lock(&ipu_client_id_mutex); 81039b9004dSPhilipp Zabel id = ipu_client_id; 81139b9004dSPhilipp Zabel ipu_client_id += ARRAY_SIZE(client_reg); 81239b9004dSPhilipp Zabel mutex_unlock(&ipu_client_id_mutex); 81339b9004dSPhilipp Zabel 81439b9004dSPhilipp Zabel for (i = 0; i < ARRAY_SIZE(client_reg); i++) { 81539b9004dSPhilipp Zabel const struct ipu_platform_reg *reg = &client_reg[i]; 81639b9004dSPhilipp Zabel struct platform_device *pdev; 817d6ca8ca7SPhilipp Zabel struct resource res; 81839b9004dSPhilipp Zabel 819d6ca8ca7SPhilipp Zabel if (reg->reg_offset) { 820d6ca8ca7SPhilipp Zabel memset(&res, 0, sizeof(res)); 821d6ca8ca7SPhilipp Zabel res.flags = IORESOURCE_MEM; 822d6ca8ca7SPhilipp Zabel res.start = ipu_base + ipu->devtype->cm_ofs + reg->reg_offset; 823d6ca8ca7SPhilipp Zabel res.end = res.start + PAGE_SIZE - 1; 824d6ca8ca7SPhilipp Zabel pdev = platform_device_register_resndata(dev, reg->name, 825d6ca8ca7SPhilipp Zabel id++, &res, 1, ®->pdata, sizeof(reg->pdata)); 826d6ca8ca7SPhilipp Zabel } else { 82739b9004dSPhilipp Zabel pdev = platform_device_register_data(dev, reg->name, 82839b9004dSPhilipp Zabel id++, ®->pdata, sizeof(reg->pdata)); 829d6ca8ca7SPhilipp Zabel } 83039b9004dSPhilipp Zabel 83139b9004dSPhilipp Zabel if (IS_ERR(pdev)) 83239b9004dSPhilipp Zabel goto err_register; 83339b9004dSPhilipp Zabel } 83439b9004dSPhilipp Zabel 83539b9004dSPhilipp Zabel return 0; 83639b9004dSPhilipp Zabel 83739b9004dSPhilipp Zabel err_register: 83839b9004dSPhilipp Zabel platform_device_unregister_children(to_platform_device(dev)); 83939b9004dSPhilipp Zabel 84039b9004dSPhilipp Zabel return ret; 84139b9004dSPhilipp Zabel } 84239b9004dSPhilipp Zabel 84339b9004dSPhilipp Zabel 84439b9004dSPhilipp Zabel static int ipu_irq_init(struct ipu_soc *ipu) 84539b9004dSPhilipp Zabel { 84639b9004dSPhilipp Zabel struct irq_chip_generic *gc; 84739b9004dSPhilipp Zabel struct irq_chip_type *ct; 84839b9004dSPhilipp Zabel unsigned long unused[IPU_NUM_IRQS / 32] = { 84939b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 85039b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 85139b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 85239b9004dSPhilipp Zabel 0x4077ffff, 0xffe7e1fd, 85339b9004dSPhilipp Zabel 0x23fffffe, 0x8880fff0, 85439b9004dSPhilipp Zabel 0xf98fe7d0, 0xfff81fff, 85539b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 85639b9004dSPhilipp Zabel 0x00000000, 85739b9004dSPhilipp Zabel }; 85839b9004dSPhilipp Zabel int ret, i; 85939b9004dSPhilipp Zabel 86039b9004dSPhilipp Zabel ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS, 86139b9004dSPhilipp Zabel &irq_generic_chip_ops, ipu); 86239b9004dSPhilipp Zabel if (!ipu->domain) { 86339b9004dSPhilipp Zabel dev_err(ipu->dev, "failed to add irq domain\n"); 86439b9004dSPhilipp Zabel return -ENODEV; 86539b9004dSPhilipp Zabel } 86639b9004dSPhilipp Zabel 86739b9004dSPhilipp Zabel ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU", 868682b7c1cSLinus Torvalds handle_level_irq, 0, 869682b7c1cSLinus Torvalds IRQF_VALID, 0); 87039b9004dSPhilipp Zabel if (ret < 0) { 87139b9004dSPhilipp Zabel dev_err(ipu->dev, "failed to alloc generic irq chips\n"); 87239b9004dSPhilipp Zabel irq_domain_remove(ipu->domain); 87339b9004dSPhilipp Zabel return ret; 87439b9004dSPhilipp Zabel } 87539b9004dSPhilipp Zabel 87639b9004dSPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i += 32) { 87739b9004dSPhilipp Zabel gc = irq_get_domain_generic_chip(ipu->domain, i); 87839b9004dSPhilipp Zabel gc->reg_base = ipu->cm_reg; 87939b9004dSPhilipp Zabel gc->unused = unused[i / 32]; 88039b9004dSPhilipp Zabel ct = gc->chip_types; 88139b9004dSPhilipp Zabel ct->chip.irq_ack = irq_gc_ack_set_bit; 88239b9004dSPhilipp Zabel ct->chip.irq_mask = irq_gc_mask_clr_bit; 88339b9004dSPhilipp Zabel ct->chip.irq_unmask = irq_gc_mask_set_bit; 88439b9004dSPhilipp Zabel ct->regs.ack = IPU_INT_STAT(i / 32); 88539b9004dSPhilipp Zabel ct->regs.mask = IPU_INT_CTRL(i / 32); 88639b9004dSPhilipp Zabel } 88739b9004dSPhilipp Zabel 88839b9004dSPhilipp Zabel irq_set_chained_handler(ipu->irq_sync, ipu_irq_handler); 88939b9004dSPhilipp Zabel irq_set_handler_data(ipu->irq_sync, ipu); 89039b9004dSPhilipp Zabel irq_set_chained_handler(ipu->irq_err, ipu_err_irq_handler); 89139b9004dSPhilipp Zabel irq_set_handler_data(ipu->irq_err, ipu); 89239b9004dSPhilipp Zabel 89339b9004dSPhilipp Zabel return 0; 89439b9004dSPhilipp Zabel } 89539b9004dSPhilipp Zabel 89639b9004dSPhilipp Zabel static void ipu_irq_exit(struct ipu_soc *ipu) 89739b9004dSPhilipp Zabel { 89839b9004dSPhilipp Zabel int i, irq; 89939b9004dSPhilipp Zabel 90039b9004dSPhilipp Zabel irq_set_chained_handler(ipu->irq_err, NULL); 90139b9004dSPhilipp Zabel irq_set_handler_data(ipu->irq_err, NULL); 90239b9004dSPhilipp Zabel irq_set_chained_handler(ipu->irq_sync, NULL); 90339b9004dSPhilipp Zabel irq_set_handler_data(ipu->irq_sync, NULL); 90439b9004dSPhilipp Zabel 90539b9004dSPhilipp Zabel /* TODO: remove irq_domain_generic_chips */ 90639b9004dSPhilipp Zabel 90739b9004dSPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i++) { 90839b9004dSPhilipp Zabel irq = irq_linear_revmap(ipu->domain, i); 90939b9004dSPhilipp Zabel if (irq) 91039b9004dSPhilipp Zabel irq_dispose_mapping(irq); 91139b9004dSPhilipp Zabel } 91239b9004dSPhilipp Zabel 91339b9004dSPhilipp Zabel irq_domain_remove(ipu->domain); 91439b9004dSPhilipp Zabel } 91539b9004dSPhilipp Zabel 91639b9004dSPhilipp Zabel static int ipu_probe(struct platform_device *pdev) 91739b9004dSPhilipp Zabel { 91839b9004dSPhilipp Zabel const struct of_device_id *of_id = 91939b9004dSPhilipp Zabel of_match_device(imx_ipu_dt_ids, &pdev->dev); 92039b9004dSPhilipp Zabel struct ipu_soc *ipu; 92139b9004dSPhilipp Zabel struct resource *res; 92239b9004dSPhilipp Zabel unsigned long ipu_base; 92339b9004dSPhilipp Zabel int i, ret, irq_sync, irq_err; 92439b9004dSPhilipp Zabel const struct ipu_devtype *devtype; 92539b9004dSPhilipp Zabel 92639b9004dSPhilipp Zabel devtype = of_id->data; 92739b9004dSPhilipp Zabel 92839b9004dSPhilipp Zabel irq_sync = platform_get_irq(pdev, 0); 92939b9004dSPhilipp Zabel irq_err = platform_get_irq(pdev, 1); 93039b9004dSPhilipp Zabel res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 93139b9004dSPhilipp Zabel 93239b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n", 93339b9004dSPhilipp Zabel irq_sync, irq_err); 93439b9004dSPhilipp Zabel 93539b9004dSPhilipp Zabel if (!res || irq_sync < 0 || irq_err < 0) 93639b9004dSPhilipp Zabel return -ENODEV; 93739b9004dSPhilipp Zabel 93839b9004dSPhilipp Zabel ipu_base = res->start; 93939b9004dSPhilipp Zabel 94039b9004dSPhilipp Zabel ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL); 94139b9004dSPhilipp Zabel if (!ipu) 94239b9004dSPhilipp Zabel return -ENODEV; 94339b9004dSPhilipp Zabel 94439b9004dSPhilipp Zabel for (i = 0; i < 64; i++) 94539b9004dSPhilipp Zabel ipu->channel[i].ipu = ipu; 94639b9004dSPhilipp Zabel ipu->devtype = devtype; 94739b9004dSPhilipp Zabel ipu->ipu_type = devtype->type; 94839b9004dSPhilipp Zabel 94939b9004dSPhilipp Zabel spin_lock_init(&ipu->lock); 95039b9004dSPhilipp Zabel mutex_init(&ipu->channel_lock); 95139b9004dSPhilipp Zabel 95239b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n", 95339b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs); 95439b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "idmac: 0x%08lx\n", 95539b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS); 95639b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n", 95739b9004dSPhilipp Zabel ipu_base + devtype->cpmem_ofs); 9582ffd48f2SSteve Longerbeam dev_dbg(&pdev->dev, "csi0: 0x%08lx\n", 9592ffd48f2SSteve Longerbeam ipu_base + devtype->csi0_ofs); 9602ffd48f2SSteve Longerbeam dev_dbg(&pdev->dev, "csi1: 0x%08lx\n", 9612ffd48f2SSteve Longerbeam ipu_base + devtype->csi1_ofs); 9621aa8ea0dSSteve Longerbeam dev_dbg(&pdev->dev, "ic: 0x%08lx\n", 9631aa8ea0dSSteve Longerbeam ipu_base + devtype->ic_ofs); 96439b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "disp0: 0x%08lx\n", 96539b9004dSPhilipp Zabel ipu_base + devtype->disp0_ofs); 96639b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "disp1: 0x%08lx\n", 96739b9004dSPhilipp Zabel ipu_base + devtype->disp1_ofs); 96839b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "srm: 0x%08lx\n", 96939b9004dSPhilipp Zabel ipu_base + devtype->srm_ofs); 97039b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "tpm: 0x%08lx\n", 97139b9004dSPhilipp Zabel ipu_base + devtype->tpm_ofs); 97239b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "dc: 0x%08lx\n", 97339b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS); 97439b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "ic: 0x%08lx\n", 97539b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS); 97639b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n", 97739b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS); 97839b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "vdi: 0x%08lx\n", 97939b9004dSPhilipp Zabel ipu_base + devtype->vdi_ofs); 98039b9004dSPhilipp Zabel 98139b9004dSPhilipp Zabel ipu->cm_reg = devm_ioremap(&pdev->dev, 98239b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs, PAGE_SIZE); 98339b9004dSPhilipp Zabel ipu->idmac_reg = devm_ioremap(&pdev->dev, 98439b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS, 98539b9004dSPhilipp Zabel PAGE_SIZE); 98639b9004dSPhilipp Zabel 9877d2691daSSteve Longerbeam if (!ipu->cm_reg || !ipu->idmac_reg) 98839b9004dSPhilipp Zabel return -ENOMEM; 98939b9004dSPhilipp Zabel 99039b9004dSPhilipp Zabel ipu->clk = devm_clk_get(&pdev->dev, "bus"); 99139b9004dSPhilipp Zabel if (IS_ERR(ipu->clk)) { 99239b9004dSPhilipp Zabel ret = PTR_ERR(ipu->clk); 99339b9004dSPhilipp Zabel dev_err(&pdev->dev, "clk_get failed with %d", ret); 99439b9004dSPhilipp Zabel return ret; 99539b9004dSPhilipp Zabel } 99639b9004dSPhilipp Zabel 99739b9004dSPhilipp Zabel platform_set_drvdata(pdev, ipu); 99839b9004dSPhilipp Zabel 99939b9004dSPhilipp Zabel ret = clk_prepare_enable(ipu->clk); 100039b9004dSPhilipp Zabel if (ret) { 100139b9004dSPhilipp Zabel dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); 100239b9004dSPhilipp Zabel return ret; 100339b9004dSPhilipp Zabel } 100439b9004dSPhilipp Zabel 100539b9004dSPhilipp Zabel ipu->dev = &pdev->dev; 100639b9004dSPhilipp Zabel ipu->irq_sync = irq_sync; 100739b9004dSPhilipp Zabel ipu->irq_err = irq_err; 100839b9004dSPhilipp Zabel 100939b9004dSPhilipp Zabel ret = ipu_irq_init(ipu); 101039b9004dSPhilipp Zabel if (ret) 101139b9004dSPhilipp Zabel goto out_failed_irq; 101239b9004dSPhilipp Zabel 101339b9004dSPhilipp Zabel ret = device_reset(&pdev->dev); 101439b9004dSPhilipp Zabel if (ret) { 101539b9004dSPhilipp Zabel dev_err(&pdev->dev, "failed to reset: %d\n", ret); 101639b9004dSPhilipp Zabel goto out_failed_reset; 101739b9004dSPhilipp Zabel } 101839b9004dSPhilipp Zabel ret = ipu_memory_reset(ipu); 101939b9004dSPhilipp Zabel if (ret) 102039b9004dSPhilipp Zabel goto out_failed_reset; 102139b9004dSPhilipp Zabel 102239b9004dSPhilipp Zabel /* Set MCU_T to divide MCU access window into 2 */ 102339b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), 102439b9004dSPhilipp Zabel IPU_DISP_GEN); 102539b9004dSPhilipp Zabel 102639b9004dSPhilipp Zabel ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk); 102739b9004dSPhilipp Zabel if (ret) 102839b9004dSPhilipp Zabel goto failed_submodules_init; 102939b9004dSPhilipp Zabel 1030d6ca8ca7SPhilipp Zabel ret = ipu_add_client_devices(ipu, ipu_base); 103139b9004dSPhilipp Zabel if (ret) { 103239b9004dSPhilipp Zabel dev_err(&pdev->dev, "adding client devices failed with %d\n", 103339b9004dSPhilipp Zabel ret); 103439b9004dSPhilipp Zabel goto failed_add_clients; 103539b9004dSPhilipp Zabel } 103639b9004dSPhilipp Zabel 103739b9004dSPhilipp Zabel dev_info(&pdev->dev, "%s probed\n", devtype->name); 103839b9004dSPhilipp Zabel 103939b9004dSPhilipp Zabel return 0; 104039b9004dSPhilipp Zabel 104139b9004dSPhilipp Zabel failed_add_clients: 104239b9004dSPhilipp Zabel ipu_submodules_exit(ipu); 104339b9004dSPhilipp Zabel failed_submodules_init: 104439b9004dSPhilipp Zabel out_failed_reset: 104539b9004dSPhilipp Zabel ipu_irq_exit(ipu); 104639b9004dSPhilipp Zabel out_failed_irq: 104739b9004dSPhilipp Zabel clk_disable_unprepare(ipu->clk); 104839b9004dSPhilipp Zabel return ret; 104939b9004dSPhilipp Zabel } 105039b9004dSPhilipp Zabel 105139b9004dSPhilipp Zabel static int ipu_remove(struct platform_device *pdev) 105239b9004dSPhilipp Zabel { 105339b9004dSPhilipp Zabel struct ipu_soc *ipu = platform_get_drvdata(pdev); 105439b9004dSPhilipp Zabel 105539b9004dSPhilipp Zabel platform_device_unregister_children(pdev); 105639b9004dSPhilipp Zabel ipu_submodules_exit(ipu); 105739b9004dSPhilipp Zabel ipu_irq_exit(ipu); 105839b9004dSPhilipp Zabel 105939b9004dSPhilipp Zabel clk_disable_unprepare(ipu->clk); 106039b9004dSPhilipp Zabel 106139b9004dSPhilipp Zabel return 0; 106239b9004dSPhilipp Zabel } 106339b9004dSPhilipp Zabel 106439b9004dSPhilipp Zabel static struct platform_driver imx_ipu_driver = { 106539b9004dSPhilipp Zabel .driver = { 106639b9004dSPhilipp Zabel .name = "imx-ipuv3", 106739b9004dSPhilipp Zabel .of_match_table = imx_ipu_dt_ids, 106839b9004dSPhilipp Zabel }, 106939b9004dSPhilipp Zabel .probe = ipu_probe, 107039b9004dSPhilipp Zabel .remove = ipu_remove, 107139b9004dSPhilipp Zabel }; 107239b9004dSPhilipp Zabel 107339b9004dSPhilipp Zabel module_platform_driver(imx_ipu_driver); 107439b9004dSPhilipp Zabel 107539b9004dSPhilipp Zabel MODULE_ALIAS("platform:imx-ipuv3"); 107639b9004dSPhilipp Zabel MODULE_DESCRIPTION("i.MX IPU v3 driver"); 107739b9004dSPhilipp Zabel MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); 107839b9004dSPhilipp Zabel MODULE_LICENSE("GPL"); 1079