xref: /openbmc/linux/drivers/gpu/ipu-v3/ipu-common.c (revision bce6f087)
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, &reg->pdata, sizeof(reg->pdata));
890d6ca8ca7SPhilipp Zabel 		} else {
89139b9004dSPhilipp Zabel 			pdev = platform_device_register_data(dev, reg->name,
89239b9004dSPhilipp Zabel 				id++, &reg->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