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>
21*722d4f06SRob Herring #include <linux/of.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
ipu_cm_read(struct ipu_soc * ipu,unsigned offset)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
ipu_cm_write(struct ipu_soc * ipu,u32 value,unsigned offset)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
ipu_get_num(struct ipu_soc * ipu)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
ipu_srm_dp_update(struct ipu_soc * ipu,bool sync)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
ipu_drm_fourcc_to_colorspace(u32 drm_fourcc)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
ipu_pixelformat_to_colorspace(u32 pixelformat)10339b9004dSPhilipp Zabel enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat)
10439b9004dSPhilipp Zabel {
10539b9004dSPhilipp Zabel switch (pixelformat) {
10639b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUV420:
10739b9004dSPhilipp Zabel case V4L2_PIX_FMT_YVU420:
1089a34cef0SSteve Longerbeam case V4L2_PIX_FMT_YUV422P:
10939b9004dSPhilipp Zabel case V4L2_PIX_FMT_UYVY:
11039b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUYV:
1119a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV12:
1129a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV21:
1139a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV16:
1149a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV61:
11539b9004dSPhilipp Zabel return IPUV3_COLORSPACE_YUV;
11639b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB565:
117a5995717SPhilipp Zabel case V4L2_PIX_FMT_BGR24:
118a5995717SPhilipp Zabel case V4L2_PIX_FMT_RGB24:
119a5995717SPhilipp Zabel case V4L2_PIX_FMT_ABGR32:
120a5995717SPhilipp Zabel case V4L2_PIX_FMT_XBGR32:
121a5995717SPhilipp Zabel case V4L2_PIX_FMT_BGRA32:
122a5995717SPhilipp Zabel case V4L2_PIX_FMT_BGRX32:
123a5995717SPhilipp Zabel case V4L2_PIX_FMT_RGBA32:
124a5995717SPhilipp Zabel case V4L2_PIX_FMT_RGBX32:
125a5995717SPhilipp Zabel case V4L2_PIX_FMT_ARGB32:
126a5995717SPhilipp Zabel case V4L2_PIX_FMT_XRGB32:
12722b2cfadSSteve Longerbeam case V4L2_PIX_FMT_RGB32:
12822b2cfadSSteve Longerbeam case V4L2_PIX_FMT_BGR32:
12939b9004dSPhilipp Zabel return IPUV3_COLORSPACE_RGB;
13039b9004dSPhilipp Zabel default:
13139b9004dSPhilipp Zabel return IPUV3_COLORSPACE_UNKNOWN;
13239b9004dSPhilipp Zabel }
13339b9004dSPhilipp Zabel }
13439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace);
13539b9004dSPhilipp Zabel
ipu_degrees_to_rot_mode(enum ipu_rotate_mode * mode,int degrees,bool hflip,bool vflip)136f835f386SSteve Longerbeam int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees,
137f835f386SSteve Longerbeam bool hflip, bool vflip)
138f835f386SSteve Longerbeam {
139f835f386SSteve Longerbeam u32 r90, vf, hf;
140f835f386SSteve Longerbeam
141f835f386SSteve Longerbeam switch (degrees) {
142f835f386SSteve Longerbeam case 0:
143f835f386SSteve Longerbeam vf = hf = r90 = 0;
144f835f386SSteve Longerbeam break;
145f835f386SSteve Longerbeam case 90:
146f835f386SSteve Longerbeam vf = hf = 0;
147f835f386SSteve Longerbeam r90 = 1;
148f835f386SSteve Longerbeam break;
149f835f386SSteve Longerbeam case 180:
150f835f386SSteve Longerbeam vf = hf = 1;
151f835f386SSteve Longerbeam r90 = 0;
152f835f386SSteve Longerbeam break;
153f835f386SSteve Longerbeam case 270:
154f835f386SSteve Longerbeam vf = hf = r90 = 1;
155f835f386SSteve Longerbeam break;
156f835f386SSteve Longerbeam default:
157f835f386SSteve Longerbeam return -EINVAL;
158f835f386SSteve Longerbeam }
159f835f386SSteve Longerbeam
160f835f386SSteve Longerbeam hf ^= (u32)hflip;
161f835f386SSteve Longerbeam vf ^= (u32)vflip;
162f835f386SSteve Longerbeam
163f835f386SSteve Longerbeam *mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf);
164f835f386SSteve Longerbeam return 0;
165f835f386SSteve Longerbeam }
166f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode);
167f835f386SSteve Longerbeam
ipu_rot_mode_to_degrees(int * degrees,enum ipu_rotate_mode mode,bool hflip,bool vflip)168f835f386SSteve Longerbeam int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode,
169f835f386SSteve Longerbeam bool hflip, bool vflip)
170f835f386SSteve Longerbeam {
171f835f386SSteve Longerbeam u32 r90, vf, hf;
172f835f386SSteve Longerbeam
173f835f386SSteve Longerbeam r90 = ((u32)mode >> 2) & 0x1;
174f835f386SSteve Longerbeam hf = ((u32)mode >> 1) & 0x1;
175f835f386SSteve Longerbeam vf = ((u32)mode >> 0) & 0x1;
176f835f386SSteve Longerbeam hf ^= (u32)hflip;
177f835f386SSteve Longerbeam vf ^= (u32)vflip;
178f835f386SSteve Longerbeam
179f835f386SSteve Longerbeam switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) {
180f835f386SSteve Longerbeam case IPU_ROTATE_NONE:
181f835f386SSteve Longerbeam *degrees = 0;
182f835f386SSteve Longerbeam break;
183f835f386SSteve Longerbeam case IPU_ROTATE_90_RIGHT:
184f835f386SSteve Longerbeam *degrees = 90;
185f835f386SSteve Longerbeam break;
186f835f386SSteve Longerbeam case IPU_ROTATE_180:
187f835f386SSteve Longerbeam *degrees = 180;
188f835f386SSteve Longerbeam break;
189f835f386SSteve Longerbeam case IPU_ROTATE_90_LEFT:
190f835f386SSteve Longerbeam *degrees = 270;
191f835f386SSteve Longerbeam break;
192f835f386SSteve Longerbeam default:
193f835f386SSteve Longerbeam return -EINVAL;
194f835f386SSteve Longerbeam }
195f835f386SSteve Longerbeam
196f835f386SSteve Longerbeam return 0;
197f835f386SSteve Longerbeam }
198f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees);
199f835f386SSteve Longerbeam
ipu_idmac_get(struct ipu_soc * ipu,unsigned num)20039b9004dSPhilipp Zabel struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num)
20139b9004dSPhilipp Zabel {
20239b9004dSPhilipp Zabel struct ipuv3_channel *channel;
20339b9004dSPhilipp Zabel
20439b9004dSPhilipp Zabel dev_dbg(ipu->dev, "%s %d\n", __func__, num);
20539b9004dSPhilipp Zabel
20639b9004dSPhilipp Zabel if (num > 63)
20739b9004dSPhilipp Zabel return ERR_PTR(-ENODEV);
20839b9004dSPhilipp Zabel
20939b9004dSPhilipp Zabel mutex_lock(&ipu->channel_lock);
21039b9004dSPhilipp Zabel
21193adc8b5SPhilipp Zabel list_for_each_entry(channel, &ipu->channels, list) {
21293adc8b5SPhilipp Zabel if (channel->num == num) {
21339b9004dSPhilipp Zabel channel = ERR_PTR(-EBUSY);
21439b9004dSPhilipp Zabel goto out;
21539b9004dSPhilipp Zabel }
21693adc8b5SPhilipp Zabel }
21739b9004dSPhilipp Zabel
21893adc8b5SPhilipp Zabel channel = kzalloc(sizeof(*channel), GFP_KERNEL);
21993adc8b5SPhilipp Zabel if (!channel) {
22093adc8b5SPhilipp Zabel channel = ERR_PTR(-ENOMEM);
22193adc8b5SPhilipp Zabel goto out;
22293adc8b5SPhilipp Zabel }
22393adc8b5SPhilipp Zabel
22439b9004dSPhilipp Zabel channel->num = num;
22593adc8b5SPhilipp Zabel channel->ipu = ipu;
22693adc8b5SPhilipp Zabel list_add(&channel->list, &ipu->channels);
22739b9004dSPhilipp Zabel
22839b9004dSPhilipp Zabel out:
22939b9004dSPhilipp Zabel mutex_unlock(&ipu->channel_lock);
23039b9004dSPhilipp Zabel
23139b9004dSPhilipp Zabel return channel;
23239b9004dSPhilipp Zabel }
23339b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get);
23439b9004dSPhilipp Zabel
ipu_idmac_put(struct ipuv3_channel * channel)23539b9004dSPhilipp Zabel void ipu_idmac_put(struct ipuv3_channel *channel)
23639b9004dSPhilipp Zabel {
23739b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu;
23839b9004dSPhilipp Zabel
23939b9004dSPhilipp Zabel dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num);
24039b9004dSPhilipp Zabel
24139b9004dSPhilipp Zabel mutex_lock(&ipu->channel_lock);
24239b9004dSPhilipp Zabel
24393adc8b5SPhilipp Zabel list_del(&channel->list);
24493adc8b5SPhilipp Zabel kfree(channel);
24539b9004dSPhilipp Zabel
24639b9004dSPhilipp Zabel mutex_unlock(&ipu->channel_lock);
24739b9004dSPhilipp Zabel }
24839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_put);
24939b9004dSPhilipp Zabel
250aa52f578SSteve Longerbeam #define idma_mask(ch) (1 << ((ch) & 0x1f))
25139b9004dSPhilipp Zabel
252e7268c69SSteve Longerbeam /*
253e7268c69SSteve Longerbeam * This is an undocumented feature, a write one to a channel bit in
254e7268c69SSteve Longerbeam * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's
255e7268c69SSteve Longerbeam * internal current buffer pointer so that transfers start from buffer
256e7268c69SSteve Longerbeam * 0 on the next channel enable (that's the theory anyway, the imx6 TRM
257e7268c69SSteve Longerbeam * only says these are read-only registers). This operation is required
258e7268c69SSteve Longerbeam * for channel linking to work correctly, for instance video capture
259e7268c69SSteve Longerbeam * pipelines that carry out image rotations will fail after the first
260e7268c69SSteve Longerbeam * streaming unless this function is called for each channel before
261e7268c69SSteve Longerbeam * re-enabling the channels.
262e7268c69SSteve Longerbeam */
__ipu_idmac_reset_current_buffer(struct ipuv3_channel * channel)263e7268c69SSteve Longerbeam static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel)
264e7268c69SSteve Longerbeam {
265e7268c69SSteve Longerbeam struct ipu_soc *ipu = channel->ipu;
266e7268c69SSteve Longerbeam unsigned int chno = channel->num;
267e7268c69SSteve Longerbeam
268e7268c69SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno));
269e7268c69SSteve Longerbeam }
270e7268c69SSteve Longerbeam
ipu_idmac_set_double_buffer(struct ipuv3_channel * channel,bool doublebuffer)27139b9004dSPhilipp Zabel void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
27239b9004dSPhilipp Zabel bool doublebuffer)
27339b9004dSPhilipp Zabel {
27439b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu;
27539b9004dSPhilipp Zabel unsigned long flags;
27639b9004dSPhilipp Zabel u32 reg;
27739b9004dSPhilipp Zabel
27839b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags);
27939b9004dSPhilipp Zabel
28039b9004dSPhilipp Zabel reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
28139b9004dSPhilipp Zabel if (doublebuffer)
28239b9004dSPhilipp Zabel reg |= idma_mask(channel->num);
28339b9004dSPhilipp Zabel else
28439b9004dSPhilipp Zabel reg &= ~idma_mask(channel->num);
28539b9004dSPhilipp Zabel ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num));
28639b9004dSPhilipp Zabel
287e7268c69SSteve Longerbeam __ipu_idmac_reset_current_buffer(channel);
288e7268c69SSteve Longerbeam
28939b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags);
29039b9004dSPhilipp Zabel }
29139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer);
29239b9004dSPhilipp Zabel
2934fd1a07aSSteve Longerbeam static const struct {
2944fd1a07aSSteve Longerbeam int chnum;
2954fd1a07aSSteve Longerbeam u32 reg;
2964fd1a07aSSteve Longerbeam int shift;
2974fd1a07aSSteve Longerbeam } idmac_lock_en_info[] = {
2984fd1a07aSSteve Longerbeam { .chnum = 5, .reg = IDMAC_CH_LOCK_EN_1, .shift = 0, },
2994fd1a07aSSteve Longerbeam { .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift = 2, },
3004fd1a07aSSteve Longerbeam { .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift = 4, },
3014fd1a07aSSteve Longerbeam { .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift = 6, },
3024fd1a07aSSteve Longerbeam { .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift = 8, },
3034fd1a07aSSteve Longerbeam { .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, },
3044fd1a07aSSteve Longerbeam { .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, },
3054fd1a07aSSteve Longerbeam { .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, },
3064fd1a07aSSteve Longerbeam { .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, },
3074fd1a07aSSteve Longerbeam { .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, },
3084fd1a07aSSteve Longerbeam { .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, },
3094fd1a07aSSteve Longerbeam { .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift = 0, },
3104fd1a07aSSteve Longerbeam { .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift = 2, },
3114fd1a07aSSteve Longerbeam { .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift = 4, },
3124fd1a07aSSteve Longerbeam { .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift = 6, },
3134fd1a07aSSteve Longerbeam { .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift = 8, },
3144fd1a07aSSteve Longerbeam { .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, },
3154fd1a07aSSteve Longerbeam };
3164fd1a07aSSteve Longerbeam
ipu_idmac_lock_enable(struct ipuv3_channel * channel,int num_bursts)3174fd1a07aSSteve Longerbeam int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts)
3184fd1a07aSSteve Longerbeam {
3194fd1a07aSSteve Longerbeam struct ipu_soc *ipu = channel->ipu;
3204fd1a07aSSteve Longerbeam unsigned long flags;
3214fd1a07aSSteve Longerbeam u32 bursts, regval;
3224fd1a07aSSteve Longerbeam int i;
3234fd1a07aSSteve Longerbeam
3244fd1a07aSSteve Longerbeam switch (num_bursts) {
3254fd1a07aSSteve Longerbeam case 0:
3264fd1a07aSSteve Longerbeam case 1:
3274fd1a07aSSteve Longerbeam bursts = 0x00; /* locking disabled */
3284fd1a07aSSteve Longerbeam break;
3294fd1a07aSSteve Longerbeam case 2:
3304fd1a07aSSteve Longerbeam bursts = 0x01;
3314fd1a07aSSteve Longerbeam break;
3324fd1a07aSSteve Longerbeam case 4:
3334fd1a07aSSteve Longerbeam bursts = 0x02;
3344fd1a07aSSteve Longerbeam break;
3354fd1a07aSSteve Longerbeam case 8:
3364fd1a07aSSteve Longerbeam bursts = 0x03;
3374fd1a07aSSteve Longerbeam break;
3384fd1a07aSSteve Longerbeam default:
3394fd1a07aSSteve Longerbeam return -EINVAL;
3404fd1a07aSSteve Longerbeam }
3414fd1a07aSSteve Longerbeam
342cda77556SPhilipp Zabel /*
343cda77556SPhilipp Zabel * IPUv3EX / i.MX51 has a different register layout, and on IPUv3M /
344cda77556SPhilipp Zabel * i.MX53 channel arbitration locking doesn't seem to work properly.
345cda77556SPhilipp Zabel * Allow enabling the lock feature on IPUv3H / i.MX6 only.
346cda77556SPhilipp Zabel */
347cda77556SPhilipp Zabel if (bursts && ipu->ipu_type != IPUV3H)
348cda77556SPhilipp Zabel return -EINVAL;
349cda77556SPhilipp Zabel
3504fd1a07aSSteve Longerbeam for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) {
3514fd1a07aSSteve Longerbeam if (channel->num == idmac_lock_en_info[i].chnum)
3524fd1a07aSSteve Longerbeam break;
3534fd1a07aSSteve Longerbeam }
3544fd1a07aSSteve Longerbeam if (i >= ARRAY_SIZE(idmac_lock_en_info))
3554fd1a07aSSteve Longerbeam return -EINVAL;
3564fd1a07aSSteve Longerbeam
3574fd1a07aSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags);
3584fd1a07aSSteve Longerbeam
3594fd1a07aSSteve Longerbeam regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg);
3604fd1a07aSSteve Longerbeam regval &= ~(0x03 << idmac_lock_en_info[i].shift);
3614fd1a07aSSteve Longerbeam regval |= (bursts << idmac_lock_en_info[i].shift);
3624fd1a07aSSteve Longerbeam ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg);
3634fd1a07aSSteve Longerbeam
3644fd1a07aSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags);
3654fd1a07aSSteve Longerbeam
3664fd1a07aSSteve Longerbeam return 0;
3674fd1a07aSSteve Longerbeam }
3684fd1a07aSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable);
3694fd1a07aSSteve Longerbeam
ipu_module_enable(struct ipu_soc * ipu,u32 mask)37039b9004dSPhilipp Zabel int ipu_module_enable(struct ipu_soc *ipu, u32 mask)
37139b9004dSPhilipp Zabel {
37239b9004dSPhilipp Zabel unsigned long lock_flags;
37339b9004dSPhilipp Zabel u32 val;
37439b9004dSPhilipp Zabel
37539b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, lock_flags);
37639b9004dSPhilipp Zabel
37739b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_DISP_GEN);
37839b9004dSPhilipp Zabel
37939b9004dSPhilipp Zabel if (mask & IPU_CONF_DI0_EN)
38039b9004dSPhilipp Zabel val |= IPU_DI0_COUNTER_RELEASE;
38139b9004dSPhilipp Zabel if (mask & IPU_CONF_DI1_EN)
38239b9004dSPhilipp Zabel val |= IPU_DI1_COUNTER_RELEASE;
38339b9004dSPhilipp Zabel
38439b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_DISP_GEN);
38539b9004dSPhilipp Zabel
38639b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CONF);
38739b9004dSPhilipp Zabel val |= mask;
38839b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CONF);
38939b9004dSPhilipp Zabel
39039b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, lock_flags);
39139b9004dSPhilipp Zabel
39239b9004dSPhilipp Zabel return 0;
39339b9004dSPhilipp Zabel }
39439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_enable);
39539b9004dSPhilipp Zabel
ipu_module_disable(struct ipu_soc * ipu,u32 mask)39639b9004dSPhilipp Zabel int ipu_module_disable(struct ipu_soc *ipu, u32 mask)
39739b9004dSPhilipp Zabel {
39839b9004dSPhilipp Zabel unsigned long lock_flags;
39939b9004dSPhilipp Zabel u32 val;
40039b9004dSPhilipp Zabel
40139b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, lock_flags);
40239b9004dSPhilipp Zabel
40339b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CONF);
40439b9004dSPhilipp Zabel val &= ~mask;
40539b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CONF);
40639b9004dSPhilipp Zabel
40739b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_DISP_GEN);
40839b9004dSPhilipp Zabel
40939b9004dSPhilipp Zabel if (mask & IPU_CONF_DI0_EN)
41039b9004dSPhilipp Zabel val &= ~IPU_DI0_COUNTER_RELEASE;
41139b9004dSPhilipp Zabel if (mask & IPU_CONF_DI1_EN)
41239b9004dSPhilipp Zabel val &= ~IPU_DI1_COUNTER_RELEASE;
41339b9004dSPhilipp Zabel
41439b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_DISP_GEN);
41539b9004dSPhilipp Zabel
41639b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, lock_flags);
41739b9004dSPhilipp Zabel
41839b9004dSPhilipp Zabel return 0;
41939b9004dSPhilipp Zabel }
42039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_disable);
42139b9004dSPhilipp Zabel
ipu_idmac_get_current_buffer(struct ipuv3_channel * channel)422e9046097SPhilipp Zabel int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel)
423e9046097SPhilipp Zabel {
424e9046097SPhilipp Zabel struct ipu_soc *ipu = channel->ipu;
425e9046097SPhilipp Zabel unsigned int chno = channel->num;
426e9046097SPhilipp Zabel
427e9046097SPhilipp Zabel return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0;
428e9046097SPhilipp Zabel }
429e9046097SPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer);
430e9046097SPhilipp Zabel
ipu_idmac_buffer_is_ready(struct ipuv3_channel * channel,u32 buf_num)431aa52f578SSteve Longerbeam bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num)
432aa52f578SSteve Longerbeam {
433aa52f578SSteve Longerbeam struct ipu_soc *ipu = channel->ipu;
434aa52f578SSteve Longerbeam unsigned long flags;
435aa52f578SSteve Longerbeam u32 reg = 0;
436aa52f578SSteve Longerbeam
437aa52f578SSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags);
438aa52f578SSteve Longerbeam switch (buf_num) {
439aa52f578SSteve Longerbeam case 0:
440aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num));
441aa52f578SSteve Longerbeam break;
442aa52f578SSteve Longerbeam case 1:
443aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num));
444aa52f578SSteve Longerbeam break;
445aa52f578SSteve Longerbeam case 2:
446aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num));
447aa52f578SSteve Longerbeam break;
448aa52f578SSteve Longerbeam }
449aa52f578SSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags);
450aa52f578SSteve Longerbeam
451aa52f578SSteve Longerbeam return ((reg & idma_mask(channel->num)) != 0);
452aa52f578SSteve Longerbeam }
453aa52f578SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready);
454aa52f578SSteve Longerbeam
ipu_idmac_select_buffer(struct ipuv3_channel * channel,u32 buf_num)45539b9004dSPhilipp Zabel void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num)
45639b9004dSPhilipp Zabel {
45739b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu;
45839b9004dSPhilipp Zabel unsigned int chno = channel->num;
45939b9004dSPhilipp Zabel unsigned long flags;
46039b9004dSPhilipp Zabel
46139b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags);
46239b9004dSPhilipp Zabel
46339b9004dSPhilipp Zabel /* Mark buffer as ready. */
46439b9004dSPhilipp Zabel if (buf_num == 0)
46539b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
46639b9004dSPhilipp Zabel else
46739b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
46839b9004dSPhilipp Zabel
46939b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags);
47039b9004dSPhilipp Zabel }
47139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer);
47239b9004dSPhilipp Zabel
ipu_idmac_clear_buffer(struct ipuv3_channel * channel,u32 buf_num)473bce6f087SSteve Longerbeam void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num)
474bce6f087SSteve Longerbeam {
475bce6f087SSteve Longerbeam struct ipu_soc *ipu = channel->ipu;
476bce6f087SSteve Longerbeam unsigned int chno = channel->num;
477bce6f087SSteve Longerbeam unsigned long flags;
478bce6f087SSteve Longerbeam
479bce6f087SSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags);
480bce6f087SSteve Longerbeam
481bce6f087SSteve Longerbeam ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */
482bce6f087SSteve Longerbeam switch (buf_num) {
483bce6f087SSteve Longerbeam case 0:
484bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
485bce6f087SSteve Longerbeam break;
486bce6f087SSteve Longerbeam case 1:
487bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
488bce6f087SSteve Longerbeam break;
489bce6f087SSteve Longerbeam case 2:
490bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno));
491bce6f087SSteve Longerbeam break;
492bce6f087SSteve Longerbeam default:
493bce6f087SSteve Longerbeam break;
494bce6f087SSteve Longerbeam }
495bce6f087SSteve Longerbeam ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
496bce6f087SSteve Longerbeam
497bce6f087SSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags);
498bce6f087SSteve Longerbeam }
499bce6f087SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer);
500bce6f087SSteve Longerbeam
ipu_idmac_enable_channel(struct ipuv3_channel * channel)50139b9004dSPhilipp Zabel int ipu_idmac_enable_channel(struct ipuv3_channel *channel)
50239b9004dSPhilipp Zabel {
50339b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu;
50439b9004dSPhilipp Zabel u32 val;
50539b9004dSPhilipp Zabel unsigned long flags;
50639b9004dSPhilipp Zabel
50739b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags);
50839b9004dSPhilipp Zabel
50939b9004dSPhilipp Zabel val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
51039b9004dSPhilipp Zabel val |= idma_mask(channel->num);
51139b9004dSPhilipp Zabel ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
51239b9004dSPhilipp Zabel
51339b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags);
51439b9004dSPhilipp Zabel
51539b9004dSPhilipp Zabel return 0;
51639b9004dSPhilipp Zabel }
51739b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
51839b9004dSPhilipp Zabel
ipu_idmac_channel_busy(struct ipu_soc * ipu,unsigned int chno)519682b7c1cSLinus Torvalds bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno)
520682b7c1cSLinus Torvalds {
521682b7c1cSLinus Torvalds return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno));
522682b7c1cSLinus Torvalds }
523682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy);
524682b7c1cSLinus Torvalds
ipu_idmac_wait_busy(struct ipuv3_channel * channel,int ms)52539b9004dSPhilipp Zabel int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms)
52639b9004dSPhilipp Zabel {
52739b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu;
52839b9004dSPhilipp Zabel unsigned long timeout;
52939b9004dSPhilipp Zabel
53039b9004dSPhilipp Zabel timeout = jiffies + msecs_to_jiffies(ms);
53139b9004dSPhilipp Zabel while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) &
53239b9004dSPhilipp Zabel idma_mask(channel->num)) {
53339b9004dSPhilipp Zabel if (time_after(jiffies, timeout))
53439b9004dSPhilipp Zabel return -ETIMEDOUT;
53539b9004dSPhilipp Zabel cpu_relax();
53639b9004dSPhilipp Zabel }
53739b9004dSPhilipp Zabel
53839b9004dSPhilipp Zabel return 0;
53939b9004dSPhilipp Zabel }
54039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy);
54139b9004dSPhilipp Zabel
ipu_idmac_disable_channel(struct ipuv3_channel * channel)54239b9004dSPhilipp Zabel int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
54339b9004dSPhilipp Zabel {
54439b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu;
54539b9004dSPhilipp Zabel u32 val;
54639b9004dSPhilipp Zabel unsigned long flags;
54739b9004dSPhilipp Zabel
54839b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags);
54939b9004dSPhilipp Zabel
55039b9004dSPhilipp Zabel /* Disable DMA channel(s) */
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
555e7268c69SSteve Longerbeam __ipu_idmac_reset_current_buffer(channel);
556e7268c69SSteve Longerbeam
55739b9004dSPhilipp Zabel /* Set channel buffers NOT to be ready */
55839b9004dSPhilipp Zabel ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */
55939b9004dSPhilipp Zabel
56039b9004dSPhilipp Zabel if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) &
56139b9004dSPhilipp Zabel idma_mask(channel->num)) {
56239b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(channel->num),
56339b9004dSPhilipp Zabel IPU_CHA_BUF0_RDY(channel->num));
56439b9004dSPhilipp Zabel }
56539b9004dSPhilipp Zabel
56639b9004dSPhilipp Zabel if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) &
56739b9004dSPhilipp Zabel idma_mask(channel->num)) {
56839b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(channel->num),
56939b9004dSPhilipp Zabel IPU_CHA_BUF1_RDY(channel->num));
57039b9004dSPhilipp Zabel }
57139b9004dSPhilipp Zabel
57239b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
57339b9004dSPhilipp Zabel
57439b9004dSPhilipp Zabel /* Reset the double buffer */
57539b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
57639b9004dSPhilipp Zabel val &= ~idma_mask(channel->num);
57739b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num));
57839b9004dSPhilipp Zabel
57939b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags);
58039b9004dSPhilipp Zabel
58139b9004dSPhilipp Zabel return 0;
58239b9004dSPhilipp Zabel }
58339b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel);
58439b9004dSPhilipp Zabel
5852bcf577eSSteve Longerbeam /*
5862bcf577eSSteve Longerbeam * The imx6 rev. D TRM says that enabling the WM feature will increase
5872bcf577eSSteve Longerbeam * a channel's priority. Refer to Table 36-8 Calculated priority value.
5882bcf577eSSteve Longerbeam * The sub-module that is the sink or source for the channel must enable
5892bcf577eSSteve Longerbeam * watermark signal for this to take effect (SMFC_WM for instance).
5902bcf577eSSteve Longerbeam */
ipu_idmac_enable_watermark(struct ipuv3_channel * channel,bool enable)5912bcf577eSSteve Longerbeam void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable)
5922bcf577eSSteve Longerbeam {
5932bcf577eSSteve Longerbeam struct ipu_soc *ipu = channel->ipu;
5942bcf577eSSteve Longerbeam unsigned long flags;
5952bcf577eSSteve Longerbeam u32 val;
5962bcf577eSSteve Longerbeam
5972bcf577eSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags);
5982bcf577eSSteve Longerbeam
5992bcf577eSSteve Longerbeam val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num));
6002bcf577eSSteve Longerbeam if (enable)
6012bcf577eSSteve Longerbeam val |= 1 << (channel->num % 32);
6022bcf577eSSteve Longerbeam else
6032bcf577eSSteve Longerbeam val &= ~(1 << (channel->num % 32));
6042bcf577eSSteve Longerbeam ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num));
6052bcf577eSSteve Longerbeam
6062bcf577eSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags);
6072bcf577eSSteve Longerbeam }
6082bcf577eSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark);
6092bcf577eSSteve Longerbeam
ipu_memory_reset(struct ipu_soc * ipu)61039b9004dSPhilipp Zabel static int ipu_memory_reset(struct ipu_soc *ipu)
61139b9004dSPhilipp Zabel {
61239b9004dSPhilipp Zabel unsigned long timeout;
61339b9004dSPhilipp Zabel
61439b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST);
61539b9004dSPhilipp Zabel
61639b9004dSPhilipp Zabel timeout = jiffies + msecs_to_jiffies(1000);
61739b9004dSPhilipp Zabel while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) {
61839b9004dSPhilipp Zabel if (time_after(jiffies, timeout))
61939b9004dSPhilipp Zabel return -ETIME;
62039b9004dSPhilipp Zabel cpu_relax();
62139b9004dSPhilipp Zabel }
62239b9004dSPhilipp Zabel
62339b9004dSPhilipp Zabel return 0;
62439b9004dSPhilipp Zabel }
62539b9004dSPhilipp Zabel
626ba07975fSSteve Longerbeam /*
627ba07975fSSteve Longerbeam * Set the source mux for the given CSI. Selects either parallel or
628ba07975fSSteve Longerbeam * MIPI CSI2 sources.
629ba07975fSSteve Longerbeam */
ipu_set_csi_src_mux(struct ipu_soc * ipu,int csi_id,bool mipi_csi2)630ba07975fSSteve Longerbeam void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2)
631ba07975fSSteve Longerbeam {
632ba07975fSSteve Longerbeam unsigned long flags;
633ba07975fSSteve Longerbeam u32 val, mask;
634ba07975fSSteve Longerbeam
635ba07975fSSteve Longerbeam mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE :
636ba07975fSSteve Longerbeam IPU_CONF_CSI0_DATA_SOURCE;
637ba07975fSSteve Longerbeam
638ba07975fSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags);
639ba07975fSSteve Longerbeam
640ba07975fSSteve Longerbeam val = ipu_cm_read(ipu, IPU_CONF);
641ba07975fSSteve Longerbeam if (mipi_csi2)
642ba07975fSSteve Longerbeam val |= mask;
643ba07975fSSteve Longerbeam else
644ba07975fSSteve Longerbeam val &= ~mask;
645ba07975fSSteve Longerbeam ipu_cm_write(ipu, val, IPU_CONF);
646ba07975fSSteve Longerbeam
647ba07975fSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags);
648ba07975fSSteve Longerbeam }
649ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux);
650ba07975fSSteve Longerbeam
651ba07975fSSteve Longerbeam /*
652ba07975fSSteve Longerbeam * Set the source mux for the IC. Selects either CSI[01] or the VDI.
653ba07975fSSteve Longerbeam */
ipu_set_ic_src_mux(struct ipu_soc * ipu,int csi_id,bool vdi)654ba07975fSSteve Longerbeam void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
655ba07975fSSteve Longerbeam {
656ba07975fSSteve Longerbeam unsigned long flags;
657ba07975fSSteve Longerbeam u32 val;
658ba07975fSSteve Longerbeam
659ba07975fSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags);
660ba07975fSSteve Longerbeam
661ba07975fSSteve Longerbeam val = ipu_cm_read(ipu, IPU_CONF);
662b7dfee24SMarek Vasut if (vdi)
663ba07975fSSteve Longerbeam val |= IPU_CONF_IC_INPUT;
664b7dfee24SMarek Vasut else
665ba07975fSSteve Longerbeam val &= ~IPU_CONF_IC_INPUT;
666b7dfee24SMarek Vasut
667ba07975fSSteve Longerbeam if (csi_id == 1)
668ba07975fSSteve Longerbeam val |= IPU_CONF_CSI_SEL;
669ba07975fSSteve Longerbeam else
670ba07975fSSteve Longerbeam val &= ~IPU_CONF_CSI_SEL;
671b7dfee24SMarek Vasut
672ba07975fSSteve Longerbeam ipu_cm_write(ipu, val, IPU_CONF);
673ba07975fSSteve Longerbeam
674ba07975fSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags);
675ba07975fSSteve Longerbeam }
676ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
677ba07975fSSteve Longerbeam
678ac4708faSSteve Longerbeam
679ac4708faSSteve Longerbeam /* Frame Synchronization Unit Channel Linking */
680ac4708faSSteve Longerbeam
681ac4708faSSteve Longerbeam struct fsu_link_reg_info {
682ac4708faSSteve Longerbeam int chno;
683ac4708faSSteve Longerbeam u32 reg;
684ac4708faSSteve Longerbeam u32 mask;
685ac4708faSSteve Longerbeam u32 val;
686ac4708faSSteve Longerbeam };
687ac4708faSSteve Longerbeam
688ac4708faSSteve Longerbeam struct fsu_link_info {
689ac4708faSSteve Longerbeam struct fsu_link_reg_info src;
690ac4708faSSteve Longerbeam struct fsu_link_reg_info sink;
691ac4708faSSteve Longerbeam };
692ac4708faSSteve Longerbeam
693ac4708faSSteve Longerbeam static const struct fsu_link_info fsu_link_info[] = {
694ac4708faSSteve Longerbeam {
695ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2,
696ac4708faSSteve Longerbeam FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC },
697ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1,
698ac4708faSSteve Longerbeam FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC },
699ac4708faSSteve Longerbeam }, {
700ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2,
701ac4708faSSteve Longerbeam FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF },
702ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1,
703ac4708faSSteve Longerbeam FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF },
704ac4708faSSteve Longerbeam }, {
705ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2,
706ac4708faSSteve Longerbeam FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP },
707ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1,
708ac4708faSSteve Longerbeam FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP },
709ac4708faSSteve Longerbeam }, {
710ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_CSI_DIRECT, 0 },
711ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1,
712ac4708faSSteve Longerbeam FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT },
713ac4708faSSteve Longerbeam },
714ac4708faSSteve Longerbeam };
715ac4708faSSteve Longerbeam
find_fsu_link_info(int src,int sink)716ac4708faSSteve Longerbeam static const struct fsu_link_info *find_fsu_link_info(int src, int sink)
717ac4708faSSteve Longerbeam {
718ac4708faSSteve Longerbeam int i;
719ac4708faSSteve Longerbeam
720ac4708faSSteve Longerbeam for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) {
721ac4708faSSteve Longerbeam if (src == fsu_link_info[i].src.chno &&
722ac4708faSSteve Longerbeam sink == fsu_link_info[i].sink.chno)
723ac4708faSSteve Longerbeam return &fsu_link_info[i];
724ac4708faSSteve Longerbeam }
725ac4708faSSteve Longerbeam
726ac4708faSSteve Longerbeam return NULL;
727ac4708faSSteve Longerbeam }
728ac4708faSSteve Longerbeam
729ac4708faSSteve Longerbeam /*
730ac4708faSSteve Longerbeam * Links a source channel to a sink channel in the FSU.
731ac4708faSSteve Longerbeam */
ipu_fsu_link(struct ipu_soc * ipu,int src_ch,int sink_ch)732ac4708faSSteve Longerbeam int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch)
733ac4708faSSteve Longerbeam {
734ac4708faSSteve Longerbeam const struct fsu_link_info *link;
735ac4708faSSteve Longerbeam u32 src_reg, sink_reg;
736ac4708faSSteve Longerbeam unsigned long flags;
737ac4708faSSteve Longerbeam
738ac4708faSSteve Longerbeam link = find_fsu_link_info(src_ch, sink_ch);
739ac4708faSSteve Longerbeam if (!link)
740ac4708faSSteve Longerbeam return -EINVAL;
741ac4708faSSteve Longerbeam
742ac4708faSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags);
743ac4708faSSteve Longerbeam
744ac4708faSSteve Longerbeam if (link->src.mask) {
745ac4708faSSteve Longerbeam src_reg = ipu_cm_read(ipu, link->src.reg);
746ac4708faSSteve Longerbeam src_reg &= ~link->src.mask;
747ac4708faSSteve Longerbeam src_reg |= link->src.val;
748ac4708faSSteve Longerbeam ipu_cm_write(ipu, src_reg, link->src.reg);
749ac4708faSSteve Longerbeam }
750ac4708faSSteve Longerbeam
751ac4708faSSteve Longerbeam if (link->sink.mask) {
752ac4708faSSteve Longerbeam sink_reg = ipu_cm_read(ipu, link->sink.reg);
753ac4708faSSteve Longerbeam sink_reg &= ~link->sink.mask;
754ac4708faSSteve Longerbeam sink_reg |= link->sink.val;
755ac4708faSSteve Longerbeam ipu_cm_write(ipu, sink_reg, link->sink.reg);
756ac4708faSSteve Longerbeam }
757ac4708faSSteve Longerbeam
758ac4708faSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags);
759ac4708faSSteve Longerbeam return 0;
760ac4708faSSteve Longerbeam }
761ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_link);
762ac4708faSSteve Longerbeam
763ac4708faSSteve Longerbeam /*
764ac4708faSSteve Longerbeam * Unlinks source and sink channels in the FSU.
765ac4708faSSteve Longerbeam */
ipu_fsu_unlink(struct ipu_soc * ipu,int src_ch,int sink_ch)766ac4708faSSteve Longerbeam int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch)
767ac4708faSSteve Longerbeam {
768ac4708faSSteve Longerbeam const struct fsu_link_info *link;
769ac4708faSSteve Longerbeam u32 src_reg, sink_reg;
770ac4708faSSteve Longerbeam unsigned long flags;
771ac4708faSSteve Longerbeam
772ac4708faSSteve Longerbeam link = find_fsu_link_info(src_ch, sink_ch);
773ac4708faSSteve Longerbeam if (!link)
774ac4708faSSteve Longerbeam return -EINVAL;
775ac4708faSSteve Longerbeam
776ac4708faSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags);
777ac4708faSSteve Longerbeam
778ac4708faSSteve Longerbeam if (link->src.mask) {
779ac4708faSSteve Longerbeam src_reg = ipu_cm_read(ipu, link->src.reg);
780ac4708faSSteve Longerbeam src_reg &= ~link->src.mask;
781ac4708faSSteve Longerbeam ipu_cm_write(ipu, src_reg, link->src.reg);
782ac4708faSSteve Longerbeam }
783ac4708faSSteve Longerbeam
784ac4708faSSteve Longerbeam if (link->sink.mask) {
785ac4708faSSteve Longerbeam sink_reg = ipu_cm_read(ipu, link->sink.reg);
786ac4708faSSteve Longerbeam sink_reg &= ~link->sink.mask;
787ac4708faSSteve Longerbeam ipu_cm_write(ipu, sink_reg, link->sink.reg);
788ac4708faSSteve Longerbeam }
789ac4708faSSteve Longerbeam
790ac4708faSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags);
791ac4708faSSteve Longerbeam return 0;
792ac4708faSSteve Longerbeam }
793ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_unlink);
794ac4708faSSteve Longerbeam
795ac4708faSSteve Longerbeam /* Link IDMAC channels in the FSU */
ipu_idmac_link(struct ipuv3_channel * src,struct ipuv3_channel * sink)796ac4708faSSteve Longerbeam int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)
797ac4708faSSteve Longerbeam {
798ac4708faSSteve Longerbeam return ipu_fsu_link(src->ipu, src->num, sink->num);
799ac4708faSSteve Longerbeam }
800ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_link);
801ac4708faSSteve Longerbeam
802ac4708faSSteve Longerbeam /* Unlink IDMAC channels in the FSU */
ipu_idmac_unlink(struct ipuv3_channel * src,struct ipuv3_channel * sink)803ac4708faSSteve Longerbeam int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink)
804ac4708faSSteve Longerbeam {
805ac4708faSSteve Longerbeam return ipu_fsu_unlink(src->ipu, src->num, sink->num);
806ac4708faSSteve Longerbeam }
807ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_unlink);
808ac4708faSSteve Longerbeam
80939b9004dSPhilipp Zabel struct ipu_devtype {
81039b9004dSPhilipp Zabel const char *name;
81139b9004dSPhilipp Zabel unsigned long cm_ofs;
81239b9004dSPhilipp Zabel unsigned long cpmem_ofs;
81339b9004dSPhilipp Zabel unsigned long srm_ofs;
81439b9004dSPhilipp Zabel unsigned long tpm_ofs;
8152ffd48f2SSteve Longerbeam unsigned long csi0_ofs;
8162ffd48f2SSteve Longerbeam unsigned long csi1_ofs;
8171aa8ea0dSSteve Longerbeam unsigned long ic_ofs;
81839b9004dSPhilipp Zabel unsigned long disp0_ofs;
81939b9004dSPhilipp Zabel unsigned long disp1_ofs;
82039b9004dSPhilipp Zabel unsigned long dc_tmpl_ofs;
82139b9004dSPhilipp Zabel unsigned long vdi_ofs;
82239b9004dSPhilipp Zabel enum ipuv3_type type;
82339b9004dSPhilipp Zabel };
82439b9004dSPhilipp Zabel
82539b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx51 = {
82639b9004dSPhilipp Zabel .name = "IPUv3EX",
82739b9004dSPhilipp Zabel .cm_ofs = 0x1e000000,
82839b9004dSPhilipp Zabel .cpmem_ofs = 0x1f000000,
82939b9004dSPhilipp Zabel .srm_ofs = 0x1f040000,
83039b9004dSPhilipp Zabel .tpm_ofs = 0x1f060000,
8312c0408ddSAlexander Shiyan .csi0_ofs = 0x1e030000,
8322c0408ddSAlexander Shiyan .csi1_ofs = 0x1e038000,
833a49e7c0dSPhilipp Zabel .ic_ofs = 0x1e020000,
83439b9004dSPhilipp Zabel .disp0_ofs = 0x1e040000,
83539b9004dSPhilipp Zabel .disp1_ofs = 0x1e048000,
83639b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x1f080000,
83739b9004dSPhilipp Zabel .vdi_ofs = 0x1e068000,
83839b9004dSPhilipp Zabel .type = IPUV3EX,
83939b9004dSPhilipp Zabel };
84039b9004dSPhilipp Zabel
84139b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx53 = {
84239b9004dSPhilipp Zabel .name = "IPUv3M",
84339b9004dSPhilipp Zabel .cm_ofs = 0x06000000,
84439b9004dSPhilipp Zabel .cpmem_ofs = 0x07000000,
84539b9004dSPhilipp Zabel .srm_ofs = 0x07040000,
84639b9004dSPhilipp Zabel .tpm_ofs = 0x07060000,
847bb867d21SSteve Longerbeam .csi0_ofs = 0x06030000,
848bb867d21SSteve Longerbeam .csi1_ofs = 0x06038000,
849a49e7c0dSPhilipp Zabel .ic_ofs = 0x06020000,
85039b9004dSPhilipp Zabel .disp0_ofs = 0x06040000,
85139b9004dSPhilipp Zabel .disp1_ofs = 0x06048000,
85239b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x07080000,
85339b9004dSPhilipp Zabel .vdi_ofs = 0x06068000,
85439b9004dSPhilipp Zabel .type = IPUV3M,
85539b9004dSPhilipp Zabel };
85639b9004dSPhilipp Zabel
85739b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx6q = {
85839b9004dSPhilipp Zabel .name = "IPUv3H",
85939b9004dSPhilipp Zabel .cm_ofs = 0x00200000,
86039b9004dSPhilipp Zabel .cpmem_ofs = 0x00300000,
86139b9004dSPhilipp Zabel .srm_ofs = 0x00340000,
86239b9004dSPhilipp Zabel .tpm_ofs = 0x00360000,
8632ffd48f2SSteve Longerbeam .csi0_ofs = 0x00230000,
8642ffd48f2SSteve Longerbeam .csi1_ofs = 0x00238000,
8651aa8ea0dSSteve Longerbeam .ic_ofs = 0x00220000,
86639b9004dSPhilipp Zabel .disp0_ofs = 0x00240000,
86739b9004dSPhilipp Zabel .disp1_ofs = 0x00248000,
86839b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x00380000,
86939b9004dSPhilipp Zabel .vdi_ofs = 0x00268000,
87039b9004dSPhilipp Zabel .type = IPUV3H,
87139b9004dSPhilipp Zabel };
87239b9004dSPhilipp Zabel
87339b9004dSPhilipp Zabel static const struct of_device_id imx_ipu_dt_ids[] = {
87439b9004dSPhilipp Zabel { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, },
87539b9004dSPhilipp Zabel { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, },
87639b9004dSPhilipp Zabel { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, },
87792681fe7SLucas Stach { .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6q, },
87839b9004dSPhilipp Zabel { /* sentinel */ }
87939b9004dSPhilipp Zabel };
88039b9004dSPhilipp Zabel MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids);
88139b9004dSPhilipp Zabel
ipu_submodules_init(struct ipu_soc * ipu,struct platform_device * pdev,unsigned long ipu_base,struct clk * ipu_clk)88239b9004dSPhilipp Zabel static int ipu_submodules_init(struct ipu_soc *ipu,
88339b9004dSPhilipp Zabel struct platform_device *pdev, unsigned long ipu_base,
88439b9004dSPhilipp Zabel struct clk *ipu_clk)
88539b9004dSPhilipp Zabel {
88639b9004dSPhilipp Zabel char *unit;
88739b9004dSPhilipp Zabel int ret;
88839b9004dSPhilipp Zabel struct device *dev = &pdev->dev;
88939b9004dSPhilipp Zabel const struct ipu_devtype *devtype = ipu->devtype;
89039b9004dSPhilipp Zabel
8917d2691daSSteve Longerbeam ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs);
8927d2691daSSteve Longerbeam if (ret) {
8937d2691daSSteve Longerbeam unit = "cpmem";
8947d2691daSSteve Longerbeam goto err_cpmem;
8957d2691daSSteve Longerbeam }
8967d2691daSSteve Longerbeam
8972ffd48f2SSteve Longerbeam ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs,
8982ffd48f2SSteve Longerbeam IPU_CONF_CSI0_EN, ipu_clk);
8992ffd48f2SSteve Longerbeam if (ret) {
9002ffd48f2SSteve Longerbeam unit = "csi0";
9012ffd48f2SSteve Longerbeam goto err_csi_0;
9022ffd48f2SSteve Longerbeam }
9032ffd48f2SSteve Longerbeam
9042ffd48f2SSteve Longerbeam ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs,
9052ffd48f2SSteve Longerbeam IPU_CONF_CSI1_EN, ipu_clk);
9062ffd48f2SSteve Longerbeam if (ret) {
9072ffd48f2SSteve Longerbeam unit = "csi1";
9082ffd48f2SSteve Longerbeam goto err_csi_1;
9092ffd48f2SSteve Longerbeam }
9102ffd48f2SSteve Longerbeam
9111aa8ea0dSSteve Longerbeam ret = ipu_ic_init(ipu, dev,
9121aa8ea0dSSteve Longerbeam ipu_base + devtype->ic_ofs,
9131aa8ea0dSSteve Longerbeam ipu_base + devtype->tpm_ofs);
9141aa8ea0dSSteve Longerbeam if (ret) {
9151aa8ea0dSSteve Longerbeam unit = "ic";
9161aa8ea0dSSteve Longerbeam goto err_ic;
9171aa8ea0dSSteve Longerbeam }
9181aa8ea0dSSteve Longerbeam
9192d2ead45SSteve Longerbeam ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs,
9202d2ead45SSteve Longerbeam IPU_CONF_VDI_EN | IPU_CONF_ISP_EN |
9212d2ead45SSteve Longerbeam IPU_CONF_IC_INPUT);
9222d2ead45SSteve Longerbeam if (ret) {
9232d2ead45SSteve Longerbeam unit = "vdi";
9242d2ead45SSteve Longerbeam goto err_vdi;
9252d2ead45SSteve Longerbeam }
9262d2ead45SSteve Longerbeam
927cd98e85aSSteve Longerbeam ret = ipu_image_convert_init(ipu, dev);
928cd98e85aSSteve Longerbeam if (ret) {
929cd98e85aSSteve Longerbeam unit = "image_convert";
930cd98e85aSSteve Longerbeam goto err_image_convert;
931cd98e85aSSteve Longerbeam }
932cd98e85aSSteve Longerbeam
93339b9004dSPhilipp Zabel ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
93439b9004dSPhilipp Zabel IPU_CONF_DI0_EN, ipu_clk);
93539b9004dSPhilipp Zabel if (ret) {
93639b9004dSPhilipp Zabel unit = "di0";
93739b9004dSPhilipp Zabel goto err_di_0;
93839b9004dSPhilipp Zabel }
93939b9004dSPhilipp Zabel
94039b9004dSPhilipp Zabel ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs,
94139b9004dSPhilipp Zabel IPU_CONF_DI1_EN, ipu_clk);
94239b9004dSPhilipp Zabel if (ret) {
94339b9004dSPhilipp Zabel unit = "di1";
94439b9004dSPhilipp Zabel goto err_di_1;
94539b9004dSPhilipp Zabel }
94639b9004dSPhilipp Zabel
94739b9004dSPhilipp Zabel ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs +
94839b9004dSPhilipp Zabel IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs);
94939b9004dSPhilipp Zabel if (ret) {
95039b9004dSPhilipp Zabel unit = "dc_template";
95139b9004dSPhilipp Zabel goto err_dc;
95239b9004dSPhilipp Zabel }
95339b9004dSPhilipp Zabel
95439b9004dSPhilipp Zabel ret = ipu_dmfc_init(ipu, dev, ipu_base +
95539b9004dSPhilipp Zabel devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk);
95639b9004dSPhilipp Zabel if (ret) {
95739b9004dSPhilipp Zabel unit = "dmfc";
95839b9004dSPhilipp Zabel goto err_dmfc;
95939b9004dSPhilipp Zabel }
96039b9004dSPhilipp Zabel
96139b9004dSPhilipp Zabel ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs);
96239b9004dSPhilipp Zabel if (ret) {
96339b9004dSPhilipp Zabel unit = "dp";
96439b9004dSPhilipp Zabel goto err_dp;
96539b9004dSPhilipp Zabel }
96639b9004dSPhilipp Zabel
96735de925fSPhilipp Zabel ret = ipu_smfc_init(ipu, dev, ipu_base +
96835de925fSPhilipp Zabel devtype->cm_ofs + IPU_CM_SMFC_REG_OFS);
96935de925fSPhilipp Zabel if (ret) {
97035de925fSPhilipp Zabel unit = "smfc";
97135de925fSPhilipp Zabel goto err_smfc;
97235de925fSPhilipp Zabel }
97335de925fSPhilipp Zabel
97439b9004dSPhilipp Zabel return 0;
97539b9004dSPhilipp Zabel
97635de925fSPhilipp Zabel err_smfc:
97735de925fSPhilipp Zabel ipu_dp_exit(ipu);
97839b9004dSPhilipp Zabel err_dp:
97939b9004dSPhilipp Zabel ipu_dmfc_exit(ipu);
98039b9004dSPhilipp Zabel err_dmfc:
98139b9004dSPhilipp Zabel ipu_dc_exit(ipu);
98239b9004dSPhilipp Zabel err_dc:
98339b9004dSPhilipp Zabel ipu_di_exit(ipu, 1);
98439b9004dSPhilipp Zabel err_di_1:
98539b9004dSPhilipp Zabel ipu_di_exit(ipu, 0);
98639b9004dSPhilipp Zabel err_di_0:
987cd98e85aSSteve Longerbeam ipu_image_convert_exit(ipu);
988cd98e85aSSteve Longerbeam err_image_convert:
9892d2ead45SSteve Longerbeam ipu_vdi_exit(ipu);
9902d2ead45SSteve Longerbeam err_vdi:
9911aa8ea0dSSteve Longerbeam ipu_ic_exit(ipu);
9921aa8ea0dSSteve Longerbeam err_ic:
9932ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 1);
9942ffd48f2SSteve Longerbeam err_csi_1:
9952ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 0);
9962ffd48f2SSteve Longerbeam err_csi_0:
9977d2691daSSteve Longerbeam ipu_cpmem_exit(ipu);
9987d2691daSSteve Longerbeam err_cpmem:
99939b9004dSPhilipp Zabel dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret);
100039b9004dSPhilipp Zabel return ret;
100139b9004dSPhilipp Zabel }
100239b9004dSPhilipp Zabel
ipu_irq_handle(struct ipu_soc * ipu,const int * regs,int num_regs)100339b9004dSPhilipp Zabel static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs)
100439b9004dSPhilipp Zabel {
100539b9004dSPhilipp Zabel unsigned long status;
100666c6594bSMarc Zyngier int i, bit;
100739b9004dSPhilipp Zabel
100839b9004dSPhilipp Zabel for (i = 0; i < num_regs; i++) {
100939b9004dSPhilipp Zabel
101039b9004dSPhilipp Zabel status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i]));
101139b9004dSPhilipp Zabel status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i]));
101239b9004dSPhilipp Zabel
101366c6594bSMarc Zyngier for_each_set_bit(bit, &status, 32)
101466c6594bSMarc Zyngier generic_handle_domain_irq(ipu->domain,
1015682b7c1cSLinus Torvalds regs[i] * 32 + bit);
101639b9004dSPhilipp Zabel }
101739b9004dSPhilipp Zabel }
101839b9004dSPhilipp Zabel
ipu_irq_handler(struct irq_desc * desc)1019bd0b9ac4SThomas Gleixner static void ipu_irq_handler(struct irq_desc *desc)
102039b9004dSPhilipp Zabel {
102139b9004dSPhilipp Zabel struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
10224d9efdfcSJiang Liu struct irq_chip *chip = irq_desc_get_chip(desc);
1023ac66b834SColin Ian King static const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14};
102439b9004dSPhilipp Zabel
102539b9004dSPhilipp Zabel chained_irq_enter(chip, desc);
102639b9004dSPhilipp Zabel
102739b9004dSPhilipp Zabel ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
102839b9004dSPhilipp Zabel
102939b9004dSPhilipp Zabel chained_irq_exit(chip, desc);
103039b9004dSPhilipp Zabel }
103139b9004dSPhilipp Zabel
ipu_err_irq_handler(struct irq_desc * desc)1032bd0b9ac4SThomas Gleixner static void ipu_err_irq_handler(struct irq_desc *desc)
103339b9004dSPhilipp Zabel {
103439b9004dSPhilipp Zabel struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
10354d9efdfcSJiang Liu struct irq_chip *chip = irq_desc_get_chip(desc);
1036ac66b834SColin Ian King static const int int_reg[] = { 4, 5, 8, 9};
103739b9004dSPhilipp Zabel
103839b9004dSPhilipp Zabel chained_irq_enter(chip, desc);
103939b9004dSPhilipp Zabel
104039b9004dSPhilipp Zabel ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
104139b9004dSPhilipp Zabel
104239b9004dSPhilipp Zabel chained_irq_exit(chip, desc);
104339b9004dSPhilipp Zabel }
104439b9004dSPhilipp Zabel
ipu_map_irq(struct ipu_soc * ipu,int irq)1045682b7c1cSLinus Torvalds int ipu_map_irq(struct ipu_soc *ipu, int irq)
1046682b7c1cSLinus Torvalds {
1047682b7c1cSLinus Torvalds int virq;
1048682b7c1cSLinus Torvalds
1049682b7c1cSLinus Torvalds virq = irq_linear_revmap(ipu->domain, irq);
1050682b7c1cSLinus Torvalds if (!virq)
1051682b7c1cSLinus Torvalds virq = irq_create_mapping(ipu->domain, irq);
1052682b7c1cSLinus Torvalds
1053682b7c1cSLinus Torvalds return virq;
1054682b7c1cSLinus Torvalds }
1055682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_map_irq);
1056682b7c1cSLinus Torvalds
ipu_idmac_channel_irq(struct ipu_soc * ipu,struct ipuv3_channel * channel,enum ipu_channel_irq irq_type)105739b9004dSPhilipp Zabel int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
105839b9004dSPhilipp Zabel enum ipu_channel_irq irq_type)
105939b9004dSPhilipp Zabel {
1060682b7c1cSLinus Torvalds return ipu_map_irq(ipu, irq_type + channel->num);
106139b9004dSPhilipp Zabel }
106239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq);
106339b9004dSPhilipp Zabel
ipu_submodules_exit(struct ipu_soc * ipu)106439b9004dSPhilipp Zabel static void ipu_submodules_exit(struct ipu_soc *ipu)
106539b9004dSPhilipp Zabel {
106635de925fSPhilipp Zabel ipu_smfc_exit(ipu);
106739b9004dSPhilipp Zabel ipu_dp_exit(ipu);
106839b9004dSPhilipp Zabel ipu_dmfc_exit(ipu);
106939b9004dSPhilipp Zabel ipu_dc_exit(ipu);
107039b9004dSPhilipp Zabel ipu_di_exit(ipu, 1);
107139b9004dSPhilipp Zabel ipu_di_exit(ipu, 0);
1072cd98e85aSSteve Longerbeam ipu_image_convert_exit(ipu);
10732d2ead45SSteve Longerbeam ipu_vdi_exit(ipu);
10741aa8ea0dSSteve Longerbeam ipu_ic_exit(ipu);
10752ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 1);
10762ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 0);
10777d2691daSSteve Longerbeam ipu_cpmem_exit(ipu);
107839b9004dSPhilipp Zabel }
107939b9004dSPhilipp Zabel
platform_remove_devices_fn(struct device * dev,void * unused)108039b9004dSPhilipp Zabel static int platform_remove_devices_fn(struct device *dev, void *unused)
108139b9004dSPhilipp Zabel {
108239b9004dSPhilipp Zabel struct platform_device *pdev = to_platform_device(dev);
108339b9004dSPhilipp Zabel
108439b9004dSPhilipp Zabel platform_device_unregister(pdev);
108539b9004dSPhilipp Zabel
108639b9004dSPhilipp Zabel return 0;
108739b9004dSPhilipp Zabel }
108839b9004dSPhilipp Zabel
platform_device_unregister_children(struct platform_device * pdev)108939b9004dSPhilipp Zabel static void platform_device_unregister_children(struct platform_device *pdev)
109039b9004dSPhilipp Zabel {
109139b9004dSPhilipp Zabel device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn);
109239b9004dSPhilipp Zabel }
109339b9004dSPhilipp Zabel
109439b9004dSPhilipp Zabel struct ipu_platform_reg {
109539b9004dSPhilipp Zabel struct ipu_client_platformdata pdata;
109639b9004dSPhilipp Zabel const char *name;
109739b9004dSPhilipp Zabel };
109839b9004dSPhilipp Zabel
1099304e6be6SPhilipp Zabel /* These must be in the order of the corresponding device tree port nodes */
1100310944d1SPhilipp Zabel static struct ipu_platform_reg client_reg[] = {
110139b9004dSPhilipp Zabel {
110239b9004dSPhilipp Zabel .pdata = {
1103304e6be6SPhilipp Zabel .csi = 0,
1104304e6be6SPhilipp Zabel .dma[0] = IPUV3_CHANNEL_CSI0,
1105304e6be6SPhilipp Zabel .dma[1] = -EINVAL,
1106304e6be6SPhilipp Zabel },
110788287ec3SSteve Longerbeam .name = "imx-ipuv3-csi",
1108304e6be6SPhilipp Zabel }, {
1109304e6be6SPhilipp Zabel .pdata = {
1110304e6be6SPhilipp Zabel .csi = 1,
1111304e6be6SPhilipp Zabel .dma[0] = IPUV3_CHANNEL_CSI1,
1112304e6be6SPhilipp Zabel .dma[1] = -EINVAL,
1113304e6be6SPhilipp Zabel },
111488287ec3SSteve Longerbeam .name = "imx-ipuv3-csi",
1115304e6be6SPhilipp Zabel }, {
1116304e6be6SPhilipp Zabel .pdata = {
111739b9004dSPhilipp Zabel .di = 0,
111839b9004dSPhilipp Zabel .dc = 5,
111939b9004dSPhilipp Zabel .dp = IPU_DP_FLOW_SYNC_BG,
112039b9004dSPhilipp Zabel .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC,
112139b9004dSPhilipp Zabel .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC,
112239b9004dSPhilipp Zabel },
112339b9004dSPhilipp Zabel .name = "imx-ipuv3-crtc",
112439b9004dSPhilipp Zabel }, {
112539b9004dSPhilipp Zabel .pdata = {
112639b9004dSPhilipp Zabel .di = 1,
112739b9004dSPhilipp Zabel .dc = 1,
112839b9004dSPhilipp Zabel .dp = -EINVAL,
112939b9004dSPhilipp Zabel .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC,
113039b9004dSPhilipp Zabel .dma[1] = -EINVAL,
113139b9004dSPhilipp Zabel },
113239b9004dSPhilipp Zabel .name = "imx-ipuv3-crtc",
113339b9004dSPhilipp Zabel },
113439b9004dSPhilipp Zabel };
113539b9004dSPhilipp Zabel
113639b9004dSPhilipp Zabel static DEFINE_MUTEX(ipu_client_id_mutex);
113739b9004dSPhilipp Zabel static int ipu_client_id;
113839b9004dSPhilipp Zabel
ipu_add_client_devices(struct ipu_soc * ipu,unsigned long ipu_base)1139d6ca8ca7SPhilipp Zabel static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base)
114039b9004dSPhilipp Zabel {
114139b9004dSPhilipp Zabel struct device *dev = ipu->dev;
114239b9004dSPhilipp Zabel unsigned i;
114339b9004dSPhilipp Zabel int id, ret;
114439b9004dSPhilipp Zabel
114539b9004dSPhilipp Zabel mutex_lock(&ipu_client_id_mutex);
114639b9004dSPhilipp Zabel id = ipu_client_id;
114739b9004dSPhilipp Zabel ipu_client_id += ARRAY_SIZE(client_reg);
114839b9004dSPhilipp Zabel mutex_unlock(&ipu_client_id_mutex);
114939b9004dSPhilipp Zabel
115039b9004dSPhilipp Zabel for (i = 0; i < ARRAY_SIZE(client_reg); i++) {
1151310944d1SPhilipp Zabel struct ipu_platform_reg *reg = &client_reg[i];
115239b9004dSPhilipp Zabel struct platform_device *pdev;
115317e05217SPhilipp Zabel struct device_node *of_node;
115417e05217SPhilipp Zabel
115517e05217SPhilipp Zabel /* Associate subdevice with the corresponding port node */
115617e05217SPhilipp Zabel of_node = of_graph_get_port_by_id(dev->of_node, i);
115717e05217SPhilipp Zabel if (!of_node) {
115817e05217SPhilipp Zabel dev_info(dev,
11594bf99144SRob Herring "no port@%d node in %pOF, not using %s%d\n",
11604bf99144SRob Herring i, dev->of_node,
116117e05217SPhilipp Zabel (i / 2) ? "DI" : "CSI", i % 2);
116217e05217SPhilipp Zabel continue;
116317e05217SPhilipp Zabel }
116439b9004dSPhilipp Zabel
1165304e6be6SPhilipp Zabel pdev = platform_device_alloc(reg->name, id++);
1166304e6be6SPhilipp Zabel if (!pdev) {
1167304e6be6SPhilipp Zabel ret = -ENOMEM;
11689afdf98cSLiang He of_node_put(of_node);
1169304e6be6SPhilipp Zabel goto err_register;
1170304e6be6SPhilipp Zabel }
117139b9004dSPhilipp Zabel
1172304e6be6SPhilipp Zabel pdev->dev.parent = dev;
1173304e6be6SPhilipp Zabel
1174310944d1SPhilipp Zabel reg->pdata.of_node = of_node;
1175304e6be6SPhilipp Zabel ret = platform_device_add_data(pdev, ®->pdata,
1176304e6be6SPhilipp Zabel sizeof(reg->pdata));
1177304e6be6SPhilipp Zabel if (!ret)
1178304e6be6SPhilipp Zabel ret = platform_device_add(pdev);
1179304e6be6SPhilipp Zabel if (ret) {
1180304e6be6SPhilipp Zabel platform_device_put(pdev);
118139b9004dSPhilipp Zabel goto err_register;
118239b9004dSPhilipp Zabel }
1183e4946cdcSAxel Lin }
118439b9004dSPhilipp Zabel
118539b9004dSPhilipp Zabel return 0;
118639b9004dSPhilipp Zabel
118739b9004dSPhilipp Zabel err_register:
118839b9004dSPhilipp Zabel platform_device_unregister_children(to_platform_device(dev));
118939b9004dSPhilipp Zabel
119039b9004dSPhilipp Zabel return ret;
119139b9004dSPhilipp Zabel }
119239b9004dSPhilipp Zabel
119339b9004dSPhilipp Zabel
ipu_irq_init(struct ipu_soc * ipu)119439b9004dSPhilipp Zabel static int ipu_irq_init(struct ipu_soc *ipu)
119539b9004dSPhilipp Zabel {
119639b9004dSPhilipp Zabel struct irq_chip_generic *gc;
119739b9004dSPhilipp Zabel struct irq_chip_type *ct;
119839b9004dSPhilipp Zabel unsigned long unused[IPU_NUM_IRQS / 32] = {
119939b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd,
120039b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd,
120139b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd,
120239b9004dSPhilipp Zabel 0x4077ffff, 0xffe7e1fd,
120339b9004dSPhilipp Zabel 0x23fffffe, 0x8880fff0,
120439b9004dSPhilipp Zabel 0xf98fe7d0, 0xfff81fff,
120539b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd,
120639b9004dSPhilipp Zabel 0x00000000,
120739b9004dSPhilipp Zabel };
120839b9004dSPhilipp Zabel int ret, i;
120939b9004dSPhilipp Zabel
121039b9004dSPhilipp Zabel ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS,
121139b9004dSPhilipp Zabel &irq_generic_chip_ops, ipu);
121239b9004dSPhilipp Zabel if (!ipu->domain) {
121339b9004dSPhilipp Zabel dev_err(ipu->dev, "failed to add irq domain\n");
121439b9004dSPhilipp Zabel return -ENODEV;
121539b9004dSPhilipp Zabel }
121639b9004dSPhilipp Zabel
121739b9004dSPhilipp Zabel ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU",
1218ca0141deSRob Herring handle_level_irq, 0, 0, 0);
121939b9004dSPhilipp Zabel if (ret < 0) {
122039b9004dSPhilipp Zabel dev_err(ipu->dev, "failed to alloc generic irq chips\n");
122139b9004dSPhilipp Zabel irq_domain_remove(ipu->domain);
122239b9004dSPhilipp Zabel return ret;
122339b9004dSPhilipp Zabel }
122439b9004dSPhilipp Zabel
1225a92d8145SPhilipp Zabel /* Mask and clear all interrupts */
1226a92d8145SPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i += 32) {
1227510e6426SRussell King ipu_cm_write(ipu, 0, IPU_INT_CTRL(i / 32));
1228a92d8145SPhilipp Zabel ipu_cm_write(ipu, ~unused[i / 32], IPU_INT_STAT(i / 32));
1229a92d8145SPhilipp Zabel }
1230510e6426SRussell King
123139b9004dSPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i += 32) {
123239b9004dSPhilipp Zabel gc = irq_get_domain_generic_chip(ipu->domain, i);
123339b9004dSPhilipp Zabel gc->reg_base = ipu->cm_reg;
123439b9004dSPhilipp Zabel gc->unused = unused[i / 32];
123539b9004dSPhilipp Zabel ct = gc->chip_types;
123639b9004dSPhilipp Zabel ct->chip.irq_ack = irq_gc_ack_set_bit;
123739b9004dSPhilipp Zabel ct->chip.irq_mask = irq_gc_mask_clr_bit;
123839b9004dSPhilipp Zabel ct->chip.irq_unmask = irq_gc_mask_set_bit;
123939b9004dSPhilipp Zabel ct->regs.ack = IPU_INT_STAT(i / 32);
124039b9004dSPhilipp Zabel ct->regs.mask = IPU_INT_CTRL(i / 32);
124139b9004dSPhilipp Zabel }
124239b9004dSPhilipp Zabel
124386f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu);
124486f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler,
124586f5e733SRussell King ipu);
124639b9004dSPhilipp Zabel
124739b9004dSPhilipp Zabel return 0;
124839b9004dSPhilipp Zabel }
124939b9004dSPhilipp Zabel
ipu_irq_exit(struct ipu_soc * ipu)125039b9004dSPhilipp Zabel static void ipu_irq_exit(struct ipu_soc *ipu)
125139b9004dSPhilipp Zabel {
125239b9004dSPhilipp Zabel int i, irq;
125339b9004dSPhilipp Zabel
125486f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL);
125586f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL);
125639b9004dSPhilipp Zabel
125739b9004dSPhilipp Zabel /* TODO: remove irq_domain_generic_chips */
125839b9004dSPhilipp Zabel
125939b9004dSPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i++) {
126039b9004dSPhilipp Zabel irq = irq_linear_revmap(ipu->domain, i);
126139b9004dSPhilipp Zabel if (irq)
126239b9004dSPhilipp Zabel irq_dispose_mapping(irq);
126339b9004dSPhilipp Zabel }
126439b9004dSPhilipp Zabel
126539b9004dSPhilipp Zabel irq_domain_remove(ipu->domain);
126639b9004dSPhilipp Zabel }
126739b9004dSPhilipp Zabel
ipu_dump(struct ipu_soc * ipu)12683feb049fSSteve Longerbeam void ipu_dump(struct ipu_soc *ipu)
12693feb049fSSteve Longerbeam {
12703feb049fSSteve Longerbeam int i;
12713feb049fSSteve Longerbeam
12723feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n",
12733feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_CONF));
12743feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n",
12753feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CONF));
12763feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n",
12773feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_EN(0)));
12783feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n",
12793feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_EN(32)));
12803feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n",
12813feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_PRI(0)));
12823feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n",
12833feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_PRI(32)));
12843feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n",
12853feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_BAND_EN(0)));
12863feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n",
12873feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_BAND_EN(32)));
12883feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n",
12893feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0)));
12903feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n",
12913feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32)));
12923feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n",
12933feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_PROC_FLOW1));
12943feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n",
12953feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_PROC_FLOW2));
12963feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n",
12973feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_PROC_FLOW3));
12983feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n",
12993feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_DISP_FLOW1));
13003feb049fSSteve Longerbeam for (i = 0; i < 15; i++)
13013feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i,
13023feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_INT_CTRL(i)));
13033feb049fSSteve Longerbeam }
13043feb049fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_dump);
13053feb049fSSteve Longerbeam
ipu_probe(struct platform_device * pdev)130639b9004dSPhilipp Zabel static int ipu_probe(struct platform_device *pdev)
130739b9004dSPhilipp Zabel {
1308572a7615SSteve Longerbeam struct device_node *np = pdev->dev.of_node;
130939b9004dSPhilipp Zabel struct ipu_soc *ipu;
131039b9004dSPhilipp Zabel struct resource *res;
131139b9004dSPhilipp Zabel unsigned long ipu_base;
131293adc8b5SPhilipp Zabel int ret, irq_sync, irq_err;
131339b9004dSPhilipp Zabel const struct ipu_devtype *devtype;
131439b9004dSPhilipp Zabel
1315e92e4478SLABBE Corentin devtype = of_device_get_match_data(&pdev->dev);
1316e92e4478SLABBE Corentin if (!devtype)
1317e92e4478SLABBE Corentin return -EINVAL;
131839b9004dSPhilipp Zabel
131939b9004dSPhilipp Zabel irq_sync = platform_get_irq(pdev, 0);
132039b9004dSPhilipp Zabel irq_err = platform_get_irq(pdev, 1);
132139b9004dSPhilipp Zabel res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
132239b9004dSPhilipp Zabel
132339b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n",
132439b9004dSPhilipp Zabel irq_sync, irq_err);
132539b9004dSPhilipp Zabel
132639b9004dSPhilipp Zabel if (!res || irq_sync < 0 || irq_err < 0)
132739b9004dSPhilipp Zabel return -ENODEV;
132839b9004dSPhilipp Zabel
132939b9004dSPhilipp Zabel ipu_base = res->start;
133039b9004dSPhilipp Zabel
133139b9004dSPhilipp Zabel ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL);
133239b9004dSPhilipp Zabel if (!ipu)
133339b9004dSPhilipp Zabel return -ENODEV;
133439b9004dSPhilipp Zabel
133592681fe7SLucas Stach ipu->id = of_alias_get_id(np, "ipu");
13362d87e6c1SPhilipp Zabel if (ipu->id < 0)
13372d87e6c1SPhilipp Zabel ipu->id = 0;
133892681fe7SLucas Stach
133930310c83SLucas Stach if (of_device_is_compatible(np, "fsl,imx6qp-ipu") &&
134030310c83SLucas Stach IS_ENABLED(CONFIG_DRM)) {
134192681fe7SLucas Stach ipu->prg_priv = ipu_prg_lookup_by_phandle(&pdev->dev,
134292681fe7SLucas Stach "fsl,prg", ipu->id);
134392681fe7SLucas Stach if (!ipu->prg_priv)
134492681fe7SLucas Stach return -EPROBE_DEFER;
134592681fe7SLucas Stach }
134692681fe7SLucas Stach
134739b9004dSPhilipp Zabel ipu->devtype = devtype;
134839b9004dSPhilipp Zabel ipu->ipu_type = devtype->type;
134939b9004dSPhilipp Zabel
135039b9004dSPhilipp Zabel spin_lock_init(&ipu->lock);
135139b9004dSPhilipp Zabel mutex_init(&ipu->channel_lock);
135293adc8b5SPhilipp Zabel INIT_LIST_HEAD(&ipu->channels);
135339b9004dSPhilipp Zabel
135439b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n",
135539b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs);
135639b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "idmac: 0x%08lx\n",
135739b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS);
135839b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n",
135939b9004dSPhilipp Zabel ipu_base + devtype->cpmem_ofs);
13602ffd48f2SSteve Longerbeam dev_dbg(&pdev->dev, "csi0: 0x%08lx\n",
13612ffd48f2SSteve Longerbeam ipu_base + devtype->csi0_ofs);
13622ffd48f2SSteve Longerbeam dev_dbg(&pdev->dev, "csi1: 0x%08lx\n",
13632ffd48f2SSteve Longerbeam ipu_base + devtype->csi1_ofs);
13641aa8ea0dSSteve Longerbeam dev_dbg(&pdev->dev, "ic: 0x%08lx\n",
13651aa8ea0dSSteve Longerbeam ipu_base + devtype->ic_ofs);
136639b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "disp0: 0x%08lx\n",
136739b9004dSPhilipp Zabel ipu_base + devtype->disp0_ofs);
136839b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "disp1: 0x%08lx\n",
136939b9004dSPhilipp Zabel ipu_base + devtype->disp1_ofs);
137039b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "srm: 0x%08lx\n",
137139b9004dSPhilipp Zabel ipu_base + devtype->srm_ofs);
137239b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "tpm: 0x%08lx\n",
137339b9004dSPhilipp Zabel ipu_base + devtype->tpm_ofs);
137439b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "dc: 0x%08lx\n",
137539b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS);
137639b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "ic: 0x%08lx\n",
137739b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS);
137839b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n",
137939b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS);
138039b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "vdi: 0x%08lx\n",
138139b9004dSPhilipp Zabel ipu_base + devtype->vdi_ofs);
138239b9004dSPhilipp Zabel
138339b9004dSPhilipp Zabel ipu->cm_reg = devm_ioremap(&pdev->dev,
138439b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs, PAGE_SIZE);
138539b9004dSPhilipp Zabel ipu->idmac_reg = devm_ioremap(&pdev->dev,
138639b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS,
138739b9004dSPhilipp Zabel PAGE_SIZE);
138839b9004dSPhilipp Zabel
13897d2691daSSteve Longerbeam if (!ipu->cm_reg || !ipu->idmac_reg)
139039b9004dSPhilipp Zabel return -ENOMEM;
139139b9004dSPhilipp Zabel
139239b9004dSPhilipp Zabel ipu->clk = devm_clk_get(&pdev->dev, "bus");
139339b9004dSPhilipp Zabel if (IS_ERR(ipu->clk)) {
139439b9004dSPhilipp Zabel ret = PTR_ERR(ipu->clk);
139539b9004dSPhilipp Zabel dev_err(&pdev->dev, "clk_get failed with %d", ret);
139639b9004dSPhilipp Zabel return ret;
139739b9004dSPhilipp Zabel }
139839b9004dSPhilipp Zabel
139939b9004dSPhilipp Zabel platform_set_drvdata(pdev, ipu);
140039b9004dSPhilipp Zabel
140139b9004dSPhilipp Zabel ret = clk_prepare_enable(ipu->clk);
140239b9004dSPhilipp Zabel if (ret) {
140339b9004dSPhilipp Zabel dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
140439b9004dSPhilipp Zabel return ret;
140539b9004dSPhilipp Zabel }
140639b9004dSPhilipp Zabel
140739b9004dSPhilipp Zabel ipu->dev = &pdev->dev;
140839b9004dSPhilipp Zabel ipu->irq_sync = irq_sync;
140939b9004dSPhilipp Zabel ipu->irq_err = irq_err;
141039b9004dSPhilipp Zabel
141139b9004dSPhilipp Zabel ret = device_reset(&pdev->dev);
141239b9004dSPhilipp Zabel if (ret) {
141339b9004dSPhilipp Zabel dev_err(&pdev->dev, "failed to reset: %d\n", ret);
141439b9004dSPhilipp Zabel goto out_failed_reset;
141539b9004dSPhilipp Zabel }
141639b9004dSPhilipp Zabel ret = ipu_memory_reset(ipu);
141739b9004dSPhilipp Zabel if (ret)
141839b9004dSPhilipp Zabel goto out_failed_reset;
141939b9004dSPhilipp Zabel
1420596a65d1SDavid Jander ret = ipu_irq_init(ipu);
1421596a65d1SDavid Jander if (ret)
1422596a65d1SDavid Jander goto out_failed_irq;
1423596a65d1SDavid Jander
142439b9004dSPhilipp Zabel /* Set MCU_T to divide MCU access window into 2 */
142539b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
142639b9004dSPhilipp Zabel IPU_DISP_GEN);
142739b9004dSPhilipp Zabel
142839b9004dSPhilipp Zabel ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk);
142939b9004dSPhilipp Zabel if (ret)
143039b9004dSPhilipp Zabel goto failed_submodules_init;
143139b9004dSPhilipp Zabel
1432d6ca8ca7SPhilipp Zabel ret = ipu_add_client_devices(ipu, ipu_base);
143339b9004dSPhilipp Zabel if (ret) {
143439b9004dSPhilipp Zabel dev_err(&pdev->dev, "adding client devices failed with %d\n",
143539b9004dSPhilipp Zabel ret);
143639b9004dSPhilipp Zabel goto failed_add_clients;
143739b9004dSPhilipp Zabel }
143839b9004dSPhilipp Zabel
143939b9004dSPhilipp Zabel dev_info(&pdev->dev, "%s probed\n", devtype->name);
144039b9004dSPhilipp Zabel
144139b9004dSPhilipp Zabel return 0;
144239b9004dSPhilipp Zabel
144339b9004dSPhilipp Zabel failed_add_clients:
144439b9004dSPhilipp Zabel ipu_submodules_exit(ipu);
144539b9004dSPhilipp Zabel failed_submodules_init:
144639b9004dSPhilipp Zabel ipu_irq_exit(ipu);
144739b9004dSPhilipp Zabel out_failed_irq:
1448596a65d1SDavid Jander out_failed_reset:
144939b9004dSPhilipp Zabel clk_disable_unprepare(ipu->clk);
145039b9004dSPhilipp Zabel return ret;
145139b9004dSPhilipp Zabel }
145239b9004dSPhilipp Zabel
ipu_remove(struct platform_device * pdev)145339b9004dSPhilipp Zabel static int ipu_remove(struct platform_device *pdev)
145439b9004dSPhilipp Zabel {
145539b9004dSPhilipp Zabel struct ipu_soc *ipu = platform_get_drvdata(pdev);
145639b9004dSPhilipp Zabel
145739b9004dSPhilipp Zabel platform_device_unregister_children(pdev);
145839b9004dSPhilipp Zabel ipu_submodules_exit(ipu);
145939b9004dSPhilipp Zabel ipu_irq_exit(ipu);
146039b9004dSPhilipp Zabel
146139b9004dSPhilipp Zabel clk_disable_unprepare(ipu->clk);
146239b9004dSPhilipp Zabel
146339b9004dSPhilipp Zabel return 0;
146439b9004dSPhilipp Zabel }
146539b9004dSPhilipp Zabel
146639b9004dSPhilipp Zabel static struct platform_driver imx_ipu_driver = {
146739b9004dSPhilipp Zabel .driver = {
146839b9004dSPhilipp Zabel .name = "imx-ipuv3",
146939b9004dSPhilipp Zabel .of_match_table = imx_ipu_dt_ids,
147039b9004dSPhilipp Zabel },
147139b9004dSPhilipp Zabel .probe = ipu_probe,
147239b9004dSPhilipp Zabel .remove = ipu_remove,
147339b9004dSPhilipp Zabel };
147439b9004dSPhilipp Zabel
1475d2a34232SLucas Stach static struct platform_driver * const drivers[] = {
147630310c83SLucas Stach #if IS_ENABLED(CONFIG_DRM)
1477d2a34232SLucas Stach &ipu_pre_drv,
1478ea9c2605SLucas Stach &ipu_prg_drv,
147930310c83SLucas Stach #endif
1480d2a34232SLucas Stach &imx_ipu_driver,
1481d2a34232SLucas Stach };
1482d2a34232SLucas Stach
imx_ipu_init(void)1483d2a34232SLucas Stach static int __init imx_ipu_init(void)
1484d2a34232SLucas Stach {
1485d2a34232SLucas Stach return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
1486d2a34232SLucas Stach }
1487d2a34232SLucas Stach module_init(imx_ipu_init);
1488d2a34232SLucas Stach
imx_ipu_exit(void)1489d2a34232SLucas Stach static void __exit imx_ipu_exit(void)
1490d2a34232SLucas Stach {
1491d2a34232SLucas Stach platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
1492d2a34232SLucas Stach }
1493d2a34232SLucas Stach module_exit(imx_ipu_exit);
149439b9004dSPhilipp Zabel
149539b9004dSPhilipp Zabel MODULE_ALIAS("platform:imx-ipuv3");
149639b9004dSPhilipp Zabel MODULE_DESCRIPTION("i.MX IPU v3 driver");
149739b9004dSPhilipp Zabel MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
149839b9004dSPhilipp Zabel MODULE_LICENSE("GPL");
1499