xref: /openbmc/linux/drivers/gpu/ipu-v3/ipu-common.c (revision 3feb049f)
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:
779a34cef0SSteve Longerbeam 	case DRM_FORMAT_YUV422:
789a34cef0SSteve Longerbeam 	case DRM_FORMAT_YVU422:
799a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV12:
809a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV21:
819a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV16:
829a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV61:
8339b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_YUV;
8439b9004dSPhilipp Zabel 	default:
8539b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_UNKNOWN;
8639b9004dSPhilipp Zabel 	}
8739b9004dSPhilipp Zabel }
8839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace);
8939b9004dSPhilipp Zabel 
9039b9004dSPhilipp Zabel enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat)
9139b9004dSPhilipp Zabel {
9239b9004dSPhilipp Zabel 	switch (pixelformat) {
9339b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YUV420:
9439b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YVU420:
959a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_YUV422P:
9639b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_UYVY:
9739b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YUYV:
989a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV12:
999a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV21:
1009a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV16:
1019a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV61:
10239b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_YUV;
10339b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB32:
10439b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_BGR32:
10539b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB24:
10639b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_BGR24:
10739b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB565:
10839b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_RGB;
10939b9004dSPhilipp Zabel 	default:
11039b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_UNKNOWN;
11139b9004dSPhilipp Zabel 	}
11239b9004dSPhilipp Zabel }
11339b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace);
11439b9004dSPhilipp Zabel 
1154cea940dSSteve Longerbeam bool ipu_pixelformat_is_planar(u32 pixelformat)
1164cea940dSSteve Longerbeam {
1174cea940dSSteve Longerbeam 	switch (pixelformat) {
1184cea940dSSteve Longerbeam 	case V4L2_PIX_FMT_YUV420:
1194cea940dSSteve Longerbeam 	case V4L2_PIX_FMT_YVU420:
1209a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_YUV422P:
1219a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV12:
1229a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV21:
1239a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV16:
1249a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV61:
1254cea940dSSteve Longerbeam 		return true;
1264cea940dSSteve Longerbeam 	}
1274cea940dSSteve Longerbeam 
1284cea940dSSteve Longerbeam 	return false;
1294cea940dSSteve Longerbeam }
1304cea940dSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar);
1314cea940dSSteve Longerbeam 
132ae0e9708SSteve Longerbeam enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code)
133ae0e9708SSteve Longerbeam {
134ae0e9708SSteve Longerbeam 	switch (mbus_code & 0xf000) {
135ae0e9708SSteve Longerbeam 	case 0x1000:
136ae0e9708SSteve Longerbeam 		return IPUV3_COLORSPACE_RGB;
137ae0e9708SSteve Longerbeam 	case 0x2000:
138ae0e9708SSteve Longerbeam 		return IPUV3_COLORSPACE_YUV;
139ae0e9708SSteve Longerbeam 	default:
140ae0e9708SSteve Longerbeam 		return IPUV3_COLORSPACE_UNKNOWN;
141ae0e9708SSteve Longerbeam 	}
142ae0e9708SSteve Longerbeam }
143ae0e9708SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace);
144ae0e9708SSteve Longerbeam 
1456930afdcSSteve Longerbeam int ipu_stride_to_bytes(u32 pixel_stride, u32 pixelformat)
1466930afdcSSteve Longerbeam {
1476930afdcSSteve Longerbeam 	switch (pixelformat) {
1486930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_YUV420:
1496930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_YVU420:
1509a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_YUV422P:
1519a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV12:
1529a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV21:
1539a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV16:
1549a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV61:
1556930afdcSSteve Longerbeam 		/*
1566930afdcSSteve Longerbeam 		 * for the planar YUV formats, the stride passed to
1576930afdcSSteve Longerbeam 		 * cpmem must be the stride in bytes of the Y plane.
1586930afdcSSteve Longerbeam 		 * And all the planar YUV formats have an 8-bit
1596930afdcSSteve Longerbeam 		 * Y component.
1606930afdcSSteve Longerbeam 		 */
1616930afdcSSteve Longerbeam 		return (8 * pixel_stride) >> 3;
1626930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_RGB565:
1636930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_YUYV:
1646930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_UYVY:
1656930afdcSSteve Longerbeam 		return (16 * pixel_stride) >> 3;
1666930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_BGR24:
1676930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_RGB24:
1686930afdcSSteve Longerbeam 		return (24 * pixel_stride) >> 3;
1696930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_BGR32:
1706930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_RGB32:
1716930afdcSSteve Longerbeam 		return (32 * pixel_stride) >> 3;
1726930afdcSSteve Longerbeam 	default:
1736930afdcSSteve Longerbeam 		break;
1746930afdcSSteve Longerbeam 	}
1756930afdcSSteve Longerbeam 
1766930afdcSSteve Longerbeam 	return -EINVAL;
1776930afdcSSteve Longerbeam }
1786930afdcSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_stride_to_bytes);
1796930afdcSSteve Longerbeam 
180f835f386SSteve Longerbeam int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees,
181f835f386SSteve Longerbeam 			    bool hflip, bool vflip)
182f835f386SSteve Longerbeam {
183f835f386SSteve Longerbeam 	u32 r90, vf, hf;
184f835f386SSteve Longerbeam 
185f835f386SSteve Longerbeam 	switch (degrees) {
186f835f386SSteve Longerbeam 	case 0:
187f835f386SSteve Longerbeam 		vf = hf = r90 = 0;
188f835f386SSteve Longerbeam 		break;
189f835f386SSteve Longerbeam 	case 90:
190f835f386SSteve Longerbeam 		vf = hf = 0;
191f835f386SSteve Longerbeam 		r90 = 1;
192f835f386SSteve Longerbeam 		break;
193f835f386SSteve Longerbeam 	case 180:
194f835f386SSteve Longerbeam 		vf = hf = 1;
195f835f386SSteve Longerbeam 		r90 = 0;
196f835f386SSteve Longerbeam 		break;
197f835f386SSteve Longerbeam 	case 270:
198f835f386SSteve Longerbeam 		vf = hf = r90 = 1;
199f835f386SSteve Longerbeam 		break;
200f835f386SSteve Longerbeam 	default:
201f835f386SSteve Longerbeam 		return -EINVAL;
202f835f386SSteve Longerbeam 	}
203f835f386SSteve Longerbeam 
204f835f386SSteve Longerbeam 	hf ^= (u32)hflip;
205f835f386SSteve Longerbeam 	vf ^= (u32)vflip;
206f835f386SSteve Longerbeam 
207f835f386SSteve Longerbeam 	*mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf);
208f835f386SSteve Longerbeam 	return 0;
209f835f386SSteve Longerbeam }
210f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode);
211f835f386SSteve Longerbeam 
212f835f386SSteve Longerbeam int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode,
213f835f386SSteve Longerbeam 			    bool hflip, bool vflip)
214f835f386SSteve Longerbeam {
215f835f386SSteve Longerbeam 	u32 r90, vf, hf;
216f835f386SSteve Longerbeam 
217f835f386SSteve Longerbeam 	r90 = ((u32)mode >> 2) & 0x1;
218f835f386SSteve Longerbeam 	hf = ((u32)mode >> 1) & 0x1;
219f835f386SSteve Longerbeam 	vf = ((u32)mode >> 0) & 0x1;
220f835f386SSteve Longerbeam 	hf ^= (u32)hflip;
221f835f386SSteve Longerbeam 	vf ^= (u32)vflip;
222f835f386SSteve Longerbeam 
223f835f386SSteve Longerbeam 	switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) {
224f835f386SSteve Longerbeam 	case IPU_ROTATE_NONE:
225f835f386SSteve Longerbeam 		*degrees = 0;
226f835f386SSteve Longerbeam 		break;
227f835f386SSteve Longerbeam 	case IPU_ROTATE_90_RIGHT:
228f835f386SSteve Longerbeam 		*degrees = 90;
229f835f386SSteve Longerbeam 		break;
230f835f386SSteve Longerbeam 	case IPU_ROTATE_180:
231f835f386SSteve Longerbeam 		*degrees = 180;
232f835f386SSteve Longerbeam 		break;
233f835f386SSteve Longerbeam 	case IPU_ROTATE_90_LEFT:
234f835f386SSteve Longerbeam 		*degrees = 270;
235f835f386SSteve Longerbeam 		break;
236f835f386SSteve Longerbeam 	default:
237f835f386SSteve Longerbeam 		return -EINVAL;
238f835f386SSteve Longerbeam 	}
239f835f386SSteve Longerbeam 
240f835f386SSteve Longerbeam 	return 0;
241f835f386SSteve Longerbeam }
242f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees);
243f835f386SSteve Longerbeam 
24439b9004dSPhilipp Zabel struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num)
24539b9004dSPhilipp Zabel {
24639b9004dSPhilipp Zabel 	struct ipuv3_channel *channel;
24739b9004dSPhilipp Zabel 
24839b9004dSPhilipp Zabel 	dev_dbg(ipu->dev, "%s %d\n", __func__, num);
24939b9004dSPhilipp Zabel 
25039b9004dSPhilipp Zabel 	if (num > 63)
25139b9004dSPhilipp Zabel 		return ERR_PTR(-ENODEV);
25239b9004dSPhilipp Zabel 
25339b9004dSPhilipp Zabel 	mutex_lock(&ipu->channel_lock);
25439b9004dSPhilipp Zabel 
25539b9004dSPhilipp Zabel 	channel = &ipu->channel[num];
25639b9004dSPhilipp Zabel 
25739b9004dSPhilipp Zabel 	if (channel->busy) {
25839b9004dSPhilipp Zabel 		channel = ERR_PTR(-EBUSY);
25939b9004dSPhilipp Zabel 		goto out;
26039b9004dSPhilipp Zabel 	}
26139b9004dSPhilipp Zabel 
26239b9004dSPhilipp Zabel 	channel->busy = true;
26339b9004dSPhilipp Zabel 	channel->num = num;
26439b9004dSPhilipp Zabel 
26539b9004dSPhilipp Zabel out:
26639b9004dSPhilipp Zabel 	mutex_unlock(&ipu->channel_lock);
26739b9004dSPhilipp Zabel 
26839b9004dSPhilipp Zabel 	return channel;
26939b9004dSPhilipp Zabel }
27039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get);
27139b9004dSPhilipp Zabel 
27239b9004dSPhilipp Zabel void ipu_idmac_put(struct ipuv3_channel *channel)
27339b9004dSPhilipp Zabel {
27439b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
27539b9004dSPhilipp Zabel 
27639b9004dSPhilipp Zabel 	dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num);
27739b9004dSPhilipp Zabel 
27839b9004dSPhilipp Zabel 	mutex_lock(&ipu->channel_lock);
27939b9004dSPhilipp Zabel 
28039b9004dSPhilipp Zabel 	channel->busy = false;
28139b9004dSPhilipp Zabel 
28239b9004dSPhilipp Zabel 	mutex_unlock(&ipu->channel_lock);
28339b9004dSPhilipp Zabel }
28439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_put);
28539b9004dSPhilipp Zabel 
286aa52f578SSteve Longerbeam #define idma_mask(ch)			(1 << ((ch) & 0x1f))
28739b9004dSPhilipp Zabel 
288e7268c69SSteve Longerbeam /*
289e7268c69SSteve Longerbeam  * This is an undocumented feature, a write one to a channel bit in
290e7268c69SSteve Longerbeam  * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's
291e7268c69SSteve Longerbeam  * internal current buffer pointer so that transfers start from buffer
292e7268c69SSteve Longerbeam  * 0 on the next channel enable (that's the theory anyway, the imx6 TRM
293e7268c69SSteve Longerbeam  * only says these are read-only registers). This operation is required
294e7268c69SSteve Longerbeam  * for channel linking to work correctly, for instance video capture
295e7268c69SSteve Longerbeam  * pipelines that carry out image rotations will fail after the first
296e7268c69SSteve Longerbeam  * streaming unless this function is called for each channel before
297e7268c69SSteve Longerbeam  * re-enabling the channels.
298e7268c69SSteve Longerbeam  */
299e7268c69SSteve Longerbeam static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel)
300e7268c69SSteve Longerbeam {
301e7268c69SSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
302e7268c69SSteve Longerbeam 	unsigned int chno = channel->num;
303e7268c69SSteve Longerbeam 
304e7268c69SSteve Longerbeam 	ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno));
305e7268c69SSteve Longerbeam }
306e7268c69SSteve Longerbeam 
30739b9004dSPhilipp Zabel void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
30839b9004dSPhilipp Zabel 		bool doublebuffer)
30939b9004dSPhilipp Zabel {
31039b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
31139b9004dSPhilipp Zabel 	unsigned long flags;
31239b9004dSPhilipp Zabel 	u32 reg;
31339b9004dSPhilipp Zabel 
31439b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
31539b9004dSPhilipp Zabel 
31639b9004dSPhilipp Zabel 	reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
31739b9004dSPhilipp Zabel 	if (doublebuffer)
31839b9004dSPhilipp Zabel 		reg |= idma_mask(channel->num);
31939b9004dSPhilipp Zabel 	else
32039b9004dSPhilipp Zabel 		reg &= ~idma_mask(channel->num);
32139b9004dSPhilipp Zabel 	ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num));
32239b9004dSPhilipp Zabel 
323e7268c69SSteve Longerbeam 	__ipu_idmac_reset_current_buffer(channel);
324e7268c69SSteve Longerbeam 
32539b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
32639b9004dSPhilipp Zabel }
32739b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer);
32839b9004dSPhilipp Zabel 
3294fd1a07aSSteve Longerbeam static const struct {
3304fd1a07aSSteve Longerbeam 	int chnum;
3314fd1a07aSSteve Longerbeam 	u32 reg;
3324fd1a07aSSteve Longerbeam 	int shift;
3334fd1a07aSSteve Longerbeam } idmac_lock_en_info[] = {
3344fd1a07aSSteve Longerbeam 	{ .chnum =  5, .reg = IDMAC_CH_LOCK_EN_1, .shift =  0, },
3354fd1a07aSSteve Longerbeam 	{ .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift =  2, },
3364fd1a07aSSteve Longerbeam 	{ .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift =  4, },
3374fd1a07aSSteve Longerbeam 	{ .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift =  6, },
3384fd1a07aSSteve Longerbeam 	{ .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift =  8, },
3394fd1a07aSSteve Longerbeam 	{ .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, },
3404fd1a07aSSteve Longerbeam 	{ .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, },
3414fd1a07aSSteve Longerbeam 	{ .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, },
3424fd1a07aSSteve Longerbeam 	{ .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, },
3434fd1a07aSSteve Longerbeam 	{ .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, },
3444fd1a07aSSteve Longerbeam 	{ .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, },
3454fd1a07aSSteve Longerbeam 	{ .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift =  0, },
3464fd1a07aSSteve Longerbeam 	{ .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift =  2, },
3474fd1a07aSSteve Longerbeam 	{ .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift =  4, },
3484fd1a07aSSteve Longerbeam 	{ .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift =  6, },
3494fd1a07aSSteve Longerbeam 	{ .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift =  8, },
3504fd1a07aSSteve Longerbeam 	{ .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, },
3514fd1a07aSSteve Longerbeam };
3524fd1a07aSSteve Longerbeam 
3534fd1a07aSSteve Longerbeam int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts)
3544fd1a07aSSteve Longerbeam {
3554fd1a07aSSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
3564fd1a07aSSteve Longerbeam 	unsigned long flags;
3574fd1a07aSSteve Longerbeam 	u32 bursts, regval;
3584fd1a07aSSteve Longerbeam 	int i;
3594fd1a07aSSteve Longerbeam 
3604fd1a07aSSteve Longerbeam 	switch (num_bursts) {
3614fd1a07aSSteve Longerbeam 	case 0:
3624fd1a07aSSteve Longerbeam 	case 1:
3634fd1a07aSSteve Longerbeam 		bursts = 0x00; /* locking disabled */
3644fd1a07aSSteve Longerbeam 		break;
3654fd1a07aSSteve Longerbeam 	case 2:
3664fd1a07aSSteve Longerbeam 		bursts = 0x01;
3674fd1a07aSSteve Longerbeam 		break;
3684fd1a07aSSteve Longerbeam 	case 4:
3694fd1a07aSSteve Longerbeam 		bursts = 0x02;
3704fd1a07aSSteve Longerbeam 		break;
3714fd1a07aSSteve Longerbeam 	case 8:
3724fd1a07aSSteve Longerbeam 		bursts = 0x03;
3734fd1a07aSSteve Longerbeam 		break;
3744fd1a07aSSteve Longerbeam 	default:
3754fd1a07aSSteve Longerbeam 		return -EINVAL;
3764fd1a07aSSteve Longerbeam 	}
3774fd1a07aSSteve Longerbeam 
3784fd1a07aSSteve Longerbeam 	for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) {
3794fd1a07aSSteve Longerbeam 		if (channel->num == idmac_lock_en_info[i].chnum)
3804fd1a07aSSteve Longerbeam 			break;
3814fd1a07aSSteve Longerbeam 	}
3824fd1a07aSSteve Longerbeam 	if (i >= ARRAY_SIZE(idmac_lock_en_info))
3834fd1a07aSSteve Longerbeam 		return -EINVAL;
3844fd1a07aSSteve Longerbeam 
3854fd1a07aSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
3864fd1a07aSSteve Longerbeam 
3874fd1a07aSSteve Longerbeam 	regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg);
3884fd1a07aSSteve Longerbeam 	regval &= ~(0x03 << idmac_lock_en_info[i].shift);
3894fd1a07aSSteve Longerbeam 	regval |= (bursts << idmac_lock_en_info[i].shift);
3904fd1a07aSSteve Longerbeam 	ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg);
3914fd1a07aSSteve Longerbeam 
3924fd1a07aSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
3934fd1a07aSSteve Longerbeam 
3944fd1a07aSSteve Longerbeam 	return 0;
3954fd1a07aSSteve Longerbeam }
3964fd1a07aSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable);
3974fd1a07aSSteve Longerbeam 
39839b9004dSPhilipp Zabel int ipu_module_enable(struct ipu_soc *ipu, u32 mask)
39939b9004dSPhilipp Zabel {
40039b9004dSPhilipp Zabel 	unsigned long lock_flags;
40139b9004dSPhilipp Zabel 	u32 val;
40239b9004dSPhilipp Zabel 
40339b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, lock_flags);
40439b9004dSPhilipp Zabel 
40539b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_DISP_GEN);
40639b9004dSPhilipp Zabel 
40739b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI0_EN)
40839b9004dSPhilipp Zabel 		val |= IPU_DI0_COUNTER_RELEASE;
40939b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI1_EN)
41039b9004dSPhilipp Zabel 		val |= IPU_DI1_COUNTER_RELEASE;
41139b9004dSPhilipp Zabel 
41239b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_DISP_GEN);
41339b9004dSPhilipp Zabel 
41439b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CONF);
41539b9004dSPhilipp Zabel 	val |= mask;
41639b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CONF);
41739b9004dSPhilipp Zabel 
41839b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, lock_flags);
41939b9004dSPhilipp Zabel 
42039b9004dSPhilipp Zabel 	return 0;
42139b9004dSPhilipp Zabel }
42239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_enable);
42339b9004dSPhilipp Zabel 
42439b9004dSPhilipp Zabel int ipu_module_disable(struct ipu_soc *ipu, u32 mask)
42539b9004dSPhilipp Zabel {
42639b9004dSPhilipp Zabel 	unsigned long lock_flags;
42739b9004dSPhilipp Zabel 	u32 val;
42839b9004dSPhilipp Zabel 
42939b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, lock_flags);
43039b9004dSPhilipp Zabel 
43139b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CONF);
43239b9004dSPhilipp Zabel 	val &= ~mask;
43339b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CONF);
43439b9004dSPhilipp Zabel 
43539b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_DISP_GEN);
43639b9004dSPhilipp Zabel 
43739b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI0_EN)
43839b9004dSPhilipp Zabel 		val &= ~IPU_DI0_COUNTER_RELEASE;
43939b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI1_EN)
44039b9004dSPhilipp Zabel 		val &= ~IPU_DI1_COUNTER_RELEASE;
44139b9004dSPhilipp Zabel 
44239b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_DISP_GEN);
44339b9004dSPhilipp Zabel 
44439b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, lock_flags);
44539b9004dSPhilipp Zabel 
44639b9004dSPhilipp Zabel 	return 0;
44739b9004dSPhilipp Zabel }
44839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_disable);
44939b9004dSPhilipp Zabel 
450e9046097SPhilipp Zabel int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel)
451e9046097SPhilipp Zabel {
452e9046097SPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
453e9046097SPhilipp Zabel 	unsigned int chno = channel->num;
454e9046097SPhilipp Zabel 
455e9046097SPhilipp Zabel 	return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0;
456e9046097SPhilipp Zabel }
457e9046097SPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer);
458e9046097SPhilipp Zabel 
459aa52f578SSteve Longerbeam bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num)
460aa52f578SSteve Longerbeam {
461aa52f578SSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
462aa52f578SSteve Longerbeam 	unsigned long flags;
463aa52f578SSteve Longerbeam 	u32 reg = 0;
464aa52f578SSteve Longerbeam 
465aa52f578SSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
466aa52f578SSteve Longerbeam 	switch (buf_num) {
467aa52f578SSteve Longerbeam 	case 0:
468aa52f578SSteve Longerbeam 		reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num));
469aa52f578SSteve Longerbeam 		break;
470aa52f578SSteve Longerbeam 	case 1:
471aa52f578SSteve Longerbeam 		reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num));
472aa52f578SSteve Longerbeam 		break;
473aa52f578SSteve Longerbeam 	case 2:
474aa52f578SSteve Longerbeam 		reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num));
475aa52f578SSteve Longerbeam 		break;
476aa52f578SSteve Longerbeam 	}
477aa52f578SSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
478aa52f578SSteve Longerbeam 
479aa52f578SSteve Longerbeam 	return ((reg & idma_mask(channel->num)) != 0);
480aa52f578SSteve Longerbeam }
481aa52f578SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready);
482aa52f578SSteve Longerbeam 
48339b9004dSPhilipp Zabel void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num)
48439b9004dSPhilipp Zabel {
48539b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
48639b9004dSPhilipp Zabel 	unsigned int chno = channel->num;
48739b9004dSPhilipp Zabel 	unsigned long flags;
48839b9004dSPhilipp Zabel 
48939b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
49039b9004dSPhilipp Zabel 
49139b9004dSPhilipp Zabel 	/* Mark buffer as ready. */
49239b9004dSPhilipp Zabel 	if (buf_num == 0)
49339b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
49439b9004dSPhilipp Zabel 	else
49539b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
49639b9004dSPhilipp Zabel 
49739b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
49839b9004dSPhilipp Zabel }
49939b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer);
50039b9004dSPhilipp Zabel 
501bce6f087SSteve Longerbeam void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num)
502bce6f087SSteve Longerbeam {
503bce6f087SSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
504bce6f087SSteve Longerbeam 	unsigned int chno = channel->num;
505bce6f087SSteve Longerbeam 	unsigned long flags;
506bce6f087SSteve Longerbeam 
507bce6f087SSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
508bce6f087SSteve Longerbeam 
509bce6f087SSteve Longerbeam 	ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */
510bce6f087SSteve Longerbeam 	switch (buf_num) {
511bce6f087SSteve Longerbeam 	case 0:
512bce6f087SSteve Longerbeam 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
513bce6f087SSteve Longerbeam 		break;
514bce6f087SSteve Longerbeam 	case 1:
515bce6f087SSteve Longerbeam 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
516bce6f087SSteve Longerbeam 		break;
517bce6f087SSteve Longerbeam 	case 2:
518bce6f087SSteve Longerbeam 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno));
519bce6f087SSteve Longerbeam 		break;
520bce6f087SSteve Longerbeam 	default:
521bce6f087SSteve Longerbeam 		break;
522bce6f087SSteve Longerbeam 	}
523bce6f087SSteve Longerbeam 	ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
524bce6f087SSteve Longerbeam 
525bce6f087SSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
526bce6f087SSteve Longerbeam }
527bce6f087SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer);
528bce6f087SSteve Longerbeam 
52939b9004dSPhilipp Zabel int ipu_idmac_enable_channel(struct ipuv3_channel *channel)
53039b9004dSPhilipp Zabel {
53139b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
53239b9004dSPhilipp Zabel 	u32 val;
53339b9004dSPhilipp Zabel 	unsigned long flags;
53439b9004dSPhilipp Zabel 
53539b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
53639b9004dSPhilipp Zabel 
53739b9004dSPhilipp Zabel 	val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
53839b9004dSPhilipp Zabel 	val |= idma_mask(channel->num);
53939b9004dSPhilipp Zabel 	ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
54039b9004dSPhilipp Zabel 
54139b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
54239b9004dSPhilipp Zabel 
54339b9004dSPhilipp Zabel 	return 0;
54439b9004dSPhilipp Zabel }
54539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
54639b9004dSPhilipp Zabel 
547682b7c1cSLinus Torvalds bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno)
548682b7c1cSLinus Torvalds {
549682b7c1cSLinus Torvalds 	return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno));
550682b7c1cSLinus Torvalds }
551682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy);
552682b7c1cSLinus Torvalds 
55339b9004dSPhilipp Zabel int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms)
55439b9004dSPhilipp Zabel {
55539b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
55639b9004dSPhilipp Zabel 	unsigned long timeout;
55739b9004dSPhilipp Zabel 
55839b9004dSPhilipp Zabel 	timeout = jiffies + msecs_to_jiffies(ms);
55939b9004dSPhilipp Zabel 	while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) &
56039b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
56139b9004dSPhilipp Zabel 		if (time_after(jiffies, timeout))
56239b9004dSPhilipp Zabel 			return -ETIMEDOUT;
56339b9004dSPhilipp Zabel 		cpu_relax();
56439b9004dSPhilipp Zabel 	}
56539b9004dSPhilipp Zabel 
56639b9004dSPhilipp Zabel 	return 0;
56739b9004dSPhilipp Zabel }
56839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy);
56939b9004dSPhilipp Zabel 
570682b7c1cSLinus Torvalds int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms)
571682b7c1cSLinus Torvalds {
572682b7c1cSLinus Torvalds 	unsigned long timeout;
573682b7c1cSLinus Torvalds 
574682b7c1cSLinus Torvalds 	timeout = jiffies + msecs_to_jiffies(ms);
575682b7c1cSLinus Torvalds 	ipu_cm_write(ipu, BIT(irq % 32), IPU_INT_STAT(irq / 32));
576682b7c1cSLinus Torvalds 	while (!(ipu_cm_read(ipu, IPU_INT_STAT(irq / 32) & BIT(irq % 32)))) {
577682b7c1cSLinus Torvalds 		if (time_after(jiffies, timeout))
578682b7c1cSLinus Torvalds 			return -ETIMEDOUT;
579682b7c1cSLinus Torvalds 		cpu_relax();
580682b7c1cSLinus Torvalds 	}
581682b7c1cSLinus Torvalds 
582682b7c1cSLinus Torvalds 	return 0;
583682b7c1cSLinus Torvalds }
584682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_wait_interrupt);
585682b7c1cSLinus Torvalds 
58639b9004dSPhilipp Zabel int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
58739b9004dSPhilipp Zabel {
58839b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
58939b9004dSPhilipp Zabel 	u32 val;
59039b9004dSPhilipp Zabel 	unsigned long flags;
59139b9004dSPhilipp Zabel 
59239b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
59339b9004dSPhilipp Zabel 
59439b9004dSPhilipp Zabel 	/* Disable DMA channel(s) */
59539b9004dSPhilipp Zabel 	val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
59639b9004dSPhilipp Zabel 	val &= ~idma_mask(channel->num);
59739b9004dSPhilipp Zabel 	ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
59839b9004dSPhilipp Zabel 
599e7268c69SSteve Longerbeam 	__ipu_idmac_reset_current_buffer(channel);
600e7268c69SSteve Longerbeam 
60139b9004dSPhilipp Zabel 	/* Set channel buffers NOT to be ready */
60239b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */
60339b9004dSPhilipp Zabel 
60439b9004dSPhilipp Zabel 	if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) &
60539b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
60639b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(channel->num),
60739b9004dSPhilipp Zabel 			     IPU_CHA_BUF0_RDY(channel->num));
60839b9004dSPhilipp Zabel 	}
60939b9004dSPhilipp Zabel 
61039b9004dSPhilipp Zabel 	if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) &
61139b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
61239b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(channel->num),
61339b9004dSPhilipp Zabel 			     IPU_CHA_BUF1_RDY(channel->num));
61439b9004dSPhilipp Zabel 	}
61539b9004dSPhilipp Zabel 
61639b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
61739b9004dSPhilipp Zabel 
61839b9004dSPhilipp Zabel 	/* Reset the double buffer */
61939b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
62039b9004dSPhilipp Zabel 	val &= ~idma_mask(channel->num);
62139b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num));
62239b9004dSPhilipp Zabel 
62339b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
62439b9004dSPhilipp Zabel 
62539b9004dSPhilipp Zabel 	return 0;
62639b9004dSPhilipp Zabel }
62739b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel);
62839b9004dSPhilipp Zabel 
6292bcf577eSSteve Longerbeam /*
6302bcf577eSSteve Longerbeam  * The imx6 rev. D TRM says that enabling the WM feature will increase
6312bcf577eSSteve Longerbeam  * a channel's priority. Refer to Table 36-8 Calculated priority value.
6322bcf577eSSteve Longerbeam  * The sub-module that is the sink or source for the channel must enable
6332bcf577eSSteve Longerbeam  * watermark signal for this to take effect (SMFC_WM for instance).
6342bcf577eSSteve Longerbeam  */
6352bcf577eSSteve Longerbeam void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable)
6362bcf577eSSteve Longerbeam {
6372bcf577eSSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
6382bcf577eSSteve Longerbeam 	unsigned long flags;
6392bcf577eSSteve Longerbeam 	u32 val;
6402bcf577eSSteve Longerbeam 
6412bcf577eSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
6422bcf577eSSteve Longerbeam 
6432bcf577eSSteve Longerbeam 	val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num));
6442bcf577eSSteve Longerbeam 	if (enable)
6452bcf577eSSteve Longerbeam 		val |= 1 << (channel->num % 32);
6462bcf577eSSteve Longerbeam 	else
6472bcf577eSSteve Longerbeam 		val &= ~(1 << (channel->num % 32));
6482bcf577eSSteve Longerbeam 	ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num));
6492bcf577eSSteve Longerbeam 
6502bcf577eSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
6512bcf577eSSteve Longerbeam }
6522bcf577eSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark);
6532bcf577eSSteve Longerbeam 
65439b9004dSPhilipp Zabel static int ipu_memory_reset(struct ipu_soc *ipu)
65539b9004dSPhilipp Zabel {
65639b9004dSPhilipp Zabel 	unsigned long timeout;
65739b9004dSPhilipp Zabel 
65839b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST);
65939b9004dSPhilipp Zabel 
66039b9004dSPhilipp Zabel 	timeout = jiffies + msecs_to_jiffies(1000);
66139b9004dSPhilipp Zabel 	while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) {
66239b9004dSPhilipp Zabel 		if (time_after(jiffies, timeout))
66339b9004dSPhilipp Zabel 			return -ETIME;
66439b9004dSPhilipp Zabel 		cpu_relax();
66539b9004dSPhilipp Zabel 	}
66639b9004dSPhilipp Zabel 
66739b9004dSPhilipp Zabel 	return 0;
66839b9004dSPhilipp Zabel }
66939b9004dSPhilipp Zabel 
670ba07975fSSteve Longerbeam /*
671ba07975fSSteve Longerbeam  * Set the source mux for the given CSI. Selects either parallel or
672ba07975fSSteve Longerbeam  * MIPI CSI2 sources.
673ba07975fSSteve Longerbeam  */
674ba07975fSSteve Longerbeam void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2)
675ba07975fSSteve Longerbeam {
676ba07975fSSteve Longerbeam 	unsigned long flags;
677ba07975fSSteve Longerbeam 	u32 val, mask;
678ba07975fSSteve Longerbeam 
679ba07975fSSteve Longerbeam 	mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE :
680ba07975fSSteve Longerbeam 		IPU_CONF_CSI0_DATA_SOURCE;
681ba07975fSSteve Longerbeam 
682ba07975fSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
683ba07975fSSteve Longerbeam 
684ba07975fSSteve Longerbeam 	val = ipu_cm_read(ipu, IPU_CONF);
685ba07975fSSteve Longerbeam 	if (mipi_csi2)
686ba07975fSSteve Longerbeam 		val |= mask;
687ba07975fSSteve Longerbeam 	else
688ba07975fSSteve Longerbeam 		val &= ~mask;
689ba07975fSSteve Longerbeam 	ipu_cm_write(ipu, val, IPU_CONF);
690ba07975fSSteve Longerbeam 
691ba07975fSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
692ba07975fSSteve Longerbeam }
693ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux);
694ba07975fSSteve Longerbeam 
695ba07975fSSteve Longerbeam /*
696ba07975fSSteve Longerbeam  * Set the source mux for the IC. Selects either CSI[01] or the VDI.
697ba07975fSSteve Longerbeam  */
698ba07975fSSteve Longerbeam void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
699ba07975fSSteve Longerbeam {
700ba07975fSSteve Longerbeam 	unsigned long flags;
701ba07975fSSteve Longerbeam 	u32 val;
702ba07975fSSteve Longerbeam 
703ba07975fSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
704ba07975fSSteve Longerbeam 
705ba07975fSSteve Longerbeam 	val = ipu_cm_read(ipu, IPU_CONF);
706ba07975fSSteve Longerbeam 	if (vdi) {
707ba07975fSSteve Longerbeam 		val |= IPU_CONF_IC_INPUT;
708ba07975fSSteve Longerbeam 	} else {
709ba07975fSSteve Longerbeam 		val &= ~IPU_CONF_IC_INPUT;
710ba07975fSSteve Longerbeam 		if (csi_id == 1)
711ba07975fSSteve Longerbeam 			val |= IPU_CONF_CSI_SEL;
712ba07975fSSteve Longerbeam 		else
713ba07975fSSteve Longerbeam 			val &= ~IPU_CONF_CSI_SEL;
714ba07975fSSteve Longerbeam 	}
715ba07975fSSteve Longerbeam 	ipu_cm_write(ipu, val, IPU_CONF);
716ba07975fSSteve Longerbeam 
717ba07975fSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
718ba07975fSSteve Longerbeam }
719ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
720ba07975fSSteve Longerbeam 
72139b9004dSPhilipp Zabel struct ipu_devtype {
72239b9004dSPhilipp Zabel 	const char *name;
72339b9004dSPhilipp Zabel 	unsigned long cm_ofs;
72439b9004dSPhilipp Zabel 	unsigned long cpmem_ofs;
72539b9004dSPhilipp Zabel 	unsigned long srm_ofs;
72639b9004dSPhilipp Zabel 	unsigned long tpm_ofs;
7272ffd48f2SSteve Longerbeam 	unsigned long csi0_ofs;
7282ffd48f2SSteve Longerbeam 	unsigned long csi1_ofs;
7291aa8ea0dSSteve Longerbeam 	unsigned long ic_ofs;
73039b9004dSPhilipp Zabel 	unsigned long disp0_ofs;
73139b9004dSPhilipp Zabel 	unsigned long disp1_ofs;
73239b9004dSPhilipp Zabel 	unsigned long dc_tmpl_ofs;
73339b9004dSPhilipp Zabel 	unsigned long vdi_ofs;
73439b9004dSPhilipp Zabel 	enum ipuv3_type type;
73539b9004dSPhilipp Zabel };
73639b9004dSPhilipp Zabel 
73739b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx51 = {
73839b9004dSPhilipp Zabel 	.name = "IPUv3EX",
73939b9004dSPhilipp Zabel 	.cm_ofs = 0x1e000000,
74039b9004dSPhilipp Zabel 	.cpmem_ofs = 0x1f000000,
74139b9004dSPhilipp Zabel 	.srm_ofs = 0x1f040000,
74239b9004dSPhilipp Zabel 	.tpm_ofs = 0x1f060000,
7432ffd48f2SSteve Longerbeam 	.csi0_ofs = 0x1f030000,
7442ffd48f2SSteve Longerbeam 	.csi1_ofs = 0x1f038000,
7451aa8ea0dSSteve Longerbeam 	.ic_ofs = 0x1f020000,
74639b9004dSPhilipp Zabel 	.disp0_ofs = 0x1e040000,
74739b9004dSPhilipp Zabel 	.disp1_ofs = 0x1e048000,
74839b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x1f080000,
74939b9004dSPhilipp Zabel 	.vdi_ofs = 0x1e068000,
75039b9004dSPhilipp Zabel 	.type = IPUV3EX,
75139b9004dSPhilipp Zabel };
75239b9004dSPhilipp Zabel 
75339b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx53 = {
75439b9004dSPhilipp Zabel 	.name = "IPUv3M",
75539b9004dSPhilipp Zabel 	.cm_ofs = 0x06000000,
75639b9004dSPhilipp Zabel 	.cpmem_ofs = 0x07000000,
75739b9004dSPhilipp Zabel 	.srm_ofs = 0x07040000,
75839b9004dSPhilipp Zabel 	.tpm_ofs = 0x07060000,
7592ffd48f2SSteve Longerbeam 	.csi0_ofs = 0x07030000,
7602ffd48f2SSteve Longerbeam 	.csi1_ofs = 0x07038000,
7611aa8ea0dSSteve Longerbeam 	.ic_ofs = 0x07020000,
76239b9004dSPhilipp Zabel 	.disp0_ofs = 0x06040000,
76339b9004dSPhilipp Zabel 	.disp1_ofs = 0x06048000,
76439b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x07080000,
76539b9004dSPhilipp Zabel 	.vdi_ofs = 0x06068000,
76639b9004dSPhilipp Zabel 	.type = IPUV3M,
76739b9004dSPhilipp Zabel };
76839b9004dSPhilipp Zabel 
76939b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx6q = {
77039b9004dSPhilipp Zabel 	.name = "IPUv3H",
77139b9004dSPhilipp Zabel 	.cm_ofs = 0x00200000,
77239b9004dSPhilipp Zabel 	.cpmem_ofs = 0x00300000,
77339b9004dSPhilipp Zabel 	.srm_ofs = 0x00340000,
77439b9004dSPhilipp Zabel 	.tpm_ofs = 0x00360000,
7752ffd48f2SSteve Longerbeam 	.csi0_ofs = 0x00230000,
7762ffd48f2SSteve Longerbeam 	.csi1_ofs = 0x00238000,
7771aa8ea0dSSteve Longerbeam 	.ic_ofs = 0x00220000,
77839b9004dSPhilipp Zabel 	.disp0_ofs = 0x00240000,
77939b9004dSPhilipp Zabel 	.disp1_ofs = 0x00248000,
78039b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x00380000,
78139b9004dSPhilipp Zabel 	.vdi_ofs = 0x00268000,
78239b9004dSPhilipp Zabel 	.type = IPUV3H,
78339b9004dSPhilipp Zabel };
78439b9004dSPhilipp Zabel 
78539b9004dSPhilipp Zabel static const struct of_device_id imx_ipu_dt_ids[] = {
78639b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, },
78739b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, },
78839b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, },
78939b9004dSPhilipp Zabel 	{ /* sentinel */ }
79039b9004dSPhilipp Zabel };
79139b9004dSPhilipp Zabel MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids);
79239b9004dSPhilipp Zabel 
79339b9004dSPhilipp Zabel static int ipu_submodules_init(struct ipu_soc *ipu,
79439b9004dSPhilipp Zabel 		struct platform_device *pdev, unsigned long ipu_base,
79539b9004dSPhilipp Zabel 		struct clk *ipu_clk)
79639b9004dSPhilipp Zabel {
79739b9004dSPhilipp Zabel 	char *unit;
79839b9004dSPhilipp Zabel 	int ret;
79939b9004dSPhilipp Zabel 	struct device *dev = &pdev->dev;
80039b9004dSPhilipp Zabel 	const struct ipu_devtype *devtype = ipu->devtype;
80139b9004dSPhilipp Zabel 
8027d2691daSSteve Longerbeam 	ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs);
8037d2691daSSteve Longerbeam 	if (ret) {
8047d2691daSSteve Longerbeam 		unit = "cpmem";
8057d2691daSSteve Longerbeam 		goto err_cpmem;
8067d2691daSSteve Longerbeam 	}
8077d2691daSSteve Longerbeam 
8082ffd48f2SSteve Longerbeam 	ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs,
8092ffd48f2SSteve Longerbeam 			   IPU_CONF_CSI0_EN, ipu_clk);
8102ffd48f2SSteve Longerbeam 	if (ret) {
8112ffd48f2SSteve Longerbeam 		unit = "csi0";
8122ffd48f2SSteve Longerbeam 		goto err_csi_0;
8132ffd48f2SSteve Longerbeam 	}
8142ffd48f2SSteve Longerbeam 
8152ffd48f2SSteve Longerbeam 	ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs,
8162ffd48f2SSteve Longerbeam 			   IPU_CONF_CSI1_EN, ipu_clk);
8172ffd48f2SSteve Longerbeam 	if (ret) {
8182ffd48f2SSteve Longerbeam 		unit = "csi1";
8192ffd48f2SSteve Longerbeam 		goto err_csi_1;
8202ffd48f2SSteve Longerbeam 	}
8212ffd48f2SSteve Longerbeam 
8221aa8ea0dSSteve Longerbeam 	ret = ipu_ic_init(ipu, dev,
8231aa8ea0dSSteve Longerbeam 			  ipu_base + devtype->ic_ofs,
8241aa8ea0dSSteve Longerbeam 			  ipu_base + devtype->tpm_ofs);
8251aa8ea0dSSteve Longerbeam 	if (ret) {
8261aa8ea0dSSteve Longerbeam 		unit = "ic";
8271aa8ea0dSSteve Longerbeam 		goto err_ic;
8281aa8ea0dSSteve Longerbeam 	}
8291aa8ea0dSSteve Longerbeam 
83039b9004dSPhilipp Zabel 	ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
83139b9004dSPhilipp Zabel 			  IPU_CONF_DI0_EN, ipu_clk);
83239b9004dSPhilipp Zabel 	if (ret) {
83339b9004dSPhilipp Zabel 		unit = "di0";
83439b9004dSPhilipp Zabel 		goto err_di_0;
83539b9004dSPhilipp Zabel 	}
83639b9004dSPhilipp Zabel 
83739b9004dSPhilipp Zabel 	ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs,
83839b9004dSPhilipp Zabel 			IPU_CONF_DI1_EN, ipu_clk);
83939b9004dSPhilipp Zabel 	if (ret) {
84039b9004dSPhilipp Zabel 		unit = "di1";
84139b9004dSPhilipp Zabel 		goto err_di_1;
84239b9004dSPhilipp Zabel 	}
84339b9004dSPhilipp Zabel 
84439b9004dSPhilipp Zabel 	ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs +
84539b9004dSPhilipp Zabel 			IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs);
84639b9004dSPhilipp Zabel 	if (ret) {
84739b9004dSPhilipp Zabel 		unit = "dc_template";
84839b9004dSPhilipp Zabel 		goto err_dc;
84939b9004dSPhilipp Zabel 	}
85039b9004dSPhilipp Zabel 
85139b9004dSPhilipp Zabel 	ret = ipu_dmfc_init(ipu, dev, ipu_base +
85239b9004dSPhilipp Zabel 			devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk);
85339b9004dSPhilipp Zabel 	if (ret) {
85439b9004dSPhilipp Zabel 		unit = "dmfc";
85539b9004dSPhilipp Zabel 		goto err_dmfc;
85639b9004dSPhilipp Zabel 	}
85739b9004dSPhilipp Zabel 
85839b9004dSPhilipp Zabel 	ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs);
85939b9004dSPhilipp Zabel 	if (ret) {
86039b9004dSPhilipp Zabel 		unit = "dp";
86139b9004dSPhilipp Zabel 		goto err_dp;
86239b9004dSPhilipp Zabel 	}
86339b9004dSPhilipp Zabel 
86435de925fSPhilipp Zabel 	ret = ipu_smfc_init(ipu, dev, ipu_base +
86535de925fSPhilipp Zabel 			devtype->cm_ofs + IPU_CM_SMFC_REG_OFS);
86635de925fSPhilipp Zabel 	if (ret) {
86735de925fSPhilipp Zabel 		unit = "smfc";
86835de925fSPhilipp Zabel 		goto err_smfc;
86935de925fSPhilipp Zabel 	}
87035de925fSPhilipp Zabel 
87139b9004dSPhilipp Zabel 	return 0;
87239b9004dSPhilipp Zabel 
87335de925fSPhilipp Zabel err_smfc:
87435de925fSPhilipp Zabel 	ipu_dp_exit(ipu);
87539b9004dSPhilipp Zabel err_dp:
87639b9004dSPhilipp Zabel 	ipu_dmfc_exit(ipu);
87739b9004dSPhilipp Zabel err_dmfc:
87839b9004dSPhilipp Zabel 	ipu_dc_exit(ipu);
87939b9004dSPhilipp Zabel err_dc:
88039b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 1);
88139b9004dSPhilipp Zabel err_di_1:
88239b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 0);
88339b9004dSPhilipp Zabel err_di_0:
8841aa8ea0dSSteve Longerbeam 	ipu_ic_exit(ipu);
8851aa8ea0dSSteve Longerbeam err_ic:
8862ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 1);
8872ffd48f2SSteve Longerbeam err_csi_1:
8882ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 0);
8892ffd48f2SSteve Longerbeam err_csi_0:
8907d2691daSSteve Longerbeam 	ipu_cpmem_exit(ipu);
8917d2691daSSteve Longerbeam err_cpmem:
89239b9004dSPhilipp Zabel 	dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret);
89339b9004dSPhilipp Zabel 	return ret;
89439b9004dSPhilipp Zabel }
89539b9004dSPhilipp Zabel 
89639b9004dSPhilipp Zabel static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs)
89739b9004dSPhilipp Zabel {
89839b9004dSPhilipp Zabel 	unsigned long status;
89939b9004dSPhilipp Zabel 	int i, bit, irq;
90039b9004dSPhilipp Zabel 
90139b9004dSPhilipp Zabel 	for (i = 0; i < num_regs; i++) {
90239b9004dSPhilipp Zabel 
90339b9004dSPhilipp Zabel 		status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i]));
90439b9004dSPhilipp Zabel 		status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i]));
90539b9004dSPhilipp Zabel 
90639b9004dSPhilipp Zabel 		for_each_set_bit(bit, &status, 32) {
907682b7c1cSLinus Torvalds 			irq = irq_linear_revmap(ipu->domain,
908682b7c1cSLinus Torvalds 						regs[i] * 32 + bit);
90939b9004dSPhilipp Zabel 			if (irq)
91039b9004dSPhilipp Zabel 				generic_handle_irq(irq);
91139b9004dSPhilipp Zabel 		}
91239b9004dSPhilipp Zabel 	}
91339b9004dSPhilipp Zabel }
91439b9004dSPhilipp Zabel 
91539b9004dSPhilipp Zabel static void ipu_irq_handler(unsigned int irq, struct irq_desc *desc)
91639b9004dSPhilipp Zabel {
91739b9004dSPhilipp Zabel 	struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
91839b9004dSPhilipp Zabel 	const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14};
91939b9004dSPhilipp Zabel 	struct irq_chip *chip = irq_get_chip(irq);
92039b9004dSPhilipp Zabel 
92139b9004dSPhilipp Zabel 	chained_irq_enter(chip, desc);
92239b9004dSPhilipp Zabel 
92339b9004dSPhilipp Zabel 	ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
92439b9004dSPhilipp Zabel 
92539b9004dSPhilipp Zabel 	chained_irq_exit(chip, desc);
92639b9004dSPhilipp Zabel }
92739b9004dSPhilipp Zabel 
92839b9004dSPhilipp Zabel static void ipu_err_irq_handler(unsigned int irq, struct irq_desc *desc)
92939b9004dSPhilipp Zabel {
93039b9004dSPhilipp Zabel 	struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
93139b9004dSPhilipp Zabel 	const int int_reg[] = { 4, 5, 8, 9};
93239b9004dSPhilipp Zabel 	struct irq_chip *chip = irq_get_chip(irq);
93339b9004dSPhilipp Zabel 
93439b9004dSPhilipp Zabel 	chained_irq_enter(chip, desc);
93539b9004dSPhilipp Zabel 
93639b9004dSPhilipp Zabel 	ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
93739b9004dSPhilipp Zabel 
93839b9004dSPhilipp Zabel 	chained_irq_exit(chip, desc);
93939b9004dSPhilipp Zabel }
94039b9004dSPhilipp Zabel 
941682b7c1cSLinus Torvalds int ipu_map_irq(struct ipu_soc *ipu, int irq)
942682b7c1cSLinus Torvalds {
943682b7c1cSLinus Torvalds 	int virq;
944682b7c1cSLinus Torvalds 
945682b7c1cSLinus Torvalds 	virq = irq_linear_revmap(ipu->domain, irq);
946682b7c1cSLinus Torvalds 	if (!virq)
947682b7c1cSLinus Torvalds 		virq = irq_create_mapping(ipu->domain, irq);
948682b7c1cSLinus Torvalds 
949682b7c1cSLinus Torvalds 	return virq;
950682b7c1cSLinus Torvalds }
951682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_map_irq);
952682b7c1cSLinus Torvalds 
95339b9004dSPhilipp Zabel int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
95439b9004dSPhilipp Zabel 		enum ipu_channel_irq irq_type)
95539b9004dSPhilipp Zabel {
956682b7c1cSLinus Torvalds 	return ipu_map_irq(ipu, irq_type + channel->num);
95739b9004dSPhilipp Zabel }
95839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq);
95939b9004dSPhilipp Zabel 
96039b9004dSPhilipp Zabel static void ipu_submodules_exit(struct ipu_soc *ipu)
96139b9004dSPhilipp Zabel {
96235de925fSPhilipp Zabel 	ipu_smfc_exit(ipu);
96339b9004dSPhilipp Zabel 	ipu_dp_exit(ipu);
96439b9004dSPhilipp Zabel 	ipu_dmfc_exit(ipu);
96539b9004dSPhilipp Zabel 	ipu_dc_exit(ipu);
96639b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 1);
96739b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 0);
9681aa8ea0dSSteve Longerbeam 	ipu_ic_exit(ipu);
9692ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 1);
9702ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 0);
9717d2691daSSteve Longerbeam 	ipu_cpmem_exit(ipu);
97239b9004dSPhilipp Zabel }
97339b9004dSPhilipp Zabel 
97439b9004dSPhilipp Zabel static int platform_remove_devices_fn(struct device *dev, void *unused)
97539b9004dSPhilipp Zabel {
97639b9004dSPhilipp Zabel 	struct platform_device *pdev = to_platform_device(dev);
97739b9004dSPhilipp Zabel 
97839b9004dSPhilipp Zabel 	platform_device_unregister(pdev);
97939b9004dSPhilipp Zabel 
98039b9004dSPhilipp Zabel 	return 0;
98139b9004dSPhilipp Zabel }
98239b9004dSPhilipp Zabel 
98339b9004dSPhilipp Zabel static void platform_device_unregister_children(struct platform_device *pdev)
98439b9004dSPhilipp Zabel {
98539b9004dSPhilipp Zabel 	device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn);
98639b9004dSPhilipp Zabel }
98739b9004dSPhilipp Zabel 
98839b9004dSPhilipp Zabel struct ipu_platform_reg {
98939b9004dSPhilipp Zabel 	struct ipu_client_platformdata pdata;
99039b9004dSPhilipp Zabel 	const char *name;
991d6ca8ca7SPhilipp Zabel 	int reg_offset;
99239b9004dSPhilipp Zabel };
99339b9004dSPhilipp Zabel 
99439b9004dSPhilipp Zabel static const struct ipu_platform_reg client_reg[] = {
99539b9004dSPhilipp Zabel 	{
99639b9004dSPhilipp Zabel 		.pdata = {
99739b9004dSPhilipp Zabel 			.di = 0,
99839b9004dSPhilipp Zabel 			.dc = 5,
99939b9004dSPhilipp Zabel 			.dp = IPU_DP_FLOW_SYNC_BG,
100039b9004dSPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC,
100139b9004dSPhilipp Zabel 			.dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC,
100239b9004dSPhilipp Zabel 		},
100339b9004dSPhilipp Zabel 		.name = "imx-ipuv3-crtc",
100439b9004dSPhilipp Zabel 	}, {
100539b9004dSPhilipp Zabel 		.pdata = {
100639b9004dSPhilipp Zabel 			.di = 1,
100739b9004dSPhilipp Zabel 			.dc = 1,
100839b9004dSPhilipp Zabel 			.dp = -EINVAL,
100939b9004dSPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC,
101039b9004dSPhilipp Zabel 			.dma[1] = -EINVAL,
101139b9004dSPhilipp Zabel 		},
101239b9004dSPhilipp Zabel 		.name = "imx-ipuv3-crtc",
1013d6ca8ca7SPhilipp Zabel 	}, {
1014d6ca8ca7SPhilipp Zabel 		.pdata = {
1015d6ca8ca7SPhilipp Zabel 			.csi = 0,
1016d6ca8ca7SPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_CSI0,
1017d6ca8ca7SPhilipp Zabel 			.dma[1] = -EINVAL,
1018d6ca8ca7SPhilipp Zabel 		},
1019d6ca8ca7SPhilipp Zabel 		.reg_offset = IPU_CM_CSI0_REG_OFS,
1020d6ca8ca7SPhilipp Zabel 		.name = "imx-ipuv3-camera",
1021d6ca8ca7SPhilipp Zabel 	}, {
1022d6ca8ca7SPhilipp Zabel 		.pdata = {
1023d6ca8ca7SPhilipp Zabel 			.csi = 1,
1024d6ca8ca7SPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_CSI1,
1025d6ca8ca7SPhilipp Zabel 			.dma[1] = -EINVAL,
1026d6ca8ca7SPhilipp Zabel 		},
1027d6ca8ca7SPhilipp Zabel 		.reg_offset = IPU_CM_CSI1_REG_OFS,
1028d6ca8ca7SPhilipp Zabel 		.name = "imx-ipuv3-camera",
102939b9004dSPhilipp Zabel 	},
103039b9004dSPhilipp Zabel };
103139b9004dSPhilipp Zabel 
103239b9004dSPhilipp Zabel static DEFINE_MUTEX(ipu_client_id_mutex);
103339b9004dSPhilipp Zabel static int ipu_client_id;
103439b9004dSPhilipp Zabel 
1035d6ca8ca7SPhilipp Zabel static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base)
103639b9004dSPhilipp Zabel {
103739b9004dSPhilipp Zabel 	struct device *dev = ipu->dev;
103839b9004dSPhilipp Zabel 	unsigned i;
103939b9004dSPhilipp Zabel 	int id, ret;
104039b9004dSPhilipp Zabel 
104139b9004dSPhilipp Zabel 	mutex_lock(&ipu_client_id_mutex);
104239b9004dSPhilipp Zabel 	id = ipu_client_id;
104339b9004dSPhilipp Zabel 	ipu_client_id += ARRAY_SIZE(client_reg);
104439b9004dSPhilipp Zabel 	mutex_unlock(&ipu_client_id_mutex);
104539b9004dSPhilipp Zabel 
104639b9004dSPhilipp Zabel 	for (i = 0; i < ARRAY_SIZE(client_reg); i++) {
104739b9004dSPhilipp Zabel 		const struct ipu_platform_reg *reg = &client_reg[i];
104839b9004dSPhilipp Zabel 		struct platform_device *pdev;
1049d6ca8ca7SPhilipp Zabel 		struct resource res;
105039b9004dSPhilipp Zabel 
1051d6ca8ca7SPhilipp Zabel 		if (reg->reg_offset) {
1052d6ca8ca7SPhilipp Zabel 			memset(&res, 0, sizeof(res));
1053d6ca8ca7SPhilipp Zabel 			res.flags = IORESOURCE_MEM;
1054d6ca8ca7SPhilipp Zabel 			res.start = ipu_base + ipu->devtype->cm_ofs + reg->reg_offset;
1055d6ca8ca7SPhilipp Zabel 			res.end = res.start + PAGE_SIZE - 1;
1056d6ca8ca7SPhilipp Zabel 			pdev = platform_device_register_resndata(dev, reg->name,
1057d6ca8ca7SPhilipp Zabel 				id++, &res, 1, &reg->pdata, sizeof(reg->pdata));
1058d6ca8ca7SPhilipp Zabel 		} else {
105939b9004dSPhilipp Zabel 			pdev = platform_device_register_data(dev, reg->name,
106039b9004dSPhilipp Zabel 				id++, &reg->pdata, sizeof(reg->pdata));
1061d6ca8ca7SPhilipp Zabel 		}
106239b9004dSPhilipp Zabel 
106339b9004dSPhilipp Zabel 		if (IS_ERR(pdev))
106439b9004dSPhilipp Zabel 			goto err_register;
106539b9004dSPhilipp Zabel 	}
106639b9004dSPhilipp Zabel 
106739b9004dSPhilipp Zabel 	return 0;
106839b9004dSPhilipp Zabel 
106939b9004dSPhilipp Zabel err_register:
107039b9004dSPhilipp Zabel 	platform_device_unregister_children(to_platform_device(dev));
107139b9004dSPhilipp Zabel 
107239b9004dSPhilipp Zabel 	return ret;
107339b9004dSPhilipp Zabel }
107439b9004dSPhilipp Zabel 
107539b9004dSPhilipp Zabel 
107639b9004dSPhilipp Zabel static int ipu_irq_init(struct ipu_soc *ipu)
107739b9004dSPhilipp Zabel {
107839b9004dSPhilipp Zabel 	struct irq_chip_generic *gc;
107939b9004dSPhilipp Zabel 	struct irq_chip_type *ct;
108039b9004dSPhilipp Zabel 	unsigned long unused[IPU_NUM_IRQS / 32] = {
108139b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
108239b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
108339b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
108439b9004dSPhilipp Zabel 		0x4077ffff, 0xffe7e1fd,
108539b9004dSPhilipp Zabel 		0x23fffffe, 0x8880fff0,
108639b9004dSPhilipp Zabel 		0xf98fe7d0, 0xfff81fff,
108739b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
108839b9004dSPhilipp Zabel 		0x00000000,
108939b9004dSPhilipp Zabel 	};
109039b9004dSPhilipp Zabel 	int ret, i;
109139b9004dSPhilipp Zabel 
109239b9004dSPhilipp Zabel 	ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS,
109339b9004dSPhilipp Zabel 					    &irq_generic_chip_ops, ipu);
109439b9004dSPhilipp Zabel 	if (!ipu->domain) {
109539b9004dSPhilipp Zabel 		dev_err(ipu->dev, "failed to add irq domain\n");
109639b9004dSPhilipp Zabel 		return -ENODEV;
109739b9004dSPhilipp Zabel 	}
109839b9004dSPhilipp Zabel 
109939b9004dSPhilipp Zabel 	ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU",
1100682b7c1cSLinus Torvalds 					     handle_level_irq, 0,
1101682b7c1cSLinus Torvalds 					     IRQF_VALID, 0);
110239b9004dSPhilipp Zabel 	if (ret < 0) {
110339b9004dSPhilipp Zabel 		dev_err(ipu->dev, "failed to alloc generic irq chips\n");
110439b9004dSPhilipp Zabel 		irq_domain_remove(ipu->domain);
110539b9004dSPhilipp Zabel 		return ret;
110639b9004dSPhilipp Zabel 	}
110739b9004dSPhilipp Zabel 
110839b9004dSPhilipp Zabel 	for (i = 0; i < IPU_NUM_IRQS; i += 32) {
110939b9004dSPhilipp Zabel 		gc = irq_get_domain_generic_chip(ipu->domain, i);
111039b9004dSPhilipp Zabel 		gc->reg_base = ipu->cm_reg;
111139b9004dSPhilipp Zabel 		gc->unused = unused[i / 32];
111239b9004dSPhilipp Zabel 		ct = gc->chip_types;
111339b9004dSPhilipp Zabel 		ct->chip.irq_ack = irq_gc_ack_set_bit;
111439b9004dSPhilipp Zabel 		ct->chip.irq_mask = irq_gc_mask_clr_bit;
111539b9004dSPhilipp Zabel 		ct->chip.irq_unmask = irq_gc_mask_set_bit;
111639b9004dSPhilipp Zabel 		ct->regs.ack = IPU_INT_STAT(i / 32);
111739b9004dSPhilipp Zabel 		ct->regs.mask = IPU_INT_CTRL(i / 32);
111839b9004dSPhilipp Zabel 	}
111939b9004dSPhilipp Zabel 
112039b9004dSPhilipp Zabel 	irq_set_chained_handler(ipu->irq_sync, ipu_irq_handler);
112139b9004dSPhilipp Zabel 	irq_set_handler_data(ipu->irq_sync, ipu);
112239b9004dSPhilipp Zabel 	irq_set_chained_handler(ipu->irq_err, ipu_err_irq_handler);
112339b9004dSPhilipp Zabel 	irq_set_handler_data(ipu->irq_err, ipu);
112439b9004dSPhilipp Zabel 
112539b9004dSPhilipp Zabel 	return 0;
112639b9004dSPhilipp Zabel }
112739b9004dSPhilipp Zabel 
112839b9004dSPhilipp Zabel static void ipu_irq_exit(struct ipu_soc *ipu)
112939b9004dSPhilipp Zabel {
113039b9004dSPhilipp Zabel 	int i, irq;
113139b9004dSPhilipp Zabel 
113239b9004dSPhilipp Zabel 	irq_set_chained_handler(ipu->irq_err, NULL);
113339b9004dSPhilipp Zabel 	irq_set_handler_data(ipu->irq_err, NULL);
113439b9004dSPhilipp Zabel 	irq_set_chained_handler(ipu->irq_sync, NULL);
113539b9004dSPhilipp Zabel 	irq_set_handler_data(ipu->irq_sync, NULL);
113639b9004dSPhilipp Zabel 
113739b9004dSPhilipp Zabel 	/* TODO: remove irq_domain_generic_chips */
113839b9004dSPhilipp Zabel 
113939b9004dSPhilipp Zabel 	for (i = 0; i < IPU_NUM_IRQS; i++) {
114039b9004dSPhilipp Zabel 		irq = irq_linear_revmap(ipu->domain, i);
114139b9004dSPhilipp Zabel 		if (irq)
114239b9004dSPhilipp Zabel 			irq_dispose_mapping(irq);
114339b9004dSPhilipp Zabel 	}
114439b9004dSPhilipp Zabel 
114539b9004dSPhilipp Zabel 	irq_domain_remove(ipu->domain);
114639b9004dSPhilipp Zabel }
114739b9004dSPhilipp Zabel 
11483feb049fSSteve Longerbeam void ipu_dump(struct ipu_soc *ipu)
11493feb049fSSteve Longerbeam {
11503feb049fSSteve Longerbeam 	int i;
11513feb049fSSteve Longerbeam 
11523feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n",
11533feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_CONF));
11543feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n",
11553feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CONF));
11563feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n",
11573feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_EN(0)));
11583feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n",
11593feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_EN(32)));
11603feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n",
11613feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_PRI(0)));
11623feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n",
11633feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_PRI(32)));
11643feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n",
11653feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_BAND_EN(0)));
11663feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n",
11673feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_BAND_EN(32)));
11683feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n",
11693feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0)));
11703feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n",
11713feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32)));
11723feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n",
11733feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_PROC_FLOW1));
11743feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n",
11753feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_PROC_FLOW2));
11763feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n",
11773feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_PROC_FLOW3));
11783feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n",
11793feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_DISP_FLOW1));
11803feb049fSSteve Longerbeam 	for (i = 0; i < 15; i++)
11813feb049fSSteve Longerbeam 		dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i,
11823feb049fSSteve Longerbeam 			ipu_cm_read(ipu, IPU_INT_CTRL(i)));
11833feb049fSSteve Longerbeam }
11843feb049fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_dump);
11853feb049fSSteve Longerbeam 
118639b9004dSPhilipp Zabel static int ipu_probe(struct platform_device *pdev)
118739b9004dSPhilipp Zabel {
118839b9004dSPhilipp Zabel 	const struct of_device_id *of_id =
118939b9004dSPhilipp Zabel 			of_match_device(imx_ipu_dt_ids, &pdev->dev);
119039b9004dSPhilipp Zabel 	struct ipu_soc *ipu;
119139b9004dSPhilipp Zabel 	struct resource *res;
119239b9004dSPhilipp Zabel 	unsigned long ipu_base;
119339b9004dSPhilipp Zabel 	int i, ret, irq_sync, irq_err;
119439b9004dSPhilipp Zabel 	const struct ipu_devtype *devtype;
119539b9004dSPhilipp Zabel 
119639b9004dSPhilipp Zabel 	devtype = of_id->data;
119739b9004dSPhilipp Zabel 
119839b9004dSPhilipp Zabel 	irq_sync = platform_get_irq(pdev, 0);
119939b9004dSPhilipp Zabel 	irq_err = platform_get_irq(pdev, 1);
120039b9004dSPhilipp Zabel 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
120139b9004dSPhilipp Zabel 
120239b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n",
120339b9004dSPhilipp Zabel 			irq_sync, irq_err);
120439b9004dSPhilipp Zabel 
120539b9004dSPhilipp Zabel 	if (!res || irq_sync < 0 || irq_err < 0)
120639b9004dSPhilipp Zabel 		return -ENODEV;
120739b9004dSPhilipp Zabel 
120839b9004dSPhilipp Zabel 	ipu_base = res->start;
120939b9004dSPhilipp Zabel 
121039b9004dSPhilipp Zabel 	ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL);
121139b9004dSPhilipp Zabel 	if (!ipu)
121239b9004dSPhilipp Zabel 		return -ENODEV;
121339b9004dSPhilipp Zabel 
121439b9004dSPhilipp Zabel 	for (i = 0; i < 64; i++)
121539b9004dSPhilipp Zabel 		ipu->channel[i].ipu = ipu;
121639b9004dSPhilipp Zabel 	ipu->devtype = devtype;
121739b9004dSPhilipp Zabel 	ipu->ipu_type = devtype->type;
121839b9004dSPhilipp Zabel 
121939b9004dSPhilipp Zabel 	spin_lock_init(&ipu->lock);
122039b9004dSPhilipp Zabel 	mutex_init(&ipu->channel_lock);
122139b9004dSPhilipp Zabel 
122239b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "cm_reg:   0x%08lx\n",
122339b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs);
122439b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "idmac:    0x%08lx\n",
122539b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS);
122639b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "cpmem:    0x%08lx\n",
122739b9004dSPhilipp Zabel 			ipu_base + devtype->cpmem_ofs);
12282ffd48f2SSteve Longerbeam 	dev_dbg(&pdev->dev, "csi0:    0x%08lx\n",
12292ffd48f2SSteve Longerbeam 			ipu_base + devtype->csi0_ofs);
12302ffd48f2SSteve Longerbeam 	dev_dbg(&pdev->dev, "csi1:    0x%08lx\n",
12312ffd48f2SSteve Longerbeam 			ipu_base + devtype->csi1_ofs);
12321aa8ea0dSSteve Longerbeam 	dev_dbg(&pdev->dev, "ic:      0x%08lx\n",
12331aa8ea0dSSteve Longerbeam 			ipu_base + devtype->ic_ofs);
123439b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "disp0:    0x%08lx\n",
123539b9004dSPhilipp Zabel 			ipu_base + devtype->disp0_ofs);
123639b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "disp1:    0x%08lx\n",
123739b9004dSPhilipp Zabel 			ipu_base + devtype->disp1_ofs);
123839b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "srm:      0x%08lx\n",
123939b9004dSPhilipp Zabel 			ipu_base + devtype->srm_ofs);
124039b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "tpm:      0x%08lx\n",
124139b9004dSPhilipp Zabel 			ipu_base + devtype->tpm_ofs);
124239b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "dc:       0x%08lx\n",
124339b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS);
124439b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "ic:       0x%08lx\n",
124539b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS);
124639b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "dmfc:     0x%08lx\n",
124739b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS);
124839b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "vdi:      0x%08lx\n",
124939b9004dSPhilipp Zabel 			ipu_base + devtype->vdi_ofs);
125039b9004dSPhilipp Zabel 
125139b9004dSPhilipp Zabel 	ipu->cm_reg = devm_ioremap(&pdev->dev,
125239b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs, PAGE_SIZE);
125339b9004dSPhilipp Zabel 	ipu->idmac_reg = devm_ioremap(&pdev->dev,
125439b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS,
125539b9004dSPhilipp Zabel 			PAGE_SIZE);
125639b9004dSPhilipp Zabel 
12577d2691daSSteve Longerbeam 	if (!ipu->cm_reg || !ipu->idmac_reg)
125839b9004dSPhilipp Zabel 		return -ENOMEM;
125939b9004dSPhilipp Zabel 
126039b9004dSPhilipp Zabel 	ipu->clk = devm_clk_get(&pdev->dev, "bus");
126139b9004dSPhilipp Zabel 	if (IS_ERR(ipu->clk)) {
126239b9004dSPhilipp Zabel 		ret = PTR_ERR(ipu->clk);
126339b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "clk_get failed with %d", ret);
126439b9004dSPhilipp Zabel 		return ret;
126539b9004dSPhilipp Zabel 	}
126639b9004dSPhilipp Zabel 
126739b9004dSPhilipp Zabel 	platform_set_drvdata(pdev, ipu);
126839b9004dSPhilipp Zabel 
126939b9004dSPhilipp Zabel 	ret = clk_prepare_enable(ipu->clk);
127039b9004dSPhilipp Zabel 	if (ret) {
127139b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
127239b9004dSPhilipp Zabel 		return ret;
127339b9004dSPhilipp Zabel 	}
127439b9004dSPhilipp Zabel 
127539b9004dSPhilipp Zabel 	ipu->dev = &pdev->dev;
127639b9004dSPhilipp Zabel 	ipu->irq_sync = irq_sync;
127739b9004dSPhilipp Zabel 	ipu->irq_err = irq_err;
127839b9004dSPhilipp Zabel 
127939b9004dSPhilipp Zabel 	ret = ipu_irq_init(ipu);
128039b9004dSPhilipp Zabel 	if (ret)
128139b9004dSPhilipp Zabel 		goto out_failed_irq;
128239b9004dSPhilipp Zabel 
128339b9004dSPhilipp Zabel 	ret = device_reset(&pdev->dev);
128439b9004dSPhilipp Zabel 	if (ret) {
128539b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "failed to reset: %d\n", ret);
128639b9004dSPhilipp Zabel 		goto out_failed_reset;
128739b9004dSPhilipp Zabel 	}
128839b9004dSPhilipp Zabel 	ret = ipu_memory_reset(ipu);
128939b9004dSPhilipp Zabel 	if (ret)
129039b9004dSPhilipp Zabel 		goto out_failed_reset;
129139b9004dSPhilipp Zabel 
129239b9004dSPhilipp Zabel 	/* Set MCU_T to divide MCU access window into 2 */
129339b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
129439b9004dSPhilipp Zabel 			IPU_DISP_GEN);
129539b9004dSPhilipp Zabel 
129639b9004dSPhilipp Zabel 	ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk);
129739b9004dSPhilipp Zabel 	if (ret)
129839b9004dSPhilipp Zabel 		goto failed_submodules_init;
129939b9004dSPhilipp Zabel 
1300d6ca8ca7SPhilipp Zabel 	ret = ipu_add_client_devices(ipu, ipu_base);
130139b9004dSPhilipp Zabel 	if (ret) {
130239b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "adding client devices failed with %d\n",
130339b9004dSPhilipp Zabel 				ret);
130439b9004dSPhilipp Zabel 		goto failed_add_clients;
130539b9004dSPhilipp Zabel 	}
130639b9004dSPhilipp Zabel 
130739b9004dSPhilipp Zabel 	dev_info(&pdev->dev, "%s probed\n", devtype->name);
130839b9004dSPhilipp Zabel 
130939b9004dSPhilipp Zabel 	return 0;
131039b9004dSPhilipp Zabel 
131139b9004dSPhilipp Zabel failed_add_clients:
131239b9004dSPhilipp Zabel 	ipu_submodules_exit(ipu);
131339b9004dSPhilipp Zabel failed_submodules_init:
131439b9004dSPhilipp Zabel out_failed_reset:
131539b9004dSPhilipp Zabel 	ipu_irq_exit(ipu);
131639b9004dSPhilipp Zabel out_failed_irq:
131739b9004dSPhilipp Zabel 	clk_disable_unprepare(ipu->clk);
131839b9004dSPhilipp Zabel 	return ret;
131939b9004dSPhilipp Zabel }
132039b9004dSPhilipp Zabel 
132139b9004dSPhilipp Zabel static int ipu_remove(struct platform_device *pdev)
132239b9004dSPhilipp Zabel {
132339b9004dSPhilipp Zabel 	struct ipu_soc *ipu = platform_get_drvdata(pdev);
132439b9004dSPhilipp Zabel 
132539b9004dSPhilipp Zabel 	platform_device_unregister_children(pdev);
132639b9004dSPhilipp Zabel 	ipu_submodules_exit(ipu);
132739b9004dSPhilipp Zabel 	ipu_irq_exit(ipu);
132839b9004dSPhilipp Zabel 
132939b9004dSPhilipp Zabel 	clk_disable_unprepare(ipu->clk);
133039b9004dSPhilipp Zabel 
133139b9004dSPhilipp Zabel 	return 0;
133239b9004dSPhilipp Zabel }
133339b9004dSPhilipp Zabel 
133439b9004dSPhilipp Zabel static struct platform_driver imx_ipu_driver = {
133539b9004dSPhilipp Zabel 	.driver = {
133639b9004dSPhilipp Zabel 		.name = "imx-ipuv3",
133739b9004dSPhilipp Zabel 		.of_match_table = imx_ipu_dt_ids,
133839b9004dSPhilipp Zabel 	},
133939b9004dSPhilipp Zabel 	.probe = ipu_probe,
134039b9004dSPhilipp Zabel 	.remove = ipu_remove,
134139b9004dSPhilipp Zabel };
134239b9004dSPhilipp Zabel 
134339b9004dSPhilipp Zabel module_platform_driver(imx_ipu_driver);
134439b9004dSPhilipp Zabel 
134539b9004dSPhilipp Zabel MODULE_ALIAS("platform:imx-ipuv3");
134639b9004dSPhilipp Zabel MODULE_DESCRIPTION("i.MX IPU v3 driver");
134739b9004dSPhilipp Zabel MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
134839b9004dSPhilipp Zabel MODULE_LICENSE("GPL");
1349