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