xref: /openbmc/linux/drivers/gpu/ipu-v3/ipu-common.c (revision 5c41bb60)
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>
31304e6be6SPhilipp Zabel #include <linux/of_graph.h>
3239b9004dSPhilipp Zabel 
3339b9004dSPhilipp Zabel #include <drm/drm_fourcc.h>
3439b9004dSPhilipp Zabel 
3539b9004dSPhilipp Zabel #include <video/imx-ipu-v3.h>
3639b9004dSPhilipp Zabel #include "ipu-prv.h"
3739b9004dSPhilipp Zabel 
3839b9004dSPhilipp Zabel static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset)
3939b9004dSPhilipp Zabel {
4039b9004dSPhilipp Zabel 	return readl(ipu->cm_reg + offset);
4139b9004dSPhilipp Zabel }
4239b9004dSPhilipp Zabel 
4339b9004dSPhilipp Zabel static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset)
4439b9004dSPhilipp Zabel {
4539b9004dSPhilipp Zabel 	writel(value, ipu->cm_reg + offset);
4639b9004dSPhilipp Zabel }
4739b9004dSPhilipp Zabel 
48572a7615SSteve Longerbeam int ipu_get_num(struct ipu_soc *ipu)
49572a7615SSteve Longerbeam {
50572a7615SSteve Longerbeam 	return ipu->id;
51572a7615SSteve Longerbeam }
52572a7615SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_get_num);
53572a7615SSteve Longerbeam 
54f9bb7acbSPhilipp Zabel void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync)
5539b9004dSPhilipp Zabel {
5639b9004dSPhilipp Zabel 	u32 val;
5739b9004dSPhilipp Zabel 
5839b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_SRM_PRI2);
59f9bb7acbSPhilipp Zabel 	val &= ~DP_S_SRM_MODE_MASK;
60f9bb7acbSPhilipp Zabel 	val |= sync ? DP_S_SRM_MODE_NEXT_FRAME :
61f9bb7acbSPhilipp Zabel 		      DP_S_SRM_MODE_NOW;
6239b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_SRM_PRI2);
6339b9004dSPhilipp Zabel }
64f9bb7acbSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_srm_dp_update);
6539b9004dSPhilipp Zabel 
6639b9004dSPhilipp Zabel enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc)
6739b9004dSPhilipp Zabel {
6839b9004dSPhilipp Zabel 	switch (drm_fourcc) {
690cb8b757SPhilipp Zabel 	case DRM_FORMAT_ARGB1555:
700cb8b757SPhilipp Zabel 	case DRM_FORMAT_ABGR1555:
710cb8b757SPhilipp Zabel 	case DRM_FORMAT_RGBA5551:
720cb8b757SPhilipp Zabel 	case DRM_FORMAT_BGRA5551:
7339b9004dSPhilipp Zabel 	case DRM_FORMAT_RGB565:
7439b9004dSPhilipp Zabel 	case DRM_FORMAT_BGR565:
7539b9004dSPhilipp Zabel 	case DRM_FORMAT_RGB888:
7639b9004dSPhilipp Zabel 	case DRM_FORMAT_BGR888:
777d2e8a20SLucas Stach 	case DRM_FORMAT_ARGB4444:
7839b9004dSPhilipp Zabel 	case DRM_FORMAT_XRGB8888:
7939b9004dSPhilipp Zabel 	case DRM_FORMAT_XBGR8888:
8039b9004dSPhilipp Zabel 	case DRM_FORMAT_RGBX8888:
8139b9004dSPhilipp Zabel 	case DRM_FORMAT_BGRX8888:
8239b9004dSPhilipp Zabel 	case DRM_FORMAT_ARGB8888:
8339b9004dSPhilipp Zabel 	case DRM_FORMAT_ABGR8888:
8439b9004dSPhilipp Zabel 	case DRM_FORMAT_RGBA8888:
8539b9004dSPhilipp Zabel 	case DRM_FORMAT_BGRA8888:
86e72db3b1SPhilipp Zabel 	case DRM_FORMAT_RGB565_A8:
87e72db3b1SPhilipp Zabel 	case DRM_FORMAT_BGR565_A8:
88e72db3b1SPhilipp Zabel 	case DRM_FORMAT_RGB888_A8:
89e72db3b1SPhilipp Zabel 	case DRM_FORMAT_BGR888_A8:
90e72db3b1SPhilipp Zabel 	case DRM_FORMAT_RGBX8888_A8:
91e72db3b1SPhilipp Zabel 	case DRM_FORMAT_BGRX8888_A8:
9239b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_RGB;
9339b9004dSPhilipp Zabel 	case DRM_FORMAT_YUYV:
9439b9004dSPhilipp Zabel 	case DRM_FORMAT_UYVY:
9539b9004dSPhilipp Zabel 	case DRM_FORMAT_YUV420:
9639b9004dSPhilipp Zabel 	case DRM_FORMAT_YVU420:
979a34cef0SSteve Longerbeam 	case DRM_FORMAT_YUV422:
989a34cef0SSteve Longerbeam 	case DRM_FORMAT_YVU422:
99c9d508c2SPhilipp Zabel 	case DRM_FORMAT_YUV444:
100c9d508c2SPhilipp Zabel 	case DRM_FORMAT_YVU444:
1019a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV12:
1029a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV21:
1039a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV16:
1049a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV61:
10539b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_YUV;
10639b9004dSPhilipp Zabel 	default:
10739b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_UNKNOWN;
10839b9004dSPhilipp Zabel 	}
10939b9004dSPhilipp Zabel }
11039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace);
11139b9004dSPhilipp Zabel 
11239b9004dSPhilipp Zabel enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat)
11339b9004dSPhilipp Zabel {
11439b9004dSPhilipp Zabel 	switch (pixelformat) {
11539b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YUV420:
11639b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YVU420:
1179a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_YUV422P:
11839b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_UYVY:
11939b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YUYV:
1209a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV12:
1219a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV21:
1229a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV16:
1239a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV61:
12439b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_YUV;
1255c41bb60SPhilipp Zabel 	case V4L2_PIX_FMT_XRGB32:
1265c41bb60SPhilipp Zabel 	case V4L2_PIX_FMT_XBGR32:
12739b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB32:
12839b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_BGR32:
12939b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB24:
13039b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_BGR24:
13139b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB565:
13239b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_RGB;
13339b9004dSPhilipp Zabel 	default:
13439b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_UNKNOWN;
13539b9004dSPhilipp Zabel 	}
13639b9004dSPhilipp Zabel }
13739b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace);
13839b9004dSPhilipp Zabel 
1394cea940dSSteve Longerbeam bool ipu_pixelformat_is_planar(u32 pixelformat)
1404cea940dSSteve Longerbeam {
1414cea940dSSteve Longerbeam 	switch (pixelformat) {
1424cea940dSSteve Longerbeam 	case V4L2_PIX_FMT_YUV420:
1434cea940dSSteve Longerbeam 	case V4L2_PIX_FMT_YVU420:
1449a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_YUV422P:
1459a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV12:
1469a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV21:
1479a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV16:
1489a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV61:
1494cea940dSSteve Longerbeam 		return true;
1504cea940dSSteve Longerbeam 	}
1514cea940dSSteve Longerbeam 
1524cea940dSSteve Longerbeam 	return false;
1534cea940dSSteve Longerbeam }
1544cea940dSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar);
1554cea940dSSteve Longerbeam 
156ae0e9708SSteve Longerbeam enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code)
157ae0e9708SSteve Longerbeam {
158ae0e9708SSteve Longerbeam 	switch (mbus_code & 0xf000) {
159ae0e9708SSteve Longerbeam 	case 0x1000:
160ae0e9708SSteve Longerbeam 		return IPUV3_COLORSPACE_RGB;
161ae0e9708SSteve Longerbeam 	case 0x2000:
162ae0e9708SSteve Longerbeam 		return IPUV3_COLORSPACE_YUV;
163ae0e9708SSteve Longerbeam 	default:
164ae0e9708SSteve Longerbeam 		return IPUV3_COLORSPACE_UNKNOWN;
165ae0e9708SSteve Longerbeam 	}
166ae0e9708SSteve Longerbeam }
167ae0e9708SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace);
168ae0e9708SSteve Longerbeam 
1696930afdcSSteve Longerbeam int ipu_stride_to_bytes(u32 pixel_stride, u32 pixelformat)
1706930afdcSSteve Longerbeam {
1716930afdcSSteve Longerbeam 	switch (pixelformat) {
1726930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_YUV420:
1736930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_YVU420:
1749a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_YUV422P:
1759a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV12:
1769a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV21:
1779a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV16:
1789a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV61:
1796930afdcSSteve Longerbeam 		/*
1806930afdcSSteve Longerbeam 		 * for the planar YUV formats, the stride passed to
1816930afdcSSteve Longerbeam 		 * cpmem must be the stride in bytes of the Y plane.
1826930afdcSSteve Longerbeam 		 * And all the planar YUV formats have an 8-bit
1836930afdcSSteve Longerbeam 		 * Y component.
1846930afdcSSteve Longerbeam 		 */
1856930afdcSSteve Longerbeam 		return (8 * pixel_stride) >> 3;
1866930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_RGB565:
1876930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_YUYV:
1886930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_UYVY:
1896930afdcSSteve Longerbeam 		return (16 * pixel_stride) >> 3;
1906930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_BGR24:
1916930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_RGB24:
1926930afdcSSteve Longerbeam 		return (24 * pixel_stride) >> 3;
1936930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_BGR32:
1946930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_RGB32:
1955c41bb60SPhilipp Zabel 	case V4L2_PIX_FMT_XBGR32:
1965c41bb60SPhilipp Zabel 	case V4L2_PIX_FMT_XRGB32:
1976930afdcSSteve Longerbeam 		return (32 * pixel_stride) >> 3;
1986930afdcSSteve Longerbeam 	default:
1996930afdcSSteve Longerbeam 		break;
2006930afdcSSteve Longerbeam 	}
2016930afdcSSteve Longerbeam 
2026930afdcSSteve Longerbeam 	return -EINVAL;
2036930afdcSSteve Longerbeam }
2046930afdcSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_stride_to_bytes);
2056930afdcSSteve Longerbeam 
206f835f386SSteve Longerbeam int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees,
207f835f386SSteve Longerbeam 			    bool hflip, bool vflip)
208f835f386SSteve Longerbeam {
209f835f386SSteve Longerbeam 	u32 r90, vf, hf;
210f835f386SSteve Longerbeam 
211f835f386SSteve Longerbeam 	switch (degrees) {
212f835f386SSteve Longerbeam 	case 0:
213f835f386SSteve Longerbeam 		vf = hf = r90 = 0;
214f835f386SSteve Longerbeam 		break;
215f835f386SSteve Longerbeam 	case 90:
216f835f386SSteve Longerbeam 		vf = hf = 0;
217f835f386SSteve Longerbeam 		r90 = 1;
218f835f386SSteve Longerbeam 		break;
219f835f386SSteve Longerbeam 	case 180:
220f835f386SSteve Longerbeam 		vf = hf = 1;
221f835f386SSteve Longerbeam 		r90 = 0;
222f835f386SSteve Longerbeam 		break;
223f835f386SSteve Longerbeam 	case 270:
224f835f386SSteve Longerbeam 		vf = hf = r90 = 1;
225f835f386SSteve Longerbeam 		break;
226f835f386SSteve Longerbeam 	default:
227f835f386SSteve Longerbeam 		return -EINVAL;
228f835f386SSteve Longerbeam 	}
229f835f386SSteve Longerbeam 
230f835f386SSteve Longerbeam 	hf ^= (u32)hflip;
231f835f386SSteve Longerbeam 	vf ^= (u32)vflip;
232f835f386SSteve Longerbeam 
233f835f386SSteve Longerbeam 	*mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf);
234f835f386SSteve Longerbeam 	return 0;
235f835f386SSteve Longerbeam }
236f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode);
237f835f386SSteve Longerbeam 
238f835f386SSteve Longerbeam int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode,
239f835f386SSteve Longerbeam 			    bool hflip, bool vflip)
240f835f386SSteve Longerbeam {
241f835f386SSteve Longerbeam 	u32 r90, vf, hf;
242f835f386SSteve Longerbeam 
243f835f386SSteve Longerbeam 	r90 = ((u32)mode >> 2) & 0x1;
244f835f386SSteve Longerbeam 	hf = ((u32)mode >> 1) & 0x1;
245f835f386SSteve Longerbeam 	vf = ((u32)mode >> 0) & 0x1;
246f835f386SSteve Longerbeam 	hf ^= (u32)hflip;
247f835f386SSteve Longerbeam 	vf ^= (u32)vflip;
248f835f386SSteve Longerbeam 
249f835f386SSteve Longerbeam 	switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) {
250f835f386SSteve Longerbeam 	case IPU_ROTATE_NONE:
251f835f386SSteve Longerbeam 		*degrees = 0;
252f835f386SSteve Longerbeam 		break;
253f835f386SSteve Longerbeam 	case IPU_ROTATE_90_RIGHT:
254f835f386SSteve Longerbeam 		*degrees = 90;
255f835f386SSteve Longerbeam 		break;
256f835f386SSteve Longerbeam 	case IPU_ROTATE_180:
257f835f386SSteve Longerbeam 		*degrees = 180;
258f835f386SSteve Longerbeam 		break;
259f835f386SSteve Longerbeam 	case IPU_ROTATE_90_LEFT:
260f835f386SSteve Longerbeam 		*degrees = 270;
261f835f386SSteve Longerbeam 		break;
262f835f386SSteve Longerbeam 	default:
263f835f386SSteve Longerbeam 		return -EINVAL;
264f835f386SSteve Longerbeam 	}
265f835f386SSteve Longerbeam 
266f835f386SSteve Longerbeam 	return 0;
267f835f386SSteve Longerbeam }
268f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees);
269f835f386SSteve Longerbeam 
27039b9004dSPhilipp Zabel struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num)
27139b9004dSPhilipp Zabel {
27239b9004dSPhilipp Zabel 	struct ipuv3_channel *channel;
27339b9004dSPhilipp Zabel 
27439b9004dSPhilipp Zabel 	dev_dbg(ipu->dev, "%s %d\n", __func__, num);
27539b9004dSPhilipp Zabel 
27639b9004dSPhilipp Zabel 	if (num > 63)
27739b9004dSPhilipp Zabel 		return ERR_PTR(-ENODEV);
27839b9004dSPhilipp Zabel 
27939b9004dSPhilipp Zabel 	mutex_lock(&ipu->channel_lock);
28039b9004dSPhilipp Zabel 
28193adc8b5SPhilipp Zabel 	list_for_each_entry(channel, &ipu->channels, list) {
28293adc8b5SPhilipp Zabel 		if (channel->num == num) {
28339b9004dSPhilipp Zabel 			channel = ERR_PTR(-EBUSY);
28439b9004dSPhilipp Zabel 			goto out;
28539b9004dSPhilipp Zabel 		}
28693adc8b5SPhilipp Zabel 	}
28739b9004dSPhilipp Zabel 
28893adc8b5SPhilipp Zabel 	channel = kzalloc(sizeof(*channel), GFP_KERNEL);
28993adc8b5SPhilipp Zabel 	if (!channel) {
29093adc8b5SPhilipp Zabel 		channel = ERR_PTR(-ENOMEM);
29193adc8b5SPhilipp Zabel 		goto out;
29293adc8b5SPhilipp Zabel 	}
29393adc8b5SPhilipp Zabel 
29439b9004dSPhilipp Zabel 	channel->num = num;
29593adc8b5SPhilipp Zabel 	channel->ipu = ipu;
29693adc8b5SPhilipp Zabel 	list_add(&channel->list, &ipu->channels);
29739b9004dSPhilipp Zabel 
29839b9004dSPhilipp Zabel out:
29939b9004dSPhilipp Zabel 	mutex_unlock(&ipu->channel_lock);
30039b9004dSPhilipp Zabel 
30139b9004dSPhilipp Zabel 	return channel;
30239b9004dSPhilipp Zabel }
30339b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get);
30439b9004dSPhilipp Zabel 
30539b9004dSPhilipp Zabel void ipu_idmac_put(struct ipuv3_channel *channel)
30639b9004dSPhilipp Zabel {
30739b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
30839b9004dSPhilipp Zabel 
30939b9004dSPhilipp Zabel 	dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num);
31039b9004dSPhilipp Zabel 
31139b9004dSPhilipp Zabel 	mutex_lock(&ipu->channel_lock);
31239b9004dSPhilipp Zabel 
31393adc8b5SPhilipp Zabel 	list_del(&channel->list);
31493adc8b5SPhilipp Zabel 	kfree(channel);
31539b9004dSPhilipp Zabel 
31639b9004dSPhilipp Zabel 	mutex_unlock(&ipu->channel_lock);
31739b9004dSPhilipp Zabel }
31839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_put);
31939b9004dSPhilipp Zabel 
320aa52f578SSteve Longerbeam #define idma_mask(ch)			(1 << ((ch) & 0x1f))
32139b9004dSPhilipp Zabel 
322e7268c69SSteve Longerbeam /*
323e7268c69SSteve Longerbeam  * This is an undocumented feature, a write one to a channel bit in
324e7268c69SSteve Longerbeam  * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's
325e7268c69SSteve Longerbeam  * internal current buffer pointer so that transfers start from buffer
326e7268c69SSteve Longerbeam  * 0 on the next channel enable (that's the theory anyway, the imx6 TRM
327e7268c69SSteve Longerbeam  * only says these are read-only registers). This operation is required
328e7268c69SSteve Longerbeam  * for channel linking to work correctly, for instance video capture
329e7268c69SSteve Longerbeam  * pipelines that carry out image rotations will fail after the first
330e7268c69SSteve Longerbeam  * streaming unless this function is called for each channel before
331e7268c69SSteve Longerbeam  * re-enabling the channels.
332e7268c69SSteve Longerbeam  */
333e7268c69SSteve Longerbeam static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel)
334e7268c69SSteve Longerbeam {
335e7268c69SSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
336e7268c69SSteve Longerbeam 	unsigned int chno = channel->num;
337e7268c69SSteve Longerbeam 
338e7268c69SSteve Longerbeam 	ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno));
339e7268c69SSteve Longerbeam }
340e7268c69SSteve Longerbeam 
34139b9004dSPhilipp Zabel void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
34239b9004dSPhilipp Zabel 		bool doublebuffer)
34339b9004dSPhilipp Zabel {
34439b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
34539b9004dSPhilipp Zabel 	unsigned long flags;
34639b9004dSPhilipp Zabel 	u32 reg;
34739b9004dSPhilipp Zabel 
34839b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
34939b9004dSPhilipp Zabel 
35039b9004dSPhilipp Zabel 	reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
35139b9004dSPhilipp Zabel 	if (doublebuffer)
35239b9004dSPhilipp Zabel 		reg |= idma_mask(channel->num);
35339b9004dSPhilipp Zabel 	else
35439b9004dSPhilipp Zabel 		reg &= ~idma_mask(channel->num);
35539b9004dSPhilipp Zabel 	ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num));
35639b9004dSPhilipp Zabel 
357e7268c69SSteve Longerbeam 	__ipu_idmac_reset_current_buffer(channel);
358e7268c69SSteve Longerbeam 
35939b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
36039b9004dSPhilipp Zabel }
36139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer);
36239b9004dSPhilipp Zabel 
3634fd1a07aSSteve Longerbeam static const struct {
3644fd1a07aSSteve Longerbeam 	int chnum;
3654fd1a07aSSteve Longerbeam 	u32 reg;
3664fd1a07aSSteve Longerbeam 	int shift;
3674fd1a07aSSteve Longerbeam } idmac_lock_en_info[] = {
3684fd1a07aSSteve Longerbeam 	{ .chnum =  5, .reg = IDMAC_CH_LOCK_EN_1, .shift =  0, },
3694fd1a07aSSteve Longerbeam 	{ .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift =  2, },
3704fd1a07aSSteve Longerbeam 	{ .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift =  4, },
3714fd1a07aSSteve Longerbeam 	{ .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift =  6, },
3724fd1a07aSSteve Longerbeam 	{ .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift =  8, },
3734fd1a07aSSteve Longerbeam 	{ .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, },
3744fd1a07aSSteve Longerbeam 	{ .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, },
3754fd1a07aSSteve Longerbeam 	{ .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, },
3764fd1a07aSSteve Longerbeam 	{ .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, },
3774fd1a07aSSteve Longerbeam 	{ .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, },
3784fd1a07aSSteve Longerbeam 	{ .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, },
3794fd1a07aSSteve Longerbeam 	{ .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift =  0, },
3804fd1a07aSSteve Longerbeam 	{ .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift =  2, },
3814fd1a07aSSteve Longerbeam 	{ .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift =  4, },
3824fd1a07aSSteve Longerbeam 	{ .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift =  6, },
3834fd1a07aSSteve Longerbeam 	{ .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift =  8, },
3844fd1a07aSSteve Longerbeam 	{ .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, },
3854fd1a07aSSteve Longerbeam };
3864fd1a07aSSteve Longerbeam 
3874fd1a07aSSteve Longerbeam int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts)
3884fd1a07aSSteve Longerbeam {
3894fd1a07aSSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
3904fd1a07aSSteve Longerbeam 	unsigned long flags;
3914fd1a07aSSteve Longerbeam 	u32 bursts, regval;
3924fd1a07aSSteve Longerbeam 	int i;
3934fd1a07aSSteve Longerbeam 
3944fd1a07aSSteve Longerbeam 	switch (num_bursts) {
3954fd1a07aSSteve Longerbeam 	case 0:
3964fd1a07aSSteve Longerbeam 	case 1:
3974fd1a07aSSteve Longerbeam 		bursts = 0x00; /* locking disabled */
3984fd1a07aSSteve Longerbeam 		break;
3994fd1a07aSSteve Longerbeam 	case 2:
4004fd1a07aSSteve Longerbeam 		bursts = 0x01;
4014fd1a07aSSteve Longerbeam 		break;
4024fd1a07aSSteve Longerbeam 	case 4:
4034fd1a07aSSteve Longerbeam 		bursts = 0x02;
4044fd1a07aSSteve Longerbeam 		break;
4054fd1a07aSSteve Longerbeam 	case 8:
4064fd1a07aSSteve Longerbeam 		bursts = 0x03;
4074fd1a07aSSteve Longerbeam 		break;
4084fd1a07aSSteve Longerbeam 	default:
4094fd1a07aSSteve Longerbeam 		return -EINVAL;
4104fd1a07aSSteve Longerbeam 	}
4114fd1a07aSSteve Longerbeam 
412cda77556SPhilipp Zabel 	/*
413cda77556SPhilipp Zabel 	 * IPUv3EX / i.MX51 has a different register layout, and on IPUv3M /
414cda77556SPhilipp Zabel 	 * i.MX53 channel arbitration locking doesn't seem to work properly.
415cda77556SPhilipp Zabel 	 * Allow enabling the lock feature on IPUv3H / i.MX6 only.
416cda77556SPhilipp Zabel 	 */
417cda77556SPhilipp Zabel 	if (bursts && ipu->ipu_type != IPUV3H)
418cda77556SPhilipp Zabel 		return -EINVAL;
419cda77556SPhilipp Zabel 
4204fd1a07aSSteve Longerbeam 	for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) {
4214fd1a07aSSteve Longerbeam 		if (channel->num == idmac_lock_en_info[i].chnum)
4224fd1a07aSSteve Longerbeam 			break;
4234fd1a07aSSteve Longerbeam 	}
4244fd1a07aSSteve Longerbeam 	if (i >= ARRAY_SIZE(idmac_lock_en_info))
4254fd1a07aSSteve Longerbeam 		return -EINVAL;
4264fd1a07aSSteve Longerbeam 
4274fd1a07aSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
4284fd1a07aSSteve Longerbeam 
4294fd1a07aSSteve Longerbeam 	regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg);
4304fd1a07aSSteve Longerbeam 	regval &= ~(0x03 << idmac_lock_en_info[i].shift);
4314fd1a07aSSteve Longerbeam 	regval |= (bursts << idmac_lock_en_info[i].shift);
4324fd1a07aSSteve Longerbeam 	ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg);
4334fd1a07aSSteve Longerbeam 
4344fd1a07aSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
4354fd1a07aSSteve Longerbeam 
4364fd1a07aSSteve Longerbeam 	return 0;
4374fd1a07aSSteve Longerbeam }
4384fd1a07aSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable);
4394fd1a07aSSteve Longerbeam 
44039b9004dSPhilipp Zabel int ipu_module_enable(struct ipu_soc *ipu, u32 mask)
44139b9004dSPhilipp Zabel {
44239b9004dSPhilipp Zabel 	unsigned long lock_flags;
44339b9004dSPhilipp Zabel 	u32 val;
44439b9004dSPhilipp Zabel 
44539b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, lock_flags);
44639b9004dSPhilipp Zabel 
44739b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_DISP_GEN);
44839b9004dSPhilipp Zabel 
44939b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI0_EN)
45039b9004dSPhilipp Zabel 		val |= IPU_DI0_COUNTER_RELEASE;
45139b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI1_EN)
45239b9004dSPhilipp Zabel 		val |= IPU_DI1_COUNTER_RELEASE;
45339b9004dSPhilipp Zabel 
45439b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_DISP_GEN);
45539b9004dSPhilipp Zabel 
45639b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CONF);
45739b9004dSPhilipp Zabel 	val |= mask;
45839b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CONF);
45939b9004dSPhilipp Zabel 
46039b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, lock_flags);
46139b9004dSPhilipp Zabel 
46239b9004dSPhilipp Zabel 	return 0;
46339b9004dSPhilipp Zabel }
46439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_enable);
46539b9004dSPhilipp Zabel 
46639b9004dSPhilipp Zabel int ipu_module_disable(struct ipu_soc *ipu, u32 mask)
46739b9004dSPhilipp Zabel {
46839b9004dSPhilipp Zabel 	unsigned long lock_flags;
46939b9004dSPhilipp Zabel 	u32 val;
47039b9004dSPhilipp Zabel 
47139b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, lock_flags);
47239b9004dSPhilipp Zabel 
47339b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CONF);
47439b9004dSPhilipp Zabel 	val &= ~mask;
47539b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CONF);
47639b9004dSPhilipp Zabel 
47739b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_DISP_GEN);
47839b9004dSPhilipp Zabel 
47939b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI0_EN)
48039b9004dSPhilipp Zabel 		val &= ~IPU_DI0_COUNTER_RELEASE;
48139b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI1_EN)
48239b9004dSPhilipp Zabel 		val &= ~IPU_DI1_COUNTER_RELEASE;
48339b9004dSPhilipp Zabel 
48439b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_DISP_GEN);
48539b9004dSPhilipp Zabel 
48639b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, lock_flags);
48739b9004dSPhilipp Zabel 
48839b9004dSPhilipp Zabel 	return 0;
48939b9004dSPhilipp Zabel }
49039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_disable);
49139b9004dSPhilipp Zabel 
492e9046097SPhilipp Zabel int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel)
493e9046097SPhilipp Zabel {
494e9046097SPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
495e9046097SPhilipp Zabel 	unsigned int chno = channel->num;
496e9046097SPhilipp Zabel 
497e9046097SPhilipp Zabel 	return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0;
498e9046097SPhilipp Zabel }
499e9046097SPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer);
500e9046097SPhilipp Zabel 
501aa52f578SSteve Longerbeam bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num)
502aa52f578SSteve Longerbeam {
503aa52f578SSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
504aa52f578SSteve Longerbeam 	unsigned long flags;
505aa52f578SSteve Longerbeam 	u32 reg = 0;
506aa52f578SSteve Longerbeam 
507aa52f578SSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
508aa52f578SSteve Longerbeam 	switch (buf_num) {
509aa52f578SSteve Longerbeam 	case 0:
510aa52f578SSteve Longerbeam 		reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num));
511aa52f578SSteve Longerbeam 		break;
512aa52f578SSteve Longerbeam 	case 1:
513aa52f578SSteve Longerbeam 		reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num));
514aa52f578SSteve Longerbeam 		break;
515aa52f578SSteve Longerbeam 	case 2:
516aa52f578SSteve Longerbeam 		reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num));
517aa52f578SSteve Longerbeam 		break;
518aa52f578SSteve Longerbeam 	}
519aa52f578SSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
520aa52f578SSteve Longerbeam 
521aa52f578SSteve Longerbeam 	return ((reg & idma_mask(channel->num)) != 0);
522aa52f578SSteve Longerbeam }
523aa52f578SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready);
524aa52f578SSteve Longerbeam 
52539b9004dSPhilipp Zabel void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num)
52639b9004dSPhilipp Zabel {
52739b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
52839b9004dSPhilipp Zabel 	unsigned int chno = channel->num;
52939b9004dSPhilipp Zabel 	unsigned long flags;
53039b9004dSPhilipp Zabel 
53139b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
53239b9004dSPhilipp Zabel 
53339b9004dSPhilipp Zabel 	/* Mark buffer as ready. */
53439b9004dSPhilipp Zabel 	if (buf_num == 0)
53539b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
53639b9004dSPhilipp Zabel 	else
53739b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
53839b9004dSPhilipp Zabel 
53939b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
54039b9004dSPhilipp Zabel }
54139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer);
54239b9004dSPhilipp Zabel 
543bce6f087SSteve Longerbeam void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num)
544bce6f087SSteve Longerbeam {
545bce6f087SSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
546bce6f087SSteve Longerbeam 	unsigned int chno = channel->num;
547bce6f087SSteve Longerbeam 	unsigned long flags;
548bce6f087SSteve Longerbeam 
549bce6f087SSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
550bce6f087SSteve Longerbeam 
551bce6f087SSteve Longerbeam 	ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */
552bce6f087SSteve Longerbeam 	switch (buf_num) {
553bce6f087SSteve Longerbeam 	case 0:
554bce6f087SSteve Longerbeam 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
555bce6f087SSteve Longerbeam 		break;
556bce6f087SSteve Longerbeam 	case 1:
557bce6f087SSteve Longerbeam 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
558bce6f087SSteve Longerbeam 		break;
559bce6f087SSteve Longerbeam 	case 2:
560bce6f087SSteve Longerbeam 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno));
561bce6f087SSteve Longerbeam 		break;
562bce6f087SSteve Longerbeam 	default:
563bce6f087SSteve Longerbeam 		break;
564bce6f087SSteve Longerbeam 	}
565bce6f087SSteve Longerbeam 	ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
566bce6f087SSteve Longerbeam 
567bce6f087SSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
568bce6f087SSteve Longerbeam }
569bce6f087SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer);
570bce6f087SSteve Longerbeam 
57139b9004dSPhilipp Zabel int ipu_idmac_enable_channel(struct ipuv3_channel *channel)
57239b9004dSPhilipp Zabel {
57339b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
57439b9004dSPhilipp Zabel 	u32 val;
57539b9004dSPhilipp Zabel 	unsigned long flags;
57639b9004dSPhilipp Zabel 
57739b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
57839b9004dSPhilipp Zabel 
57939b9004dSPhilipp Zabel 	val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
58039b9004dSPhilipp Zabel 	val |= idma_mask(channel->num);
58139b9004dSPhilipp Zabel 	ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
58239b9004dSPhilipp Zabel 
58339b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
58439b9004dSPhilipp Zabel 
58539b9004dSPhilipp Zabel 	return 0;
58639b9004dSPhilipp Zabel }
58739b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
58839b9004dSPhilipp Zabel 
589682b7c1cSLinus Torvalds bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno)
590682b7c1cSLinus Torvalds {
591682b7c1cSLinus Torvalds 	return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno));
592682b7c1cSLinus Torvalds }
593682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy);
594682b7c1cSLinus Torvalds 
59539b9004dSPhilipp Zabel int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms)
59639b9004dSPhilipp Zabel {
59739b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
59839b9004dSPhilipp Zabel 	unsigned long timeout;
59939b9004dSPhilipp Zabel 
60039b9004dSPhilipp Zabel 	timeout = jiffies + msecs_to_jiffies(ms);
60139b9004dSPhilipp Zabel 	while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) &
60239b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
60339b9004dSPhilipp Zabel 		if (time_after(jiffies, timeout))
60439b9004dSPhilipp Zabel 			return -ETIMEDOUT;
60539b9004dSPhilipp Zabel 		cpu_relax();
60639b9004dSPhilipp Zabel 	}
60739b9004dSPhilipp Zabel 
60839b9004dSPhilipp Zabel 	return 0;
60939b9004dSPhilipp Zabel }
61039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy);
61139b9004dSPhilipp Zabel 
61239b9004dSPhilipp Zabel int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
61339b9004dSPhilipp Zabel {
61439b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
61539b9004dSPhilipp Zabel 	u32 val;
61639b9004dSPhilipp Zabel 	unsigned long flags;
61739b9004dSPhilipp Zabel 
61839b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
61939b9004dSPhilipp Zabel 
62039b9004dSPhilipp Zabel 	/* Disable DMA channel(s) */
62139b9004dSPhilipp Zabel 	val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
62239b9004dSPhilipp Zabel 	val &= ~idma_mask(channel->num);
62339b9004dSPhilipp Zabel 	ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
62439b9004dSPhilipp Zabel 
625e7268c69SSteve Longerbeam 	__ipu_idmac_reset_current_buffer(channel);
626e7268c69SSteve Longerbeam 
62739b9004dSPhilipp Zabel 	/* Set channel buffers NOT to be ready */
62839b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */
62939b9004dSPhilipp Zabel 
63039b9004dSPhilipp Zabel 	if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) &
63139b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
63239b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(channel->num),
63339b9004dSPhilipp Zabel 			     IPU_CHA_BUF0_RDY(channel->num));
63439b9004dSPhilipp Zabel 	}
63539b9004dSPhilipp Zabel 
63639b9004dSPhilipp Zabel 	if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) &
63739b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
63839b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(channel->num),
63939b9004dSPhilipp Zabel 			     IPU_CHA_BUF1_RDY(channel->num));
64039b9004dSPhilipp Zabel 	}
64139b9004dSPhilipp Zabel 
64239b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
64339b9004dSPhilipp Zabel 
64439b9004dSPhilipp Zabel 	/* Reset the double buffer */
64539b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
64639b9004dSPhilipp Zabel 	val &= ~idma_mask(channel->num);
64739b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num));
64839b9004dSPhilipp Zabel 
64939b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
65039b9004dSPhilipp Zabel 
65139b9004dSPhilipp Zabel 	return 0;
65239b9004dSPhilipp Zabel }
65339b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel);
65439b9004dSPhilipp Zabel 
6552bcf577eSSteve Longerbeam /*
6562bcf577eSSteve Longerbeam  * The imx6 rev. D TRM says that enabling the WM feature will increase
6572bcf577eSSteve Longerbeam  * a channel's priority. Refer to Table 36-8 Calculated priority value.
6582bcf577eSSteve Longerbeam  * The sub-module that is the sink or source for the channel must enable
6592bcf577eSSteve Longerbeam  * watermark signal for this to take effect (SMFC_WM for instance).
6602bcf577eSSteve Longerbeam  */
6612bcf577eSSteve Longerbeam void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable)
6622bcf577eSSteve Longerbeam {
6632bcf577eSSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
6642bcf577eSSteve Longerbeam 	unsigned long flags;
6652bcf577eSSteve Longerbeam 	u32 val;
6662bcf577eSSteve Longerbeam 
6672bcf577eSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
6682bcf577eSSteve Longerbeam 
6692bcf577eSSteve Longerbeam 	val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num));
6702bcf577eSSteve Longerbeam 	if (enable)
6712bcf577eSSteve Longerbeam 		val |= 1 << (channel->num % 32);
6722bcf577eSSteve Longerbeam 	else
6732bcf577eSSteve Longerbeam 		val &= ~(1 << (channel->num % 32));
6742bcf577eSSteve Longerbeam 	ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num));
6752bcf577eSSteve Longerbeam 
6762bcf577eSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
6772bcf577eSSteve Longerbeam }
6782bcf577eSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark);
6792bcf577eSSteve Longerbeam 
68039b9004dSPhilipp Zabel static int ipu_memory_reset(struct ipu_soc *ipu)
68139b9004dSPhilipp Zabel {
68239b9004dSPhilipp Zabel 	unsigned long timeout;
68339b9004dSPhilipp Zabel 
68439b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST);
68539b9004dSPhilipp Zabel 
68639b9004dSPhilipp Zabel 	timeout = jiffies + msecs_to_jiffies(1000);
68739b9004dSPhilipp Zabel 	while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) {
68839b9004dSPhilipp Zabel 		if (time_after(jiffies, timeout))
68939b9004dSPhilipp Zabel 			return -ETIME;
69039b9004dSPhilipp Zabel 		cpu_relax();
69139b9004dSPhilipp Zabel 	}
69239b9004dSPhilipp Zabel 
69339b9004dSPhilipp Zabel 	return 0;
69439b9004dSPhilipp Zabel }
69539b9004dSPhilipp Zabel 
696ba07975fSSteve Longerbeam /*
697ba07975fSSteve Longerbeam  * Set the source mux for the given CSI. Selects either parallel or
698ba07975fSSteve Longerbeam  * MIPI CSI2 sources.
699ba07975fSSteve Longerbeam  */
700ba07975fSSteve Longerbeam void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2)
701ba07975fSSteve Longerbeam {
702ba07975fSSteve Longerbeam 	unsigned long flags;
703ba07975fSSteve Longerbeam 	u32 val, mask;
704ba07975fSSteve Longerbeam 
705ba07975fSSteve Longerbeam 	mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE :
706ba07975fSSteve Longerbeam 		IPU_CONF_CSI0_DATA_SOURCE;
707ba07975fSSteve Longerbeam 
708ba07975fSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
709ba07975fSSteve Longerbeam 
710ba07975fSSteve Longerbeam 	val = ipu_cm_read(ipu, IPU_CONF);
711ba07975fSSteve Longerbeam 	if (mipi_csi2)
712ba07975fSSteve Longerbeam 		val |= mask;
713ba07975fSSteve Longerbeam 	else
714ba07975fSSteve Longerbeam 		val &= ~mask;
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_csi_src_mux);
720ba07975fSSteve Longerbeam 
721ba07975fSSteve Longerbeam /*
722ba07975fSSteve Longerbeam  * Set the source mux for the IC. Selects either CSI[01] or the VDI.
723ba07975fSSteve Longerbeam  */
724ba07975fSSteve Longerbeam void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
725ba07975fSSteve Longerbeam {
726ba07975fSSteve Longerbeam 	unsigned long flags;
727ba07975fSSteve Longerbeam 	u32 val;
728ba07975fSSteve Longerbeam 
729ba07975fSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
730ba07975fSSteve Longerbeam 
731ba07975fSSteve Longerbeam 	val = ipu_cm_read(ipu, IPU_CONF);
732b7dfee24SMarek Vasut 	if (vdi)
733ba07975fSSteve Longerbeam 		val |= IPU_CONF_IC_INPUT;
734b7dfee24SMarek Vasut 	else
735ba07975fSSteve Longerbeam 		val &= ~IPU_CONF_IC_INPUT;
736b7dfee24SMarek Vasut 
737ba07975fSSteve Longerbeam 	if (csi_id == 1)
738ba07975fSSteve Longerbeam 		val |= IPU_CONF_CSI_SEL;
739ba07975fSSteve Longerbeam 	else
740ba07975fSSteve Longerbeam 		val &= ~IPU_CONF_CSI_SEL;
741b7dfee24SMarek Vasut 
742ba07975fSSteve Longerbeam 	ipu_cm_write(ipu, val, IPU_CONF);
743ba07975fSSteve Longerbeam 
744ba07975fSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
745ba07975fSSteve Longerbeam }
746ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
747ba07975fSSteve Longerbeam 
748ac4708faSSteve Longerbeam 
749ac4708faSSteve Longerbeam /* Frame Synchronization Unit Channel Linking */
750ac4708faSSteve Longerbeam 
751ac4708faSSteve Longerbeam struct fsu_link_reg_info {
752ac4708faSSteve Longerbeam 	int chno;
753ac4708faSSteve Longerbeam 	u32 reg;
754ac4708faSSteve Longerbeam 	u32 mask;
755ac4708faSSteve Longerbeam 	u32 val;
756ac4708faSSteve Longerbeam };
757ac4708faSSteve Longerbeam 
758ac4708faSSteve Longerbeam struct fsu_link_info {
759ac4708faSSteve Longerbeam 	struct fsu_link_reg_info src;
760ac4708faSSteve Longerbeam 	struct fsu_link_reg_info sink;
761ac4708faSSteve Longerbeam };
762ac4708faSSteve Longerbeam 
763ac4708faSSteve Longerbeam static const struct fsu_link_info fsu_link_info[] = {
764ac4708faSSteve Longerbeam 	{
765ac4708faSSteve Longerbeam 		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2,
766ac4708faSSteve Longerbeam 			  FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC },
767ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1,
768ac4708faSSteve Longerbeam 			  FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC },
769ac4708faSSteve Longerbeam 	}, {
770ac4708faSSteve Longerbeam 		.src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2,
771ac4708faSSteve Longerbeam 			  FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF },
772ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1,
773ac4708faSSteve Longerbeam 			  FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF },
774ac4708faSSteve Longerbeam 	}, {
775ac4708faSSteve Longerbeam 		.src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2,
776ac4708faSSteve Longerbeam 			  FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP },
777ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1,
778ac4708faSSteve Longerbeam 			  FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP },
779ac4708faSSteve Longerbeam 	}, {
780ac4708faSSteve Longerbeam 		.src =  { IPUV3_CHANNEL_CSI_DIRECT, 0 },
781ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1,
782ac4708faSSteve Longerbeam 			  FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT },
783ac4708faSSteve Longerbeam 	},
784ac4708faSSteve Longerbeam };
785ac4708faSSteve Longerbeam 
786ac4708faSSteve Longerbeam static const struct fsu_link_info *find_fsu_link_info(int src, int sink)
787ac4708faSSteve Longerbeam {
788ac4708faSSteve Longerbeam 	int i;
789ac4708faSSteve Longerbeam 
790ac4708faSSteve Longerbeam 	for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) {
791ac4708faSSteve Longerbeam 		if (src == fsu_link_info[i].src.chno &&
792ac4708faSSteve Longerbeam 		    sink == fsu_link_info[i].sink.chno)
793ac4708faSSteve Longerbeam 			return &fsu_link_info[i];
794ac4708faSSteve Longerbeam 	}
795ac4708faSSteve Longerbeam 
796ac4708faSSteve Longerbeam 	return NULL;
797ac4708faSSteve Longerbeam }
798ac4708faSSteve Longerbeam 
799ac4708faSSteve Longerbeam /*
800ac4708faSSteve Longerbeam  * Links a source channel to a sink channel in the FSU.
801ac4708faSSteve Longerbeam  */
802ac4708faSSteve Longerbeam int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch)
803ac4708faSSteve Longerbeam {
804ac4708faSSteve Longerbeam 	const struct fsu_link_info *link;
805ac4708faSSteve Longerbeam 	u32 src_reg, sink_reg;
806ac4708faSSteve Longerbeam 	unsigned long flags;
807ac4708faSSteve Longerbeam 
808ac4708faSSteve Longerbeam 	link = find_fsu_link_info(src_ch, sink_ch);
809ac4708faSSteve Longerbeam 	if (!link)
810ac4708faSSteve Longerbeam 		return -EINVAL;
811ac4708faSSteve Longerbeam 
812ac4708faSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
813ac4708faSSteve Longerbeam 
814ac4708faSSteve Longerbeam 	if (link->src.mask) {
815ac4708faSSteve Longerbeam 		src_reg = ipu_cm_read(ipu, link->src.reg);
816ac4708faSSteve Longerbeam 		src_reg &= ~link->src.mask;
817ac4708faSSteve Longerbeam 		src_reg |= link->src.val;
818ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, src_reg, link->src.reg);
819ac4708faSSteve Longerbeam 	}
820ac4708faSSteve Longerbeam 
821ac4708faSSteve Longerbeam 	if (link->sink.mask) {
822ac4708faSSteve Longerbeam 		sink_reg = ipu_cm_read(ipu, link->sink.reg);
823ac4708faSSteve Longerbeam 		sink_reg &= ~link->sink.mask;
824ac4708faSSteve Longerbeam 		sink_reg |= link->sink.val;
825ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, sink_reg, link->sink.reg);
826ac4708faSSteve Longerbeam 	}
827ac4708faSSteve Longerbeam 
828ac4708faSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
829ac4708faSSteve Longerbeam 	return 0;
830ac4708faSSteve Longerbeam }
831ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_link);
832ac4708faSSteve Longerbeam 
833ac4708faSSteve Longerbeam /*
834ac4708faSSteve Longerbeam  * Unlinks source and sink channels in the FSU.
835ac4708faSSteve Longerbeam  */
836ac4708faSSteve Longerbeam int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch)
837ac4708faSSteve Longerbeam {
838ac4708faSSteve Longerbeam 	const struct fsu_link_info *link;
839ac4708faSSteve Longerbeam 	u32 src_reg, sink_reg;
840ac4708faSSteve Longerbeam 	unsigned long flags;
841ac4708faSSteve Longerbeam 
842ac4708faSSteve Longerbeam 	link = find_fsu_link_info(src_ch, sink_ch);
843ac4708faSSteve Longerbeam 	if (!link)
844ac4708faSSteve Longerbeam 		return -EINVAL;
845ac4708faSSteve Longerbeam 
846ac4708faSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
847ac4708faSSteve Longerbeam 
848ac4708faSSteve Longerbeam 	if (link->src.mask) {
849ac4708faSSteve Longerbeam 		src_reg = ipu_cm_read(ipu, link->src.reg);
850ac4708faSSteve Longerbeam 		src_reg &= ~link->src.mask;
851ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, src_reg, link->src.reg);
852ac4708faSSteve Longerbeam 	}
853ac4708faSSteve Longerbeam 
854ac4708faSSteve Longerbeam 	if (link->sink.mask) {
855ac4708faSSteve Longerbeam 		sink_reg = ipu_cm_read(ipu, link->sink.reg);
856ac4708faSSteve Longerbeam 		sink_reg &= ~link->sink.mask;
857ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, sink_reg, link->sink.reg);
858ac4708faSSteve Longerbeam 	}
859ac4708faSSteve Longerbeam 
860ac4708faSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
861ac4708faSSteve Longerbeam 	return 0;
862ac4708faSSteve Longerbeam }
863ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_unlink);
864ac4708faSSteve Longerbeam 
865ac4708faSSteve Longerbeam /* Link IDMAC channels in the FSU */
866ac4708faSSteve Longerbeam int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)
867ac4708faSSteve Longerbeam {
868ac4708faSSteve Longerbeam 	return ipu_fsu_link(src->ipu, src->num, sink->num);
869ac4708faSSteve Longerbeam }
870ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_link);
871ac4708faSSteve Longerbeam 
872ac4708faSSteve Longerbeam /* Unlink IDMAC channels in the FSU */
873ac4708faSSteve Longerbeam int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink)
874ac4708faSSteve Longerbeam {
875ac4708faSSteve Longerbeam 	return ipu_fsu_unlink(src->ipu, src->num, sink->num);
876ac4708faSSteve Longerbeam }
877ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_unlink);
878ac4708faSSteve Longerbeam 
87939b9004dSPhilipp Zabel struct ipu_devtype {
88039b9004dSPhilipp Zabel 	const char *name;
88139b9004dSPhilipp Zabel 	unsigned long cm_ofs;
88239b9004dSPhilipp Zabel 	unsigned long cpmem_ofs;
88339b9004dSPhilipp Zabel 	unsigned long srm_ofs;
88439b9004dSPhilipp Zabel 	unsigned long tpm_ofs;
8852ffd48f2SSteve Longerbeam 	unsigned long csi0_ofs;
8862ffd48f2SSteve Longerbeam 	unsigned long csi1_ofs;
8871aa8ea0dSSteve Longerbeam 	unsigned long ic_ofs;
88839b9004dSPhilipp Zabel 	unsigned long disp0_ofs;
88939b9004dSPhilipp Zabel 	unsigned long disp1_ofs;
89039b9004dSPhilipp Zabel 	unsigned long dc_tmpl_ofs;
89139b9004dSPhilipp Zabel 	unsigned long vdi_ofs;
89239b9004dSPhilipp Zabel 	enum ipuv3_type type;
89339b9004dSPhilipp Zabel };
89439b9004dSPhilipp Zabel 
89539b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx51 = {
89639b9004dSPhilipp Zabel 	.name = "IPUv3EX",
89739b9004dSPhilipp Zabel 	.cm_ofs = 0x1e000000,
89839b9004dSPhilipp Zabel 	.cpmem_ofs = 0x1f000000,
89939b9004dSPhilipp Zabel 	.srm_ofs = 0x1f040000,
90039b9004dSPhilipp Zabel 	.tpm_ofs = 0x1f060000,
9012ffd48f2SSteve Longerbeam 	.csi0_ofs = 0x1f030000,
9022ffd48f2SSteve Longerbeam 	.csi1_ofs = 0x1f038000,
903a49e7c0dSPhilipp Zabel 	.ic_ofs = 0x1e020000,
90439b9004dSPhilipp Zabel 	.disp0_ofs = 0x1e040000,
90539b9004dSPhilipp Zabel 	.disp1_ofs = 0x1e048000,
90639b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x1f080000,
90739b9004dSPhilipp Zabel 	.vdi_ofs = 0x1e068000,
90839b9004dSPhilipp Zabel 	.type = IPUV3EX,
90939b9004dSPhilipp Zabel };
91039b9004dSPhilipp Zabel 
91139b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx53 = {
91239b9004dSPhilipp Zabel 	.name = "IPUv3M",
91339b9004dSPhilipp Zabel 	.cm_ofs = 0x06000000,
91439b9004dSPhilipp Zabel 	.cpmem_ofs = 0x07000000,
91539b9004dSPhilipp Zabel 	.srm_ofs = 0x07040000,
91639b9004dSPhilipp Zabel 	.tpm_ofs = 0x07060000,
9172ffd48f2SSteve Longerbeam 	.csi0_ofs = 0x07030000,
9182ffd48f2SSteve Longerbeam 	.csi1_ofs = 0x07038000,
919a49e7c0dSPhilipp Zabel 	.ic_ofs = 0x06020000,
92039b9004dSPhilipp Zabel 	.disp0_ofs = 0x06040000,
92139b9004dSPhilipp Zabel 	.disp1_ofs = 0x06048000,
92239b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x07080000,
92339b9004dSPhilipp Zabel 	.vdi_ofs = 0x06068000,
92439b9004dSPhilipp Zabel 	.type = IPUV3M,
92539b9004dSPhilipp Zabel };
92639b9004dSPhilipp Zabel 
92739b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx6q = {
92839b9004dSPhilipp Zabel 	.name = "IPUv3H",
92939b9004dSPhilipp Zabel 	.cm_ofs = 0x00200000,
93039b9004dSPhilipp Zabel 	.cpmem_ofs = 0x00300000,
93139b9004dSPhilipp Zabel 	.srm_ofs = 0x00340000,
93239b9004dSPhilipp Zabel 	.tpm_ofs = 0x00360000,
9332ffd48f2SSteve Longerbeam 	.csi0_ofs = 0x00230000,
9342ffd48f2SSteve Longerbeam 	.csi1_ofs = 0x00238000,
9351aa8ea0dSSteve Longerbeam 	.ic_ofs = 0x00220000,
93639b9004dSPhilipp Zabel 	.disp0_ofs = 0x00240000,
93739b9004dSPhilipp Zabel 	.disp1_ofs = 0x00248000,
93839b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x00380000,
93939b9004dSPhilipp Zabel 	.vdi_ofs = 0x00268000,
94039b9004dSPhilipp Zabel 	.type = IPUV3H,
94139b9004dSPhilipp Zabel };
94239b9004dSPhilipp Zabel 
94339b9004dSPhilipp Zabel static const struct of_device_id imx_ipu_dt_ids[] = {
94439b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, },
94539b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, },
94639b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, },
94792681fe7SLucas Stach 	{ .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6q, },
94839b9004dSPhilipp Zabel 	{ /* sentinel */ }
94939b9004dSPhilipp Zabel };
95039b9004dSPhilipp Zabel MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids);
95139b9004dSPhilipp Zabel 
95239b9004dSPhilipp Zabel static int ipu_submodules_init(struct ipu_soc *ipu,
95339b9004dSPhilipp Zabel 		struct platform_device *pdev, unsigned long ipu_base,
95439b9004dSPhilipp Zabel 		struct clk *ipu_clk)
95539b9004dSPhilipp Zabel {
95639b9004dSPhilipp Zabel 	char *unit;
95739b9004dSPhilipp Zabel 	int ret;
95839b9004dSPhilipp Zabel 	struct device *dev = &pdev->dev;
95939b9004dSPhilipp Zabel 	const struct ipu_devtype *devtype = ipu->devtype;
96039b9004dSPhilipp Zabel 
9617d2691daSSteve Longerbeam 	ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs);
9627d2691daSSteve Longerbeam 	if (ret) {
9637d2691daSSteve Longerbeam 		unit = "cpmem";
9647d2691daSSteve Longerbeam 		goto err_cpmem;
9657d2691daSSteve Longerbeam 	}
9667d2691daSSteve Longerbeam 
9672ffd48f2SSteve Longerbeam 	ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs,
9682ffd48f2SSteve Longerbeam 			   IPU_CONF_CSI0_EN, ipu_clk);
9692ffd48f2SSteve Longerbeam 	if (ret) {
9702ffd48f2SSteve Longerbeam 		unit = "csi0";
9712ffd48f2SSteve Longerbeam 		goto err_csi_0;
9722ffd48f2SSteve Longerbeam 	}
9732ffd48f2SSteve Longerbeam 
9742ffd48f2SSteve Longerbeam 	ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs,
9752ffd48f2SSteve Longerbeam 			   IPU_CONF_CSI1_EN, ipu_clk);
9762ffd48f2SSteve Longerbeam 	if (ret) {
9772ffd48f2SSteve Longerbeam 		unit = "csi1";
9782ffd48f2SSteve Longerbeam 		goto err_csi_1;
9792ffd48f2SSteve Longerbeam 	}
9802ffd48f2SSteve Longerbeam 
9811aa8ea0dSSteve Longerbeam 	ret = ipu_ic_init(ipu, dev,
9821aa8ea0dSSteve Longerbeam 			  ipu_base + devtype->ic_ofs,
9831aa8ea0dSSteve Longerbeam 			  ipu_base + devtype->tpm_ofs);
9841aa8ea0dSSteve Longerbeam 	if (ret) {
9851aa8ea0dSSteve Longerbeam 		unit = "ic";
9861aa8ea0dSSteve Longerbeam 		goto err_ic;
9871aa8ea0dSSteve Longerbeam 	}
9881aa8ea0dSSteve Longerbeam 
9892d2ead45SSteve Longerbeam 	ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs,
9902d2ead45SSteve Longerbeam 			   IPU_CONF_VDI_EN | IPU_CONF_ISP_EN |
9912d2ead45SSteve Longerbeam 			   IPU_CONF_IC_INPUT);
9922d2ead45SSteve Longerbeam 	if (ret) {
9932d2ead45SSteve Longerbeam 		unit = "vdi";
9942d2ead45SSteve Longerbeam 		goto err_vdi;
9952d2ead45SSteve Longerbeam 	}
9962d2ead45SSteve Longerbeam 
997cd98e85aSSteve Longerbeam 	ret = ipu_image_convert_init(ipu, dev);
998cd98e85aSSteve Longerbeam 	if (ret) {
999cd98e85aSSteve Longerbeam 		unit = "image_convert";
1000cd98e85aSSteve Longerbeam 		goto err_image_convert;
1001cd98e85aSSteve Longerbeam 	}
1002cd98e85aSSteve Longerbeam 
100339b9004dSPhilipp Zabel 	ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
100439b9004dSPhilipp Zabel 			  IPU_CONF_DI0_EN, ipu_clk);
100539b9004dSPhilipp Zabel 	if (ret) {
100639b9004dSPhilipp Zabel 		unit = "di0";
100739b9004dSPhilipp Zabel 		goto err_di_0;
100839b9004dSPhilipp Zabel 	}
100939b9004dSPhilipp Zabel 
101039b9004dSPhilipp Zabel 	ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs,
101139b9004dSPhilipp Zabel 			IPU_CONF_DI1_EN, ipu_clk);
101239b9004dSPhilipp Zabel 	if (ret) {
101339b9004dSPhilipp Zabel 		unit = "di1";
101439b9004dSPhilipp Zabel 		goto err_di_1;
101539b9004dSPhilipp Zabel 	}
101639b9004dSPhilipp Zabel 
101739b9004dSPhilipp Zabel 	ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs +
101839b9004dSPhilipp Zabel 			IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs);
101939b9004dSPhilipp Zabel 	if (ret) {
102039b9004dSPhilipp Zabel 		unit = "dc_template";
102139b9004dSPhilipp Zabel 		goto err_dc;
102239b9004dSPhilipp Zabel 	}
102339b9004dSPhilipp Zabel 
102439b9004dSPhilipp Zabel 	ret = ipu_dmfc_init(ipu, dev, ipu_base +
102539b9004dSPhilipp Zabel 			devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk);
102639b9004dSPhilipp Zabel 	if (ret) {
102739b9004dSPhilipp Zabel 		unit = "dmfc";
102839b9004dSPhilipp Zabel 		goto err_dmfc;
102939b9004dSPhilipp Zabel 	}
103039b9004dSPhilipp Zabel 
103139b9004dSPhilipp Zabel 	ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs);
103239b9004dSPhilipp Zabel 	if (ret) {
103339b9004dSPhilipp Zabel 		unit = "dp";
103439b9004dSPhilipp Zabel 		goto err_dp;
103539b9004dSPhilipp Zabel 	}
103639b9004dSPhilipp Zabel 
103735de925fSPhilipp Zabel 	ret = ipu_smfc_init(ipu, dev, ipu_base +
103835de925fSPhilipp Zabel 			devtype->cm_ofs + IPU_CM_SMFC_REG_OFS);
103935de925fSPhilipp Zabel 	if (ret) {
104035de925fSPhilipp Zabel 		unit = "smfc";
104135de925fSPhilipp Zabel 		goto err_smfc;
104235de925fSPhilipp Zabel 	}
104335de925fSPhilipp Zabel 
104439b9004dSPhilipp Zabel 	return 0;
104539b9004dSPhilipp Zabel 
104635de925fSPhilipp Zabel err_smfc:
104735de925fSPhilipp Zabel 	ipu_dp_exit(ipu);
104839b9004dSPhilipp Zabel err_dp:
104939b9004dSPhilipp Zabel 	ipu_dmfc_exit(ipu);
105039b9004dSPhilipp Zabel err_dmfc:
105139b9004dSPhilipp Zabel 	ipu_dc_exit(ipu);
105239b9004dSPhilipp Zabel err_dc:
105339b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 1);
105439b9004dSPhilipp Zabel err_di_1:
105539b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 0);
105639b9004dSPhilipp Zabel err_di_0:
1057cd98e85aSSteve Longerbeam 	ipu_image_convert_exit(ipu);
1058cd98e85aSSteve Longerbeam err_image_convert:
10592d2ead45SSteve Longerbeam 	ipu_vdi_exit(ipu);
10602d2ead45SSteve Longerbeam err_vdi:
10611aa8ea0dSSteve Longerbeam 	ipu_ic_exit(ipu);
10621aa8ea0dSSteve Longerbeam err_ic:
10632ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 1);
10642ffd48f2SSteve Longerbeam err_csi_1:
10652ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 0);
10662ffd48f2SSteve Longerbeam err_csi_0:
10677d2691daSSteve Longerbeam 	ipu_cpmem_exit(ipu);
10687d2691daSSteve Longerbeam err_cpmem:
106939b9004dSPhilipp Zabel 	dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret);
107039b9004dSPhilipp Zabel 	return ret;
107139b9004dSPhilipp Zabel }
107239b9004dSPhilipp Zabel 
107339b9004dSPhilipp Zabel static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs)
107439b9004dSPhilipp Zabel {
107539b9004dSPhilipp Zabel 	unsigned long status;
107639b9004dSPhilipp Zabel 	int i, bit, irq;
107739b9004dSPhilipp Zabel 
107839b9004dSPhilipp Zabel 	for (i = 0; i < num_regs; i++) {
107939b9004dSPhilipp Zabel 
108039b9004dSPhilipp Zabel 		status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i]));
108139b9004dSPhilipp Zabel 		status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i]));
108239b9004dSPhilipp Zabel 
108339b9004dSPhilipp Zabel 		for_each_set_bit(bit, &status, 32) {
1084682b7c1cSLinus Torvalds 			irq = irq_linear_revmap(ipu->domain,
1085682b7c1cSLinus Torvalds 						regs[i] * 32 + bit);
108639b9004dSPhilipp Zabel 			if (irq)
108739b9004dSPhilipp Zabel 				generic_handle_irq(irq);
108839b9004dSPhilipp Zabel 		}
108939b9004dSPhilipp Zabel 	}
109039b9004dSPhilipp Zabel }
109139b9004dSPhilipp Zabel 
1092bd0b9ac4SThomas Gleixner static void ipu_irq_handler(struct irq_desc *desc)
109339b9004dSPhilipp Zabel {
109439b9004dSPhilipp Zabel 	struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
10954d9efdfcSJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
1096ac66b834SColin Ian King 	static const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14};
109739b9004dSPhilipp Zabel 
109839b9004dSPhilipp Zabel 	chained_irq_enter(chip, desc);
109939b9004dSPhilipp Zabel 
110039b9004dSPhilipp Zabel 	ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
110139b9004dSPhilipp Zabel 
110239b9004dSPhilipp Zabel 	chained_irq_exit(chip, desc);
110339b9004dSPhilipp Zabel }
110439b9004dSPhilipp Zabel 
1105bd0b9ac4SThomas Gleixner static void ipu_err_irq_handler(struct irq_desc *desc)
110639b9004dSPhilipp Zabel {
110739b9004dSPhilipp Zabel 	struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
11084d9efdfcSJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
1109ac66b834SColin Ian King 	static const int int_reg[] = { 4, 5, 8, 9};
111039b9004dSPhilipp Zabel 
111139b9004dSPhilipp Zabel 	chained_irq_enter(chip, desc);
111239b9004dSPhilipp Zabel 
111339b9004dSPhilipp Zabel 	ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
111439b9004dSPhilipp Zabel 
111539b9004dSPhilipp Zabel 	chained_irq_exit(chip, desc);
111639b9004dSPhilipp Zabel }
111739b9004dSPhilipp Zabel 
1118682b7c1cSLinus Torvalds int ipu_map_irq(struct ipu_soc *ipu, int irq)
1119682b7c1cSLinus Torvalds {
1120682b7c1cSLinus Torvalds 	int virq;
1121682b7c1cSLinus Torvalds 
1122682b7c1cSLinus Torvalds 	virq = irq_linear_revmap(ipu->domain, irq);
1123682b7c1cSLinus Torvalds 	if (!virq)
1124682b7c1cSLinus Torvalds 		virq = irq_create_mapping(ipu->domain, irq);
1125682b7c1cSLinus Torvalds 
1126682b7c1cSLinus Torvalds 	return virq;
1127682b7c1cSLinus Torvalds }
1128682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_map_irq);
1129682b7c1cSLinus Torvalds 
113039b9004dSPhilipp Zabel int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
113139b9004dSPhilipp Zabel 		enum ipu_channel_irq irq_type)
113239b9004dSPhilipp Zabel {
1133682b7c1cSLinus Torvalds 	return ipu_map_irq(ipu, irq_type + channel->num);
113439b9004dSPhilipp Zabel }
113539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq);
113639b9004dSPhilipp Zabel 
113739b9004dSPhilipp Zabel static void ipu_submodules_exit(struct ipu_soc *ipu)
113839b9004dSPhilipp Zabel {
113935de925fSPhilipp Zabel 	ipu_smfc_exit(ipu);
114039b9004dSPhilipp Zabel 	ipu_dp_exit(ipu);
114139b9004dSPhilipp Zabel 	ipu_dmfc_exit(ipu);
114239b9004dSPhilipp Zabel 	ipu_dc_exit(ipu);
114339b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 1);
114439b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 0);
1145cd98e85aSSteve Longerbeam 	ipu_image_convert_exit(ipu);
11462d2ead45SSteve Longerbeam 	ipu_vdi_exit(ipu);
11471aa8ea0dSSteve Longerbeam 	ipu_ic_exit(ipu);
11482ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 1);
11492ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 0);
11507d2691daSSteve Longerbeam 	ipu_cpmem_exit(ipu);
115139b9004dSPhilipp Zabel }
115239b9004dSPhilipp Zabel 
115339b9004dSPhilipp Zabel static int platform_remove_devices_fn(struct device *dev, void *unused)
115439b9004dSPhilipp Zabel {
115539b9004dSPhilipp Zabel 	struct platform_device *pdev = to_platform_device(dev);
115639b9004dSPhilipp Zabel 
115739b9004dSPhilipp Zabel 	platform_device_unregister(pdev);
115839b9004dSPhilipp Zabel 
115939b9004dSPhilipp Zabel 	return 0;
116039b9004dSPhilipp Zabel }
116139b9004dSPhilipp Zabel 
116239b9004dSPhilipp Zabel static void platform_device_unregister_children(struct platform_device *pdev)
116339b9004dSPhilipp Zabel {
116439b9004dSPhilipp Zabel 	device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn);
116539b9004dSPhilipp Zabel }
116639b9004dSPhilipp Zabel 
116739b9004dSPhilipp Zabel struct ipu_platform_reg {
116839b9004dSPhilipp Zabel 	struct ipu_client_platformdata pdata;
116939b9004dSPhilipp Zabel 	const char *name;
117039b9004dSPhilipp Zabel };
117139b9004dSPhilipp Zabel 
1172304e6be6SPhilipp Zabel /* These must be in the order of the corresponding device tree port nodes */
1173310944d1SPhilipp Zabel static struct ipu_platform_reg client_reg[] = {
117439b9004dSPhilipp Zabel 	{
117539b9004dSPhilipp Zabel 		.pdata = {
1176304e6be6SPhilipp Zabel 			.csi = 0,
1177304e6be6SPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_CSI0,
1178304e6be6SPhilipp Zabel 			.dma[1] = -EINVAL,
1179304e6be6SPhilipp Zabel 		},
118088287ec3SSteve Longerbeam 		.name = "imx-ipuv3-csi",
1181304e6be6SPhilipp Zabel 	}, {
1182304e6be6SPhilipp Zabel 		.pdata = {
1183304e6be6SPhilipp Zabel 			.csi = 1,
1184304e6be6SPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_CSI1,
1185304e6be6SPhilipp Zabel 			.dma[1] = -EINVAL,
1186304e6be6SPhilipp Zabel 		},
118788287ec3SSteve Longerbeam 		.name = "imx-ipuv3-csi",
1188304e6be6SPhilipp Zabel 	}, {
1189304e6be6SPhilipp Zabel 		.pdata = {
119039b9004dSPhilipp Zabel 			.di = 0,
119139b9004dSPhilipp Zabel 			.dc = 5,
119239b9004dSPhilipp Zabel 			.dp = IPU_DP_FLOW_SYNC_BG,
119339b9004dSPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC,
119439b9004dSPhilipp Zabel 			.dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC,
119539b9004dSPhilipp Zabel 		},
119639b9004dSPhilipp Zabel 		.name = "imx-ipuv3-crtc",
119739b9004dSPhilipp Zabel 	}, {
119839b9004dSPhilipp Zabel 		.pdata = {
119939b9004dSPhilipp Zabel 			.di = 1,
120039b9004dSPhilipp Zabel 			.dc = 1,
120139b9004dSPhilipp Zabel 			.dp = -EINVAL,
120239b9004dSPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC,
120339b9004dSPhilipp Zabel 			.dma[1] = -EINVAL,
120439b9004dSPhilipp Zabel 		},
120539b9004dSPhilipp Zabel 		.name = "imx-ipuv3-crtc",
120639b9004dSPhilipp Zabel 	},
120739b9004dSPhilipp Zabel };
120839b9004dSPhilipp Zabel 
120939b9004dSPhilipp Zabel static DEFINE_MUTEX(ipu_client_id_mutex);
121039b9004dSPhilipp Zabel static int ipu_client_id;
121139b9004dSPhilipp Zabel 
1212d6ca8ca7SPhilipp Zabel static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base)
121339b9004dSPhilipp Zabel {
121439b9004dSPhilipp Zabel 	struct device *dev = ipu->dev;
121539b9004dSPhilipp Zabel 	unsigned i;
121639b9004dSPhilipp Zabel 	int id, ret;
121739b9004dSPhilipp Zabel 
121839b9004dSPhilipp Zabel 	mutex_lock(&ipu_client_id_mutex);
121939b9004dSPhilipp Zabel 	id = ipu_client_id;
122039b9004dSPhilipp Zabel 	ipu_client_id += ARRAY_SIZE(client_reg);
122139b9004dSPhilipp Zabel 	mutex_unlock(&ipu_client_id_mutex);
122239b9004dSPhilipp Zabel 
122339b9004dSPhilipp Zabel 	for (i = 0; i < ARRAY_SIZE(client_reg); i++) {
1224310944d1SPhilipp Zabel 		struct ipu_platform_reg *reg = &client_reg[i];
122539b9004dSPhilipp Zabel 		struct platform_device *pdev;
122617e05217SPhilipp Zabel 		struct device_node *of_node;
122717e05217SPhilipp Zabel 
122817e05217SPhilipp Zabel 		/* Associate subdevice with the corresponding port node */
122917e05217SPhilipp Zabel 		of_node = of_graph_get_port_by_id(dev->of_node, i);
123017e05217SPhilipp Zabel 		if (!of_node) {
123117e05217SPhilipp Zabel 			dev_info(dev,
12324bf99144SRob Herring 				 "no port@%d node in %pOF, not using %s%d\n",
12334bf99144SRob Herring 				 i, dev->of_node,
123417e05217SPhilipp Zabel 				 (i / 2) ? "DI" : "CSI", i % 2);
123517e05217SPhilipp Zabel 			continue;
123617e05217SPhilipp Zabel 		}
123739b9004dSPhilipp Zabel 
1238304e6be6SPhilipp Zabel 		pdev = platform_device_alloc(reg->name, id++);
1239304e6be6SPhilipp Zabel 		if (!pdev) {
1240304e6be6SPhilipp Zabel 			ret = -ENOMEM;
1241304e6be6SPhilipp Zabel 			goto err_register;
1242304e6be6SPhilipp Zabel 		}
124339b9004dSPhilipp Zabel 
1244304e6be6SPhilipp Zabel 		pdev->dev.parent = dev;
1245304e6be6SPhilipp Zabel 
1246310944d1SPhilipp Zabel 		reg->pdata.of_node = of_node;
1247304e6be6SPhilipp Zabel 		ret = platform_device_add_data(pdev, &reg->pdata,
1248304e6be6SPhilipp Zabel 					       sizeof(reg->pdata));
1249304e6be6SPhilipp Zabel 		if (!ret)
1250304e6be6SPhilipp Zabel 			ret = platform_device_add(pdev);
1251304e6be6SPhilipp Zabel 		if (ret) {
1252304e6be6SPhilipp Zabel 			platform_device_put(pdev);
125339b9004dSPhilipp Zabel 			goto err_register;
125439b9004dSPhilipp Zabel 		}
1255e4946cdcSAxel Lin 	}
125639b9004dSPhilipp Zabel 
125739b9004dSPhilipp Zabel 	return 0;
125839b9004dSPhilipp Zabel 
125939b9004dSPhilipp Zabel err_register:
126039b9004dSPhilipp Zabel 	platform_device_unregister_children(to_platform_device(dev));
126139b9004dSPhilipp Zabel 
126239b9004dSPhilipp Zabel 	return ret;
126339b9004dSPhilipp Zabel }
126439b9004dSPhilipp Zabel 
126539b9004dSPhilipp Zabel 
126639b9004dSPhilipp Zabel static int ipu_irq_init(struct ipu_soc *ipu)
126739b9004dSPhilipp Zabel {
126839b9004dSPhilipp Zabel 	struct irq_chip_generic *gc;
126939b9004dSPhilipp Zabel 	struct irq_chip_type *ct;
127039b9004dSPhilipp Zabel 	unsigned long unused[IPU_NUM_IRQS / 32] = {
127139b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
127239b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
127339b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
127439b9004dSPhilipp Zabel 		0x4077ffff, 0xffe7e1fd,
127539b9004dSPhilipp Zabel 		0x23fffffe, 0x8880fff0,
127639b9004dSPhilipp Zabel 		0xf98fe7d0, 0xfff81fff,
127739b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
127839b9004dSPhilipp Zabel 		0x00000000,
127939b9004dSPhilipp Zabel 	};
128039b9004dSPhilipp Zabel 	int ret, i;
128139b9004dSPhilipp Zabel 
128239b9004dSPhilipp Zabel 	ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS,
128339b9004dSPhilipp Zabel 					    &irq_generic_chip_ops, ipu);
128439b9004dSPhilipp Zabel 	if (!ipu->domain) {
128539b9004dSPhilipp Zabel 		dev_err(ipu->dev, "failed to add irq domain\n");
128639b9004dSPhilipp Zabel 		return -ENODEV;
128739b9004dSPhilipp Zabel 	}
128839b9004dSPhilipp Zabel 
128939b9004dSPhilipp Zabel 	ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU",
1290ca0141deSRob Herring 					     handle_level_irq, 0, 0, 0);
129139b9004dSPhilipp Zabel 	if (ret < 0) {
129239b9004dSPhilipp Zabel 		dev_err(ipu->dev, "failed to alloc generic irq chips\n");
129339b9004dSPhilipp Zabel 		irq_domain_remove(ipu->domain);
129439b9004dSPhilipp Zabel 		return ret;
129539b9004dSPhilipp Zabel 	}
129639b9004dSPhilipp Zabel 
1297a92d8145SPhilipp Zabel 	/* Mask and clear all interrupts */
1298a92d8145SPhilipp Zabel 	for (i = 0; i < IPU_NUM_IRQS; i += 32) {
1299510e6426SRussell King 		ipu_cm_write(ipu, 0, IPU_INT_CTRL(i / 32));
1300a92d8145SPhilipp Zabel 		ipu_cm_write(ipu, ~unused[i / 32], IPU_INT_STAT(i / 32));
1301a92d8145SPhilipp Zabel 	}
1302510e6426SRussell King 
130339b9004dSPhilipp Zabel 	for (i = 0; i < IPU_NUM_IRQS; i += 32) {
130439b9004dSPhilipp Zabel 		gc = irq_get_domain_generic_chip(ipu->domain, i);
130539b9004dSPhilipp Zabel 		gc->reg_base = ipu->cm_reg;
130639b9004dSPhilipp Zabel 		gc->unused = unused[i / 32];
130739b9004dSPhilipp Zabel 		ct = gc->chip_types;
130839b9004dSPhilipp Zabel 		ct->chip.irq_ack = irq_gc_ack_set_bit;
130939b9004dSPhilipp Zabel 		ct->chip.irq_mask = irq_gc_mask_clr_bit;
131039b9004dSPhilipp Zabel 		ct->chip.irq_unmask = irq_gc_mask_set_bit;
131139b9004dSPhilipp Zabel 		ct->regs.ack = IPU_INT_STAT(i / 32);
131239b9004dSPhilipp Zabel 		ct->regs.mask = IPU_INT_CTRL(i / 32);
131339b9004dSPhilipp Zabel 	}
131439b9004dSPhilipp Zabel 
131586f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu);
131686f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler,
131786f5e733SRussell King 					 ipu);
131839b9004dSPhilipp Zabel 
131939b9004dSPhilipp Zabel 	return 0;
132039b9004dSPhilipp Zabel }
132139b9004dSPhilipp Zabel 
132239b9004dSPhilipp Zabel static void ipu_irq_exit(struct ipu_soc *ipu)
132339b9004dSPhilipp Zabel {
132439b9004dSPhilipp Zabel 	int i, irq;
132539b9004dSPhilipp Zabel 
132686f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL);
132786f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL);
132839b9004dSPhilipp Zabel 
132939b9004dSPhilipp Zabel 	/* TODO: remove irq_domain_generic_chips */
133039b9004dSPhilipp Zabel 
133139b9004dSPhilipp Zabel 	for (i = 0; i < IPU_NUM_IRQS; i++) {
133239b9004dSPhilipp Zabel 		irq = irq_linear_revmap(ipu->domain, i);
133339b9004dSPhilipp Zabel 		if (irq)
133439b9004dSPhilipp Zabel 			irq_dispose_mapping(irq);
133539b9004dSPhilipp Zabel 	}
133639b9004dSPhilipp Zabel 
133739b9004dSPhilipp Zabel 	irq_domain_remove(ipu->domain);
133839b9004dSPhilipp Zabel }
133939b9004dSPhilipp Zabel 
13403feb049fSSteve Longerbeam void ipu_dump(struct ipu_soc *ipu)
13413feb049fSSteve Longerbeam {
13423feb049fSSteve Longerbeam 	int i;
13433feb049fSSteve Longerbeam 
13443feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n",
13453feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_CONF));
13463feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n",
13473feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CONF));
13483feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n",
13493feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_EN(0)));
13503feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n",
13513feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_EN(32)));
13523feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n",
13533feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_PRI(0)));
13543feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n",
13553feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_PRI(32)));
13563feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n",
13573feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_BAND_EN(0)));
13583feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n",
13593feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_BAND_EN(32)));
13603feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n",
13613feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0)));
13623feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n",
13633feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32)));
13643feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n",
13653feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_PROC_FLOW1));
13663feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n",
13673feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_PROC_FLOW2));
13683feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n",
13693feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_PROC_FLOW3));
13703feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n",
13713feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_DISP_FLOW1));
13723feb049fSSteve Longerbeam 	for (i = 0; i < 15; i++)
13733feb049fSSteve Longerbeam 		dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i,
13743feb049fSSteve Longerbeam 			ipu_cm_read(ipu, IPU_INT_CTRL(i)));
13753feb049fSSteve Longerbeam }
13763feb049fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_dump);
13773feb049fSSteve Longerbeam 
137839b9004dSPhilipp Zabel static int ipu_probe(struct platform_device *pdev)
137939b9004dSPhilipp Zabel {
1380572a7615SSteve Longerbeam 	struct device_node *np = pdev->dev.of_node;
138139b9004dSPhilipp Zabel 	struct ipu_soc *ipu;
138239b9004dSPhilipp Zabel 	struct resource *res;
138339b9004dSPhilipp Zabel 	unsigned long ipu_base;
138493adc8b5SPhilipp Zabel 	int ret, irq_sync, irq_err;
138539b9004dSPhilipp Zabel 	const struct ipu_devtype *devtype;
138639b9004dSPhilipp Zabel 
1387e92e4478SLABBE Corentin 	devtype = of_device_get_match_data(&pdev->dev);
1388e92e4478SLABBE Corentin 	if (!devtype)
1389e92e4478SLABBE Corentin 		return -EINVAL;
139039b9004dSPhilipp Zabel 
139139b9004dSPhilipp Zabel 	irq_sync = platform_get_irq(pdev, 0);
139239b9004dSPhilipp Zabel 	irq_err = platform_get_irq(pdev, 1);
139339b9004dSPhilipp Zabel 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
139439b9004dSPhilipp Zabel 
139539b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n",
139639b9004dSPhilipp Zabel 			irq_sync, irq_err);
139739b9004dSPhilipp Zabel 
139839b9004dSPhilipp Zabel 	if (!res || irq_sync < 0 || irq_err < 0)
139939b9004dSPhilipp Zabel 		return -ENODEV;
140039b9004dSPhilipp Zabel 
140139b9004dSPhilipp Zabel 	ipu_base = res->start;
140239b9004dSPhilipp Zabel 
140339b9004dSPhilipp Zabel 	ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL);
140439b9004dSPhilipp Zabel 	if (!ipu)
140539b9004dSPhilipp Zabel 		return -ENODEV;
140639b9004dSPhilipp Zabel 
140792681fe7SLucas Stach 	ipu->id = of_alias_get_id(np, "ipu");
140892681fe7SLucas Stach 
140930310c83SLucas Stach 	if (of_device_is_compatible(np, "fsl,imx6qp-ipu") &&
141030310c83SLucas Stach 	    IS_ENABLED(CONFIG_DRM)) {
141192681fe7SLucas Stach 		ipu->prg_priv = ipu_prg_lookup_by_phandle(&pdev->dev,
141292681fe7SLucas Stach 							  "fsl,prg", ipu->id);
141392681fe7SLucas Stach 		if (!ipu->prg_priv)
141492681fe7SLucas Stach 			return -EPROBE_DEFER;
141592681fe7SLucas Stach 	}
141692681fe7SLucas Stach 
141739b9004dSPhilipp Zabel 	ipu->devtype = devtype;
141839b9004dSPhilipp Zabel 	ipu->ipu_type = devtype->type;
141939b9004dSPhilipp Zabel 
142039b9004dSPhilipp Zabel 	spin_lock_init(&ipu->lock);
142139b9004dSPhilipp Zabel 	mutex_init(&ipu->channel_lock);
142293adc8b5SPhilipp Zabel 	INIT_LIST_HEAD(&ipu->channels);
142339b9004dSPhilipp Zabel 
142439b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "cm_reg:   0x%08lx\n",
142539b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs);
142639b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "idmac:    0x%08lx\n",
142739b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS);
142839b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "cpmem:    0x%08lx\n",
142939b9004dSPhilipp Zabel 			ipu_base + devtype->cpmem_ofs);
14302ffd48f2SSteve Longerbeam 	dev_dbg(&pdev->dev, "csi0:    0x%08lx\n",
14312ffd48f2SSteve Longerbeam 			ipu_base + devtype->csi0_ofs);
14322ffd48f2SSteve Longerbeam 	dev_dbg(&pdev->dev, "csi1:    0x%08lx\n",
14332ffd48f2SSteve Longerbeam 			ipu_base + devtype->csi1_ofs);
14341aa8ea0dSSteve Longerbeam 	dev_dbg(&pdev->dev, "ic:      0x%08lx\n",
14351aa8ea0dSSteve Longerbeam 			ipu_base + devtype->ic_ofs);
143639b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "disp0:    0x%08lx\n",
143739b9004dSPhilipp Zabel 			ipu_base + devtype->disp0_ofs);
143839b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "disp1:    0x%08lx\n",
143939b9004dSPhilipp Zabel 			ipu_base + devtype->disp1_ofs);
144039b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "srm:      0x%08lx\n",
144139b9004dSPhilipp Zabel 			ipu_base + devtype->srm_ofs);
144239b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "tpm:      0x%08lx\n",
144339b9004dSPhilipp Zabel 			ipu_base + devtype->tpm_ofs);
144439b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "dc:       0x%08lx\n",
144539b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS);
144639b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "ic:       0x%08lx\n",
144739b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS);
144839b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "dmfc:     0x%08lx\n",
144939b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS);
145039b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "vdi:      0x%08lx\n",
145139b9004dSPhilipp Zabel 			ipu_base + devtype->vdi_ofs);
145239b9004dSPhilipp Zabel 
145339b9004dSPhilipp Zabel 	ipu->cm_reg = devm_ioremap(&pdev->dev,
145439b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs, PAGE_SIZE);
145539b9004dSPhilipp Zabel 	ipu->idmac_reg = devm_ioremap(&pdev->dev,
145639b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS,
145739b9004dSPhilipp Zabel 			PAGE_SIZE);
145839b9004dSPhilipp Zabel 
14597d2691daSSteve Longerbeam 	if (!ipu->cm_reg || !ipu->idmac_reg)
146039b9004dSPhilipp Zabel 		return -ENOMEM;
146139b9004dSPhilipp Zabel 
146239b9004dSPhilipp Zabel 	ipu->clk = devm_clk_get(&pdev->dev, "bus");
146339b9004dSPhilipp Zabel 	if (IS_ERR(ipu->clk)) {
146439b9004dSPhilipp Zabel 		ret = PTR_ERR(ipu->clk);
146539b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "clk_get failed with %d", ret);
146639b9004dSPhilipp Zabel 		return ret;
146739b9004dSPhilipp Zabel 	}
146839b9004dSPhilipp Zabel 
146939b9004dSPhilipp Zabel 	platform_set_drvdata(pdev, ipu);
147039b9004dSPhilipp Zabel 
147139b9004dSPhilipp Zabel 	ret = clk_prepare_enable(ipu->clk);
147239b9004dSPhilipp Zabel 	if (ret) {
147339b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
147439b9004dSPhilipp Zabel 		return ret;
147539b9004dSPhilipp Zabel 	}
147639b9004dSPhilipp Zabel 
147739b9004dSPhilipp Zabel 	ipu->dev = &pdev->dev;
147839b9004dSPhilipp Zabel 	ipu->irq_sync = irq_sync;
147939b9004dSPhilipp Zabel 	ipu->irq_err = irq_err;
148039b9004dSPhilipp Zabel 
148139b9004dSPhilipp Zabel 	ret = device_reset(&pdev->dev);
148239b9004dSPhilipp Zabel 	if (ret) {
148339b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "failed to reset: %d\n", ret);
148439b9004dSPhilipp Zabel 		goto out_failed_reset;
148539b9004dSPhilipp Zabel 	}
148639b9004dSPhilipp Zabel 	ret = ipu_memory_reset(ipu);
148739b9004dSPhilipp Zabel 	if (ret)
148839b9004dSPhilipp Zabel 		goto out_failed_reset;
148939b9004dSPhilipp Zabel 
1490596a65d1SDavid Jander 	ret = ipu_irq_init(ipu);
1491596a65d1SDavid Jander 	if (ret)
1492596a65d1SDavid Jander 		goto out_failed_irq;
1493596a65d1SDavid Jander 
149439b9004dSPhilipp Zabel 	/* Set MCU_T to divide MCU access window into 2 */
149539b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
149639b9004dSPhilipp Zabel 			IPU_DISP_GEN);
149739b9004dSPhilipp Zabel 
149839b9004dSPhilipp Zabel 	ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk);
149939b9004dSPhilipp Zabel 	if (ret)
150039b9004dSPhilipp Zabel 		goto failed_submodules_init;
150139b9004dSPhilipp Zabel 
1502d6ca8ca7SPhilipp Zabel 	ret = ipu_add_client_devices(ipu, ipu_base);
150339b9004dSPhilipp Zabel 	if (ret) {
150439b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "adding client devices failed with %d\n",
150539b9004dSPhilipp Zabel 				ret);
150639b9004dSPhilipp Zabel 		goto failed_add_clients;
150739b9004dSPhilipp Zabel 	}
150839b9004dSPhilipp Zabel 
150939b9004dSPhilipp Zabel 	dev_info(&pdev->dev, "%s probed\n", devtype->name);
151039b9004dSPhilipp Zabel 
151139b9004dSPhilipp Zabel 	return 0;
151239b9004dSPhilipp Zabel 
151339b9004dSPhilipp Zabel failed_add_clients:
151439b9004dSPhilipp Zabel 	ipu_submodules_exit(ipu);
151539b9004dSPhilipp Zabel failed_submodules_init:
151639b9004dSPhilipp Zabel 	ipu_irq_exit(ipu);
151739b9004dSPhilipp Zabel out_failed_irq:
1518596a65d1SDavid Jander out_failed_reset:
151939b9004dSPhilipp Zabel 	clk_disable_unprepare(ipu->clk);
152039b9004dSPhilipp Zabel 	return ret;
152139b9004dSPhilipp Zabel }
152239b9004dSPhilipp Zabel 
152339b9004dSPhilipp Zabel static int ipu_remove(struct platform_device *pdev)
152439b9004dSPhilipp Zabel {
152539b9004dSPhilipp Zabel 	struct ipu_soc *ipu = platform_get_drvdata(pdev);
152639b9004dSPhilipp Zabel 
152739b9004dSPhilipp Zabel 	platform_device_unregister_children(pdev);
152839b9004dSPhilipp Zabel 	ipu_submodules_exit(ipu);
152939b9004dSPhilipp Zabel 	ipu_irq_exit(ipu);
153039b9004dSPhilipp Zabel 
153139b9004dSPhilipp Zabel 	clk_disable_unprepare(ipu->clk);
153239b9004dSPhilipp Zabel 
153339b9004dSPhilipp Zabel 	return 0;
153439b9004dSPhilipp Zabel }
153539b9004dSPhilipp Zabel 
153639b9004dSPhilipp Zabel static struct platform_driver imx_ipu_driver = {
153739b9004dSPhilipp Zabel 	.driver = {
153839b9004dSPhilipp Zabel 		.name = "imx-ipuv3",
153939b9004dSPhilipp Zabel 		.of_match_table = imx_ipu_dt_ids,
154039b9004dSPhilipp Zabel 	},
154139b9004dSPhilipp Zabel 	.probe = ipu_probe,
154239b9004dSPhilipp Zabel 	.remove = ipu_remove,
154339b9004dSPhilipp Zabel };
154439b9004dSPhilipp Zabel 
1545d2a34232SLucas Stach static struct platform_driver * const drivers[] = {
154630310c83SLucas Stach #if IS_ENABLED(CONFIG_DRM)
1547d2a34232SLucas Stach 	&ipu_pre_drv,
1548ea9c2605SLucas Stach 	&ipu_prg_drv,
154930310c83SLucas Stach #endif
1550d2a34232SLucas Stach 	&imx_ipu_driver,
1551d2a34232SLucas Stach };
1552d2a34232SLucas Stach 
1553d2a34232SLucas Stach static int __init imx_ipu_init(void)
1554d2a34232SLucas Stach {
1555d2a34232SLucas Stach 	return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
1556d2a34232SLucas Stach }
1557d2a34232SLucas Stach module_init(imx_ipu_init);
1558d2a34232SLucas Stach 
1559d2a34232SLucas Stach static void __exit imx_ipu_exit(void)
1560d2a34232SLucas Stach {
1561d2a34232SLucas Stach 	platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
1562d2a34232SLucas Stach }
1563d2a34232SLucas Stach module_exit(imx_ipu_exit);
156439b9004dSPhilipp Zabel 
156539b9004dSPhilipp Zabel MODULE_ALIAS("platform:imx-ipuv3");
156639b9004dSPhilipp Zabel MODULE_DESCRIPTION("i.MX IPU v3 driver");
156739b9004dSPhilipp Zabel MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
156839b9004dSPhilipp Zabel MODULE_LICENSE("GPL");
1569