xref: /openbmc/linux/drivers/gpu/ipu-v3/ipu-common.c (revision 39b9004d)
139b9004dSPhilipp Zabel /*
239b9004dSPhilipp Zabel  * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
339b9004dSPhilipp Zabel  * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
439b9004dSPhilipp Zabel  *
539b9004dSPhilipp Zabel  * This program is free software; you can redistribute it and/or modify it
639b9004dSPhilipp Zabel  * under the terms of the GNU General Public License as published by the
739b9004dSPhilipp Zabel  * Free Software Foundation; either version 2 of the License, or (at your
839b9004dSPhilipp Zabel  * option) any later version.
939b9004dSPhilipp Zabel  *
1039b9004dSPhilipp Zabel  * This program is distributed in the hope that it will be useful, but
1139b9004dSPhilipp Zabel  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
1239b9004dSPhilipp Zabel  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1339b9004dSPhilipp Zabel  * for more details.
1439b9004dSPhilipp Zabel  */
1539b9004dSPhilipp Zabel #include <linux/module.h>
1639b9004dSPhilipp Zabel #include <linux/export.h>
1739b9004dSPhilipp Zabel #include <linux/types.h>
1839b9004dSPhilipp Zabel #include <linux/reset.h>
1939b9004dSPhilipp Zabel #include <linux/platform_device.h>
2039b9004dSPhilipp Zabel #include <linux/err.h>
2139b9004dSPhilipp Zabel #include <linux/spinlock.h>
2239b9004dSPhilipp Zabel #include <linux/delay.h>
2339b9004dSPhilipp Zabel #include <linux/interrupt.h>
2439b9004dSPhilipp Zabel #include <linux/io.h>
2539b9004dSPhilipp Zabel #include <linux/clk.h>
2639b9004dSPhilipp Zabel #include <linux/list.h>
2739b9004dSPhilipp Zabel #include <linux/irq.h>
2839b9004dSPhilipp Zabel #include <linux/irqchip/chained_irq.h>
2939b9004dSPhilipp Zabel #include <linux/irqdomain.h>
3039b9004dSPhilipp Zabel #include <linux/of_device.h>
3139b9004dSPhilipp Zabel 
3239b9004dSPhilipp Zabel #include <drm/drm_fourcc.h>
3339b9004dSPhilipp Zabel 
3439b9004dSPhilipp Zabel #include <video/imx-ipu-v3.h>
3539b9004dSPhilipp Zabel #include "ipu-prv.h"
3639b9004dSPhilipp Zabel 
3739b9004dSPhilipp Zabel static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset)
3839b9004dSPhilipp Zabel {
3939b9004dSPhilipp Zabel 	return readl(ipu->cm_reg + offset);
4039b9004dSPhilipp Zabel }
4139b9004dSPhilipp Zabel 
4239b9004dSPhilipp Zabel static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset)
4339b9004dSPhilipp Zabel {
4439b9004dSPhilipp Zabel 	writel(value, ipu->cm_reg + offset);
4539b9004dSPhilipp Zabel }
4639b9004dSPhilipp Zabel 
4739b9004dSPhilipp Zabel static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset)
4839b9004dSPhilipp Zabel {
4939b9004dSPhilipp Zabel 	return readl(ipu->idmac_reg + offset);
5039b9004dSPhilipp Zabel }
5139b9004dSPhilipp Zabel 
5239b9004dSPhilipp Zabel static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value,
5339b9004dSPhilipp Zabel 		unsigned offset)
5439b9004dSPhilipp Zabel {
5539b9004dSPhilipp Zabel 	writel(value, ipu->idmac_reg + offset);
5639b9004dSPhilipp Zabel }
5739b9004dSPhilipp Zabel 
5839b9004dSPhilipp Zabel void ipu_srm_dp_sync_update(struct ipu_soc *ipu)
5939b9004dSPhilipp Zabel {
6039b9004dSPhilipp Zabel 	u32 val;
6139b9004dSPhilipp Zabel 
6239b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_SRM_PRI2);
6339b9004dSPhilipp Zabel 	val |= 0x8;
6439b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_SRM_PRI2);
6539b9004dSPhilipp Zabel }
6639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_srm_dp_sync_update);
6739b9004dSPhilipp Zabel 
6839b9004dSPhilipp Zabel struct ipu_ch_param __iomem *ipu_get_cpmem(struct ipuv3_channel *channel)
6939b9004dSPhilipp Zabel {
7039b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
7139b9004dSPhilipp Zabel 
7239b9004dSPhilipp Zabel 	return ipu->cpmem_base + channel->num;
7339b9004dSPhilipp Zabel }
7439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_get_cpmem);
7539b9004dSPhilipp Zabel 
7639b9004dSPhilipp Zabel void ipu_cpmem_set_high_priority(struct ipuv3_channel *channel)
7739b9004dSPhilipp Zabel {
7839b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
7939b9004dSPhilipp Zabel 	struct ipu_ch_param __iomem *p = ipu_get_cpmem(channel);
8039b9004dSPhilipp Zabel 	u32 val;
8139b9004dSPhilipp Zabel 
8239b9004dSPhilipp Zabel 	if (ipu->ipu_type == IPUV3EX)
8339b9004dSPhilipp Zabel 		ipu_ch_param_write_field(p, IPU_FIELD_ID, 1);
8439b9004dSPhilipp Zabel 
8539b9004dSPhilipp Zabel 	val = ipu_idmac_read(ipu, IDMAC_CHA_PRI(channel->num));
8639b9004dSPhilipp Zabel 	val |= 1 << (channel->num % 32);
8739b9004dSPhilipp Zabel 	ipu_idmac_write(ipu, val, IDMAC_CHA_PRI(channel->num));
8839b9004dSPhilipp Zabel };
8939b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_cpmem_set_high_priority);
9039b9004dSPhilipp Zabel 
9139b9004dSPhilipp Zabel void ipu_ch_param_write_field(struct ipu_ch_param __iomem *base, u32 wbs, u32 v)
9239b9004dSPhilipp Zabel {
9339b9004dSPhilipp Zabel 	u32 bit = (wbs >> 8) % 160;
9439b9004dSPhilipp Zabel 	u32 size = wbs & 0xff;
9539b9004dSPhilipp Zabel 	u32 word = (wbs >> 8) / 160;
9639b9004dSPhilipp Zabel 	u32 i = bit / 32;
9739b9004dSPhilipp Zabel 	u32 ofs = bit % 32;
9839b9004dSPhilipp Zabel 	u32 mask = (1 << size) - 1;
9939b9004dSPhilipp Zabel 	u32 val;
10039b9004dSPhilipp Zabel 
10139b9004dSPhilipp Zabel 	pr_debug("%s %d %d %d\n", __func__, word, bit , size);
10239b9004dSPhilipp Zabel 
10339b9004dSPhilipp Zabel 	val = readl(&base->word[word].data[i]);
10439b9004dSPhilipp Zabel 	val &= ~(mask << ofs);
10539b9004dSPhilipp Zabel 	val |= v << ofs;
10639b9004dSPhilipp Zabel 	writel(val, &base->word[word].data[i]);
10739b9004dSPhilipp Zabel 
10839b9004dSPhilipp Zabel 	if ((bit + size - 1) / 32 > i) {
10939b9004dSPhilipp Zabel 		val = readl(&base->word[word].data[i + 1]);
11039b9004dSPhilipp Zabel 		val &= ~(mask >> (ofs ? (32 - ofs) : 0));
11139b9004dSPhilipp Zabel 		val |= v >> (ofs ? (32 - ofs) : 0);
11239b9004dSPhilipp Zabel 		writel(val, &base->word[word].data[i + 1]);
11339b9004dSPhilipp Zabel 	}
11439b9004dSPhilipp Zabel }
11539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_ch_param_write_field);
11639b9004dSPhilipp Zabel 
11739b9004dSPhilipp Zabel u32 ipu_ch_param_read_field(struct ipu_ch_param __iomem *base, u32 wbs)
11839b9004dSPhilipp Zabel {
11939b9004dSPhilipp Zabel 	u32 bit = (wbs >> 8) % 160;
12039b9004dSPhilipp Zabel 	u32 size = wbs & 0xff;
12139b9004dSPhilipp Zabel 	u32 word = (wbs >> 8) / 160;
12239b9004dSPhilipp Zabel 	u32 i = bit / 32;
12339b9004dSPhilipp Zabel 	u32 ofs = bit % 32;
12439b9004dSPhilipp Zabel 	u32 mask = (1 << size) - 1;
12539b9004dSPhilipp Zabel 	u32 val = 0;
12639b9004dSPhilipp Zabel 
12739b9004dSPhilipp Zabel 	pr_debug("%s %d %d %d\n", __func__, word, bit , size);
12839b9004dSPhilipp Zabel 
12939b9004dSPhilipp Zabel 	val = (readl(&base->word[word].data[i]) >> ofs) & mask;
13039b9004dSPhilipp Zabel 
13139b9004dSPhilipp Zabel 	if ((bit + size - 1) / 32 > i) {
13239b9004dSPhilipp Zabel 		u32 tmp;
13339b9004dSPhilipp Zabel 		tmp = readl(&base->word[word].data[i + 1]);
13439b9004dSPhilipp Zabel 		tmp &= mask >> (ofs ? (32 - ofs) : 0);
13539b9004dSPhilipp Zabel 		val |= tmp << (ofs ? (32 - ofs) : 0);
13639b9004dSPhilipp Zabel 	}
13739b9004dSPhilipp Zabel 
13839b9004dSPhilipp Zabel 	return val;
13939b9004dSPhilipp Zabel }
14039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_ch_param_read_field);
14139b9004dSPhilipp Zabel 
14239b9004dSPhilipp Zabel int ipu_cpmem_set_format_rgb(struct ipu_ch_param __iomem *p,
14339b9004dSPhilipp Zabel 		const struct ipu_rgb *rgb)
14439b9004dSPhilipp Zabel {
14539b9004dSPhilipp Zabel 	int bpp = 0, npb = 0, ro, go, bo, to;
14639b9004dSPhilipp Zabel 
14739b9004dSPhilipp Zabel 	ro = rgb->bits_per_pixel - rgb->red.length - rgb->red.offset;
14839b9004dSPhilipp Zabel 	go = rgb->bits_per_pixel - rgb->green.length - rgb->green.offset;
14939b9004dSPhilipp Zabel 	bo = rgb->bits_per_pixel - rgb->blue.length - rgb->blue.offset;
15039b9004dSPhilipp Zabel 	to = rgb->bits_per_pixel - rgb->transp.length - rgb->transp.offset;
15139b9004dSPhilipp Zabel 
15239b9004dSPhilipp Zabel 	ipu_ch_param_write_field(p, IPU_FIELD_WID0, rgb->red.length - 1);
15339b9004dSPhilipp Zabel 	ipu_ch_param_write_field(p, IPU_FIELD_OFS0, ro);
15439b9004dSPhilipp Zabel 	ipu_ch_param_write_field(p, IPU_FIELD_WID1, rgb->green.length - 1);
15539b9004dSPhilipp Zabel 	ipu_ch_param_write_field(p, IPU_FIELD_OFS1, go);
15639b9004dSPhilipp Zabel 	ipu_ch_param_write_field(p, IPU_FIELD_WID2, rgb->blue.length - 1);
15739b9004dSPhilipp Zabel 	ipu_ch_param_write_field(p, IPU_FIELD_OFS2, bo);
15839b9004dSPhilipp Zabel 
15939b9004dSPhilipp Zabel 	if (rgb->transp.length) {
16039b9004dSPhilipp Zabel 		ipu_ch_param_write_field(p, IPU_FIELD_WID3,
16139b9004dSPhilipp Zabel 				rgb->transp.length - 1);
16239b9004dSPhilipp Zabel 		ipu_ch_param_write_field(p, IPU_FIELD_OFS3, to);
16339b9004dSPhilipp Zabel 	} else {
16439b9004dSPhilipp Zabel 		ipu_ch_param_write_field(p, IPU_FIELD_WID3, 7);
16539b9004dSPhilipp Zabel 		ipu_ch_param_write_field(p, IPU_FIELD_OFS3,
16639b9004dSPhilipp Zabel 				rgb->bits_per_pixel);
16739b9004dSPhilipp Zabel 	}
16839b9004dSPhilipp Zabel 
16939b9004dSPhilipp Zabel 	switch (rgb->bits_per_pixel) {
17039b9004dSPhilipp Zabel 	case 32:
17139b9004dSPhilipp Zabel 		bpp = 0;
17239b9004dSPhilipp Zabel 		npb = 15;
17339b9004dSPhilipp Zabel 		break;
17439b9004dSPhilipp Zabel 	case 24:
17539b9004dSPhilipp Zabel 		bpp = 1;
17639b9004dSPhilipp Zabel 		npb = 19;
17739b9004dSPhilipp Zabel 		break;
17839b9004dSPhilipp Zabel 	case 16:
17939b9004dSPhilipp Zabel 		bpp = 3;
18039b9004dSPhilipp Zabel 		npb = 31;
18139b9004dSPhilipp Zabel 		break;
18239b9004dSPhilipp Zabel 	case 8:
18339b9004dSPhilipp Zabel 		bpp = 5;
18439b9004dSPhilipp Zabel 		npb = 63;
18539b9004dSPhilipp Zabel 		break;
18639b9004dSPhilipp Zabel 	default:
18739b9004dSPhilipp Zabel 		return -EINVAL;
18839b9004dSPhilipp Zabel 	}
18939b9004dSPhilipp Zabel 	ipu_ch_param_write_field(p, IPU_FIELD_BPP, bpp);
19039b9004dSPhilipp Zabel 	ipu_ch_param_write_field(p, IPU_FIELD_NPB, npb);
19139b9004dSPhilipp Zabel 	ipu_ch_param_write_field(p, IPU_FIELD_PFS, 7); /* rgb mode */
19239b9004dSPhilipp Zabel 
19339b9004dSPhilipp Zabel 	return 0;
19439b9004dSPhilipp Zabel }
19539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_rgb);
19639b9004dSPhilipp Zabel 
19739b9004dSPhilipp Zabel int ipu_cpmem_set_format_passthrough(struct ipu_ch_param __iomem *p,
19839b9004dSPhilipp Zabel 		int width)
19939b9004dSPhilipp Zabel {
20039b9004dSPhilipp Zabel 	int bpp = 0, npb = 0;
20139b9004dSPhilipp Zabel 
20239b9004dSPhilipp Zabel 	switch (width) {
20339b9004dSPhilipp Zabel 	case 32:
20439b9004dSPhilipp Zabel 		bpp = 0;
20539b9004dSPhilipp Zabel 		npb = 15;
20639b9004dSPhilipp Zabel 		break;
20739b9004dSPhilipp Zabel 	case 24:
20839b9004dSPhilipp Zabel 		bpp = 1;
20939b9004dSPhilipp Zabel 		npb = 19;
21039b9004dSPhilipp Zabel 		break;
21139b9004dSPhilipp Zabel 	case 16:
21239b9004dSPhilipp Zabel 		bpp = 3;
21339b9004dSPhilipp Zabel 		npb = 31;
21439b9004dSPhilipp Zabel 		break;
21539b9004dSPhilipp Zabel 	case 8:
21639b9004dSPhilipp Zabel 		bpp = 5;
21739b9004dSPhilipp Zabel 		npb = 63;
21839b9004dSPhilipp Zabel 		break;
21939b9004dSPhilipp Zabel 	default:
22039b9004dSPhilipp Zabel 		return -EINVAL;
22139b9004dSPhilipp Zabel 	}
22239b9004dSPhilipp Zabel 
22339b9004dSPhilipp Zabel 	ipu_ch_param_write_field(p, IPU_FIELD_BPP, bpp);
22439b9004dSPhilipp Zabel 	ipu_ch_param_write_field(p, IPU_FIELD_NPB, npb);
22539b9004dSPhilipp Zabel 	ipu_ch_param_write_field(p, IPU_FIELD_PFS, 6); /* raw mode */
22639b9004dSPhilipp Zabel 
22739b9004dSPhilipp Zabel 	return 0;
22839b9004dSPhilipp Zabel }
22939b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_passthrough);
23039b9004dSPhilipp Zabel 
23139b9004dSPhilipp Zabel void ipu_cpmem_set_yuv_interleaved(struct ipu_ch_param __iomem *p,
23239b9004dSPhilipp Zabel 				   u32 pixel_format)
23339b9004dSPhilipp Zabel {
23439b9004dSPhilipp Zabel 	switch (pixel_format) {
23539b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_UYVY:
23639b9004dSPhilipp Zabel 		ipu_ch_param_write_field(p, IPU_FIELD_BPP, 3);    /* bits/pixel */
23739b9004dSPhilipp Zabel 		ipu_ch_param_write_field(p, IPU_FIELD_PFS, 0xA);  /* pix format */
23839b9004dSPhilipp Zabel 		ipu_ch_param_write_field(p, IPU_FIELD_NPB, 31);   /* burst size */
23939b9004dSPhilipp Zabel 		break;
24039b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YUYV:
24139b9004dSPhilipp Zabel 		ipu_ch_param_write_field(p, IPU_FIELD_BPP, 3);    /* bits/pixel */
24239b9004dSPhilipp Zabel 		ipu_ch_param_write_field(p, IPU_FIELD_PFS, 0x8);  /* pix format */
24339b9004dSPhilipp Zabel 		ipu_ch_param_write_field(p, IPU_FIELD_NPB, 31);   /* burst size */
24439b9004dSPhilipp Zabel 		break;
24539b9004dSPhilipp Zabel 	}
24639b9004dSPhilipp Zabel }
24739b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_interleaved);
24839b9004dSPhilipp Zabel 
24939b9004dSPhilipp Zabel void ipu_cpmem_set_yuv_planar_full(struct ipu_ch_param __iomem *p,
25039b9004dSPhilipp Zabel 		u32 pixel_format, int stride, int u_offset, int v_offset)
25139b9004dSPhilipp Zabel {
25239b9004dSPhilipp Zabel 	switch (pixel_format) {
25339b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YUV420:
25439b9004dSPhilipp Zabel 		ipu_ch_param_write_field(p, IPU_FIELD_SLUV, (stride / 2) - 1);
25539b9004dSPhilipp Zabel 		ipu_ch_param_write_field(p, IPU_FIELD_UBO, u_offset / 8);
25639b9004dSPhilipp Zabel 		ipu_ch_param_write_field(p, IPU_FIELD_VBO, v_offset / 8);
25739b9004dSPhilipp Zabel 		break;
25839b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YVU420:
25939b9004dSPhilipp Zabel 		ipu_ch_param_write_field(p, IPU_FIELD_SLUV, (stride / 2) - 1);
26039b9004dSPhilipp Zabel 		ipu_ch_param_write_field(p, IPU_FIELD_UBO, v_offset / 8);
26139b9004dSPhilipp Zabel 		ipu_ch_param_write_field(p, IPU_FIELD_VBO, u_offset / 8);
26239b9004dSPhilipp Zabel 		break;
26339b9004dSPhilipp Zabel 	}
26439b9004dSPhilipp Zabel }
26539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar_full);
26639b9004dSPhilipp Zabel 
26739b9004dSPhilipp Zabel void ipu_cpmem_set_yuv_planar(struct ipu_ch_param __iomem *p, u32 pixel_format,
26839b9004dSPhilipp Zabel 		int stride, int height)
26939b9004dSPhilipp Zabel {
27039b9004dSPhilipp Zabel 	int u_offset, v_offset;
27139b9004dSPhilipp Zabel 	int uv_stride = 0;
27239b9004dSPhilipp Zabel 
27339b9004dSPhilipp Zabel 	switch (pixel_format) {
27439b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YUV420:
27539b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YVU420:
27639b9004dSPhilipp Zabel 		uv_stride = stride / 2;
27739b9004dSPhilipp Zabel 		u_offset = stride * height;
27839b9004dSPhilipp Zabel 		v_offset = u_offset + (uv_stride * height / 2);
27939b9004dSPhilipp Zabel 		ipu_cpmem_set_yuv_planar_full(p, pixel_format, stride,
28039b9004dSPhilipp Zabel 				u_offset, v_offset);
28139b9004dSPhilipp Zabel 		break;
28239b9004dSPhilipp Zabel 	}
28339b9004dSPhilipp Zabel }
28439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar);
28539b9004dSPhilipp Zabel 
28639b9004dSPhilipp Zabel static const struct ipu_rgb def_rgb_32 = {
28739b9004dSPhilipp Zabel 	.red	= { .offset = 16, .length = 8, },
28839b9004dSPhilipp Zabel 	.green	= { .offset =  8, .length = 8, },
28939b9004dSPhilipp Zabel 	.blue	= { .offset =  0, .length = 8, },
29039b9004dSPhilipp Zabel 	.transp = { .offset = 24, .length = 8, },
29139b9004dSPhilipp Zabel 	.bits_per_pixel = 32,
29239b9004dSPhilipp Zabel };
29339b9004dSPhilipp Zabel 
29439b9004dSPhilipp Zabel static const struct ipu_rgb def_bgr_32 = {
29539b9004dSPhilipp Zabel 	.red	= { .offset =  0, .length = 8, },
29639b9004dSPhilipp Zabel 	.green	= { .offset =  8, .length = 8, },
29739b9004dSPhilipp Zabel 	.blue	= { .offset = 16, .length = 8, },
29839b9004dSPhilipp Zabel 	.transp = { .offset = 24, .length = 8, },
29939b9004dSPhilipp Zabel 	.bits_per_pixel = 32,
30039b9004dSPhilipp Zabel };
30139b9004dSPhilipp Zabel 
30239b9004dSPhilipp Zabel static const struct ipu_rgb def_rgb_24 = {
30339b9004dSPhilipp Zabel 	.red	= { .offset = 16, .length = 8, },
30439b9004dSPhilipp Zabel 	.green	= { .offset =  8, .length = 8, },
30539b9004dSPhilipp Zabel 	.blue	= { .offset =  0, .length = 8, },
30639b9004dSPhilipp Zabel 	.transp = { .offset =  0, .length = 0, },
30739b9004dSPhilipp Zabel 	.bits_per_pixel = 24,
30839b9004dSPhilipp Zabel };
30939b9004dSPhilipp Zabel 
31039b9004dSPhilipp Zabel static const struct ipu_rgb def_bgr_24 = {
31139b9004dSPhilipp Zabel 	.red	= { .offset =  0, .length = 8, },
31239b9004dSPhilipp Zabel 	.green	= { .offset =  8, .length = 8, },
31339b9004dSPhilipp Zabel 	.blue	= { .offset = 16, .length = 8, },
31439b9004dSPhilipp Zabel 	.transp = { .offset =  0, .length = 0, },
31539b9004dSPhilipp Zabel 	.bits_per_pixel = 24,
31639b9004dSPhilipp Zabel };
31739b9004dSPhilipp Zabel 
31839b9004dSPhilipp Zabel static const struct ipu_rgb def_rgb_16 = {
31939b9004dSPhilipp Zabel 	.red	= { .offset = 11, .length = 5, },
32039b9004dSPhilipp Zabel 	.green	= { .offset =  5, .length = 6, },
32139b9004dSPhilipp Zabel 	.blue	= { .offset =  0, .length = 5, },
32239b9004dSPhilipp Zabel 	.transp = { .offset =  0, .length = 0, },
32339b9004dSPhilipp Zabel 	.bits_per_pixel = 16,
32439b9004dSPhilipp Zabel };
32539b9004dSPhilipp Zabel 
32639b9004dSPhilipp Zabel static const struct ipu_rgb def_bgr_16 = {
32739b9004dSPhilipp Zabel 	.red	= { .offset =  0, .length = 5, },
32839b9004dSPhilipp Zabel 	.green	= { .offset =  5, .length = 6, },
32939b9004dSPhilipp Zabel 	.blue	= { .offset = 11, .length = 5, },
33039b9004dSPhilipp Zabel 	.transp = { .offset =  0, .length = 0, },
33139b9004dSPhilipp Zabel 	.bits_per_pixel = 16,
33239b9004dSPhilipp Zabel };
33339b9004dSPhilipp Zabel 
33439b9004dSPhilipp Zabel #define Y_OFFSET(pix, x, y)	((x) + pix->width * (y))
33539b9004dSPhilipp Zabel #define U_OFFSET(pix, x, y)	((pix->width * pix->height) + \
33639b9004dSPhilipp Zabel 					(pix->width * (y) / 4) + (x) / 2)
33739b9004dSPhilipp Zabel #define V_OFFSET(pix, x, y)	((pix->width * pix->height) + \
33839b9004dSPhilipp Zabel 					(pix->width * pix->height / 4) + \
33939b9004dSPhilipp Zabel 					(pix->width * (y) / 4) + (x) / 2)
34039b9004dSPhilipp Zabel 
34139b9004dSPhilipp Zabel int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 drm_fourcc)
34239b9004dSPhilipp Zabel {
34339b9004dSPhilipp Zabel 	switch (drm_fourcc) {
34439b9004dSPhilipp Zabel 	case DRM_FORMAT_YUV420:
34539b9004dSPhilipp Zabel 	case DRM_FORMAT_YVU420:
34639b9004dSPhilipp Zabel 		/* pix format */
34739b9004dSPhilipp Zabel 		ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 2);
34839b9004dSPhilipp Zabel 		/* burst size */
34939b9004dSPhilipp Zabel 		ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 63);
35039b9004dSPhilipp Zabel 		break;
35139b9004dSPhilipp Zabel 	case DRM_FORMAT_UYVY:
35239b9004dSPhilipp Zabel 		/* bits/pixel */
35339b9004dSPhilipp Zabel 		ipu_ch_param_write_field(cpmem, IPU_FIELD_BPP, 3);
35439b9004dSPhilipp Zabel 		/* pix format */
35539b9004dSPhilipp Zabel 		ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 0xA);
35639b9004dSPhilipp Zabel 		/* burst size */
35739b9004dSPhilipp Zabel 		ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 31);
35839b9004dSPhilipp Zabel 		break;
35939b9004dSPhilipp Zabel 	case DRM_FORMAT_YUYV:
36039b9004dSPhilipp Zabel 		/* bits/pixel */
36139b9004dSPhilipp Zabel 		ipu_ch_param_write_field(cpmem, IPU_FIELD_BPP, 3);
36239b9004dSPhilipp Zabel 		/* pix format */
36339b9004dSPhilipp Zabel 		ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 0x8);
36439b9004dSPhilipp Zabel 		/* burst size */
36539b9004dSPhilipp Zabel 		ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 31);
36639b9004dSPhilipp Zabel 		break;
36739b9004dSPhilipp Zabel 	case DRM_FORMAT_ABGR8888:
36839b9004dSPhilipp Zabel 	case DRM_FORMAT_XBGR8888:
36939b9004dSPhilipp Zabel 		ipu_cpmem_set_format_rgb(cpmem, &def_bgr_32);
37039b9004dSPhilipp Zabel 		break;
37139b9004dSPhilipp Zabel 	case DRM_FORMAT_ARGB8888:
37239b9004dSPhilipp Zabel 	case DRM_FORMAT_XRGB8888:
37339b9004dSPhilipp Zabel 		ipu_cpmem_set_format_rgb(cpmem, &def_rgb_32);
37439b9004dSPhilipp Zabel 		break;
37539b9004dSPhilipp Zabel 	case DRM_FORMAT_BGR888:
37639b9004dSPhilipp Zabel 		ipu_cpmem_set_format_rgb(cpmem, &def_bgr_24);
37739b9004dSPhilipp Zabel 		break;
37839b9004dSPhilipp Zabel 	case DRM_FORMAT_RGB888:
37939b9004dSPhilipp Zabel 		ipu_cpmem_set_format_rgb(cpmem, &def_rgb_24);
38039b9004dSPhilipp Zabel 		break;
38139b9004dSPhilipp Zabel 	case DRM_FORMAT_RGB565:
38239b9004dSPhilipp Zabel 		ipu_cpmem_set_format_rgb(cpmem, &def_rgb_16);
38339b9004dSPhilipp Zabel 		break;
38439b9004dSPhilipp Zabel 	case DRM_FORMAT_BGR565:
38539b9004dSPhilipp Zabel 		ipu_cpmem_set_format_rgb(cpmem, &def_bgr_16);
38639b9004dSPhilipp Zabel 		break;
38739b9004dSPhilipp Zabel 	default:
38839b9004dSPhilipp Zabel 		return -EINVAL;
38939b9004dSPhilipp Zabel 	}
39039b9004dSPhilipp Zabel 
39139b9004dSPhilipp Zabel 	return 0;
39239b9004dSPhilipp Zabel }
39339b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt);
39439b9004dSPhilipp Zabel 
39539b9004dSPhilipp Zabel /*
39639b9004dSPhilipp Zabel  * The V4L2 spec defines packed RGB formats in memory byte order, which from
39739b9004dSPhilipp Zabel  * point of view of the IPU corresponds to little-endian words with the first
39839b9004dSPhilipp Zabel  * component in the least significant bits.
39939b9004dSPhilipp Zabel  * The DRM pixel formats and IPU internal representation are ordered the other
40039b9004dSPhilipp Zabel  * way around, with the first named component ordered at the most significant
40139b9004dSPhilipp Zabel  * bits. Further, V4L2 formats are not well defined:
40239b9004dSPhilipp Zabel  *     http://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html
40339b9004dSPhilipp Zabel  * We choose the interpretation which matches GStreamer behavior.
40439b9004dSPhilipp Zabel  */
40539b9004dSPhilipp Zabel static int v4l2_pix_fmt_to_drm_fourcc(u32 pixelformat)
40639b9004dSPhilipp Zabel {
40739b9004dSPhilipp Zabel 	switch (pixelformat) {
40839b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB565:
40939b9004dSPhilipp Zabel 		/*
41039b9004dSPhilipp Zabel 		 * Here we choose the 'corrected' interpretation of RGBP, a
41139b9004dSPhilipp Zabel 		 * little-endian 16-bit word with the red component at the most
41239b9004dSPhilipp Zabel 		 * significant bits:
41339b9004dSPhilipp Zabel 		 * g[2:0]b[4:0] r[4:0]g[5:3] <=> [16:0] R:G:B
41439b9004dSPhilipp Zabel 		 */
41539b9004dSPhilipp Zabel 		return DRM_FORMAT_RGB565;
41639b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_BGR24:
41739b9004dSPhilipp Zabel 		/* B G R <=> [24:0] R:G:B */
41839b9004dSPhilipp Zabel 		return DRM_FORMAT_RGB888;
41939b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB24:
42039b9004dSPhilipp Zabel 		/* R G B <=> [24:0] B:G:R */
42139b9004dSPhilipp Zabel 		return DRM_FORMAT_BGR888;
42239b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_BGR32:
42339b9004dSPhilipp Zabel 		/* B G R A <=> [32:0] A:B:G:R */
42439b9004dSPhilipp Zabel 		return DRM_FORMAT_XRGB8888;
42539b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB32:
42639b9004dSPhilipp Zabel 		/* R G B A <=> [32:0] A:B:G:R */
42739b9004dSPhilipp Zabel 		return DRM_FORMAT_XBGR8888;
42839b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_UYVY:
42939b9004dSPhilipp Zabel 		return DRM_FORMAT_UYVY;
43039b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YUYV:
43139b9004dSPhilipp Zabel 		return DRM_FORMAT_YUYV;
43239b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YUV420:
43339b9004dSPhilipp Zabel 		return DRM_FORMAT_YUV420;
43439b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YVU420:
43539b9004dSPhilipp Zabel 		return DRM_FORMAT_YVU420;
43639b9004dSPhilipp Zabel 	}
43739b9004dSPhilipp Zabel 
43839b9004dSPhilipp Zabel 	return -EINVAL;
43939b9004dSPhilipp Zabel }
44039b9004dSPhilipp Zabel 
44139b9004dSPhilipp Zabel enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc)
44239b9004dSPhilipp Zabel {
44339b9004dSPhilipp Zabel 	switch (drm_fourcc) {
44439b9004dSPhilipp Zabel 	case DRM_FORMAT_RGB565:
44539b9004dSPhilipp Zabel 	case DRM_FORMAT_BGR565:
44639b9004dSPhilipp Zabel 	case DRM_FORMAT_RGB888:
44739b9004dSPhilipp Zabel 	case DRM_FORMAT_BGR888:
44839b9004dSPhilipp Zabel 	case DRM_FORMAT_XRGB8888:
44939b9004dSPhilipp Zabel 	case DRM_FORMAT_XBGR8888:
45039b9004dSPhilipp Zabel 	case DRM_FORMAT_RGBX8888:
45139b9004dSPhilipp Zabel 	case DRM_FORMAT_BGRX8888:
45239b9004dSPhilipp Zabel 	case DRM_FORMAT_ARGB8888:
45339b9004dSPhilipp Zabel 	case DRM_FORMAT_ABGR8888:
45439b9004dSPhilipp Zabel 	case DRM_FORMAT_RGBA8888:
45539b9004dSPhilipp Zabel 	case DRM_FORMAT_BGRA8888:
45639b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_RGB;
45739b9004dSPhilipp Zabel 	case DRM_FORMAT_YUYV:
45839b9004dSPhilipp Zabel 	case DRM_FORMAT_UYVY:
45939b9004dSPhilipp Zabel 	case DRM_FORMAT_YUV420:
46039b9004dSPhilipp Zabel 	case DRM_FORMAT_YVU420:
46139b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_YUV;
46239b9004dSPhilipp Zabel 	default:
46339b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_UNKNOWN;
46439b9004dSPhilipp Zabel 	}
46539b9004dSPhilipp Zabel }
46639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace);
46739b9004dSPhilipp Zabel 
46839b9004dSPhilipp Zabel int ipu_cpmem_set_image(struct ipu_ch_param __iomem *cpmem,
46939b9004dSPhilipp Zabel 		struct ipu_image *image)
47039b9004dSPhilipp Zabel {
47139b9004dSPhilipp Zabel 	struct v4l2_pix_format *pix = &image->pix;
47239b9004dSPhilipp Zabel 	int y_offset, u_offset, v_offset;
47339b9004dSPhilipp Zabel 
47439b9004dSPhilipp Zabel 	pr_debug("%s: resolution: %dx%d stride: %d\n",
47539b9004dSPhilipp Zabel 			__func__, pix->width, pix->height,
47639b9004dSPhilipp Zabel 			pix->bytesperline);
47739b9004dSPhilipp Zabel 
47839b9004dSPhilipp Zabel 	ipu_cpmem_set_resolution(cpmem, image->rect.width,
47939b9004dSPhilipp Zabel 			image->rect.height);
48039b9004dSPhilipp Zabel 	ipu_cpmem_set_stride(cpmem, pix->bytesperline);
48139b9004dSPhilipp Zabel 
48239b9004dSPhilipp Zabel 	ipu_cpmem_set_fmt(cpmem, v4l2_pix_fmt_to_drm_fourcc(pix->pixelformat));
48339b9004dSPhilipp Zabel 
48439b9004dSPhilipp Zabel 	switch (pix->pixelformat) {
48539b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YUV420:
48639b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YVU420:
48739b9004dSPhilipp Zabel 		y_offset = Y_OFFSET(pix, image->rect.left, image->rect.top);
48839b9004dSPhilipp Zabel 		u_offset = U_OFFSET(pix, image->rect.left,
48939b9004dSPhilipp Zabel 				image->rect.top) - y_offset;
49039b9004dSPhilipp Zabel 		v_offset = V_OFFSET(pix, image->rect.left,
49139b9004dSPhilipp Zabel 				image->rect.top) - y_offset;
49239b9004dSPhilipp Zabel 
49339b9004dSPhilipp Zabel 		ipu_cpmem_set_yuv_planar_full(cpmem, pix->pixelformat,
49439b9004dSPhilipp Zabel 				pix->bytesperline, u_offset, v_offset);
49539b9004dSPhilipp Zabel 		ipu_cpmem_set_buffer(cpmem, 0, image->phys + y_offset);
49639b9004dSPhilipp Zabel 		break;
49739b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_UYVY:
49839b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YUYV:
49939b9004dSPhilipp Zabel 		ipu_cpmem_set_buffer(cpmem, 0, image->phys +
50039b9004dSPhilipp Zabel 				image->rect.left * 2 +
50139b9004dSPhilipp Zabel 				image->rect.top * image->pix.bytesperline);
50239b9004dSPhilipp Zabel 		break;
50339b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB32:
50439b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_BGR32:
50539b9004dSPhilipp Zabel 		ipu_cpmem_set_buffer(cpmem, 0, image->phys +
50639b9004dSPhilipp Zabel 				image->rect.left * 4 +
50739b9004dSPhilipp Zabel 				image->rect.top * image->pix.bytesperline);
50839b9004dSPhilipp Zabel 		break;
50939b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB565:
51039b9004dSPhilipp Zabel 		ipu_cpmem_set_buffer(cpmem, 0, image->phys +
51139b9004dSPhilipp Zabel 				image->rect.left * 2 +
51239b9004dSPhilipp Zabel 				image->rect.top * image->pix.bytesperline);
51339b9004dSPhilipp Zabel 		break;
51439b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB24:
51539b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_BGR24:
51639b9004dSPhilipp Zabel 		ipu_cpmem_set_buffer(cpmem, 0, image->phys +
51739b9004dSPhilipp Zabel 				image->rect.left * 3 +
51839b9004dSPhilipp Zabel 				image->rect.top * image->pix.bytesperline);
51939b9004dSPhilipp Zabel 		break;
52039b9004dSPhilipp Zabel 	default:
52139b9004dSPhilipp Zabel 		return -EINVAL;
52239b9004dSPhilipp Zabel 	}
52339b9004dSPhilipp Zabel 
52439b9004dSPhilipp Zabel 	return 0;
52539b9004dSPhilipp Zabel }
52639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_cpmem_set_image);
52739b9004dSPhilipp Zabel 
52839b9004dSPhilipp Zabel enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat)
52939b9004dSPhilipp Zabel {
53039b9004dSPhilipp Zabel 	switch (pixelformat) {
53139b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YUV420:
53239b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YVU420:
53339b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_UYVY:
53439b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_YUYV:
53539b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_YUV;
53639b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB32:
53739b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_BGR32:
53839b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB24:
53939b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_BGR24:
54039b9004dSPhilipp Zabel 	case V4L2_PIX_FMT_RGB565:
54139b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_RGB;
54239b9004dSPhilipp Zabel 	default:
54339b9004dSPhilipp Zabel 		return IPUV3_COLORSPACE_UNKNOWN;
54439b9004dSPhilipp Zabel 	}
54539b9004dSPhilipp Zabel }
54639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace);
54739b9004dSPhilipp Zabel 
54839b9004dSPhilipp Zabel struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num)
54939b9004dSPhilipp Zabel {
55039b9004dSPhilipp Zabel 	struct ipuv3_channel *channel;
55139b9004dSPhilipp Zabel 
55239b9004dSPhilipp Zabel 	dev_dbg(ipu->dev, "%s %d\n", __func__, num);
55339b9004dSPhilipp Zabel 
55439b9004dSPhilipp Zabel 	if (num > 63)
55539b9004dSPhilipp Zabel 		return ERR_PTR(-ENODEV);
55639b9004dSPhilipp Zabel 
55739b9004dSPhilipp Zabel 	mutex_lock(&ipu->channel_lock);
55839b9004dSPhilipp Zabel 
55939b9004dSPhilipp Zabel 	channel = &ipu->channel[num];
56039b9004dSPhilipp Zabel 
56139b9004dSPhilipp Zabel 	if (channel->busy) {
56239b9004dSPhilipp Zabel 		channel = ERR_PTR(-EBUSY);
56339b9004dSPhilipp Zabel 		goto out;
56439b9004dSPhilipp Zabel 	}
56539b9004dSPhilipp Zabel 
56639b9004dSPhilipp Zabel 	channel->busy = true;
56739b9004dSPhilipp Zabel 	channel->num = num;
56839b9004dSPhilipp Zabel 
56939b9004dSPhilipp Zabel out:
57039b9004dSPhilipp Zabel 	mutex_unlock(&ipu->channel_lock);
57139b9004dSPhilipp Zabel 
57239b9004dSPhilipp Zabel 	return channel;
57339b9004dSPhilipp Zabel }
57439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get);
57539b9004dSPhilipp Zabel 
57639b9004dSPhilipp Zabel void ipu_idmac_put(struct ipuv3_channel *channel)
57739b9004dSPhilipp Zabel {
57839b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
57939b9004dSPhilipp Zabel 
58039b9004dSPhilipp Zabel 	dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num);
58139b9004dSPhilipp Zabel 
58239b9004dSPhilipp Zabel 	mutex_lock(&ipu->channel_lock);
58339b9004dSPhilipp Zabel 
58439b9004dSPhilipp Zabel 	channel->busy = false;
58539b9004dSPhilipp Zabel 
58639b9004dSPhilipp Zabel 	mutex_unlock(&ipu->channel_lock);
58739b9004dSPhilipp Zabel }
58839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_put);
58939b9004dSPhilipp Zabel 
59039b9004dSPhilipp Zabel #define idma_mask(ch)			(1 << (ch & 0x1f))
59139b9004dSPhilipp Zabel 
59239b9004dSPhilipp Zabel void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
59339b9004dSPhilipp Zabel 		bool doublebuffer)
59439b9004dSPhilipp Zabel {
59539b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
59639b9004dSPhilipp Zabel 	unsigned long flags;
59739b9004dSPhilipp Zabel 	u32 reg;
59839b9004dSPhilipp Zabel 
59939b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
60039b9004dSPhilipp Zabel 
60139b9004dSPhilipp Zabel 	reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
60239b9004dSPhilipp Zabel 	if (doublebuffer)
60339b9004dSPhilipp Zabel 		reg |= idma_mask(channel->num);
60439b9004dSPhilipp Zabel 	else
60539b9004dSPhilipp Zabel 		reg &= ~idma_mask(channel->num);
60639b9004dSPhilipp Zabel 	ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num));
60739b9004dSPhilipp Zabel 
60839b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
60939b9004dSPhilipp Zabel }
61039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer);
61139b9004dSPhilipp Zabel 
61239b9004dSPhilipp Zabel int ipu_module_enable(struct ipu_soc *ipu, u32 mask)
61339b9004dSPhilipp Zabel {
61439b9004dSPhilipp Zabel 	unsigned long lock_flags;
61539b9004dSPhilipp Zabel 	u32 val;
61639b9004dSPhilipp Zabel 
61739b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, lock_flags);
61839b9004dSPhilipp Zabel 
61939b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_DISP_GEN);
62039b9004dSPhilipp Zabel 
62139b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI0_EN)
62239b9004dSPhilipp Zabel 		val |= IPU_DI0_COUNTER_RELEASE;
62339b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI1_EN)
62439b9004dSPhilipp Zabel 		val |= IPU_DI1_COUNTER_RELEASE;
62539b9004dSPhilipp Zabel 
62639b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_DISP_GEN);
62739b9004dSPhilipp Zabel 
62839b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CONF);
62939b9004dSPhilipp Zabel 	val |= mask;
63039b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CONF);
63139b9004dSPhilipp Zabel 
63239b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, lock_flags);
63339b9004dSPhilipp Zabel 
63439b9004dSPhilipp Zabel 	return 0;
63539b9004dSPhilipp Zabel }
63639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_enable);
63739b9004dSPhilipp Zabel 
63839b9004dSPhilipp Zabel int ipu_module_disable(struct ipu_soc *ipu, u32 mask)
63939b9004dSPhilipp Zabel {
64039b9004dSPhilipp Zabel 	unsigned long lock_flags;
64139b9004dSPhilipp Zabel 	u32 val;
64239b9004dSPhilipp Zabel 
64339b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, lock_flags);
64439b9004dSPhilipp Zabel 
64539b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CONF);
64639b9004dSPhilipp Zabel 	val &= ~mask;
64739b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CONF);
64839b9004dSPhilipp Zabel 
64939b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_DISP_GEN);
65039b9004dSPhilipp Zabel 
65139b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI0_EN)
65239b9004dSPhilipp Zabel 		val &= ~IPU_DI0_COUNTER_RELEASE;
65339b9004dSPhilipp Zabel 	if (mask & IPU_CONF_DI1_EN)
65439b9004dSPhilipp Zabel 		val &= ~IPU_DI1_COUNTER_RELEASE;
65539b9004dSPhilipp Zabel 
65639b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_DISP_GEN);
65739b9004dSPhilipp Zabel 
65839b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, lock_flags);
65939b9004dSPhilipp Zabel 
66039b9004dSPhilipp Zabel 	return 0;
66139b9004dSPhilipp Zabel }
66239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_disable);
66339b9004dSPhilipp Zabel 
66439b9004dSPhilipp Zabel void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num)
66539b9004dSPhilipp Zabel {
66639b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
66739b9004dSPhilipp Zabel 	unsigned int chno = channel->num;
66839b9004dSPhilipp Zabel 	unsigned long flags;
66939b9004dSPhilipp Zabel 
67039b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
67139b9004dSPhilipp Zabel 
67239b9004dSPhilipp Zabel 	/* Mark buffer as ready. */
67339b9004dSPhilipp Zabel 	if (buf_num == 0)
67439b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
67539b9004dSPhilipp Zabel 	else
67639b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
67739b9004dSPhilipp Zabel 
67839b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
67939b9004dSPhilipp Zabel }
68039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer);
68139b9004dSPhilipp Zabel 
68239b9004dSPhilipp Zabel int ipu_idmac_enable_channel(struct ipuv3_channel *channel)
68339b9004dSPhilipp Zabel {
68439b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
68539b9004dSPhilipp Zabel 	u32 val;
68639b9004dSPhilipp Zabel 	unsigned long flags;
68739b9004dSPhilipp Zabel 
68839b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
68939b9004dSPhilipp Zabel 
69039b9004dSPhilipp Zabel 	val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
69139b9004dSPhilipp Zabel 	val |= idma_mask(channel->num);
69239b9004dSPhilipp Zabel 	ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
69339b9004dSPhilipp Zabel 
69439b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
69539b9004dSPhilipp Zabel 
69639b9004dSPhilipp Zabel 	return 0;
69739b9004dSPhilipp Zabel }
69839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
69939b9004dSPhilipp Zabel 
70039b9004dSPhilipp Zabel int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms)
70139b9004dSPhilipp Zabel {
70239b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
70339b9004dSPhilipp Zabel 	unsigned long timeout;
70439b9004dSPhilipp Zabel 
70539b9004dSPhilipp Zabel 	timeout = jiffies + msecs_to_jiffies(ms);
70639b9004dSPhilipp Zabel 	while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) &
70739b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
70839b9004dSPhilipp Zabel 		if (time_after(jiffies, timeout))
70939b9004dSPhilipp Zabel 			return -ETIMEDOUT;
71039b9004dSPhilipp Zabel 		cpu_relax();
71139b9004dSPhilipp Zabel 	}
71239b9004dSPhilipp Zabel 
71339b9004dSPhilipp Zabel 	return 0;
71439b9004dSPhilipp Zabel }
71539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy);
71639b9004dSPhilipp Zabel 
71739b9004dSPhilipp Zabel int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
71839b9004dSPhilipp Zabel {
71939b9004dSPhilipp Zabel 	struct ipu_soc *ipu = channel->ipu;
72039b9004dSPhilipp Zabel 	u32 val;
72139b9004dSPhilipp Zabel 	unsigned long flags;
72239b9004dSPhilipp Zabel 
72339b9004dSPhilipp Zabel 	spin_lock_irqsave(&ipu->lock, flags);
72439b9004dSPhilipp Zabel 
72539b9004dSPhilipp Zabel 	/* Disable DMA channel(s) */
72639b9004dSPhilipp Zabel 	val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
72739b9004dSPhilipp Zabel 	val &= ~idma_mask(channel->num);
72839b9004dSPhilipp Zabel 	ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
72939b9004dSPhilipp Zabel 
73039b9004dSPhilipp Zabel 	/* Set channel buffers NOT to be ready */
73139b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */
73239b9004dSPhilipp Zabel 
73339b9004dSPhilipp Zabel 	if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) &
73439b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
73539b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(channel->num),
73639b9004dSPhilipp Zabel 			     IPU_CHA_BUF0_RDY(channel->num));
73739b9004dSPhilipp Zabel 	}
73839b9004dSPhilipp Zabel 
73939b9004dSPhilipp Zabel 	if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) &
74039b9004dSPhilipp Zabel 			idma_mask(channel->num)) {
74139b9004dSPhilipp Zabel 		ipu_cm_write(ipu, idma_mask(channel->num),
74239b9004dSPhilipp Zabel 			     IPU_CHA_BUF1_RDY(channel->num));
74339b9004dSPhilipp Zabel 	}
74439b9004dSPhilipp Zabel 
74539b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
74639b9004dSPhilipp Zabel 
74739b9004dSPhilipp Zabel 	/* Reset the double buffer */
74839b9004dSPhilipp Zabel 	val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
74939b9004dSPhilipp Zabel 	val &= ~idma_mask(channel->num);
75039b9004dSPhilipp Zabel 	ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num));
75139b9004dSPhilipp Zabel 
75239b9004dSPhilipp Zabel 	spin_unlock_irqrestore(&ipu->lock, flags);
75339b9004dSPhilipp Zabel 
75439b9004dSPhilipp Zabel 	return 0;
75539b9004dSPhilipp Zabel }
75639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel);
75739b9004dSPhilipp Zabel 
75839b9004dSPhilipp Zabel static int ipu_memory_reset(struct ipu_soc *ipu)
75939b9004dSPhilipp Zabel {
76039b9004dSPhilipp Zabel 	unsigned long timeout;
76139b9004dSPhilipp Zabel 
76239b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST);
76339b9004dSPhilipp Zabel 
76439b9004dSPhilipp Zabel 	timeout = jiffies + msecs_to_jiffies(1000);
76539b9004dSPhilipp Zabel 	while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) {
76639b9004dSPhilipp Zabel 		if (time_after(jiffies, timeout))
76739b9004dSPhilipp Zabel 			return -ETIME;
76839b9004dSPhilipp Zabel 		cpu_relax();
76939b9004dSPhilipp Zabel 	}
77039b9004dSPhilipp Zabel 
77139b9004dSPhilipp Zabel 	return 0;
77239b9004dSPhilipp Zabel }
77339b9004dSPhilipp Zabel 
77439b9004dSPhilipp Zabel struct ipu_devtype {
77539b9004dSPhilipp Zabel 	const char *name;
77639b9004dSPhilipp Zabel 	unsigned long cm_ofs;
77739b9004dSPhilipp Zabel 	unsigned long cpmem_ofs;
77839b9004dSPhilipp Zabel 	unsigned long srm_ofs;
77939b9004dSPhilipp Zabel 	unsigned long tpm_ofs;
78039b9004dSPhilipp Zabel 	unsigned long disp0_ofs;
78139b9004dSPhilipp Zabel 	unsigned long disp1_ofs;
78239b9004dSPhilipp Zabel 	unsigned long dc_tmpl_ofs;
78339b9004dSPhilipp Zabel 	unsigned long vdi_ofs;
78439b9004dSPhilipp Zabel 	enum ipuv3_type type;
78539b9004dSPhilipp Zabel };
78639b9004dSPhilipp Zabel 
78739b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx51 = {
78839b9004dSPhilipp Zabel 	.name = "IPUv3EX",
78939b9004dSPhilipp Zabel 	.cm_ofs = 0x1e000000,
79039b9004dSPhilipp Zabel 	.cpmem_ofs = 0x1f000000,
79139b9004dSPhilipp Zabel 	.srm_ofs = 0x1f040000,
79239b9004dSPhilipp Zabel 	.tpm_ofs = 0x1f060000,
79339b9004dSPhilipp Zabel 	.disp0_ofs = 0x1e040000,
79439b9004dSPhilipp Zabel 	.disp1_ofs = 0x1e048000,
79539b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x1f080000,
79639b9004dSPhilipp Zabel 	.vdi_ofs = 0x1e068000,
79739b9004dSPhilipp Zabel 	.type = IPUV3EX,
79839b9004dSPhilipp Zabel };
79939b9004dSPhilipp Zabel 
80039b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx53 = {
80139b9004dSPhilipp Zabel 	.name = "IPUv3M",
80239b9004dSPhilipp Zabel 	.cm_ofs = 0x06000000,
80339b9004dSPhilipp Zabel 	.cpmem_ofs = 0x07000000,
80439b9004dSPhilipp Zabel 	.srm_ofs = 0x07040000,
80539b9004dSPhilipp Zabel 	.tpm_ofs = 0x07060000,
80639b9004dSPhilipp Zabel 	.disp0_ofs = 0x06040000,
80739b9004dSPhilipp Zabel 	.disp1_ofs = 0x06048000,
80839b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x07080000,
80939b9004dSPhilipp Zabel 	.vdi_ofs = 0x06068000,
81039b9004dSPhilipp Zabel 	.type = IPUV3M,
81139b9004dSPhilipp Zabel };
81239b9004dSPhilipp Zabel 
81339b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx6q = {
81439b9004dSPhilipp Zabel 	.name = "IPUv3H",
81539b9004dSPhilipp Zabel 	.cm_ofs = 0x00200000,
81639b9004dSPhilipp Zabel 	.cpmem_ofs = 0x00300000,
81739b9004dSPhilipp Zabel 	.srm_ofs = 0x00340000,
81839b9004dSPhilipp Zabel 	.tpm_ofs = 0x00360000,
81939b9004dSPhilipp Zabel 	.disp0_ofs = 0x00240000,
82039b9004dSPhilipp Zabel 	.disp1_ofs = 0x00248000,
82139b9004dSPhilipp Zabel 	.dc_tmpl_ofs = 0x00380000,
82239b9004dSPhilipp Zabel 	.vdi_ofs = 0x00268000,
82339b9004dSPhilipp Zabel 	.type = IPUV3H,
82439b9004dSPhilipp Zabel };
82539b9004dSPhilipp Zabel 
82639b9004dSPhilipp Zabel static const struct of_device_id imx_ipu_dt_ids[] = {
82739b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, },
82839b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, },
82939b9004dSPhilipp Zabel 	{ .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, },
83039b9004dSPhilipp Zabel 	{ /* sentinel */ }
83139b9004dSPhilipp Zabel };
83239b9004dSPhilipp Zabel MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids);
83339b9004dSPhilipp Zabel 
83439b9004dSPhilipp Zabel static int ipu_submodules_init(struct ipu_soc *ipu,
83539b9004dSPhilipp Zabel 		struct platform_device *pdev, unsigned long ipu_base,
83639b9004dSPhilipp Zabel 		struct clk *ipu_clk)
83739b9004dSPhilipp Zabel {
83839b9004dSPhilipp Zabel 	char *unit;
83939b9004dSPhilipp Zabel 	int ret;
84039b9004dSPhilipp Zabel 	struct device *dev = &pdev->dev;
84139b9004dSPhilipp Zabel 	const struct ipu_devtype *devtype = ipu->devtype;
84239b9004dSPhilipp Zabel 
84339b9004dSPhilipp Zabel 	ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
84439b9004dSPhilipp Zabel 			IPU_CONF_DI0_EN, ipu_clk);
84539b9004dSPhilipp Zabel 	if (ret) {
84639b9004dSPhilipp Zabel 		unit = "di0";
84739b9004dSPhilipp Zabel 		goto err_di_0;
84839b9004dSPhilipp Zabel 	}
84939b9004dSPhilipp Zabel 
85039b9004dSPhilipp Zabel 	ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs,
85139b9004dSPhilipp Zabel 			IPU_CONF_DI1_EN, ipu_clk);
85239b9004dSPhilipp Zabel 	if (ret) {
85339b9004dSPhilipp Zabel 		unit = "di1";
85439b9004dSPhilipp Zabel 		goto err_di_1;
85539b9004dSPhilipp Zabel 	}
85639b9004dSPhilipp Zabel 
85739b9004dSPhilipp Zabel 	ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs +
85839b9004dSPhilipp Zabel 			IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs);
85939b9004dSPhilipp Zabel 	if (ret) {
86039b9004dSPhilipp Zabel 		unit = "dc_template";
86139b9004dSPhilipp Zabel 		goto err_dc;
86239b9004dSPhilipp Zabel 	}
86339b9004dSPhilipp Zabel 
86439b9004dSPhilipp Zabel 	ret = ipu_dmfc_init(ipu, dev, ipu_base +
86539b9004dSPhilipp Zabel 			devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk);
86639b9004dSPhilipp Zabel 	if (ret) {
86739b9004dSPhilipp Zabel 		unit = "dmfc";
86839b9004dSPhilipp Zabel 		goto err_dmfc;
86939b9004dSPhilipp Zabel 	}
87039b9004dSPhilipp Zabel 
87139b9004dSPhilipp Zabel 	ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs);
87239b9004dSPhilipp Zabel 	if (ret) {
87339b9004dSPhilipp Zabel 		unit = "dp";
87439b9004dSPhilipp Zabel 		goto err_dp;
87539b9004dSPhilipp Zabel 	}
87639b9004dSPhilipp Zabel 
87739b9004dSPhilipp Zabel 	return 0;
87839b9004dSPhilipp Zabel 
87939b9004dSPhilipp Zabel err_dp:
88039b9004dSPhilipp Zabel 	ipu_dmfc_exit(ipu);
88139b9004dSPhilipp Zabel err_dmfc:
88239b9004dSPhilipp Zabel 	ipu_dc_exit(ipu);
88339b9004dSPhilipp Zabel err_dc:
88439b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 1);
88539b9004dSPhilipp Zabel err_di_1:
88639b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 0);
88739b9004dSPhilipp Zabel err_di_0:
88839b9004dSPhilipp Zabel 	dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret);
88939b9004dSPhilipp Zabel 	return ret;
89039b9004dSPhilipp Zabel }
89139b9004dSPhilipp Zabel 
89239b9004dSPhilipp Zabel static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs)
89339b9004dSPhilipp Zabel {
89439b9004dSPhilipp Zabel 	unsigned long status;
89539b9004dSPhilipp Zabel 	int i, bit, irq;
89639b9004dSPhilipp Zabel 
89739b9004dSPhilipp Zabel 	for (i = 0; i < num_regs; i++) {
89839b9004dSPhilipp Zabel 
89939b9004dSPhilipp Zabel 		status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i]));
90039b9004dSPhilipp Zabel 		status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i]));
90139b9004dSPhilipp Zabel 
90239b9004dSPhilipp Zabel 		for_each_set_bit(bit, &status, 32) {
90339b9004dSPhilipp Zabel 			irq = irq_linear_revmap(ipu->domain, regs[i] * 32 + bit);
90439b9004dSPhilipp Zabel 			if (irq)
90539b9004dSPhilipp Zabel 				generic_handle_irq(irq);
90639b9004dSPhilipp Zabel 		}
90739b9004dSPhilipp Zabel 	}
90839b9004dSPhilipp Zabel }
90939b9004dSPhilipp Zabel 
91039b9004dSPhilipp Zabel static void ipu_irq_handler(unsigned int irq, struct irq_desc *desc)
91139b9004dSPhilipp Zabel {
91239b9004dSPhilipp Zabel 	struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
91339b9004dSPhilipp Zabel 	const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14};
91439b9004dSPhilipp Zabel 	struct irq_chip *chip = irq_get_chip(irq);
91539b9004dSPhilipp Zabel 
91639b9004dSPhilipp Zabel 	chained_irq_enter(chip, desc);
91739b9004dSPhilipp Zabel 
91839b9004dSPhilipp Zabel 	ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
91939b9004dSPhilipp Zabel 
92039b9004dSPhilipp Zabel 	chained_irq_exit(chip, desc);
92139b9004dSPhilipp Zabel }
92239b9004dSPhilipp Zabel 
92339b9004dSPhilipp Zabel static void ipu_err_irq_handler(unsigned int irq, struct irq_desc *desc)
92439b9004dSPhilipp Zabel {
92539b9004dSPhilipp Zabel 	struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
92639b9004dSPhilipp Zabel 	const int int_reg[] = { 4, 5, 8, 9};
92739b9004dSPhilipp Zabel 	struct irq_chip *chip = irq_get_chip(irq);
92839b9004dSPhilipp Zabel 
92939b9004dSPhilipp Zabel 	chained_irq_enter(chip, desc);
93039b9004dSPhilipp Zabel 
93139b9004dSPhilipp Zabel 	ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
93239b9004dSPhilipp Zabel 
93339b9004dSPhilipp Zabel 	chained_irq_exit(chip, desc);
93439b9004dSPhilipp Zabel }
93539b9004dSPhilipp Zabel 
93639b9004dSPhilipp Zabel int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
93739b9004dSPhilipp Zabel 		enum ipu_channel_irq irq_type)
93839b9004dSPhilipp Zabel {
93939b9004dSPhilipp Zabel 	int irq = irq_linear_revmap(ipu->domain, irq_type + channel->num);
94039b9004dSPhilipp Zabel 
94139b9004dSPhilipp Zabel 	if (!irq)
94239b9004dSPhilipp Zabel 		irq = irq_create_mapping(ipu->domain, irq_type + channel->num);
94339b9004dSPhilipp Zabel 
94439b9004dSPhilipp Zabel 	return irq;
94539b9004dSPhilipp Zabel }
94639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq);
94739b9004dSPhilipp Zabel 
94839b9004dSPhilipp Zabel static void ipu_submodules_exit(struct ipu_soc *ipu)
94939b9004dSPhilipp Zabel {
95039b9004dSPhilipp Zabel 	ipu_dp_exit(ipu);
95139b9004dSPhilipp Zabel 	ipu_dmfc_exit(ipu);
95239b9004dSPhilipp Zabel 	ipu_dc_exit(ipu);
95339b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 1);
95439b9004dSPhilipp Zabel 	ipu_di_exit(ipu, 0);
95539b9004dSPhilipp Zabel }
95639b9004dSPhilipp Zabel 
95739b9004dSPhilipp Zabel static int platform_remove_devices_fn(struct device *dev, void *unused)
95839b9004dSPhilipp Zabel {
95939b9004dSPhilipp Zabel 	struct platform_device *pdev = to_platform_device(dev);
96039b9004dSPhilipp Zabel 
96139b9004dSPhilipp Zabel 	platform_device_unregister(pdev);
96239b9004dSPhilipp Zabel 
96339b9004dSPhilipp Zabel 	return 0;
96439b9004dSPhilipp Zabel }
96539b9004dSPhilipp Zabel 
96639b9004dSPhilipp Zabel static void platform_device_unregister_children(struct platform_device *pdev)
96739b9004dSPhilipp Zabel {
96839b9004dSPhilipp Zabel 	device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn);
96939b9004dSPhilipp Zabel }
97039b9004dSPhilipp Zabel 
97139b9004dSPhilipp Zabel struct ipu_platform_reg {
97239b9004dSPhilipp Zabel 	struct ipu_client_platformdata pdata;
97339b9004dSPhilipp Zabel 	const char *name;
97439b9004dSPhilipp Zabel };
97539b9004dSPhilipp Zabel 
97639b9004dSPhilipp Zabel static const struct ipu_platform_reg client_reg[] = {
97739b9004dSPhilipp Zabel 	{
97839b9004dSPhilipp Zabel 		.pdata = {
97939b9004dSPhilipp Zabel 			.di = 0,
98039b9004dSPhilipp Zabel 			.dc = 5,
98139b9004dSPhilipp Zabel 			.dp = IPU_DP_FLOW_SYNC_BG,
98239b9004dSPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC,
98339b9004dSPhilipp Zabel 			.dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC,
98439b9004dSPhilipp Zabel 		},
98539b9004dSPhilipp Zabel 		.name = "imx-ipuv3-crtc",
98639b9004dSPhilipp Zabel 	}, {
98739b9004dSPhilipp Zabel 		.pdata = {
98839b9004dSPhilipp Zabel 			.di = 1,
98939b9004dSPhilipp Zabel 			.dc = 1,
99039b9004dSPhilipp Zabel 			.dp = -EINVAL,
99139b9004dSPhilipp Zabel 			.dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC,
99239b9004dSPhilipp Zabel 			.dma[1] = -EINVAL,
99339b9004dSPhilipp Zabel 		},
99439b9004dSPhilipp Zabel 		.name = "imx-ipuv3-crtc",
99539b9004dSPhilipp Zabel 	},
99639b9004dSPhilipp Zabel };
99739b9004dSPhilipp Zabel 
99839b9004dSPhilipp Zabel static DEFINE_MUTEX(ipu_client_id_mutex);
99939b9004dSPhilipp Zabel static int ipu_client_id;
100039b9004dSPhilipp Zabel 
100139b9004dSPhilipp Zabel static int ipu_add_client_devices(struct ipu_soc *ipu)
100239b9004dSPhilipp Zabel {
100339b9004dSPhilipp Zabel 	struct device *dev = ipu->dev;
100439b9004dSPhilipp Zabel 	unsigned i;
100539b9004dSPhilipp Zabel 	int id, ret;
100639b9004dSPhilipp Zabel 
100739b9004dSPhilipp Zabel 	mutex_lock(&ipu_client_id_mutex);
100839b9004dSPhilipp Zabel 	id = ipu_client_id;
100939b9004dSPhilipp Zabel 	ipu_client_id += ARRAY_SIZE(client_reg);
101039b9004dSPhilipp Zabel 	mutex_unlock(&ipu_client_id_mutex);
101139b9004dSPhilipp Zabel 
101239b9004dSPhilipp Zabel 	for (i = 0; i < ARRAY_SIZE(client_reg); i++) {
101339b9004dSPhilipp Zabel 		const struct ipu_platform_reg *reg = &client_reg[i];
101439b9004dSPhilipp Zabel 		struct platform_device *pdev;
101539b9004dSPhilipp Zabel 
101639b9004dSPhilipp Zabel 		pdev = platform_device_register_data(dev, reg->name,
101739b9004dSPhilipp Zabel 			id++, &reg->pdata, sizeof(reg->pdata));
101839b9004dSPhilipp Zabel 
101939b9004dSPhilipp Zabel 		if (IS_ERR(pdev))
102039b9004dSPhilipp Zabel 			goto err_register;
102139b9004dSPhilipp Zabel 	}
102239b9004dSPhilipp Zabel 
102339b9004dSPhilipp Zabel 	return 0;
102439b9004dSPhilipp Zabel 
102539b9004dSPhilipp Zabel err_register:
102639b9004dSPhilipp Zabel 	platform_device_unregister_children(to_platform_device(dev));
102739b9004dSPhilipp Zabel 
102839b9004dSPhilipp Zabel 	return ret;
102939b9004dSPhilipp Zabel }
103039b9004dSPhilipp Zabel 
103139b9004dSPhilipp Zabel 
103239b9004dSPhilipp Zabel static int ipu_irq_init(struct ipu_soc *ipu)
103339b9004dSPhilipp Zabel {
103439b9004dSPhilipp Zabel 	struct irq_chip_generic *gc;
103539b9004dSPhilipp Zabel 	struct irq_chip_type *ct;
103639b9004dSPhilipp Zabel 	unsigned long unused[IPU_NUM_IRQS / 32] = {
103739b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
103839b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
103939b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
104039b9004dSPhilipp Zabel 		0x4077ffff, 0xffe7e1fd,
104139b9004dSPhilipp Zabel 		0x23fffffe, 0x8880fff0,
104239b9004dSPhilipp Zabel 		0xf98fe7d0, 0xfff81fff,
104339b9004dSPhilipp Zabel 		0x400100d0, 0xffe000fd,
104439b9004dSPhilipp Zabel 		0x00000000,
104539b9004dSPhilipp Zabel 	};
104639b9004dSPhilipp Zabel 	int ret, i;
104739b9004dSPhilipp Zabel 
104839b9004dSPhilipp Zabel 	ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS,
104939b9004dSPhilipp Zabel 					    &irq_generic_chip_ops, ipu);
105039b9004dSPhilipp Zabel 	if (!ipu->domain) {
105139b9004dSPhilipp Zabel 		dev_err(ipu->dev, "failed to add irq domain\n");
105239b9004dSPhilipp Zabel 		return -ENODEV;
105339b9004dSPhilipp Zabel 	}
105439b9004dSPhilipp Zabel 
105539b9004dSPhilipp Zabel 	ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU",
105639b9004dSPhilipp Zabel 					     handle_level_irq, 0, IRQF_VALID, 0);
105739b9004dSPhilipp Zabel 	if (ret < 0) {
105839b9004dSPhilipp Zabel 		dev_err(ipu->dev, "failed to alloc generic irq chips\n");
105939b9004dSPhilipp Zabel 		irq_domain_remove(ipu->domain);
106039b9004dSPhilipp Zabel 		return ret;
106139b9004dSPhilipp Zabel 	}
106239b9004dSPhilipp Zabel 
106339b9004dSPhilipp Zabel 	for (i = 0; i < IPU_NUM_IRQS; i += 32) {
106439b9004dSPhilipp Zabel 		gc = irq_get_domain_generic_chip(ipu->domain, i);
106539b9004dSPhilipp Zabel 		gc->reg_base = ipu->cm_reg;
106639b9004dSPhilipp Zabel 		gc->unused = unused[i / 32];
106739b9004dSPhilipp Zabel 		ct = gc->chip_types;
106839b9004dSPhilipp Zabel 		ct->chip.irq_ack = irq_gc_ack_set_bit;
106939b9004dSPhilipp Zabel 		ct->chip.irq_mask = irq_gc_mask_clr_bit;
107039b9004dSPhilipp Zabel 		ct->chip.irq_unmask = irq_gc_mask_set_bit;
107139b9004dSPhilipp Zabel 		ct->regs.ack = IPU_INT_STAT(i / 32);
107239b9004dSPhilipp Zabel 		ct->regs.mask = IPU_INT_CTRL(i / 32);
107339b9004dSPhilipp Zabel 	}
107439b9004dSPhilipp Zabel 
107539b9004dSPhilipp Zabel 	irq_set_chained_handler(ipu->irq_sync, ipu_irq_handler);
107639b9004dSPhilipp Zabel 	irq_set_handler_data(ipu->irq_sync, ipu);
107739b9004dSPhilipp Zabel 	irq_set_chained_handler(ipu->irq_err, ipu_err_irq_handler);
107839b9004dSPhilipp Zabel 	irq_set_handler_data(ipu->irq_err, ipu);
107939b9004dSPhilipp Zabel 
108039b9004dSPhilipp Zabel 	return 0;
108139b9004dSPhilipp Zabel }
108239b9004dSPhilipp Zabel 
108339b9004dSPhilipp Zabel static void ipu_irq_exit(struct ipu_soc *ipu)
108439b9004dSPhilipp Zabel {
108539b9004dSPhilipp Zabel 	int i, irq;
108639b9004dSPhilipp Zabel 
108739b9004dSPhilipp Zabel 	irq_set_chained_handler(ipu->irq_err, NULL);
108839b9004dSPhilipp Zabel 	irq_set_handler_data(ipu->irq_err, NULL);
108939b9004dSPhilipp Zabel 	irq_set_chained_handler(ipu->irq_sync, NULL);
109039b9004dSPhilipp Zabel 	irq_set_handler_data(ipu->irq_sync, NULL);
109139b9004dSPhilipp Zabel 
109239b9004dSPhilipp Zabel 	/* TODO: remove irq_domain_generic_chips */
109339b9004dSPhilipp Zabel 
109439b9004dSPhilipp Zabel 	for (i = 0; i < IPU_NUM_IRQS; i++) {
109539b9004dSPhilipp Zabel 		irq = irq_linear_revmap(ipu->domain, i);
109639b9004dSPhilipp Zabel 		if (irq)
109739b9004dSPhilipp Zabel 			irq_dispose_mapping(irq);
109839b9004dSPhilipp Zabel 	}
109939b9004dSPhilipp Zabel 
110039b9004dSPhilipp Zabel 	irq_domain_remove(ipu->domain);
110139b9004dSPhilipp Zabel }
110239b9004dSPhilipp Zabel 
110339b9004dSPhilipp Zabel static int ipu_probe(struct platform_device *pdev)
110439b9004dSPhilipp Zabel {
110539b9004dSPhilipp Zabel 	const struct of_device_id *of_id =
110639b9004dSPhilipp Zabel 			of_match_device(imx_ipu_dt_ids, &pdev->dev);
110739b9004dSPhilipp Zabel 	struct ipu_soc *ipu;
110839b9004dSPhilipp Zabel 	struct resource *res;
110939b9004dSPhilipp Zabel 	unsigned long ipu_base;
111039b9004dSPhilipp Zabel 	int i, ret, irq_sync, irq_err;
111139b9004dSPhilipp Zabel 	const struct ipu_devtype *devtype;
111239b9004dSPhilipp Zabel 
111339b9004dSPhilipp Zabel 	devtype = of_id->data;
111439b9004dSPhilipp Zabel 
111539b9004dSPhilipp Zabel 	irq_sync = platform_get_irq(pdev, 0);
111639b9004dSPhilipp Zabel 	irq_err = platform_get_irq(pdev, 1);
111739b9004dSPhilipp Zabel 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
111839b9004dSPhilipp Zabel 
111939b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n",
112039b9004dSPhilipp Zabel 			irq_sync, irq_err);
112139b9004dSPhilipp Zabel 
112239b9004dSPhilipp Zabel 	if (!res || irq_sync < 0 || irq_err < 0)
112339b9004dSPhilipp Zabel 		return -ENODEV;
112439b9004dSPhilipp Zabel 
112539b9004dSPhilipp Zabel 	ipu_base = res->start;
112639b9004dSPhilipp Zabel 
112739b9004dSPhilipp Zabel 	ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL);
112839b9004dSPhilipp Zabel 	if (!ipu)
112939b9004dSPhilipp Zabel 		return -ENODEV;
113039b9004dSPhilipp Zabel 
113139b9004dSPhilipp Zabel 	for (i = 0; i < 64; i++)
113239b9004dSPhilipp Zabel 		ipu->channel[i].ipu = ipu;
113339b9004dSPhilipp Zabel 	ipu->devtype = devtype;
113439b9004dSPhilipp Zabel 	ipu->ipu_type = devtype->type;
113539b9004dSPhilipp Zabel 
113639b9004dSPhilipp Zabel 	spin_lock_init(&ipu->lock);
113739b9004dSPhilipp Zabel 	mutex_init(&ipu->channel_lock);
113839b9004dSPhilipp Zabel 
113939b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "cm_reg:   0x%08lx\n",
114039b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs);
114139b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "idmac:    0x%08lx\n",
114239b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS);
114339b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "cpmem:    0x%08lx\n",
114439b9004dSPhilipp Zabel 			ipu_base + devtype->cpmem_ofs);
114539b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "disp0:    0x%08lx\n",
114639b9004dSPhilipp Zabel 			ipu_base + devtype->disp0_ofs);
114739b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "disp1:    0x%08lx\n",
114839b9004dSPhilipp Zabel 			ipu_base + devtype->disp1_ofs);
114939b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "srm:      0x%08lx\n",
115039b9004dSPhilipp Zabel 			ipu_base + devtype->srm_ofs);
115139b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "tpm:      0x%08lx\n",
115239b9004dSPhilipp Zabel 			ipu_base + devtype->tpm_ofs);
115339b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "dc:       0x%08lx\n",
115439b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS);
115539b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "ic:       0x%08lx\n",
115639b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS);
115739b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "dmfc:     0x%08lx\n",
115839b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS);
115939b9004dSPhilipp Zabel 	dev_dbg(&pdev->dev, "vdi:      0x%08lx\n",
116039b9004dSPhilipp Zabel 			ipu_base + devtype->vdi_ofs);
116139b9004dSPhilipp Zabel 
116239b9004dSPhilipp Zabel 	ipu->cm_reg = devm_ioremap(&pdev->dev,
116339b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs, PAGE_SIZE);
116439b9004dSPhilipp Zabel 	ipu->idmac_reg = devm_ioremap(&pdev->dev,
116539b9004dSPhilipp Zabel 			ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS,
116639b9004dSPhilipp Zabel 			PAGE_SIZE);
116739b9004dSPhilipp Zabel 	ipu->cpmem_base = devm_ioremap(&pdev->dev,
116839b9004dSPhilipp Zabel 			ipu_base + devtype->cpmem_ofs, PAGE_SIZE);
116939b9004dSPhilipp Zabel 
117039b9004dSPhilipp Zabel 	if (!ipu->cm_reg || !ipu->idmac_reg || !ipu->cpmem_base)
117139b9004dSPhilipp Zabel 		return -ENOMEM;
117239b9004dSPhilipp Zabel 
117339b9004dSPhilipp Zabel 	ipu->clk = devm_clk_get(&pdev->dev, "bus");
117439b9004dSPhilipp Zabel 	if (IS_ERR(ipu->clk)) {
117539b9004dSPhilipp Zabel 		ret = PTR_ERR(ipu->clk);
117639b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "clk_get failed with %d", ret);
117739b9004dSPhilipp Zabel 		return ret;
117839b9004dSPhilipp Zabel 	}
117939b9004dSPhilipp Zabel 
118039b9004dSPhilipp Zabel 	platform_set_drvdata(pdev, ipu);
118139b9004dSPhilipp Zabel 
118239b9004dSPhilipp Zabel 	ret = clk_prepare_enable(ipu->clk);
118339b9004dSPhilipp Zabel 	if (ret) {
118439b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
118539b9004dSPhilipp Zabel 		return ret;
118639b9004dSPhilipp Zabel 	}
118739b9004dSPhilipp Zabel 
118839b9004dSPhilipp Zabel 	ipu->dev = &pdev->dev;
118939b9004dSPhilipp Zabel 	ipu->irq_sync = irq_sync;
119039b9004dSPhilipp Zabel 	ipu->irq_err = irq_err;
119139b9004dSPhilipp Zabel 
119239b9004dSPhilipp Zabel 	ret = ipu_irq_init(ipu);
119339b9004dSPhilipp Zabel 	if (ret)
119439b9004dSPhilipp Zabel 		goto out_failed_irq;
119539b9004dSPhilipp Zabel 
119639b9004dSPhilipp Zabel 	ret = device_reset(&pdev->dev);
119739b9004dSPhilipp Zabel 	if (ret) {
119839b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "failed to reset: %d\n", ret);
119939b9004dSPhilipp Zabel 		goto out_failed_reset;
120039b9004dSPhilipp Zabel 	}
120139b9004dSPhilipp Zabel 	ret = ipu_memory_reset(ipu);
120239b9004dSPhilipp Zabel 	if (ret)
120339b9004dSPhilipp Zabel 		goto out_failed_reset;
120439b9004dSPhilipp Zabel 
120539b9004dSPhilipp Zabel 	/* Set MCU_T to divide MCU access window into 2 */
120639b9004dSPhilipp Zabel 	ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
120739b9004dSPhilipp Zabel 			IPU_DISP_GEN);
120839b9004dSPhilipp Zabel 
120939b9004dSPhilipp Zabel 	ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk);
121039b9004dSPhilipp Zabel 	if (ret)
121139b9004dSPhilipp Zabel 		goto failed_submodules_init;
121239b9004dSPhilipp Zabel 
121339b9004dSPhilipp Zabel 	ret = ipu_add_client_devices(ipu);
121439b9004dSPhilipp Zabel 	if (ret) {
121539b9004dSPhilipp Zabel 		dev_err(&pdev->dev, "adding client devices failed with %d\n",
121639b9004dSPhilipp Zabel 				ret);
121739b9004dSPhilipp Zabel 		goto failed_add_clients;
121839b9004dSPhilipp Zabel 	}
121939b9004dSPhilipp Zabel 
122039b9004dSPhilipp Zabel 	dev_info(&pdev->dev, "%s probed\n", devtype->name);
122139b9004dSPhilipp Zabel 
122239b9004dSPhilipp Zabel 	return 0;
122339b9004dSPhilipp Zabel 
122439b9004dSPhilipp Zabel failed_add_clients:
122539b9004dSPhilipp Zabel 	ipu_submodules_exit(ipu);
122639b9004dSPhilipp Zabel failed_submodules_init:
122739b9004dSPhilipp Zabel out_failed_reset:
122839b9004dSPhilipp Zabel 	ipu_irq_exit(ipu);
122939b9004dSPhilipp Zabel out_failed_irq:
123039b9004dSPhilipp Zabel 	clk_disable_unprepare(ipu->clk);
123139b9004dSPhilipp Zabel 	return ret;
123239b9004dSPhilipp Zabel }
123339b9004dSPhilipp Zabel 
123439b9004dSPhilipp Zabel static int ipu_remove(struct platform_device *pdev)
123539b9004dSPhilipp Zabel {
123639b9004dSPhilipp Zabel 	struct ipu_soc *ipu = platform_get_drvdata(pdev);
123739b9004dSPhilipp Zabel 
123839b9004dSPhilipp Zabel 	platform_device_unregister_children(pdev);
123939b9004dSPhilipp Zabel 	ipu_submodules_exit(ipu);
124039b9004dSPhilipp Zabel 	ipu_irq_exit(ipu);
124139b9004dSPhilipp Zabel 
124239b9004dSPhilipp Zabel 	clk_disable_unprepare(ipu->clk);
124339b9004dSPhilipp Zabel 
124439b9004dSPhilipp Zabel 	return 0;
124539b9004dSPhilipp Zabel }
124639b9004dSPhilipp Zabel 
124739b9004dSPhilipp Zabel static struct platform_driver imx_ipu_driver = {
124839b9004dSPhilipp Zabel 	.driver = {
124939b9004dSPhilipp Zabel 		.name = "imx-ipuv3",
125039b9004dSPhilipp Zabel 		.of_match_table = imx_ipu_dt_ids,
125139b9004dSPhilipp Zabel 	},
125239b9004dSPhilipp Zabel 	.probe = ipu_probe,
125339b9004dSPhilipp Zabel 	.remove = ipu_remove,
125439b9004dSPhilipp Zabel };
125539b9004dSPhilipp Zabel 
125639b9004dSPhilipp Zabel module_platform_driver(imx_ipu_driver);
125739b9004dSPhilipp Zabel 
125839b9004dSPhilipp Zabel MODULE_ALIAS("platform:imx-ipuv3");
125939b9004dSPhilipp Zabel MODULE_DESCRIPTION("i.MX IPU v3 driver");
126039b9004dSPhilipp Zabel MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
126139b9004dSPhilipp Zabel MODULE_LICENSE("GPL");
1262