xref: /linux/drivers/gpu/ipu-v3/ipu-dmfc.c (revision d7868cb7ac58640e9c0383205ba31bd6a985cc6f)
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/export.h>
1639b9004dSPhilipp Zabel #include <linux/types.h>
1739b9004dSPhilipp Zabel #include <linux/errno.h>
1839b9004dSPhilipp Zabel #include <linux/io.h>
1939b9004dSPhilipp Zabel 
2039b9004dSPhilipp Zabel #include <video/imx-ipu-v3.h>
2139b9004dSPhilipp Zabel #include "ipu-prv.h"
2239b9004dSPhilipp Zabel 
2339b9004dSPhilipp Zabel #define DMFC_RD_CHAN		0x0000
2439b9004dSPhilipp Zabel #define DMFC_WR_CHAN		0x0004
2539b9004dSPhilipp Zabel #define DMFC_WR_CHAN_DEF	0x0008
2639b9004dSPhilipp Zabel #define DMFC_DP_CHAN		0x000c
2739b9004dSPhilipp Zabel #define DMFC_DP_CHAN_DEF	0x0010
2839b9004dSPhilipp Zabel #define DMFC_GENERAL1		0x0014
2939b9004dSPhilipp Zabel #define DMFC_GENERAL2		0x0018
3039b9004dSPhilipp Zabel #define DMFC_IC_CTRL		0x001c
31682b7c1cSLinus Torvalds #define DMFC_WR_CHAN_ALT	0x0020
32682b7c1cSLinus Torvalds #define DMFC_WR_CHAN_DEF_ALT	0x0024
33682b7c1cSLinus Torvalds #define DMFC_DP_CHAN_ALT	0x0028
34682b7c1cSLinus Torvalds #define DMFC_DP_CHAN_DEF_ALT	0x002c
35682b7c1cSLinus Torvalds #define DMFC_GENERAL1_ALT	0x0030
36682b7c1cSLinus Torvalds #define DMFC_STAT		0x0034
3739b9004dSPhilipp Zabel 
3839b9004dSPhilipp Zabel #define DMFC_WR_CHAN_1_28		0
3939b9004dSPhilipp Zabel #define DMFC_WR_CHAN_2_41		8
4039b9004dSPhilipp Zabel #define DMFC_WR_CHAN_1C_42		16
4139b9004dSPhilipp Zabel #define DMFC_WR_CHAN_2C_43		24
4239b9004dSPhilipp Zabel 
4339b9004dSPhilipp Zabel #define DMFC_DP_CHAN_5B_23		0
4439b9004dSPhilipp Zabel #define DMFC_DP_CHAN_5F_27		8
4539b9004dSPhilipp Zabel #define DMFC_DP_CHAN_6B_24		16
4639b9004dSPhilipp Zabel #define DMFC_DP_CHAN_6F_29		24
4739b9004dSPhilipp Zabel 
4839b9004dSPhilipp Zabel struct dmfc_channel_data {
4939b9004dSPhilipp Zabel 	int		ipu_channel;
5039b9004dSPhilipp Zabel 	unsigned long	channel_reg;
5139b9004dSPhilipp Zabel 	unsigned long	shift;
5239b9004dSPhilipp Zabel 	unsigned	eot_shift;
5339b9004dSPhilipp Zabel 	unsigned	max_fifo_lines;
5439b9004dSPhilipp Zabel };
5539b9004dSPhilipp Zabel 
5639b9004dSPhilipp Zabel static const struct dmfc_channel_data dmfcdata[] = {
5739b9004dSPhilipp Zabel 	{
5839b9004dSPhilipp Zabel 		.ipu_channel	= IPUV3_CHANNEL_MEM_BG_SYNC,
5939b9004dSPhilipp Zabel 		.channel_reg	= DMFC_DP_CHAN,
6039b9004dSPhilipp Zabel 		.shift		= DMFC_DP_CHAN_5B_23,
6139b9004dSPhilipp Zabel 		.eot_shift	= 20,
6239b9004dSPhilipp Zabel 		.max_fifo_lines	= 3,
6339b9004dSPhilipp Zabel 	}, {
6439b9004dSPhilipp Zabel 		.ipu_channel	= 24,
6539b9004dSPhilipp Zabel 		.channel_reg	= DMFC_DP_CHAN,
6639b9004dSPhilipp Zabel 		.shift		= DMFC_DP_CHAN_6B_24,
6739b9004dSPhilipp Zabel 		.eot_shift	= 22,
6839b9004dSPhilipp Zabel 		.max_fifo_lines	= 1,
6939b9004dSPhilipp Zabel 	}, {
7039b9004dSPhilipp Zabel 		.ipu_channel	= IPUV3_CHANNEL_MEM_FG_SYNC,
7139b9004dSPhilipp Zabel 		.channel_reg	= DMFC_DP_CHAN,
7239b9004dSPhilipp Zabel 		.shift		= DMFC_DP_CHAN_5F_27,
7339b9004dSPhilipp Zabel 		.eot_shift	= 21,
7439b9004dSPhilipp Zabel 		.max_fifo_lines	= 2,
7539b9004dSPhilipp Zabel 	}, {
7639b9004dSPhilipp Zabel 		.ipu_channel	= IPUV3_CHANNEL_MEM_DC_SYNC,
7739b9004dSPhilipp Zabel 		.channel_reg	= DMFC_WR_CHAN,
7839b9004dSPhilipp Zabel 		.shift		= DMFC_WR_CHAN_1_28,
7939b9004dSPhilipp Zabel 		.eot_shift	= 16,
8039b9004dSPhilipp Zabel 		.max_fifo_lines	= 2,
8139b9004dSPhilipp Zabel 	}, {
8239b9004dSPhilipp Zabel 		.ipu_channel	= 29,
8339b9004dSPhilipp Zabel 		.channel_reg	= DMFC_DP_CHAN,
8439b9004dSPhilipp Zabel 		.shift		= DMFC_DP_CHAN_6F_29,
8539b9004dSPhilipp Zabel 		.eot_shift	= 23,
8639b9004dSPhilipp Zabel 		.max_fifo_lines	= 1,
8739b9004dSPhilipp Zabel 	},
8839b9004dSPhilipp Zabel };
8939b9004dSPhilipp Zabel 
9039b9004dSPhilipp Zabel #define DMFC_NUM_CHANNELS	ARRAY_SIZE(dmfcdata)
9139b9004dSPhilipp Zabel 
9239b9004dSPhilipp Zabel struct ipu_dmfc_priv;
9339b9004dSPhilipp Zabel 
9439b9004dSPhilipp Zabel struct dmfc_channel {
9539b9004dSPhilipp Zabel 	unsigned			slots;
9639b9004dSPhilipp Zabel 	struct ipu_soc			*ipu;
9739b9004dSPhilipp Zabel 	struct ipu_dmfc_priv		*priv;
9839b9004dSPhilipp Zabel 	const struct dmfc_channel_data	*data;
9939b9004dSPhilipp Zabel };
10039b9004dSPhilipp Zabel 
10139b9004dSPhilipp Zabel struct ipu_dmfc_priv {
10239b9004dSPhilipp Zabel 	struct ipu_soc *ipu;
10339b9004dSPhilipp Zabel 	struct device *dev;
10439b9004dSPhilipp Zabel 	struct dmfc_channel channels[DMFC_NUM_CHANNELS];
10539b9004dSPhilipp Zabel 	struct mutex mutex;
10639b9004dSPhilipp Zabel 	void __iomem *base;
10739b9004dSPhilipp Zabel 	int use_count;
10839b9004dSPhilipp Zabel };
10939b9004dSPhilipp Zabel 
11039b9004dSPhilipp Zabel int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc)
11139b9004dSPhilipp Zabel {
11239b9004dSPhilipp Zabel 	struct ipu_dmfc_priv *priv = dmfc->priv;
11339b9004dSPhilipp Zabel 	mutex_lock(&priv->mutex);
11439b9004dSPhilipp Zabel 
11539b9004dSPhilipp Zabel 	if (!priv->use_count)
11639b9004dSPhilipp Zabel 		ipu_module_enable(priv->ipu, IPU_CONF_DMFC_EN);
11739b9004dSPhilipp Zabel 
11839b9004dSPhilipp Zabel 	priv->use_count++;
11939b9004dSPhilipp Zabel 
12039b9004dSPhilipp Zabel 	mutex_unlock(&priv->mutex);
12139b9004dSPhilipp Zabel 
12239b9004dSPhilipp Zabel 	return 0;
12339b9004dSPhilipp Zabel }
12439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel);
12539b9004dSPhilipp Zabel 
126682b7c1cSLinus Torvalds static void ipu_dmfc_wait_fifos(struct ipu_dmfc_priv *priv)
127682b7c1cSLinus Torvalds {
128682b7c1cSLinus Torvalds 	unsigned long timeout = jiffies + msecs_to_jiffies(1000);
129682b7c1cSLinus Torvalds 
130682b7c1cSLinus Torvalds 	while ((readl(priv->base + DMFC_STAT) & 0x02fff000) != 0x02fff000) {
131682b7c1cSLinus Torvalds 		if (time_after(jiffies, timeout)) {
132682b7c1cSLinus Torvalds 			dev_warn(priv->dev,
133682b7c1cSLinus Torvalds 				 "Timeout waiting for DMFC FIFOs to clear\n");
134682b7c1cSLinus Torvalds 			break;
135682b7c1cSLinus Torvalds 		}
136682b7c1cSLinus Torvalds 		cpu_relax();
137682b7c1cSLinus Torvalds 	}
138682b7c1cSLinus Torvalds }
139682b7c1cSLinus Torvalds 
14039b9004dSPhilipp Zabel void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc)
14139b9004dSPhilipp Zabel {
14239b9004dSPhilipp Zabel 	struct ipu_dmfc_priv *priv = dmfc->priv;
14339b9004dSPhilipp Zabel 
14439b9004dSPhilipp Zabel 	mutex_lock(&priv->mutex);
14539b9004dSPhilipp Zabel 
14639b9004dSPhilipp Zabel 	priv->use_count--;
14739b9004dSPhilipp Zabel 
148682b7c1cSLinus Torvalds 	if (!priv->use_count) {
149682b7c1cSLinus Torvalds 		ipu_dmfc_wait_fifos(priv);
15039b9004dSPhilipp Zabel 		ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN);
151682b7c1cSLinus Torvalds 	}
15239b9004dSPhilipp Zabel 
15339b9004dSPhilipp Zabel 	if (priv->use_count < 0)
15439b9004dSPhilipp Zabel 		priv->use_count = 0;
15539b9004dSPhilipp Zabel 
15639b9004dSPhilipp Zabel 	mutex_unlock(&priv->mutex);
15739b9004dSPhilipp Zabel }
15839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_dmfc_disable_channel);
15939b9004dSPhilipp Zabel 
16027630c20SLiu Ying void ipu_dmfc_config_wait4eot(struct dmfc_channel *dmfc, int width)
16139b9004dSPhilipp Zabel {
16239b9004dSPhilipp Zabel 	struct ipu_dmfc_priv *priv = dmfc->priv;
16339b9004dSPhilipp Zabel 	u32 dmfc_gen1;
16439b9004dSPhilipp Zabel 
16532c26a56SLiu Ying 	mutex_lock(&priv->mutex);
16632c26a56SLiu Ying 
16739b9004dSPhilipp Zabel 	dmfc_gen1 = readl(priv->base + DMFC_GENERAL1);
16839b9004dSPhilipp Zabel 
16939b9004dSPhilipp Zabel 	if ((dmfc->slots * 64 * 4) / width > dmfc->data->max_fifo_lines)
17039b9004dSPhilipp Zabel 		dmfc_gen1 |= 1 << dmfc->data->eot_shift;
17139b9004dSPhilipp Zabel 	else
17239b9004dSPhilipp Zabel 		dmfc_gen1 &= ~(1 << dmfc->data->eot_shift);
17339b9004dSPhilipp Zabel 
17439b9004dSPhilipp Zabel 	writel(dmfc_gen1, priv->base + DMFC_GENERAL1);
17539b9004dSPhilipp Zabel 
17632c26a56SLiu Ying 	mutex_unlock(&priv->mutex);
17739b9004dSPhilipp Zabel }
17827630c20SLiu Ying EXPORT_SYMBOL_GPL(ipu_dmfc_config_wait4eot);
17939b9004dSPhilipp Zabel 
18039b9004dSPhilipp Zabel struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipu_channel)
18139b9004dSPhilipp Zabel {
18239b9004dSPhilipp Zabel 	struct ipu_dmfc_priv *priv = ipu->dmfc_priv;
18339b9004dSPhilipp Zabel 	int i;
18439b9004dSPhilipp Zabel 
18539b9004dSPhilipp Zabel 	for (i = 0; i < DMFC_NUM_CHANNELS; i++)
18639b9004dSPhilipp Zabel 		if (dmfcdata[i].ipu_channel == ipu_channel)
18739b9004dSPhilipp Zabel 			return &priv->channels[i];
18839b9004dSPhilipp Zabel 	return ERR_PTR(-ENODEV);
18939b9004dSPhilipp Zabel }
19039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_dmfc_get);
19139b9004dSPhilipp Zabel 
19239b9004dSPhilipp Zabel void ipu_dmfc_put(struct dmfc_channel *dmfc)
19339b9004dSPhilipp Zabel {
19439b9004dSPhilipp Zabel }
19539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_dmfc_put);
19639b9004dSPhilipp Zabel 
19739b9004dSPhilipp Zabel int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base,
19839b9004dSPhilipp Zabel 		struct clk *ipu_clk)
19939b9004dSPhilipp Zabel {
20039b9004dSPhilipp Zabel 	struct ipu_dmfc_priv *priv;
20139b9004dSPhilipp Zabel 	int i;
20239b9004dSPhilipp Zabel 
20339b9004dSPhilipp Zabel 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
20439b9004dSPhilipp Zabel 	if (!priv)
20539b9004dSPhilipp Zabel 		return -ENOMEM;
20639b9004dSPhilipp Zabel 
20739b9004dSPhilipp Zabel 	priv->base = devm_ioremap(dev, base, PAGE_SIZE);
20839b9004dSPhilipp Zabel 	if (!priv->base)
20939b9004dSPhilipp Zabel 		return -ENOMEM;
21039b9004dSPhilipp Zabel 
21139b9004dSPhilipp Zabel 	priv->dev = dev;
21239b9004dSPhilipp Zabel 	priv->ipu = ipu;
21339b9004dSPhilipp Zabel 	mutex_init(&priv->mutex);
21439b9004dSPhilipp Zabel 
21539b9004dSPhilipp Zabel 	ipu->dmfc_priv = priv;
21639b9004dSPhilipp Zabel 
21739b9004dSPhilipp Zabel 	for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
21839b9004dSPhilipp Zabel 		priv->channels[i].priv = priv;
21939b9004dSPhilipp Zabel 		priv->channels[i].ipu = ipu;
22039b9004dSPhilipp Zabel 		priv->channels[i].data = &dmfcdata[i];
221*d7868cb7SLiu Ying 
222*d7868cb7SLiu Ying 		if (dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_BG_SYNC ||
223*d7868cb7SLiu Ying 		    dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_FG_SYNC ||
224*d7868cb7SLiu Ying 		    dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_DC_SYNC)
225*d7868cb7SLiu Ying 			priv->channels[i].slots = 2;
22639b9004dSPhilipp Zabel 	}
22739b9004dSPhilipp Zabel 
228*d7868cb7SLiu Ying 	writel(0x00000050, priv->base + DMFC_WR_CHAN);
229*d7868cb7SLiu Ying 	writel(0x00005654, priv->base + DMFC_DP_CHAN);
23039b9004dSPhilipp Zabel 	writel(0x202020f6, priv->base + DMFC_WR_CHAN_DEF);
23139b9004dSPhilipp Zabel 	writel(0x2020f6f6, priv->base + DMFC_DP_CHAN_DEF);
23239b9004dSPhilipp Zabel 	writel(0x00000003, priv->base + DMFC_GENERAL1);
23339b9004dSPhilipp Zabel 
23439b9004dSPhilipp Zabel 	return 0;
23539b9004dSPhilipp Zabel }
23639b9004dSPhilipp Zabel 
23739b9004dSPhilipp Zabel void ipu_dmfc_exit(struct ipu_soc *ipu)
23839b9004dSPhilipp Zabel {
23939b9004dSPhilipp Zabel }
240