162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
462306a36Sopenharmony_ci * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/export.h>
862306a36Sopenharmony_ci#include <linux/types.h>
962306a36Sopenharmony_ci#include <linux/reset.h>
1062306a36Sopenharmony_ci#include <linux/platform_device.h>
1162306a36Sopenharmony_ci#include <linux/err.h>
1262306a36Sopenharmony_ci#include <linux/spinlock.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/io.h>
1662306a36Sopenharmony_ci#include <linux/clk.h>
1762306a36Sopenharmony_ci#include <linux/list.h>
1862306a36Sopenharmony_ci#include <linux/irq.h>
1962306a36Sopenharmony_ci#include <linux/irqchip/chained_irq.h>
2062306a36Sopenharmony_ci#include <linux/irqdomain.h>
2162306a36Sopenharmony_ci#include <linux/of.h>
2262306a36Sopenharmony_ci#include <linux/of_graph.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <drm/drm_fourcc.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <video/imx-ipu-v3.h>
2762306a36Sopenharmony_ci#include "ipu-prv.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	return readl(ipu->cm_reg + offset);
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	writel(value, ipu->cm_reg + offset);
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ciint ipu_get_num(struct ipu_soc *ipu)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	return ipu->id;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_get_num);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_civoid ipu_srm_dp_update(struct ipu_soc *ipu, bool sync)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	u32 val;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	val = ipu_cm_read(ipu, IPU_SRM_PRI2);
5062306a36Sopenharmony_ci	val &= ~DP_S_SRM_MODE_MASK;
5162306a36Sopenharmony_ci	val |= sync ? DP_S_SRM_MODE_NEXT_FRAME :
5262306a36Sopenharmony_ci		      DP_S_SRM_MODE_NOW;
5362306a36Sopenharmony_ci	ipu_cm_write(ipu, val, IPU_SRM_PRI2);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_srm_dp_update);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cienum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	switch (drm_fourcc) {
6062306a36Sopenharmony_ci	case DRM_FORMAT_ARGB1555:
6162306a36Sopenharmony_ci	case DRM_FORMAT_ABGR1555:
6262306a36Sopenharmony_ci	case DRM_FORMAT_RGBA5551:
6362306a36Sopenharmony_ci	case DRM_FORMAT_BGRA5551:
6462306a36Sopenharmony_ci	case DRM_FORMAT_RGB565:
6562306a36Sopenharmony_ci	case DRM_FORMAT_BGR565:
6662306a36Sopenharmony_ci	case DRM_FORMAT_RGB888:
6762306a36Sopenharmony_ci	case DRM_FORMAT_BGR888:
6862306a36Sopenharmony_ci	case DRM_FORMAT_ARGB4444:
6962306a36Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
7062306a36Sopenharmony_ci	case DRM_FORMAT_XBGR8888:
7162306a36Sopenharmony_ci	case DRM_FORMAT_RGBX8888:
7262306a36Sopenharmony_ci	case DRM_FORMAT_BGRX8888:
7362306a36Sopenharmony_ci	case DRM_FORMAT_ARGB8888:
7462306a36Sopenharmony_ci	case DRM_FORMAT_ABGR8888:
7562306a36Sopenharmony_ci	case DRM_FORMAT_RGBA8888:
7662306a36Sopenharmony_ci	case DRM_FORMAT_BGRA8888:
7762306a36Sopenharmony_ci	case DRM_FORMAT_RGB565_A8:
7862306a36Sopenharmony_ci	case DRM_FORMAT_BGR565_A8:
7962306a36Sopenharmony_ci	case DRM_FORMAT_RGB888_A8:
8062306a36Sopenharmony_ci	case DRM_FORMAT_BGR888_A8:
8162306a36Sopenharmony_ci	case DRM_FORMAT_RGBX8888_A8:
8262306a36Sopenharmony_ci	case DRM_FORMAT_BGRX8888_A8:
8362306a36Sopenharmony_ci		return IPUV3_COLORSPACE_RGB;
8462306a36Sopenharmony_ci	case DRM_FORMAT_YUYV:
8562306a36Sopenharmony_ci	case DRM_FORMAT_UYVY:
8662306a36Sopenharmony_ci	case DRM_FORMAT_YUV420:
8762306a36Sopenharmony_ci	case DRM_FORMAT_YVU420:
8862306a36Sopenharmony_ci	case DRM_FORMAT_YUV422:
8962306a36Sopenharmony_ci	case DRM_FORMAT_YVU422:
9062306a36Sopenharmony_ci	case DRM_FORMAT_YUV444:
9162306a36Sopenharmony_ci	case DRM_FORMAT_YVU444:
9262306a36Sopenharmony_ci	case DRM_FORMAT_NV12:
9362306a36Sopenharmony_ci	case DRM_FORMAT_NV21:
9462306a36Sopenharmony_ci	case DRM_FORMAT_NV16:
9562306a36Sopenharmony_ci	case DRM_FORMAT_NV61:
9662306a36Sopenharmony_ci		return IPUV3_COLORSPACE_YUV;
9762306a36Sopenharmony_ci	default:
9862306a36Sopenharmony_ci		return IPUV3_COLORSPACE_UNKNOWN;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cienum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	switch (pixelformat) {
10662306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUV420:
10762306a36Sopenharmony_ci	case V4L2_PIX_FMT_YVU420:
10862306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUV422P:
10962306a36Sopenharmony_ci	case V4L2_PIX_FMT_UYVY:
11062306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUYV:
11162306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV12:
11262306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV21:
11362306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV16:
11462306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV61:
11562306a36Sopenharmony_ci		return IPUV3_COLORSPACE_YUV;
11662306a36Sopenharmony_ci	case V4L2_PIX_FMT_RGB565:
11762306a36Sopenharmony_ci	case V4L2_PIX_FMT_BGR24:
11862306a36Sopenharmony_ci	case V4L2_PIX_FMT_RGB24:
11962306a36Sopenharmony_ci	case V4L2_PIX_FMT_ABGR32:
12062306a36Sopenharmony_ci	case V4L2_PIX_FMT_XBGR32:
12162306a36Sopenharmony_ci	case V4L2_PIX_FMT_BGRA32:
12262306a36Sopenharmony_ci	case V4L2_PIX_FMT_BGRX32:
12362306a36Sopenharmony_ci	case V4L2_PIX_FMT_RGBA32:
12462306a36Sopenharmony_ci	case V4L2_PIX_FMT_RGBX32:
12562306a36Sopenharmony_ci	case V4L2_PIX_FMT_ARGB32:
12662306a36Sopenharmony_ci	case V4L2_PIX_FMT_XRGB32:
12762306a36Sopenharmony_ci	case V4L2_PIX_FMT_RGB32:
12862306a36Sopenharmony_ci	case V4L2_PIX_FMT_BGR32:
12962306a36Sopenharmony_ci		return IPUV3_COLORSPACE_RGB;
13062306a36Sopenharmony_ci	default:
13162306a36Sopenharmony_ci		return IPUV3_COLORSPACE_UNKNOWN;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ciint ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees,
13762306a36Sopenharmony_ci			    bool hflip, bool vflip)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	u32 r90, vf, hf;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	switch (degrees) {
14262306a36Sopenharmony_ci	case 0:
14362306a36Sopenharmony_ci		vf = hf = r90 = 0;
14462306a36Sopenharmony_ci		break;
14562306a36Sopenharmony_ci	case 90:
14662306a36Sopenharmony_ci		vf = hf = 0;
14762306a36Sopenharmony_ci		r90 = 1;
14862306a36Sopenharmony_ci		break;
14962306a36Sopenharmony_ci	case 180:
15062306a36Sopenharmony_ci		vf = hf = 1;
15162306a36Sopenharmony_ci		r90 = 0;
15262306a36Sopenharmony_ci		break;
15362306a36Sopenharmony_ci	case 270:
15462306a36Sopenharmony_ci		vf = hf = r90 = 1;
15562306a36Sopenharmony_ci		break;
15662306a36Sopenharmony_ci	default:
15762306a36Sopenharmony_ci		return -EINVAL;
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	hf ^= (u32)hflip;
16162306a36Sopenharmony_ci	vf ^= (u32)vflip;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	*mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf);
16462306a36Sopenharmony_ci	return 0;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ciint ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode,
16962306a36Sopenharmony_ci			    bool hflip, bool vflip)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	u32 r90, vf, hf;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	r90 = ((u32)mode >> 2) & 0x1;
17462306a36Sopenharmony_ci	hf = ((u32)mode >> 1) & 0x1;
17562306a36Sopenharmony_ci	vf = ((u32)mode >> 0) & 0x1;
17662306a36Sopenharmony_ci	hf ^= (u32)hflip;
17762306a36Sopenharmony_ci	vf ^= (u32)vflip;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) {
18062306a36Sopenharmony_ci	case IPU_ROTATE_NONE:
18162306a36Sopenharmony_ci		*degrees = 0;
18262306a36Sopenharmony_ci		break;
18362306a36Sopenharmony_ci	case IPU_ROTATE_90_RIGHT:
18462306a36Sopenharmony_ci		*degrees = 90;
18562306a36Sopenharmony_ci		break;
18662306a36Sopenharmony_ci	case IPU_ROTATE_180:
18762306a36Sopenharmony_ci		*degrees = 180;
18862306a36Sopenharmony_ci		break;
18962306a36Sopenharmony_ci	case IPU_ROTATE_90_LEFT:
19062306a36Sopenharmony_ci		*degrees = 270;
19162306a36Sopenharmony_ci		break;
19262306a36Sopenharmony_ci	default:
19362306a36Sopenharmony_ci		return -EINVAL;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return 0;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistruct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct ipuv3_channel *channel;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	dev_dbg(ipu->dev, "%s %d\n", __func__, num);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	if (num > 63)
20762306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	mutex_lock(&ipu->channel_lock);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	list_for_each_entry(channel, &ipu->channels, list) {
21262306a36Sopenharmony_ci		if (channel->num == num) {
21362306a36Sopenharmony_ci			channel = ERR_PTR(-EBUSY);
21462306a36Sopenharmony_ci			goto out;
21562306a36Sopenharmony_ci		}
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	channel = kzalloc(sizeof(*channel), GFP_KERNEL);
21962306a36Sopenharmony_ci	if (!channel) {
22062306a36Sopenharmony_ci		channel = ERR_PTR(-ENOMEM);
22162306a36Sopenharmony_ci		goto out;
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	channel->num = num;
22562306a36Sopenharmony_ci	channel->ipu = ipu;
22662306a36Sopenharmony_ci	list_add(&channel->list, &ipu->channels);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ciout:
22962306a36Sopenharmony_ci	mutex_unlock(&ipu->channel_lock);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	return channel;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_get);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_civoid ipu_idmac_put(struct ipuv3_channel *channel)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	struct ipu_soc *ipu = channel->ipu;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	mutex_lock(&ipu->channel_lock);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	list_del(&channel->list);
24462306a36Sopenharmony_ci	kfree(channel);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	mutex_unlock(&ipu->channel_lock);
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_put);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci#define idma_mask(ch)			(1 << ((ch) & 0x1f))
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci/*
25362306a36Sopenharmony_ci * This is an undocumented feature, a write one to a channel bit in
25462306a36Sopenharmony_ci * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's
25562306a36Sopenharmony_ci * internal current buffer pointer so that transfers start from buffer
25662306a36Sopenharmony_ci * 0 on the next channel enable (that's the theory anyway, the imx6 TRM
25762306a36Sopenharmony_ci * only says these are read-only registers). This operation is required
25862306a36Sopenharmony_ci * for channel linking to work correctly, for instance video capture
25962306a36Sopenharmony_ci * pipelines that carry out image rotations will fail after the first
26062306a36Sopenharmony_ci * streaming unless this function is called for each channel before
26162306a36Sopenharmony_ci * re-enabling the channels.
26262306a36Sopenharmony_ci */
26362306a36Sopenharmony_cistatic void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct ipu_soc *ipu = channel->ipu;
26662306a36Sopenharmony_ci	unsigned int chno = channel->num;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno));
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_civoid ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
27262306a36Sopenharmony_ci		bool doublebuffer)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct ipu_soc *ipu = channel->ipu;
27562306a36Sopenharmony_ci	unsigned long flags;
27662306a36Sopenharmony_ci	u32 reg;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, flags);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
28162306a36Sopenharmony_ci	if (doublebuffer)
28262306a36Sopenharmony_ci		reg |= idma_mask(channel->num);
28362306a36Sopenharmony_ci	else
28462306a36Sopenharmony_ci		reg &= ~idma_mask(channel->num);
28562306a36Sopenharmony_ci	ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num));
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	__ipu_idmac_reset_current_buffer(channel);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, flags);
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic const struct {
29462306a36Sopenharmony_ci	int chnum;
29562306a36Sopenharmony_ci	u32 reg;
29662306a36Sopenharmony_ci	int shift;
29762306a36Sopenharmony_ci} idmac_lock_en_info[] = {
29862306a36Sopenharmony_ci	{ .chnum =  5, .reg = IDMAC_CH_LOCK_EN_1, .shift =  0, },
29962306a36Sopenharmony_ci	{ .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift =  2, },
30062306a36Sopenharmony_ci	{ .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift =  4, },
30162306a36Sopenharmony_ci	{ .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift =  6, },
30262306a36Sopenharmony_ci	{ .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift =  8, },
30362306a36Sopenharmony_ci	{ .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, },
30462306a36Sopenharmony_ci	{ .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, },
30562306a36Sopenharmony_ci	{ .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, },
30662306a36Sopenharmony_ci	{ .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, },
30762306a36Sopenharmony_ci	{ .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, },
30862306a36Sopenharmony_ci	{ .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, },
30962306a36Sopenharmony_ci	{ .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift =  0, },
31062306a36Sopenharmony_ci	{ .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift =  2, },
31162306a36Sopenharmony_ci	{ .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift =  4, },
31262306a36Sopenharmony_ci	{ .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift =  6, },
31362306a36Sopenharmony_ci	{ .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift =  8, },
31462306a36Sopenharmony_ci	{ .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, },
31562306a36Sopenharmony_ci};
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ciint ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct ipu_soc *ipu = channel->ipu;
32062306a36Sopenharmony_ci	unsigned long flags;
32162306a36Sopenharmony_ci	u32 bursts, regval;
32262306a36Sopenharmony_ci	int i;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	switch (num_bursts) {
32562306a36Sopenharmony_ci	case 0:
32662306a36Sopenharmony_ci	case 1:
32762306a36Sopenharmony_ci		bursts = 0x00; /* locking disabled */
32862306a36Sopenharmony_ci		break;
32962306a36Sopenharmony_ci	case 2:
33062306a36Sopenharmony_ci		bursts = 0x01;
33162306a36Sopenharmony_ci		break;
33262306a36Sopenharmony_ci	case 4:
33362306a36Sopenharmony_ci		bursts = 0x02;
33462306a36Sopenharmony_ci		break;
33562306a36Sopenharmony_ci	case 8:
33662306a36Sopenharmony_ci		bursts = 0x03;
33762306a36Sopenharmony_ci		break;
33862306a36Sopenharmony_ci	default:
33962306a36Sopenharmony_ci		return -EINVAL;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/*
34362306a36Sopenharmony_ci	 * IPUv3EX / i.MX51 has a different register layout, and on IPUv3M /
34462306a36Sopenharmony_ci	 * i.MX53 channel arbitration locking doesn't seem to work properly.
34562306a36Sopenharmony_ci	 * Allow enabling the lock feature on IPUv3H / i.MX6 only.
34662306a36Sopenharmony_ci	 */
34762306a36Sopenharmony_ci	if (bursts && ipu->ipu_type != IPUV3H)
34862306a36Sopenharmony_ci		return -EINVAL;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) {
35162306a36Sopenharmony_ci		if (channel->num == idmac_lock_en_info[i].chnum)
35262306a36Sopenharmony_ci			break;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci	if (i >= ARRAY_SIZE(idmac_lock_en_info))
35562306a36Sopenharmony_ci		return -EINVAL;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, flags);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg);
36062306a36Sopenharmony_ci	regval &= ~(0x03 << idmac_lock_en_info[i].shift);
36162306a36Sopenharmony_ci	regval |= (bursts << idmac_lock_en_info[i].shift);
36262306a36Sopenharmony_ci	ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, flags);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	return 0;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_lock_enable);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ciint ipu_module_enable(struct ipu_soc *ipu, u32 mask)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	unsigned long lock_flags;
37362306a36Sopenharmony_ci	u32 val;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, lock_flags);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	val = ipu_cm_read(ipu, IPU_DISP_GEN);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (mask & IPU_CONF_DI0_EN)
38062306a36Sopenharmony_ci		val |= IPU_DI0_COUNTER_RELEASE;
38162306a36Sopenharmony_ci	if (mask & IPU_CONF_DI1_EN)
38262306a36Sopenharmony_ci		val |= IPU_DI1_COUNTER_RELEASE;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	ipu_cm_write(ipu, val, IPU_DISP_GEN);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	val = ipu_cm_read(ipu, IPU_CONF);
38762306a36Sopenharmony_ci	val |= mask;
38862306a36Sopenharmony_ci	ipu_cm_write(ipu, val, IPU_CONF);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, lock_flags);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return 0;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_module_enable);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ciint ipu_module_disable(struct ipu_soc *ipu, u32 mask)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	unsigned long lock_flags;
39962306a36Sopenharmony_ci	u32 val;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, lock_flags);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	val = ipu_cm_read(ipu, IPU_CONF);
40462306a36Sopenharmony_ci	val &= ~mask;
40562306a36Sopenharmony_ci	ipu_cm_write(ipu, val, IPU_CONF);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	val = ipu_cm_read(ipu, IPU_DISP_GEN);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (mask & IPU_CONF_DI0_EN)
41062306a36Sopenharmony_ci		val &= ~IPU_DI0_COUNTER_RELEASE;
41162306a36Sopenharmony_ci	if (mask & IPU_CONF_DI1_EN)
41262306a36Sopenharmony_ci		val &= ~IPU_DI1_COUNTER_RELEASE;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	ipu_cm_write(ipu, val, IPU_DISP_GEN);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, lock_flags);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	return 0;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_module_disable);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ciint ipu_idmac_get_current_buffer(struct ipuv3_channel *channel)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct ipu_soc *ipu = channel->ipu;
42562306a36Sopenharmony_ci	unsigned int chno = channel->num;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0;
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cibool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	struct ipu_soc *ipu = channel->ipu;
43462306a36Sopenharmony_ci	unsigned long flags;
43562306a36Sopenharmony_ci	u32 reg = 0;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, flags);
43862306a36Sopenharmony_ci	switch (buf_num) {
43962306a36Sopenharmony_ci	case 0:
44062306a36Sopenharmony_ci		reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num));
44162306a36Sopenharmony_ci		break;
44262306a36Sopenharmony_ci	case 1:
44362306a36Sopenharmony_ci		reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num));
44462306a36Sopenharmony_ci		break;
44562306a36Sopenharmony_ci	case 2:
44662306a36Sopenharmony_ci		reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num));
44762306a36Sopenharmony_ci		break;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, flags);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	return ((reg & idma_mask(channel->num)) != 0);
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_civoid ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	struct ipu_soc *ipu = channel->ipu;
45862306a36Sopenharmony_ci	unsigned int chno = channel->num;
45962306a36Sopenharmony_ci	unsigned long flags;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, flags);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	/* Mark buffer as ready. */
46462306a36Sopenharmony_ci	if (buf_num == 0)
46562306a36Sopenharmony_ci		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
46662306a36Sopenharmony_ci	else
46762306a36Sopenharmony_ci		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, flags);
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_select_buffer);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_civoid ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	struct ipu_soc *ipu = channel->ipu;
47662306a36Sopenharmony_ci	unsigned int chno = channel->num;
47762306a36Sopenharmony_ci	unsigned long flags;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, flags);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */
48262306a36Sopenharmony_ci	switch (buf_num) {
48362306a36Sopenharmony_ci	case 0:
48462306a36Sopenharmony_ci		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
48562306a36Sopenharmony_ci		break;
48662306a36Sopenharmony_ci	case 1:
48762306a36Sopenharmony_ci		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
48862306a36Sopenharmony_ci		break;
48962306a36Sopenharmony_ci	case 2:
49062306a36Sopenharmony_ci		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno));
49162306a36Sopenharmony_ci		break;
49262306a36Sopenharmony_ci	default:
49362306a36Sopenharmony_ci		break;
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci	ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, flags);
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ciint ipu_idmac_enable_channel(struct ipuv3_channel *channel)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	struct ipu_soc *ipu = channel->ipu;
50462306a36Sopenharmony_ci	u32 val;
50562306a36Sopenharmony_ci	unsigned long flags;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, flags);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
51062306a36Sopenharmony_ci	val |= idma_mask(channel->num);
51162306a36Sopenharmony_ci	ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, flags);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	return 0;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cibool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno));
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_channel_busy);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ciint ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	struct ipu_soc *ipu = channel->ipu;
52862306a36Sopenharmony_ci	unsigned long timeout;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(ms);
53162306a36Sopenharmony_ci	while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) &
53262306a36Sopenharmony_ci			idma_mask(channel->num)) {
53362306a36Sopenharmony_ci		if (time_after(jiffies, timeout))
53462306a36Sopenharmony_ci			return -ETIMEDOUT;
53562306a36Sopenharmony_ci		cpu_relax();
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	return 0;
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_wait_busy);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ciint ipu_idmac_disable_channel(struct ipuv3_channel *channel)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	struct ipu_soc *ipu = channel->ipu;
54562306a36Sopenharmony_ci	u32 val;
54662306a36Sopenharmony_ci	unsigned long flags;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, flags);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	/* Disable DMA channel(s) */
55162306a36Sopenharmony_ci	val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
55262306a36Sopenharmony_ci	val &= ~idma_mask(channel->num);
55362306a36Sopenharmony_ci	ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	__ipu_idmac_reset_current_buffer(channel);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	/* Set channel buffers NOT to be ready */
55862306a36Sopenharmony_ci	ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) &
56162306a36Sopenharmony_ci			idma_mask(channel->num)) {
56262306a36Sopenharmony_ci		ipu_cm_write(ipu, idma_mask(channel->num),
56362306a36Sopenharmony_ci			     IPU_CHA_BUF0_RDY(channel->num));
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) &
56762306a36Sopenharmony_ci			idma_mask(channel->num)) {
56862306a36Sopenharmony_ci		ipu_cm_write(ipu, idma_mask(channel->num),
56962306a36Sopenharmony_ci			     IPU_CHA_BUF1_RDY(channel->num));
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	/* Reset the double buffer */
57562306a36Sopenharmony_ci	val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
57662306a36Sopenharmony_ci	val &= ~idma_mask(channel->num);
57762306a36Sopenharmony_ci	ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num));
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, flags);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	return 0;
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_disable_channel);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci/*
58662306a36Sopenharmony_ci * The imx6 rev. D TRM says that enabling the WM feature will increase
58762306a36Sopenharmony_ci * a channel's priority. Refer to Table 36-8 Calculated priority value.
58862306a36Sopenharmony_ci * The sub-module that is the sink or source for the channel must enable
58962306a36Sopenharmony_ci * watermark signal for this to take effect (SMFC_WM for instance).
59062306a36Sopenharmony_ci */
59162306a36Sopenharmony_civoid ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable)
59262306a36Sopenharmony_ci{
59362306a36Sopenharmony_ci	struct ipu_soc *ipu = channel->ipu;
59462306a36Sopenharmony_ci	unsigned long flags;
59562306a36Sopenharmony_ci	u32 val;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, flags);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num));
60062306a36Sopenharmony_ci	if (enable)
60162306a36Sopenharmony_ci		val |= 1 << (channel->num % 32);
60262306a36Sopenharmony_ci	else
60362306a36Sopenharmony_ci		val &= ~(1 << (channel->num % 32));
60462306a36Sopenharmony_ci	ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num));
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, flags);
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic int ipu_memory_reset(struct ipu_soc *ipu)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	unsigned long timeout;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(1000);
61762306a36Sopenharmony_ci	while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) {
61862306a36Sopenharmony_ci		if (time_after(jiffies, timeout))
61962306a36Sopenharmony_ci			return -ETIME;
62062306a36Sopenharmony_ci		cpu_relax();
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	return 0;
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci/*
62762306a36Sopenharmony_ci * Set the source mux for the given CSI. Selects either parallel or
62862306a36Sopenharmony_ci * MIPI CSI2 sources.
62962306a36Sopenharmony_ci */
63062306a36Sopenharmony_civoid ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	unsigned long flags;
63362306a36Sopenharmony_ci	u32 val, mask;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE :
63662306a36Sopenharmony_ci		IPU_CONF_CSI0_DATA_SOURCE;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, flags);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	val = ipu_cm_read(ipu, IPU_CONF);
64162306a36Sopenharmony_ci	if (mipi_csi2)
64262306a36Sopenharmony_ci		val |= mask;
64362306a36Sopenharmony_ci	else
64462306a36Sopenharmony_ci		val &= ~mask;
64562306a36Sopenharmony_ci	ipu_cm_write(ipu, val, IPU_CONF);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, flags);
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_set_csi_src_mux);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci/*
65262306a36Sopenharmony_ci * Set the source mux for the IC. Selects either CSI[01] or the VDI.
65362306a36Sopenharmony_ci */
65462306a36Sopenharmony_civoid ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
65562306a36Sopenharmony_ci{
65662306a36Sopenharmony_ci	unsigned long flags;
65762306a36Sopenharmony_ci	u32 val;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, flags);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	val = ipu_cm_read(ipu, IPU_CONF);
66262306a36Sopenharmony_ci	if (vdi)
66362306a36Sopenharmony_ci		val |= IPU_CONF_IC_INPUT;
66462306a36Sopenharmony_ci	else
66562306a36Sopenharmony_ci		val &= ~IPU_CONF_IC_INPUT;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	if (csi_id == 1)
66862306a36Sopenharmony_ci		val |= IPU_CONF_CSI_SEL;
66962306a36Sopenharmony_ci	else
67062306a36Sopenharmony_ci		val &= ~IPU_CONF_CSI_SEL;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	ipu_cm_write(ipu, val, IPU_CONF);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, flags);
67562306a36Sopenharmony_ci}
67662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci/* Frame Synchronization Unit Channel Linking */
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_cistruct fsu_link_reg_info {
68262306a36Sopenharmony_ci	int chno;
68362306a36Sopenharmony_ci	u32 reg;
68462306a36Sopenharmony_ci	u32 mask;
68562306a36Sopenharmony_ci	u32 val;
68662306a36Sopenharmony_ci};
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_cistruct fsu_link_info {
68962306a36Sopenharmony_ci	struct fsu_link_reg_info src;
69062306a36Sopenharmony_ci	struct fsu_link_reg_info sink;
69162306a36Sopenharmony_ci};
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic const struct fsu_link_info fsu_link_info[] = {
69462306a36Sopenharmony_ci	{
69562306a36Sopenharmony_ci		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2,
69662306a36Sopenharmony_ci			  FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC },
69762306a36Sopenharmony_ci		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1,
69862306a36Sopenharmony_ci			  FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC },
69962306a36Sopenharmony_ci	}, {
70062306a36Sopenharmony_ci		.src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2,
70162306a36Sopenharmony_ci			  FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF },
70262306a36Sopenharmony_ci		.sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1,
70362306a36Sopenharmony_ci			  FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF },
70462306a36Sopenharmony_ci	}, {
70562306a36Sopenharmony_ci		.src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2,
70662306a36Sopenharmony_ci			  FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP },
70762306a36Sopenharmony_ci		.sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1,
70862306a36Sopenharmony_ci			  FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP },
70962306a36Sopenharmony_ci	}, {
71062306a36Sopenharmony_ci		.src =  { IPUV3_CHANNEL_CSI_DIRECT, 0 },
71162306a36Sopenharmony_ci		.sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1,
71262306a36Sopenharmony_ci			  FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT },
71362306a36Sopenharmony_ci	},
71462306a36Sopenharmony_ci};
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_cistatic const struct fsu_link_info *find_fsu_link_info(int src, int sink)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	int i;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) {
72162306a36Sopenharmony_ci		if (src == fsu_link_info[i].src.chno &&
72262306a36Sopenharmony_ci		    sink == fsu_link_info[i].sink.chno)
72362306a36Sopenharmony_ci			return &fsu_link_info[i];
72462306a36Sopenharmony_ci	}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	return NULL;
72762306a36Sopenharmony_ci}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci/*
73062306a36Sopenharmony_ci * Links a source channel to a sink channel in the FSU.
73162306a36Sopenharmony_ci */
73262306a36Sopenharmony_ciint ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	const struct fsu_link_info *link;
73562306a36Sopenharmony_ci	u32 src_reg, sink_reg;
73662306a36Sopenharmony_ci	unsigned long flags;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	link = find_fsu_link_info(src_ch, sink_ch);
73962306a36Sopenharmony_ci	if (!link)
74062306a36Sopenharmony_ci		return -EINVAL;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, flags);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	if (link->src.mask) {
74562306a36Sopenharmony_ci		src_reg = ipu_cm_read(ipu, link->src.reg);
74662306a36Sopenharmony_ci		src_reg &= ~link->src.mask;
74762306a36Sopenharmony_ci		src_reg |= link->src.val;
74862306a36Sopenharmony_ci		ipu_cm_write(ipu, src_reg, link->src.reg);
74962306a36Sopenharmony_ci	}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	if (link->sink.mask) {
75262306a36Sopenharmony_ci		sink_reg = ipu_cm_read(ipu, link->sink.reg);
75362306a36Sopenharmony_ci		sink_reg &= ~link->sink.mask;
75462306a36Sopenharmony_ci		sink_reg |= link->sink.val;
75562306a36Sopenharmony_ci		ipu_cm_write(ipu, sink_reg, link->sink.reg);
75662306a36Sopenharmony_ci	}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, flags);
75962306a36Sopenharmony_ci	return 0;
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_fsu_link);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci/*
76462306a36Sopenharmony_ci * Unlinks source and sink channels in the FSU.
76562306a36Sopenharmony_ci */
76662306a36Sopenharmony_ciint ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch)
76762306a36Sopenharmony_ci{
76862306a36Sopenharmony_ci	const struct fsu_link_info *link;
76962306a36Sopenharmony_ci	u32 src_reg, sink_reg;
77062306a36Sopenharmony_ci	unsigned long flags;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	link = find_fsu_link_info(src_ch, sink_ch);
77362306a36Sopenharmony_ci	if (!link)
77462306a36Sopenharmony_ci		return -EINVAL;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, flags);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	if (link->src.mask) {
77962306a36Sopenharmony_ci		src_reg = ipu_cm_read(ipu, link->src.reg);
78062306a36Sopenharmony_ci		src_reg &= ~link->src.mask;
78162306a36Sopenharmony_ci		ipu_cm_write(ipu, src_reg, link->src.reg);
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	if (link->sink.mask) {
78562306a36Sopenharmony_ci		sink_reg = ipu_cm_read(ipu, link->sink.reg);
78662306a36Sopenharmony_ci		sink_reg &= ~link->sink.mask;
78762306a36Sopenharmony_ci		ipu_cm_write(ipu, sink_reg, link->sink.reg);
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, flags);
79162306a36Sopenharmony_ci	return 0;
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_fsu_unlink);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci/* Link IDMAC channels in the FSU */
79662306a36Sopenharmony_ciint ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)
79762306a36Sopenharmony_ci{
79862306a36Sopenharmony_ci	return ipu_fsu_link(src->ipu, src->num, sink->num);
79962306a36Sopenharmony_ci}
80062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_link);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci/* Unlink IDMAC channels in the FSU */
80362306a36Sopenharmony_ciint ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	return ipu_fsu_unlink(src->ipu, src->num, sink->num);
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_unlink);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_cistruct ipu_devtype {
81062306a36Sopenharmony_ci	const char *name;
81162306a36Sopenharmony_ci	unsigned long cm_ofs;
81262306a36Sopenharmony_ci	unsigned long cpmem_ofs;
81362306a36Sopenharmony_ci	unsigned long srm_ofs;
81462306a36Sopenharmony_ci	unsigned long tpm_ofs;
81562306a36Sopenharmony_ci	unsigned long csi0_ofs;
81662306a36Sopenharmony_ci	unsigned long csi1_ofs;
81762306a36Sopenharmony_ci	unsigned long ic_ofs;
81862306a36Sopenharmony_ci	unsigned long disp0_ofs;
81962306a36Sopenharmony_ci	unsigned long disp1_ofs;
82062306a36Sopenharmony_ci	unsigned long dc_tmpl_ofs;
82162306a36Sopenharmony_ci	unsigned long vdi_ofs;
82262306a36Sopenharmony_ci	enum ipuv3_type type;
82362306a36Sopenharmony_ci};
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_cistatic struct ipu_devtype ipu_type_imx51 = {
82662306a36Sopenharmony_ci	.name = "IPUv3EX",
82762306a36Sopenharmony_ci	.cm_ofs = 0x1e000000,
82862306a36Sopenharmony_ci	.cpmem_ofs = 0x1f000000,
82962306a36Sopenharmony_ci	.srm_ofs = 0x1f040000,
83062306a36Sopenharmony_ci	.tpm_ofs = 0x1f060000,
83162306a36Sopenharmony_ci	.csi0_ofs = 0x1e030000,
83262306a36Sopenharmony_ci	.csi1_ofs = 0x1e038000,
83362306a36Sopenharmony_ci	.ic_ofs = 0x1e020000,
83462306a36Sopenharmony_ci	.disp0_ofs = 0x1e040000,
83562306a36Sopenharmony_ci	.disp1_ofs = 0x1e048000,
83662306a36Sopenharmony_ci	.dc_tmpl_ofs = 0x1f080000,
83762306a36Sopenharmony_ci	.vdi_ofs = 0x1e068000,
83862306a36Sopenharmony_ci	.type = IPUV3EX,
83962306a36Sopenharmony_ci};
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_cistatic struct ipu_devtype ipu_type_imx53 = {
84262306a36Sopenharmony_ci	.name = "IPUv3M",
84362306a36Sopenharmony_ci	.cm_ofs = 0x06000000,
84462306a36Sopenharmony_ci	.cpmem_ofs = 0x07000000,
84562306a36Sopenharmony_ci	.srm_ofs = 0x07040000,
84662306a36Sopenharmony_ci	.tpm_ofs = 0x07060000,
84762306a36Sopenharmony_ci	.csi0_ofs = 0x06030000,
84862306a36Sopenharmony_ci	.csi1_ofs = 0x06038000,
84962306a36Sopenharmony_ci	.ic_ofs = 0x06020000,
85062306a36Sopenharmony_ci	.disp0_ofs = 0x06040000,
85162306a36Sopenharmony_ci	.disp1_ofs = 0x06048000,
85262306a36Sopenharmony_ci	.dc_tmpl_ofs = 0x07080000,
85362306a36Sopenharmony_ci	.vdi_ofs = 0x06068000,
85462306a36Sopenharmony_ci	.type = IPUV3M,
85562306a36Sopenharmony_ci};
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_cistatic struct ipu_devtype ipu_type_imx6q = {
85862306a36Sopenharmony_ci	.name = "IPUv3H",
85962306a36Sopenharmony_ci	.cm_ofs = 0x00200000,
86062306a36Sopenharmony_ci	.cpmem_ofs = 0x00300000,
86162306a36Sopenharmony_ci	.srm_ofs = 0x00340000,
86262306a36Sopenharmony_ci	.tpm_ofs = 0x00360000,
86362306a36Sopenharmony_ci	.csi0_ofs = 0x00230000,
86462306a36Sopenharmony_ci	.csi1_ofs = 0x00238000,
86562306a36Sopenharmony_ci	.ic_ofs = 0x00220000,
86662306a36Sopenharmony_ci	.disp0_ofs = 0x00240000,
86762306a36Sopenharmony_ci	.disp1_ofs = 0x00248000,
86862306a36Sopenharmony_ci	.dc_tmpl_ofs = 0x00380000,
86962306a36Sopenharmony_ci	.vdi_ofs = 0x00268000,
87062306a36Sopenharmony_ci	.type = IPUV3H,
87162306a36Sopenharmony_ci};
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_cistatic const struct of_device_id imx_ipu_dt_ids[] = {
87462306a36Sopenharmony_ci	{ .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, },
87562306a36Sopenharmony_ci	{ .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, },
87662306a36Sopenharmony_ci	{ .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, },
87762306a36Sopenharmony_ci	{ .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6q, },
87862306a36Sopenharmony_ci	{ /* sentinel */ }
87962306a36Sopenharmony_ci};
88062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, imx_ipu_dt_ids);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_cistatic int ipu_submodules_init(struct ipu_soc *ipu,
88362306a36Sopenharmony_ci		struct platform_device *pdev, unsigned long ipu_base,
88462306a36Sopenharmony_ci		struct clk *ipu_clk)
88562306a36Sopenharmony_ci{
88662306a36Sopenharmony_ci	char *unit;
88762306a36Sopenharmony_ci	int ret;
88862306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
88962306a36Sopenharmony_ci	const struct ipu_devtype *devtype = ipu->devtype;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs);
89262306a36Sopenharmony_ci	if (ret) {
89362306a36Sopenharmony_ci		unit = "cpmem";
89462306a36Sopenharmony_ci		goto err_cpmem;
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs,
89862306a36Sopenharmony_ci			   IPU_CONF_CSI0_EN, ipu_clk);
89962306a36Sopenharmony_ci	if (ret) {
90062306a36Sopenharmony_ci		unit = "csi0";
90162306a36Sopenharmony_ci		goto err_csi_0;
90262306a36Sopenharmony_ci	}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs,
90562306a36Sopenharmony_ci			   IPU_CONF_CSI1_EN, ipu_clk);
90662306a36Sopenharmony_ci	if (ret) {
90762306a36Sopenharmony_ci		unit = "csi1";
90862306a36Sopenharmony_ci		goto err_csi_1;
90962306a36Sopenharmony_ci	}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	ret = ipu_ic_init(ipu, dev,
91262306a36Sopenharmony_ci			  ipu_base + devtype->ic_ofs,
91362306a36Sopenharmony_ci			  ipu_base + devtype->tpm_ofs);
91462306a36Sopenharmony_ci	if (ret) {
91562306a36Sopenharmony_ci		unit = "ic";
91662306a36Sopenharmony_ci		goto err_ic;
91762306a36Sopenharmony_ci	}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs,
92062306a36Sopenharmony_ci			   IPU_CONF_VDI_EN | IPU_CONF_ISP_EN |
92162306a36Sopenharmony_ci			   IPU_CONF_IC_INPUT);
92262306a36Sopenharmony_ci	if (ret) {
92362306a36Sopenharmony_ci		unit = "vdi";
92462306a36Sopenharmony_ci		goto err_vdi;
92562306a36Sopenharmony_ci	}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	ret = ipu_image_convert_init(ipu, dev);
92862306a36Sopenharmony_ci	if (ret) {
92962306a36Sopenharmony_ci		unit = "image_convert";
93062306a36Sopenharmony_ci		goto err_image_convert;
93162306a36Sopenharmony_ci	}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
93462306a36Sopenharmony_ci			  IPU_CONF_DI0_EN, ipu_clk);
93562306a36Sopenharmony_ci	if (ret) {
93662306a36Sopenharmony_ci		unit = "di0";
93762306a36Sopenharmony_ci		goto err_di_0;
93862306a36Sopenharmony_ci	}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs,
94162306a36Sopenharmony_ci			IPU_CONF_DI1_EN, ipu_clk);
94262306a36Sopenharmony_ci	if (ret) {
94362306a36Sopenharmony_ci		unit = "di1";
94462306a36Sopenharmony_ci		goto err_di_1;
94562306a36Sopenharmony_ci	}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs +
94862306a36Sopenharmony_ci			IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs);
94962306a36Sopenharmony_ci	if (ret) {
95062306a36Sopenharmony_ci		unit = "dc_template";
95162306a36Sopenharmony_ci		goto err_dc;
95262306a36Sopenharmony_ci	}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	ret = ipu_dmfc_init(ipu, dev, ipu_base +
95562306a36Sopenharmony_ci			devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk);
95662306a36Sopenharmony_ci	if (ret) {
95762306a36Sopenharmony_ci		unit = "dmfc";
95862306a36Sopenharmony_ci		goto err_dmfc;
95962306a36Sopenharmony_ci	}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs);
96262306a36Sopenharmony_ci	if (ret) {
96362306a36Sopenharmony_ci		unit = "dp";
96462306a36Sopenharmony_ci		goto err_dp;
96562306a36Sopenharmony_ci	}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	ret = ipu_smfc_init(ipu, dev, ipu_base +
96862306a36Sopenharmony_ci			devtype->cm_ofs + IPU_CM_SMFC_REG_OFS);
96962306a36Sopenharmony_ci	if (ret) {
97062306a36Sopenharmony_ci		unit = "smfc";
97162306a36Sopenharmony_ci		goto err_smfc;
97262306a36Sopenharmony_ci	}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	return 0;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_cierr_smfc:
97762306a36Sopenharmony_ci	ipu_dp_exit(ipu);
97862306a36Sopenharmony_cierr_dp:
97962306a36Sopenharmony_ci	ipu_dmfc_exit(ipu);
98062306a36Sopenharmony_cierr_dmfc:
98162306a36Sopenharmony_ci	ipu_dc_exit(ipu);
98262306a36Sopenharmony_cierr_dc:
98362306a36Sopenharmony_ci	ipu_di_exit(ipu, 1);
98462306a36Sopenharmony_cierr_di_1:
98562306a36Sopenharmony_ci	ipu_di_exit(ipu, 0);
98662306a36Sopenharmony_cierr_di_0:
98762306a36Sopenharmony_ci	ipu_image_convert_exit(ipu);
98862306a36Sopenharmony_cierr_image_convert:
98962306a36Sopenharmony_ci	ipu_vdi_exit(ipu);
99062306a36Sopenharmony_cierr_vdi:
99162306a36Sopenharmony_ci	ipu_ic_exit(ipu);
99262306a36Sopenharmony_cierr_ic:
99362306a36Sopenharmony_ci	ipu_csi_exit(ipu, 1);
99462306a36Sopenharmony_cierr_csi_1:
99562306a36Sopenharmony_ci	ipu_csi_exit(ipu, 0);
99662306a36Sopenharmony_cierr_csi_0:
99762306a36Sopenharmony_ci	ipu_cpmem_exit(ipu);
99862306a36Sopenharmony_cierr_cpmem:
99962306a36Sopenharmony_ci	dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret);
100062306a36Sopenharmony_ci	return ret;
100162306a36Sopenharmony_ci}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_cistatic void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs)
100462306a36Sopenharmony_ci{
100562306a36Sopenharmony_ci	unsigned long status;
100662306a36Sopenharmony_ci	int i, bit;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	for (i = 0; i < num_regs; i++) {
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci		status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i]));
101162306a36Sopenharmony_ci		status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i]));
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci		for_each_set_bit(bit, &status, 32)
101462306a36Sopenharmony_ci			generic_handle_domain_irq(ipu->domain,
101562306a36Sopenharmony_ci						  regs[i] * 32 + bit);
101662306a36Sopenharmony_ci	}
101762306a36Sopenharmony_ci}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_cistatic void ipu_irq_handler(struct irq_desc *desc)
102062306a36Sopenharmony_ci{
102162306a36Sopenharmony_ci	struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
102262306a36Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
102362306a36Sopenharmony_ci	static const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14};
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	chained_irq_enter(chip, desc);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	chained_irq_exit(chip, desc);
103062306a36Sopenharmony_ci}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_cistatic void ipu_err_irq_handler(struct irq_desc *desc)
103362306a36Sopenharmony_ci{
103462306a36Sopenharmony_ci	struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
103562306a36Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
103662306a36Sopenharmony_ci	static const int int_reg[] = { 4, 5, 8, 9};
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	chained_irq_enter(chip, desc);
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	chained_irq_exit(chip, desc);
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ciint ipu_map_irq(struct ipu_soc *ipu, int irq)
104662306a36Sopenharmony_ci{
104762306a36Sopenharmony_ci	int virq;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	virq = irq_linear_revmap(ipu->domain, irq);
105062306a36Sopenharmony_ci	if (!virq)
105162306a36Sopenharmony_ci		virq = irq_create_mapping(ipu->domain, irq);
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	return virq;
105462306a36Sopenharmony_ci}
105562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_map_irq);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ciint ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
105862306a36Sopenharmony_ci		enum ipu_channel_irq irq_type)
105962306a36Sopenharmony_ci{
106062306a36Sopenharmony_ci	return ipu_map_irq(ipu, irq_type + channel->num);
106162306a36Sopenharmony_ci}
106262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_channel_irq);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_cistatic void ipu_submodules_exit(struct ipu_soc *ipu)
106562306a36Sopenharmony_ci{
106662306a36Sopenharmony_ci	ipu_smfc_exit(ipu);
106762306a36Sopenharmony_ci	ipu_dp_exit(ipu);
106862306a36Sopenharmony_ci	ipu_dmfc_exit(ipu);
106962306a36Sopenharmony_ci	ipu_dc_exit(ipu);
107062306a36Sopenharmony_ci	ipu_di_exit(ipu, 1);
107162306a36Sopenharmony_ci	ipu_di_exit(ipu, 0);
107262306a36Sopenharmony_ci	ipu_image_convert_exit(ipu);
107362306a36Sopenharmony_ci	ipu_vdi_exit(ipu);
107462306a36Sopenharmony_ci	ipu_ic_exit(ipu);
107562306a36Sopenharmony_ci	ipu_csi_exit(ipu, 1);
107662306a36Sopenharmony_ci	ipu_csi_exit(ipu, 0);
107762306a36Sopenharmony_ci	ipu_cpmem_exit(ipu);
107862306a36Sopenharmony_ci}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_cistatic int platform_remove_devices_fn(struct device *dev, void *unused)
108162306a36Sopenharmony_ci{
108262306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	platform_device_unregister(pdev);
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	return 0;
108762306a36Sopenharmony_ci}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_cistatic void platform_device_unregister_children(struct platform_device *pdev)
109062306a36Sopenharmony_ci{
109162306a36Sopenharmony_ci	device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn);
109262306a36Sopenharmony_ci}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_cistruct ipu_platform_reg {
109562306a36Sopenharmony_ci	struct ipu_client_platformdata pdata;
109662306a36Sopenharmony_ci	const char *name;
109762306a36Sopenharmony_ci};
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci/* These must be in the order of the corresponding device tree port nodes */
110062306a36Sopenharmony_cistatic struct ipu_platform_reg client_reg[] = {
110162306a36Sopenharmony_ci	{
110262306a36Sopenharmony_ci		.pdata = {
110362306a36Sopenharmony_ci			.csi = 0,
110462306a36Sopenharmony_ci			.dma[0] = IPUV3_CHANNEL_CSI0,
110562306a36Sopenharmony_ci			.dma[1] = -EINVAL,
110662306a36Sopenharmony_ci		},
110762306a36Sopenharmony_ci		.name = "imx-ipuv3-csi",
110862306a36Sopenharmony_ci	}, {
110962306a36Sopenharmony_ci		.pdata = {
111062306a36Sopenharmony_ci			.csi = 1,
111162306a36Sopenharmony_ci			.dma[0] = IPUV3_CHANNEL_CSI1,
111262306a36Sopenharmony_ci			.dma[1] = -EINVAL,
111362306a36Sopenharmony_ci		},
111462306a36Sopenharmony_ci		.name = "imx-ipuv3-csi",
111562306a36Sopenharmony_ci	}, {
111662306a36Sopenharmony_ci		.pdata = {
111762306a36Sopenharmony_ci			.di = 0,
111862306a36Sopenharmony_ci			.dc = 5,
111962306a36Sopenharmony_ci			.dp = IPU_DP_FLOW_SYNC_BG,
112062306a36Sopenharmony_ci			.dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC,
112162306a36Sopenharmony_ci			.dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC,
112262306a36Sopenharmony_ci		},
112362306a36Sopenharmony_ci		.name = "imx-ipuv3-crtc",
112462306a36Sopenharmony_ci	}, {
112562306a36Sopenharmony_ci		.pdata = {
112662306a36Sopenharmony_ci			.di = 1,
112762306a36Sopenharmony_ci			.dc = 1,
112862306a36Sopenharmony_ci			.dp = -EINVAL,
112962306a36Sopenharmony_ci			.dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC,
113062306a36Sopenharmony_ci			.dma[1] = -EINVAL,
113162306a36Sopenharmony_ci		},
113262306a36Sopenharmony_ci		.name = "imx-ipuv3-crtc",
113362306a36Sopenharmony_ci	},
113462306a36Sopenharmony_ci};
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_cistatic DEFINE_MUTEX(ipu_client_id_mutex);
113762306a36Sopenharmony_cistatic int ipu_client_id;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_cistatic int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base)
114062306a36Sopenharmony_ci{
114162306a36Sopenharmony_ci	struct device *dev = ipu->dev;
114262306a36Sopenharmony_ci	unsigned i;
114362306a36Sopenharmony_ci	int id, ret;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	mutex_lock(&ipu_client_id_mutex);
114662306a36Sopenharmony_ci	id = ipu_client_id;
114762306a36Sopenharmony_ci	ipu_client_id += ARRAY_SIZE(client_reg);
114862306a36Sopenharmony_ci	mutex_unlock(&ipu_client_id_mutex);
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(client_reg); i++) {
115162306a36Sopenharmony_ci		struct ipu_platform_reg *reg = &client_reg[i];
115262306a36Sopenharmony_ci		struct platform_device *pdev;
115362306a36Sopenharmony_ci		struct device_node *of_node;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci		/* Associate subdevice with the corresponding port node */
115662306a36Sopenharmony_ci		of_node = of_graph_get_port_by_id(dev->of_node, i);
115762306a36Sopenharmony_ci		if (!of_node) {
115862306a36Sopenharmony_ci			dev_info(dev,
115962306a36Sopenharmony_ci				 "no port@%d node in %pOF, not using %s%d\n",
116062306a36Sopenharmony_ci				 i, dev->of_node,
116162306a36Sopenharmony_ci				 (i / 2) ? "DI" : "CSI", i % 2);
116262306a36Sopenharmony_ci			continue;
116362306a36Sopenharmony_ci		}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci		pdev = platform_device_alloc(reg->name, id++);
116662306a36Sopenharmony_ci		if (!pdev) {
116762306a36Sopenharmony_ci			ret = -ENOMEM;
116862306a36Sopenharmony_ci			of_node_put(of_node);
116962306a36Sopenharmony_ci			goto err_register;
117062306a36Sopenharmony_ci		}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci		pdev->dev.parent = dev;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci		reg->pdata.of_node = of_node;
117562306a36Sopenharmony_ci		ret = platform_device_add_data(pdev, &reg->pdata,
117662306a36Sopenharmony_ci					       sizeof(reg->pdata));
117762306a36Sopenharmony_ci		if (!ret)
117862306a36Sopenharmony_ci			ret = platform_device_add(pdev);
117962306a36Sopenharmony_ci		if (ret) {
118062306a36Sopenharmony_ci			platform_device_put(pdev);
118162306a36Sopenharmony_ci			goto err_register;
118262306a36Sopenharmony_ci		}
118362306a36Sopenharmony_ci	}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	return 0;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_cierr_register:
118862306a36Sopenharmony_ci	platform_device_unregister_children(to_platform_device(dev));
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	return ret;
119162306a36Sopenharmony_ci}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_cistatic int ipu_irq_init(struct ipu_soc *ipu)
119562306a36Sopenharmony_ci{
119662306a36Sopenharmony_ci	struct irq_chip_generic *gc;
119762306a36Sopenharmony_ci	struct irq_chip_type *ct;
119862306a36Sopenharmony_ci	unsigned long unused[IPU_NUM_IRQS / 32] = {
119962306a36Sopenharmony_ci		0x400100d0, 0xffe000fd,
120062306a36Sopenharmony_ci		0x400100d0, 0xffe000fd,
120162306a36Sopenharmony_ci		0x400100d0, 0xffe000fd,
120262306a36Sopenharmony_ci		0x4077ffff, 0xffe7e1fd,
120362306a36Sopenharmony_ci		0x23fffffe, 0x8880fff0,
120462306a36Sopenharmony_ci		0xf98fe7d0, 0xfff81fff,
120562306a36Sopenharmony_ci		0x400100d0, 0xffe000fd,
120662306a36Sopenharmony_ci		0x00000000,
120762306a36Sopenharmony_ci	};
120862306a36Sopenharmony_ci	int ret, i;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS,
121162306a36Sopenharmony_ci					    &irq_generic_chip_ops, ipu);
121262306a36Sopenharmony_ci	if (!ipu->domain) {
121362306a36Sopenharmony_ci		dev_err(ipu->dev, "failed to add irq domain\n");
121462306a36Sopenharmony_ci		return -ENODEV;
121562306a36Sopenharmony_ci	}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU",
121862306a36Sopenharmony_ci					     handle_level_irq, 0, 0, 0);
121962306a36Sopenharmony_ci	if (ret < 0) {
122062306a36Sopenharmony_ci		dev_err(ipu->dev, "failed to alloc generic irq chips\n");
122162306a36Sopenharmony_ci		irq_domain_remove(ipu->domain);
122262306a36Sopenharmony_ci		return ret;
122362306a36Sopenharmony_ci	}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	/* Mask and clear all interrupts */
122662306a36Sopenharmony_ci	for (i = 0; i < IPU_NUM_IRQS; i += 32) {
122762306a36Sopenharmony_ci		ipu_cm_write(ipu, 0, IPU_INT_CTRL(i / 32));
122862306a36Sopenharmony_ci		ipu_cm_write(ipu, ~unused[i / 32], IPU_INT_STAT(i / 32));
122962306a36Sopenharmony_ci	}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	for (i = 0; i < IPU_NUM_IRQS; i += 32) {
123262306a36Sopenharmony_ci		gc = irq_get_domain_generic_chip(ipu->domain, i);
123362306a36Sopenharmony_ci		gc->reg_base = ipu->cm_reg;
123462306a36Sopenharmony_ci		gc->unused = unused[i / 32];
123562306a36Sopenharmony_ci		ct = gc->chip_types;
123662306a36Sopenharmony_ci		ct->chip.irq_ack = irq_gc_ack_set_bit;
123762306a36Sopenharmony_ci		ct->chip.irq_mask = irq_gc_mask_clr_bit;
123862306a36Sopenharmony_ci		ct->chip.irq_unmask = irq_gc_mask_set_bit;
123962306a36Sopenharmony_ci		ct->regs.ack = IPU_INT_STAT(i / 32);
124062306a36Sopenharmony_ci		ct->regs.mask = IPU_INT_CTRL(i / 32);
124162306a36Sopenharmony_ci	}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu);
124462306a36Sopenharmony_ci	irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler,
124562306a36Sopenharmony_ci					 ipu);
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	return 0;
124862306a36Sopenharmony_ci}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_cistatic void ipu_irq_exit(struct ipu_soc *ipu)
125162306a36Sopenharmony_ci{
125262306a36Sopenharmony_ci	int i, irq;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL);
125562306a36Sopenharmony_ci	irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	/* TODO: remove irq_domain_generic_chips */
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	for (i = 0; i < IPU_NUM_IRQS; i++) {
126062306a36Sopenharmony_ci		irq = irq_linear_revmap(ipu->domain, i);
126162306a36Sopenharmony_ci		if (irq)
126262306a36Sopenharmony_ci			irq_dispose_mapping(irq);
126362306a36Sopenharmony_ci	}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	irq_domain_remove(ipu->domain);
126662306a36Sopenharmony_ci}
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_civoid ipu_dump(struct ipu_soc *ipu)
126962306a36Sopenharmony_ci{
127062306a36Sopenharmony_ci	int i;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n",
127362306a36Sopenharmony_ci		ipu_cm_read(ipu, IPU_CONF));
127462306a36Sopenharmony_ci	dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n",
127562306a36Sopenharmony_ci		ipu_idmac_read(ipu, IDMAC_CONF));
127662306a36Sopenharmony_ci	dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n",
127762306a36Sopenharmony_ci		ipu_idmac_read(ipu, IDMAC_CHA_EN(0)));
127862306a36Sopenharmony_ci	dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n",
127962306a36Sopenharmony_ci		ipu_idmac_read(ipu, IDMAC_CHA_EN(32)));
128062306a36Sopenharmony_ci	dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n",
128162306a36Sopenharmony_ci		ipu_idmac_read(ipu, IDMAC_CHA_PRI(0)));
128262306a36Sopenharmony_ci	dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n",
128362306a36Sopenharmony_ci		ipu_idmac_read(ipu, IDMAC_CHA_PRI(32)));
128462306a36Sopenharmony_ci	dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n",
128562306a36Sopenharmony_ci		ipu_idmac_read(ipu, IDMAC_BAND_EN(0)));
128662306a36Sopenharmony_ci	dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n",
128762306a36Sopenharmony_ci		ipu_idmac_read(ipu, IDMAC_BAND_EN(32)));
128862306a36Sopenharmony_ci	dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n",
128962306a36Sopenharmony_ci		ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0)));
129062306a36Sopenharmony_ci	dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n",
129162306a36Sopenharmony_ci		ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32)));
129262306a36Sopenharmony_ci	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n",
129362306a36Sopenharmony_ci		ipu_cm_read(ipu, IPU_FS_PROC_FLOW1));
129462306a36Sopenharmony_ci	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n",
129562306a36Sopenharmony_ci		ipu_cm_read(ipu, IPU_FS_PROC_FLOW2));
129662306a36Sopenharmony_ci	dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n",
129762306a36Sopenharmony_ci		ipu_cm_read(ipu, IPU_FS_PROC_FLOW3));
129862306a36Sopenharmony_ci	dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n",
129962306a36Sopenharmony_ci		ipu_cm_read(ipu, IPU_FS_DISP_FLOW1));
130062306a36Sopenharmony_ci	for (i = 0; i < 15; i++)
130162306a36Sopenharmony_ci		dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i,
130262306a36Sopenharmony_ci			ipu_cm_read(ipu, IPU_INT_CTRL(i)));
130362306a36Sopenharmony_ci}
130462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_dump);
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_cistatic int ipu_probe(struct platform_device *pdev)
130762306a36Sopenharmony_ci{
130862306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
130962306a36Sopenharmony_ci	struct ipu_soc *ipu;
131062306a36Sopenharmony_ci	struct resource *res;
131162306a36Sopenharmony_ci	unsigned long ipu_base;
131262306a36Sopenharmony_ci	int ret, irq_sync, irq_err;
131362306a36Sopenharmony_ci	const struct ipu_devtype *devtype;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	devtype = of_device_get_match_data(&pdev->dev);
131662306a36Sopenharmony_ci	if (!devtype)
131762306a36Sopenharmony_ci		return -EINVAL;
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	irq_sync = platform_get_irq(pdev, 0);
132062306a36Sopenharmony_ci	irq_err = platform_get_irq(pdev, 1);
132162306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n",
132462306a36Sopenharmony_ci			irq_sync, irq_err);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	if (!res || irq_sync < 0 || irq_err < 0)
132762306a36Sopenharmony_ci		return -ENODEV;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	ipu_base = res->start;
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL);
133262306a36Sopenharmony_ci	if (!ipu)
133362306a36Sopenharmony_ci		return -ENODEV;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	ipu->id = of_alias_get_id(np, "ipu");
133662306a36Sopenharmony_ci	if (ipu->id < 0)
133762306a36Sopenharmony_ci		ipu->id = 0;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	if (of_device_is_compatible(np, "fsl,imx6qp-ipu") &&
134062306a36Sopenharmony_ci	    IS_ENABLED(CONFIG_DRM)) {
134162306a36Sopenharmony_ci		ipu->prg_priv = ipu_prg_lookup_by_phandle(&pdev->dev,
134262306a36Sopenharmony_ci							  "fsl,prg", ipu->id);
134362306a36Sopenharmony_ci		if (!ipu->prg_priv)
134462306a36Sopenharmony_ci			return -EPROBE_DEFER;
134562306a36Sopenharmony_ci	}
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	ipu->devtype = devtype;
134862306a36Sopenharmony_ci	ipu->ipu_type = devtype->type;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	spin_lock_init(&ipu->lock);
135162306a36Sopenharmony_ci	mutex_init(&ipu->channel_lock);
135262306a36Sopenharmony_ci	INIT_LIST_HEAD(&ipu->channels);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "cm_reg:   0x%08lx\n",
135562306a36Sopenharmony_ci			ipu_base + devtype->cm_ofs);
135662306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "idmac:    0x%08lx\n",
135762306a36Sopenharmony_ci			ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS);
135862306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "cpmem:    0x%08lx\n",
135962306a36Sopenharmony_ci			ipu_base + devtype->cpmem_ofs);
136062306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "csi0:    0x%08lx\n",
136162306a36Sopenharmony_ci			ipu_base + devtype->csi0_ofs);
136262306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "csi1:    0x%08lx\n",
136362306a36Sopenharmony_ci			ipu_base + devtype->csi1_ofs);
136462306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "ic:      0x%08lx\n",
136562306a36Sopenharmony_ci			ipu_base + devtype->ic_ofs);
136662306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "disp0:    0x%08lx\n",
136762306a36Sopenharmony_ci			ipu_base + devtype->disp0_ofs);
136862306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "disp1:    0x%08lx\n",
136962306a36Sopenharmony_ci			ipu_base + devtype->disp1_ofs);
137062306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "srm:      0x%08lx\n",
137162306a36Sopenharmony_ci			ipu_base + devtype->srm_ofs);
137262306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "tpm:      0x%08lx\n",
137362306a36Sopenharmony_ci			ipu_base + devtype->tpm_ofs);
137462306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "dc:       0x%08lx\n",
137562306a36Sopenharmony_ci			ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS);
137662306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "ic:       0x%08lx\n",
137762306a36Sopenharmony_ci			ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS);
137862306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "dmfc:     0x%08lx\n",
137962306a36Sopenharmony_ci			ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS);
138062306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "vdi:      0x%08lx\n",
138162306a36Sopenharmony_ci			ipu_base + devtype->vdi_ofs);
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	ipu->cm_reg = devm_ioremap(&pdev->dev,
138462306a36Sopenharmony_ci			ipu_base + devtype->cm_ofs, PAGE_SIZE);
138562306a36Sopenharmony_ci	ipu->idmac_reg = devm_ioremap(&pdev->dev,
138662306a36Sopenharmony_ci			ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS,
138762306a36Sopenharmony_ci			PAGE_SIZE);
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	if (!ipu->cm_reg || !ipu->idmac_reg)
139062306a36Sopenharmony_ci		return -ENOMEM;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	ipu->clk = devm_clk_get(&pdev->dev, "bus");
139362306a36Sopenharmony_ci	if (IS_ERR(ipu->clk)) {
139462306a36Sopenharmony_ci		ret = PTR_ERR(ipu->clk);
139562306a36Sopenharmony_ci		dev_err(&pdev->dev, "clk_get failed with %d", ret);
139662306a36Sopenharmony_ci		return ret;
139762306a36Sopenharmony_ci	}
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	platform_set_drvdata(pdev, ipu);
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	ret = clk_prepare_enable(ipu->clk);
140262306a36Sopenharmony_ci	if (ret) {
140362306a36Sopenharmony_ci		dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
140462306a36Sopenharmony_ci		return ret;
140562306a36Sopenharmony_ci	}
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	ipu->dev = &pdev->dev;
140862306a36Sopenharmony_ci	ipu->irq_sync = irq_sync;
140962306a36Sopenharmony_ci	ipu->irq_err = irq_err;
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	ret = device_reset(&pdev->dev);
141262306a36Sopenharmony_ci	if (ret) {
141362306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to reset: %d\n", ret);
141462306a36Sopenharmony_ci		goto out_failed_reset;
141562306a36Sopenharmony_ci	}
141662306a36Sopenharmony_ci	ret = ipu_memory_reset(ipu);
141762306a36Sopenharmony_ci	if (ret)
141862306a36Sopenharmony_ci		goto out_failed_reset;
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	ret = ipu_irq_init(ipu);
142162306a36Sopenharmony_ci	if (ret)
142262306a36Sopenharmony_ci		goto out_failed_irq;
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	/* Set MCU_T to divide MCU access window into 2 */
142562306a36Sopenharmony_ci	ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
142662306a36Sopenharmony_ci			IPU_DISP_GEN);
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk);
142962306a36Sopenharmony_ci	if (ret)
143062306a36Sopenharmony_ci		goto failed_submodules_init;
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	ret = ipu_add_client_devices(ipu, ipu_base);
143362306a36Sopenharmony_ci	if (ret) {
143462306a36Sopenharmony_ci		dev_err(&pdev->dev, "adding client devices failed with %d\n",
143562306a36Sopenharmony_ci				ret);
143662306a36Sopenharmony_ci		goto failed_add_clients;
143762306a36Sopenharmony_ci	}
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	dev_info(&pdev->dev, "%s probed\n", devtype->name);
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	return 0;
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_cifailed_add_clients:
144462306a36Sopenharmony_ci	ipu_submodules_exit(ipu);
144562306a36Sopenharmony_cifailed_submodules_init:
144662306a36Sopenharmony_ci	ipu_irq_exit(ipu);
144762306a36Sopenharmony_ciout_failed_irq:
144862306a36Sopenharmony_ciout_failed_reset:
144962306a36Sopenharmony_ci	clk_disable_unprepare(ipu->clk);
145062306a36Sopenharmony_ci	return ret;
145162306a36Sopenharmony_ci}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_cistatic int ipu_remove(struct platform_device *pdev)
145462306a36Sopenharmony_ci{
145562306a36Sopenharmony_ci	struct ipu_soc *ipu = platform_get_drvdata(pdev);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	platform_device_unregister_children(pdev);
145862306a36Sopenharmony_ci	ipu_submodules_exit(ipu);
145962306a36Sopenharmony_ci	ipu_irq_exit(ipu);
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	clk_disable_unprepare(ipu->clk);
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	return 0;
146462306a36Sopenharmony_ci}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_cistatic struct platform_driver imx_ipu_driver = {
146762306a36Sopenharmony_ci	.driver = {
146862306a36Sopenharmony_ci		.name = "imx-ipuv3",
146962306a36Sopenharmony_ci		.of_match_table = imx_ipu_dt_ids,
147062306a36Sopenharmony_ci	},
147162306a36Sopenharmony_ci	.probe = ipu_probe,
147262306a36Sopenharmony_ci	.remove = ipu_remove,
147362306a36Sopenharmony_ci};
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_cistatic struct platform_driver * const drivers[] = {
147662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_DRM)
147762306a36Sopenharmony_ci	&ipu_pre_drv,
147862306a36Sopenharmony_ci	&ipu_prg_drv,
147962306a36Sopenharmony_ci#endif
148062306a36Sopenharmony_ci	&imx_ipu_driver,
148162306a36Sopenharmony_ci};
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_cistatic int __init imx_ipu_init(void)
148462306a36Sopenharmony_ci{
148562306a36Sopenharmony_ci	return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
148662306a36Sopenharmony_ci}
148762306a36Sopenharmony_cimodule_init(imx_ipu_init);
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_cistatic void __exit imx_ipu_exit(void)
149062306a36Sopenharmony_ci{
149162306a36Sopenharmony_ci	platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
149262306a36Sopenharmony_ci}
149362306a36Sopenharmony_cimodule_exit(imx_ipu_exit);
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ciMODULE_ALIAS("platform:imx-ipuv3");
149662306a36Sopenharmony_ciMODULE_DESCRIPTION("i.MX IPU v3 driver");
149762306a36Sopenharmony_ciMODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
149862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1499