xref: /openbmc/linux/drivers/gpu/ipu-v3/ipu-common.c (revision c9d508c2)
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 
5439b9004dSPhilipp Zabel void ipu_srm_dp_sync_update(struct ipu_soc *ipu)
5539b9004dSPhilipp Zabel {
5639b9004dSPhilipp Zabel 	u32 val;
5739b9004dSPhilipp Zabel 
5839b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_SRM_PRI2);
5939b9004dSPhilipp Zabel 	val |= 0x8;
6039b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_SRM_PRI2);
6139b9004dSPhilipp Zabel }
6239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_srm_dp_sync_update);
6339b9004dSPhilipp Zabel 
6439b9004dSPhilipp Zabel enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc)
6539b9004dSPhilipp Zabel {
6639b9004dSPhilipp Zabel 	switch (drm_fourcc) {
670cb8b757SPhilipp Zabel 	case DRM_FORMAT_ARGB1555:
680cb8b757SPhilipp Zabel 	case DRM_FORMAT_ABGR1555:
690cb8b757SPhilipp Zabel 	case DRM_FORMAT_RGBA5551:
700cb8b757SPhilipp Zabel 	case DRM_FORMAT_BGRA5551:
7139b9004dSPhilipp Zabel 	case DRM_FORMAT_RGB565:
7239b9004dSPhilipp Zabel 	case DRM_FORMAT_BGR565:
7339b9004dSPhilipp Zabel 	case DRM_FORMAT_RGB888:
7439b9004dSPhilipp Zabel 	case DRM_FORMAT_BGR888:
757d2e8a20SLucas Stach 	case DRM_FORMAT_ARGB4444:
7639b9004dSPhilipp Zabel 	case DRM_FORMAT_XRGB8888:
7739b9004dSPhilipp Zabel 	case DRM_FORMAT_XBGR8888:
7839b9004dSPhilipp Zabel 	case DRM_FORMAT_RGBX8888:
7939b9004dSPhilipp Zabel 	case DRM_FORMAT_BGRX8888:
8039b9004dSPhilipp Zabel 	case DRM_FORMAT_ARGB8888:
8139b9004dSPhilipp Zabel 	case DRM_FORMAT_ABGR8888:
8239b9004dSPhilipp Zabel 	case DRM_FORMAT_RGBA8888:
8339b9004dSPhilipp Zabel 	case DRM_FORMAT_BGRA8888:
8439b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_RGB;
8539b9004dSPhilipp Zabel 	case DRM_FORMAT_YUYV:
8639b9004dSPhilipp Zabel 	case DRM_FORMAT_UYVY:
8739b9004dSPhilipp Zabel 	case DRM_FORMAT_YUV420:
8839b9004dSPhilipp Zabel 	case DRM_FORMAT_YVU420:
899a34cef0SSteve Longerbeam 	case DRM_FORMAT_YUV422:
909a34cef0SSteve Longerbeam 	case DRM_FORMAT_YVU422:
91c9d508c2SPhilipp Zabel 	case DRM_FORMAT_YUV444:
92c9d508c2SPhilipp Zabel 	case DRM_FORMAT_YVU444:
939a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV12:
949a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV21:
959a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV16:
969a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV61:
9739b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_YUV;
9839b9004dSPhilipp Zabel 	default:
9939b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_UNKNOWN;
10039b9004dSPhilipp Zabel 	}
10139b9004dSPhilipp Zabel }
10239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace);
10339b9004dSPhilipp Zabel 
10439b9004dSPhilipp Zabel enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat)
10539b9004dSPhilipp Zabel {
10639b9004dSPhilipp Zabel 	switch (pixelformat) {
10739b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YUV420:
10839b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YVU420:
1099a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_YUV422P:
11039b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_UYVY:
11139b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YUYV:
1129a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV12:
1139a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV21:
1149a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV16:
1159a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV61:
11639b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_YUV;
11739b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB32:
11839b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_BGR32:
11939b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB24:
12039b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_BGR24:
12139b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB565:
12239b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_RGB;
12339b9004dSPhilipp Zabel 	default:
12439b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_UNKNOWN;
12539b9004dSPhilipp Zabel 	}
12639b9004dSPhilipp Zabel }
12739b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace);
12839b9004dSPhilipp Zabel 
1294cea940dSSteve Longerbeam bool ipu_pixelformat_is_planar(u32 pixelformat)
1304cea940dSSteve Longerbeam {
1314cea940dSSteve Longerbeam 	switch (pixelformat) {
1324cea940dSSteve Longerbeam 	case V4L2_PIX_FMT_YUV420:
1334cea940dSSteve Longerbeam 	case V4L2_PIX_FMT_YVU420:
1349a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_YUV422P:
1359a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV12:
1369a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV21:
1379a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV16:
1389a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV61:
1394cea940dSSteve Longerbeam 		return true;
1404cea940dSSteve Longerbeam 	}
1414cea940dSSteve Longerbeam 
1424cea940dSSteve Longerbeam 	return false;
1434cea940dSSteve Longerbeam }
1444cea940dSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar);
1454cea940dSSteve Longerbeam 
146ae0e9708SSteve Longerbeam enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code)
147ae0e9708SSteve Longerbeam {
148ae0e9708SSteve Longerbeam 	switch (mbus_code & 0xf000) {
149ae0e9708SSteve Longerbeam 	case 0x1000:
150ae0e9708SSteve Longerbeam 		return IPUV3_COLORSPACE_RGB;
151ae0e9708SSteve Longerbeam 	case 0x2000:
152ae0e9708SSteve Longerbeam 		return IPUV3_COLORSPACE_YUV;
153ae0e9708SSteve Longerbeam 	default:
154ae0e9708SSteve Longerbeam 		return IPUV3_COLORSPACE_UNKNOWN;
155ae0e9708SSteve Longerbeam 	}
156ae0e9708SSteve Longerbeam }
157ae0e9708SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace);
158ae0e9708SSteve Longerbeam 
1596930afdcSSteve Longerbeam int ipu_stride_to_bytes(u32 pixel_stride, u32 pixelformat)
1606930afdcSSteve Longerbeam {
1616930afdcSSteve Longerbeam 	switch (pixelformat) {
1626930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_YUV420:
1636930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_YVU420:
1649a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_YUV422P:
1659a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV12:
1669a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV21:
1679a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV16:
1689a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV61:
1696930afdcSSteve Longerbeam 		/*
1706930afdcSSteve Longerbeam 		 * for the planar YUV formats, the stride passed to
1716930afdcSSteve Longerbeam 		 * cpmem must be the stride in bytes of the Y plane.
1726930afdcSSteve Longerbeam 		 * And all the planar YUV formats have an 8-bit
1736930afdcSSteve Longerbeam 		 * Y component.
1746930afdcSSteve Longerbeam 		 */
1756930afdcSSteve Longerbeam 		return (8 * pixel_stride) >> 3;
1766930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_RGB565:
1776930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_YUYV:
1786930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_UYVY:
1796930afdcSSteve Longerbeam 		return (16 * pixel_stride) >> 3;
1806930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_BGR24:
1816930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_RGB24:
1826930afdcSSteve Longerbeam 		return (24 * pixel_stride) >> 3;
1836930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_BGR32:
1846930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_RGB32:
1856930afdcSSteve Longerbeam 		return (32 * pixel_stride) >> 3;
1866930afdcSSteve Longerbeam 	default:
1876930afdcSSteve Longerbeam 		break;
1886930afdcSSteve Longerbeam 	}
1896930afdcSSteve Longerbeam 
1906930afdcSSteve Longerbeam 	return -EINVAL;
1916930afdcSSteve Longerbeam }
1926930afdcSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_stride_to_bytes);
1936930afdcSSteve Longerbeam 
194f835f386SSteve Longerbeam int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees,
195f835f386SSteve Longerbeam 			    bool hflip, bool vflip)
196f835f386SSteve Longerbeam {
197f835f386SSteve Longerbeam 	u32 r90, vf, hf;
198f835f386SSteve Longerbeam 
199f835f386SSteve Longerbeam 	switch (degrees) {
200f835f386SSteve Longerbeam 	case 0:
201f835f386SSteve Longerbeam 		vf = hf = r90 = 0;
202f835f386SSteve Longerbeam 		break;
203f835f386SSteve Longerbeam 	case 90:
204f835f386SSteve Longerbeam 		vf = hf = 0;
205f835f386SSteve Longerbeam 		r90 = 1;
206f835f386SSteve Longerbeam 		break;
207f835f386SSteve Longerbeam 	case 180:
208f835f386SSteve Longerbeam 		vf = hf = 1;
209f835f386SSteve Longerbeam 		r90 = 0;
210f835f386SSteve Longerbeam 		break;
211f835f386SSteve Longerbeam 	case 270:
212f835f386SSteve Longerbeam 		vf = hf = r90 = 1;
213f835f386SSteve Longerbeam 		break;
214f835f386SSteve Longerbeam 	default:
215f835f386SSteve Longerbeam 		return -EINVAL;
216f835f386SSteve Longerbeam 	}
217f835f386SSteve Longerbeam 
218f835f386SSteve Longerbeam 	hf ^= (u32)hflip;
219f835f386SSteve Longerbeam 	vf ^= (u32)vflip;
220f835f386SSteve Longerbeam 
221f835f386SSteve Longerbeam 	*mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf);
222f835f386SSteve Longerbeam 	return 0;
223f835f386SSteve Longerbeam }
224f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode);
225f835f386SSteve Longerbeam 
226f835f386SSteve Longerbeam int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode,
227f835f386SSteve Longerbeam 			    bool hflip, bool vflip)
228f835f386SSteve Longerbeam {
229f835f386SSteve Longerbeam 	u32 r90, vf, hf;
230f835f386SSteve Longerbeam 
231f835f386SSteve Longerbeam 	r90 = ((u32)mode >> 2) & 0x1;
232f835f386SSteve Longerbeam 	hf = ((u32)mode >> 1) & 0x1;
233f835f386SSteve Longerbeam 	vf = ((u32)mode >> 0) & 0x1;
234f835f386SSteve Longerbeam 	hf ^= (u32)hflip;
235f835f386SSteve Longerbeam 	vf ^= (u32)vflip;
236f835f386SSteve Longerbeam 
237f835f386SSteve Longerbeam 	switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) {
238f835f386SSteve Longerbeam 	case IPU_ROTATE_NONE:
239f835f386SSteve Longerbeam 		*degrees = 0;
240f835f386SSteve Longerbeam 		break;
241f835f386SSteve Longerbeam 	case IPU_ROTATE_90_RIGHT:
242f835f386SSteve Longerbeam 		*degrees = 90;
243f835f386SSteve Longerbeam 		break;
244f835f386SSteve Longerbeam 	case IPU_ROTATE_180:
245f835f386SSteve Longerbeam 		*degrees = 180;
246f835f386SSteve Longerbeam 		break;
247f835f386SSteve Longerbeam 	case IPU_ROTATE_90_LEFT:
248f835f386SSteve Longerbeam 		*degrees = 270;
249f835f386SSteve Longerbeam 		break;
250f835f386SSteve Longerbeam 	default:
251f835f386SSteve Longerbeam 		return -EINVAL;
252f835f386SSteve Longerbeam 	}
253f835f386SSteve Longerbeam 
254f835f386SSteve Longerbeam 	return 0;
255f835f386SSteve Longerbeam }
256f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees);
257f835f386SSteve Longerbeam 
25839b9004dSPhilipp Zabel struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num)
25939b9004dSPhilipp Zabel {
26039b9004dSPhilipp Zabel 	struct ipuv3_channel *channel;
26139b9004dSPhilipp Zabel 
26239b9004dSPhilipp Zabel 	dev_dbg(ipu->dev, "%s %d\n", __func__, num);
26339b9004dSPhilipp Zabel 
26439b9004dSPhilipp Zabel 	if (num > 63)
26539b9004dSPhilipp Zabel 		return ERR_PTR(-ENODEV);
26639b9004dSPhilipp Zabel 
26739b9004dSPhilipp Zabel 	mutex_lock(&ipu->channel_lock);
26839b9004dSPhilipp Zabel 
26939b9004dSPhilipp Zabel 	channel = &ipu->channel[num];
27039b9004dSPhilipp Zabel 
27139b9004dSPhilipp Zabel 	if (channel->busy) {
27239b9004dSPhilipp Zabel 		channel = ERR_PTR(-EBUSY);
27339b9004dSPhilipp Zabel 		goto out;
27439b9004dSPhilipp Zabel 	}
27539b9004dSPhilipp Zabel 
27639b9004dSPhilipp Zabel 	channel->busy = true;
27739b9004dSPhilipp Zabel 	channel->num = num;
27839b9004dSPhilipp Zabel 
27939b9004dSPhilipp Zabel out:
28039b9004dSPhilipp Zabel 	mutex_unlock(&ipu->channel_lock);
28139b9004dSPhilipp Zabel 
28239b9004dSPhilipp Zabel 	return channel;
28339b9004dSPhilipp Zabel }
28439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get);
28539b9004dSPhilipp Zabel 
28639b9004dSPhilipp Zabel void ipu_idmac_put(struct ipuv3_channel *channel)
28739b9004dSPhilipp Zabel {
28839b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
28939b9004dSPhilipp Zabel 
29039b9004dSPhilipp Zabel 	dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num);
29139b9004dSPhilipp Zabel 
29239b9004dSPhilipp Zabel 	mutex_lock(&ipu->channel_lock);
29339b9004dSPhilipp Zabel 
29439b9004dSPhilipp Zabel 	channel->busy = false;
29539b9004dSPhilipp Zabel 
29639b9004dSPhilipp Zabel 	mutex_unlock(&ipu->channel_lock);
29739b9004dSPhilipp Zabel }
29839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_put);
29939b9004dSPhilipp Zabel 
300aa52f578SSteve Longerbeam #define idma_mask(ch)			(1 << ((ch) & 0x1f))
30139b9004dSPhilipp Zabel 
302e7268c69SSteve Longerbeam /*
303e7268c69SSteve Longerbeam  * This is an undocumented feature, a write one to a channel bit in
304e7268c69SSteve Longerbeam  * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's
305e7268c69SSteve Longerbeam  * internal current buffer pointer so that transfers start from buffer
306e7268c69SSteve Longerbeam  * 0 on the next channel enable (that's the theory anyway, the imx6 TRM
307e7268c69SSteve Longerbeam  * only says these are read-only registers). This operation is required
308e7268c69SSteve Longerbeam  * for channel linking to work correctly, for instance video capture
309e7268c69SSteve Longerbeam  * pipelines that carry out image rotations will fail after the first
310e7268c69SSteve Longerbeam  * streaming unless this function is called for each channel before
311e7268c69SSteve Longerbeam  * re-enabling the channels.
312e7268c69SSteve Longerbeam  */
313e7268c69SSteve Longerbeam static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel)
314e7268c69SSteve Longerbeam {
315e7268c69SSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
316e7268c69SSteve Longerbeam 	unsigned int chno = channel->num;
317e7268c69SSteve Longerbeam 
318e7268c69SSteve Longerbeam 	ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno));
319e7268c69SSteve Longerbeam }
320e7268c69SSteve Longerbeam 
32139b9004dSPhilipp Zabel void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
32239b9004dSPhilipp Zabel 		bool doublebuffer)
32339b9004dSPhilipp Zabel {
32439b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
32539b9004dSPhilipp Zabel 	unsigned long flags;
32639b9004dSPhilipp Zabel 	u32 reg;
32739b9004dSPhilipp Zabel 
32839b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
32939b9004dSPhilipp Zabel 
33039b9004dSPhilipp Zabel 	reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
33139b9004dSPhilipp Zabel 	if (doublebuffer)
33239b9004dSPhilipp Zabel 		reg |= idma_mask(channel->num);
33339b9004dSPhilipp Zabel 	else
33439b9004dSPhilipp Zabel 		reg &= ~idma_mask(channel->num);
33539b9004dSPhilipp Zabel 	ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num));
33639b9004dSPhilipp Zabel 
337e7268c69SSteve Longerbeam 	__ipu_idmac_reset_current_buffer(channel);
338e7268c69SSteve Longerbeam 
33939b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
34039b9004dSPhilipp Zabel }
34139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer);
34239b9004dSPhilipp Zabel 
3434fd1a07aSSteve Longerbeam static const struct {
3444fd1a07aSSteve Longerbeam 	int chnum;
3454fd1a07aSSteve Longerbeam 	u32 reg;
3464fd1a07aSSteve Longerbeam 	int shift;
3474fd1a07aSSteve Longerbeam } idmac_lock_en_info[] = {
3484fd1a07aSSteve Longerbeam 	{ .chnum =  5, .reg = IDMAC_CH_LOCK_EN_1, .shift =  0, },
3494fd1a07aSSteve Longerbeam 	{ .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift =  2, },
3504fd1a07aSSteve Longerbeam 	{ .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift =  4, },
3514fd1a07aSSteve Longerbeam 	{ .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift =  6, },
3524fd1a07aSSteve Longerbeam 	{ .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift =  8, },
3534fd1a07aSSteve Longerbeam 	{ .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, },
3544fd1a07aSSteve Longerbeam 	{ .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, },
3554fd1a07aSSteve Longerbeam 	{ .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, },
3564fd1a07aSSteve Longerbeam 	{ .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, },
3574fd1a07aSSteve Longerbeam 	{ .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, },
3584fd1a07aSSteve Longerbeam 	{ .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, },
3594fd1a07aSSteve Longerbeam 	{ .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift =  0, },
3604fd1a07aSSteve Longerbeam 	{ .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift =  2, },
3614fd1a07aSSteve Longerbeam 	{ .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift =  4, },
3624fd1a07aSSteve Longerbeam 	{ .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift =  6, },
3634fd1a07aSSteve Longerbeam 	{ .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift =  8, },
3644fd1a07aSSteve Longerbeam 	{ .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, },
3654fd1a07aSSteve Longerbeam };
3664fd1a07aSSteve Longerbeam 
3674fd1a07aSSteve Longerbeam int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts)
3684fd1a07aSSteve Longerbeam {
3694fd1a07aSSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
3704fd1a07aSSteve Longerbeam 	unsigned long flags;
3714fd1a07aSSteve Longerbeam 	u32 bursts, regval;
3724fd1a07aSSteve Longerbeam 	int i;
3734fd1a07aSSteve Longerbeam 
3744fd1a07aSSteve Longerbeam 	switch (num_bursts) {
3754fd1a07aSSteve Longerbeam 	case 0:
3764fd1a07aSSteve Longerbeam 	case 1:
3774fd1a07aSSteve Longerbeam 		bursts = 0x00; /* locking disabled */
3784fd1a07aSSteve Longerbeam 		break;
3794fd1a07aSSteve Longerbeam 	case 2:
3804fd1a07aSSteve Longerbeam 		bursts = 0x01;
3814fd1a07aSSteve Longerbeam 		break;
3824fd1a07aSSteve Longerbeam 	case 4:
3834fd1a07aSSteve Longerbeam 		bursts = 0x02;
3844fd1a07aSSteve Longerbeam 		break;
3854fd1a07aSSteve Longerbeam 	case 8:
3864fd1a07aSSteve Longerbeam 		bursts = 0x03;
3874fd1a07aSSteve Longerbeam 		break;
3884fd1a07aSSteve Longerbeam 	default:
3894fd1a07aSSteve Longerbeam 		return -EINVAL;
3904fd1a07aSSteve Longerbeam 	}
3914fd1a07aSSteve Longerbeam 
3924fd1a07aSSteve Longerbeam 	for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) {
3934fd1a07aSSteve Longerbeam 		if (channel->num == idmac_lock_en_info[i].chnum)
3944fd1a07aSSteve Longerbeam 			break;
3954fd1a07aSSteve Longerbeam 	}
3964fd1a07aSSteve Longerbeam 	if (i >= ARRAY_SIZE(idmac_lock_en_info))
3974fd1a07aSSteve Longerbeam 		return -EINVAL;
3984fd1a07aSSteve Longerbeam 
3994fd1a07aSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
4004fd1a07aSSteve Longerbeam 
4014fd1a07aSSteve Longerbeam 	regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg);
4024fd1a07aSSteve Longerbeam 	regval &= ~(0x03 << idmac_lock_en_info[i].shift);
4034fd1a07aSSteve Longerbeam 	regval |= (bursts << idmac_lock_en_info[i].shift);
4044fd1a07aSSteve Longerbeam 	ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg);
4054fd1a07aSSteve Longerbeam 
4064fd1a07aSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
4074fd1a07aSSteve Longerbeam 
4084fd1a07aSSteve Longerbeam 	return 0;
4094fd1a07aSSteve Longerbeam }
4104fd1a07aSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable);
4114fd1a07aSSteve Longerbeam 
41239b9004dSPhilipp Zabel int ipu_module_enable(struct ipu_soc *ipu, u32 mask)
41339b9004dSPhilipp Zabel {
41439b9004dSPhilipp Zabel 	unsigned long lock_flags;
41539b9004dSPhilipp Zabel 	u32 val;
41639b9004dSPhilipp Zabel 
41739b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, lock_flags);
41839b9004dSPhilipp Zabel 
41939b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_DISP_GEN);
42039b9004dSPhilipp Zabel 
42139b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI0_EN)
42239b9004dSPhilipp Zabel 		val |= IPU_DI0_COUNTER_RELEASE;
42339b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI1_EN)
42439b9004dSPhilipp Zabel 		val |= IPU_DI1_COUNTER_RELEASE;
42539b9004dSPhilipp Zabel 
42639b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_DISP_GEN);
42739b9004dSPhilipp Zabel 
42839b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CONF);
42939b9004dSPhilipp Zabel 	val |= mask;
43039b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CONF);
43139b9004dSPhilipp Zabel 
43239b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, lock_flags);
43339b9004dSPhilipp Zabel 
43439b9004dSPhilipp Zabel 	return 0;
43539b9004dSPhilipp Zabel }
43639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_enable);
43739b9004dSPhilipp Zabel 
43839b9004dSPhilipp Zabel int ipu_module_disable(struct ipu_soc *ipu, u32 mask)
43939b9004dSPhilipp Zabel {
44039b9004dSPhilipp Zabel 	unsigned long lock_flags;
44139b9004dSPhilipp Zabel 	u32 val;
44239b9004dSPhilipp Zabel 
44339b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, lock_flags);
44439b9004dSPhilipp Zabel 
44539b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CONF);
44639b9004dSPhilipp Zabel 	val &= ~mask;
44739b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CONF);
44839b9004dSPhilipp Zabel 
44939b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_DISP_GEN);
45039b9004dSPhilipp Zabel 
45139b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI0_EN)
45239b9004dSPhilipp Zabel 		val &= ~IPU_DI0_COUNTER_RELEASE;
45339b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI1_EN)
45439b9004dSPhilipp Zabel 		val &= ~IPU_DI1_COUNTER_RELEASE;
45539b9004dSPhilipp Zabel 
45639b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_DISP_GEN);
45739b9004dSPhilipp Zabel 
45839b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, lock_flags);
45939b9004dSPhilipp Zabel 
46039b9004dSPhilipp Zabel 	return 0;
46139b9004dSPhilipp Zabel }
46239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_disable);
46339b9004dSPhilipp Zabel 
464e9046097SPhilipp Zabel int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel)
465e9046097SPhilipp Zabel {
466e9046097SPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
467e9046097SPhilipp Zabel 	unsigned int chno = channel->num;
468e9046097SPhilipp Zabel 
469e9046097SPhilipp Zabel 	return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0;
470e9046097SPhilipp Zabel }
471e9046097SPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer);
472e9046097SPhilipp Zabel 
473aa52f578SSteve Longerbeam bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num)
474aa52f578SSteve Longerbeam {
475aa52f578SSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
476aa52f578SSteve Longerbeam 	unsigned long flags;
477aa52f578SSteve Longerbeam 	u32 reg = 0;
478aa52f578SSteve Longerbeam 
479aa52f578SSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
480aa52f578SSteve Longerbeam 	switch (buf_num) {
481aa52f578SSteve Longerbeam 	case 0:
482aa52f578SSteve Longerbeam 		reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num));
483aa52f578SSteve Longerbeam 		break;
484aa52f578SSteve Longerbeam 	case 1:
485aa52f578SSteve Longerbeam 		reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num));
486aa52f578SSteve Longerbeam 		break;
487aa52f578SSteve Longerbeam 	case 2:
488aa52f578SSteve Longerbeam 		reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num));
489aa52f578SSteve Longerbeam 		break;
490aa52f578SSteve Longerbeam 	}
491aa52f578SSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
492aa52f578SSteve Longerbeam 
493aa52f578SSteve Longerbeam 	return ((reg & idma_mask(channel->num)) != 0);
494aa52f578SSteve Longerbeam }
495aa52f578SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready);
496aa52f578SSteve Longerbeam 
49739b9004dSPhilipp Zabel void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num)
49839b9004dSPhilipp Zabel {
49939b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
50039b9004dSPhilipp Zabel 	unsigned int chno = channel->num;
50139b9004dSPhilipp Zabel 	unsigned long flags;
50239b9004dSPhilipp Zabel 
50339b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
50439b9004dSPhilipp Zabel 
50539b9004dSPhilipp Zabel 	/* Mark buffer as ready. */
50639b9004dSPhilipp Zabel 	if (buf_num == 0)
50739b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
50839b9004dSPhilipp Zabel 	else
50939b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
51039b9004dSPhilipp Zabel 
51139b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
51239b9004dSPhilipp Zabel }
51339b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer);
51439b9004dSPhilipp Zabel 
515bce6f087SSteve Longerbeam void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num)
516bce6f087SSteve Longerbeam {
517bce6f087SSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
518bce6f087SSteve Longerbeam 	unsigned int chno = channel->num;
519bce6f087SSteve Longerbeam 	unsigned long flags;
520bce6f087SSteve Longerbeam 
521bce6f087SSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
522bce6f087SSteve Longerbeam 
523bce6f087SSteve Longerbeam 	ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */
524bce6f087SSteve Longerbeam 	switch (buf_num) {
525bce6f087SSteve Longerbeam 	case 0:
526bce6f087SSteve Longerbeam 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
527bce6f087SSteve Longerbeam 		break;
528bce6f087SSteve Longerbeam 	case 1:
529bce6f087SSteve Longerbeam 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
530bce6f087SSteve Longerbeam 		break;
531bce6f087SSteve Longerbeam 	case 2:
532bce6f087SSteve Longerbeam 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno));
533bce6f087SSteve Longerbeam 		break;
534bce6f087SSteve Longerbeam 	default:
535bce6f087SSteve Longerbeam 		break;
536bce6f087SSteve Longerbeam 	}
537bce6f087SSteve Longerbeam 	ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
538bce6f087SSteve Longerbeam 
539bce6f087SSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
540bce6f087SSteve Longerbeam }
541bce6f087SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer);
542bce6f087SSteve Longerbeam 
54339b9004dSPhilipp Zabel int ipu_idmac_enable_channel(struct ipuv3_channel *channel)
54439b9004dSPhilipp Zabel {
54539b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
54639b9004dSPhilipp Zabel 	u32 val;
54739b9004dSPhilipp Zabel 	unsigned long flags;
54839b9004dSPhilipp Zabel 
54939b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
55039b9004dSPhilipp Zabel 
55139b9004dSPhilipp Zabel 	val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
55239b9004dSPhilipp Zabel 	val |= idma_mask(channel->num);
55339b9004dSPhilipp Zabel 	ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
55439b9004dSPhilipp Zabel 
55539b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
55639b9004dSPhilipp Zabel 
55739b9004dSPhilipp Zabel 	return 0;
55839b9004dSPhilipp Zabel }
55939b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
56039b9004dSPhilipp Zabel 
561682b7c1cSLinus Torvalds bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno)
562682b7c1cSLinus Torvalds {
563682b7c1cSLinus Torvalds 	return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno));
564682b7c1cSLinus Torvalds }
565682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy);
566682b7c1cSLinus Torvalds 
56739b9004dSPhilipp Zabel int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms)
56839b9004dSPhilipp Zabel {
56939b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
57039b9004dSPhilipp Zabel 	unsigned long timeout;
57139b9004dSPhilipp Zabel 
57239b9004dSPhilipp Zabel 	timeout = jiffies + msecs_to_jiffies(ms);
57339b9004dSPhilipp Zabel 	while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) &
57439b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
57539b9004dSPhilipp Zabel 		if (time_after(jiffies, timeout))
57639b9004dSPhilipp Zabel 			return -ETIMEDOUT;
57739b9004dSPhilipp Zabel 		cpu_relax();
57839b9004dSPhilipp Zabel 	}
57939b9004dSPhilipp Zabel 
58039b9004dSPhilipp Zabel 	return 0;
58139b9004dSPhilipp Zabel }
58239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy);
58339b9004dSPhilipp Zabel 
584682b7c1cSLinus Torvalds int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms)
585682b7c1cSLinus Torvalds {
586682b7c1cSLinus Torvalds 	unsigned long timeout;
587682b7c1cSLinus Torvalds 
588682b7c1cSLinus Torvalds 	timeout = jiffies + msecs_to_jiffies(ms);
589682b7c1cSLinus Torvalds 	ipu_cm_write(ipu, BIT(irq % 32), IPU_INT_STAT(irq / 32));
590682b7c1cSLinus Torvalds 	while (!(ipu_cm_read(ipu, IPU_INT_STAT(irq / 32) & BIT(irq % 32)))) {
591682b7c1cSLinus Torvalds 		if (time_after(jiffies, timeout))
592682b7c1cSLinus Torvalds 			return -ETIMEDOUT;
593682b7c1cSLinus Torvalds 		cpu_relax();
594682b7c1cSLinus Torvalds 	}
595682b7c1cSLinus Torvalds 
596682b7c1cSLinus Torvalds 	return 0;
597682b7c1cSLinus Torvalds }
598682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_wait_interrupt);
599682b7c1cSLinus Torvalds 
60039b9004dSPhilipp Zabel int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
60139b9004dSPhilipp Zabel {
60239b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
60339b9004dSPhilipp Zabel 	u32 val;
60439b9004dSPhilipp Zabel 	unsigned long flags;
60539b9004dSPhilipp Zabel 
60639b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
60739b9004dSPhilipp Zabel 
60839b9004dSPhilipp Zabel 	/* Disable DMA channel(s) */
60939b9004dSPhilipp Zabel 	val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
61039b9004dSPhilipp Zabel 	val &= ~idma_mask(channel->num);
61139b9004dSPhilipp Zabel 	ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
61239b9004dSPhilipp Zabel 
613e7268c69SSteve Longerbeam 	__ipu_idmac_reset_current_buffer(channel);
614e7268c69SSteve Longerbeam 
61539b9004dSPhilipp Zabel 	/* Set channel buffers NOT to be ready */
61639b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */
61739b9004dSPhilipp Zabel 
61839b9004dSPhilipp Zabel 	if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) &
61939b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
62039b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(channel->num),
62139b9004dSPhilipp Zabel 			     IPU_CHA_BUF0_RDY(channel->num));
62239b9004dSPhilipp Zabel 	}
62339b9004dSPhilipp Zabel 
62439b9004dSPhilipp Zabel 	if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) &
62539b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
62639b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(channel->num),
62739b9004dSPhilipp Zabel 			     IPU_CHA_BUF1_RDY(channel->num));
62839b9004dSPhilipp Zabel 	}
62939b9004dSPhilipp Zabel 
63039b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
63139b9004dSPhilipp Zabel 
63239b9004dSPhilipp Zabel 	/* Reset the double buffer */
63339b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
63439b9004dSPhilipp Zabel 	val &= ~idma_mask(channel->num);
63539b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num));
63639b9004dSPhilipp Zabel 
63739b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
63839b9004dSPhilipp Zabel 
63939b9004dSPhilipp Zabel 	return 0;
64039b9004dSPhilipp Zabel }
64139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel);
64239b9004dSPhilipp Zabel 
6432bcf577eSSteve Longerbeam /*
6442bcf577eSSteve Longerbeam  * The imx6 rev. D TRM says that enabling the WM feature will increase
6452bcf577eSSteve Longerbeam  * a channel's priority. Refer to Table 36-8 Calculated priority value.
6462bcf577eSSteve Longerbeam  * The sub-module that is the sink or source for the channel must enable
6472bcf577eSSteve Longerbeam  * watermark signal for this to take effect (SMFC_WM for instance).
6482bcf577eSSteve Longerbeam  */
6492bcf577eSSteve Longerbeam void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable)
6502bcf577eSSteve Longerbeam {
6512bcf577eSSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
6522bcf577eSSteve Longerbeam 	unsigned long flags;
6532bcf577eSSteve Longerbeam 	u32 val;
6542bcf577eSSteve Longerbeam 
6552bcf577eSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
6562bcf577eSSteve Longerbeam 
6572bcf577eSSteve Longerbeam 	val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num));
6582bcf577eSSteve Longerbeam 	if (enable)
6592bcf577eSSteve Longerbeam 		val |= 1 << (channel->num % 32);
6602bcf577eSSteve Longerbeam 	else
6612bcf577eSSteve Longerbeam 		val &= ~(1 << (channel->num % 32));
6622bcf577eSSteve Longerbeam 	ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num));
6632bcf577eSSteve Longerbeam 
6642bcf577eSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
6652bcf577eSSteve Longerbeam }
6662bcf577eSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark);
6672bcf577eSSteve Longerbeam 
66839b9004dSPhilipp Zabel static int ipu_memory_reset(struct ipu_soc *ipu)
66939b9004dSPhilipp Zabel {
67039b9004dSPhilipp Zabel 	unsigned long timeout;
67139b9004dSPhilipp Zabel 
67239b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST);
67339b9004dSPhilipp Zabel 
67439b9004dSPhilipp Zabel 	timeout = jiffies + msecs_to_jiffies(1000);
67539b9004dSPhilipp Zabel 	while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) {
67639b9004dSPhilipp Zabel 		if (time_after(jiffies, timeout))
67739b9004dSPhilipp Zabel 			return -ETIME;
67839b9004dSPhilipp Zabel 		cpu_relax();
67939b9004dSPhilipp Zabel 	}
68039b9004dSPhilipp Zabel 
68139b9004dSPhilipp Zabel 	return 0;
68239b9004dSPhilipp Zabel }
68339b9004dSPhilipp Zabel 
684ba07975fSSteve Longerbeam /*
685ba07975fSSteve Longerbeam  * Set the source mux for the given CSI. Selects either parallel or
686ba07975fSSteve Longerbeam  * MIPI CSI2 sources.
687ba07975fSSteve Longerbeam  */
688ba07975fSSteve Longerbeam void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2)
689ba07975fSSteve Longerbeam {
690ba07975fSSteve Longerbeam 	unsigned long flags;
691ba07975fSSteve Longerbeam 	u32 val, mask;
692ba07975fSSteve Longerbeam 
693ba07975fSSteve Longerbeam 	mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE :
694ba07975fSSteve Longerbeam 		IPU_CONF_CSI0_DATA_SOURCE;
695ba07975fSSteve Longerbeam 
696ba07975fSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
697ba07975fSSteve Longerbeam 
698ba07975fSSteve Longerbeam 	val = ipu_cm_read(ipu, IPU_CONF);
699ba07975fSSteve Longerbeam 	if (mipi_csi2)
700ba07975fSSteve Longerbeam 		val |= mask;
701ba07975fSSteve Longerbeam 	else
702ba07975fSSteve Longerbeam 		val &= ~mask;
703ba07975fSSteve Longerbeam 	ipu_cm_write(ipu, val, IPU_CONF);
704ba07975fSSteve Longerbeam 
705ba07975fSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
706ba07975fSSteve Longerbeam }
707ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux);
708ba07975fSSteve Longerbeam 
709ba07975fSSteve Longerbeam /*
710ba07975fSSteve Longerbeam  * Set the source mux for the IC. Selects either CSI[01] or the VDI.
711ba07975fSSteve Longerbeam  */
712ba07975fSSteve Longerbeam void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
713ba07975fSSteve Longerbeam {
714ba07975fSSteve Longerbeam 	unsigned long flags;
715ba07975fSSteve Longerbeam 	u32 val;
716ba07975fSSteve Longerbeam 
717ba07975fSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
718ba07975fSSteve Longerbeam 
719ba07975fSSteve Longerbeam 	val = ipu_cm_read(ipu, IPU_CONF);
720ba07975fSSteve Longerbeam 	if (vdi) {
721ba07975fSSteve Longerbeam 		val |= IPU_CONF_IC_INPUT;
722ba07975fSSteve Longerbeam 	} else {
723ba07975fSSteve Longerbeam 		val &= ~IPU_CONF_IC_INPUT;
724ba07975fSSteve Longerbeam 		if (csi_id == 1)
725ba07975fSSteve Longerbeam 			val |= IPU_CONF_CSI_SEL;
726ba07975fSSteve Longerbeam 		else
727ba07975fSSteve Longerbeam 			val &= ~IPU_CONF_CSI_SEL;
728ba07975fSSteve Longerbeam 	}
729ba07975fSSteve Longerbeam 	ipu_cm_write(ipu, val, IPU_CONF);
730ba07975fSSteve Longerbeam 
731ba07975fSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
732ba07975fSSteve Longerbeam }
733ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
734ba07975fSSteve Longerbeam 
735ac4708faSSteve Longerbeam 
736ac4708faSSteve Longerbeam /* Frame Synchronization Unit Channel Linking */
737ac4708faSSteve Longerbeam 
738ac4708faSSteve Longerbeam struct fsu_link_reg_info {
739ac4708faSSteve Longerbeam 	int chno;
740ac4708faSSteve Longerbeam 	u32 reg;
741ac4708faSSteve Longerbeam 	u32 mask;
742ac4708faSSteve Longerbeam 	u32 val;
743ac4708faSSteve Longerbeam };
744ac4708faSSteve Longerbeam 
745ac4708faSSteve Longerbeam struct fsu_link_info {
746ac4708faSSteve Longerbeam 	struct fsu_link_reg_info src;
747ac4708faSSteve Longerbeam 	struct fsu_link_reg_info sink;
748ac4708faSSteve Longerbeam };
749ac4708faSSteve Longerbeam 
750ac4708faSSteve Longerbeam static const struct fsu_link_info fsu_link_info[] = {
751ac4708faSSteve Longerbeam 	{
752ac4708faSSteve Longerbeam 		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2,
753ac4708faSSteve Longerbeam 			  FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC },
754ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1,
755ac4708faSSteve Longerbeam 			  FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC },
756ac4708faSSteve Longerbeam 	}, {
757ac4708faSSteve Longerbeam 		.src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2,
758ac4708faSSteve Longerbeam 			  FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF },
759ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1,
760ac4708faSSteve Longerbeam 			  FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF },
761ac4708faSSteve Longerbeam 	}, {
762ac4708faSSteve Longerbeam 		.src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2,
763ac4708faSSteve Longerbeam 			  FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP },
764ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1,
765ac4708faSSteve Longerbeam 			  FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP },
766ac4708faSSteve Longerbeam 	}, {
767ac4708faSSteve Longerbeam 		.src =  { IPUV3_CHANNEL_CSI_DIRECT, 0 },
768ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1,
769ac4708faSSteve Longerbeam 			  FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT },
770ac4708faSSteve Longerbeam 	},
771ac4708faSSteve Longerbeam };
772ac4708faSSteve Longerbeam 
773ac4708faSSteve Longerbeam static const struct fsu_link_info *find_fsu_link_info(int src, int sink)
774ac4708faSSteve Longerbeam {
775ac4708faSSteve Longerbeam 	int i;
776ac4708faSSteve Longerbeam 
777ac4708faSSteve Longerbeam 	for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) {
778ac4708faSSteve Longerbeam 		if (src == fsu_link_info[i].src.chno &&
779ac4708faSSteve Longerbeam 		    sink == fsu_link_info[i].sink.chno)
780ac4708faSSteve Longerbeam 			return &fsu_link_info[i];
781ac4708faSSteve Longerbeam 	}
782ac4708faSSteve Longerbeam 
783ac4708faSSteve Longerbeam 	return NULL;
784ac4708faSSteve Longerbeam }
785ac4708faSSteve Longerbeam 
786ac4708faSSteve Longerbeam /*
787ac4708faSSteve Longerbeam  * Links a source channel to a sink channel in the FSU.
788ac4708faSSteve Longerbeam  */
789ac4708faSSteve Longerbeam int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch)
790ac4708faSSteve Longerbeam {
791ac4708faSSteve Longerbeam 	const struct fsu_link_info *link;
792ac4708faSSteve Longerbeam 	u32 src_reg, sink_reg;
793ac4708faSSteve Longerbeam 	unsigned long flags;
794ac4708faSSteve Longerbeam 
795ac4708faSSteve Longerbeam 	link = find_fsu_link_info(src_ch, sink_ch);
796ac4708faSSteve Longerbeam 	if (!link)
797ac4708faSSteve Longerbeam 		return -EINVAL;
798ac4708faSSteve Longerbeam 
799ac4708faSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
800ac4708faSSteve Longerbeam 
801ac4708faSSteve Longerbeam 	if (link->src.mask) {
802ac4708faSSteve Longerbeam 		src_reg = ipu_cm_read(ipu, link->src.reg);
803ac4708faSSteve Longerbeam 		src_reg &= ~link->src.mask;
804ac4708faSSteve Longerbeam 		src_reg |= link->src.val;
805ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, src_reg, link->src.reg);
806ac4708faSSteve Longerbeam 	}
807ac4708faSSteve Longerbeam 
808ac4708faSSteve Longerbeam 	if (link->sink.mask) {
809ac4708faSSteve Longerbeam 		sink_reg = ipu_cm_read(ipu, link->sink.reg);
810ac4708faSSteve Longerbeam 		sink_reg &= ~link->sink.mask;
811ac4708faSSteve Longerbeam 		sink_reg |= link->sink.val;
812ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, sink_reg, link->sink.reg);
813ac4708faSSteve Longerbeam 	}
814ac4708faSSteve Longerbeam 
815ac4708faSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
816ac4708faSSteve Longerbeam 	return 0;
817ac4708faSSteve Longerbeam }
818ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_link);
819ac4708faSSteve Longerbeam 
820ac4708faSSteve Longerbeam /*
821ac4708faSSteve Longerbeam  * Unlinks source and sink channels in the FSU.
822ac4708faSSteve Longerbeam  */
823ac4708faSSteve Longerbeam int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch)
824ac4708faSSteve Longerbeam {
825ac4708faSSteve Longerbeam 	const struct fsu_link_info *link;
826ac4708faSSteve Longerbeam 	u32 src_reg, sink_reg;
827ac4708faSSteve Longerbeam 	unsigned long flags;
828ac4708faSSteve Longerbeam 
829ac4708faSSteve Longerbeam 	link = find_fsu_link_info(src_ch, sink_ch);
830ac4708faSSteve Longerbeam 	if (!link)
831ac4708faSSteve Longerbeam 		return -EINVAL;
832ac4708faSSteve Longerbeam 
833ac4708faSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
834ac4708faSSteve Longerbeam 
835ac4708faSSteve Longerbeam 	if (link->src.mask) {
836ac4708faSSteve Longerbeam 		src_reg = ipu_cm_read(ipu, link->src.reg);
837ac4708faSSteve Longerbeam 		src_reg &= ~link->src.mask;
838ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, src_reg, link->src.reg);
839ac4708faSSteve Longerbeam 	}
840ac4708faSSteve Longerbeam 
841ac4708faSSteve Longerbeam 	if (link->sink.mask) {
842ac4708faSSteve Longerbeam 		sink_reg = ipu_cm_read(ipu, link->sink.reg);
843ac4708faSSteve Longerbeam 		sink_reg &= ~link->sink.mask;
844ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, sink_reg, link->sink.reg);
845ac4708faSSteve Longerbeam 	}
846ac4708faSSteve Longerbeam 
847ac4708faSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
848ac4708faSSteve Longerbeam 	return 0;
849ac4708faSSteve Longerbeam }
850ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_unlink);
851ac4708faSSteve Longerbeam 
852ac4708faSSteve Longerbeam /* Link IDMAC channels in the FSU */
853ac4708faSSteve Longerbeam int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)
854ac4708faSSteve Longerbeam {
855ac4708faSSteve Longerbeam 	return ipu_fsu_link(src->ipu, src->num, sink->num);
856ac4708faSSteve Longerbeam }
857ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_link);
858ac4708faSSteve Longerbeam 
859ac4708faSSteve Longerbeam /* Unlink IDMAC channels in the FSU */
860ac4708faSSteve Longerbeam int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink)
861ac4708faSSteve Longerbeam {
862ac4708faSSteve Longerbeam 	return ipu_fsu_unlink(src->ipu, src->num, sink->num);
863ac4708faSSteve Longerbeam }
864ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_unlink);
865ac4708faSSteve Longerbeam 
86639b9004dSPhilipp Zabel struct ipu_devtype {
86739b9004dSPhilipp Zabel 	const char *name;
86839b9004dSPhilipp Zabel 	unsigned long cm_ofs;
86939b9004dSPhilipp Zabel 	unsigned long cpmem_ofs;
87039b9004dSPhilipp Zabel 	unsigned long srm_ofs;
87139b9004dSPhilipp Zabel 	unsigned long tpm_ofs;
8722ffd48f2SSteve Longerbeam 	unsigned long csi0_ofs;
8732ffd48f2SSteve Longerbeam 	unsigned long csi1_ofs;
8741aa8ea0dSSteve Longerbeam 	unsigned long ic_ofs;
87539b9004dSPhilipp Zabel 	unsigned long disp0_ofs;
87639b9004dSPhilipp Zabel 	unsigned long disp1_ofs;
87739b9004dSPhilipp Zabel 	unsigned long dc_tmpl_ofs;
87839b9004dSPhilipp Zabel 	unsigned long vdi_ofs;
87939b9004dSPhilipp Zabel 	enum ipuv3_type type;
88039b9004dSPhilipp Zabel };
88139b9004dSPhilipp Zabel 
88239b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx51 = {
88339b9004dSPhilipp Zabel 	.name = "IPUv3EX",
88439b9004dSPhilipp Zabel 	.cm_ofs = 0x1e000000,
88539b9004dSPhilipp Zabel 	.cpmem_ofs = 0x1f000000,
88639b9004dSPhilipp Zabel 	.srm_ofs = 0x1f040000,
88739b9004dSPhilipp Zabel 	.tpm_ofs = 0x1f060000,
8882ffd48f2SSteve Longerbeam 	.csi0_ofs = 0x1f030000,
8892ffd48f2SSteve Longerbeam 	.csi1_ofs = 0x1f038000,
890a49e7c0dSPhilipp Zabel 	.ic_ofs = 0x1e020000,
89139b9004dSPhilipp Zabel 	.disp0_ofs = 0x1e040000,
89239b9004dSPhilipp Zabel 	.disp1_ofs = 0x1e048000,
89339b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x1f080000,
89439b9004dSPhilipp Zabel 	.vdi_ofs = 0x1e068000,
89539b9004dSPhilipp Zabel 	.type = IPUV3EX,
89639b9004dSPhilipp Zabel };
89739b9004dSPhilipp Zabel 
89839b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx53 = {
89939b9004dSPhilipp Zabel 	.name = "IPUv3M",
90039b9004dSPhilipp Zabel 	.cm_ofs = 0x06000000,
90139b9004dSPhilipp Zabel 	.cpmem_ofs = 0x07000000,
90239b9004dSPhilipp Zabel 	.srm_ofs = 0x07040000,
90339b9004dSPhilipp Zabel 	.tpm_ofs = 0x07060000,
9042ffd48f2SSteve Longerbeam 	.csi0_ofs = 0x07030000,
9052ffd48f2SSteve Longerbeam 	.csi1_ofs = 0x07038000,
906a49e7c0dSPhilipp Zabel 	.ic_ofs = 0x06020000,
90739b9004dSPhilipp Zabel 	.disp0_ofs = 0x06040000,
90839b9004dSPhilipp Zabel 	.disp1_ofs = 0x06048000,
90939b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x07080000,
91039b9004dSPhilipp Zabel 	.vdi_ofs = 0x06068000,
91139b9004dSPhilipp Zabel 	.type = IPUV3M,
91239b9004dSPhilipp Zabel };
91339b9004dSPhilipp Zabel 
91439b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx6q = {
91539b9004dSPhilipp Zabel 	.name = "IPUv3H",
91639b9004dSPhilipp Zabel 	.cm_ofs = 0x00200000,
91739b9004dSPhilipp Zabel 	.cpmem_ofs = 0x00300000,
91839b9004dSPhilipp Zabel 	.srm_ofs = 0x00340000,
91939b9004dSPhilipp Zabel 	.tpm_ofs = 0x00360000,
9202ffd48f2SSteve Longerbeam 	.csi0_ofs = 0x00230000,
9212ffd48f2SSteve Longerbeam 	.csi1_ofs = 0x00238000,
9221aa8ea0dSSteve Longerbeam 	.ic_ofs = 0x00220000,
92339b9004dSPhilipp Zabel 	.disp0_ofs = 0x00240000,
92439b9004dSPhilipp Zabel 	.disp1_ofs = 0x00248000,
92539b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x00380000,
92639b9004dSPhilipp Zabel 	.vdi_ofs = 0x00268000,
92739b9004dSPhilipp Zabel 	.type = IPUV3H,
92839b9004dSPhilipp Zabel };
92939b9004dSPhilipp Zabel 
93039b9004dSPhilipp Zabel static const struct of_device_id imx_ipu_dt_ids[] = {
93139b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, },
93239b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, },
93339b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, },
93439b9004dSPhilipp Zabel 	{ /* sentinel */ }
93539b9004dSPhilipp Zabel };
93639b9004dSPhilipp Zabel MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids);
93739b9004dSPhilipp Zabel 
93839b9004dSPhilipp Zabel static int ipu_submodules_init(struct ipu_soc *ipu,
93939b9004dSPhilipp Zabel 		struct platform_device *pdev, unsigned long ipu_base,
94039b9004dSPhilipp Zabel 		struct clk *ipu_clk)
94139b9004dSPhilipp Zabel {
94239b9004dSPhilipp Zabel 	char *unit;
94339b9004dSPhilipp Zabel 	int ret;
94439b9004dSPhilipp Zabel 	struct device *dev = &pdev->dev;
94539b9004dSPhilipp Zabel 	const struct ipu_devtype *devtype = ipu->devtype;
94639b9004dSPhilipp Zabel 
9477d2691daSSteve Longerbeam 	ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs);
9487d2691daSSteve Longerbeam 	if (ret) {
9497d2691daSSteve Longerbeam 		unit = "cpmem";
9507d2691daSSteve Longerbeam 		goto err_cpmem;
9517d2691daSSteve Longerbeam 	}
9527d2691daSSteve Longerbeam 
9532ffd48f2SSteve Longerbeam 	ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs,
9542ffd48f2SSteve Longerbeam 			   IPU_CONF_CSI0_EN, ipu_clk);
9552ffd48f2SSteve Longerbeam 	if (ret) {
9562ffd48f2SSteve Longerbeam 		unit = "csi0";
9572ffd48f2SSteve Longerbeam 		goto err_csi_0;
9582ffd48f2SSteve Longerbeam 	}
9592ffd48f2SSteve Longerbeam 
9602ffd48f2SSteve Longerbeam 	ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs,
9612ffd48f2SSteve Longerbeam 			   IPU_CONF_CSI1_EN, ipu_clk);
9622ffd48f2SSteve Longerbeam 	if (ret) {
9632ffd48f2SSteve Longerbeam 		unit = "csi1";
9642ffd48f2SSteve Longerbeam 		goto err_csi_1;
9652ffd48f2SSteve Longerbeam 	}
9662ffd48f2SSteve Longerbeam 
9671aa8ea0dSSteve Longerbeam 	ret = ipu_ic_init(ipu, dev,
9681aa8ea0dSSteve Longerbeam 			  ipu_base + devtype->ic_ofs,
9691aa8ea0dSSteve Longerbeam 			  ipu_base + devtype->tpm_ofs);
9701aa8ea0dSSteve Longerbeam 	if (ret) {
9711aa8ea0dSSteve Longerbeam 		unit = "ic";
9721aa8ea0dSSteve Longerbeam 		goto err_ic;
9731aa8ea0dSSteve Longerbeam 	}
9741aa8ea0dSSteve Longerbeam 
9752d2ead45SSteve Longerbeam 	ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs,
9762d2ead45SSteve Longerbeam 			   IPU_CONF_VDI_EN | IPU_CONF_ISP_EN |
9772d2ead45SSteve Longerbeam 			   IPU_CONF_IC_INPUT);
9782d2ead45SSteve Longerbeam 	if (ret) {
9792d2ead45SSteve Longerbeam 		unit = "vdi";
9802d2ead45SSteve Longerbeam 		goto err_vdi;
9812d2ead45SSteve Longerbeam 	}
9822d2ead45SSteve Longerbeam 
983cd98e85aSSteve Longerbeam 	ret = ipu_image_convert_init(ipu, dev);
984cd98e85aSSteve Longerbeam 	if (ret) {
985cd98e85aSSteve Longerbeam 		unit = "image_convert";
986cd98e85aSSteve Longerbeam 		goto err_image_convert;
987cd98e85aSSteve Longerbeam 	}
988cd98e85aSSteve Longerbeam 
98939b9004dSPhilipp Zabel 	ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
99039b9004dSPhilipp Zabel 			  IPU_CONF_DI0_EN, ipu_clk);
99139b9004dSPhilipp Zabel 	if (ret) {
99239b9004dSPhilipp Zabel 		unit = "di0";
99339b9004dSPhilipp Zabel 		goto err_di_0;
99439b9004dSPhilipp Zabel 	}
99539b9004dSPhilipp Zabel 
99639b9004dSPhilipp Zabel 	ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs,
99739b9004dSPhilipp Zabel 			IPU_CONF_DI1_EN, ipu_clk);
99839b9004dSPhilipp Zabel 	if (ret) {
99939b9004dSPhilipp Zabel 		unit = "di1";
100039b9004dSPhilipp Zabel 		goto err_di_1;
100139b9004dSPhilipp Zabel 	}
100239b9004dSPhilipp Zabel 
100339b9004dSPhilipp Zabel 	ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs +
100439b9004dSPhilipp Zabel 			IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs);
100539b9004dSPhilipp Zabel 	if (ret) {
100639b9004dSPhilipp Zabel 		unit = "dc_template";
100739b9004dSPhilipp Zabel 		goto err_dc;
100839b9004dSPhilipp Zabel 	}
100939b9004dSPhilipp Zabel 
101039b9004dSPhilipp Zabel 	ret = ipu_dmfc_init(ipu, dev, ipu_base +
101139b9004dSPhilipp Zabel 			devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk);
101239b9004dSPhilipp Zabel 	if (ret) {
101339b9004dSPhilipp Zabel 		unit = "dmfc";
101439b9004dSPhilipp Zabel 		goto err_dmfc;
101539b9004dSPhilipp Zabel 	}
101639b9004dSPhilipp Zabel 
101739b9004dSPhilipp Zabel 	ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs);
101839b9004dSPhilipp Zabel 	if (ret) {
101939b9004dSPhilipp Zabel 		unit = "dp";
102039b9004dSPhilipp Zabel 		goto err_dp;
102139b9004dSPhilipp Zabel 	}
102239b9004dSPhilipp Zabel 
102335de925fSPhilipp Zabel 	ret = ipu_smfc_init(ipu, dev, ipu_base +
102435de925fSPhilipp Zabel 			devtype->cm_ofs + IPU_CM_SMFC_REG_OFS);
102535de925fSPhilipp Zabel 	if (ret) {
102635de925fSPhilipp Zabel 		unit = "smfc";
102735de925fSPhilipp Zabel 		goto err_smfc;
102835de925fSPhilipp Zabel 	}
102935de925fSPhilipp Zabel 
103039b9004dSPhilipp Zabel 	return 0;
103139b9004dSPhilipp Zabel 
103235de925fSPhilipp Zabel err_smfc:
103335de925fSPhilipp Zabel 	ipu_dp_exit(ipu);
103439b9004dSPhilipp Zabel err_dp:
103539b9004dSPhilipp Zabel 	ipu_dmfc_exit(ipu);
103639b9004dSPhilipp Zabel err_dmfc:
103739b9004dSPhilipp Zabel 	ipu_dc_exit(ipu);
103839b9004dSPhilipp Zabel err_dc:
103939b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 1);
104039b9004dSPhilipp Zabel err_di_1:
104139b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 0);
104239b9004dSPhilipp Zabel err_di_0:
1043cd98e85aSSteve Longerbeam 	ipu_image_convert_exit(ipu);
1044cd98e85aSSteve Longerbeam err_image_convert:
10452d2ead45SSteve Longerbeam 	ipu_vdi_exit(ipu);
10462d2ead45SSteve Longerbeam err_vdi:
10471aa8ea0dSSteve Longerbeam 	ipu_ic_exit(ipu);
10481aa8ea0dSSteve Longerbeam err_ic:
10492ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 1);
10502ffd48f2SSteve Longerbeam err_csi_1:
10512ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 0);
10522ffd48f2SSteve Longerbeam err_csi_0:
10537d2691daSSteve Longerbeam 	ipu_cpmem_exit(ipu);
10547d2691daSSteve Longerbeam err_cpmem:
105539b9004dSPhilipp Zabel 	dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret);
105639b9004dSPhilipp Zabel 	return ret;
105739b9004dSPhilipp Zabel }
105839b9004dSPhilipp Zabel 
105939b9004dSPhilipp Zabel static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs)
106039b9004dSPhilipp Zabel {
106139b9004dSPhilipp Zabel 	unsigned long status;
106239b9004dSPhilipp Zabel 	int i, bit, irq;
106339b9004dSPhilipp Zabel 
106439b9004dSPhilipp Zabel 	for (i = 0; i < num_regs; i++) {
106539b9004dSPhilipp Zabel 
106639b9004dSPhilipp Zabel 		status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i]));
106739b9004dSPhilipp Zabel 		status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i]));
106839b9004dSPhilipp Zabel 
106939b9004dSPhilipp Zabel 		for_each_set_bit(bit, &status, 32) {
1070682b7c1cSLinus Torvalds 			irq = irq_linear_revmap(ipu->domain,
1071682b7c1cSLinus Torvalds 						regs[i] * 32 + bit);
107239b9004dSPhilipp Zabel 			if (irq)
107339b9004dSPhilipp Zabel 				generic_handle_irq(irq);
107439b9004dSPhilipp Zabel 		}
107539b9004dSPhilipp Zabel 	}
107639b9004dSPhilipp Zabel }
107739b9004dSPhilipp Zabel 
1078bd0b9ac4SThomas Gleixner static void ipu_irq_handler(struct irq_desc *desc)
107939b9004dSPhilipp Zabel {
108039b9004dSPhilipp Zabel 	struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
10814d9efdfcSJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
108239b9004dSPhilipp Zabel 	const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14};
108339b9004dSPhilipp Zabel 
108439b9004dSPhilipp Zabel 	chained_irq_enter(chip, desc);
108539b9004dSPhilipp Zabel 
108639b9004dSPhilipp Zabel 	ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
108739b9004dSPhilipp Zabel 
108839b9004dSPhilipp Zabel 	chained_irq_exit(chip, desc);
108939b9004dSPhilipp Zabel }
109039b9004dSPhilipp Zabel 
1091bd0b9ac4SThomas Gleixner static void ipu_err_irq_handler(struct irq_desc *desc)
109239b9004dSPhilipp Zabel {
109339b9004dSPhilipp Zabel 	struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
10944d9efdfcSJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
109539b9004dSPhilipp Zabel 	const int int_reg[] = { 4, 5, 8, 9};
109639b9004dSPhilipp Zabel 
109739b9004dSPhilipp Zabel 	chained_irq_enter(chip, desc);
109839b9004dSPhilipp Zabel 
109939b9004dSPhilipp Zabel 	ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
110039b9004dSPhilipp Zabel 
110139b9004dSPhilipp Zabel 	chained_irq_exit(chip, desc);
110239b9004dSPhilipp Zabel }
110339b9004dSPhilipp Zabel 
1104682b7c1cSLinus Torvalds int ipu_map_irq(struct ipu_soc *ipu, int irq)
1105682b7c1cSLinus Torvalds {
1106682b7c1cSLinus Torvalds 	int virq;
1107682b7c1cSLinus Torvalds 
1108682b7c1cSLinus Torvalds 	virq = irq_linear_revmap(ipu->domain, irq);
1109682b7c1cSLinus Torvalds 	if (!virq)
1110682b7c1cSLinus Torvalds 		virq = irq_create_mapping(ipu->domain, irq);
1111682b7c1cSLinus Torvalds 
1112682b7c1cSLinus Torvalds 	return virq;
1113682b7c1cSLinus Torvalds }
1114682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_map_irq);
1115682b7c1cSLinus Torvalds 
111639b9004dSPhilipp Zabel int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
111739b9004dSPhilipp Zabel 		enum ipu_channel_irq irq_type)
111839b9004dSPhilipp Zabel {
1119682b7c1cSLinus Torvalds 	return ipu_map_irq(ipu, irq_type + channel->num);
112039b9004dSPhilipp Zabel }
112139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq);
112239b9004dSPhilipp Zabel 
112339b9004dSPhilipp Zabel static void ipu_submodules_exit(struct ipu_soc *ipu)
112439b9004dSPhilipp Zabel {
112535de925fSPhilipp Zabel 	ipu_smfc_exit(ipu);
112639b9004dSPhilipp Zabel 	ipu_dp_exit(ipu);
112739b9004dSPhilipp Zabel 	ipu_dmfc_exit(ipu);
112839b9004dSPhilipp Zabel 	ipu_dc_exit(ipu);
112939b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 1);
113039b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 0);
1131cd98e85aSSteve Longerbeam 	ipu_image_convert_exit(ipu);
11322d2ead45SSteve Longerbeam 	ipu_vdi_exit(ipu);
11331aa8ea0dSSteve Longerbeam 	ipu_ic_exit(ipu);
11342ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 1);
11352ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 0);
11367d2691daSSteve Longerbeam 	ipu_cpmem_exit(ipu);
113739b9004dSPhilipp Zabel }
113839b9004dSPhilipp Zabel 
113939b9004dSPhilipp Zabel static int platform_remove_devices_fn(struct device *dev, void *unused)
114039b9004dSPhilipp Zabel {
114139b9004dSPhilipp Zabel 	struct platform_device *pdev = to_platform_device(dev);
114239b9004dSPhilipp Zabel 
114339b9004dSPhilipp Zabel 	platform_device_unregister(pdev);
114439b9004dSPhilipp Zabel 
114539b9004dSPhilipp Zabel 	return 0;
114639b9004dSPhilipp Zabel }
114739b9004dSPhilipp Zabel 
114839b9004dSPhilipp Zabel static void platform_device_unregister_children(struct platform_device *pdev)
114939b9004dSPhilipp Zabel {
115039b9004dSPhilipp Zabel 	device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn);
115139b9004dSPhilipp Zabel }
115239b9004dSPhilipp Zabel 
115339b9004dSPhilipp Zabel struct ipu_platform_reg {
115439b9004dSPhilipp Zabel 	struct ipu_client_platformdata pdata;
115539b9004dSPhilipp Zabel 	const char *name;
115639b9004dSPhilipp Zabel };
115739b9004dSPhilipp Zabel 
1158304e6be6SPhilipp Zabel /* These must be in the order of the corresponding device tree port nodes */
1159310944d1SPhilipp Zabel static struct ipu_platform_reg client_reg[] = {
116039b9004dSPhilipp Zabel 	{
116139b9004dSPhilipp Zabel 		.pdata = {
1162304e6be6SPhilipp Zabel 			.csi = 0,
1163304e6be6SPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_CSI0,
1164304e6be6SPhilipp Zabel 			.dma[1] = -EINVAL,
1165304e6be6SPhilipp Zabel 		},
116688287ec3SSteve Longerbeam 		.name = "imx-ipuv3-csi",
1167304e6be6SPhilipp Zabel 	}, {
1168304e6be6SPhilipp Zabel 		.pdata = {
1169304e6be6SPhilipp Zabel 			.csi = 1,
1170304e6be6SPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_CSI1,
1171304e6be6SPhilipp Zabel 			.dma[1] = -EINVAL,
1172304e6be6SPhilipp Zabel 		},
117388287ec3SSteve Longerbeam 		.name = "imx-ipuv3-csi",
1174304e6be6SPhilipp Zabel 	}, {
1175304e6be6SPhilipp Zabel 		.pdata = {
117639b9004dSPhilipp Zabel 			.di = 0,
117739b9004dSPhilipp Zabel 			.dc = 5,
117839b9004dSPhilipp Zabel 			.dp = IPU_DP_FLOW_SYNC_BG,
117939b9004dSPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC,
118039b9004dSPhilipp Zabel 			.dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC,
118139b9004dSPhilipp Zabel 		},
118239b9004dSPhilipp Zabel 		.name = "imx-ipuv3-crtc",
118339b9004dSPhilipp Zabel 	}, {
118439b9004dSPhilipp Zabel 		.pdata = {
118539b9004dSPhilipp Zabel 			.di = 1,
118639b9004dSPhilipp Zabel 			.dc = 1,
118739b9004dSPhilipp Zabel 			.dp = -EINVAL,
118839b9004dSPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC,
118939b9004dSPhilipp Zabel 			.dma[1] = -EINVAL,
119039b9004dSPhilipp Zabel 		},
119139b9004dSPhilipp Zabel 		.name = "imx-ipuv3-crtc",
119239b9004dSPhilipp Zabel 	},
119339b9004dSPhilipp Zabel };
119439b9004dSPhilipp Zabel 
119539b9004dSPhilipp Zabel static DEFINE_MUTEX(ipu_client_id_mutex);
119639b9004dSPhilipp Zabel static int ipu_client_id;
119739b9004dSPhilipp Zabel 
1198d6ca8ca7SPhilipp Zabel static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base)
119939b9004dSPhilipp Zabel {
120039b9004dSPhilipp Zabel 	struct device *dev = ipu->dev;
120139b9004dSPhilipp Zabel 	unsigned i;
120239b9004dSPhilipp Zabel 	int id, ret;
120339b9004dSPhilipp Zabel 
120439b9004dSPhilipp Zabel 	mutex_lock(&ipu_client_id_mutex);
120539b9004dSPhilipp Zabel 	id = ipu_client_id;
120639b9004dSPhilipp Zabel 	ipu_client_id += ARRAY_SIZE(client_reg);
120739b9004dSPhilipp Zabel 	mutex_unlock(&ipu_client_id_mutex);
120839b9004dSPhilipp Zabel 
120939b9004dSPhilipp Zabel 	for (i = 0; i < ARRAY_SIZE(client_reg); i++) {
1210310944d1SPhilipp Zabel 		struct ipu_platform_reg *reg = &client_reg[i];
121139b9004dSPhilipp Zabel 		struct platform_device *pdev;
121217e05217SPhilipp Zabel 		struct device_node *of_node;
121317e05217SPhilipp Zabel 
121417e05217SPhilipp Zabel 		/* Associate subdevice with the corresponding port node */
121517e05217SPhilipp Zabel 		of_node = of_graph_get_port_by_id(dev->of_node, i);
121617e05217SPhilipp Zabel 		if (!of_node) {
121717e05217SPhilipp Zabel 			dev_info(dev,
121817e05217SPhilipp Zabel 				 "no port@%d node in %s, not using %s%d\n",
121917e05217SPhilipp Zabel 				 i, dev->of_node->full_name,
122017e05217SPhilipp Zabel 				 (i / 2) ? "DI" : "CSI", i % 2);
122117e05217SPhilipp Zabel 			continue;
122217e05217SPhilipp Zabel 		}
122339b9004dSPhilipp Zabel 
1224304e6be6SPhilipp Zabel 		pdev = platform_device_alloc(reg->name, id++);
1225304e6be6SPhilipp Zabel 		if (!pdev) {
1226304e6be6SPhilipp Zabel 			ret = -ENOMEM;
1227304e6be6SPhilipp Zabel 			goto err_register;
1228304e6be6SPhilipp Zabel 		}
122939b9004dSPhilipp Zabel 
1230304e6be6SPhilipp Zabel 		pdev->dev.parent = dev;
1231304e6be6SPhilipp Zabel 
1232310944d1SPhilipp Zabel 		reg->pdata.of_node = of_node;
1233304e6be6SPhilipp Zabel 		ret = platform_device_add_data(pdev, &reg->pdata,
1234304e6be6SPhilipp Zabel 					       sizeof(reg->pdata));
1235304e6be6SPhilipp Zabel 		if (!ret)
1236304e6be6SPhilipp Zabel 			ret = platform_device_add(pdev);
1237304e6be6SPhilipp Zabel 		if (ret) {
1238304e6be6SPhilipp Zabel 			platform_device_put(pdev);
123939b9004dSPhilipp Zabel 			goto err_register;
124039b9004dSPhilipp Zabel 		}
1241503fe87bSPhilipp Zabel 
1242503fe87bSPhilipp Zabel 		/*
1243503fe87bSPhilipp Zabel 		 * Set of_node only after calling platform_device_add. Otherwise
1244503fe87bSPhilipp Zabel 		 * the platform:imx-ipuv3-crtc modalias won't be used.
1245503fe87bSPhilipp Zabel 		 */
1246503fe87bSPhilipp Zabel 		pdev->dev.of_node = of_node;
1247e4946cdcSAxel Lin 	}
124839b9004dSPhilipp Zabel 
124939b9004dSPhilipp Zabel 	return 0;
125039b9004dSPhilipp Zabel 
125139b9004dSPhilipp Zabel err_register:
125239b9004dSPhilipp Zabel 	platform_device_unregister_children(to_platform_device(dev));
125339b9004dSPhilipp Zabel 
125439b9004dSPhilipp Zabel 	return ret;
125539b9004dSPhilipp Zabel }
125639b9004dSPhilipp Zabel 
125739b9004dSPhilipp Zabel 
125839b9004dSPhilipp Zabel static int ipu_irq_init(struct ipu_soc *ipu)
125939b9004dSPhilipp Zabel {
126039b9004dSPhilipp Zabel 	struct irq_chip_generic *gc;
126139b9004dSPhilipp Zabel 	struct irq_chip_type *ct;
126239b9004dSPhilipp Zabel 	unsigned long unused[IPU_NUM_IRQS / 32] = {
126339b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
126439b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
126539b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
126639b9004dSPhilipp Zabel 		0x4077ffff, 0xffe7e1fd,
126739b9004dSPhilipp Zabel 		0x23fffffe, 0x8880fff0,
126839b9004dSPhilipp Zabel 		0xf98fe7d0, 0xfff81fff,
126939b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
127039b9004dSPhilipp Zabel 		0x00000000,
127139b9004dSPhilipp Zabel 	};
127239b9004dSPhilipp Zabel 	int ret, i;
127339b9004dSPhilipp Zabel 
127439b9004dSPhilipp Zabel 	ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS,
127539b9004dSPhilipp Zabel 					    &irq_generic_chip_ops, ipu);
127639b9004dSPhilipp Zabel 	if (!ipu->domain) {
127739b9004dSPhilipp Zabel 		dev_err(ipu->dev, "failed to add irq domain\n");
127839b9004dSPhilipp Zabel 		return -ENODEV;
127939b9004dSPhilipp Zabel 	}
128039b9004dSPhilipp Zabel 
128139b9004dSPhilipp Zabel 	ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU",
1282ca0141deSRob Herring 					     handle_level_irq, 0, 0, 0);
128339b9004dSPhilipp Zabel 	if (ret < 0) {
128439b9004dSPhilipp Zabel 		dev_err(ipu->dev, "failed to alloc generic irq chips\n");
128539b9004dSPhilipp Zabel 		irq_domain_remove(ipu->domain);
128639b9004dSPhilipp Zabel 		return ret;
128739b9004dSPhilipp Zabel 	}
128839b9004dSPhilipp Zabel 
1289510e6426SRussell King 	for (i = 0; i < IPU_NUM_IRQS; i += 32)
1290510e6426SRussell King 		ipu_cm_write(ipu, 0, IPU_INT_CTRL(i / 32));
1291510e6426SRussell King 
129239b9004dSPhilipp Zabel 	for (i = 0; i < IPU_NUM_IRQS; i += 32) {
129339b9004dSPhilipp Zabel 		gc = irq_get_domain_generic_chip(ipu->domain, i);
129439b9004dSPhilipp Zabel 		gc->reg_base = ipu->cm_reg;
129539b9004dSPhilipp Zabel 		gc->unused = unused[i / 32];
129639b9004dSPhilipp Zabel 		ct = gc->chip_types;
129739b9004dSPhilipp Zabel 		ct->chip.irq_ack = irq_gc_ack_set_bit;
129839b9004dSPhilipp Zabel 		ct->chip.irq_mask = irq_gc_mask_clr_bit;
129939b9004dSPhilipp Zabel 		ct->chip.irq_unmask = irq_gc_mask_set_bit;
130039b9004dSPhilipp Zabel 		ct->regs.ack = IPU_INT_STAT(i / 32);
130139b9004dSPhilipp Zabel 		ct->regs.mask = IPU_INT_CTRL(i / 32);
130239b9004dSPhilipp Zabel 	}
130339b9004dSPhilipp Zabel 
130486f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu);
130586f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler,
130686f5e733SRussell King 					 ipu);
130739b9004dSPhilipp Zabel 
130839b9004dSPhilipp Zabel 	return 0;
130939b9004dSPhilipp Zabel }
131039b9004dSPhilipp Zabel 
131139b9004dSPhilipp Zabel static void ipu_irq_exit(struct ipu_soc *ipu)
131239b9004dSPhilipp Zabel {
131339b9004dSPhilipp Zabel 	int i, irq;
131439b9004dSPhilipp Zabel 
131586f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL);
131686f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL);
131739b9004dSPhilipp Zabel 
131839b9004dSPhilipp Zabel 	/* TODO: remove irq_domain_generic_chips */
131939b9004dSPhilipp Zabel 
132039b9004dSPhilipp Zabel 	for (i = 0; i < IPU_NUM_IRQS; i++) {
132139b9004dSPhilipp Zabel 		irq = irq_linear_revmap(ipu->domain, i);
132239b9004dSPhilipp Zabel 		if (irq)
132339b9004dSPhilipp Zabel 			irq_dispose_mapping(irq);
132439b9004dSPhilipp Zabel 	}
132539b9004dSPhilipp Zabel 
132639b9004dSPhilipp Zabel 	irq_domain_remove(ipu->domain);
132739b9004dSPhilipp Zabel }
132839b9004dSPhilipp Zabel 
13293feb049fSSteve Longerbeam void ipu_dump(struct ipu_soc *ipu)
13303feb049fSSteve Longerbeam {
13313feb049fSSteve Longerbeam 	int i;
13323feb049fSSteve Longerbeam 
13333feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n",
13343feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_CONF));
13353feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n",
13363feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CONF));
13373feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n",
13383feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_EN(0)));
13393feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n",
13403feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_EN(32)));
13413feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n",
13423feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_PRI(0)));
13433feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n",
13443feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_PRI(32)));
13453feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n",
13463feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_BAND_EN(0)));
13473feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n",
13483feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_BAND_EN(32)));
13493feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n",
13503feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0)));
13513feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n",
13523feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32)));
13533feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n",
13543feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_PROC_FLOW1));
13553feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n",
13563feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_PROC_FLOW2));
13573feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n",
13583feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_PROC_FLOW3));
13593feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n",
13603feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_DISP_FLOW1));
13613feb049fSSteve Longerbeam 	for (i = 0; i < 15; i++)
13623feb049fSSteve Longerbeam 		dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i,
13633feb049fSSteve Longerbeam 			ipu_cm_read(ipu, IPU_INT_CTRL(i)));
13643feb049fSSteve Longerbeam }
13653feb049fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_dump);
13663feb049fSSteve Longerbeam 
136739b9004dSPhilipp Zabel static int ipu_probe(struct platform_device *pdev)
136839b9004dSPhilipp Zabel {
1369572a7615SSteve Longerbeam 	struct device_node *np = pdev->dev.of_node;
137039b9004dSPhilipp Zabel 	struct ipu_soc *ipu;
137139b9004dSPhilipp Zabel 	struct resource *res;
137239b9004dSPhilipp Zabel 	unsigned long ipu_base;
137339b9004dSPhilipp Zabel 	int i, ret, irq_sync, irq_err;
137439b9004dSPhilipp Zabel 	const struct ipu_devtype *devtype;
137539b9004dSPhilipp Zabel 
1376e92e4478SLABBE Corentin 	devtype = of_device_get_match_data(&pdev->dev);
1377e92e4478SLABBE Corentin 	if (!devtype)
1378e92e4478SLABBE Corentin 		return -EINVAL;
137939b9004dSPhilipp Zabel 
138039b9004dSPhilipp Zabel 	irq_sync = platform_get_irq(pdev, 0);
138139b9004dSPhilipp Zabel 	irq_err = platform_get_irq(pdev, 1);
138239b9004dSPhilipp Zabel 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
138339b9004dSPhilipp Zabel 
138439b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n",
138539b9004dSPhilipp Zabel 			irq_sync, irq_err);
138639b9004dSPhilipp Zabel 
138739b9004dSPhilipp Zabel 	if (!res || irq_sync < 0 || irq_err < 0)
138839b9004dSPhilipp Zabel 		return -ENODEV;
138939b9004dSPhilipp Zabel 
139039b9004dSPhilipp Zabel 	ipu_base = res->start;
139139b9004dSPhilipp Zabel 
139239b9004dSPhilipp Zabel 	ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL);
139339b9004dSPhilipp Zabel 	if (!ipu)
139439b9004dSPhilipp Zabel 		return -ENODEV;
139539b9004dSPhilipp Zabel 
139639b9004dSPhilipp Zabel 	for (i = 0; i < 64; i++)
139739b9004dSPhilipp Zabel 		ipu->channel[i].ipu = ipu;
139839b9004dSPhilipp Zabel 	ipu->devtype = devtype;
139939b9004dSPhilipp Zabel 	ipu->ipu_type = devtype->type;
1400572a7615SSteve Longerbeam 	ipu->id = of_alias_get_id(np, "ipu");
140139b9004dSPhilipp Zabel 
140239b9004dSPhilipp Zabel 	spin_lock_init(&ipu->lock);
140339b9004dSPhilipp Zabel 	mutex_init(&ipu->channel_lock);
140439b9004dSPhilipp Zabel 
140539b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "cm_reg:   0x%08lx\n",
140639b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs);
140739b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "idmac:    0x%08lx\n",
140839b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS);
140939b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "cpmem:    0x%08lx\n",
141039b9004dSPhilipp Zabel 			ipu_base + devtype->cpmem_ofs);
14112ffd48f2SSteve Longerbeam 	dev_dbg(&pdev->dev, "csi0:    0x%08lx\n",
14122ffd48f2SSteve Longerbeam 			ipu_base + devtype->csi0_ofs);
14132ffd48f2SSteve Longerbeam 	dev_dbg(&pdev->dev, "csi1:    0x%08lx\n",
14142ffd48f2SSteve Longerbeam 			ipu_base + devtype->csi1_ofs);
14151aa8ea0dSSteve Longerbeam 	dev_dbg(&pdev->dev, "ic:      0x%08lx\n",
14161aa8ea0dSSteve Longerbeam 			ipu_base + devtype->ic_ofs);
141739b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "disp0:    0x%08lx\n",
141839b9004dSPhilipp Zabel 			ipu_base + devtype->disp0_ofs);
141939b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "disp1:    0x%08lx\n",
142039b9004dSPhilipp Zabel 			ipu_base + devtype->disp1_ofs);
142139b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "srm:      0x%08lx\n",
142239b9004dSPhilipp Zabel 			ipu_base + devtype->srm_ofs);
142339b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "tpm:      0x%08lx\n",
142439b9004dSPhilipp Zabel 			ipu_base + devtype->tpm_ofs);
142539b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "dc:       0x%08lx\n",
142639b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS);
142739b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "ic:       0x%08lx\n",
142839b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS);
142939b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "dmfc:     0x%08lx\n",
143039b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS);
143139b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "vdi:      0x%08lx\n",
143239b9004dSPhilipp Zabel 			ipu_base + devtype->vdi_ofs);
143339b9004dSPhilipp Zabel 
143439b9004dSPhilipp Zabel 	ipu->cm_reg = devm_ioremap(&pdev->dev,
143539b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs, PAGE_SIZE);
143639b9004dSPhilipp Zabel 	ipu->idmac_reg = devm_ioremap(&pdev->dev,
143739b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS,
143839b9004dSPhilipp Zabel 			PAGE_SIZE);
143939b9004dSPhilipp Zabel 
14407d2691daSSteve Longerbeam 	if (!ipu->cm_reg || !ipu->idmac_reg)
144139b9004dSPhilipp Zabel 		return -ENOMEM;
144239b9004dSPhilipp Zabel 
144339b9004dSPhilipp Zabel 	ipu->clk = devm_clk_get(&pdev->dev, "bus");
144439b9004dSPhilipp Zabel 	if (IS_ERR(ipu->clk)) {
144539b9004dSPhilipp Zabel 		ret = PTR_ERR(ipu->clk);
144639b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "clk_get failed with %d", ret);
144739b9004dSPhilipp Zabel 		return ret;
144839b9004dSPhilipp Zabel 	}
144939b9004dSPhilipp Zabel 
145039b9004dSPhilipp Zabel 	platform_set_drvdata(pdev, ipu);
145139b9004dSPhilipp Zabel 
145239b9004dSPhilipp Zabel 	ret = clk_prepare_enable(ipu->clk);
145339b9004dSPhilipp Zabel 	if (ret) {
145439b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
145539b9004dSPhilipp Zabel 		return ret;
145639b9004dSPhilipp Zabel 	}
145739b9004dSPhilipp Zabel 
145839b9004dSPhilipp Zabel 	ipu->dev = &pdev->dev;
145939b9004dSPhilipp Zabel 	ipu->irq_sync = irq_sync;
146039b9004dSPhilipp Zabel 	ipu->irq_err = irq_err;
146139b9004dSPhilipp Zabel 
146239b9004dSPhilipp Zabel 	ret = device_reset(&pdev->dev);
146339b9004dSPhilipp Zabel 	if (ret) {
146439b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "failed to reset: %d\n", ret);
146539b9004dSPhilipp Zabel 		goto out_failed_reset;
146639b9004dSPhilipp Zabel 	}
146739b9004dSPhilipp Zabel 	ret = ipu_memory_reset(ipu);
146839b9004dSPhilipp Zabel 	if (ret)
146939b9004dSPhilipp Zabel 		goto out_failed_reset;
147039b9004dSPhilipp Zabel 
1471596a65d1SDavid Jander 	ret = ipu_irq_init(ipu);
1472596a65d1SDavid Jander 	if (ret)
1473596a65d1SDavid Jander 		goto out_failed_irq;
1474596a65d1SDavid Jander 
147539b9004dSPhilipp Zabel 	/* Set MCU_T to divide MCU access window into 2 */
147639b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
147739b9004dSPhilipp Zabel 			IPU_DISP_GEN);
147839b9004dSPhilipp Zabel 
147939b9004dSPhilipp Zabel 	ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk);
148039b9004dSPhilipp Zabel 	if (ret)
148139b9004dSPhilipp Zabel 		goto failed_submodules_init;
148239b9004dSPhilipp Zabel 
1483d6ca8ca7SPhilipp Zabel 	ret = ipu_add_client_devices(ipu, ipu_base);
148439b9004dSPhilipp Zabel 	if (ret) {
148539b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "adding client devices failed with %d\n",
148639b9004dSPhilipp Zabel 				ret);
148739b9004dSPhilipp Zabel 		goto failed_add_clients;
148839b9004dSPhilipp Zabel 	}
148939b9004dSPhilipp Zabel 
149039b9004dSPhilipp Zabel 	dev_info(&pdev->dev, "%s probed\n", devtype->name);
149139b9004dSPhilipp Zabel 
149239b9004dSPhilipp Zabel 	return 0;
149339b9004dSPhilipp Zabel 
149439b9004dSPhilipp Zabel failed_add_clients:
149539b9004dSPhilipp Zabel 	ipu_submodules_exit(ipu);
149639b9004dSPhilipp Zabel failed_submodules_init:
149739b9004dSPhilipp Zabel 	ipu_irq_exit(ipu);
149839b9004dSPhilipp Zabel out_failed_irq:
1499596a65d1SDavid Jander out_failed_reset:
150039b9004dSPhilipp Zabel 	clk_disable_unprepare(ipu->clk);
150139b9004dSPhilipp Zabel 	return ret;
150239b9004dSPhilipp Zabel }
150339b9004dSPhilipp Zabel 
150439b9004dSPhilipp Zabel static int ipu_remove(struct platform_device *pdev)
150539b9004dSPhilipp Zabel {
150639b9004dSPhilipp Zabel 	struct ipu_soc *ipu = platform_get_drvdata(pdev);
150739b9004dSPhilipp Zabel 
150839b9004dSPhilipp Zabel 	platform_device_unregister_children(pdev);
150939b9004dSPhilipp Zabel 	ipu_submodules_exit(ipu);
151039b9004dSPhilipp Zabel 	ipu_irq_exit(ipu);
151139b9004dSPhilipp Zabel 
151239b9004dSPhilipp Zabel 	clk_disable_unprepare(ipu->clk);
151339b9004dSPhilipp Zabel 
151439b9004dSPhilipp Zabel 	return 0;
151539b9004dSPhilipp Zabel }
151639b9004dSPhilipp Zabel 
151739b9004dSPhilipp Zabel static struct platform_driver imx_ipu_driver = {
151839b9004dSPhilipp Zabel 	.driver = {
151939b9004dSPhilipp Zabel 		.name = "imx-ipuv3",
152039b9004dSPhilipp Zabel 		.of_match_table = imx_ipu_dt_ids,
152139b9004dSPhilipp Zabel 	},
152239b9004dSPhilipp Zabel 	.probe = ipu_probe,
152339b9004dSPhilipp Zabel 	.remove = ipu_remove,
152439b9004dSPhilipp Zabel };
152539b9004dSPhilipp Zabel 
152639b9004dSPhilipp Zabel module_platform_driver(imx_ipu_driver);
152739b9004dSPhilipp Zabel 
152839b9004dSPhilipp Zabel MODULE_ALIAS("platform:imx-ipuv3");
152939b9004dSPhilipp Zabel MODULE_DESCRIPTION("i.MX IPU v3 driver");
153039b9004dSPhilipp Zabel MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
153139b9004dSPhilipp Zabel MODULE_LICENSE("GPL");
1532