xref: /openbmc/linux/drivers/gpu/ipu-v3/ipu-common.c (revision c942fddf)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
239b9004dSPhilipp Zabel /*
339b9004dSPhilipp Zabel  * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
439b9004dSPhilipp Zabel  * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
539b9004dSPhilipp Zabel  */
639b9004dSPhilipp Zabel #include <linux/module.h>
739b9004dSPhilipp Zabel #include <linux/export.h>
839b9004dSPhilipp Zabel #include <linux/types.h>
939b9004dSPhilipp Zabel #include <linux/reset.h>
1039b9004dSPhilipp Zabel #include <linux/platform_device.h>
1139b9004dSPhilipp Zabel #include <linux/err.h>
1239b9004dSPhilipp Zabel #include <linux/spinlock.h>
1339b9004dSPhilipp Zabel #include <linux/delay.h>
1439b9004dSPhilipp Zabel #include <linux/interrupt.h>
1539b9004dSPhilipp Zabel #include <linux/io.h>
1639b9004dSPhilipp Zabel #include <linux/clk.h>
1739b9004dSPhilipp Zabel #include <linux/list.h>
1839b9004dSPhilipp Zabel #include <linux/irq.h>
1939b9004dSPhilipp Zabel #include <linux/irqchip/chained_irq.h>
2039b9004dSPhilipp Zabel #include <linux/irqdomain.h>
2139b9004dSPhilipp Zabel #include <linux/of_device.h>
22304e6be6SPhilipp Zabel #include <linux/of_graph.h>
2339b9004dSPhilipp Zabel 
2439b9004dSPhilipp Zabel #include <drm/drm_fourcc.h>
2539b9004dSPhilipp Zabel 
2639b9004dSPhilipp Zabel #include <video/imx-ipu-v3.h>
2739b9004dSPhilipp Zabel #include "ipu-prv.h"
2839b9004dSPhilipp Zabel 
2939b9004dSPhilipp Zabel static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset)
3039b9004dSPhilipp Zabel {
3139b9004dSPhilipp Zabel 	return readl(ipu->cm_reg + offset);
3239b9004dSPhilipp Zabel }
3339b9004dSPhilipp Zabel 
3439b9004dSPhilipp Zabel static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset)
3539b9004dSPhilipp Zabel {
3639b9004dSPhilipp Zabel 	writel(value, ipu->cm_reg + offset);
3739b9004dSPhilipp Zabel }
3839b9004dSPhilipp Zabel 
39572a7615SSteve Longerbeam int ipu_get_num(struct ipu_soc *ipu)
40572a7615SSteve Longerbeam {
41572a7615SSteve Longerbeam 	return ipu->id;
42572a7615SSteve Longerbeam }
43572a7615SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_get_num);
44572a7615SSteve Longerbeam 
45f9bb7acbSPhilipp Zabel void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync)
4639b9004dSPhilipp Zabel {
4739b9004dSPhilipp Zabel 	u32 val;
4839b9004dSPhilipp Zabel 
4939b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_SRM_PRI2);
50f9bb7acbSPhilipp Zabel 	val &= ~DP_S_SRM_MODE_MASK;
51f9bb7acbSPhilipp Zabel 	val |= sync ? DP_S_SRM_MODE_NEXT_FRAME :
52f9bb7acbSPhilipp Zabel 		      DP_S_SRM_MODE_NOW;
5339b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_SRM_PRI2);
5439b9004dSPhilipp Zabel }
55f9bb7acbSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_srm_dp_update);
5639b9004dSPhilipp Zabel 
5739b9004dSPhilipp Zabel enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc)
5839b9004dSPhilipp Zabel {
5939b9004dSPhilipp Zabel 	switch (drm_fourcc) {
600cb8b757SPhilipp Zabel 	case DRM_FORMAT_ARGB1555:
610cb8b757SPhilipp Zabel 	case DRM_FORMAT_ABGR1555:
620cb8b757SPhilipp Zabel 	case DRM_FORMAT_RGBA5551:
630cb8b757SPhilipp Zabel 	case DRM_FORMAT_BGRA5551:
6439b9004dSPhilipp Zabel 	case DRM_FORMAT_RGB565:
6539b9004dSPhilipp Zabel 	case DRM_FORMAT_BGR565:
6639b9004dSPhilipp Zabel 	case DRM_FORMAT_RGB888:
6739b9004dSPhilipp Zabel 	case DRM_FORMAT_BGR888:
687d2e8a20SLucas Stach 	case DRM_FORMAT_ARGB4444:
6939b9004dSPhilipp Zabel 	case DRM_FORMAT_XRGB8888:
7039b9004dSPhilipp Zabel 	case DRM_FORMAT_XBGR8888:
7139b9004dSPhilipp Zabel 	case DRM_FORMAT_RGBX8888:
7239b9004dSPhilipp Zabel 	case DRM_FORMAT_BGRX8888:
7339b9004dSPhilipp Zabel 	case DRM_FORMAT_ARGB8888:
7439b9004dSPhilipp Zabel 	case DRM_FORMAT_ABGR8888:
7539b9004dSPhilipp Zabel 	case DRM_FORMAT_RGBA8888:
7639b9004dSPhilipp Zabel 	case DRM_FORMAT_BGRA8888:
77e72db3b1SPhilipp Zabel 	case DRM_FORMAT_RGB565_A8:
78e72db3b1SPhilipp Zabel 	case DRM_FORMAT_BGR565_A8:
79e72db3b1SPhilipp Zabel 	case DRM_FORMAT_RGB888_A8:
80e72db3b1SPhilipp Zabel 	case DRM_FORMAT_BGR888_A8:
81e72db3b1SPhilipp Zabel 	case DRM_FORMAT_RGBX8888_A8:
82e72db3b1SPhilipp Zabel 	case DRM_FORMAT_BGRX8888_A8:
8339b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_RGB;
8439b9004dSPhilipp Zabel 	case DRM_FORMAT_YUYV:
8539b9004dSPhilipp Zabel 	case DRM_FORMAT_UYVY:
8639b9004dSPhilipp Zabel 	case DRM_FORMAT_YUV420:
8739b9004dSPhilipp Zabel 	case DRM_FORMAT_YVU420:
889a34cef0SSteve Longerbeam 	case DRM_FORMAT_YUV422:
899a34cef0SSteve Longerbeam 	case DRM_FORMAT_YVU422:
90c9d508c2SPhilipp Zabel 	case DRM_FORMAT_YUV444:
91c9d508c2SPhilipp Zabel 	case DRM_FORMAT_YVU444:
929a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV12:
939a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV21:
949a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV16:
959a34cef0SSteve Longerbeam 	case DRM_FORMAT_NV61:
9639b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_YUV;
9739b9004dSPhilipp Zabel 	default:
9839b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_UNKNOWN;
9939b9004dSPhilipp Zabel 	}
10039b9004dSPhilipp Zabel }
10139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace);
10239b9004dSPhilipp Zabel 
10339b9004dSPhilipp Zabel enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat)
10439b9004dSPhilipp Zabel {
10539b9004dSPhilipp Zabel 	switch (pixelformat) {
10639b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YUV420:
10739b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YVU420:
1089a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_YUV422P:
10939b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_UYVY:
11039b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YUYV:
1119a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV12:
1129a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV21:
1139a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV16:
1149a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV61:
11539b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_YUV;
1165c41bb60SPhilipp Zabel 	case V4L2_PIX_FMT_XRGB32:
1175c41bb60SPhilipp Zabel 	case V4L2_PIX_FMT_XBGR32:
11839b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB32:
11939b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_BGR32:
12039b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB24:
12139b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_BGR24:
12239b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB565:
12339b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_RGB;
12439b9004dSPhilipp Zabel 	default:
12539b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_UNKNOWN;
12639b9004dSPhilipp Zabel 	}
12739b9004dSPhilipp Zabel }
12839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace);
12939b9004dSPhilipp Zabel 
1304cea940dSSteve Longerbeam bool ipu_pixelformat_is_planar(u32 pixelformat)
1314cea940dSSteve Longerbeam {
1324cea940dSSteve Longerbeam 	switch (pixelformat) {
1334cea940dSSteve Longerbeam 	case V4L2_PIX_FMT_YUV420:
1344cea940dSSteve Longerbeam 	case V4L2_PIX_FMT_YVU420:
1359a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_YUV422P:
1369a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV12:
1379a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV21:
1389a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV16:
1399a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV61:
1404cea940dSSteve Longerbeam 		return true;
1414cea940dSSteve Longerbeam 	}
1424cea940dSSteve Longerbeam 
1434cea940dSSteve Longerbeam 	return false;
1444cea940dSSteve Longerbeam }
1454cea940dSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar);
1464cea940dSSteve Longerbeam 
147ae0e9708SSteve Longerbeam enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code)
148ae0e9708SSteve Longerbeam {
149ae0e9708SSteve Longerbeam 	switch (mbus_code & 0xf000) {
150ae0e9708SSteve Longerbeam 	case 0x1000:
151ae0e9708SSteve Longerbeam 		return IPUV3_COLORSPACE_RGB;
152ae0e9708SSteve Longerbeam 	case 0x2000:
153ae0e9708SSteve Longerbeam 		return IPUV3_COLORSPACE_YUV;
154ae0e9708SSteve Longerbeam 	default:
155ae0e9708SSteve Longerbeam 		return IPUV3_COLORSPACE_UNKNOWN;
156ae0e9708SSteve Longerbeam 	}
157ae0e9708SSteve Longerbeam }
158ae0e9708SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace);
159ae0e9708SSteve Longerbeam 
1606930afdcSSteve Longerbeam int ipu_stride_to_bytes(u32 pixel_stride, u32 pixelformat)
1616930afdcSSteve Longerbeam {
1626930afdcSSteve Longerbeam 	switch (pixelformat) {
1636930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_YUV420:
1646930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_YVU420:
1659a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_YUV422P:
1669a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV12:
1679a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV21:
1689a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV16:
1699a34cef0SSteve Longerbeam 	case V4L2_PIX_FMT_NV61:
1706930afdcSSteve Longerbeam 		/*
1716930afdcSSteve Longerbeam 		 * for the planar YUV formats, the stride passed to
1726930afdcSSteve Longerbeam 		 * cpmem must be the stride in bytes of the Y plane.
1736930afdcSSteve Longerbeam 		 * And all the planar YUV formats have an 8-bit
1746930afdcSSteve Longerbeam 		 * Y component.
1756930afdcSSteve Longerbeam 		 */
1766930afdcSSteve Longerbeam 		return (8 * pixel_stride) >> 3;
1776930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_RGB565:
1786930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_YUYV:
1796930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_UYVY:
1806930afdcSSteve Longerbeam 		return (16 * pixel_stride) >> 3;
1816930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_BGR24:
1826930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_RGB24:
1836930afdcSSteve Longerbeam 		return (24 * pixel_stride) >> 3;
1846930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_BGR32:
1856930afdcSSteve Longerbeam 	case V4L2_PIX_FMT_RGB32:
1865c41bb60SPhilipp Zabel 	case V4L2_PIX_FMT_XBGR32:
1875c41bb60SPhilipp Zabel 	case V4L2_PIX_FMT_XRGB32:
1886930afdcSSteve Longerbeam 		return (32 * pixel_stride) >> 3;
1896930afdcSSteve Longerbeam 	default:
1906930afdcSSteve Longerbeam 		break;
1916930afdcSSteve Longerbeam 	}
1926930afdcSSteve Longerbeam 
1936930afdcSSteve Longerbeam 	return -EINVAL;
1946930afdcSSteve Longerbeam }
1956930afdcSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_stride_to_bytes);
1966930afdcSSteve Longerbeam 
197f835f386SSteve Longerbeam int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees,
198f835f386SSteve Longerbeam 			    bool hflip, bool vflip)
199f835f386SSteve Longerbeam {
200f835f386SSteve Longerbeam 	u32 r90, vf, hf;
201f835f386SSteve Longerbeam 
202f835f386SSteve Longerbeam 	switch (degrees) {
203f835f386SSteve Longerbeam 	case 0:
204f835f386SSteve Longerbeam 		vf = hf = r90 = 0;
205f835f386SSteve Longerbeam 		break;
206f835f386SSteve Longerbeam 	case 90:
207f835f386SSteve Longerbeam 		vf = hf = 0;
208f835f386SSteve Longerbeam 		r90 = 1;
209f835f386SSteve Longerbeam 		break;
210f835f386SSteve Longerbeam 	case 180:
211f835f386SSteve Longerbeam 		vf = hf = 1;
212f835f386SSteve Longerbeam 		r90 = 0;
213f835f386SSteve Longerbeam 		break;
214f835f386SSteve Longerbeam 	case 270:
215f835f386SSteve Longerbeam 		vf = hf = r90 = 1;
216f835f386SSteve Longerbeam 		break;
217f835f386SSteve Longerbeam 	default:
218f835f386SSteve Longerbeam 		return -EINVAL;
219f835f386SSteve Longerbeam 	}
220f835f386SSteve Longerbeam 
221f835f386SSteve Longerbeam 	hf ^= (u32)hflip;
222f835f386SSteve Longerbeam 	vf ^= (u32)vflip;
223f835f386SSteve Longerbeam 
224f835f386SSteve Longerbeam 	*mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf);
225f835f386SSteve Longerbeam 	return 0;
226f835f386SSteve Longerbeam }
227f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode);
228f835f386SSteve Longerbeam 
229f835f386SSteve Longerbeam int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode,
230f835f386SSteve Longerbeam 			    bool hflip, bool vflip)
231f835f386SSteve Longerbeam {
232f835f386SSteve Longerbeam 	u32 r90, vf, hf;
233f835f386SSteve Longerbeam 
234f835f386SSteve Longerbeam 	r90 = ((u32)mode >> 2) & 0x1;
235f835f386SSteve Longerbeam 	hf = ((u32)mode >> 1) & 0x1;
236f835f386SSteve Longerbeam 	vf = ((u32)mode >> 0) & 0x1;
237f835f386SSteve Longerbeam 	hf ^= (u32)hflip;
238f835f386SSteve Longerbeam 	vf ^= (u32)vflip;
239f835f386SSteve Longerbeam 
240f835f386SSteve Longerbeam 	switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) {
241f835f386SSteve Longerbeam 	case IPU_ROTATE_NONE:
242f835f386SSteve Longerbeam 		*degrees = 0;
243f835f386SSteve Longerbeam 		break;
244f835f386SSteve Longerbeam 	case IPU_ROTATE_90_RIGHT:
245f835f386SSteve Longerbeam 		*degrees = 90;
246f835f386SSteve Longerbeam 		break;
247f835f386SSteve Longerbeam 	case IPU_ROTATE_180:
248f835f386SSteve Longerbeam 		*degrees = 180;
249f835f386SSteve Longerbeam 		break;
250f835f386SSteve Longerbeam 	case IPU_ROTATE_90_LEFT:
251f835f386SSteve Longerbeam 		*degrees = 270;
252f835f386SSteve Longerbeam 		break;
253f835f386SSteve Longerbeam 	default:
254f835f386SSteve Longerbeam 		return -EINVAL;
255f835f386SSteve Longerbeam 	}
256f835f386SSteve Longerbeam 
257f835f386SSteve Longerbeam 	return 0;
258f835f386SSteve Longerbeam }
259f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees);
260f835f386SSteve Longerbeam 
26139b9004dSPhilipp Zabel struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num)
26239b9004dSPhilipp Zabel {
26339b9004dSPhilipp Zabel 	struct ipuv3_channel *channel;
26439b9004dSPhilipp Zabel 
26539b9004dSPhilipp Zabel 	dev_dbg(ipu->dev, "%s %d\n", __func__, num);
26639b9004dSPhilipp Zabel 
26739b9004dSPhilipp Zabel 	if (num > 63)
26839b9004dSPhilipp Zabel 		return ERR_PTR(-ENODEV);
26939b9004dSPhilipp Zabel 
27039b9004dSPhilipp Zabel 	mutex_lock(&ipu->channel_lock);
27139b9004dSPhilipp Zabel 
27293adc8b5SPhilipp Zabel 	list_for_each_entry(channel, &ipu->channels, list) {
27393adc8b5SPhilipp Zabel 		if (channel->num == num) {
27439b9004dSPhilipp Zabel 			channel = ERR_PTR(-EBUSY);
27539b9004dSPhilipp Zabel 			goto out;
27639b9004dSPhilipp Zabel 		}
27793adc8b5SPhilipp Zabel 	}
27839b9004dSPhilipp Zabel 
27993adc8b5SPhilipp Zabel 	channel = kzalloc(sizeof(*channel), GFP_KERNEL);
28093adc8b5SPhilipp Zabel 	if (!channel) {
28193adc8b5SPhilipp Zabel 		channel = ERR_PTR(-ENOMEM);
28293adc8b5SPhilipp Zabel 		goto out;
28393adc8b5SPhilipp Zabel 	}
28493adc8b5SPhilipp Zabel 
28539b9004dSPhilipp Zabel 	channel->num = num;
28693adc8b5SPhilipp Zabel 	channel->ipu = ipu;
28793adc8b5SPhilipp Zabel 	list_add(&channel->list, &ipu->channels);
28839b9004dSPhilipp Zabel 
28939b9004dSPhilipp Zabel out:
29039b9004dSPhilipp Zabel 	mutex_unlock(&ipu->channel_lock);
29139b9004dSPhilipp Zabel 
29239b9004dSPhilipp Zabel 	return channel;
29339b9004dSPhilipp Zabel }
29439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get);
29539b9004dSPhilipp Zabel 
29639b9004dSPhilipp Zabel void ipu_idmac_put(struct ipuv3_channel *channel)
29739b9004dSPhilipp Zabel {
29839b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
29939b9004dSPhilipp Zabel 
30039b9004dSPhilipp Zabel 	dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num);
30139b9004dSPhilipp Zabel 
30239b9004dSPhilipp Zabel 	mutex_lock(&ipu->channel_lock);
30339b9004dSPhilipp Zabel 
30493adc8b5SPhilipp Zabel 	list_del(&channel->list);
30593adc8b5SPhilipp Zabel 	kfree(channel);
30639b9004dSPhilipp Zabel 
30739b9004dSPhilipp Zabel 	mutex_unlock(&ipu->channel_lock);
30839b9004dSPhilipp Zabel }
30939b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_put);
31039b9004dSPhilipp Zabel 
311aa52f578SSteve Longerbeam #define idma_mask(ch)			(1 << ((ch) & 0x1f))
31239b9004dSPhilipp Zabel 
313e7268c69SSteve Longerbeam /*
314e7268c69SSteve Longerbeam  * This is an undocumented feature, a write one to a channel bit in
315e7268c69SSteve Longerbeam  * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's
316e7268c69SSteve Longerbeam  * internal current buffer pointer so that transfers start from buffer
317e7268c69SSteve Longerbeam  * 0 on the next channel enable (that's the theory anyway, the imx6 TRM
318e7268c69SSteve Longerbeam  * only says these are read-only registers). This operation is required
319e7268c69SSteve Longerbeam  * for channel linking to work correctly, for instance video capture
320e7268c69SSteve Longerbeam  * pipelines that carry out image rotations will fail after the first
321e7268c69SSteve Longerbeam  * streaming unless this function is called for each channel before
322e7268c69SSteve Longerbeam  * re-enabling the channels.
323e7268c69SSteve Longerbeam  */
324e7268c69SSteve Longerbeam static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel)
325e7268c69SSteve Longerbeam {
326e7268c69SSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
327e7268c69SSteve Longerbeam 	unsigned int chno = channel->num;
328e7268c69SSteve Longerbeam 
329e7268c69SSteve Longerbeam 	ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno));
330e7268c69SSteve Longerbeam }
331e7268c69SSteve Longerbeam 
33239b9004dSPhilipp Zabel void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
33339b9004dSPhilipp Zabel 		bool doublebuffer)
33439b9004dSPhilipp Zabel {
33539b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
33639b9004dSPhilipp Zabel 	unsigned long flags;
33739b9004dSPhilipp Zabel 	u32 reg;
33839b9004dSPhilipp Zabel 
33939b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
34039b9004dSPhilipp Zabel 
34139b9004dSPhilipp Zabel 	reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
34239b9004dSPhilipp Zabel 	if (doublebuffer)
34339b9004dSPhilipp Zabel 		reg |= idma_mask(channel->num);
34439b9004dSPhilipp Zabel 	else
34539b9004dSPhilipp Zabel 		reg &= ~idma_mask(channel->num);
34639b9004dSPhilipp Zabel 	ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num));
34739b9004dSPhilipp Zabel 
348e7268c69SSteve Longerbeam 	__ipu_idmac_reset_current_buffer(channel);
349e7268c69SSteve Longerbeam 
35039b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
35139b9004dSPhilipp Zabel }
35239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer);
35339b9004dSPhilipp Zabel 
3544fd1a07aSSteve Longerbeam static const struct {
3554fd1a07aSSteve Longerbeam 	int chnum;
3564fd1a07aSSteve Longerbeam 	u32 reg;
3574fd1a07aSSteve Longerbeam 	int shift;
3584fd1a07aSSteve Longerbeam } idmac_lock_en_info[] = {
3594fd1a07aSSteve Longerbeam 	{ .chnum =  5, .reg = IDMAC_CH_LOCK_EN_1, .shift =  0, },
3604fd1a07aSSteve Longerbeam 	{ .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift =  2, },
3614fd1a07aSSteve Longerbeam 	{ .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift =  4, },
3624fd1a07aSSteve Longerbeam 	{ .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift =  6, },
3634fd1a07aSSteve Longerbeam 	{ .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift =  8, },
3644fd1a07aSSteve Longerbeam 	{ .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, },
3654fd1a07aSSteve Longerbeam 	{ .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, },
3664fd1a07aSSteve Longerbeam 	{ .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, },
3674fd1a07aSSteve Longerbeam 	{ .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, },
3684fd1a07aSSteve Longerbeam 	{ .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, },
3694fd1a07aSSteve Longerbeam 	{ .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, },
3704fd1a07aSSteve Longerbeam 	{ .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift =  0, },
3714fd1a07aSSteve Longerbeam 	{ .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift =  2, },
3724fd1a07aSSteve Longerbeam 	{ .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift =  4, },
3734fd1a07aSSteve Longerbeam 	{ .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift =  6, },
3744fd1a07aSSteve Longerbeam 	{ .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift =  8, },
3754fd1a07aSSteve Longerbeam 	{ .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, },
3764fd1a07aSSteve Longerbeam };
3774fd1a07aSSteve Longerbeam 
3784fd1a07aSSteve Longerbeam int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts)
3794fd1a07aSSteve Longerbeam {
3804fd1a07aSSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
3814fd1a07aSSteve Longerbeam 	unsigned long flags;
3824fd1a07aSSteve Longerbeam 	u32 bursts, regval;
3834fd1a07aSSteve Longerbeam 	int i;
3844fd1a07aSSteve Longerbeam 
3854fd1a07aSSteve Longerbeam 	switch (num_bursts) {
3864fd1a07aSSteve Longerbeam 	case 0:
3874fd1a07aSSteve Longerbeam 	case 1:
3884fd1a07aSSteve Longerbeam 		bursts = 0x00; /* locking disabled */
3894fd1a07aSSteve Longerbeam 		break;
3904fd1a07aSSteve Longerbeam 	case 2:
3914fd1a07aSSteve Longerbeam 		bursts = 0x01;
3924fd1a07aSSteve Longerbeam 		break;
3934fd1a07aSSteve Longerbeam 	case 4:
3944fd1a07aSSteve Longerbeam 		bursts = 0x02;
3954fd1a07aSSteve Longerbeam 		break;
3964fd1a07aSSteve Longerbeam 	case 8:
3974fd1a07aSSteve Longerbeam 		bursts = 0x03;
3984fd1a07aSSteve Longerbeam 		break;
3994fd1a07aSSteve Longerbeam 	default:
4004fd1a07aSSteve Longerbeam 		return -EINVAL;
4014fd1a07aSSteve Longerbeam 	}
4024fd1a07aSSteve Longerbeam 
403cda77556SPhilipp Zabel 	/*
404cda77556SPhilipp Zabel 	 * IPUv3EX / i.MX51 has a different register layout, and on IPUv3M /
405cda77556SPhilipp Zabel 	 * i.MX53 channel arbitration locking doesn't seem to work properly.
406cda77556SPhilipp Zabel 	 * Allow enabling the lock feature on IPUv3H / i.MX6 only.
407cda77556SPhilipp Zabel 	 */
408cda77556SPhilipp Zabel 	if (bursts && ipu->ipu_type != IPUV3H)
409cda77556SPhilipp Zabel 		return -EINVAL;
410cda77556SPhilipp Zabel 
4114fd1a07aSSteve Longerbeam 	for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) {
4124fd1a07aSSteve Longerbeam 		if (channel->num == idmac_lock_en_info[i].chnum)
4134fd1a07aSSteve Longerbeam 			break;
4144fd1a07aSSteve Longerbeam 	}
4154fd1a07aSSteve Longerbeam 	if (i >= ARRAY_SIZE(idmac_lock_en_info))
4164fd1a07aSSteve Longerbeam 		return -EINVAL;
4174fd1a07aSSteve Longerbeam 
4184fd1a07aSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
4194fd1a07aSSteve Longerbeam 
4204fd1a07aSSteve Longerbeam 	regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg);
4214fd1a07aSSteve Longerbeam 	regval &= ~(0x03 << idmac_lock_en_info[i].shift);
4224fd1a07aSSteve Longerbeam 	regval |= (bursts << idmac_lock_en_info[i].shift);
4234fd1a07aSSteve Longerbeam 	ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg);
4244fd1a07aSSteve Longerbeam 
4254fd1a07aSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
4264fd1a07aSSteve Longerbeam 
4274fd1a07aSSteve Longerbeam 	return 0;
4284fd1a07aSSteve Longerbeam }
4294fd1a07aSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable);
4304fd1a07aSSteve Longerbeam 
43139b9004dSPhilipp Zabel int ipu_module_enable(struct ipu_soc *ipu, u32 mask)
43239b9004dSPhilipp Zabel {
43339b9004dSPhilipp Zabel 	unsigned long lock_flags;
43439b9004dSPhilipp Zabel 	u32 val;
43539b9004dSPhilipp Zabel 
43639b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, lock_flags);
43739b9004dSPhilipp Zabel 
43839b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_DISP_GEN);
43939b9004dSPhilipp Zabel 
44039b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI0_EN)
44139b9004dSPhilipp Zabel 		val |= IPU_DI0_COUNTER_RELEASE;
44239b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI1_EN)
44339b9004dSPhilipp Zabel 		val |= IPU_DI1_COUNTER_RELEASE;
44439b9004dSPhilipp Zabel 
44539b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_DISP_GEN);
44639b9004dSPhilipp Zabel 
44739b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CONF);
44839b9004dSPhilipp Zabel 	val |= mask;
44939b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CONF);
45039b9004dSPhilipp Zabel 
45139b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, lock_flags);
45239b9004dSPhilipp Zabel 
45339b9004dSPhilipp Zabel 	return 0;
45439b9004dSPhilipp Zabel }
45539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_enable);
45639b9004dSPhilipp Zabel 
45739b9004dSPhilipp Zabel int ipu_module_disable(struct ipu_soc *ipu, u32 mask)
45839b9004dSPhilipp Zabel {
45939b9004dSPhilipp Zabel 	unsigned long lock_flags;
46039b9004dSPhilipp Zabel 	u32 val;
46139b9004dSPhilipp Zabel 
46239b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, lock_flags);
46339b9004dSPhilipp Zabel 
46439b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CONF);
46539b9004dSPhilipp Zabel 	val &= ~mask;
46639b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CONF);
46739b9004dSPhilipp Zabel 
46839b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_DISP_GEN);
46939b9004dSPhilipp Zabel 
47039b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI0_EN)
47139b9004dSPhilipp Zabel 		val &= ~IPU_DI0_COUNTER_RELEASE;
47239b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI1_EN)
47339b9004dSPhilipp Zabel 		val &= ~IPU_DI1_COUNTER_RELEASE;
47439b9004dSPhilipp Zabel 
47539b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_DISP_GEN);
47639b9004dSPhilipp Zabel 
47739b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, lock_flags);
47839b9004dSPhilipp Zabel 
47939b9004dSPhilipp Zabel 	return 0;
48039b9004dSPhilipp Zabel }
48139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_disable);
48239b9004dSPhilipp Zabel 
483e9046097SPhilipp Zabel int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel)
484e9046097SPhilipp Zabel {
485e9046097SPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
486e9046097SPhilipp Zabel 	unsigned int chno = channel->num;
487e9046097SPhilipp Zabel 
488e9046097SPhilipp Zabel 	return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0;
489e9046097SPhilipp Zabel }
490e9046097SPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer);
491e9046097SPhilipp Zabel 
492aa52f578SSteve Longerbeam bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num)
493aa52f578SSteve Longerbeam {
494aa52f578SSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
495aa52f578SSteve Longerbeam 	unsigned long flags;
496aa52f578SSteve Longerbeam 	u32 reg = 0;
497aa52f578SSteve Longerbeam 
498aa52f578SSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
499aa52f578SSteve Longerbeam 	switch (buf_num) {
500aa52f578SSteve Longerbeam 	case 0:
501aa52f578SSteve Longerbeam 		reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num));
502aa52f578SSteve Longerbeam 		break;
503aa52f578SSteve Longerbeam 	case 1:
504aa52f578SSteve Longerbeam 		reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num));
505aa52f578SSteve Longerbeam 		break;
506aa52f578SSteve Longerbeam 	case 2:
507aa52f578SSteve Longerbeam 		reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num));
508aa52f578SSteve Longerbeam 		break;
509aa52f578SSteve Longerbeam 	}
510aa52f578SSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
511aa52f578SSteve Longerbeam 
512aa52f578SSteve Longerbeam 	return ((reg & idma_mask(channel->num)) != 0);
513aa52f578SSteve Longerbeam }
514aa52f578SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready);
515aa52f578SSteve Longerbeam 
51639b9004dSPhilipp Zabel void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num)
51739b9004dSPhilipp Zabel {
51839b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
51939b9004dSPhilipp Zabel 	unsigned int chno = channel->num;
52039b9004dSPhilipp Zabel 	unsigned long flags;
52139b9004dSPhilipp Zabel 
52239b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
52339b9004dSPhilipp Zabel 
52439b9004dSPhilipp Zabel 	/* Mark buffer as ready. */
52539b9004dSPhilipp Zabel 	if (buf_num == 0)
52639b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
52739b9004dSPhilipp Zabel 	else
52839b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
52939b9004dSPhilipp Zabel 
53039b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
53139b9004dSPhilipp Zabel }
53239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer);
53339b9004dSPhilipp Zabel 
534bce6f087SSteve Longerbeam void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num)
535bce6f087SSteve Longerbeam {
536bce6f087SSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
537bce6f087SSteve Longerbeam 	unsigned int chno = channel->num;
538bce6f087SSteve Longerbeam 	unsigned long flags;
539bce6f087SSteve Longerbeam 
540bce6f087SSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
541bce6f087SSteve Longerbeam 
542bce6f087SSteve Longerbeam 	ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */
543bce6f087SSteve Longerbeam 	switch (buf_num) {
544bce6f087SSteve Longerbeam 	case 0:
545bce6f087SSteve Longerbeam 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
546bce6f087SSteve Longerbeam 		break;
547bce6f087SSteve Longerbeam 	case 1:
548bce6f087SSteve Longerbeam 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
549bce6f087SSteve Longerbeam 		break;
550bce6f087SSteve Longerbeam 	case 2:
551bce6f087SSteve Longerbeam 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno));
552bce6f087SSteve Longerbeam 		break;
553bce6f087SSteve Longerbeam 	default:
554bce6f087SSteve Longerbeam 		break;
555bce6f087SSteve Longerbeam 	}
556bce6f087SSteve Longerbeam 	ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
557bce6f087SSteve Longerbeam 
558bce6f087SSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
559bce6f087SSteve Longerbeam }
560bce6f087SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer);
561bce6f087SSteve Longerbeam 
56239b9004dSPhilipp Zabel int ipu_idmac_enable_channel(struct ipuv3_channel *channel)
56339b9004dSPhilipp Zabel {
56439b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
56539b9004dSPhilipp Zabel 	u32 val;
56639b9004dSPhilipp Zabel 	unsigned long flags;
56739b9004dSPhilipp Zabel 
56839b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
56939b9004dSPhilipp Zabel 
57039b9004dSPhilipp Zabel 	val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
57139b9004dSPhilipp Zabel 	val |= idma_mask(channel->num);
57239b9004dSPhilipp Zabel 	ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
57339b9004dSPhilipp Zabel 
57439b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
57539b9004dSPhilipp Zabel 
57639b9004dSPhilipp Zabel 	return 0;
57739b9004dSPhilipp Zabel }
57839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
57939b9004dSPhilipp Zabel 
580682b7c1cSLinus Torvalds bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno)
581682b7c1cSLinus Torvalds {
582682b7c1cSLinus Torvalds 	return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno));
583682b7c1cSLinus Torvalds }
584682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy);
585682b7c1cSLinus Torvalds 
58639b9004dSPhilipp Zabel int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms)
58739b9004dSPhilipp Zabel {
58839b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
58939b9004dSPhilipp Zabel 	unsigned long timeout;
59039b9004dSPhilipp Zabel 
59139b9004dSPhilipp Zabel 	timeout = jiffies + msecs_to_jiffies(ms);
59239b9004dSPhilipp Zabel 	while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) &
59339b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
59439b9004dSPhilipp Zabel 		if (time_after(jiffies, timeout))
59539b9004dSPhilipp Zabel 			return -ETIMEDOUT;
59639b9004dSPhilipp Zabel 		cpu_relax();
59739b9004dSPhilipp Zabel 	}
59839b9004dSPhilipp Zabel 
59939b9004dSPhilipp Zabel 	return 0;
60039b9004dSPhilipp Zabel }
60139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy);
60239b9004dSPhilipp Zabel 
60339b9004dSPhilipp Zabel int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
60439b9004dSPhilipp Zabel {
60539b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
60639b9004dSPhilipp Zabel 	u32 val;
60739b9004dSPhilipp Zabel 	unsigned long flags;
60839b9004dSPhilipp Zabel 
60939b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
61039b9004dSPhilipp Zabel 
61139b9004dSPhilipp Zabel 	/* Disable DMA channel(s) */
61239b9004dSPhilipp Zabel 	val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
61339b9004dSPhilipp Zabel 	val &= ~idma_mask(channel->num);
61439b9004dSPhilipp Zabel 	ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
61539b9004dSPhilipp Zabel 
616e7268c69SSteve Longerbeam 	__ipu_idmac_reset_current_buffer(channel);
617e7268c69SSteve Longerbeam 
61839b9004dSPhilipp Zabel 	/* Set channel buffers NOT to be ready */
61939b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */
62039b9004dSPhilipp Zabel 
62139b9004dSPhilipp Zabel 	if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) &
62239b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
62339b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(channel->num),
62439b9004dSPhilipp Zabel 			     IPU_CHA_BUF0_RDY(channel->num));
62539b9004dSPhilipp Zabel 	}
62639b9004dSPhilipp Zabel 
62739b9004dSPhilipp Zabel 	if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) &
62839b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
62939b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(channel->num),
63039b9004dSPhilipp Zabel 			     IPU_CHA_BUF1_RDY(channel->num));
63139b9004dSPhilipp Zabel 	}
63239b9004dSPhilipp Zabel 
63339b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
63439b9004dSPhilipp Zabel 
63539b9004dSPhilipp Zabel 	/* Reset the double buffer */
63639b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
63739b9004dSPhilipp Zabel 	val &= ~idma_mask(channel->num);
63839b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num));
63939b9004dSPhilipp Zabel 
64039b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
64139b9004dSPhilipp Zabel 
64239b9004dSPhilipp Zabel 	return 0;
64339b9004dSPhilipp Zabel }
64439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel);
64539b9004dSPhilipp Zabel 
6462bcf577eSSteve Longerbeam /*
6472bcf577eSSteve Longerbeam  * The imx6 rev. D TRM says that enabling the WM feature will increase
6482bcf577eSSteve Longerbeam  * a channel's priority. Refer to Table 36-8 Calculated priority value.
6492bcf577eSSteve Longerbeam  * The sub-module that is the sink or source for the channel must enable
6502bcf577eSSteve Longerbeam  * watermark signal for this to take effect (SMFC_WM for instance).
6512bcf577eSSteve Longerbeam  */
6522bcf577eSSteve Longerbeam void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable)
6532bcf577eSSteve Longerbeam {
6542bcf577eSSteve Longerbeam 	struct ipu_soc *ipu = channel->ipu;
6552bcf577eSSteve Longerbeam 	unsigned long flags;
6562bcf577eSSteve Longerbeam 	u32 val;
6572bcf577eSSteve Longerbeam 
6582bcf577eSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
6592bcf577eSSteve Longerbeam 
6602bcf577eSSteve Longerbeam 	val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num));
6612bcf577eSSteve Longerbeam 	if (enable)
6622bcf577eSSteve Longerbeam 		val |= 1 << (channel->num % 32);
6632bcf577eSSteve Longerbeam 	else
6642bcf577eSSteve Longerbeam 		val &= ~(1 << (channel->num % 32));
6652bcf577eSSteve Longerbeam 	ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num));
6662bcf577eSSteve Longerbeam 
6672bcf577eSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
6682bcf577eSSteve Longerbeam }
6692bcf577eSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark);
6702bcf577eSSteve Longerbeam 
67139b9004dSPhilipp Zabel static int ipu_memory_reset(struct ipu_soc *ipu)
67239b9004dSPhilipp Zabel {
67339b9004dSPhilipp Zabel 	unsigned long timeout;
67439b9004dSPhilipp Zabel 
67539b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST);
67639b9004dSPhilipp Zabel 
67739b9004dSPhilipp Zabel 	timeout = jiffies + msecs_to_jiffies(1000);
67839b9004dSPhilipp Zabel 	while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) {
67939b9004dSPhilipp Zabel 		if (time_after(jiffies, timeout))
68039b9004dSPhilipp Zabel 			return -ETIME;
68139b9004dSPhilipp Zabel 		cpu_relax();
68239b9004dSPhilipp Zabel 	}
68339b9004dSPhilipp Zabel 
68439b9004dSPhilipp Zabel 	return 0;
68539b9004dSPhilipp Zabel }
68639b9004dSPhilipp Zabel 
687ba07975fSSteve Longerbeam /*
688ba07975fSSteve Longerbeam  * Set the source mux for the given CSI. Selects either parallel or
689ba07975fSSteve Longerbeam  * MIPI CSI2 sources.
690ba07975fSSteve Longerbeam  */
691ba07975fSSteve Longerbeam void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2)
692ba07975fSSteve Longerbeam {
693ba07975fSSteve Longerbeam 	unsigned long flags;
694ba07975fSSteve Longerbeam 	u32 val, mask;
695ba07975fSSteve Longerbeam 
696ba07975fSSteve Longerbeam 	mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE :
697ba07975fSSteve Longerbeam 		IPU_CONF_CSI0_DATA_SOURCE;
698ba07975fSSteve Longerbeam 
699ba07975fSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
700ba07975fSSteve Longerbeam 
701ba07975fSSteve Longerbeam 	val = ipu_cm_read(ipu, IPU_CONF);
702ba07975fSSteve Longerbeam 	if (mipi_csi2)
703ba07975fSSteve Longerbeam 		val |= mask;
704ba07975fSSteve Longerbeam 	else
705ba07975fSSteve Longerbeam 		val &= ~mask;
706ba07975fSSteve Longerbeam 	ipu_cm_write(ipu, val, IPU_CONF);
707ba07975fSSteve Longerbeam 
708ba07975fSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
709ba07975fSSteve Longerbeam }
710ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux);
711ba07975fSSteve Longerbeam 
712ba07975fSSteve Longerbeam /*
713ba07975fSSteve Longerbeam  * Set the source mux for the IC. Selects either CSI[01] or the VDI.
714ba07975fSSteve Longerbeam  */
715ba07975fSSteve Longerbeam void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
716ba07975fSSteve Longerbeam {
717ba07975fSSteve Longerbeam 	unsigned long flags;
718ba07975fSSteve Longerbeam 	u32 val;
719ba07975fSSteve Longerbeam 
720ba07975fSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
721ba07975fSSteve Longerbeam 
722ba07975fSSteve Longerbeam 	val = ipu_cm_read(ipu, IPU_CONF);
723b7dfee24SMarek Vasut 	if (vdi)
724ba07975fSSteve Longerbeam 		val |= IPU_CONF_IC_INPUT;
725b7dfee24SMarek Vasut 	else
726ba07975fSSteve Longerbeam 		val &= ~IPU_CONF_IC_INPUT;
727b7dfee24SMarek Vasut 
728ba07975fSSteve Longerbeam 	if (csi_id == 1)
729ba07975fSSteve Longerbeam 		val |= IPU_CONF_CSI_SEL;
730ba07975fSSteve Longerbeam 	else
731ba07975fSSteve Longerbeam 		val &= ~IPU_CONF_CSI_SEL;
732b7dfee24SMarek Vasut 
733ba07975fSSteve Longerbeam 	ipu_cm_write(ipu, val, IPU_CONF);
734ba07975fSSteve Longerbeam 
735ba07975fSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
736ba07975fSSteve Longerbeam }
737ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
738ba07975fSSteve Longerbeam 
739ac4708faSSteve Longerbeam 
740ac4708faSSteve Longerbeam /* Frame Synchronization Unit Channel Linking */
741ac4708faSSteve Longerbeam 
742ac4708faSSteve Longerbeam struct fsu_link_reg_info {
743ac4708faSSteve Longerbeam 	int chno;
744ac4708faSSteve Longerbeam 	u32 reg;
745ac4708faSSteve Longerbeam 	u32 mask;
746ac4708faSSteve Longerbeam 	u32 val;
747ac4708faSSteve Longerbeam };
748ac4708faSSteve Longerbeam 
749ac4708faSSteve Longerbeam struct fsu_link_info {
750ac4708faSSteve Longerbeam 	struct fsu_link_reg_info src;
751ac4708faSSteve Longerbeam 	struct fsu_link_reg_info sink;
752ac4708faSSteve Longerbeam };
753ac4708faSSteve Longerbeam 
754ac4708faSSteve Longerbeam static const struct fsu_link_info fsu_link_info[] = {
755ac4708faSSteve Longerbeam 	{
756ac4708faSSteve Longerbeam 		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2,
757ac4708faSSteve Longerbeam 			  FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC },
758ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1,
759ac4708faSSteve Longerbeam 			  FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC },
760ac4708faSSteve Longerbeam 	}, {
761ac4708faSSteve Longerbeam 		.src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2,
762ac4708faSSteve Longerbeam 			  FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF },
763ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1,
764ac4708faSSteve Longerbeam 			  FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF },
765ac4708faSSteve Longerbeam 	}, {
766ac4708faSSteve Longerbeam 		.src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2,
767ac4708faSSteve Longerbeam 			  FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP },
768ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1,
769ac4708faSSteve Longerbeam 			  FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP },
770ac4708faSSteve Longerbeam 	}, {
771ac4708faSSteve Longerbeam 		.src =  { IPUV3_CHANNEL_CSI_DIRECT, 0 },
772ac4708faSSteve Longerbeam 		.sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1,
773ac4708faSSteve Longerbeam 			  FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT },
774ac4708faSSteve Longerbeam 	},
775ac4708faSSteve Longerbeam };
776ac4708faSSteve Longerbeam 
777ac4708faSSteve Longerbeam static const struct fsu_link_info *find_fsu_link_info(int src, int sink)
778ac4708faSSteve Longerbeam {
779ac4708faSSteve Longerbeam 	int i;
780ac4708faSSteve Longerbeam 
781ac4708faSSteve Longerbeam 	for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) {
782ac4708faSSteve Longerbeam 		if (src == fsu_link_info[i].src.chno &&
783ac4708faSSteve Longerbeam 		    sink == fsu_link_info[i].sink.chno)
784ac4708faSSteve Longerbeam 			return &fsu_link_info[i];
785ac4708faSSteve Longerbeam 	}
786ac4708faSSteve Longerbeam 
787ac4708faSSteve Longerbeam 	return NULL;
788ac4708faSSteve Longerbeam }
789ac4708faSSteve Longerbeam 
790ac4708faSSteve Longerbeam /*
791ac4708faSSteve Longerbeam  * Links a source channel to a sink channel in the FSU.
792ac4708faSSteve Longerbeam  */
793ac4708faSSteve Longerbeam int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch)
794ac4708faSSteve Longerbeam {
795ac4708faSSteve Longerbeam 	const struct fsu_link_info *link;
796ac4708faSSteve Longerbeam 	u32 src_reg, sink_reg;
797ac4708faSSteve Longerbeam 	unsigned long flags;
798ac4708faSSteve Longerbeam 
799ac4708faSSteve Longerbeam 	link = find_fsu_link_info(src_ch, sink_ch);
800ac4708faSSteve Longerbeam 	if (!link)
801ac4708faSSteve Longerbeam 		return -EINVAL;
802ac4708faSSteve Longerbeam 
803ac4708faSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
804ac4708faSSteve Longerbeam 
805ac4708faSSteve Longerbeam 	if (link->src.mask) {
806ac4708faSSteve Longerbeam 		src_reg = ipu_cm_read(ipu, link->src.reg);
807ac4708faSSteve Longerbeam 		src_reg &= ~link->src.mask;
808ac4708faSSteve Longerbeam 		src_reg |= link->src.val;
809ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, src_reg, link->src.reg);
810ac4708faSSteve Longerbeam 	}
811ac4708faSSteve Longerbeam 
812ac4708faSSteve Longerbeam 	if (link->sink.mask) {
813ac4708faSSteve Longerbeam 		sink_reg = ipu_cm_read(ipu, link->sink.reg);
814ac4708faSSteve Longerbeam 		sink_reg &= ~link->sink.mask;
815ac4708faSSteve Longerbeam 		sink_reg |= link->sink.val;
816ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, sink_reg, link->sink.reg);
817ac4708faSSteve Longerbeam 	}
818ac4708faSSteve Longerbeam 
819ac4708faSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
820ac4708faSSteve Longerbeam 	return 0;
821ac4708faSSteve Longerbeam }
822ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_link);
823ac4708faSSteve Longerbeam 
824ac4708faSSteve Longerbeam /*
825ac4708faSSteve Longerbeam  * Unlinks source and sink channels in the FSU.
826ac4708faSSteve Longerbeam  */
827ac4708faSSteve Longerbeam int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch)
828ac4708faSSteve Longerbeam {
829ac4708faSSteve Longerbeam 	const struct fsu_link_info *link;
830ac4708faSSteve Longerbeam 	u32 src_reg, sink_reg;
831ac4708faSSteve Longerbeam 	unsigned long flags;
832ac4708faSSteve Longerbeam 
833ac4708faSSteve Longerbeam 	link = find_fsu_link_info(src_ch, sink_ch);
834ac4708faSSteve Longerbeam 	if (!link)
835ac4708faSSteve Longerbeam 		return -EINVAL;
836ac4708faSSteve Longerbeam 
837ac4708faSSteve Longerbeam 	spin_lock_irqsave(&ipu->lock, flags);
838ac4708faSSteve Longerbeam 
839ac4708faSSteve Longerbeam 	if (link->src.mask) {
840ac4708faSSteve Longerbeam 		src_reg = ipu_cm_read(ipu, link->src.reg);
841ac4708faSSteve Longerbeam 		src_reg &= ~link->src.mask;
842ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, src_reg, link->src.reg);
843ac4708faSSteve Longerbeam 	}
844ac4708faSSteve Longerbeam 
845ac4708faSSteve Longerbeam 	if (link->sink.mask) {
846ac4708faSSteve Longerbeam 		sink_reg = ipu_cm_read(ipu, link->sink.reg);
847ac4708faSSteve Longerbeam 		sink_reg &= ~link->sink.mask;
848ac4708faSSteve Longerbeam 		ipu_cm_write(ipu, sink_reg, link->sink.reg);
849ac4708faSSteve Longerbeam 	}
850ac4708faSSteve Longerbeam 
851ac4708faSSteve Longerbeam 	spin_unlock_irqrestore(&ipu->lock, flags);
852ac4708faSSteve Longerbeam 	return 0;
853ac4708faSSteve Longerbeam }
854ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_unlink);
855ac4708faSSteve Longerbeam 
856ac4708faSSteve Longerbeam /* Link IDMAC channels in the FSU */
857ac4708faSSteve Longerbeam int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)
858ac4708faSSteve Longerbeam {
859ac4708faSSteve Longerbeam 	return ipu_fsu_link(src->ipu, src->num, sink->num);
860ac4708faSSteve Longerbeam }
861ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_link);
862ac4708faSSteve Longerbeam 
863ac4708faSSteve Longerbeam /* Unlink IDMAC channels in the FSU */
864ac4708faSSteve Longerbeam int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink)
865ac4708faSSteve Longerbeam {
866ac4708faSSteve Longerbeam 	return ipu_fsu_unlink(src->ipu, src->num, sink->num);
867ac4708faSSteve Longerbeam }
868ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_unlink);
869ac4708faSSteve Longerbeam 
87039b9004dSPhilipp Zabel struct ipu_devtype {
87139b9004dSPhilipp Zabel 	const char *name;
87239b9004dSPhilipp Zabel 	unsigned long cm_ofs;
87339b9004dSPhilipp Zabel 	unsigned long cpmem_ofs;
87439b9004dSPhilipp Zabel 	unsigned long srm_ofs;
87539b9004dSPhilipp Zabel 	unsigned long tpm_ofs;
8762ffd48f2SSteve Longerbeam 	unsigned long csi0_ofs;
8772ffd48f2SSteve Longerbeam 	unsigned long csi1_ofs;
8781aa8ea0dSSteve Longerbeam 	unsigned long ic_ofs;
87939b9004dSPhilipp Zabel 	unsigned long disp0_ofs;
88039b9004dSPhilipp Zabel 	unsigned long disp1_ofs;
88139b9004dSPhilipp Zabel 	unsigned long dc_tmpl_ofs;
88239b9004dSPhilipp Zabel 	unsigned long vdi_ofs;
88339b9004dSPhilipp Zabel 	enum ipuv3_type type;
88439b9004dSPhilipp Zabel };
88539b9004dSPhilipp Zabel 
88639b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx51 = {
88739b9004dSPhilipp Zabel 	.name = "IPUv3EX",
88839b9004dSPhilipp Zabel 	.cm_ofs = 0x1e000000,
88939b9004dSPhilipp Zabel 	.cpmem_ofs = 0x1f000000,
89039b9004dSPhilipp Zabel 	.srm_ofs = 0x1f040000,
89139b9004dSPhilipp Zabel 	.tpm_ofs = 0x1f060000,
8922c0408ddSAlexander Shiyan 	.csi0_ofs = 0x1e030000,
8932c0408ddSAlexander Shiyan 	.csi1_ofs = 0x1e038000,
894a49e7c0dSPhilipp Zabel 	.ic_ofs = 0x1e020000,
89539b9004dSPhilipp Zabel 	.disp0_ofs = 0x1e040000,
89639b9004dSPhilipp Zabel 	.disp1_ofs = 0x1e048000,
89739b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x1f080000,
89839b9004dSPhilipp Zabel 	.vdi_ofs = 0x1e068000,
89939b9004dSPhilipp Zabel 	.type = IPUV3EX,
90039b9004dSPhilipp Zabel };
90139b9004dSPhilipp Zabel 
90239b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx53 = {
90339b9004dSPhilipp Zabel 	.name = "IPUv3M",
90439b9004dSPhilipp Zabel 	.cm_ofs = 0x06000000,
90539b9004dSPhilipp Zabel 	.cpmem_ofs = 0x07000000,
90639b9004dSPhilipp Zabel 	.srm_ofs = 0x07040000,
90739b9004dSPhilipp Zabel 	.tpm_ofs = 0x07060000,
908bb867d21SSteve Longerbeam 	.csi0_ofs = 0x06030000,
909bb867d21SSteve Longerbeam 	.csi1_ofs = 0x06038000,
910a49e7c0dSPhilipp Zabel 	.ic_ofs = 0x06020000,
91139b9004dSPhilipp Zabel 	.disp0_ofs = 0x06040000,
91239b9004dSPhilipp Zabel 	.disp1_ofs = 0x06048000,
91339b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x07080000,
91439b9004dSPhilipp Zabel 	.vdi_ofs = 0x06068000,
91539b9004dSPhilipp Zabel 	.type = IPUV3M,
91639b9004dSPhilipp Zabel };
91739b9004dSPhilipp Zabel 
91839b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx6q = {
91939b9004dSPhilipp Zabel 	.name = "IPUv3H",
92039b9004dSPhilipp Zabel 	.cm_ofs = 0x00200000,
92139b9004dSPhilipp Zabel 	.cpmem_ofs = 0x00300000,
92239b9004dSPhilipp Zabel 	.srm_ofs = 0x00340000,
92339b9004dSPhilipp Zabel 	.tpm_ofs = 0x00360000,
9242ffd48f2SSteve Longerbeam 	.csi0_ofs = 0x00230000,
9252ffd48f2SSteve Longerbeam 	.csi1_ofs = 0x00238000,
9261aa8ea0dSSteve Longerbeam 	.ic_ofs = 0x00220000,
92739b9004dSPhilipp Zabel 	.disp0_ofs = 0x00240000,
92839b9004dSPhilipp Zabel 	.disp1_ofs = 0x00248000,
92939b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x00380000,
93039b9004dSPhilipp Zabel 	.vdi_ofs = 0x00268000,
93139b9004dSPhilipp Zabel 	.type = IPUV3H,
93239b9004dSPhilipp Zabel };
93339b9004dSPhilipp Zabel 
93439b9004dSPhilipp Zabel static const struct of_device_id imx_ipu_dt_ids[] = {
93539b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, },
93639b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, },
93739b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, },
93892681fe7SLucas Stach 	{ .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6q, },
93939b9004dSPhilipp Zabel 	{ /* sentinel */ }
94039b9004dSPhilipp Zabel };
94139b9004dSPhilipp Zabel MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids);
94239b9004dSPhilipp Zabel 
94339b9004dSPhilipp Zabel static int ipu_submodules_init(struct ipu_soc *ipu,
94439b9004dSPhilipp Zabel 		struct platform_device *pdev, unsigned long ipu_base,
94539b9004dSPhilipp Zabel 		struct clk *ipu_clk)
94639b9004dSPhilipp Zabel {
94739b9004dSPhilipp Zabel 	char *unit;
94839b9004dSPhilipp Zabel 	int ret;
94939b9004dSPhilipp Zabel 	struct device *dev = &pdev->dev;
95039b9004dSPhilipp Zabel 	const struct ipu_devtype *devtype = ipu->devtype;
95139b9004dSPhilipp Zabel 
9527d2691daSSteve Longerbeam 	ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs);
9537d2691daSSteve Longerbeam 	if (ret) {
9547d2691daSSteve Longerbeam 		unit = "cpmem";
9557d2691daSSteve Longerbeam 		goto err_cpmem;
9567d2691daSSteve Longerbeam 	}
9577d2691daSSteve Longerbeam 
9582ffd48f2SSteve Longerbeam 	ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs,
9592ffd48f2SSteve Longerbeam 			   IPU_CONF_CSI0_EN, ipu_clk);
9602ffd48f2SSteve Longerbeam 	if (ret) {
9612ffd48f2SSteve Longerbeam 		unit = "csi0";
9622ffd48f2SSteve Longerbeam 		goto err_csi_0;
9632ffd48f2SSteve Longerbeam 	}
9642ffd48f2SSteve Longerbeam 
9652ffd48f2SSteve Longerbeam 	ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs,
9662ffd48f2SSteve Longerbeam 			   IPU_CONF_CSI1_EN, ipu_clk);
9672ffd48f2SSteve Longerbeam 	if (ret) {
9682ffd48f2SSteve Longerbeam 		unit = "csi1";
9692ffd48f2SSteve Longerbeam 		goto err_csi_1;
9702ffd48f2SSteve Longerbeam 	}
9712ffd48f2SSteve Longerbeam 
9721aa8ea0dSSteve Longerbeam 	ret = ipu_ic_init(ipu, dev,
9731aa8ea0dSSteve Longerbeam 			  ipu_base + devtype->ic_ofs,
9741aa8ea0dSSteve Longerbeam 			  ipu_base + devtype->tpm_ofs);
9751aa8ea0dSSteve Longerbeam 	if (ret) {
9761aa8ea0dSSteve Longerbeam 		unit = "ic";
9771aa8ea0dSSteve Longerbeam 		goto err_ic;
9781aa8ea0dSSteve Longerbeam 	}
9791aa8ea0dSSteve Longerbeam 
9802d2ead45SSteve Longerbeam 	ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs,
9812d2ead45SSteve Longerbeam 			   IPU_CONF_VDI_EN | IPU_CONF_ISP_EN |
9822d2ead45SSteve Longerbeam 			   IPU_CONF_IC_INPUT);
9832d2ead45SSteve Longerbeam 	if (ret) {
9842d2ead45SSteve Longerbeam 		unit = "vdi";
9852d2ead45SSteve Longerbeam 		goto err_vdi;
9862d2ead45SSteve Longerbeam 	}
9872d2ead45SSteve Longerbeam 
988cd98e85aSSteve Longerbeam 	ret = ipu_image_convert_init(ipu, dev);
989cd98e85aSSteve Longerbeam 	if (ret) {
990cd98e85aSSteve Longerbeam 		unit = "image_convert";
991cd98e85aSSteve Longerbeam 		goto err_image_convert;
992cd98e85aSSteve Longerbeam 	}
993cd98e85aSSteve Longerbeam 
99439b9004dSPhilipp Zabel 	ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
99539b9004dSPhilipp Zabel 			  IPU_CONF_DI0_EN, ipu_clk);
99639b9004dSPhilipp Zabel 	if (ret) {
99739b9004dSPhilipp Zabel 		unit = "di0";
99839b9004dSPhilipp Zabel 		goto err_di_0;
99939b9004dSPhilipp Zabel 	}
100039b9004dSPhilipp Zabel 
100139b9004dSPhilipp Zabel 	ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs,
100239b9004dSPhilipp Zabel 			IPU_CONF_DI1_EN, ipu_clk);
100339b9004dSPhilipp Zabel 	if (ret) {
100439b9004dSPhilipp Zabel 		unit = "di1";
100539b9004dSPhilipp Zabel 		goto err_di_1;
100639b9004dSPhilipp Zabel 	}
100739b9004dSPhilipp Zabel 
100839b9004dSPhilipp Zabel 	ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs +
100939b9004dSPhilipp Zabel 			IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs);
101039b9004dSPhilipp Zabel 	if (ret) {
101139b9004dSPhilipp Zabel 		unit = "dc_template";
101239b9004dSPhilipp Zabel 		goto err_dc;
101339b9004dSPhilipp Zabel 	}
101439b9004dSPhilipp Zabel 
101539b9004dSPhilipp Zabel 	ret = ipu_dmfc_init(ipu, dev, ipu_base +
101639b9004dSPhilipp Zabel 			devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk);
101739b9004dSPhilipp Zabel 	if (ret) {
101839b9004dSPhilipp Zabel 		unit = "dmfc";
101939b9004dSPhilipp Zabel 		goto err_dmfc;
102039b9004dSPhilipp Zabel 	}
102139b9004dSPhilipp Zabel 
102239b9004dSPhilipp Zabel 	ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs);
102339b9004dSPhilipp Zabel 	if (ret) {
102439b9004dSPhilipp Zabel 		unit = "dp";
102539b9004dSPhilipp Zabel 		goto err_dp;
102639b9004dSPhilipp Zabel 	}
102739b9004dSPhilipp Zabel 
102835de925fSPhilipp Zabel 	ret = ipu_smfc_init(ipu, dev, ipu_base +
102935de925fSPhilipp Zabel 			devtype->cm_ofs + IPU_CM_SMFC_REG_OFS);
103035de925fSPhilipp Zabel 	if (ret) {
103135de925fSPhilipp Zabel 		unit = "smfc";
103235de925fSPhilipp Zabel 		goto err_smfc;
103335de925fSPhilipp Zabel 	}
103435de925fSPhilipp Zabel 
103539b9004dSPhilipp Zabel 	return 0;
103639b9004dSPhilipp Zabel 
103735de925fSPhilipp Zabel err_smfc:
103835de925fSPhilipp Zabel 	ipu_dp_exit(ipu);
103939b9004dSPhilipp Zabel err_dp:
104039b9004dSPhilipp Zabel 	ipu_dmfc_exit(ipu);
104139b9004dSPhilipp Zabel err_dmfc:
104239b9004dSPhilipp Zabel 	ipu_dc_exit(ipu);
104339b9004dSPhilipp Zabel err_dc:
104439b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 1);
104539b9004dSPhilipp Zabel err_di_1:
104639b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 0);
104739b9004dSPhilipp Zabel err_di_0:
1048cd98e85aSSteve Longerbeam 	ipu_image_convert_exit(ipu);
1049cd98e85aSSteve Longerbeam err_image_convert:
10502d2ead45SSteve Longerbeam 	ipu_vdi_exit(ipu);
10512d2ead45SSteve Longerbeam err_vdi:
10521aa8ea0dSSteve Longerbeam 	ipu_ic_exit(ipu);
10531aa8ea0dSSteve Longerbeam err_ic:
10542ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 1);
10552ffd48f2SSteve Longerbeam err_csi_1:
10562ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 0);
10572ffd48f2SSteve Longerbeam err_csi_0:
10587d2691daSSteve Longerbeam 	ipu_cpmem_exit(ipu);
10597d2691daSSteve Longerbeam err_cpmem:
106039b9004dSPhilipp Zabel 	dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret);
106139b9004dSPhilipp Zabel 	return ret;
106239b9004dSPhilipp Zabel }
106339b9004dSPhilipp Zabel 
106439b9004dSPhilipp Zabel static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs)
106539b9004dSPhilipp Zabel {
106639b9004dSPhilipp Zabel 	unsigned long status;
106739b9004dSPhilipp Zabel 	int i, bit, irq;
106839b9004dSPhilipp Zabel 
106939b9004dSPhilipp Zabel 	for (i = 0; i < num_regs; i++) {
107039b9004dSPhilipp Zabel 
107139b9004dSPhilipp Zabel 		status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i]));
107239b9004dSPhilipp Zabel 		status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i]));
107339b9004dSPhilipp Zabel 
107439b9004dSPhilipp Zabel 		for_each_set_bit(bit, &status, 32) {
1075682b7c1cSLinus Torvalds 			irq = irq_linear_revmap(ipu->domain,
1076682b7c1cSLinus Torvalds 						regs[i] * 32 + bit);
107739b9004dSPhilipp Zabel 			if (irq)
107839b9004dSPhilipp Zabel 				generic_handle_irq(irq);
107939b9004dSPhilipp Zabel 		}
108039b9004dSPhilipp Zabel 	}
108139b9004dSPhilipp Zabel }
108239b9004dSPhilipp Zabel 
1083bd0b9ac4SThomas Gleixner static void ipu_irq_handler(struct irq_desc *desc)
108439b9004dSPhilipp Zabel {
108539b9004dSPhilipp Zabel 	struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
10864d9efdfcSJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
1087ac66b834SColin Ian King 	static const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14};
108839b9004dSPhilipp Zabel 
108939b9004dSPhilipp Zabel 	chained_irq_enter(chip, desc);
109039b9004dSPhilipp Zabel 
109139b9004dSPhilipp Zabel 	ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
109239b9004dSPhilipp Zabel 
109339b9004dSPhilipp Zabel 	chained_irq_exit(chip, desc);
109439b9004dSPhilipp Zabel }
109539b9004dSPhilipp Zabel 
1096bd0b9ac4SThomas Gleixner static void ipu_err_irq_handler(struct irq_desc *desc)
109739b9004dSPhilipp Zabel {
109839b9004dSPhilipp Zabel 	struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
10994d9efdfcSJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
1100ac66b834SColin Ian King 	static const int int_reg[] = { 4, 5, 8, 9};
110139b9004dSPhilipp Zabel 
110239b9004dSPhilipp Zabel 	chained_irq_enter(chip, desc);
110339b9004dSPhilipp Zabel 
110439b9004dSPhilipp Zabel 	ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
110539b9004dSPhilipp Zabel 
110639b9004dSPhilipp Zabel 	chained_irq_exit(chip, desc);
110739b9004dSPhilipp Zabel }
110839b9004dSPhilipp Zabel 
1109682b7c1cSLinus Torvalds int ipu_map_irq(struct ipu_soc *ipu, int irq)
1110682b7c1cSLinus Torvalds {
1111682b7c1cSLinus Torvalds 	int virq;
1112682b7c1cSLinus Torvalds 
1113682b7c1cSLinus Torvalds 	virq = irq_linear_revmap(ipu->domain, irq);
1114682b7c1cSLinus Torvalds 	if (!virq)
1115682b7c1cSLinus Torvalds 		virq = irq_create_mapping(ipu->domain, irq);
1116682b7c1cSLinus Torvalds 
1117682b7c1cSLinus Torvalds 	return virq;
1118682b7c1cSLinus Torvalds }
1119682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_map_irq);
1120682b7c1cSLinus Torvalds 
112139b9004dSPhilipp Zabel int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
112239b9004dSPhilipp Zabel 		enum ipu_channel_irq irq_type)
112339b9004dSPhilipp Zabel {
1124682b7c1cSLinus Torvalds 	return ipu_map_irq(ipu, irq_type + channel->num);
112539b9004dSPhilipp Zabel }
112639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq);
112739b9004dSPhilipp Zabel 
112839b9004dSPhilipp Zabel static void ipu_submodules_exit(struct ipu_soc *ipu)
112939b9004dSPhilipp Zabel {
113035de925fSPhilipp Zabel 	ipu_smfc_exit(ipu);
113139b9004dSPhilipp Zabel 	ipu_dp_exit(ipu);
113239b9004dSPhilipp Zabel 	ipu_dmfc_exit(ipu);
113339b9004dSPhilipp Zabel 	ipu_dc_exit(ipu);
113439b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 1);
113539b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 0);
1136cd98e85aSSteve Longerbeam 	ipu_image_convert_exit(ipu);
11372d2ead45SSteve Longerbeam 	ipu_vdi_exit(ipu);
11381aa8ea0dSSteve Longerbeam 	ipu_ic_exit(ipu);
11392ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 1);
11402ffd48f2SSteve Longerbeam 	ipu_csi_exit(ipu, 0);
11417d2691daSSteve Longerbeam 	ipu_cpmem_exit(ipu);
114239b9004dSPhilipp Zabel }
114339b9004dSPhilipp Zabel 
114439b9004dSPhilipp Zabel static int platform_remove_devices_fn(struct device *dev, void *unused)
114539b9004dSPhilipp Zabel {
114639b9004dSPhilipp Zabel 	struct platform_device *pdev = to_platform_device(dev);
114739b9004dSPhilipp Zabel 
114839b9004dSPhilipp Zabel 	platform_device_unregister(pdev);
114939b9004dSPhilipp Zabel 
115039b9004dSPhilipp Zabel 	return 0;
115139b9004dSPhilipp Zabel }
115239b9004dSPhilipp Zabel 
115339b9004dSPhilipp Zabel static void platform_device_unregister_children(struct platform_device *pdev)
115439b9004dSPhilipp Zabel {
115539b9004dSPhilipp Zabel 	device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn);
115639b9004dSPhilipp Zabel }
115739b9004dSPhilipp Zabel 
115839b9004dSPhilipp Zabel struct ipu_platform_reg {
115939b9004dSPhilipp Zabel 	struct ipu_client_platformdata pdata;
116039b9004dSPhilipp Zabel 	const char *name;
116139b9004dSPhilipp Zabel };
116239b9004dSPhilipp Zabel 
1163304e6be6SPhilipp Zabel /* These must be in the order of the corresponding device tree port nodes */
1164310944d1SPhilipp Zabel static struct ipu_platform_reg client_reg[] = {
116539b9004dSPhilipp Zabel 	{
116639b9004dSPhilipp Zabel 		.pdata = {
1167304e6be6SPhilipp Zabel 			.csi = 0,
1168304e6be6SPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_CSI0,
1169304e6be6SPhilipp Zabel 			.dma[1] = -EINVAL,
1170304e6be6SPhilipp Zabel 		},
117188287ec3SSteve Longerbeam 		.name = "imx-ipuv3-csi",
1172304e6be6SPhilipp Zabel 	}, {
1173304e6be6SPhilipp Zabel 		.pdata = {
1174304e6be6SPhilipp Zabel 			.csi = 1,
1175304e6be6SPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_CSI1,
1176304e6be6SPhilipp Zabel 			.dma[1] = -EINVAL,
1177304e6be6SPhilipp Zabel 		},
117888287ec3SSteve Longerbeam 		.name = "imx-ipuv3-csi",
1179304e6be6SPhilipp Zabel 	}, {
1180304e6be6SPhilipp Zabel 		.pdata = {
118139b9004dSPhilipp Zabel 			.di = 0,
118239b9004dSPhilipp Zabel 			.dc = 5,
118339b9004dSPhilipp Zabel 			.dp = IPU_DP_FLOW_SYNC_BG,
118439b9004dSPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC,
118539b9004dSPhilipp Zabel 			.dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC,
118639b9004dSPhilipp Zabel 		},
118739b9004dSPhilipp Zabel 		.name = "imx-ipuv3-crtc",
118839b9004dSPhilipp Zabel 	}, {
118939b9004dSPhilipp Zabel 		.pdata = {
119039b9004dSPhilipp Zabel 			.di = 1,
119139b9004dSPhilipp Zabel 			.dc = 1,
119239b9004dSPhilipp Zabel 			.dp = -EINVAL,
119339b9004dSPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC,
119439b9004dSPhilipp Zabel 			.dma[1] = -EINVAL,
119539b9004dSPhilipp Zabel 		},
119639b9004dSPhilipp Zabel 		.name = "imx-ipuv3-crtc",
119739b9004dSPhilipp Zabel 	},
119839b9004dSPhilipp Zabel };
119939b9004dSPhilipp Zabel 
120039b9004dSPhilipp Zabel static DEFINE_MUTEX(ipu_client_id_mutex);
120139b9004dSPhilipp Zabel static int ipu_client_id;
120239b9004dSPhilipp Zabel 
1203d6ca8ca7SPhilipp Zabel static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base)
120439b9004dSPhilipp Zabel {
120539b9004dSPhilipp Zabel 	struct device *dev = ipu->dev;
120639b9004dSPhilipp Zabel 	unsigned i;
120739b9004dSPhilipp Zabel 	int id, ret;
120839b9004dSPhilipp Zabel 
120939b9004dSPhilipp Zabel 	mutex_lock(&ipu_client_id_mutex);
121039b9004dSPhilipp Zabel 	id = ipu_client_id;
121139b9004dSPhilipp Zabel 	ipu_client_id += ARRAY_SIZE(client_reg);
121239b9004dSPhilipp Zabel 	mutex_unlock(&ipu_client_id_mutex);
121339b9004dSPhilipp Zabel 
121439b9004dSPhilipp Zabel 	for (i = 0; i < ARRAY_SIZE(client_reg); i++) {
1215310944d1SPhilipp Zabel 		struct ipu_platform_reg *reg = &client_reg[i];
121639b9004dSPhilipp Zabel 		struct platform_device *pdev;
121717e05217SPhilipp Zabel 		struct device_node *of_node;
121817e05217SPhilipp Zabel 
121917e05217SPhilipp Zabel 		/* Associate subdevice with the corresponding port node */
122017e05217SPhilipp Zabel 		of_node = of_graph_get_port_by_id(dev->of_node, i);
122117e05217SPhilipp Zabel 		if (!of_node) {
122217e05217SPhilipp Zabel 			dev_info(dev,
12234bf99144SRob Herring 				 "no port@%d node in %pOF, not using %s%d\n",
12244bf99144SRob Herring 				 i, dev->of_node,
122517e05217SPhilipp Zabel 				 (i / 2) ? "DI" : "CSI", i % 2);
122617e05217SPhilipp Zabel 			continue;
122717e05217SPhilipp Zabel 		}
122839b9004dSPhilipp Zabel 
1229304e6be6SPhilipp Zabel 		pdev = platform_device_alloc(reg->name, id++);
1230304e6be6SPhilipp Zabel 		if (!pdev) {
1231304e6be6SPhilipp Zabel 			ret = -ENOMEM;
1232304e6be6SPhilipp Zabel 			goto err_register;
1233304e6be6SPhilipp Zabel 		}
123439b9004dSPhilipp Zabel 
1235304e6be6SPhilipp Zabel 		pdev->dev.parent = dev;
1236304e6be6SPhilipp Zabel 
1237310944d1SPhilipp Zabel 		reg->pdata.of_node = of_node;
1238304e6be6SPhilipp Zabel 		ret = platform_device_add_data(pdev, &reg->pdata,
1239304e6be6SPhilipp Zabel 					       sizeof(reg->pdata));
1240304e6be6SPhilipp Zabel 		if (!ret)
1241304e6be6SPhilipp Zabel 			ret = platform_device_add(pdev);
1242304e6be6SPhilipp Zabel 		if (ret) {
1243304e6be6SPhilipp Zabel 			platform_device_put(pdev);
124439b9004dSPhilipp Zabel 			goto err_register;
124539b9004dSPhilipp Zabel 		}
1246e4946cdcSAxel Lin 	}
124739b9004dSPhilipp Zabel 
124839b9004dSPhilipp Zabel 	return 0;
124939b9004dSPhilipp Zabel 
125039b9004dSPhilipp Zabel err_register:
125139b9004dSPhilipp Zabel 	platform_device_unregister_children(to_platform_device(dev));
125239b9004dSPhilipp Zabel 
125339b9004dSPhilipp Zabel 	return ret;
125439b9004dSPhilipp Zabel }
125539b9004dSPhilipp Zabel 
125639b9004dSPhilipp Zabel 
125739b9004dSPhilipp Zabel static int ipu_irq_init(struct ipu_soc *ipu)
125839b9004dSPhilipp Zabel {
125939b9004dSPhilipp Zabel 	struct irq_chip_generic *gc;
126039b9004dSPhilipp Zabel 	struct irq_chip_type *ct;
126139b9004dSPhilipp Zabel 	unsigned long unused[IPU_NUM_IRQS / 32] = {
126239b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
126339b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
126439b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
126539b9004dSPhilipp Zabel 		0x4077ffff, 0xffe7e1fd,
126639b9004dSPhilipp Zabel 		0x23fffffe, 0x8880fff0,
126739b9004dSPhilipp Zabel 		0xf98fe7d0, 0xfff81fff,
126839b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
126939b9004dSPhilipp Zabel 		0x00000000,
127039b9004dSPhilipp Zabel 	};
127139b9004dSPhilipp Zabel 	int ret, i;
127239b9004dSPhilipp Zabel 
127339b9004dSPhilipp Zabel 	ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS,
127439b9004dSPhilipp Zabel 					    &irq_generic_chip_ops, ipu);
127539b9004dSPhilipp Zabel 	if (!ipu->domain) {
127639b9004dSPhilipp Zabel 		dev_err(ipu->dev, "failed to add irq domain\n");
127739b9004dSPhilipp Zabel 		return -ENODEV;
127839b9004dSPhilipp Zabel 	}
127939b9004dSPhilipp Zabel 
128039b9004dSPhilipp Zabel 	ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU",
1281ca0141deSRob Herring 					     handle_level_irq, 0, 0, 0);
128239b9004dSPhilipp Zabel 	if (ret < 0) {
128339b9004dSPhilipp Zabel 		dev_err(ipu->dev, "failed to alloc generic irq chips\n");
128439b9004dSPhilipp Zabel 		irq_domain_remove(ipu->domain);
128539b9004dSPhilipp Zabel 		return ret;
128639b9004dSPhilipp Zabel 	}
128739b9004dSPhilipp Zabel 
1288a92d8145SPhilipp Zabel 	/* Mask and clear all interrupts */
1289a92d8145SPhilipp Zabel 	for (i = 0; i < IPU_NUM_IRQS; i += 32) {
1290510e6426SRussell King 		ipu_cm_write(ipu, 0, IPU_INT_CTRL(i / 32));
1291a92d8145SPhilipp Zabel 		ipu_cm_write(ipu, ~unused[i / 32], IPU_INT_STAT(i / 32));
1292a92d8145SPhilipp Zabel 	}
1293510e6426SRussell King 
129439b9004dSPhilipp Zabel 	for (i = 0; i < IPU_NUM_IRQS; i += 32) {
129539b9004dSPhilipp Zabel 		gc = irq_get_domain_generic_chip(ipu->domain, i);
129639b9004dSPhilipp Zabel 		gc->reg_base = ipu->cm_reg;
129739b9004dSPhilipp Zabel 		gc->unused = unused[i / 32];
129839b9004dSPhilipp Zabel 		ct = gc->chip_types;
129939b9004dSPhilipp Zabel 		ct->chip.irq_ack = irq_gc_ack_set_bit;
130039b9004dSPhilipp Zabel 		ct->chip.irq_mask = irq_gc_mask_clr_bit;
130139b9004dSPhilipp Zabel 		ct->chip.irq_unmask = irq_gc_mask_set_bit;
130239b9004dSPhilipp Zabel 		ct->regs.ack = IPU_INT_STAT(i / 32);
130339b9004dSPhilipp Zabel 		ct->regs.mask = IPU_INT_CTRL(i / 32);
130439b9004dSPhilipp Zabel 	}
130539b9004dSPhilipp Zabel 
130686f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu);
130786f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler,
130886f5e733SRussell King 					 ipu);
130939b9004dSPhilipp Zabel 
131039b9004dSPhilipp Zabel 	return 0;
131139b9004dSPhilipp Zabel }
131239b9004dSPhilipp Zabel 
131339b9004dSPhilipp Zabel static void ipu_irq_exit(struct ipu_soc *ipu)
131439b9004dSPhilipp Zabel {
131539b9004dSPhilipp Zabel 	int i, irq;
131639b9004dSPhilipp Zabel 
131786f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL);
131886f5e733SRussell King 	irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL);
131939b9004dSPhilipp Zabel 
132039b9004dSPhilipp Zabel 	/* TODO: remove irq_domain_generic_chips */
132139b9004dSPhilipp Zabel 
132239b9004dSPhilipp Zabel 	for (i = 0; i < IPU_NUM_IRQS; i++) {
132339b9004dSPhilipp Zabel 		irq = irq_linear_revmap(ipu->domain, i);
132439b9004dSPhilipp Zabel 		if (irq)
132539b9004dSPhilipp Zabel 			irq_dispose_mapping(irq);
132639b9004dSPhilipp Zabel 	}
132739b9004dSPhilipp Zabel 
132839b9004dSPhilipp Zabel 	irq_domain_remove(ipu->domain);
132939b9004dSPhilipp Zabel }
133039b9004dSPhilipp Zabel 
13313feb049fSSteve Longerbeam void ipu_dump(struct ipu_soc *ipu)
13323feb049fSSteve Longerbeam {
13333feb049fSSteve Longerbeam 	int i;
13343feb049fSSteve Longerbeam 
13353feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n",
13363feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_CONF));
13373feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n",
13383feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CONF));
13393feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n",
13403feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_EN(0)));
13413feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n",
13423feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_EN(32)));
13433feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n",
13443feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_PRI(0)));
13453feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n",
13463feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_CHA_PRI(32)));
13473feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n",
13483feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_BAND_EN(0)));
13493feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n",
13503feb049fSSteve Longerbeam 		ipu_idmac_read(ipu, IDMAC_BAND_EN(32)));
13513feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n",
13523feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0)));
13533feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n",
13543feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32)));
13553feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n",
13563feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_PROC_FLOW1));
13573feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n",
13583feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_PROC_FLOW2));
13593feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n",
13603feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_PROC_FLOW3));
13613feb049fSSteve Longerbeam 	dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n",
13623feb049fSSteve Longerbeam 		ipu_cm_read(ipu, IPU_FS_DISP_FLOW1));
13633feb049fSSteve Longerbeam 	for (i = 0; i < 15; i++)
13643feb049fSSteve Longerbeam 		dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i,
13653feb049fSSteve Longerbeam 			ipu_cm_read(ipu, IPU_INT_CTRL(i)));
13663feb049fSSteve Longerbeam }
13673feb049fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_dump);
13683feb049fSSteve Longerbeam 
136939b9004dSPhilipp Zabel static int ipu_probe(struct platform_device *pdev)
137039b9004dSPhilipp Zabel {
1371572a7615SSteve Longerbeam 	struct device_node *np = pdev->dev.of_node;
137239b9004dSPhilipp Zabel 	struct ipu_soc *ipu;
137339b9004dSPhilipp Zabel 	struct resource *res;
137439b9004dSPhilipp Zabel 	unsigned long ipu_base;
137593adc8b5SPhilipp Zabel 	int ret, irq_sync, irq_err;
137639b9004dSPhilipp Zabel 	const struct ipu_devtype *devtype;
137739b9004dSPhilipp Zabel 
1378e92e4478SLABBE Corentin 	devtype = of_device_get_match_data(&pdev->dev);
1379e92e4478SLABBE Corentin 	if (!devtype)
1380e92e4478SLABBE Corentin 		return -EINVAL;
138139b9004dSPhilipp Zabel 
138239b9004dSPhilipp Zabel 	irq_sync = platform_get_irq(pdev, 0);
138339b9004dSPhilipp Zabel 	irq_err = platform_get_irq(pdev, 1);
138439b9004dSPhilipp Zabel 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
138539b9004dSPhilipp Zabel 
138639b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n",
138739b9004dSPhilipp Zabel 			irq_sync, irq_err);
138839b9004dSPhilipp Zabel 
138939b9004dSPhilipp Zabel 	if (!res || irq_sync < 0 || irq_err < 0)
139039b9004dSPhilipp Zabel 		return -ENODEV;
139139b9004dSPhilipp Zabel 
139239b9004dSPhilipp Zabel 	ipu_base = res->start;
139339b9004dSPhilipp Zabel 
139439b9004dSPhilipp Zabel 	ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL);
139539b9004dSPhilipp Zabel 	if (!ipu)
139639b9004dSPhilipp Zabel 		return -ENODEV;
139739b9004dSPhilipp Zabel 
139892681fe7SLucas Stach 	ipu->id = of_alias_get_id(np, "ipu");
13992d87e6c1SPhilipp Zabel 	if (ipu->id < 0)
14002d87e6c1SPhilipp Zabel 		ipu->id = 0;
140192681fe7SLucas Stach 
140230310c83SLucas Stach 	if (of_device_is_compatible(np, "fsl,imx6qp-ipu") &&
140330310c83SLucas Stach 	    IS_ENABLED(CONFIG_DRM)) {
140492681fe7SLucas Stach 		ipu->prg_priv = ipu_prg_lookup_by_phandle(&pdev->dev,
140592681fe7SLucas Stach 							  "fsl,prg", ipu->id);
140692681fe7SLucas Stach 		if (!ipu->prg_priv)
140792681fe7SLucas Stach 			return -EPROBE_DEFER;
140892681fe7SLucas Stach 	}
140992681fe7SLucas Stach 
141039b9004dSPhilipp Zabel 	ipu->devtype = devtype;
141139b9004dSPhilipp Zabel 	ipu->ipu_type = devtype->type;
141239b9004dSPhilipp Zabel 
141339b9004dSPhilipp Zabel 	spin_lock_init(&ipu->lock);
141439b9004dSPhilipp Zabel 	mutex_init(&ipu->channel_lock);
141593adc8b5SPhilipp Zabel 	INIT_LIST_HEAD(&ipu->channels);
141639b9004dSPhilipp Zabel 
141739b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "cm_reg:   0x%08lx\n",
141839b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs);
141939b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "idmac:    0x%08lx\n",
142039b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS);
142139b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "cpmem:    0x%08lx\n",
142239b9004dSPhilipp Zabel 			ipu_base + devtype->cpmem_ofs);
14232ffd48f2SSteve Longerbeam 	dev_dbg(&pdev->dev, "csi0:    0x%08lx\n",
14242ffd48f2SSteve Longerbeam 			ipu_base + devtype->csi0_ofs);
14252ffd48f2SSteve Longerbeam 	dev_dbg(&pdev->dev, "csi1:    0x%08lx\n",
14262ffd48f2SSteve Longerbeam 			ipu_base + devtype->csi1_ofs);
14271aa8ea0dSSteve Longerbeam 	dev_dbg(&pdev->dev, "ic:      0x%08lx\n",
14281aa8ea0dSSteve Longerbeam 			ipu_base + devtype->ic_ofs);
142939b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "disp0:    0x%08lx\n",
143039b9004dSPhilipp Zabel 			ipu_base + devtype->disp0_ofs);
143139b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "disp1:    0x%08lx\n",
143239b9004dSPhilipp Zabel 			ipu_base + devtype->disp1_ofs);
143339b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "srm:      0x%08lx\n",
143439b9004dSPhilipp Zabel 			ipu_base + devtype->srm_ofs);
143539b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "tpm:      0x%08lx\n",
143639b9004dSPhilipp Zabel 			ipu_base + devtype->tpm_ofs);
143739b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "dc:       0x%08lx\n",
143839b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS);
143939b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "ic:       0x%08lx\n",
144039b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS);
144139b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "dmfc:     0x%08lx\n",
144239b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS);
144339b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "vdi:      0x%08lx\n",
144439b9004dSPhilipp Zabel 			ipu_base + devtype->vdi_ofs);
144539b9004dSPhilipp Zabel 
144639b9004dSPhilipp Zabel 	ipu->cm_reg = devm_ioremap(&pdev->dev,
144739b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs, PAGE_SIZE);
144839b9004dSPhilipp Zabel 	ipu->idmac_reg = devm_ioremap(&pdev->dev,
144939b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS,
145039b9004dSPhilipp Zabel 			PAGE_SIZE);
145139b9004dSPhilipp Zabel 
14527d2691daSSteve Longerbeam 	if (!ipu->cm_reg || !ipu->idmac_reg)
145339b9004dSPhilipp Zabel 		return -ENOMEM;
145439b9004dSPhilipp Zabel 
145539b9004dSPhilipp Zabel 	ipu->clk = devm_clk_get(&pdev->dev, "bus");
145639b9004dSPhilipp Zabel 	if (IS_ERR(ipu->clk)) {
145739b9004dSPhilipp Zabel 		ret = PTR_ERR(ipu->clk);
145839b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "clk_get failed with %d", ret);
145939b9004dSPhilipp Zabel 		return ret;
146039b9004dSPhilipp Zabel 	}
146139b9004dSPhilipp Zabel 
146239b9004dSPhilipp Zabel 	platform_set_drvdata(pdev, ipu);
146339b9004dSPhilipp Zabel 
146439b9004dSPhilipp Zabel 	ret = clk_prepare_enable(ipu->clk);
146539b9004dSPhilipp Zabel 	if (ret) {
146639b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
146739b9004dSPhilipp Zabel 		return ret;
146839b9004dSPhilipp Zabel 	}
146939b9004dSPhilipp Zabel 
147039b9004dSPhilipp Zabel 	ipu->dev = &pdev->dev;
147139b9004dSPhilipp Zabel 	ipu->irq_sync = irq_sync;
147239b9004dSPhilipp Zabel 	ipu->irq_err = irq_err;
147339b9004dSPhilipp Zabel 
147439b9004dSPhilipp Zabel 	ret = device_reset(&pdev->dev);
147539b9004dSPhilipp Zabel 	if (ret) {
147639b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "failed to reset: %d\n", ret);
147739b9004dSPhilipp Zabel 		goto out_failed_reset;
147839b9004dSPhilipp Zabel 	}
147939b9004dSPhilipp Zabel 	ret = ipu_memory_reset(ipu);
148039b9004dSPhilipp Zabel 	if (ret)
148139b9004dSPhilipp Zabel 		goto out_failed_reset;
148239b9004dSPhilipp Zabel 
1483596a65d1SDavid Jander 	ret = ipu_irq_init(ipu);
1484596a65d1SDavid Jander 	if (ret)
1485596a65d1SDavid Jander 		goto out_failed_irq;
1486596a65d1SDavid Jander 
148739b9004dSPhilipp Zabel 	/* Set MCU_T to divide MCU access window into 2 */
148839b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
148939b9004dSPhilipp Zabel 			IPU_DISP_GEN);
149039b9004dSPhilipp Zabel 
149139b9004dSPhilipp Zabel 	ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk);
149239b9004dSPhilipp Zabel 	if (ret)
149339b9004dSPhilipp Zabel 		goto failed_submodules_init;
149439b9004dSPhilipp Zabel 
1495d6ca8ca7SPhilipp Zabel 	ret = ipu_add_client_devices(ipu, ipu_base);
149639b9004dSPhilipp Zabel 	if (ret) {
149739b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "adding client devices failed with %d\n",
149839b9004dSPhilipp Zabel 				ret);
149939b9004dSPhilipp Zabel 		goto failed_add_clients;
150039b9004dSPhilipp Zabel 	}
150139b9004dSPhilipp Zabel 
150239b9004dSPhilipp Zabel 	dev_info(&pdev->dev, "%s probed\n", devtype->name);
150339b9004dSPhilipp Zabel 
150439b9004dSPhilipp Zabel 	return 0;
150539b9004dSPhilipp Zabel 
150639b9004dSPhilipp Zabel failed_add_clients:
150739b9004dSPhilipp Zabel 	ipu_submodules_exit(ipu);
150839b9004dSPhilipp Zabel failed_submodules_init:
150939b9004dSPhilipp Zabel 	ipu_irq_exit(ipu);
151039b9004dSPhilipp Zabel out_failed_irq:
1511596a65d1SDavid Jander out_failed_reset:
151239b9004dSPhilipp Zabel 	clk_disable_unprepare(ipu->clk);
151339b9004dSPhilipp Zabel 	return ret;
151439b9004dSPhilipp Zabel }
151539b9004dSPhilipp Zabel 
151639b9004dSPhilipp Zabel static int ipu_remove(struct platform_device *pdev)
151739b9004dSPhilipp Zabel {
151839b9004dSPhilipp Zabel 	struct ipu_soc *ipu = platform_get_drvdata(pdev);
151939b9004dSPhilipp Zabel 
152039b9004dSPhilipp Zabel 	platform_device_unregister_children(pdev);
152139b9004dSPhilipp Zabel 	ipu_submodules_exit(ipu);
152239b9004dSPhilipp Zabel 	ipu_irq_exit(ipu);
152339b9004dSPhilipp Zabel 
152439b9004dSPhilipp Zabel 	clk_disable_unprepare(ipu->clk);
152539b9004dSPhilipp Zabel 
152639b9004dSPhilipp Zabel 	return 0;
152739b9004dSPhilipp Zabel }
152839b9004dSPhilipp Zabel 
152939b9004dSPhilipp Zabel static struct platform_driver imx_ipu_driver = {
153039b9004dSPhilipp Zabel 	.driver = {
153139b9004dSPhilipp Zabel 		.name = "imx-ipuv3",
153239b9004dSPhilipp Zabel 		.of_match_table = imx_ipu_dt_ids,
153339b9004dSPhilipp Zabel 	},
153439b9004dSPhilipp Zabel 	.probe = ipu_probe,
153539b9004dSPhilipp Zabel 	.remove = ipu_remove,
153639b9004dSPhilipp Zabel };
153739b9004dSPhilipp Zabel 
1538d2a34232SLucas Stach static struct platform_driver * const drivers[] = {
153930310c83SLucas Stach #if IS_ENABLED(CONFIG_DRM)
1540d2a34232SLucas Stach 	&ipu_pre_drv,
1541ea9c2605SLucas Stach 	&ipu_prg_drv,
154230310c83SLucas Stach #endif
1543d2a34232SLucas Stach 	&imx_ipu_driver,
1544d2a34232SLucas Stach };
1545d2a34232SLucas Stach 
1546d2a34232SLucas Stach static int __init imx_ipu_init(void)
1547d2a34232SLucas Stach {
1548d2a34232SLucas Stach 	return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
1549d2a34232SLucas Stach }
1550d2a34232SLucas Stach module_init(imx_ipu_init);
1551d2a34232SLucas Stach 
1552d2a34232SLucas Stach static void __exit imx_ipu_exit(void)
1553d2a34232SLucas Stach {
1554d2a34232SLucas Stach 	platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
1555d2a34232SLucas Stach }
1556d2a34232SLucas Stach module_exit(imx_ipu_exit);
155739b9004dSPhilipp Zabel 
155839b9004dSPhilipp Zabel MODULE_ALIAS("platform:imx-ipuv3");
155939b9004dSPhilipp Zabel MODULE_DESCRIPTION("i.MX IPU v3 driver");
156039b9004dSPhilipp Zabel MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
156139b9004dSPhilipp Zabel MODULE_LICENSE("GPL");
1562