xref: /openbmc/linux/drivers/gpu/ipu-v3/ipu-common.c (revision a5995717)
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>
2139b9004dSPhilipp Zabel #include <linux/of_device.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 
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 
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 
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 
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 
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 
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:
12739b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_RGB;
12839b9004dSPhilipp Zabel 	default:
12939b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_UNKNOWN;
13039b9004dSPhilipp Zabel 	}
13139b9004dSPhilipp Zabel }
13239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace);
13339b9004dSPhilipp Zabel 
1344cea940dSSteve Longerbeam bool ipu_pixelformat_is_planar(u32 pixelformat)
1354cea940dSSteve Longerbeam {
1364cea940dSSteve Longerbeam 	switch (pixelformat) {
1374cea940dSSteve Longerbeam 	case V4L2_PIX_FMT_YUV420:
1384cea940dSSteve Longerbeam 	case V4L2_PIX_FMT_YVU420:
1399a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_YUV422P:
1409a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV12:
1419a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV21:
1429a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV16:
1439a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV61:
1444cea940dSSteve Longerbeam 		return true;
1454cea940dSSteve Longerbeam 	}
1464cea940dSSteve Longerbeam 
1474cea940dSSteve Longerbeam 	return false;
1484cea940dSSteve Longerbeam }
1494cea940dSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar);
1504cea940dSSteve Longerbeam 
151ae0e9708SSteve Longerbeam enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code)
152ae0e9708SSteve Longerbeam {
153ae0e9708SSteve Longerbeam 	switch (mbus_code & 0xf000) {
154ae0e9708SSteve Longerbeam 	case 0x1000:
155ae0e9708SSteve Longerbeam 		return IPUV3_COLORSPACE_RGB;
156ae0e9708SSteve Longerbeam 	case 0x2000:
157ae0e9708SSteve Longerbeam 		return IPUV3_COLORSPACE_YUV;
158ae0e9708SSteve Longerbeam 	default:
159ae0e9708SSteve Longerbeam 		return IPUV3_COLORSPACE_UNKNOWN;
160ae0e9708SSteve Longerbeam 	}
161ae0e9708SSteve Longerbeam }
162ae0e9708SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace);
163ae0e9708SSteve Longerbeam 
1646930afdcSSteve Longerbeam int ipu_stride_to_bytes(u32 pixel_stride, u32 pixelformat)
1656930afdcSSteve Longerbeam {
1666930afdcSSteve Longerbeam 	switch (pixelformat) {
1676930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_YUV420:
1686930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_YVU420:
1699a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_YUV422P:
1709a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV12:
1719a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV21:
1729a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV16:
1739a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV61:
1746930afdcSSteve Longerbeam 		/*
1756930afdcSSteve Longerbeam 		 * for the planar YUV formats, the stride passed to
1766930afdcSSteve Longerbeam 		 * cpmem must be the stride in bytes of the Y plane.
1776930afdcSSteve Longerbeam 		 * And all the planar YUV formats have an 8-bit
1786930afdcSSteve Longerbeam 		 * Y component.
1796930afdcSSteve Longerbeam 		 */
1806930afdcSSteve Longerbeam 		return (8 * pixel_stride) >> 3;
1816930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_RGB565:
1826930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_YUYV:
1836930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_UYVY:
1846930afdcSSteve Longerbeam 		return (16 * pixel_stride) >> 3;
1856930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_BGR24:
1866930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_RGB24:
1876930afdcSSteve Longerbeam 		return (24 * pixel_stride) >> 3;
1886930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_BGR32:
1896930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_RGB32:
1905c41bb60SPhilipp Zabel 	case V4L2_PIX_FMT_XBGR32:
1915c41bb60SPhilipp Zabel 	case V4L2_PIX_FMT_XRGB32:
1926930afdcSSteve Longerbeam 		return (32 * pixel_stride) >> 3;
1936930afdcSSteve Longerbeam 	default:
1946930afdcSSteve Longerbeam 		break;
1956930afdcSSteve Longerbeam 	}
1966930afdcSSteve Longerbeam 
1976930afdcSSteve Longerbeam 	return -EINVAL;
1986930afdcSSteve Longerbeam }
1996930afdcSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_stride_to_bytes);
2006930afdcSSteve Longerbeam 
201f835f386SSteve Longerbeam int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees,
202f835f386SSteve Longerbeam 			    bool hflip, bool vflip)
203f835f386SSteve Longerbeam {
204f835f386SSteve Longerbeam 	u32 r90, vf, hf;
205f835f386SSteve Longerbeam 
206f835f386SSteve Longerbeam 	switch (degrees) {
207f835f386SSteve Longerbeam 	case 0:
208f835f386SSteve Longerbeam 		vf = hf = r90 = 0;
209f835f386SSteve Longerbeam 		break;
210f835f386SSteve Longerbeam 	case 90:
211f835f386SSteve Longerbeam 		vf = hf = 0;
212f835f386SSteve Longerbeam 		r90 = 1;
213f835f386SSteve Longerbeam 		break;
214f835f386SSteve Longerbeam 	case 180:
215f835f386SSteve Longerbeam 		vf = hf = 1;
216f835f386SSteve Longerbeam 		r90 = 0;
217f835f386SSteve Longerbeam 		break;
218f835f386SSteve Longerbeam 	case 270:
219f835f386SSteve Longerbeam 		vf = hf = r90 = 1;
220f835f386SSteve Longerbeam 		break;
221f835f386SSteve Longerbeam 	default:
222f835f386SSteve Longerbeam 		return -EINVAL;
223f835f386SSteve Longerbeam 	}
224f835f386SSteve Longerbeam 
225f835f386SSteve Longerbeam 	hf ^= (u32)hflip;
226f835f386SSteve Longerbeam 	vf ^= (u32)vflip;
227f835f386SSteve Longerbeam 
228f835f386SSteve Longerbeam 	*mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf);
229f835f386SSteve Longerbeam 	return 0;
230f835f386SSteve Longerbeam }
231f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode);
232f835f386SSteve Longerbeam 
233f835f386SSteve Longerbeam int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode,
234f835f386SSteve Longerbeam 			    bool hflip, bool vflip)
235f835f386SSteve Longerbeam {
236f835f386SSteve Longerbeam 	u32 r90, vf, hf;
237f835f386SSteve Longerbeam 
238f835f386SSteve Longerbeam 	r90 = ((u32)mode >> 2) & 0x1;
239f835f386SSteve Longerbeam 	hf = ((u32)mode >> 1) & 0x1;
240f835f386SSteve Longerbeam 	vf = ((u32)mode >> 0) & 0x1;
241f835f386SSteve Longerbeam 	hf ^= (u32)hflip;
242f835f386SSteve Longerbeam 	vf ^= (u32)vflip;
243f835f386SSteve Longerbeam 
244f835f386SSteve Longerbeam 	switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) {
245f835f386SSteve Longerbeam 	case IPU_ROTATE_NONE:
246f835f386SSteve Longerbeam 		*degrees = 0;
247f835f386SSteve Longerbeam 		break;
248f835f386SSteve Longerbeam 	case IPU_ROTATE_90_RIGHT:
249f835f386SSteve Longerbeam 		*degrees = 90;
250f835f386SSteve Longerbeam 		break;
251f835f386SSteve Longerbeam 	case IPU_ROTATE_180:
252f835f386SSteve Longerbeam 		*degrees = 180;
253f835f386SSteve Longerbeam 		break;
254f835f386SSteve Longerbeam 	case IPU_ROTATE_90_LEFT:
255f835f386SSteve Longerbeam 		*degrees = 270;
256f835f386SSteve Longerbeam 		break;
257f835f386SSteve Longerbeam 	default:
258f835f386SSteve Longerbeam 		return -EINVAL;
259f835f386SSteve Longerbeam 	}
260f835f386SSteve Longerbeam 
261f835f386SSteve Longerbeam 	return 0;
262f835f386SSteve Longerbeam }
263f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees);
264f835f386SSteve Longerbeam 
26539b9004dSPhilipp Zabel struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num)
26639b9004dSPhilipp Zabel {
26739b9004dSPhilipp Zabel 	struct ipuv3_channel *channel;
26839b9004dSPhilipp Zabel 
26939b9004dSPhilipp Zabel 	dev_dbg(ipu->dev, "%s %d\n", __func__, num);
27039b9004dSPhilipp Zabel 
27139b9004dSPhilipp Zabel 	if (num > 63)
27239b9004dSPhilipp Zabel 		return ERR_PTR(-ENODEV);
27339b9004dSPhilipp Zabel 
27439b9004dSPhilipp Zabel 	mutex_lock(&ipu->channel_lock);
27539b9004dSPhilipp Zabel 
27693adc8b5SPhilipp Zabel 	list_for_each_entry(channel, &ipu->channels, list) {
27793adc8b5SPhilipp Zabel 		if (channel->num == num) {
27839b9004dSPhilipp Zabel 			channel = ERR_PTR(-EBUSY);
27939b9004dSPhilipp Zabel 			goto out;
28039b9004dSPhilipp Zabel 		}
28193adc8b5SPhilipp Zabel 	}
28239b9004dSPhilipp Zabel 
28393adc8b5SPhilipp Zabel 	channel = kzalloc(sizeof(*channel), GFP_KERNEL);
28493adc8b5SPhilipp Zabel 	if (!channel) {
28593adc8b5SPhilipp Zabel 		channel = ERR_PTR(-ENOMEM);
28693adc8b5SPhilipp Zabel 		goto out;
28793adc8b5SPhilipp Zabel 	}
28893adc8b5SPhilipp Zabel 
28939b9004dSPhilipp Zabel 	channel->num = num;
29093adc8b5SPhilipp Zabel 	channel->ipu = ipu;
29193adc8b5SPhilipp Zabel 	list_add(&channel->list, &ipu->channels);
29239b9004dSPhilipp Zabel 
29339b9004dSPhilipp Zabel out:
29439b9004dSPhilipp Zabel 	mutex_unlock(&ipu->channel_lock);
29539b9004dSPhilipp Zabel 
29639b9004dSPhilipp Zabel 	return channel;
29739b9004dSPhilipp Zabel }
29839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get);
29939b9004dSPhilipp Zabel 
30039b9004dSPhilipp Zabel void ipu_idmac_put(struct ipuv3_channel *channel)
30139b9004dSPhilipp Zabel {
30239b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
30339b9004dSPhilipp Zabel 
30439b9004dSPhilipp Zabel 	dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num);
30539b9004dSPhilipp Zabel 
30639b9004dSPhilipp Zabel 	mutex_lock(&ipu->channel_lock);
30739b9004dSPhilipp Zabel 
30893adc8b5SPhilipp Zabel 	list_del(&channel->list);
30993adc8b5SPhilipp Zabel 	kfree(channel);
31039b9004dSPhilipp Zabel 
31139b9004dSPhilipp Zabel 	mutex_unlock(&ipu->channel_lock);
31239b9004dSPhilipp Zabel }
31339b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_put);
31439b9004dSPhilipp Zabel 
315aa52f578SSteve Longerbeam #define idma_mask(ch)			(1 << ((ch) & 0x1f))
31639b9004dSPhilipp Zabel 
317e7268c69SSteve Longerbeam /*
318e7268c69SSteve Longerbeam  * This is an undocumented feature, a write one to a channel bit in
319e7268c69SSteve Longerbeam  * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's
320e7268c69SSteve Longerbeam  * internal current buffer pointer so that transfers start from buffer
321e7268c69SSteve Longerbeam  * 0 on the next channel enable (that's the theory anyway, the imx6 TRM
322e7268c69SSteve Longerbeam  * only says these are read-only registers). This operation is required
323e7268c69SSteve Longerbeam  * for channel linking to work correctly, for instance video capture
324e7268c69SSteve Longerbeam  * pipelines that carry out image rotations will fail after the first
325e7268c69SSteve Longerbeam  * streaming unless this function is called for each channel before
326e7268c69SSteve Longerbeam  * re-enabling the channels.
327e7268c69SSteve Longerbeam  */
328e7268c69SSteve Longerbeam static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel)
329e7268c69SSteve Longerbeam {
330e7268c69SSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
331e7268c69SSteve Longerbeam 	unsigned int chno = channel->num;
332e7268c69SSteve Longerbeam 
333e7268c69SSteve Longerbeam 	ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno));
334e7268c69SSteve Longerbeam }
335e7268c69SSteve Longerbeam 
33639b9004dSPhilipp Zabel void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
33739b9004dSPhilipp Zabel 		bool doublebuffer)
33839b9004dSPhilipp Zabel {
33939b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
34039b9004dSPhilipp Zabel 	unsigned long flags;
34139b9004dSPhilipp Zabel 	u32 reg;
34239b9004dSPhilipp Zabel 
34339b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
34439b9004dSPhilipp Zabel 
34539b9004dSPhilipp Zabel 	reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
34639b9004dSPhilipp Zabel 	if (doublebuffer)
34739b9004dSPhilipp Zabel 		reg |= idma_mask(channel->num);
34839b9004dSPhilipp Zabel 	else
34939b9004dSPhilipp Zabel 		reg &= ~idma_mask(channel->num);
35039b9004dSPhilipp Zabel 	ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num));
35139b9004dSPhilipp Zabel 
352e7268c69SSteve Longerbeam 	__ipu_idmac_reset_current_buffer(channel);
353e7268c69SSteve Longerbeam 
35439b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
35539b9004dSPhilipp Zabel }
35639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer);
35739b9004dSPhilipp Zabel 
3584fd1a07aSSteve Longerbeam static const struct {
3594fd1a07aSSteve Longerbeam 	int chnum;
3604fd1a07aSSteve Longerbeam 	u32 reg;
3614fd1a07aSSteve Longerbeam 	int shift;
3624fd1a07aSSteve Longerbeam } idmac_lock_en_info[] = {
3634fd1a07aSSteve Longerbeam 	{ .chnum =  5, .reg = IDMAC_CH_LOCK_EN_1, .shift =  0, },
3644fd1a07aSSteve Longerbeam 	{ .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift =  2, },
3654fd1a07aSSteve Longerbeam 	{ .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift =  4, },
3664fd1a07aSSteve Longerbeam 	{ .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift =  6, },
3674fd1a07aSSteve Longerbeam 	{ .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift =  8, },
3684fd1a07aSSteve Longerbeam 	{ .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, },
3694fd1a07aSSteve Longerbeam 	{ .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, },
3704fd1a07aSSteve Longerbeam 	{ .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, },
3714fd1a07aSSteve Longerbeam 	{ .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, },
3724fd1a07aSSteve Longerbeam 	{ .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, },
3734fd1a07aSSteve Longerbeam 	{ .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, },
3744fd1a07aSSteve Longerbeam 	{ .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift =  0, },
3754fd1a07aSSteve Longerbeam 	{ .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift =  2, },
3764fd1a07aSSteve Longerbeam 	{ .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift =  4, },
3774fd1a07aSSteve Longerbeam 	{ .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift =  6, },
3784fd1a07aSSteve Longerbeam 	{ .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift =  8, },
3794fd1a07aSSteve Longerbeam 	{ .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, },
3804fd1a07aSSteve Longerbeam };
3814fd1a07aSSteve Longerbeam 
3824fd1a07aSSteve Longerbeam int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts)
3834fd1a07aSSteve Longerbeam {
3844fd1a07aSSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
3854fd1a07aSSteve Longerbeam 	unsigned long flags;
3864fd1a07aSSteve Longerbeam 	u32 bursts, regval;
3874fd1a07aSSteve Longerbeam 	int i;
3884fd1a07aSSteve Longerbeam 
3894fd1a07aSSteve Longerbeam 	switch (num_bursts) {
3904fd1a07aSSteve Longerbeam 	case 0:
3914fd1a07aSSteve Longerbeam 	case 1:
3924fd1a07aSSteve Longerbeam 		bursts = 0x00; /* locking disabled */
3934fd1a07aSSteve Longerbeam 		break;
3944fd1a07aSSteve Longerbeam 	case 2:
3954fd1a07aSSteve Longerbeam 		bursts = 0x01;
3964fd1a07aSSteve Longerbeam 		break;
3974fd1a07aSSteve Longerbeam 	case 4:
3984fd1a07aSSteve Longerbeam 		bursts = 0x02;
3994fd1a07aSSteve Longerbeam 		break;
4004fd1a07aSSteve Longerbeam 	case 8:
4014fd1a07aSSteve Longerbeam 		bursts = 0x03;
4024fd1a07aSSteve Longerbeam 		break;
4034fd1a07aSSteve Longerbeam 	default:
4044fd1a07aSSteve Longerbeam 		return -EINVAL;
4054fd1a07aSSteve Longerbeam 	}
4064fd1a07aSSteve Longerbeam 
407cda77556SPhilipp Zabel 	/*
408cda77556SPhilipp Zabel 	 * IPUv3EX / i.MX51 has a different register layout, and on IPUv3M /
409cda77556SPhilipp Zabel 	 * i.MX53 channel arbitration locking doesn't seem to work properly.
410cda77556SPhilipp Zabel 	 * Allow enabling the lock feature on IPUv3H / i.MX6 only.
411cda77556SPhilipp Zabel 	 */
412cda77556SPhilipp Zabel 	if (bursts && ipu->ipu_type != IPUV3H)
413cda77556SPhilipp Zabel 		return -EINVAL;
414cda77556SPhilipp Zabel 
4154fd1a07aSSteve Longerbeam 	for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) {
4164fd1a07aSSteve Longerbeam 		if (channel->num == idmac_lock_en_info[i].chnum)
4174fd1a07aSSteve Longerbeam 			break;
4184fd1a07aSSteve Longerbeam 	}
4194fd1a07aSSteve Longerbeam 	if (i >= ARRAY_SIZE(idmac_lock_en_info))
4204fd1a07aSSteve Longerbeam 		return -EINVAL;
4214fd1a07aSSteve Longerbeam 
4224fd1a07aSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
4234fd1a07aSSteve Longerbeam 
4244fd1a07aSSteve Longerbeam 	regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg);
4254fd1a07aSSteve Longerbeam 	regval &= ~(0x03 << idmac_lock_en_info[i].shift);
4264fd1a07aSSteve Longerbeam 	regval |= (bursts << idmac_lock_en_info[i].shift);
4274fd1a07aSSteve Longerbeam 	ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg);
4284fd1a07aSSteve Longerbeam 
4294fd1a07aSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
4304fd1a07aSSteve Longerbeam 
4314fd1a07aSSteve Longerbeam 	return 0;
4324fd1a07aSSteve Longerbeam }
4334fd1a07aSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable);
4344fd1a07aSSteve Longerbeam 
43539b9004dSPhilipp Zabel int ipu_module_enable(struct ipu_soc *ipu, u32 mask)
43639b9004dSPhilipp Zabel {
43739b9004dSPhilipp Zabel 	unsigned long lock_flags;
43839b9004dSPhilipp Zabel 	u32 val;
43939b9004dSPhilipp Zabel 
44039b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, lock_flags);
44139b9004dSPhilipp Zabel 
44239b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_DISP_GEN);
44339b9004dSPhilipp Zabel 
44439b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI0_EN)
44539b9004dSPhilipp Zabel 		val |= IPU_DI0_COUNTER_RELEASE;
44639b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI1_EN)
44739b9004dSPhilipp Zabel 		val |= IPU_DI1_COUNTER_RELEASE;
44839b9004dSPhilipp Zabel 
44939b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_DISP_GEN);
45039b9004dSPhilipp Zabel 
45139b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CONF);
45239b9004dSPhilipp Zabel 	val |= mask;
45339b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CONF);
45439b9004dSPhilipp Zabel 
45539b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, lock_flags);
45639b9004dSPhilipp Zabel 
45739b9004dSPhilipp Zabel 	return 0;
45839b9004dSPhilipp Zabel }
45939b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_enable);
46039b9004dSPhilipp Zabel 
46139b9004dSPhilipp Zabel int ipu_module_disable(struct ipu_soc *ipu, u32 mask)
46239b9004dSPhilipp Zabel {
46339b9004dSPhilipp Zabel 	unsigned long lock_flags;
46439b9004dSPhilipp Zabel 	u32 val;
46539b9004dSPhilipp Zabel 
46639b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, lock_flags);
46739b9004dSPhilipp Zabel 
46839b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CONF);
46939b9004dSPhilipp Zabel 	val &= ~mask;
47039b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CONF);
47139b9004dSPhilipp Zabel 
47239b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_DISP_GEN);
47339b9004dSPhilipp Zabel 
47439b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI0_EN)
47539b9004dSPhilipp Zabel 		val &= ~IPU_DI0_COUNTER_RELEASE;
47639b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI1_EN)
47739b9004dSPhilipp Zabel 		val &= ~IPU_DI1_COUNTER_RELEASE;
47839b9004dSPhilipp Zabel 
47939b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_DISP_GEN);
48039b9004dSPhilipp Zabel 
48139b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, lock_flags);
48239b9004dSPhilipp Zabel 
48339b9004dSPhilipp Zabel 	return 0;
48439b9004dSPhilipp Zabel }
48539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_disable);
48639b9004dSPhilipp Zabel 
487e9046097SPhilipp Zabel int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel)
488e9046097SPhilipp Zabel {
489e9046097SPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
490e9046097SPhilipp Zabel 	unsigned int chno = channel->num;
491e9046097SPhilipp Zabel 
492e9046097SPhilipp Zabel 	return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0;
493e9046097SPhilipp Zabel }
494e9046097SPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer);
495e9046097SPhilipp Zabel 
496aa52f578SSteve Longerbeam bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num)
497aa52f578SSteve Longerbeam {
498aa52f578SSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
499aa52f578SSteve Longerbeam 	unsigned long flags;
500aa52f578SSteve Longerbeam 	u32 reg = 0;
501aa52f578SSteve Longerbeam 
502aa52f578SSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
503aa52f578SSteve Longerbeam 	switch (buf_num) {
504aa52f578SSteve Longerbeam 	case 0:
505aa52f578SSteve Longerbeam 		reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num));
506aa52f578SSteve Longerbeam 		break;
507aa52f578SSteve Longerbeam 	case 1:
508aa52f578SSteve Longerbeam 		reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num));
509aa52f578SSteve Longerbeam 		break;
510aa52f578SSteve Longerbeam 	case 2:
511aa52f578SSteve Longerbeam 		reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num));
512aa52f578SSteve Longerbeam 		break;
513aa52f578SSteve Longerbeam 	}
514aa52f578SSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
515aa52f578SSteve Longerbeam 
516aa52f578SSteve Longerbeam 	return ((reg & idma_mask(channel->num)) != 0);
517aa52f578SSteve Longerbeam }
518aa52f578SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready);
519aa52f578SSteve Longerbeam 
52039b9004dSPhilipp Zabel void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num)
52139b9004dSPhilipp Zabel {
52239b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
52339b9004dSPhilipp Zabel 	unsigned int chno = channel->num;
52439b9004dSPhilipp Zabel 	unsigned long flags;
52539b9004dSPhilipp Zabel 
52639b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
52739b9004dSPhilipp Zabel 
52839b9004dSPhilipp Zabel 	/* Mark buffer as ready. */
52939b9004dSPhilipp Zabel 	if (buf_num == 0)
53039b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
53139b9004dSPhilipp Zabel 	else
53239b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
53339b9004dSPhilipp Zabel 
53439b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
53539b9004dSPhilipp Zabel }
53639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer);
53739b9004dSPhilipp Zabel 
538bce6f087SSteve Longerbeam void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num)
539bce6f087SSteve Longerbeam {
540bce6f087SSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
541bce6f087SSteve Longerbeam 	unsigned int chno = channel->num;
542bce6f087SSteve Longerbeam 	unsigned long flags;
543bce6f087SSteve Longerbeam 
544bce6f087SSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
545bce6f087SSteve Longerbeam 
546bce6f087SSteve Longerbeam 	ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */
547bce6f087SSteve Longerbeam 	switch (buf_num) {
548bce6f087SSteve Longerbeam 	case 0:
549bce6f087SSteve Longerbeam 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
550bce6f087SSteve Longerbeam 		break;
551bce6f087SSteve Longerbeam 	case 1:
552bce6f087SSteve Longerbeam 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
553bce6f087SSteve Longerbeam 		break;
554bce6f087SSteve Longerbeam 	case 2:
555bce6f087SSteve Longerbeam 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno));
556bce6f087SSteve Longerbeam 		break;
557bce6f087SSteve Longerbeam 	default:
558bce6f087SSteve Longerbeam 		break;
559bce6f087SSteve Longerbeam 	}
560bce6f087SSteve Longerbeam 	ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
561bce6f087SSteve Longerbeam 
562bce6f087SSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
563bce6f087SSteve Longerbeam }
564bce6f087SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer);
565bce6f087SSteve Longerbeam 
56639b9004dSPhilipp Zabel int ipu_idmac_enable_channel(struct ipuv3_channel *channel)
56739b9004dSPhilipp Zabel {
56839b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
56939b9004dSPhilipp Zabel 	u32 val;
57039b9004dSPhilipp Zabel 	unsigned long flags;
57139b9004dSPhilipp Zabel 
57239b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
57339b9004dSPhilipp Zabel 
57439b9004dSPhilipp Zabel 	val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
57539b9004dSPhilipp Zabel 	val |= idma_mask(channel->num);
57639b9004dSPhilipp Zabel 	ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
57739b9004dSPhilipp Zabel 
57839b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
57939b9004dSPhilipp Zabel 
58039b9004dSPhilipp Zabel 	return 0;
58139b9004dSPhilipp Zabel }
58239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
58339b9004dSPhilipp Zabel 
584682b7c1cSLinus Torvalds bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno)
585682b7c1cSLinus Torvalds {
586682b7c1cSLinus Torvalds 	return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno));
587682b7c1cSLinus Torvalds }
588682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy);
589682b7c1cSLinus Torvalds 
59039b9004dSPhilipp Zabel int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms)
59139b9004dSPhilipp Zabel {
59239b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
59339b9004dSPhilipp Zabel 	unsigned long timeout;
59439b9004dSPhilipp Zabel 
59539b9004dSPhilipp Zabel 	timeout = jiffies + msecs_to_jiffies(ms);
59639b9004dSPhilipp Zabel 	while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) &
59739b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
59839b9004dSPhilipp Zabel 		if (time_after(jiffies, timeout))
59939b9004dSPhilipp Zabel 			return -ETIMEDOUT;
60039b9004dSPhilipp Zabel 		cpu_relax();
60139b9004dSPhilipp Zabel 	}
60239b9004dSPhilipp Zabel 
60339b9004dSPhilipp Zabel 	return 0;
60439b9004dSPhilipp Zabel }
60539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy);
60639b9004dSPhilipp Zabel 
60739b9004dSPhilipp Zabel int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
60839b9004dSPhilipp Zabel {
60939b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
61039b9004dSPhilipp Zabel 	u32 val;
61139b9004dSPhilipp Zabel 	unsigned long flags;
61239b9004dSPhilipp Zabel 
61339b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
61439b9004dSPhilipp Zabel 
61539b9004dSPhilipp Zabel 	/* Disable DMA channel(s) */
61639b9004dSPhilipp Zabel 	val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
61739b9004dSPhilipp Zabel 	val &= ~idma_mask(channel->num);
61839b9004dSPhilipp Zabel 	ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
61939b9004dSPhilipp Zabel 
620e7268c69SSteve Longerbeam 	__ipu_idmac_reset_current_buffer(channel);
621e7268c69SSteve Longerbeam 
62239b9004dSPhilipp Zabel 	/* Set channel buffers NOT to be ready */
62339b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */
62439b9004dSPhilipp Zabel 
62539b9004dSPhilipp Zabel 	if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) &
62639b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
62739b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(channel->num),
62839b9004dSPhilipp Zabel 			     IPU_CHA_BUF0_RDY(channel->num));
62939b9004dSPhilipp Zabel 	}
63039b9004dSPhilipp Zabel 
63139b9004dSPhilipp Zabel 	if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) &
63239b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
63339b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(channel->num),
63439b9004dSPhilipp Zabel 			     IPU_CHA_BUF1_RDY(channel->num));
63539b9004dSPhilipp Zabel 	}
63639b9004dSPhilipp Zabel 
63739b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
63839b9004dSPhilipp Zabel 
63939b9004dSPhilipp Zabel 	/* Reset the double buffer */
64039b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
64139b9004dSPhilipp Zabel 	val &= ~idma_mask(channel->num);
64239b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num));
64339b9004dSPhilipp Zabel 
64439b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
64539b9004dSPhilipp Zabel 
64639b9004dSPhilipp Zabel 	return 0;
64739b9004dSPhilipp Zabel }
64839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel);
64939b9004dSPhilipp Zabel 
6502bcf577eSSteve Longerbeam /*
6512bcf577eSSteve Longerbeam  * The imx6 rev. D TRM says that enabling the WM feature will increase
6522bcf577eSSteve Longerbeam  * a channel's priority. Refer to Table 36-8 Calculated priority value.
6532bcf577eSSteve Longerbeam  * The sub-module that is the sink or source for the channel must enable
6542bcf577eSSteve Longerbeam  * watermark signal for this to take effect (SMFC_WM for instance).
6552bcf577eSSteve Longerbeam  */
6562bcf577eSSteve Longerbeam void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable)
6572bcf577eSSteve Longerbeam {
6582bcf577eSSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
6592bcf577eSSteve Longerbeam 	unsigned long flags;
6602bcf577eSSteve Longerbeam 	u32 val;
6612bcf577eSSteve Longerbeam 
6622bcf577eSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
6632bcf577eSSteve Longerbeam 
6642bcf577eSSteve Longerbeam 	val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num));
6652bcf577eSSteve Longerbeam 	if (enable)
6662bcf577eSSteve Longerbeam 		val |= 1 << (channel->num % 32);
6672bcf577eSSteve Longerbeam 	else
6682bcf577eSSteve Longerbeam 		val &= ~(1 << (channel->num % 32));
6692bcf577eSSteve Longerbeam 	ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num));
6702bcf577eSSteve Longerbeam 
6712bcf577eSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
6722bcf577eSSteve Longerbeam }
6732bcf577eSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark);
6742bcf577eSSteve Longerbeam 
67539b9004dSPhilipp Zabel static int ipu_memory_reset(struct ipu_soc *ipu)
67639b9004dSPhilipp Zabel {
67739b9004dSPhilipp Zabel 	unsigned long timeout;
67839b9004dSPhilipp Zabel 
67939b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST);
68039b9004dSPhilipp Zabel 
68139b9004dSPhilipp Zabel 	timeout = jiffies + msecs_to_jiffies(1000);
68239b9004dSPhilipp Zabel 	while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) {
68339b9004dSPhilipp Zabel 		if (time_after(jiffies, timeout))
68439b9004dSPhilipp Zabel 			return -ETIME;
68539b9004dSPhilipp Zabel 		cpu_relax();
68639b9004dSPhilipp Zabel 	}
68739b9004dSPhilipp Zabel 
68839b9004dSPhilipp Zabel 	return 0;
68939b9004dSPhilipp Zabel }
69039b9004dSPhilipp Zabel 
691ba07975fSSteve Longerbeam /*
692ba07975fSSteve Longerbeam  * Set the source mux for the given CSI. Selects either parallel or
693ba07975fSSteve Longerbeam  * MIPI CSI2 sources.
694ba07975fSSteve Longerbeam  */
695ba07975fSSteve Longerbeam void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2)
696ba07975fSSteve Longerbeam {
697ba07975fSSteve Longerbeam 	unsigned long flags;
698ba07975fSSteve Longerbeam 	u32 val, mask;
699ba07975fSSteve Longerbeam 
700ba07975fSSteve Longerbeam 	mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE :
701ba07975fSSteve Longerbeam 		IPU_CONF_CSI0_DATA_SOURCE;
702ba07975fSSteve Longerbeam 
703ba07975fSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
704ba07975fSSteve Longerbeam 
705ba07975fSSteve Longerbeam 	val = ipu_cm_read(ipu, IPU_CONF);
706ba07975fSSteve Longerbeam 	if (mipi_csi2)
707ba07975fSSteve Longerbeam 		val |= mask;
708ba07975fSSteve Longerbeam 	else
709ba07975fSSteve Longerbeam 		val &= ~mask;
710ba07975fSSteve Longerbeam 	ipu_cm_write(ipu, val, IPU_CONF);
711ba07975fSSteve Longerbeam 
712ba07975fSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
713ba07975fSSteve Longerbeam }
714ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux);
715ba07975fSSteve Longerbeam 
716ba07975fSSteve Longerbeam /*
717ba07975fSSteve Longerbeam  * Set the source mux for the IC. Selects either CSI[01] or the VDI.
718ba07975fSSteve Longerbeam  */
719ba07975fSSteve Longerbeam void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
720ba07975fSSteve Longerbeam {
721ba07975fSSteve Longerbeam 	unsigned long flags;
722ba07975fSSteve Longerbeam 	u32 val;
723ba07975fSSteve Longerbeam 
724ba07975fSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
725ba07975fSSteve Longerbeam 
726ba07975fSSteve Longerbeam 	val = ipu_cm_read(ipu, IPU_CONF);
727b7dfee24SMarek Vasut 	if (vdi)
728ba07975fSSteve Longerbeam 		val |= IPU_CONF_IC_INPUT;
729b7dfee24SMarek Vasut 	else
730ba07975fSSteve Longerbeam 		val &= ~IPU_CONF_IC_INPUT;
731b7dfee24SMarek Vasut 
732ba07975fSSteve Longerbeam 	if (csi_id == 1)
733ba07975fSSteve Longerbeam 		val |= IPU_CONF_CSI_SEL;
734ba07975fSSteve Longerbeam 	else
735ba07975fSSteve Longerbeam 		val &= ~IPU_CONF_CSI_SEL;
736b7dfee24SMarek Vasut 
737ba07975fSSteve Longerbeam 	ipu_cm_write(ipu, val, IPU_CONF);
738ba07975fSSteve Longerbeam 
739ba07975fSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
740ba07975fSSteve Longerbeam }
741ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
742ba07975fSSteve Longerbeam 
743ac4708faSSteve Longerbeam 
744ac4708faSSteve Longerbeam /* Frame Synchronization Unit Channel Linking */
745ac4708faSSteve Longerbeam 
746ac4708faSSteve Longerbeam struct fsu_link_reg_info {
747ac4708faSSteve Longerbeam 	int chno;
748ac4708faSSteve Longerbeam 	u32 reg;
749ac4708faSSteve Longerbeam 	u32 mask;
750ac4708faSSteve Longerbeam 	u32 val;
751ac4708faSSteve Longerbeam };
752ac4708faSSteve Longerbeam 
753ac4708faSSteve Longerbeam struct fsu_link_info {
754ac4708faSSteve Longerbeam 	struct fsu_link_reg_info src;
755ac4708faSSteve Longerbeam 	struct fsu_link_reg_info sink;
756ac4708faSSteve Longerbeam };
757ac4708faSSteve Longerbeam 
758ac4708faSSteve Longerbeam static const struct fsu_link_info fsu_link_info[] = {
759ac4708faSSteve Longerbeam 	{
760ac4708faSSteve Longerbeam 		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2,
761ac4708faSSteve Longerbeam 			  FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC },
762ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1,
763ac4708faSSteve Longerbeam 			  FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC },
764ac4708faSSteve Longerbeam 	}, {
765ac4708faSSteve Longerbeam 		.src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2,
766ac4708faSSteve Longerbeam 			  FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF },
767ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1,
768ac4708faSSteve Longerbeam 			  FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF },
769ac4708faSSteve Longerbeam 	}, {
770ac4708faSSteve Longerbeam 		.src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2,
771ac4708faSSteve Longerbeam 			  FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP },
772ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1,
773ac4708faSSteve Longerbeam 			  FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP },
774ac4708faSSteve Longerbeam 	}, {
775ac4708faSSteve Longerbeam 		.src =  { IPUV3_CHANNEL_CSI_DIRECT, 0 },
776ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1,
777ac4708faSSteve Longerbeam 			  FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT },
778ac4708faSSteve Longerbeam 	},
779ac4708faSSteve Longerbeam };
780ac4708faSSteve Longerbeam 
781ac4708faSSteve Longerbeam static const struct fsu_link_info *find_fsu_link_info(int src, int sink)
782ac4708faSSteve Longerbeam {
783ac4708faSSteve Longerbeam 	int i;
784ac4708faSSteve Longerbeam 
785ac4708faSSteve Longerbeam 	for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) {
786ac4708faSSteve Longerbeam 		if (src == fsu_link_info[i].src.chno &&
787ac4708faSSteve Longerbeam 		    sink == fsu_link_info[i].sink.chno)
788ac4708faSSteve Longerbeam 			return &fsu_link_info[i];
789ac4708faSSteve Longerbeam 	}
790ac4708faSSteve Longerbeam 
791ac4708faSSteve Longerbeam 	return NULL;
792ac4708faSSteve Longerbeam }
793ac4708faSSteve Longerbeam 
794ac4708faSSteve Longerbeam /*
795ac4708faSSteve Longerbeam  * Links a source channel to a sink channel in the FSU.
796ac4708faSSteve Longerbeam  */
797ac4708faSSteve Longerbeam int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch)
798ac4708faSSteve Longerbeam {
799ac4708faSSteve Longerbeam 	const struct fsu_link_info *link;
800ac4708faSSteve Longerbeam 	u32 src_reg, sink_reg;
801ac4708faSSteve Longerbeam 	unsigned long flags;
802ac4708faSSteve Longerbeam 
803ac4708faSSteve Longerbeam 	link = find_fsu_link_info(src_ch, sink_ch);
804ac4708faSSteve Longerbeam 	if (!link)
805ac4708faSSteve Longerbeam 		return -EINVAL;
806ac4708faSSteve Longerbeam 
807ac4708faSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
808ac4708faSSteve Longerbeam 
809ac4708faSSteve Longerbeam 	if (link->src.mask) {
810ac4708faSSteve Longerbeam 		src_reg = ipu_cm_read(ipu, link->src.reg);
811ac4708faSSteve Longerbeam 		src_reg &= ~link->src.mask;
812ac4708faSSteve Longerbeam 		src_reg |= link->src.val;
813ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, src_reg, link->src.reg);
814ac4708faSSteve Longerbeam 	}
815ac4708faSSteve Longerbeam 
816ac4708faSSteve Longerbeam 	if (link->sink.mask) {
817ac4708faSSteve Longerbeam 		sink_reg = ipu_cm_read(ipu, link->sink.reg);
818ac4708faSSteve Longerbeam 		sink_reg &= ~link->sink.mask;
819ac4708faSSteve Longerbeam 		sink_reg |= link->sink.val;
820ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, sink_reg, link->sink.reg);
821ac4708faSSteve Longerbeam 	}
822ac4708faSSteve Longerbeam 
823ac4708faSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
824ac4708faSSteve Longerbeam 	return 0;
825ac4708faSSteve Longerbeam }
826ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_link);
827ac4708faSSteve Longerbeam 
828ac4708faSSteve Longerbeam /*
829ac4708faSSteve Longerbeam  * Unlinks source and sink channels in the FSU.
830ac4708faSSteve Longerbeam  */
831ac4708faSSteve Longerbeam int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch)
832ac4708faSSteve Longerbeam {
833ac4708faSSteve Longerbeam 	const struct fsu_link_info *link;
834ac4708faSSteve Longerbeam 	u32 src_reg, sink_reg;
835ac4708faSSteve Longerbeam 	unsigned long flags;
836ac4708faSSteve Longerbeam 
837ac4708faSSteve Longerbeam 	link = find_fsu_link_info(src_ch, sink_ch);
838ac4708faSSteve Longerbeam 	if (!link)
839ac4708faSSteve Longerbeam 		return -EINVAL;
840ac4708faSSteve Longerbeam 
841ac4708faSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
842ac4708faSSteve Longerbeam 
843ac4708faSSteve Longerbeam 	if (link->src.mask) {
844ac4708faSSteve Longerbeam 		src_reg = ipu_cm_read(ipu, link->src.reg);
845ac4708faSSteve Longerbeam 		src_reg &= ~link->src.mask;
846ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, src_reg, link->src.reg);
847ac4708faSSteve Longerbeam 	}
848ac4708faSSteve Longerbeam 
849ac4708faSSteve Longerbeam 	if (link->sink.mask) {
850ac4708faSSteve Longerbeam 		sink_reg = ipu_cm_read(ipu, link->sink.reg);
851ac4708faSSteve Longerbeam 		sink_reg &= ~link->sink.mask;
852ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, sink_reg, link->sink.reg);
853ac4708faSSteve Longerbeam 	}
854ac4708faSSteve Longerbeam 
855ac4708faSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
856ac4708faSSteve Longerbeam 	return 0;
857ac4708faSSteve Longerbeam }
858ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_unlink);
859ac4708faSSteve Longerbeam 
860ac4708faSSteve Longerbeam /* Link IDMAC channels in the FSU */
861ac4708faSSteve Longerbeam int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)
862ac4708faSSteve Longerbeam {
863ac4708faSSteve Longerbeam 	return ipu_fsu_link(src->ipu, src->num, sink->num);
864ac4708faSSteve Longerbeam }
865ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_link);
866ac4708faSSteve Longerbeam 
867ac4708faSSteve Longerbeam /* Unlink IDMAC channels in the FSU */
868ac4708faSSteve Longerbeam int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink)
869ac4708faSSteve Longerbeam {
870ac4708faSSteve Longerbeam 	return ipu_fsu_unlink(src->ipu, src->num, sink->num);
871ac4708faSSteve Longerbeam }
872ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_unlink);
873ac4708faSSteve Longerbeam 
87439b9004dSPhilipp Zabel struct ipu_devtype {
87539b9004dSPhilipp Zabel 	const char *name;
87639b9004dSPhilipp Zabel 	unsigned long cm_ofs;
87739b9004dSPhilipp Zabel 	unsigned long cpmem_ofs;
87839b9004dSPhilipp Zabel 	unsigned long srm_ofs;
87939b9004dSPhilipp Zabel 	unsigned long tpm_ofs;
8802ffd48f2SSteve Longerbeam 	unsigned long csi0_ofs;
8812ffd48f2SSteve Longerbeam 	unsigned long csi1_ofs;
8821aa8ea0dSSteve Longerbeam 	unsigned long ic_ofs;
88339b9004dSPhilipp Zabel 	unsigned long disp0_ofs;
88439b9004dSPhilipp Zabel 	unsigned long disp1_ofs;
88539b9004dSPhilipp Zabel 	unsigned long dc_tmpl_ofs;
88639b9004dSPhilipp Zabel 	unsigned long vdi_ofs;
88739b9004dSPhilipp Zabel 	enum ipuv3_type type;
88839b9004dSPhilipp Zabel };
88939b9004dSPhilipp Zabel 
89039b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx51 = {
89139b9004dSPhilipp Zabel 	.name = "IPUv3EX",
89239b9004dSPhilipp Zabel 	.cm_ofs = 0x1e000000,
89339b9004dSPhilipp Zabel 	.cpmem_ofs = 0x1f000000,
89439b9004dSPhilipp Zabel 	.srm_ofs = 0x1f040000,
89539b9004dSPhilipp Zabel 	.tpm_ofs = 0x1f060000,
8962c0408ddSAlexander Shiyan 	.csi0_ofs = 0x1e030000,
8972c0408ddSAlexander Shiyan 	.csi1_ofs = 0x1e038000,
898a49e7c0dSPhilipp Zabel 	.ic_ofs = 0x1e020000,
89939b9004dSPhilipp Zabel 	.disp0_ofs = 0x1e040000,
90039b9004dSPhilipp Zabel 	.disp1_ofs = 0x1e048000,
90139b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x1f080000,
90239b9004dSPhilipp Zabel 	.vdi_ofs = 0x1e068000,
90339b9004dSPhilipp Zabel 	.type = IPUV3EX,
90439b9004dSPhilipp Zabel };
90539b9004dSPhilipp Zabel 
90639b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx53 = {
90739b9004dSPhilipp Zabel 	.name = "IPUv3M",
90839b9004dSPhilipp Zabel 	.cm_ofs = 0x06000000,
90939b9004dSPhilipp Zabel 	.cpmem_ofs = 0x07000000,
91039b9004dSPhilipp Zabel 	.srm_ofs = 0x07040000,
91139b9004dSPhilipp Zabel 	.tpm_ofs = 0x07060000,
912bb867d21SSteve Longerbeam 	.csi0_ofs = 0x06030000,
913bb867d21SSteve Longerbeam 	.csi1_ofs = 0x06038000,
914a49e7c0dSPhilipp Zabel 	.ic_ofs = 0x06020000,
91539b9004dSPhilipp Zabel 	.disp0_ofs = 0x06040000,
91639b9004dSPhilipp Zabel 	.disp1_ofs = 0x06048000,
91739b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x07080000,
91839b9004dSPhilipp Zabel 	.vdi_ofs = 0x06068000,
91939b9004dSPhilipp Zabel 	.type = IPUV3M,
92039b9004dSPhilipp Zabel };
92139b9004dSPhilipp Zabel 
92239b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx6q = {
92339b9004dSPhilipp Zabel 	.name = "IPUv3H",
92439b9004dSPhilipp Zabel 	.cm_ofs = 0x00200000,
92539b9004dSPhilipp Zabel 	.cpmem_ofs = 0x00300000,
92639b9004dSPhilipp Zabel 	.srm_ofs = 0x00340000,
92739b9004dSPhilipp Zabel 	.tpm_ofs = 0x00360000,
9282ffd48f2SSteve Longerbeam 	.csi0_ofs = 0x00230000,
9292ffd48f2SSteve Longerbeam 	.csi1_ofs = 0x00238000,
9301aa8ea0dSSteve Longerbeam 	.ic_ofs = 0x00220000,
93139b9004dSPhilipp Zabel 	.disp0_ofs = 0x00240000,
93239b9004dSPhilipp Zabel 	.disp1_ofs = 0x00248000,
93339b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x00380000,
93439b9004dSPhilipp Zabel 	.vdi_ofs = 0x00268000,
93539b9004dSPhilipp Zabel 	.type = IPUV3H,
93639b9004dSPhilipp Zabel };
93739b9004dSPhilipp Zabel 
93839b9004dSPhilipp Zabel static const struct of_device_id imx_ipu_dt_ids[] = {
93939b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, },
94039b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, },
94139b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, },
94292681fe7SLucas Stach 	{ .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6q, },
94339b9004dSPhilipp Zabel 	{ /* sentinel */ }
94439b9004dSPhilipp Zabel };
94539b9004dSPhilipp Zabel MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids);
94639b9004dSPhilipp Zabel 
94739b9004dSPhilipp Zabel static int ipu_submodules_init(struct ipu_soc *ipu,
94839b9004dSPhilipp Zabel 		struct platform_device *pdev, unsigned long ipu_base,
94939b9004dSPhilipp Zabel 		struct clk *ipu_clk)
95039b9004dSPhilipp Zabel {
95139b9004dSPhilipp Zabel 	char *unit;
95239b9004dSPhilipp Zabel 	int ret;
95339b9004dSPhilipp Zabel 	struct device *dev = &pdev->dev;
95439b9004dSPhilipp Zabel 	const struct ipu_devtype *devtype = ipu->devtype;
95539b9004dSPhilipp Zabel 
9567d2691daSSteve Longerbeam 	ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs);
9577d2691daSSteve Longerbeam 	if (ret) {
9587d2691daSSteve Longerbeam 		unit = "cpmem";
9597d2691daSSteve Longerbeam 		goto err_cpmem;
9607d2691daSSteve Longerbeam 	}
9617d2691daSSteve Longerbeam 
9622ffd48f2SSteve Longerbeam 	ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs,
9632ffd48f2SSteve Longerbeam 			   IPU_CONF_CSI0_EN, ipu_clk);
9642ffd48f2SSteve Longerbeam 	if (ret) {
9652ffd48f2SSteve Longerbeam 		unit = "csi0";
9662ffd48f2SSteve Longerbeam 		goto err_csi_0;
9672ffd48f2SSteve Longerbeam 	}
9682ffd48f2SSteve Longerbeam 
9692ffd48f2SSteve Longerbeam 	ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs,
9702ffd48f2SSteve Longerbeam 			   IPU_CONF_CSI1_EN, ipu_clk);
9712ffd48f2SSteve Longerbeam 	if (ret) {
9722ffd48f2SSteve Longerbeam 		unit = "csi1";
9732ffd48f2SSteve Longerbeam 		goto err_csi_1;
9742ffd48f2SSteve Longerbeam 	}
9752ffd48f2SSteve Longerbeam 
9761aa8ea0dSSteve Longerbeam 	ret = ipu_ic_init(ipu, dev,
9771aa8ea0dSSteve Longerbeam 			  ipu_base + devtype->ic_ofs,
9781aa8ea0dSSteve Longerbeam 			  ipu_base + devtype->tpm_ofs);
9791aa8ea0dSSteve Longerbeam 	if (ret) {
9801aa8ea0dSSteve Longerbeam 		unit = "ic";
9811aa8ea0dSSteve Longerbeam 		goto err_ic;
9821aa8ea0dSSteve Longerbeam 	}
9831aa8ea0dSSteve Longerbeam 
9842d2ead45SSteve Longerbeam 	ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs,
9852d2ead45SSteve Longerbeam 			   IPU_CONF_VDI_EN | IPU_CONF_ISP_EN |
9862d2ead45SSteve Longerbeam 			   IPU_CONF_IC_INPUT);
9872d2ead45SSteve Longerbeam 	if (ret) {
9882d2ead45SSteve Longerbeam 		unit = "vdi";
9892d2ead45SSteve Longerbeam 		goto err_vdi;
9902d2ead45SSteve Longerbeam 	}
9912d2ead45SSteve Longerbeam 
992cd98e85aSSteve Longerbeam 	ret = ipu_image_convert_init(ipu, dev);
993cd98e85aSSteve Longerbeam 	if (ret) {
994cd98e85aSSteve Longerbeam 		unit = "image_convert";
995cd98e85aSSteve Longerbeam 		goto err_image_convert;
996cd98e85aSSteve Longerbeam 	}
997cd98e85aSSteve Longerbeam 
99839b9004dSPhilipp Zabel 	ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
99939b9004dSPhilipp Zabel 			  IPU_CONF_DI0_EN, ipu_clk);
100039b9004dSPhilipp Zabel 	if (ret) {
100139b9004dSPhilipp Zabel 		unit = "di0";
100239b9004dSPhilipp Zabel 		goto err_di_0;
100339b9004dSPhilipp Zabel 	}
100439b9004dSPhilipp Zabel 
100539b9004dSPhilipp Zabel 	ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs,
100639b9004dSPhilipp Zabel 			IPU_CONF_DI1_EN, ipu_clk);
100739b9004dSPhilipp Zabel 	if (ret) {
100839b9004dSPhilipp Zabel 		unit = "di1";
100939b9004dSPhilipp Zabel 		goto err_di_1;
101039b9004dSPhilipp Zabel 	}
101139b9004dSPhilipp Zabel 
101239b9004dSPhilipp Zabel 	ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs +
101339b9004dSPhilipp Zabel 			IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs);
101439b9004dSPhilipp Zabel 	if (ret) {
101539b9004dSPhilipp Zabel 		unit = "dc_template";
101639b9004dSPhilipp Zabel 		goto err_dc;
101739b9004dSPhilipp Zabel 	}
101839b9004dSPhilipp Zabel 
101939b9004dSPhilipp Zabel 	ret = ipu_dmfc_init(ipu, dev, ipu_base +
102039b9004dSPhilipp Zabel 			devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk);
102139b9004dSPhilipp Zabel 	if (ret) {
102239b9004dSPhilipp Zabel 		unit = "dmfc";
102339b9004dSPhilipp Zabel 		goto err_dmfc;
102439b9004dSPhilipp Zabel 	}
102539b9004dSPhilipp Zabel 
102639b9004dSPhilipp Zabel 	ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs);
102739b9004dSPhilipp Zabel 	if (ret) {
102839b9004dSPhilipp Zabel 		unit = "dp";
102939b9004dSPhilipp Zabel 		goto err_dp;
103039b9004dSPhilipp Zabel 	}
103139b9004dSPhilipp Zabel 
103235de925fSPhilipp Zabel 	ret = ipu_smfc_init(ipu, dev, ipu_base +
103335de925fSPhilipp Zabel 			devtype->cm_ofs + IPU_CM_SMFC_REG_OFS);
103435de925fSPhilipp Zabel 	if (ret) {
103535de925fSPhilipp Zabel 		unit = "smfc";
103635de925fSPhilipp Zabel 		goto err_smfc;
103735de925fSPhilipp Zabel 	}
103835de925fSPhilipp Zabel 
103939b9004dSPhilipp Zabel 	return 0;
104039b9004dSPhilipp Zabel 
104135de925fSPhilipp Zabel err_smfc:
104235de925fSPhilipp Zabel 	ipu_dp_exit(ipu);
104339b9004dSPhilipp Zabel err_dp:
104439b9004dSPhilipp Zabel 	ipu_dmfc_exit(ipu);
104539b9004dSPhilipp Zabel err_dmfc:
104639b9004dSPhilipp Zabel 	ipu_dc_exit(ipu);
104739b9004dSPhilipp Zabel err_dc:
104839b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 1);
104939b9004dSPhilipp Zabel err_di_1:
105039b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 0);
105139b9004dSPhilipp Zabel err_di_0:
1052cd98e85aSSteve Longerbeam 	ipu_image_convert_exit(ipu);
1053cd98e85aSSteve Longerbeam err_image_convert:
10542d2ead45SSteve Longerbeam 	ipu_vdi_exit(ipu);
10552d2ead45SSteve Longerbeam err_vdi:
10561aa8ea0dSSteve Longerbeam 	ipu_ic_exit(ipu);
10571aa8ea0dSSteve Longerbeam err_ic:
10582ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 1);
10592ffd48f2SSteve Longerbeam err_csi_1:
10602ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 0);
10612ffd48f2SSteve Longerbeam err_csi_0:
10627d2691daSSteve Longerbeam 	ipu_cpmem_exit(ipu);
10637d2691daSSteve Longerbeam err_cpmem:
106439b9004dSPhilipp Zabel 	dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret);
106539b9004dSPhilipp Zabel 	return ret;
106639b9004dSPhilipp Zabel }
106739b9004dSPhilipp Zabel 
106839b9004dSPhilipp Zabel static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs)
106939b9004dSPhilipp Zabel {
107039b9004dSPhilipp Zabel 	unsigned long status;
107139b9004dSPhilipp Zabel 	int i, bit, irq;
107239b9004dSPhilipp Zabel 
107339b9004dSPhilipp Zabel 	for (i = 0; i < num_regs; i++) {
107439b9004dSPhilipp Zabel 
107539b9004dSPhilipp Zabel 		status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i]));
107639b9004dSPhilipp Zabel 		status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i]));
107739b9004dSPhilipp Zabel 
107839b9004dSPhilipp Zabel 		for_each_set_bit(bit, &status, 32) {
1079682b7c1cSLinus Torvalds 			irq = irq_linear_revmap(ipu->domain,
1080682b7c1cSLinus Torvalds 						regs[i] * 32 + bit);
108139b9004dSPhilipp Zabel 			if (irq)
108239b9004dSPhilipp Zabel 				generic_handle_irq(irq);
108339b9004dSPhilipp Zabel 		}
108439b9004dSPhilipp Zabel 	}
108539b9004dSPhilipp Zabel }
108639b9004dSPhilipp Zabel 
1087bd0b9ac4SThomas Gleixner static void ipu_irq_handler(struct irq_desc *desc)
108839b9004dSPhilipp Zabel {
108939b9004dSPhilipp Zabel 	struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
10904d9efdfcSJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
1091ac66b834SColin Ian King 	static const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14};
109239b9004dSPhilipp Zabel 
109339b9004dSPhilipp Zabel 	chained_irq_enter(chip, desc);
109439b9004dSPhilipp Zabel 
109539b9004dSPhilipp Zabel 	ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
109639b9004dSPhilipp Zabel 
109739b9004dSPhilipp Zabel 	chained_irq_exit(chip, desc);
109839b9004dSPhilipp Zabel }
109939b9004dSPhilipp Zabel 
1100bd0b9ac4SThomas Gleixner static void ipu_err_irq_handler(struct irq_desc *desc)
110139b9004dSPhilipp Zabel {
110239b9004dSPhilipp Zabel 	struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
11034d9efdfcSJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
1104ac66b834SColin Ian King 	static const int int_reg[] = { 4, 5, 8, 9};
110539b9004dSPhilipp Zabel 
110639b9004dSPhilipp Zabel 	chained_irq_enter(chip, desc);
110739b9004dSPhilipp Zabel 
110839b9004dSPhilipp Zabel 	ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
110939b9004dSPhilipp Zabel 
111039b9004dSPhilipp Zabel 	chained_irq_exit(chip, desc);
111139b9004dSPhilipp Zabel }
111239b9004dSPhilipp Zabel 
1113682b7c1cSLinus Torvalds int ipu_map_irq(struct ipu_soc *ipu, int irq)
1114682b7c1cSLinus Torvalds {
1115682b7c1cSLinus Torvalds 	int virq;
1116682b7c1cSLinus Torvalds 
1117682b7c1cSLinus Torvalds 	virq = irq_linear_revmap(ipu->domain, irq);
1118682b7c1cSLinus Torvalds 	if (!virq)
1119682b7c1cSLinus Torvalds 		virq = irq_create_mapping(ipu->domain, irq);
1120682b7c1cSLinus Torvalds 
1121682b7c1cSLinus Torvalds 	return virq;
1122682b7c1cSLinus Torvalds }
1123682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_map_irq);
1124682b7c1cSLinus Torvalds 
112539b9004dSPhilipp Zabel int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
112639b9004dSPhilipp Zabel 		enum ipu_channel_irq irq_type)
112739b9004dSPhilipp Zabel {
1128682b7c1cSLinus Torvalds 	return ipu_map_irq(ipu, irq_type + channel->num);
112939b9004dSPhilipp Zabel }
113039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq);
113139b9004dSPhilipp Zabel 
113239b9004dSPhilipp Zabel static void ipu_submodules_exit(struct ipu_soc *ipu)
113339b9004dSPhilipp Zabel {
113435de925fSPhilipp Zabel 	ipu_smfc_exit(ipu);
113539b9004dSPhilipp Zabel 	ipu_dp_exit(ipu);
113639b9004dSPhilipp Zabel 	ipu_dmfc_exit(ipu);
113739b9004dSPhilipp Zabel 	ipu_dc_exit(ipu);
113839b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 1);
113939b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 0);
1140cd98e85aSSteve Longerbeam 	ipu_image_convert_exit(ipu);
11412d2ead45SSteve Longerbeam 	ipu_vdi_exit(ipu);
11421aa8ea0dSSteve Longerbeam 	ipu_ic_exit(ipu);
11432ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 1);
11442ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 0);
11457d2691daSSteve Longerbeam 	ipu_cpmem_exit(ipu);
114639b9004dSPhilipp Zabel }
114739b9004dSPhilipp Zabel 
114839b9004dSPhilipp Zabel static int platform_remove_devices_fn(struct device *dev, void *unused)
114939b9004dSPhilipp Zabel {
115039b9004dSPhilipp Zabel 	struct platform_device *pdev = to_platform_device(dev);
115139b9004dSPhilipp Zabel 
115239b9004dSPhilipp Zabel 	platform_device_unregister(pdev);
115339b9004dSPhilipp Zabel 
115439b9004dSPhilipp Zabel 	return 0;
115539b9004dSPhilipp Zabel }
115639b9004dSPhilipp Zabel 
115739b9004dSPhilipp Zabel static void platform_device_unregister_children(struct platform_device *pdev)
115839b9004dSPhilipp Zabel {
115939b9004dSPhilipp Zabel 	device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn);
116039b9004dSPhilipp Zabel }
116139b9004dSPhilipp Zabel 
116239b9004dSPhilipp Zabel struct ipu_platform_reg {
116339b9004dSPhilipp Zabel 	struct ipu_client_platformdata pdata;
116439b9004dSPhilipp Zabel 	const char *name;
116539b9004dSPhilipp Zabel };
116639b9004dSPhilipp Zabel 
1167304e6be6SPhilipp Zabel /* These must be in the order of the corresponding device tree port nodes */
1168310944d1SPhilipp Zabel static struct ipu_platform_reg client_reg[] = {
116939b9004dSPhilipp Zabel 	{
117039b9004dSPhilipp Zabel 		.pdata = {
1171304e6be6SPhilipp Zabel 			.csi = 0,
1172304e6be6SPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_CSI0,
1173304e6be6SPhilipp Zabel 			.dma[1] = -EINVAL,
1174304e6be6SPhilipp Zabel 		},
117588287ec3SSteve Longerbeam 		.name = "imx-ipuv3-csi",
1176304e6be6SPhilipp Zabel 	}, {
1177304e6be6SPhilipp Zabel 		.pdata = {
1178304e6be6SPhilipp Zabel 			.csi = 1,
1179304e6be6SPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_CSI1,
1180304e6be6SPhilipp Zabel 			.dma[1] = -EINVAL,
1181304e6be6SPhilipp Zabel 		},
118288287ec3SSteve Longerbeam 		.name = "imx-ipuv3-csi",
1183304e6be6SPhilipp Zabel 	}, {
1184304e6be6SPhilipp Zabel 		.pdata = {
118539b9004dSPhilipp Zabel 			.di = 0,
118639b9004dSPhilipp Zabel 			.dc = 5,
118739b9004dSPhilipp Zabel 			.dp = IPU_DP_FLOW_SYNC_BG,
118839b9004dSPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC,
118939b9004dSPhilipp Zabel 			.dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC,
119039b9004dSPhilipp Zabel 		},
119139b9004dSPhilipp Zabel 		.name = "imx-ipuv3-crtc",
119239b9004dSPhilipp Zabel 	}, {
119339b9004dSPhilipp Zabel 		.pdata = {
119439b9004dSPhilipp Zabel 			.di = 1,
119539b9004dSPhilipp Zabel 			.dc = 1,
119639b9004dSPhilipp Zabel 			.dp = -EINVAL,
119739b9004dSPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC,
119839b9004dSPhilipp Zabel 			.dma[1] = -EINVAL,
119939b9004dSPhilipp Zabel 		},
120039b9004dSPhilipp Zabel 		.name = "imx-ipuv3-crtc",
120139b9004dSPhilipp Zabel 	},
120239b9004dSPhilipp Zabel };
120339b9004dSPhilipp Zabel 
120439b9004dSPhilipp Zabel static DEFINE_MUTEX(ipu_client_id_mutex);
120539b9004dSPhilipp Zabel static int ipu_client_id;
120639b9004dSPhilipp Zabel 
1207d6ca8ca7SPhilipp Zabel static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base)
120839b9004dSPhilipp Zabel {
120939b9004dSPhilipp Zabel 	struct device *dev = ipu->dev;
121039b9004dSPhilipp Zabel 	unsigned i;
121139b9004dSPhilipp Zabel 	int id, ret;
121239b9004dSPhilipp Zabel 
121339b9004dSPhilipp Zabel 	mutex_lock(&ipu_client_id_mutex);
121439b9004dSPhilipp Zabel 	id = ipu_client_id;
121539b9004dSPhilipp Zabel 	ipu_client_id += ARRAY_SIZE(client_reg);
121639b9004dSPhilipp Zabel 	mutex_unlock(&ipu_client_id_mutex);
121739b9004dSPhilipp Zabel 
121839b9004dSPhilipp Zabel 	for (i = 0; i < ARRAY_SIZE(client_reg); i++) {
1219310944d1SPhilipp Zabel 		struct ipu_platform_reg *reg = &client_reg[i];
122039b9004dSPhilipp Zabel 		struct platform_device *pdev;
122117e05217SPhilipp Zabel 		struct device_node *of_node;
122217e05217SPhilipp Zabel 
122317e05217SPhilipp Zabel 		/* Associate subdevice with the corresponding port node */
122417e05217SPhilipp Zabel 		of_node = of_graph_get_port_by_id(dev->of_node, i);
122517e05217SPhilipp Zabel 		if (!of_node) {
122617e05217SPhilipp Zabel 			dev_info(dev,
12274bf99144SRob Herring 				 "no port@%d node in %pOF, not using %s%d\n",
12284bf99144SRob Herring 				 i, dev->of_node,
122917e05217SPhilipp Zabel 				 (i / 2) ? "DI" : "CSI", i % 2);
123017e05217SPhilipp Zabel 			continue;
123117e05217SPhilipp Zabel 		}
123239b9004dSPhilipp Zabel 
1233304e6be6SPhilipp Zabel 		pdev = platform_device_alloc(reg->name, id++);
1234304e6be6SPhilipp Zabel 		if (!pdev) {
1235304e6be6SPhilipp Zabel 			ret = -ENOMEM;
1236304e6be6SPhilipp Zabel 			goto err_register;
1237304e6be6SPhilipp Zabel 		}
123839b9004dSPhilipp Zabel 
1239304e6be6SPhilipp Zabel 		pdev->dev.parent = dev;
1240304e6be6SPhilipp Zabel 
1241310944d1SPhilipp Zabel 		reg->pdata.of_node = of_node;
1242304e6be6SPhilipp Zabel 		ret = platform_device_add_data(pdev, &reg->pdata,
1243304e6be6SPhilipp Zabel 					       sizeof(reg->pdata));
1244304e6be6SPhilipp Zabel 		if (!ret)
1245304e6be6SPhilipp Zabel 			ret = platform_device_add(pdev);
1246304e6be6SPhilipp Zabel 		if (ret) {
1247304e6be6SPhilipp Zabel 			platform_device_put(pdev);
124839b9004dSPhilipp Zabel 			goto err_register;
124939b9004dSPhilipp Zabel 		}
1250e4946cdcSAxel Lin 	}
125139b9004dSPhilipp Zabel 
125239b9004dSPhilipp Zabel 	return 0;
125339b9004dSPhilipp Zabel 
125439b9004dSPhilipp Zabel err_register:
125539b9004dSPhilipp Zabel 	platform_device_unregister_children(to_platform_device(dev));
125639b9004dSPhilipp Zabel 
125739b9004dSPhilipp Zabel 	return ret;
125839b9004dSPhilipp Zabel }
125939b9004dSPhilipp Zabel 
126039b9004dSPhilipp Zabel 
126139b9004dSPhilipp Zabel static int ipu_irq_init(struct ipu_soc *ipu)
126239b9004dSPhilipp Zabel {
126339b9004dSPhilipp Zabel 	struct irq_chip_generic *gc;
126439b9004dSPhilipp Zabel 	struct irq_chip_type *ct;
126539b9004dSPhilipp Zabel 	unsigned long unused[IPU_NUM_IRQS / 32] = {
126639b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
126739b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
126839b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
126939b9004dSPhilipp Zabel 		0x4077ffff, 0xffe7e1fd,
127039b9004dSPhilipp Zabel 		0x23fffffe, 0x8880fff0,
127139b9004dSPhilipp Zabel 		0xf98fe7d0, 0xfff81fff,
127239b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
127339b9004dSPhilipp Zabel 		0x00000000,
127439b9004dSPhilipp Zabel 	};
127539b9004dSPhilipp Zabel 	int ret, i;
127639b9004dSPhilipp Zabel 
127739b9004dSPhilipp Zabel 	ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS,
127839b9004dSPhilipp Zabel 					    &irq_generic_chip_ops, ipu);
127939b9004dSPhilipp Zabel 	if (!ipu->domain) {
128039b9004dSPhilipp Zabel 		dev_err(ipu->dev, "failed to add irq domain\n");
128139b9004dSPhilipp Zabel 		return -ENODEV;
128239b9004dSPhilipp Zabel 	}
128339b9004dSPhilipp Zabel 
128439b9004dSPhilipp Zabel 	ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU",
1285ca0141deSRob Herring 					     handle_level_irq, 0, 0, 0);
128639b9004dSPhilipp Zabel 	if (ret < 0) {
128739b9004dSPhilipp Zabel 		dev_err(ipu->dev, "failed to alloc generic irq chips\n");
128839b9004dSPhilipp Zabel 		irq_domain_remove(ipu->domain);
128939b9004dSPhilipp Zabel 		return ret;
129039b9004dSPhilipp Zabel 	}
129139b9004dSPhilipp Zabel 
1292a92d8145SPhilipp Zabel 	/* Mask and clear all interrupts */
1293a92d8145SPhilipp Zabel 	for (i = 0; i < IPU_NUM_IRQS; i += 32) {
1294510e6426SRussell King 		ipu_cm_write(ipu, 0, IPU_INT_CTRL(i / 32));
1295a92d8145SPhilipp Zabel 		ipu_cm_write(ipu, ~unused[i / 32], IPU_INT_STAT(i / 32));
1296a92d8145SPhilipp Zabel 	}
1297510e6426SRussell King 
129839b9004dSPhilipp Zabel 	for (i = 0; i < IPU_NUM_IRQS; i += 32) {
129939b9004dSPhilipp Zabel 		gc = irq_get_domain_generic_chip(ipu->domain, i);
130039b9004dSPhilipp Zabel 		gc->reg_base = ipu->cm_reg;
130139b9004dSPhilipp Zabel 		gc->unused = unused[i / 32];
130239b9004dSPhilipp Zabel 		ct = gc->chip_types;
130339b9004dSPhilipp Zabel 		ct->chip.irq_ack = irq_gc_ack_set_bit;
130439b9004dSPhilipp Zabel 		ct->chip.irq_mask = irq_gc_mask_clr_bit;
130539b9004dSPhilipp Zabel 		ct->chip.irq_unmask = irq_gc_mask_set_bit;
130639b9004dSPhilipp Zabel 		ct->regs.ack = IPU_INT_STAT(i / 32);
130739b9004dSPhilipp Zabel 		ct->regs.mask = IPU_INT_CTRL(i / 32);
130839b9004dSPhilipp Zabel 	}
130939b9004dSPhilipp Zabel 
131086f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu);
131186f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler,
131286f5e733SRussell King 					 ipu);
131339b9004dSPhilipp Zabel 
131439b9004dSPhilipp Zabel 	return 0;
131539b9004dSPhilipp Zabel }
131639b9004dSPhilipp Zabel 
131739b9004dSPhilipp Zabel static void ipu_irq_exit(struct ipu_soc *ipu)
131839b9004dSPhilipp Zabel {
131939b9004dSPhilipp Zabel 	int i, irq;
132039b9004dSPhilipp Zabel 
132186f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL);
132286f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL);
132339b9004dSPhilipp Zabel 
132439b9004dSPhilipp Zabel 	/* TODO: remove irq_domain_generic_chips */
132539b9004dSPhilipp Zabel 
132639b9004dSPhilipp Zabel 	for (i = 0; i < IPU_NUM_IRQS; i++) {
132739b9004dSPhilipp Zabel 		irq = irq_linear_revmap(ipu->domain, i);
132839b9004dSPhilipp Zabel 		if (irq)
132939b9004dSPhilipp Zabel 			irq_dispose_mapping(irq);
133039b9004dSPhilipp Zabel 	}
133139b9004dSPhilipp Zabel 
133239b9004dSPhilipp Zabel 	irq_domain_remove(ipu->domain);
133339b9004dSPhilipp Zabel }
133439b9004dSPhilipp Zabel 
13353feb049fSSteve Longerbeam void ipu_dump(struct ipu_soc *ipu)
13363feb049fSSteve Longerbeam {
13373feb049fSSteve Longerbeam 	int i;
13383feb049fSSteve Longerbeam 
13393feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n",
13403feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_CONF));
13413feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n",
13423feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CONF));
13433feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n",
13443feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_EN(0)));
13453feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n",
13463feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_EN(32)));
13473feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n",
13483feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_PRI(0)));
13493feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n",
13503feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_PRI(32)));
13513feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n",
13523feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_BAND_EN(0)));
13533feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n",
13543feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_BAND_EN(32)));
13553feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n",
13563feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0)));
13573feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n",
13583feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32)));
13593feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n",
13603feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_PROC_FLOW1));
13613feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n",
13623feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_PROC_FLOW2));
13633feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n",
13643feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_PROC_FLOW3));
13653feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n",
13663feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_DISP_FLOW1));
13673feb049fSSteve Longerbeam 	for (i = 0; i < 15; i++)
13683feb049fSSteve Longerbeam 		dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i,
13693feb049fSSteve Longerbeam 			ipu_cm_read(ipu, IPU_INT_CTRL(i)));
13703feb049fSSteve Longerbeam }
13713feb049fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_dump);
13723feb049fSSteve Longerbeam 
137339b9004dSPhilipp Zabel static int ipu_probe(struct platform_device *pdev)
137439b9004dSPhilipp Zabel {
1375572a7615SSteve Longerbeam 	struct device_node *np = pdev->dev.of_node;
137639b9004dSPhilipp Zabel 	struct ipu_soc *ipu;
137739b9004dSPhilipp Zabel 	struct resource *res;
137839b9004dSPhilipp Zabel 	unsigned long ipu_base;
137993adc8b5SPhilipp Zabel 	int ret, irq_sync, irq_err;
138039b9004dSPhilipp Zabel 	const struct ipu_devtype *devtype;
138139b9004dSPhilipp Zabel 
1382e92e4478SLABBE Corentin 	devtype = of_device_get_match_data(&pdev->dev);
1383e92e4478SLABBE Corentin 	if (!devtype)
1384e92e4478SLABBE Corentin 		return -EINVAL;
138539b9004dSPhilipp Zabel 
138639b9004dSPhilipp Zabel 	irq_sync = platform_get_irq(pdev, 0);
138739b9004dSPhilipp Zabel 	irq_err = platform_get_irq(pdev, 1);
138839b9004dSPhilipp Zabel 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
138939b9004dSPhilipp Zabel 
139039b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n",
139139b9004dSPhilipp Zabel 			irq_sync, irq_err);
139239b9004dSPhilipp Zabel 
139339b9004dSPhilipp Zabel 	if (!res || irq_sync < 0 || irq_err < 0)
139439b9004dSPhilipp Zabel 		return -ENODEV;
139539b9004dSPhilipp Zabel 
139639b9004dSPhilipp Zabel 	ipu_base = res->start;
139739b9004dSPhilipp Zabel 
139839b9004dSPhilipp Zabel 	ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL);
139939b9004dSPhilipp Zabel 	if (!ipu)
140039b9004dSPhilipp Zabel 		return -ENODEV;
140139b9004dSPhilipp Zabel 
140292681fe7SLucas Stach 	ipu->id = of_alias_get_id(np, "ipu");
14032d87e6c1SPhilipp Zabel 	if (ipu->id < 0)
14042d87e6c1SPhilipp Zabel 		ipu->id = 0;
140592681fe7SLucas Stach 
140630310c83SLucas Stach 	if (of_device_is_compatible(np, "fsl,imx6qp-ipu") &&
140730310c83SLucas Stach 	    IS_ENABLED(CONFIG_DRM)) {
140892681fe7SLucas Stach 		ipu->prg_priv = ipu_prg_lookup_by_phandle(&pdev->dev,
140992681fe7SLucas Stach 							  "fsl,prg", ipu->id);
141092681fe7SLucas Stach 		if (!ipu->prg_priv)
141192681fe7SLucas Stach 			return -EPROBE_DEFER;
141292681fe7SLucas Stach 	}
141392681fe7SLucas Stach 
141439b9004dSPhilipp Zabel 	ipu->devtype = devtype;
141539b9004dSPhilipp Zabel 	ipu->ipu_type = devtype->type;
141639b9004dSPhilipp Zabel 
141739b9004dSPhilipp Zabel 	spin_lock_init(&ipu->lock);
141839b9004dSPhilipp Zabel 	mutex_init(&ipu->channel_lock);
141993adc8b5SPhilipp Zabel 	INIT_LIST_HEAD(&ipu->channels);
142039b9004dSPhilipp Zabel 
142139b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "cm_reg:   0x%08lx\n",
142239b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs);
142339b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "idmac:    0x%08lx\n",
142439b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS);
142539b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "cpmem:    0x%08lx\n",
142639b9004dSPhilipp Zabel 			ipu_base + devtype->cpmem_ofs);
14272ffd48f2SSteve Longerbeam 	dev_dbg(&pdev->dev, "csi0:    0x%08lx\n",
14282ffd48f2SSteve Longerbeam 			ipu_base + devtype->csi0_ofs);
14292ffd48f2SSteve Longerbeam 	dev_dbg(&pdev->dev, "csi1:    0x%08lx\n",
14302ffd48f2SSteve Longerbeam 			ipu_base + devtype->csi1_ofs);
14311aa8ea0dSSteve Longerbeam 	dev_dbg(&pdev->dev, "ic:      0x%08lx\n",
14321aa8ea0dSSteve Longerbeam 			ipu_base + devtype->ic_ofs);
143339b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "disp0:    0x%08lx\n",
143439b9004dSPhilipp Zabel 			ipu_base + devtype->disp0_ofs);
143539b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "disp1:    0x%08lx\n",
143639b9004dSPhilipp Zabel 			ipu_base + devtype->disp1_ofs);
143739b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "srm:      0x%08lx\n",
143839b9004dSPhilipp Zabel 			ipu_base + devtype->srm_ofs);
143939b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "tpm:      0x%08lx\n",
144039b9004dSPhilipp Zabel 			ipu_base + devtype->tpm_ofs);
144139b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "dc:       0x%08lx\n",
144239b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS);
144339b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "ic:       0x%08lx\n",
144439b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS);
144539b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "dmfc:     0x%08lx\n",
144639b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS);
144739b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "vdi:      0x%08lx\n",
144839b9004dSPhilipp Zabel 			ipu_base + devtype->vdi_ofs);
144939b9004dSPhilipp Zabel 
145039b9004dSPhilipp Zabel 	ipu->cm_reg = devm_ioremap(&pdev->dev,
145139b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs, PAGE_SIZE);
145239b9004dSPhilipp Zabel 	ipu->idmac_reg = devm_ioremap(&pdev->dev,
145339b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS,
145439b9004dSPhilipp Zabel 			PAGE_SIZE);
145539b9004dSPhilipp Zabel 
14567d2691daSSteve Longerbeam 	if (!ipu->cm_reg || !ipu->idmac_reg)
145739b9004dSPhilipp Zabel 		return -ENOMEM;
145839b9004dSPhilipp Zabel 
145939b9004dSPhilipp Zabel 	ipu->clk = devm_clk_get(&pdev->dev, "bus");
146039b9004dSPhilipp Zabel 	if (IS_ERR(ipu->clk)) {
146139b9004dSPhilipp Zabel 		ret = PTR_ERR(ipu->clk);
146239b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "clk_get failed with %d", ret);
146339b9004dSPhilipp Zabel 		return ret;
146439b9004dSPhilipp Zabel 	}
146539b9004dSPhilipp Zabel 
146639b9004dSPhilipp Zabel 	platform_set_drvdata(pdev, ipu);
146739b9004dSPhilipp Zabel 
146839b9004dSPhilipp Zabel 	ret = clk_prepare_enable(ipu->clk);
146939b9004dSPhilipp Zabel 	if (ret) {
147039b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
147139b9004dSPhilipp Zabel 		return ret;
147239b9004dSPhilipp Zabel 	}
147339b9004dSPhilipp Zabel 
147439b9004dSPhilipp Zabel 	ipu->dev = &pdev->dev;
147539b9004dSPhilipp Zabel 	ipu->irq_sync = irq_sync;
147639b9004dSPhilipp Zabel 	ipu->irq_err = irq_err;
147739b9004dSPhilipp Zabel 
147839b9004dSPhilipp Zabel 	ret = device_reset(&pdev->dev);
147939b9004dSPhilipp Zabel 	if (ret) {
148039b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "failed to reset: %d\n", ret);
148139b9004dSPhilipp Zabel 		goto out_failed_reset;
148239b9004dSPhilipp Zabel 	}
148339b9004dSPhilipp Zabel 	ret = ipu_memory_reset(ipu);
148439b9004dSPhilipp Zabel 	if (ret)
148539b9004dSPhilipp Zabel 		goto out_failed_reset;
148639b9004dSPhilipp Zabel 
1487596a65d1SDavid Jander 	ret = ipu_irq_init(ipu);
1488596a65d1SDavid Jander 	if (ret)
1489596a65d1SDavid Jander 		goto out_failed_irq;
1490596a65d1SDavid Jander 
149139b9004dSPhilipp Zabel 	/* Set MCU_T to divide MCU access window into 2 */
149239b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
149339b9004dSPhilipp Zabel 			IPU_DISP_GEN);
149439b9004dSPhilipp Zabel 
149539b9004dSPhilipp Zabel 	ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk);
149639b9004dSPhilipp Zabel 	if (ret)
149739b9004dSPhilipp Zabel 		goto failed_submodules_init;
149839b9004dSPhilipp Zabel 
1499d6ca8ca7SPhilipp Zabel 	ret = ipu_add_client_devices(ipu, ipu_base);
150039b9004dSPhilipp Zabel 	if (ret) {
150139b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "adding client devices failed with %d\n",
150239b9004dSPhilipp Zabel 				ret);
150339b9004dSPhilipp Zabel 		goto failed_add_clients;
150439b9004dSPhilipp Zabel 	}
150539b9004dSPhilipp Zabel 
150639b9004dSPhilipp Zabel 	dev_info(&pdev->dev, "%s probed\n", devtype->name);
150739b9004dSPhilipp Zabel 
150839b9004dSPhilipp Zabel 	return 0;
150939b9004dSPhilipp Zabel 
151039b9004dSPhilipp Zabel failed_add_clients:
151139b9004dSPhilipp Zabel 	ipu_submodules_exit(ipu);
151239b9004dSPhilipp Zabel failed_submodules_init:
151339b9004dSPhilipp Zabel 	ipu_irq_exit(ipu);
151439b9004dSPhilipp Zabel out_failed_irq:
1515596a65d1SDavid Jander out_failed_reset:
151639b9004dSPhilipp Zabel 	clk_disable_unprepare(ipu->clk);
151739b9004dSPhilipp Zabel 	return ret;
151839b9004dSPhilipp Zabel }
151939b9004dSPhilipp Zabel 
152039b9004dSPhilipp Zabel static int ipu_remove(struct platform_device *pdev)
152139b9004dSPhilipp Zabel {
152239b9004dSPhilipp Zabel 	struct ipu_soc *ipu = platform_get_drvdata(pdev);
152339b9004dSPhilipp Zabel 
152439b9004dSPhilipp Zabel 	platform_device_unregister_children(pdev);
152539b9004dSPhilipp Zabel 	ipu_submodules_exit(ipu);
152639b9004dSPhilipp Zabel 	ipu_irq_exit(ipu);
152739b9004dSPhilipp Zabel 
152839b9004dSPhilipp Zabel 	clk_disable_unprepare(ipu->clk);
152939b9004dSPhilipp Zabel 
153039b9004dSPhilipp Zabel 	return 0;
153139b9004dSPhilipp Zabel }
153239b9004dSPhilipp Zabel 
153339b9004dSPhilipp Zabel static struct platform_driver imx_ipu_driver = {
153439b9004dSPhilipp Zabel 	.driver = {
153539b9004dSPhilipp Zabel 		.name = "imx-ipuv3",
153639b9004dSPhilipp Zabel 		.of_match_table = imx_ipu_dt_ids,
153739b9004dSPhilipp Zabel 	},
153839b9004dSPhilipp Zabel 	.probe = ipu_probe,
153939b9004dSPhilipp Zabel 	.remove = ipu_remove,
154039b9004dSPhilipp Zabel };
154139b9004dSPhilipp Zabel 
1542d2a34232SLucas Stach static struct platform_driver * const drivers[] = {
154330310c83SLucas Stach #if IS_ENABLED(CONFIG_DRM)
1544d2a34232SLucas Stach 	&ipu_pre_drv,
1545ea9c2605SLucas Stach 	&ipu_prg_drv,
154630310c83SLucas Stach #endif
1547d2a34232SLucas Stach 	&imx_ipu_driver,
1548d2a34232SLucas Stach };
1549d2a34232SLucas Stach 
1550d2a34232SLucas Stach static int __init imx_ipu_init(void)
1551d2a34232SLucas Stach {
1552d2a34232SLucas Stach 	return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
1553d2a34232SLucas Stach }
1554d2a34232SLucas Stach module_init(imx_ipu_init);
1555d2a34232SLucas Stach 
1556d2a34232SLucas Stach static void __exit imx_ipu_exit(void)
1557d2a34232SLucas Stach {
1558d2a34232SLucas Stach 	platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
1559d2a34232SLucas Stach }
1560d2a34232SLucas Stach module_exit(imx_ipu_exit);
156139b9004dSPhilipp Zabel 
156239b9004dSPhilipp Zabel MODULE_ALIAS("platform:imx-ipuv3");
156339b9004dSPhilipp Zabel MODULE_DESCRIPTION("i.MX IPU v3 driver");
156439b9004dSPhilipp Zabel MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
156539b9004dSPhilipp Zabel MODULE_LICENSE("GPL");
1566