xref: /openbmc/linux/drivers/gpu/ipu-v3/ipu-common.c (revision 722d4f06)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
239b9004dSPhilipp Zabel /*
339b9004dSPhilipp Zabel  * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
439b9004dSPhilipp Zabel  * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
539b9004dSPhilipp Zabel  */
639b9004dSPhilipp Zabel #include <linux/module.h>
739b9004dSPhilipp Zabel #include <linux/export.h>
839b9004dSPhilipp Zabel #include <linux/types.h>
939b9004dSPhilipp Zabel #include <linux/reset.h>
1039b9004dSPhilipp Zabel #include <linux/platform_device.h>
1139b9004dSPhilipp Zabel #include <linux/err.h>
1239b9004dSPhilipp Zabel #include <linux/spinlock.h>
1339b9004dSPhilipp Zabel #include <linux/delay.h>
1439b9004dSPhilipp Zabel #include <linux/interrupt.h>
1539b9004dSPhilipp Zabel #include <linux/io.h>
1639b9004dSPhilipp Zabel #include <linux/clk.h>
1739b9004dSPhilipp Zabel #include <linux/list.h>
1839b9004dSPhilipp Zabel #include <linux/irq.h>
1939b9004dSPhilipp Zabel #include <linux/irqchip/chained_irq.h>
2039b9004dSPhilipp Zabel #include <linux/irqdomain.h>
21*722d4f06SRob Herring #include <linux/of.h>
22304e6be6SPhilipp Zabel #include <linux/of_graph.h>
2339b9004dSPhilipp Zabel 
2439b9004dSPhilipp Zabel #include <drm/drm_fourcc.h>
2539b9004dSPhilipp Zabel 
2639b9004dSPhilipp Zabel #include <video/imx-ipu-v3.h>
2739b9004dSPhilipp Zabel #include "ipu-prv.h"
2839b9004dSPhilipp Zabel 
ipu_cm_read(struct ipu_soc * ipu,unsigned offset)2939b9004dSPhilipp Zabel static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset)
3039b9004dSPhilipp Zabel {
3139b9004dSPhilipp Zabel 	return readl(ipu->cm_reg + offset);
3239b9004dSPhilipp Zabel }
3339b9004dSPhilipp Zabel 
ipu_cm_write(struct ipu_soc * ipu,u32 value,unsigned offset)3439b9004dSPhilipp Zabel static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset)
3539b9004dSPhilipp Zabel {
3639b9004dSPhilipp Zabel 	writel(value, ipu->cm_reg + offset);
3739b9004dSPhilipp Zabel }
3839b9004dSPhilipp Zabel 
ipu_get_num(struct ipu_soc * ipu)39572a7615SSteve Longerbeam int ipu_get_num(struct ipu_soc *ipu)
40572a7615SSteve Longerbeam {
41572a7615SSteve Longerbeam 	return ipu->id;
42572a7615SSteve Longerbeam }
43572a7615SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_get_num);
44572a7615SSteve Longerbeam 
ipu_srm_dp_update(struct ipu_soc * ipu,bool sync)45f9bb7acbSPhilipp Zabel void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync)
4639b9004dSPhilipp Zabel {
4739b9004dSPhilipp Zabel 	u32 val;
4839b9004dSPhilipp Zabel 
4939b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_SRM_PRI2);
50f9bb7acbSPhilipp Zabel 	val &= ~DP_S_SRM_MODE_MASK;
51f9bb7acbSPhilipp Zabel 	val |= sync ? DP_S_SRM_MODE_NEXT_FRAME :
52f9bb7acbSPhilipp Zabel 		      DP_S_SRM_MODE_NOW;
5339b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_SRM_PRI2);
5439b9004dSPhilipp Zabel }
55f9bb7acbSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_srm_dp_update);
5639b9004dSPhilipp Zabel 
ipu_drm_fourcc_to_colorspace(u32 drm_fourcc)5739b9004dSPhilipp Zabel enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc)
5839b9004dSPhilipp Zabel {
5939b9004dSPhilipp Zabel 	switch (drm_fourcc) {
600cb8b757SPhilipp Zabel 	case DRM_FORMAT_ARGB1555:
610cb8b757SPhilipp Zabel 	case DRM_FORMAT_ABGR1555:
620cb8b757SPhilipp Zabel 	case DRM_FORMAT_RGBA5551:
630cb8b757SPhilipp Zabel 	case DRM_FORMAT_BGRA5551:
6439b9004dSPhilipp Zabel 	case DRM_FORMAT_RGB565:
6539b9004dSPhilipp Zabel 	case DRM_FORMAT_BGR565:
6639b9004dSPhilipp Zabel 	case DRM_FORMAT_RGB888:
6739b9004dSPhilipp Zabel 	case DRM_FORMAT_BGR888:
687d2e8a20SLucas Stach 	case DRM_FORMAT_ARGB4444:
6939b9004dSPhilipp Zabel 	case DRM_FORMAT_XRGB8888:
7039b9004dSPhilipp Zabel 	case DRM_FORMAT_XBGR8888:
7139b9004dSPhilipp Zabel 	case DRM_FORMAT_RGBX8888:
7239b9004dSPhilipp Zabel 	case DRM_FORMAT_BGRX8888:
7339b9004dSPhilipp Zabel 	case DRM_FORMAT_ARGB8888:
7439b9004dSPhilipp Zabel 	case DRM_FORMAT_ABGR8888:
7539b9004dSPhilipp Zabel 	case DRM_FORMAT_RGBA8888:
7639b9004dSPhilipp Zabel 	case DRM_FORMAT_BGRA8888:
77e72db3b1SPhilipp Zabel 	case DRM_FORMAT_RGB565_A8:
78e72db3b1SPhilipp Zabel 	case DRM_FORMAT_BGR565_A8:
79e72db3b1SPhilipp Zabel 	case DRM_FORMAT_RGB888_A8:
80e72db3b1SPhilipp Zabel 	case DRM_FORMAT_BGR888_A8:
81e72db3b1SPhilipp Zabel 	case DRM_FORMAT_RGBX8888_A8:
82e72db3b1SPhilipp Zabel 	case DRM_FORMAT_BGRX8888_A8:
8339b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_RGB;
8439b9004dSPhilipp Zabel 	case DRM_FORMAT_YUYV:
8539b9004dSPhilipp Zabel 	case DRM_FORMAT_UYVY:
8639b9004dSPhilipp Zabel 	case DRM_FORMAT_YUV420:
8739b9004dSPhilipp Zabel 	case DRM_FORMAT_YVU420:
889a34cef0SSteve Longerbeam 	case DRM_FORMAT_YUV422:
899a34cef0SSteve Longerbeam 	case DRM_FORMAT_YVU422:
90c9d508c2SPhilipp Zabel 	case DRM_FORMAT_YUV444:
91c9d508c2SPhilipp Zabel 	case DRM_FORMAT_YVU444:
929a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV12:
939a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV21:
949a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV16:
959a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV61:
9639b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_YUV;
9739b9004dSPhilipp Zabel 	default:
9839b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_UNKNOWN;
9939b9004dSPhilipp Zabel 	}
10039b9004dSPhilipp Zabel }
10139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace);
10239b9004dSPhilipp Zabel 
ipu_pixelformat_to_colorspace(u32 pixelformat)10339b9004dSPhilipp Zabel enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat)
10439b9004dSPhilipp Zabel {
10539b9004dSPhilipp Zabel 	switch (pixelformat) {
10639b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YUV420:
10739b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YVU420:
1089a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_YUV422P:
10939b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_UYVY:
11039b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YUYV:
1119a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV12:
1129a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV21:
1139a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV16:
1149a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV61:
11539b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_YUV;
11639b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB565:
117a5995717SPhilipp Zabel 	case V4L2_PIX_FMT_BGR24:
118a5995717SPhilipp Zabel 	case V4L2_PIX_FMT_RGB24:
119a5995717SPhilipp Zabel 	case V4L2_PIX_FMT_ABGR32:
120a5995717SPhilipp Zabel 	case V4L2_PIX_FMT_XBGR32:
121a5995717SPhilipp Zabel 	case V4L2_PIX_FMT_BGRA32:
122a5995717SPhilipp Zabel 	case V4L2_PIX_FMT_BGRX32:
123a5995717SPhilipp Zabel 	case V4L2_PIX_FMT_RGBA32:
124a5995717SPhilipp Zabel 	case V4L2_PIX_FMT_RGBX32:
125a5995717SPhilipp Zabel 	case V4L2_PIX_FMT_ARGB32:
126a5995717SPhilipp Zabel 	case V4L2_PIX_FMT_XRGB32:
12722b2cfadSSteve Longerbeam 	case V4L2_PIX_FMT_RGB32:
12822b2cfadSSteve Longerbeam 	case V4L2_PIX_FMT_BGR32:
12939b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_RGB;
13039b9004dSPhilipp Zabel 	default:
13139b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_UNKNOWN;
13239b9004dSPhilipp Zabel 	}
13339b9004dSPhilipp Zabel }
13439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace);
13539b9004dSPhilipp Zabel 
ipu_degrees_to_rot_mode(enum ipu_rotate_mode * mode,int degrees,bool hflip,bool vflip)136f835f386SSteve Longerbeam int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees,
137f835f386SSteve Longerbeam 			    bool hflip, bool vflip)
138f835f386SSteve Longerbeam {
139f835f386SSteve Longerbeam 	u32 r90, vf, hf;
140f835f386SSteve Longerbeam 
141f835f386SSteve Longerbeam 	switch (degrees) {
142f835f386SSteve Longerbeam 	case 0:
143f835f386SSteve Longerbeam 		vf = hf = r90 = 0;
144f835f386SSteve Longerbeam 		break;
145f835f386SSteve Longerbeam 	case 90:
146f835f386SSteve Longerbeam 		vf = hf = 0;
147f835f386SSteve Longerbeam 		r90 = 1;
148f835f386SSteve Longerbeam 		break;
149f835f386SSteve Longerbeam 	case 180:
150f835f386SSteve Longerbeam 		vf = hf = 1;
151f835f386SSteve Longerbeam 		r90 = 0;
152f835f386SSteve Longerbeam 		break;
153f835f386SSteve Longerbeam 	case 270:
154f835f386SSteve Longerbeam 		vf = hf = r90 = 1;
155f835f386SSteve Longerbeam 		break;
156f835f386SSteve Longerbeam 	default:
157f835f386SSteve Longerbeam 		return -EINVAL;
158f835f386SSteve Longerbeam 	}
159f835f386SSteve Longerbeam 
160f835f386SSteve Longerbeam 	hf ^= (u32)hflip;
161f835f386SSteve Longerbeam 	vf ^= (u32)vflip;
162f835f386SSteve Longerbeam 
163f835f386SSteve Longerbeam 	*mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf);
164f835f386SSteve Longerbeam 	return 0;
165f835f386SSteve Longerbeam }
166f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode);
167f835f386SSteve Longerbeam 
ipu_rot_mode_to_degrees(int * degrees,enum ipu_rotate_mode mode,bool hflip,bool vflip)168f835f386SSteve Longerbeam int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode,
169f835f386SSteve Longerbeam 			    bool hflip, bool vflip)
170f835f386SSteve Longerbeam {
171f835f386SSteve Longerbeam 	u32 r90, vf, hf;
172f835f386SSteve Longerbeam 
173f835f386SSteve Longerbeam 	r90 = ((u32)mode >> 2) & 0x1;
174f835f386SSteve Longerbeam 	hf = ((u32)mode >> 1) & 0x1;
175f835f386SSteve Longerbeam 	vf = ((u32)mode >> 0) & 0x1;
176f835f386SSteve Longerbeam 	hf ^= (u32)hflip;
177f835f386SSteve Longerbeam 	vf ^= (u32)vflip;
178f835f386SSteve Longerbeam 
179f835f386SSteve Longerbeam 	switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) {
180f835f386SSteve Longerbeam 	case IPU_ROTATE_NONE:
181f835f386SSteve Longerbeam 		*degrees = 0;
182f835f386SSteve Longerbeam 		break;
183f835f386SSteve Longerbeam 	case IPU_ROTATE_90_RIGHT:
184f835f386SSteve Longerbeam 		*degrees = 90;
185f835f386SSteve Longerbeam 		break;
186f835f386SSteve Longerbeam 	case IPU_ROTATE_180:
187f835f386SSteve Longerbeam 		*degrees = 180;
188f835f386SSteve Longerbeam 		break;
189f835f386SSteve Longerbeam 	case IPU_ROTATE_90_LEFT:
190f835f386SSteve Longerbeam 		*degrees = 270;
191f835f386SSteve Longerbeam 		break;
192f835f386SSteve Longerbeam 	default:
193f835f386SSteve Longerbeam 		return -EINVAL;
194f835f386SSteve Longerbeam 	}
195f835f386SSteve Longerbeam 
196f835f386SSteve Longerbeam 	return 0;
197f835f386SSteve Longerbeam }
198f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees);
199f835f386SSteve Longerbeam 
ipu_idmac_get(struct ipu_soc * ipu,unsigned num)20039b9004dSPhilipp Zabel struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num)
20139b9004dSPhilipp Zabel {
20239b9004dSPhilipp Zabel 	struct ipuv3_channel *channel;
20339b9004dSPhilipp Zabel 
20439b9004dSPhilipp Zabel 	dev_dbg(ipu->dev, "%s %d\n", __func__, num);
20539b9004dSPhilipp Zabel 
20639b9004dSPhilipp Zabel 	if (num > 63)
20739b9004dSPhilipp Zabel 		return ERR_PTR(-ENODEV);
20839b9004dSPhilipp Zabel 
20939b9004dSPhilipp Zabel 	mutex_lock(&ipu->channel_lock);
21039b9004dSPhilipp Zabel 
21193adc8b5SPhilipp Zabel 	list_for_each_entry(channel, &ipu->channels, list) {
21293adc8b5SPhilipp Zabel 		if (channel->num == num) {
21339b9004dSPhilipp Zabel 			channel = ERR_PTR(-EBUSY);
21439b9004dSPhilipp Zabel 			goto out;
21539b9004dSPhilipp Zabel 		}
21693adc8b5SPhilipp Zabel 	}
21739b9004dSPhilipp Zabel 
21893adc8b5SPhilipp Zabel 	channel = kzalloc(sizeof(*channel), GFP_KERNEL);
21993adc8b5SPhilipp Zabel 	if (!channel) {
22093adc8b5SPhilipp Zabel 		channel = ERR_PTR(-ENOMEM);
22193adc8b5SPhilipp Zabel 		goto out;
22293adc8b5SPhilipp Zabel 	}
22393adc8b5SPhilipp Zabel 
22439b9004dSPhilipp Zabel 	channel->num = num;
22593adc8b5SPhilipp Zabel 	channel->ipu = ipu;
22693adc8b5SPhilipp Zabel 	list_add(&channel->list, &ipu->channels);
22739b9004dSPhilipp Zabel 
22839b9004dSPhilipp Zabel out:
22939b9004dSPhilipp Zabel 	mutex_unlock(&ipu->channel_lock);
23039b9004dSPhilipp Zabel 
23139b9004dSPhilipp Zabel 	return channel;
23239b9004dSPhilipp Zabel }
23339b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get);
23439b9004dSPhilipp Zabel 
ipu_idmac_put(struct ipuv3_channel * channel)23539b9004dSPhilipp Zabel void ipu_idmac_put(struct ipuv3_channel *channel)
23639b9004dSPhilipp Zabel {
23739b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
23839b9004dSPhilipp Zabel 
23939b9004dSPhilipp Zabel 	dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num);
24039b9004dSPhilipp Zabel 
24139b9004dSPhilipp Zabel 	mutex_lock(&ipu->channel_lock);
24239b9004dSPhilipp Zabel 
24393adc8b5SPhilipp Zabel 	list_del(&channel->list);
24493adc8b5SPhilipp Zabel 	kfree(channel);
24539b9004dSPhilipp Zabel 
24639b9004dSPhilipp Zabel 	mutex_unlock(&ipu->channel_lock);
24739b9004dSPhilipp Zabel }
24839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_put);
24939b9004dSPhilipp Zabel 
250aa52f578SSteve Longerbeam #define idma_mask(ch)			(1 << ((ch) & 0x1f))
25139b9004dSPhilipp Zabel 
252e7268c69SSteve Longerbeam /*
253e7268c69SSteve Longerbeam  * This is an undocumented feature, a write one to a channel bit in
254e7268c69SSteve Longerbeam  * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's
255e7268c69SSteve Longerbeam  * internal current buffer pointer so that transfers start from buffer
256e7268c69SSteve Longerbeam  * 0 on the next channel enable (that's the theory anyway, the imx6 TRM
257e7268c69SSteve Longerbeam  * only says these are read-only registers). This operation is required
258e7268c69SSteve Longerbeam  * for channel linking to work correctly, for instance video capture
259e7268c69SSteve Longerbeam  * pipelines that carry out image rotations will fail after the first
260e7268c69SSteve Longerbeam  * streaming unless this function is called for each channel before
261e7268c69SSteve Longerbeam  * re-enabling the channels.
262e7268c69SSteve Longerbeam  */
__ipu_idmac_reset_current_buffer(struct ipuv3_channel * channel)263e7268c69SSteve Longerbeam static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel)
264e7268c69SSteve Longerbeam {
265e7268c69SSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
266e7268c69SSteve Longerbeam 	unsigned int chno = channel->num;
267e7268c69SSteve Longerbeam 
268e7268c69SSteve Longerbeam 	ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno));
269e7268c69SSteve Longerbeam }
270e7268c69SSteve Longerbeam 
ipu_idmac_set_double_buffer(struct ipuv3_channel * channel,bool doublebuffer)27139b9004dSPhilipp Zabel void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
27239b9004dSPhilipp Zabel 		bool doublebuffer)
27339b9004dSPhilipp Zabel {
27439b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
27539b9004dSPhilipp Zabel 	unsigned long flags;
27639b9004dSPhilipp Zabel 	u32 reg;
27739b9004dSPhilipp Zabel 
27839b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
27939b9004dSPhilipp Zabel 
28039b9004dSPhilipp Zabel 	reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
28139b9004dSPhilipp Zabel 	if (doublebuffer)
28239b9004dSPhilipp Zabel 		reg |= idma_mask(channel->num);
28339b9004dSPhilipp Zabel 	else
28439b9004dSPhilipp Zabel 		reg &= ~idma_mask(channel->num);
28539b9004dSPhilipp Zabel 	ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num));
28639b9004dSPhilipp Zabel 
287e7268c69SSteve Longerbeam 	__ipu_idmac_reset_current_buffer(channel);
288e7268c69SSteve Longerbeam 
28939b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
29039b9004dSPhilipp Zabel }
29139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer);
29239b9004dSPhilipp Zabel 
2934fd1a07aSSteve Longerbeam static const struct {
2944fd1a07aSSteve Longerbeam 	int chnum;
2954fd1a07aSSteve Longerbeam 	u32 reg;
2964fd1a07aSSteve Longerbeam 	int shift;
2974fd1a07aSSteve Longerbeam } idmac_lock_en_info[] = {
2984fd1a07aSSteve Longerbeam 	{ .chnum =  5, .reg = IDMAC_CH_LOCK_EN_1, .shift =  0, },
2994fd1a07aSSteve Longerbeam 	{ .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift =  2, },
3004fd1a07aSSteve Longerbeam 	{ .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift =  4, },
3014fd1a07aSSteve Longerbeam 	{ .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift =  6, },
3024fd1a07aSSteve Longerbeam 	{ .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift =  8, },
3034fd1a07aSSteve Longerbeam 	{ .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, },
3044fd1a07aSSteve Longerbeam 	{ .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, },
3054fd1a07aSSteve Longerbeam 	{ .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, },
3064fd1a07aSSteve Longerbeam 	{ .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, },
3074fd1a07aSSteve Longerbeam 	{ .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, },
3084fd1a07aSSteve Longerbeam 	{ .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, },
3094fd1a07aSSteve Longerbeam 	{ .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift =  0, },
3104fd1a07aSSteve Longerbeam 	{ .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift =  2, },
3114fd1a07aSSteve Longerbeam 	{ .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift =  4, },
3124fd1a07aSSteve Longerbeam 	{ .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift =  6, },
3134fd1a07aSSteve Longerbeam 	{ .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift =  8, },
3144fd1a07aSSteve Longerbeam 	{ .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, },
3154fd1a07aSSteve Longerbeam };
3164fd1a07aSSteve Longerbeam 
ipu_idmac_lock_enable(struct ipuv3_channel * channel,int num_bursts)3174fd1a07aSSteve Longerbeam int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts)
3184fd1a07aSSteve Longerbeam {
3194fd1a07aSSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
3204fd1a07aSSteve Longerbeam 	unsigned long flags;
3214fd1a07aSSteve Longerbeam 	u32 bursts, regval;
3224fd1a07aSSteve Longerbeam 	int i;
3234fd1a07aSSteve Longerbeam 
3244fd1a07aSSteve Longerbeam 	switch (num_bursts) {
3254fd1a07aSSteve Longerbeam 	case 0:
3264fd1a07aSSteve Longerbeam 	case 1:
3274fd1a07aSSteve Longerbeam 		bursts = 0x00; /* locking disabled */
3284fd1a07aSSteve Longerbeam 		break;
3294fd1a07aSSteve Longerbeam 	case 2:
3304fd1a07aSSteve Longerbeam 		bursts = 0x01;
3314fd1a07aSSteve Longerbeam 		break;
3324fd1a07aSSteve Longerbeam 	case 4:
3334fd1a07aSSteve Longerbeam 		bursts = 0x02;
3344fd1a07aSSteve Longerbeam 		break;
3354fd1a07aSSteve Longerbeam 	case 8:
3364fd1a07aSSteve Longerbeam 		bursts = 0x03;
3374fd1a07aSSteve Longerbeam 		break;
3384fd1a07aSSteve Longerbeam 	default:
3394fd1a07aSSteve Longerbeam 		return -EINVAL;
3404fd1a07aSSteve Longerbeam 	}
3414fd1a07aSSteve Longerbeam 
342cda77556SPhilipp Zabel 	/*
343cda77556SPhilipp Zabel 	 * IPUv3EX / i.MX51 has a different register layout, and on IPUv3M /
344cda77556SPhilipp Zabel 	 * i.MX53 channel arbitration locking doesn't seem to work properly.
345cda77556SPhilipp Zabel 	 * Allow enabling the lock feature on IPUv3H / i.MX6 only.
346cda77556SPhilipp Zabel 	 */
347cda77556SPhilipp Zabel 	if (bursts && ipu->ipu_type != IPUV3H)
348cda77556SPhilipp Zabel 		return -EINVAL;
349cda77556SPhilipp Zabel 
3504fd1a07aSSteve Longerbeam 	for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) {
3514fd1a07aSSteve Longerbeam 		if (channel->num == idmac_lock_en_info[i].chnum)
3524fd1a07aSSteve Longerbeam 			break;
3534fd1a07aSSteve Longerbeam 	}
3544fd1a07aSSteve Longerbeam 	if (i >= ARRAY_SIZE(idmac_lock_en_info))
3554fd1a07aSSteve Longerbeam 		return -EINVAL;
3564fd1a07aSSteve Longerbeam 
3574fd1a07aSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
3584fd1a07aSSteve Longerbeam 
3594fd1a07aSSteve Longerbeam 	regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg);
3604fd1a07aSSteve Longerbeam 	regval &= ~(0x03 << idmac_lock_en_info[i].shift);
3614fd1a07aSSteve Longerbeam 	regval |= (bursts << idmac_lock_en_info[i].shift);
3624fd1a07aSSteve Longerbeam 	ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg);
3634fd1a07aSSteve Longerbeam 
3644fd1a07aSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
3654fd1a07aSSteve Longerbeam 
3664fd1a07aSSteve Longerbeam 	return 0;
3674fd1a07aSSteve Longerbeam }
3684fd1a07aSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable);
3694fd1a07aSSteve Longerbeam 
ipu_module_enable(struct ipu_soc * ipu,u32 mask)37039b9004dSPhilipp Zabel int ipu_module_enable(struct ipu_soc *ipu, u32 mask)
37139b9004dSPhilipp Zabel {
37239b9004dSPhilipp Zabel 	unsigned long lock_flags;
37339b9004dSPhilipp Zabel 	u32 val;
37439b9004dSPhilipp Zabel 
37539b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, lock_flags);
37639b9004dSPhilipp Zabel 
37739b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_DISP_GEN);
37839b9004dSPhilipp Zabel 
37939b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI0_EN)
38039b9004dSPhilipp Zabel 		val |= IPU_DI0_COUNTER_RELEASE;
38139b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI1_EN)
38239b9004dSPhilipp Zabel 		val |= IPU_DI1_COUNTER_RELEASE;
38339b9004dSPhilipp Zabel 
38439b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_DISP_GEN);
38539b9004dSPhilipp Zabel 
38639b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CONF);
38739b9004dSPhilipp Zabel 	val |= mask;
38839b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CONF);
38939b9004dSPhilipp Zabel 
39039b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, lock_flags);
39139b9004dSPhilipp Zabel 
39239b9004dSPhilipp Zabel 	return 0;
39339b9004dSPhilipp Zabel }
39439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_enable);
39539b9004dSPhilipp Zabel 
ipu_module_disable(struct ipu_soc * ipu,u32 mask)39639b9004dSPhilipp Zabel int ipu_module_disable(struct ipu_soc *ipu, u32 mask)
39739b9004dSPhilipp Zabel {
39839b9004dSPhilipp Zabel 	unsigned long lock_flags;
39939b9004dSPhilipp Zabel 	u32 val;
40039b9004dSPhilipp Zabel 
40139b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, lock_flags);
40239b9004dSPhilipp Zabel 
40339b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CONF);
40439b9004dSPhilipp Zabel 	val &= ~mask;
40539b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CONF);
40639b9004dSPhilipp Zabel 
40739b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_DISP_GEN);
40839b9004dSPhilipp Zabel 
40939b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI0_EN)
41039b9004dSPhilipp Zabel 		val &= ~IPU_DI0_COUNTER_RELEASE;
41139b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI1_EN)
41239b9004dSPhilipp Zabel 		val &= ~IPU_DI1_COUNTER_RELEASE;
41339b9004dSPhilipp Zabel 
41439b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_DISP_GEN);
41539b9004dSPhilipp Zabel 
41639b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, lock_flags);
41739b9004dSPhilipp Zabel 
41839b9004dSPhilipp Zabel 	return 0;
41939b9004dSPhilipp Zabel }
42039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_disable);
42139b9004dSPhilipp Zabel 
ipu_idmac_get_current_buffer(struct ipuv3_channel * channel)422e9046097SPhilipp Zabel int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel)
423e9046097SPhilipp Zabel {
424e9046097SPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
425e9046097SPhilipp Zabel 	unsigned int chno = channel->num;
426e9046097SPhilipp Zabel 
427e9046097SPhilipp Zabel 	return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0;
428e9046097SPhilipp Zabel }
429e9046097SPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer);
430e9046097SPhilipp Zabel 
ipu_idmac_buffer_is_ready(struct ipuv3_channel * channel,u32 buf_num)431aa52f578SSteve Longerbeam bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num)
432aa52f578SSteve Longerbeam {
433aa52f578SSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
434aa52f578SSteve Longerbeam 	unsigned long flags;
435aa52f578SSteve Longerbeam 	u32 reg = 0;
436aa52f578SSteve Longerbeam 
437aa52f578SSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
438aa52f578SSteve Longerbeam 	switch (buf_num) {
439aa52f578SSteve Longerbeam 	case 0:
440aa52f578SSteve Longerbeam 		reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num));
441aa52f578SSteve Longerbeam 		break;
442aa52f578SSteve Longerbeam 	case 1:
443aa52f578SSteve Longerbeam 		reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num));
444aa52f578SSteve Longerbeam 		break;
445aa52f578SSteve Longerbeam 	case 2:
446aa52f578SSteve Longerbeam 		reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num));
447aa52f578SSteve Longerbeam 		break;
448aa52f578SSteve Longerbeam 	}
449aa52f578SSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
450aa52f578SSteve Longerbeam 
451aa52f578SSteve Longerbeam 	return ((reg & idma_mask(channel->num)) != 0);
452aa52f578SSteve Longerbeam }
453aa52f578SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready);
454aa52f578SSteve Longerbeam 
ipu_idmac_select_buffer(struct ipuv3_channel * channel,u32 buf_num)45539b9004dSPhilipp Zabel void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num)
45639b9004dSPhilipp Zabel {
45739b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
45839b9004dSPhilipp Zabel 	unsigned int chno = channel->num;
45939b9004dSPhilipp Zabel 	unsigned long flags;
46039b9004dSPhilipp Zabel 
46139b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
46239b9004dSPhilipp Zabel 
46339b9004dSPhilipp Zabel 	/* Mark buffer as ready. */
46439b9004dSPhilipp Zabel 	if (buf_num == 0)
46539b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
46639b9004dSPhilipp Zabel 	else
46739b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
46839b9004dSPhilipp Zabel 
46939b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
47039b9004dSPhilipp Zabel }
47139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer);
47239b9004dSPhilipp Zabel 
ipu_idmac_clear_buffer(struct ipuv3_channel * channel,u32 buf_num)473bce6f087SSteve Longerbeam void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num)
474bce6f087SSteve Longerbeam {
475bce6f087SSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
476bce6f087SSteve Longerbeam 	unsigned int chno = channel->num;
477bce6f087SSteve Longerbeam 	unsigned long flags;
478bce6f087SSteve Longerbeam 
479bce6f087SSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
480bce6f087SSteve Longerbeam 
481bce6f087SSteve Longerbeam 	ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */
482bce6f087SSteve Longerbeam 	switch (buf_num) {
483bce6f087SSteve Longerbeam 	case 0:
484bce6f087SSteve Longerbeam 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
485bce6f087SSteve Longerbeam 		break;
486bce6f087SSteve Longerbeam 	case 1:
487bce6f087SSteve Longerbeam 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
488bce6f087SSteve Longerbeam 		break;
489bce6f087SSteve Longerbeam 	case 2:
490bce6f087SSteve Longerbeam 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno));
491bce6f087SSteve Longerbeam 		break;
492bce6f087SSteve Longerbeam 	default:
493bce6f087SSteve Longerbeam 		break;
494bce6f087SSteve Longerbeam 	}
495bce6f087SSteve Longerbeam 	ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
496bce6f087SSteve Longerbeam 
497bce6f087SSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
498bce6f087SSteve Longerbeam }
499bce6f087SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer);
500bce6f087SSteve Longerbeam 
ipu_idmac_enable_channel(struct ipuv3_channel * channel)50139b9004dSPhilipp Zabel int ipu_idmac_enable_channel(struct ipuv3_channel *channel)
50239b9004dSPhilipp Zabel {
50339b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
50439b9004dSPhilipp Zabel 	u32 val;
50539b9004dSPhilipp Zabel 	unsigned long flags;
50639b9004dSPhilipp Zabel 
50739b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
50839b9004dSPhilipp Zabel 
50939b9004dSPhilipp Zabel 	val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
51039b9004dSPhilipp Zabel 	val |= idma_mask(channel->num);
51139b9004dSPhilipp Zabel 	ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
51239b9004dSPhilipp Zabel 
51339b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
51439b9004dSPhilipp Zabel 
51539b9004dSPhilipp Zabel 	return 0;
51639b9004dSPhilipp Zabel }
51739b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
51839b9004dSPhilipp Zabel 
ipu_idmac_channel_busy(struct ipu_soc * ipu,unsigned int chno)519682b7c1cSLinus Torvalds bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno)
520682b7c1cSLinus Torvalds {
521682b7c1cSLinus Torvalds 	return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno));
522682b7c1cSLinus Torvalds }
523682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy);
524682b7c1cSLinus Torvalds 
ipu_idmac_wait_busy(struct ipuv3_channel * channel,int ms)52539b9004dSPhilipp Zabel int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms)
52639b9004dSPhilipp Zabel {
52739b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
52839b9004dSPhilipp Zabel 	unsigned long timeout;
52939b9004dSPhilipp Zabel 
53039b9004dSPhilipp Zabel 	timeout = jiffies + msecs_to_jiffies(ms);
53139b9004dSPhilipp Zabel 	while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) &
53239b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
53339b9004dSPhilipp Zabel 		if (time_after(jiffies, timeout))
53439b9004dSPhilipp Zabel 			return -ETIMEDOUT;
53539b9004dSPhilipp Zabel 		cpu_relax();
53639b9004dSPhilipp Zabel 	}
53739b9004dSPhilipp Zabel 
53839b9004dSPhilipp Zabel 	return 0;
53939b9004dSPhilipp Zabel }
54039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy);
54139b9004dSPhilipp Zabel 
ipu_idmac_disable_channel(struct ipuv3_channel * channel)54239b9004dSPhilipp Zabel int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
54339b9004dSPhilipp Zabel {
54439b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
54539b9004dSPhilipp Zabel 	u32 val;
54639b9004dSPhilipp Zabel 	unsigned long flags;
54739b9004dSPhilipp Zabel 
54839b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
54939b9004dSPhilipp Zabel 
55039b9004dSPhilipp Zabel 	/* Disable DMA channel(s) */
55139b9004dSPhilipp Zabel 	val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
55239b9004dSPhilipp Zabel 	val &= ~idma_mask(channel->num);
55339b9004dSPhilipp Zabel 	ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
55439b9004dSPhilipp Zabel 
555e7268c69SSteve Longerbeam 	__ipu_idmac_reset_current_buffer(channel);
556e7268c69SSteve Longerbeam 
55739b9004dSPhilipp Zabel 	/* Set channel buffers NOT to be ready */
55839b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */
55939b9004dSPhilipp Zabel 
56039b9004dSPhilipp Zabel 	if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) &
56139b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
56239b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(channel->num),
56339b9004dSPhilipp Zabel 			     IPU_CHA_BUF0_RDY(channel->num));
56439b9004dSPhilipp Zabel 	}
56539b9004dSPhilipp Zabel 
56639b9004dSPhilipp Zabel 	if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) &
56739b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
56839b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(channel->num),
56939b9004dSPhilipp Zabel 			     IPU_CHA_BUF1_RDY(channel->num));
57039b9004dSPhilipp Zabel 	}
57139b9004dSPhilipp Zabel 
57239b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
57339b9004dSPhilipp Zabel 
57439b9004dSPhilipp Zabel 	/* Reset the double buffer */
57539b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
57639b9004dSPhilipp Zabel 	val &= ~idma_mask(channel->num);
57739b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num));
57839b9004dSPhilipp Zabel 
57939b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
58039b9004dSPhilipp Zabel 
58139b9004dSPhilipp Zabel 	return 0;
58239b9004dSPhilipp Zabel }
58339b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel);
58439b9004dSPhilipp Zabel 
5852bcf577eSSteve Longerbeam /*
5862bcf577eSSteve Longerbeam  * The imx6 rev. D TRM says that enabling the WM feature will increase
5872bcf577eSSteve Longerbeam  * a channel's priority. Refer to Table 36-8 Calculated priority value.
5882bcf577eSSteve Longerbeam  * The sub-module that is the sink or source for the channel must enable
5892bcf577eSSteve Longerbeam  * watermark signal for this to take effect (SMFC_WM for instance).
5902bcf577eSSteve Longerbeam  */
ipu_idmac_enable_watermark(struct ipuv3_channel * channel,bool enable)5912bcf577eSSteve Longerbeam void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable)
5922bcf577eSSteve Longerbeam {
5932bcf577eSSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
5942bcf577eSSteve Longerbeam 	unsigned long flags;
5952bcf577eSSteve Longerbeam 	u32 val;
5962bcf577eSSteve Longerbeam 
5972bcf577eSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
5982bcf577eSSteve Longerbeam 
5992bcf577eSSteve Longerbeam 	val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num));
6002bcf577eSSteve Longerbeam 	if (enable)
6012bcf577eSSteve Longerbeam 		val |= 1 << (channel->num % 32);
6022bcf577eSSteve Longerbeam 	else
6032bcf577eSSteve Longerbeam 		val &= ~(1 << (channel->num % 32));
6042bcf577eSSteve Longerbeam 	ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num));
6052bcf577eSSteve Longerbeam 
6062bcf577eSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
6072bcf577eSSteve Longerbeam }
6082bcf577eSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark);
6092bcf577eSSteve Longerbeam 
ipu_memory_reset(struct ipu_soc * ipu)61039b9004dSPhilipp Zabel static int ipu_memory_reset(struct ipu_soc *ipu)
61139b9004dSPhilipp Zabel {
61239b9004dSPhilipp Zabel 	unsigned long timeout;
61339b9004dSPhilipp Zabel 
61439b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST);
61539b9004dSPhilipp Zabel 
61639b9004dSPhilipp Zabel 	timeout = jiffies + msecs_to_jiffies(1000);
61739b9004dSPhilipp Zabel 	while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) {
61839b9004dSPhilipp Zabel 		if (time_after(jiffies, timeout))
61939b9004dSPhilipp Zabel 			return -ETIME;
62039b9004dSPhilipp Zabel 		cpu_relax();
62139b9004dSPhilipp Zabel 	}
62239b9004dSPhilipp Zabel 
62339b9004dSPhilipp Zabel 	return 0;
62439b9004dSPhilipp Zabel }
62539b9004dSPhilipp Zabel 
626ba07975fSSteve Longerbeam /*
627ba07975fSSteve Longerbeam  * Set the source mux for the given CSI. Selects either parallel or
628ba07975fSSteve Longerbeam  * MIPI CSI2 sources.
629ba07975fSSteve Longerbeam  */
ipu_set_csi_src_mux(struct ipu_soc * ipu,int csi_id,bool mipi_csi2)630ba07975fSSteve Longerbeam void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2)
631ba07975fSSteve Longerbeam {
632ba07975fSSteve Longerbeam 	unsigned long flags;
633ba07975fSSteve Longerbeam 	u32 val, mask;
634ba07975fSSteve Longerbeam 
635ba07975fSSteve Longerbeam 	mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE :
636ba07975fSSteve Longerbeam 		IPU_CONF_CSI0_DATA_SOURCE;
637ba07975fSSteve Longerbeam 
638ba07975fSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
639ba07975fSSteve Longerbeam 
640ba07975fSSteve Longerbeam 	val = ipu_cm_read(ipu, IPU_CONF);
641ba07975fSSteve Longerbeam 	if (mipi_csi2)
642ba07975fSSteve Longerbeam 		val |= mask;
643ba07975fSSteve Longerbeam 	else
644ba07975fSSteve Longerbeam 		val &= ~mask;
645ba07975fSSteve Longerbeam 	ipu_cm_write(ipu, val, IPU_CONF);
646ba07975fSSteve Longerbeam 
647ba07975fSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
648ba07975fSSteve Longerbeam }
649ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux);
650ba07975fSSteve Longerbeam 
651ba07975fSSteve Longerbeam /*
652ba07975fSSteve Longerbeam  * Set the source mux for the IC. Selects either CSI[01] or the VDI.
653ba07975fSSteve Longerbeam  */
ipu_set_ic_src_mux(struct ipu_soc * ipu,int csi_id,bool vdi)654ba07975fSSteve Longerbeam void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
655ba07975fSSteve Longerbeam {
656ba07975fSSteve Longerbeam 	unsigned long flags;
657ba07975fSSteve Longerbeam 	u32 val;
658ba07975fSSteve Longerbeam 
659ba07975fSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
660ba07975fSSteve Longerbeam 
661ba07975fSSteve Longerbeam 	val = ipu_cm_read(ipu, IPU_CONF);
662b7dfee24SMarek Vasut 	if (vdi)
663ba07975fSSteve Longerbeam 		val |= IPU_CONF_IC_INPUT;
664b7dfee24SMarek Vasut 	else
665ba07975fSSteve Longerbeam 		val &= ~IPU_CONF_IC_INPUT;
666b7dfee24SMarek Vasut 
667ba07975fSSteve Longerbeam 	if (csi_id == 1)
668ba07975fSSteve Longerbeam 		val |= IPU_CONF_CSI_SEL;
669ba07975fSSteve Longerbeam 	else
670ba07975fSSteve Longerbeam 		val &= ~IPU_CONF_CSI_SEL;
671b7dfee24SMarek Vasut 
672ba07975fSSteve Longerbeam 	ipu_cm_write(ipu, val, IPU_CONF);
673ba07975fSSteve Longerbeam 
674ba07975fSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
675ba07975fSSteve Longerbeam }
676ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
677ba07975fSSteve Longerbeam 
678ac4708faSSteve Longerbeam 
679ac4708faSSteve Longerbeam /* Frame Synchronization Unit Channel Linking */
680ac4708faSSteve Longerbeam 
681ac4708faSSteve Longerbeam struct fsu_link_reg_info {
682ac4708faSSteve Longerbeam 	int chno;
683ac4708faSSteve Longerbeam 	u32 reg;
684ac4708faSSteve Longerbeam 	u32 mask;
685ac4708faSSteve Longerbeam 	u32 val;
686ac4708faSSteve Longerbeam };
687ac4708faSSteve Longerbeam 
688ac4708faSSteve Longerbeam struct fsu_link_info {
689ac4708faSSteve Longerbeam 	struct fsu_link_reg_info src;
690ac4708faSSteve Longerbeam 	struct fsu_link_reg_info sink;
691ac4708faSSteve Longerbeam };
692ac4708faSSteve Longerbeam 
693ac4708faSSteve Longerbeam static const struct fsu_link_info fsu_link_info[] = {
694ac4708faSSteve Longerbeam 	{
695ac4708faSSteve Longerbeam 		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2,
696ac4708faSSteve Longerbeam 			  FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC },
697ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1,
698ac4708faSSteve Longerbeam 			  FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC },
699ac4708faSSteve Longerbeam 	}, {
700ac4708faSSteve Longerbeam 		.src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2,
701ac4708faSSteve Longerbeam 			  FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF },
702ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1,
703ac4708faSSteve Longerbeam 			  FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF },
704ac4708faSSteve Longerbeam 	}, {
705ac4708faSSteve Longerbeam 		.src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2,
706ac4708faSSteve Longerbeam 			  FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP },
707ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1,
708ac4708faSSteve Longerbeam 			  FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP },
709ac4708faSSteve Longerbeam 	}, {
710ac4708faSSteve Longerbeam 		.src =  { IPUV3_CHANNEL_CSI_DIRECT, 0 },
711ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1,
712ac4708faSSteve Longerbeam 			  FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT },
713ac4708faSSteve Longerbeam 	},
714ac4708faSSteve Longerbeam };
715ac4708faSSteve Longerbeam 
find_fsu_link_info(int src,int sink)716ac4708faSSteve Longerbeam static const struct fsu_link_info *find_fsu_link_info(int src, int sink)
717ac4708faSSteve Longerbeam {
718ac4708faSSteve Longerbeam 	int i;
719ac4708faSSteve Longerbeam 
720ac4708faSSteve Longerbeam 	for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) {
721ac4708faSSteve Longerbeam 		if (src == fsu_link_info[i].src.chno &&
722ac4708faSSteve Longerbeam 		    sink == fsu_link_info[i].sink.chno)
723ac4708faSSteve Longerbeam 			return &fsu_link_info[i];
724ac4708faSSteve Longerbeam 	}
725ac4708faSSteve Longerbeam 
726ac4708faSSteve Longerbeam 	return NULL;
727ac4708faSSteve Longerbeam }
728ac4708faSSteve Longerbeam 
729ac4708faSSteve Longerbeam /*
730ac4708faSSteve Longerbeam  * Links a source channel to a sink channel in the FSU.
731ac4708faSSteve Longerbeam  */
ipu_fsu_link(struct ipu_soc * ipu,int src_ch,int sink_ch)732ac4708faSSteve Longerbeam int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch)
733ac4708faSSteve Longerbeam {
734ac4708faSSteve Longerbeam 	const struct fsu_link_info *link;
735ac4708faSSteve Longerbeam 	u32 src_reg, sink_reg;
736ac4708faSSteve Longerbeam 	unsigned long flags;
737ac4708faSSteve Longerbeam 
738ac4708faSSteve Longerbeam 	link = find_fsu_link_info(src_ch, sink_ch);
739ac4708faSSteve Longerbeam 	if (!link)
740ac4708faSSteve Longerbeam 		return -EINVAL;
741ac4708faSSteve Longerbeam 
742ac4708faSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
743ac4708faSSteve Longerbeam 
744ac4708faSSteve Longerbeam 	if (link->src.mask) {
745ac4708faSSteve Longerbeam 		src_reg = ipu_cm_read(ipu, link->src.reg);
746ac4708faSSteve Longerbeam 		src_reg &= ~link->src.mask;
747ac4708faSSteve Longerbeam 		src_reg |= link->src.val;
748ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, src_reg, link->src.reg);
749ac4708faSSteve Longerbeam 	}
750ac4708faSSteve Longerbeam 
751ac4708faSSteve Longerbeam 	if (link->sink.mask) {
752ac4708faSSteve Longerbeam 		sink_reg = ipu_cm_read(ipu, link->sink.reg);
753ac4708faSSteve Longerbeam 		sink_reg &= ~link->sink.mask;
754ac4708faSSteve Longerbeam 		sink_reg |= link->sink.val;
755ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, sink_reg, link->sink.reg);
756ac4708faSSteve Longerbeam 	}
757ac4708faSSteve Longerbeam 
758ac4708faSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
759ac4708faSSteve Longerbeam 	return 0;
760ac4708faSSteve Longerbeam }
761ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_link);
762ac4708faSSteve Longerbeam 
763ac4708faSSteve Longerbeam /*
764ac4708faSSteve Longerbeam  * Unlinks source and sink channels in the FSU.
765ac4708faSSteve Longerbeam  */
ipu_fsu_unlink(struct ipu_soc * ipu,int src_ch,int sink_ch)766ac4708faSSteve Longerbeam int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch)
767ac4708faSSteve Longerbeam {
768ac4708faSSteve Longerbeam 	const struct fsu_link_info *link;
769ac4708faSSteve Longerbeam 	u32 src_reg, sink_reg;
770ac4708faSSteve Longerbeam 	unsigned long flags;
771ac4708faSSteve Longerbeam 
772ac4708faSSteve Longerbeam 	link = find_fsu_link_info(src_ch, sink_ch);
773ac4708faSSteve Longerbeam 	if (!link)
774ac4708faSSteve Longerbeam 		return -EINVAL;
775ac4708faSSteve Longerbeam 
776ac4708faSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
777ac4708faSSteve Longerbeam 
778ac4708faSSteve Longerbeam 	if (link->src.mask) {
779ac4708faSSteve Longerbeam 		src_reg = ipu_cm_read(ipu, link->src.reg);
780ac4708faSSteve Longerbeam 		src_reg &= ~link->src.mask;
781ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, src_reg, link->src.reg);
782ac4708faSSteve Longerbeam 	}
783ac4708faSSteve Longerbeam 
784ac4708faSSteve Longerbeam 	if (link->sink.mask) {
785ac4708faSSteve Longerbeam 		sink_reg = ipu_cm_read(ipu, link->sink.reg);
786ac4708faSSteve Longerbeam 		sink_reg &= ~link->sink.mask;
787ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, sink_reg, link->sink.reg);
788ac4708faSSteve Longerbeam 	}
789ac4708faSSteve Longerbeam 
790ac4708faSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
791ac4708faSSteve Longerbeam 	return 0;
792ac4708faSSteve Longerbeam }
793ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_unlink);
794ac4708faSSteve Longerbeam 
795ac4708faSSteve Longerbeam /* Link IDMAC channels in the FSU */
ipu_idmac_link(struct ipuv3_channel * src,struct ipuv3_channel * sink)796ac4708faSSteve Longerbeam int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)
797ac4708faSSteve Longerbeam {
798ac4708faSSteve Longerbeam 	return ipu_fsu_link(src->ipu, src->num, sink->num);
799ac4708faSSteve Longerbeam }
800ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_link);
801ac4708faSSteve Longerbeam 
802ac4708faSSteve Longerbeam /* Unlink IDMAC channels in the FSU */
ipu_idmac_unlink(struct ipuv3_channel * src,struct ipuv3_channel * sink)803ac4708faSSteve Longerbeam int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink)
804ac4708faSSteve Longerbeam {
805ac4708faSSteve Longerbeam 	return ipu_fsu_unlink(src->ipu, src->num, sink->num);
806ac4708faSSteve Longerbeam }
807ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_unlink);
808ac4708faSSteve Longerbeam 
80939b9004dSPhilipp Zabel struct ipu_devtype {
81039b9004dSPhilipp Zabel 	const char *name;
81139b9004dSPhilipp Zabel 	unsigned long cm_ofs;
81239b9004dSPhilipp Zabel 	unsigned long cpmem_ofs;
81339b9004dSPhilipp Zabel 	unsigned long srm_ofs;
81439b9004dSPhilipp Zabel 	unsigned long tpm_ofs;
8152ffd48f2SSteve Longerbeam 	unsigned long csi0_ofs;
8162ffd48f2SSteve Longerbeam 	unsigned long csi1_ofs;
8171aa8ea0dSSteve Longerbeam 	unsigned long ic_ofs;
81839b9004dSPhilipp Zabel 	unsigned long disp0_ofs;
81939b9004dSPhilipp Zabel 	unsigned long disp1_ofs;
82039b9004dSPhilipp Zabel 	unsigned long dc_tmpl_ofs;
82139b9004dSPhilipp Zabel 	unsigned long vdi_ofs;
82239b9004dSPhilipp Zabel 	enum ipuv3_type type;
82339b9004dSPhilipp Zabel };
82439b9004dSPhilipp Zabel 
82539b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx51 = {
82639b9004dSPhilipp Zabel 	.name = "IPUv3EX",
82739b9004dSPhilipp Zabel 	.cm_ofs = 0x1e000000,
82839b9004dSPhilipp Zabel 	.cpmem_ofs = 0x1f000000,
82939b9004dSPhilipp Zabel 	.srm_ofs = 0x1f040000,
83039b9004dSPhilipp Zabel 	.tpm_ofs = 0x1f060000,
8312c0408ddSAlexander Shiyan 	.csi0_ofs = 0x1e030000,
8322c0408ddSAlexander Shiyan 	.csi1_ofs = 0x1e038000,
833a49e7c0dSPhilipp Zabel 	.ic_ofs = 0x1e020000,
83439b9004dSPhilipp Zabel 	.disp0_ofs = 0x1e040000,
83539b9004dSPhilipp Zabel 	.disp1_ofs = 0x1e048000,
83639b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x1f080000,
83739b9004dSPhilipp Zabel 	.vdi_ofs = 0x1e068000,
83839b9004dSPhilipp Zabel 	.type = IPUV3EX,
83939b9004dSPhilipp Zabel };
84039b9004dSPhilipp Zabel 
84139b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx53 = {
84239b9004dSPhilipp Zabel 	.name = "IPUv3M",
84339b9004dSPhilipp Zabel 	.cm_ofs = 0x06000000,
84439b9004dSPhilipp Zabel 	.cpmem_ofs = 0x07000000,
84539b9004dSPhilipp Zabel 	.srm_ofs = 0x07040000,
84639b9004dSPhilipp Zabel 	.tpm_ofs = 0x07060000,
847bb867d21SSteve Longerbeam 	.csi0_ofs = 0x06030000,
848bb867d21SSteve Longerbeam 	.csi1_ofs = 0x06038000,
849a49e7c0dSPhilipp Zabel 	.ic_ofs = 0x06020000,
85039b9004dSPhilipp Zabel 	.disp0_ofs = 0x06040000,
85139b9004dSPhilipp Zabel 	.disp1_ofs = 0x06048000,
85239b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x07080000,
85339b9004dSPhilipp Zabel 	.vdi_ofs = 0x06068000,
85439b9004dSPhilipp Zabel 	.type = IPUV3M,
85539b9004dSPhilipp Zabel };
85639b9004dSPhilipp Zabel 
85739b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx6q = {
85839b9004dSPhilipp Zabel 	.name = "IPUv3H",
85939b9004dSPhilipp Zabel 	.cm_ofs = 0x00200000,
86039b9004dSPhilipp Zabel 	.cpmem_ofs = 0x00300000,
86139b9004dSPhilipp Zabel 	.srm_ofs = 0x00340000,
86239b9004dSPhilipp Zabel 	.tpm_ofs = 0x00360000,
8632ffd48f2SSteve Longerbeam 	.csi0_ofs = 0x00230000,
8642ffd48f2SSteve Longerbeam 	.csi1_ofs = 0x00238000,
8651aa8ea0dSSteve Longerbeam 	.ic_ofs = 0x00220000,
86639b9004dSPhilipp Zabel 	.disp0_ofs = 0x00240000,
86739b9004dSPhilipp Zabel 	.disp1_ofs = 0x00248000,
86839b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x00380000,
86939b9004dSPhilipp Zabel 	.vdi_ofs = 0x00268000,
87039b9004dSPhilipp Zabel 	.type = IPUV3H,
87139b9004dSPhilipp Zabel };
87239b9004dSPhilipp Zabel 
87339b9004dSPhilipp Zabel static const struct of_device_id imx_ipu_dt_ids[] = {
87439b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, },
87539b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, },
87639b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, },
87792681fe7SLucas Stach 	{ .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6q, },
87839b9004dSPhilipp Zabel 	{ /* sentinel */ }
87939b9004dSPhilipp Zabel };
88039b9004dSPhilipp Zabel MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids);
88139b9004dSPhilipp Zabel 
ipu_submodules_init(struct ipu_soc * ipu,struct platform_device * pdev,unsigned long ipu_base,struct clk * ipu_clk)88239b9004dSPhilipp Zabel static int ipu_submodules_init(struct ipu_soc *ipu,
88339b9004dSPhilipp Zabel 		struct platform_device *pdev, unsigned long ipu_base,
88439b9004dSPhilipp Zabel 		struct clk *ipu_clk)
88539b9004dSPhilipp Zabel {
88639b9004dSPhilipp Zabel 	char *unit;
88739b9004dSPhilipp Zabel 	int ret;
88839b9004dSPhilipp Zabel 	struct device *dev = &pdev->dev;
88939b9004dSPhilipp Zabel 	const struct ipu_devtype *devtype = ipu->devtype;
89039b9004dSPhilipp Zabel 
8917d2691daSSteve Longerbeam 	ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs);
8927d2691daSSteve Longerbeam 	if (ret) {
8937d2691daSSteve Longerbeam 		unit = "cpmem";
8947d2691daSSteve Longerbeam 		goto err_cpmem;
8957d2691daSSteve Longerbeam 	}
8967d2691daSSteve Longerbeam 
8972ffd48f2SSteve Longerbeam 	ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs,
8982ffd48f2SSteve Longerbeam 			   IPU_CONF_CSI0_EN, ipu_clk);
8992ffd48f2SSteve Longerbeam 	if (ret) {
9002ffd48f2SSteve Longerbeam 		unit = "csi0";
9012ffd48f2SSteve Longerbeam 		goto err_csi_0;
9022ffd48f2SSteve Longerbeam 	}
9032ffd48f2SSteve Longerbeam 
9042ffd48f2SSteve Longerbeam 	ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs,
9052ffd48f2SSteve Longerbeam 			   IPU_CONF_CSI1_EN, ipu_clk);
9062ffd48f2SSteve Longerbeam 	if (ret) {
9072ffd48f2SSteve Longerbeam 		unit = "csi1";
9082ffd48f2SSteve Longerbeam 		goto err_csi_1;
9092ffd48f2SSteve Longerbeam 	}
9102ffd48f2SSteve Longerbeam 
9111aa8ea0dSSteve Longerbeam 	ret = ipu_ic_init(ipu, dev,
9121aa8ea0dSSteve Longerbeam 			  ipu_base + devtype->ic_ofs,
9131aa8ea0dSSteve Longerbeam 			  ipu_base + devtype->tpm_ofs);
9141aa8ea0dSSteve Longerbeam 	if (ret) {
9151aa8ea0dSSteve Longerbeam 		unit = "ic";
9161aa8ea0dSSteve Longerbeam 		goto err_ic;
9171aa8ea0dSSteve Longerbeam 	}
9181aa8ea0dSSteve Longerbeam 
9192d2ead45SSteve Longerbeam 	ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs,
9202d2ead45SSteve Longerbeam 			   IPU_CONF_VDI_EN | IPU_CONF_ISP_EN |
9212d2ead45SSteve Longerbeam 			   IPU_CONF_IC_INPUT);
9222d2ead45SSteve Longerbeam 	if (ret) {
9232d2ead45SSteve Longerbeam 		unit = "vdi";
9242d2ead45SSteve Longerbeam 		goto err_vdi;
9252d2ead45SSteve Longerbeam 	}
9262d2ead45SSteve Longerbeam 
927cd98e85aSSteve Longerbeam 	ret = ipu_image_convert_init(ipu, dev);
928cd98e85aSSteve Longerbeam 	if (ret) {
929cd98e85aSSteve Longerbeam 		unit = "image_convert";
930cd98e85aSSteve Longerbeam 		goto err_image_convert;
931cd98e85aSSteve Longerbeam 	}
932cd98e85aSSteve Longerbeam 
93339b9004dSPhilipp Zabel 	ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
93439b9004dSPhilipp Zabel 			  IPU_CONF_DI0_EN, ipu_clk);
93539b9004dSPhilipp Zabel 	if (ret) {
93639b9004dSPhilipp Zabel 		unit = "di0";
93739b9004dSPhilipp Zabel 		goto err_di_0;
93839b9004dSPhilipp Zabel 	}
93939b9004dSPhilipp Zabel 
94039b9004dSPhilipp Zabel 	ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs,
94139b9004dSPhilipp Zabel 			IPU_CONF_DI1_EN, ipu_clk);
94239b9004dSPhilipp Zabel 	if (ret) {
94339b9004dSPhilipp Zabel 		unit = "di1";
94439b9004dSPhilipp Zabel 		goto err_di_1;
94539b9004dSPhilipp Zabel 	}
94639b9004dSPhilipp Zabel 
94739b9004dSPhilipp Zabel 	ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs +
94839b9004dSPhilipp Zabel 			IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs);
94939b9004dSPhilipp Zabel 	if (ret) {
95039b9004dSPhilipp Zabel 		unit = "dc_template";
95139b9004dSPhilipp Zabel 		goto err_dc;
95239b9004dSPhilipp Zabel 	}
95339b9004dSPhilipp Zabel 
95439b9004dSPhilipp Zabel 	ret = ipu_dmfc_init(ipu, dev, ipu_base +
95539b9004dSPhilipp Zabel 			devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk);
95639b9004dSPhilipp Zabel 	if (ret) {
95739b9004dSPhilipp Zabel 		unit = "dmfc";
95839b9004dSPhilipp Zabel 		goto err_dmfc;
95939b9004dSPhilipp Zabel 	}
96039b9004dSPhilipp Zabel 
96139b9004dSPhilipp Zabel 	ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs);
96239b9004dSPhilipp Zabel 	if (ret) {
96339b9004dSPhilipp Zabel 		unit = "dp";
96439b9004dSPhilipp Zabel 		goto err_dp;
96539b9004dSPhilipp Zabel 	}
96639b9004dSPhilipp Zabel 
96735de925fSPhilipp Zabel 	ret = ipu_smfc_init(ipu, dev, ipu_base +
96835de925fSPhilipp Zabel 			devtype->cm_ofs + IPU_CM_SMFC_REG_OFS);
96935de925fSPhilipp Zabel 	if (ret) {
97035de925fSPhilipp Zabel 		unit = "smfc";
97135de925fSPhilipp Zabel 		goto err_smfc;
97235de925fSPhilipp Zabel 	}
97335de925fSPhilipp Zabel 
97439b9004dSPhilipp Zabel 	return 0;
97539b9004dSPhilipp Zabel 
97635de925fSPhilipp Zabel err_smfc:
97735de925fSPhilipp Zabel 	ipu_dp_exit(ipu);
97839b9004dSPhilipp Zabel err_dp:
97939b9004dSPhilipp Zabel 	ipu_dmfc_exit(ipu);
98039b9004dSPhilipp Zabel err_dmfc:
98139b9004dSPhilipp Zabel 	ipu_dc_exit(ipu);
98239b9004dSPhilipp Zabel err_dc:
98339b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 1);
98439b9004dSPhilipp Zabel err_di_1:
98539b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 0);
98639b9004dSPhilipp Zabel err_di_0:
987cd98e85aSSteve Longerbeam 	ipu_image_convert_exit(ipu);
988cd98e85aSSteve Longerbeam err_image_convert:
9892d2ead45SSteve Longerbeam 	ipu_vdi_exit(ipu);
9902d2ead45SSteve Longerbeam err_vdi:
9911aa8ea0dSSteve Longerbeam 	ipu_ic_exit(ipu);
9921aa8ea0dSSteve Longerbeam err_ic:
9932ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 1);
9942ffd48f2SSteve Longerbeam err_csi_1:
9952ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 0);
9962ffd48f2SSteve Longerbeam err_csi_0:
9977d2691daSSteve Longerbeam 	ipu_cpmem_exit(ipu);
9987d2691daSSteve Longerbeam err_cpmem:
99939b9004dSPhilipp Zabel 	dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret);
100039b9004dSPhilipp Zabel 	return ret;
100139b9004dSPhilipp Zabel }
100239b9004dSPhilipp Zabel 
ipu_irq_handle(struct ipu_soc * ipu,const int * regs,int num_regs)100339b9004dSPhilipp Zabel static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs)
100439b9004dSPhilipp Zabel {
100539b9004dSPhilipp Zabel 	unsigned long status;
100666c6594bSMarc Zyngier 	int i, bit;
100739b9004dSPhilipp Zabel 
100839b9004dSPhilipp Zabel 	for (i = 0; i < num_regs; i++) {
100939b9004dSPhilipp Zabel 
101039b9004dSPhilipp Zabel 		status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i]));
101139b9004dSPhilipp Zabel 		status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i]));
101239b9004dSPhilipp Zabel 
101366c6594bSMarc Zyngier 		for_each_set_bit(bit, &status, 32)
101466c6594bSMarc Zyngier 			generic_handle_domain_irq(ipu->domain,
1015682b7c1cSLinus Torvalds 						  regs[i] * 32 + bit);
101639b9004dSPhilipp Zabel 	}
101739b9004dSPhilipp Zabel }
101839b9004dSPhilipp Zabel 
ipu_irq_handler(struct irq_desc * desc)1019bd0b9ac4SThomas Gleixner static void ipu_irq_handler(struct irq_desc *desc)
102039b9004dSPhilipp Zabel {
102139b9004dSPhilipp Zabel 	struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
10224d9efdfcSJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
1023ac66b834SColin Ian King 	static const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14};
102439b9004dSPhilipp Zabel 
102539b9004dSPhilipp Zabel 	chained_irq_enter(chip, desc);
102639b9004dSPhilipp Zabel 
102739b9004dSPhilipp Zabel 	ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
102839b9004dSPhilipp Zabel 
102939b9004dSPhilipp Zabel 	chained_irq_exit(chip, desc);
103039b9004dSPhilipp Zabel }
103139b9004dSPhilipp Zabel 
ipu_err_irq_handler(struct irq_desc * desc)1032bd0b9ac4SThomas Gleixner static void ipu_err_irq_handler(struct irq_desc *desc)
103339b9004dSPhilipp Zabel {
103439b9004dSPhilipp Zabel 	struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
10354d9efdfcSJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
1036ac66b834SColin Ian King 	static const int int_reg[] = { 4, 5, 8, 9};
103739b9004dSPhilipp Zabel 
103839b9004dSPhilipp Zabel 	chained_irq_enter(chip, desc);
103939b9004dSPhilipp Zabel 
104039b9004dSPhilipp Zabel 	ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
104139b9004dSPhilipp Zabel 
104239b9004dSPhilipp Zabel 	chained_irq_exit(chip, desc);
104339b9004dSPhilipp Zabel }
104439b9004dSPhilipp Zabel 
ipu_map_irq(struct ipu_soc * ipu,int irq)1045682b7c1cSLinus Torvalds int ipu_map_irq(struct ipu_soc *ipu, int irq)
1046682b7c1cSLinus Torvalds {
1047682b7c1cSLinus Torvalds 	int virq;
1048682b7c1cSLinus Torvalds 
1049682b7c1cSLinus Torvalds 	virq = irq_linear_revmap(ipu->domain, irq);
1050682b7c1cSLinus Torvalds 	if (!virq)
1051682b7c1cSLinus Torvalds 		virq = irq_create_mapping(ipu->domain, irq);
1052682b7c1cSLinus Torvalds 
1053682b7c1cSLinus Torvalds 	return virq;
1054682b7c1cSLinus Torvalds }
1055682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_map_irq);
1056682b7c1cSLinus Torvalds 
ipu_idmac_channel_irq(struct ipu_soc * ipu,struct ipuv3_channel * channel,enum ipu_channel_irq irq_type)105739b9004dSPhilipp Zabel int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
105839b9004dSPhilipp Zabel 		enum ipu_channel_irq irq_type)
105939b9004dSPhilipp Zabel {
1060682b7c1cSLinus Torvalds 	return ipu_map_irq(ipu, irq_type + channel->num);
106139b9004dSPhilipp Zabel }
106239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq);
106339b9004dSPhilipp Zabel 
ipu_submodules_exit(struct ipu_soc * ipu)106439b9004dSPhilipp Zabel static void ipu_submodules_exit(struct ipu_soc *ipu)
106539b9004dSPhilipp Zabel {
106635de925fSPhilipp Zabel 	ipu_smfc_exit(ipu);
106739b9004dSPhilipp Zabel 	ipu_dp_exit(ipu);
106839b9004dSPhilipp Zabel 	ipu_dmfc_exit(ipu);
106939b9004dSPhilipp Zabel 	ipu_dc_exit(ipu);
107039b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 1);
107139b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 0);
1072cd98e85aSSteve Longerbeam 	ipu_image_convert_exit(ipu);
10732d2ead45SSteve Longerbeam 	ipu_vdi_exit(ipu);
10741aa8ea0dSSteve Longerbeam 	ipu_ic_exit(ipu);
10752ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 1);
10762ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 0);
10777d2691daSSteve Longerbeam 	ipu_cpmem_exit(ipu);
107839b9004dSPhilipp Zabel }
107939b9004dSPhilipp Zabel 
platform_remove_devices_fn(struct device * dev,void * unused)108039b9004dSPhilipp Zabel static int platform_remove_devices_fn(struct device *dev, void *unused)
108139b9004dSPhilipp Zabel {
108239b9004dSPhilipp Zabel 	struct platform_device *pdev = to_platform_device(dev);
108339b9004dSPhilipp Zabel 
108439b9004dSPhilipp Zabel 	platform_device_unregister(pdev);
108539b9004dSPhilipp Zabel 
108639b9004dSPhilipp Zabel 	return 0;
108739b9004dSPhilipp Zabel }
108839b9004dSPhilipp Zabel 
platform_device_unregister_children(struct platform_device * pdev)108939b9004dSPhilipp Zabel static void platform_device_unregister_children(struct platform_device *pdev)
109039b9004dSPhilipp Zabel {
109139b9004dSPhilipp Zabel 	device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn);
109239b9004dSPhilipp Zabel }
109339b9004dSPhilipp Zabel 
109439b9004dSPhilipp Zabel struct ipu_platform_reg {
109539b9004dSPhilipp Zabel 	struct ipu_client_platformdata pdata;
109639b9004dSPhilipp Zabel 	const char *name;
109739b9004dSPhilipp Zabel };
109839b9004dSPhilipp Zabel 
1099304e6be6SPhilipp Zabel /* These must be in the order of the corresponding device tree port nodes */
1100310944d1SPhilipp Zabel static struct ipu_platform_reg client_reg[] = {
110139b9004dSPhilipp Zabel 	{
110239b9004dSPhilipp Zabel 		.pdata = {
1103304e6be6SPhilipp Zabel 			.csi = 0,
1104304e6be6SPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_CSI0,
1105304e6be6SPhilipp Zabel 			.dma[1] = -EINVAL,
1106304e6be6SPhilipp Zabel 		},
110788287ec3SSteve Longerbeam 		.name = "imx-ipuv3-csi",
1108304e6be6SPhilipp Zabel 	}, {
1109304e6be6SPhilipp Zabel 		.pdata = {
1110304e6be6SPhilipp Zabel 			.csi = 1,
1111304e6be6SPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_CSI1,
1112304e6be6SPhilipp Zabel 			.dma[1] = -EINVAL,
1113304e6be6SPhilipp Zabel 		},
111488287ec3SSteve Longerbeam 		.name = "imx-ipuv3-csi",
1115304e6be6SPhilipp Zabel 	}, {
1116304e6be6SPhilipp Zabel 		.pdata = {
111739b9004dSPhilipp Zabel 			.di = 0,
111839b9004dSPhilipp Zabel 			.dc = 5,
111939b9004dSPhilipp Zabel 			.dp = IPU_DP_FLOW_SYNC_BG,
112039b9004dSPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC,
112139b9004dSPhilipp Zabel 			.dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC,
112239b9004dSPhilipp Zabel 		},
112339b9004dSPhilipp Zabel 		.name = "imx-ipuv3-crtc",
112439b9004dSPhilipp Zabel 	}, {
112539b9004dSPhilipp Zabel 		.pdata = {
112639b9004dSPhilipp Zabel 			.di = 1,
112739b9004dSPhilipp Zabel 			.dc = 1,
112839b9004dSPhilipp Zabel 			.dp = -EINVAL,
112939b9004dSPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC,
113039b9004dSPhilipp Zabel 			.dma[1] = -EINVAL,
113139b9004dSPhilipp Zabel 		},
113239b9004dSPhilipp Zabel 		.name = "imx-ipuv3-crtc",
113339b9004dSPhilipp Zabel 	},
113439b9004dSPhilipp Zabel };
113539b9004dSPhilipp Zabel 
113639b9004dSPhilipp Zabel static DEFINE_MUTEX(ipu_client_id_mutex);
113739b9004dSPhilipp Zabel static int ipu_client_id;
113839b9004dSPhilipp Zabel 
ipu_add_client_devices(struct ipu_soc * ipu,unsigned long ipu_base)1139d6ca8ca7SPhilipp Zabel static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base)
114039b9004dSPhilipp Zabel {
114139b9004dSPhilipp Zabel 	struct device *dev = ipu->dev;
114239b9004dSPhilipp Zabel 	unsigned i;
114339b9004dSPhilipp Zabel 	int id, ret;
114439b9004dSPhilipp Zabel 
114539b9004dSPhilipp Zabel 	mutex_lock(&ipu_client_id_mutex);
114639b9004dSPhilipp Zabel 	id = ipu_client_id;
114739b9004dSPhilipp Zabel 	ipu_client_id += ARRAY_SIZE(client_reg);
114839b9004dSPhilipp Zabel 	mutex_unlock(&ipu_client_id_mutex);
114939b9004dSPhilipp Zabel 
115039b9004dSPhilipp Zabel 	for (i = 0; i < ARRAY_SIZE(client_reg); i++) {
1151310944d1SPhilipp Zabel 		struct ipu_platform_reg *reg = &client_reg[i];
115239b9004dSPhilipp Zabel 		struct platform_device *pdev;
115317e05217SPhilipp Zabel 		struct device_node *of_node;
115417e05217SPhilipp Zabel 
115517e05217SPhilipp Zabel 		/* Associate subdevice with the corresponding port node */
115617e05217SPhilipp Zabel 		of_node = of_graph_get_port_by_id(dev->of_node, i);
115717e05217SPhilipp Zabel 		if (!of_node) {
115817e05217SPhilipp Zabel 			dev_info(dev,
11594bf99144SRob Herring 				 "no port@%d node in %pOF, not using %s%d\n",
11604bf99144SRob Herring 				 i, dev->of_node,
116117e05217SPhilipp Zabel 				 (i / 2) ? "DI" : "CSI", i % 2);
116217e05217SPhilipp Zabel 			continue;
116317e05217SPhilipp Zabel 		}
116439b9004dSPhilipp Zabel 
1165304e6be6SPhilipp Zabel 		pdev = platform_device_alloc(reg->name, id++);
1166304e6be6SPhilipp Zabel 		if (!pdev) {
1167304e6be6SPhilipp Zabel 			ret = -ENOMEM;
11689afdf98cSLiang He 			of_node_put(of_node);
1169304e6be6SPhilipp Zabel 			goto err_register;
1170304e6be6SPhilipp Zabel 		}
117139b9004dSPhilipp Zabel 
1172304e6be6SPhilipp Zabel 		pdev->dev.parent = dev;
1173304e6be6SPhilipp Zabel 
1174310944d1SPhilipp Zabel 		reg->pdata.of_node = of_node;
1175304e6be6SPhilipp Zabel 		ret = platform_device_add_data(pdev, &reg->pdata,
1176304e6be6SPhilipp Zabel 					       sizeof(reg->pdata));
1177304e6be6SPhilipp Zabel 		if (!ret)
1178304e6be6SPhilipp Zabel 			ret = platform_device_add(pdev);
1179304e6be6SPhilipp Zabel 		if (ret) {
1180304e6be6SPhilipp Zabel 			platform_device_put(pdev);
118139b9004dSPhilipp Zabel 			goto err_register;
118239b9004dSPhilipp Zabel 		}
1183e4946cdcSAxel Lin 	}
118439b9004dSPhilipp Zabel 
118539b9004dSPhilipp Zabel 	return 0;
118639b9004dSPhilipp Zabel 
118739b9004dSPhilipp Zabel err_register:
118839b9004dSPhilipp Zabel 	platform_device_unregister_children(to_platform_device(dev));
118939b9004dSPhilipp Zabel 
119039b9004dSPhilipp Zabel 	return ret;
119139b9004dSPhilipp Zabel }
119239b9004dSPhilipp Zabel 
119339b9004dSPhilipp Zabel 
ipu_irq_init(struct ipu_soc * ipu)119439b9004dSPhilipp Zabel static int ipu_irq_init(struct ipu_soc *ipu)
119539b9004dSPhilipp Zabel {
119639b9004dSPhilipp Zabel 	struct irq_chip_generic *gc;
119739b9004dSPhilipp Zabel 	struct irq_chip_type *ct;
119839b9004dSPhilipp Zabel 	unsigned long unused[IPU_NUM_IRQS / 32] = {
119939b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
120039b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
120139b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
120239b9004dSPhilipp Zabel 		0x4077ffff, 0xffe7e1fd,
120339b9004dSPhilipp Zabel 		0x23fffffe, 0x8880fff0,
120439b9004dSPhilipp Zabel 		0xf98fe7d0, 0xfff81fff,
120539b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
120639b9004dSPhilipp Zabel 		0x00000000,
120739b9004dSPhilipp Zabel 	};
120839b9004dSPhilipp Zabel 	int ret, i;
120939b9004dSPhilipp Zabel 
121039b9004dSPhilipp Zabel 	ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS,
121139b9004dSPhilipp Zabel 					    &irq_generic_chip_ops, ipu);
121239b9004dSPhilipp Zabel 	if (!ipu->domain) {
121339b9004dSPhilipp Zabel 		dev_err(ipu->dev, "failed to add irq domain\n");
121439b9004dSPhilipp Zabel 		return -ENODEV;
121539b9004dSPhilipp Zabel 	}
121639b9004dSPhilipp Zabel 
121739b9004dSPhilipp Zabel 	ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU",
1218ca0141deSRob Herring 					     handle_level_irq, 0, 0, 0);
121939b9004dSPhilipp Zabel 	if (ret < 0) {
122039b9004dSPhilipp Zabel 		dev_err(ipu->dev, "failed to alloc generic irq chips\n");
122139b9004dSPhilipp Zabel 		irq_domain_remove(ipu->domain);
122239b9004dSPhilipp Zabel 		return ret;
122339b9004dSPhilipp Zabel 	}
122439b9004dSPhilipp Zabel 
1225a92d8145SPhilipp Zabel 	/* Mask and clear all interrupts */
1226a92d8145SPhilipp Zabel 	for (i = 0; i < IPU_NUM_IRQS; i += 32) {
1227510e6426SRussell King 		ipu_cm_write(ipu, 0, IPU_INT_CTRL(i / 32));
1228a92d8145SPhilipp Zabel 		ipu_cm_write(ipu, ~unused[i / 32], IPU_INT_STAT(i / 32));
1229a92d8145SPhilipp Zabel 	}
1230510e6426SRussell King 
123139b9004dSPhilipp Zabel 	for (i = 0; i < IPU_NUM_IRQS; i += 32) {
123239b9004dSPhilipp Zabel 		gc = irq_get_domain_generic_chip(ipu->domain, i);
123339b9004dSPhilipp Zabel 		gc->reg_base = ipu->cm_reg;
123439b9004dSPhilipp Zabel 		gc->unused = unused[i / 32];
123539b9004dSPhilipp Zabel 		ct = gc->chip_types;
123639b9004dSPhilipp Zabel 		ct->chip.irq_ack = irq_gc_ack_set_bit;
123739b9004dSPhilipp Zabel 		ct->chip.irq_mask = irq_gc_mask_clr_bit;
123839b9004dSPhilipp Zabel 		ct->chip.irq_unmask = irq_gc_mask_set_bit;
123939b9004dSPhilipp Zabel 		ct->regs.ack = IPU_INT_STAT(i / 32);
124039b9004dSPhilipp Zabel 		ct->regs.mask = IPU_INT_CTRL(i / 32);
124139b9004dSPhilipp Zabel 	}
124239b9004dSPhilipp Zabel 
124386f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu);
124486f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler,
124586f5e733SRussell King 					 ipu);
124639b9004dSPhilipp Zabel 
124739b9004dSPhilipp Zabel 	return 0;
124839b9004dSPhilipp Zabel }
124939b9004dSPhilipp Zabel 
ipu_irq_exit(struct ipu_soc * ipu)125039b9004dSPhilipp Zabel static void ipu_irq_exit(struct ipu_soc *ipu)
125139b9004dSPhilipp Zabel {
125239b9004dSPhilipp Zabel 	int i, irq;
125339b9004dSPhilipp Zabel 
125486f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL);
125586f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL);
125639b9004dSPhilipp Zabel 
125739b9004dSPhilipp Zabel 	/* TODO: remove irq_domain_generic_chips */
125839b9004dSPhilipp Zabel 
125939b9004dSPhilipp Zabel 	for (i = 0; i < IPU_NUM_IRQS; i++) {
126039b9004dSPhilipp Zabel 		irq = irq_linear_revmap(ipu->domain, i);
126139b9004dSPhilipp Zabel 		if (irq)
126239b9004dSPhilipp Zabel 			irq_dispose_mapping(irq);
126339b9004dSPhilipp Zabel 	}
126439b9004dSPhilipp Zabel 
126539b9004dSPhilipp Zabel 	irq_domain_remove(ipu->domain);
126639b9004dSPhilipp Zabel }
126739b9004dSPhilipp Zabel 
ipu_dump(struct ipu_soc * ipu)12683feb049fSSteve Longerbeam void ipu_dump(struct ipu_soc *ipu)
12693feb049fSSteve Longerbeam {
12703feb049fSSteve Longerbeam 	int i;
12713feb049fSSteve Longerbeam 
12723feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n",
12733feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_CONF));
12743feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n",
12753feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CONF));
12763feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n",
12773feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_EN(0)));
12783feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n",
12793feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_EN(32)));
12803feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n",
12813feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_PRI(0)));
12823feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n",
12833feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_PRI(32)));
12843feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n",
12853feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_BAND_EN(0)));
12863feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n",
12873feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_BAND_EN(32)));
12883feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n",
12893feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0)));
12903feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n",
12913feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32)));
12923feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n",
12933feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_PROC_FLOW1));
12943feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n",
12953feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_PROC_FLOW2));
12963feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n",
12973feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_PROC_FLOW3));
12983feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n",
12993feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_DISP_FLOW1));
13003feb049fSSteve Longerbeam 	for (i = 0; i < 15; i++)
13013feb049fSSteve Longerbeam 		dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i,
13023feb049fSSteve Longerbeam 			ipu_cm_read(ipu, IPU_INT_CTRL(i)));
13033feb049fSSteve Longerbeam }
13043feb049fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_dump);
13053feb049fSSteve Longerbeam 
ipu_probe(struct platform_device * pdev)130639b9004dSPhilipp Zabel static int ipu_probe(struct platform_device *pdev)
130739b9004dSPhilipp Zabel {
1308572a7615SSteve Longerbeam 	struct device_node *np = pdev->dev.of_node;
130939b9004dSPhilipp Zabel 	struct ipu_soc *ipu;
131039b9004dSPhilipp Zabel 	struct resource *res;
131139b9004dSPhilipp Zabel 	unsigned long ipu_base;
131293adc8b5SPhilipp Zabel 	int ret, irq_sync, irq_err;
131339b9004dSPhilipp Zabel 	const struct ipu_devtype *devtype;
131439b9004dSPhilipp Zabel 
1315e92e4478SLABBE Corentin 	devtype = of_device_get_match_data(&pdev->dev);
1316e92e4478SLABBE Corentin 	if (!devtype)
1317e92e4478SLABBE Corentin 		return -EINVAL;
131839b9004dSPhilipp Zabel 
131939b9004dSPhilipp Zabel 	irq_sync = platform_get_irq(pdev, 0);
132039b9004dSPhilipp Zabel 	irq_err = platform_get_irq(pdev, 1);
132139b9004dSPhilipp Zabel 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
132239b9004dSPhilipp Zabel 
132339b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n",
132439b9004dSPhilipp Zabel 			irq_sync, irq_err);
132539b9004dSPhilipp Zabel 
132639b9004dSPhilipp Zabel 	if (!res || irq_sync < 0 || irq_err < 0)
132739b9004dSPhilipp Zabel 		return -ENODEV;
132839b9004dSPhilipp Zabel 
132939b9004dSPhilipp Zabel 	ipu_base = res->start;
133039b9004dSPhilipp Zabel 
133139b9004dSPhilipp Zabel 	ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL);
133239b9004dSPhilipp Zabel 	if (!ipu)
133339b9004dSPhilipp Zabel 		return -ENODEV;
133439b9004dSPhilipp Zabel 
133592681fe7SLucas Stach 	ipu->id = of_alias_get_id(np, "ipu");
13362d87e6c1SPhilipp Zabel 	if (ipu->id < 0)
13372d87e6c1SPhilipp Zabel 		ipu->id = 0;
133892681fe7SLucas Stach 
133930310c83SLucas Stach 	if (of_device_is_compatible(np, "fsl,imx6qp-ipu") &&
134030310c83SLucas Stach 	    IS_ENABLED(CONFIG_DRM)) {
134192681fe7SLucas Stach 		ipu->prg_priv = ipu_prg_lookup_by_phandle(&pdev->dev,
134292681fe7SLucas Stach 							  "fsl,prg", ipu->id);
134392681fe7SLucas Stach 		if (!ipu->prg_priv)
134492681fe7SLucas Stach 			return -EPROBE_DEFER;
134592681fe7SLucas Stach 	}
134692681fe7SLucas Stach 
134739b9004dSPhilipp Zabel 	ipu->devtype = devtype;
134839b9004dSPhilipp Zabel 	ipu->ipu_type = devtype->type;
134939b9004dSPhilipp Zabel 
135039b9004dSPhilipp Zabel 	spin_lock_init(&ipu->lock);
135139b9004dSPhilipp Zabel 	mutex_init(&ipu->channel_lock);
135293adc8b5SPhilipp Zabel 	INIT_LIST_HEAD(&ipu->channels);
135339b9004dSPhilipp Zabel 
135439b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "cm_reg:   0x%08lx\n",
135539b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs);
135639b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "idmac:    0x%08lx\n",
135739b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS);
135839b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "cpmem:    0x%08lx\n",
135939b9004dSPhilipp Zabel 			ipu_base + devtype->cpmem_ofs);
13602ffd48f2SSteve Longerbeam 	dev_dbg(&pdev->dev, "csi0:    0x%08lx\n",
13612ffd48f2SSteve Longerbeam 			ipu_base + devtype->csi0_ofs);
13622ffd48f2SSteve Longerbeam 	dev_dbg(&pdev->dev, "csi1:    0x%08lx\n",
13632ffd48f2SSteve Longerbeam 			ipu_base + devtype->csi1_ofs);
13641aa8ea0dSSteve Longerbeam 	dev_dbg(&pdev->dev, "ic:      0x%08lx\n",
13651aa8ea0dSSteve Longerbeam 			ipu_base + devtype->ic_ofs);
136639b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "disp0:    0x%08lx\n",
136739b9004dSPhilipp Zabel 			ipu_base + devtype->disp0_ofs);
136839b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "disp1:    0x%08lx\n",
136939b9004dSPhilipp Zabel 			ipu_base + devtype->disp1_ofs);
137039b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "srm:      0x%08lx\n",
137139b9004dSPhilipp Zabel 			ipu_base + devtype->srm_ofs);
137239b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "tpm:      0x%08lx\n",
137339b9004dSPhilipp Zabel 			ipu_base + devtype->tpm_ofs);
137439b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "dc:       0x%08lx\n",
137539b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS);
137639b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "ic:       0x%08lx\n",
137739b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS);
137839b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "dmfc:     0x%08lx\n",
137939b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS);
138039b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "vdi:      0x%08lx\n",
138139b9004dSPhilipp Zabel 			ipu_base + devtype->vdi_ofs);
138239b9004dSPhilipp Zabel 
138339b9004dSPhilipp Zabel 	ipu->cm_reg = devm_ioremap(&pdev->dev,
138439b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs, PAGE_SIZE);
138539b9004dSPhilipp Zabel 	ipu->idmac_reg = devm_ioremap(&pdev->dev,
138639b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS,
138739b9004dSPhilipp Zabel 			PAGE_SIZE);
138839b9004dSPhilipp Zabel 
13897d2691daSSteve Longerbeam 	if (!ipu->cm_reg || !ipu->idmac_reg)
139039b9004dSPhilipp Zabel 		return -ENOMEM;
139139b9004dSPhilipp Zabel 
139239b9004dSPhilipp Zabel 	ipu->clk = devm_clk_get(&pdev->dev, "bus");
139339b9004dSPhilipp Zabel 	if (IS_ERR(ipu->clk)) {
139439b9004dSPhilipp Zabel 		ret = PTR_ERR(ipu->clk);
139539b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "clk_get failed with %d", ret);
139639b9004dSPhilipp Zabel 		return ret;
139739b9004dSPhilipp Zabel 	}
139839b9004dSPhilipp Zabel 
139939b9004dSPhilipp Zabel 	platform_set_drvdata(pdev, ipu);
140039b9004dSPhilipp Zabel 
140139b9004dSPhilipp Zabel 	ret = clk_prepare_enable(ipu->clk);
140239b9004dSPhilipp Zabel 	if (ret) {
140339b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
140439b9004dSPhilipp Zabel 		return ret;
140539b9004dSPhilipp Zabel 	}
140639b9004dSPhilipp Zabel 
140739b9004dSPhilipp Zabel 	ipu->dev = &pdev->dev;
140839b9004dSPhilipp Zabel 	ipu->irq_sync = irq_sync;
140939b9004dSPhilipp Zabel 	ipu->irq_err = irq_err;
141039b9004dSPhilipp Zabel 
141139b9004dSPhilipp Zabel 	ret = device_reset(&pdev->dev);
141239b9004dSPhilipp Zabel 	if (ret) {
141339b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "failed to reset: %d\n", ret);
141439b9004dSPhilipp Zabel 		goto out_failed_reset;
141539b9004dSPhilipp Zabel 	}
141639b9004dSPhilipp Zabel 	ret = ipu_memory_reset(ipu);
141739b9004dSPhilipp Zabel 	if (ret)
141839b9004dSPhilipp Zabel 		goto out_failed_reset;
141939b9004dSPhilipp Zabel 
1420596a65d1SDavid Jander 	ret = ipu_irq_init(ipu);
1421596a65d1SDavid Jander 	if (ret)
1422596a65d1SDavid Jander 		goto out_failed_irq;
1423596a65d1SDavid Jander 
142439b9004dSPhilipp Zabel 	/* Set MCU_T to divide MCU access window into 2 */
142539b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
142639b9004dSPhilipp Zabel 			IPU_DISP_GEN);
142739b9004dSPhilipp Zabel 
142839b9004dSPhilipp Zabel 	ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk);
142939b9004dSPhilipp Zabel 	if (ret)
143039b9004dSPhilipp Zabel 		goto failed_submodules_init;
143139b9004dSPhilipp Zabel 
1432d6ca8ca7SPhilipp Zabel 	ret = ipu_add_client_devices(ipu, ipu_base);
143339b9004dSPhilipp Zabel 	if (ret) {
143439b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "adding client devices failed with %d\n",
143539b9004dSPhilipp Zabel 				ret);
143639b9004dSPhilipp Zabel 		goto failed_add_clients;
143739b9004dSPhilipp Zabel 	}
143839b9004dSPhilipp Zabel 
143939b9004dSPhilipp Zabel 	dev_info(&pdev->dev, "%s probed\n", devtype->name);
144039b9004dSPhilipp Zabel 
144139b9004dSPhilipp Zabel 	return 0;
144239b9004dSPhilipp Zabel 
144339b9004dSPhilipp Zabel failed_add_clients:
144439b9004dSPhilipp Zabel 	ipu_submodules_exit(ipu);
144539b9004dSPhilipp Zabel failed_submodules_init:
144639b9004dSPhilipp Zabel 	ipu_irq_exit(ipu);
144739b9004dSPhilipp Zabel out_failed_irq:
1448596a65d1SDavid Jander out_failed_reset:
144939b9004dSPhilipp Zabel 	clk_disable_unprepare(ipu->clk);
145039b9004dSPhilipp Zabel 	return ret;
145139b9004dSPhilipp Zabel }
145239b9004dSPhilipp Zabel 
ipu_remove(struct platform_device * pdev)145339b9004dSPhilipp Zabel static int ipu_remove(struct platform_device *pdev)
145439b9004dSPhilipp Zabel {
145539b9004dSPhilipp Zabel 	struct ipu_soc *ipu = platform_get_drvdata(pdev);
145639b9004dSPhilipp Zabel 
145739b9004dSPhilipp Zabel 	platform_device_unregister_children(pdev);
145839b9004dSPhilipp Zabel 	ipu_submodules_exit(ipu);
145939b9004dSPhilipp Zabel 	ipu_irq_exit(ipu);
146039b9004dSPhilipp Zabel 
146139b9004dSPhilipp Zabel 	clk_disable_unprepare(ipu->clk);
146239b9004dSPhilipp Zabel 
146339b9004dSPhilipp Zabel 	return 0;
146439b9004dSPhilipp Zabel }
146539b9004dSPhilipp Zabel 
146639b9004dSPhilipp Zabel static struct platform_driver imx_ipu_driver = {
146739b9004dSPhilipp Zabel 	.driver = {
146839b9004dSPhilipp Zabel 		.name = "imx-ipuv3",
146939b9004dSPhilipp Zabel 		.of_match_table = imx_ipu_dt_ids,
147039b9004dSPhilipp Zabel 	},
147139b9004dSPhilipp Zabel 	.probe = ipu_probe,
147239b9004dSPhilipp Zabel 	.remove = ipu_remove,
147339b9004dSPhilipp Zabel };
147439b9004dSPhilipp Zabel 
1475d2a34232SLucas Stach static struct platform_driver * const drivers[] = {
147630310c83SLucas Stach #if IS_ENABLED(CONFIG_DRM)
1477d2a34232SLucas Stach 	&ipu_pre_drv,
1478ea9c2605SLucas Stach 	&ipu_prg_drv,
147930310c83SLucas Stach #endif
1480d2a34232SLucas Stach 	&imx_ipu_driver,
1481d2a34232SLucas Stach };
1482d2a34232SLucas Stach 
imx_ipu_init(void)1483d2a34232SLucas Stach static int __init imx_ipu_init(void)
1484d2a34232SLucas Stach {
1485d2a34232SLucas Stach 	return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
1486d2a34232SLucas Stach }
1487d2a34232SLucas Stach module_init(imx_ipu_init);
1488d2a34232SLucas Stach 
imx_ipu_exit(void)1489d2a34232SLucas Stach static void __exit imx_ipu_exit(void)
1490d2a34232SLucas Stach {
1491d2a34232SLucas Stach 	platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
1492d2a34232SLucas Stach }
1493d2a34232SLucas Stach module_exit(imx_ipu_exit);
149439b9004dSPhilipp Zabel 
149539b9004dSPhilipp Zabel MODULE_ALIAS("platform:imx-ipuv3");
149639b9004dSPhilipp Zabel MODULE_DESCRIPTION("i.MX IPU v3 driver");
149739b9004dSPhilipp Zabel MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
149839b9004dSPhilipp Zabel MODULE_LICENSE("GPL");
1499