135de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel/*
235de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
335de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel *
435de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel * The code contained herein is licensed under the GNU General Public
535de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel * License. You may obtain a copy of the GNU General Public License
635de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel * Version 2 or later at the following locations:
735de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel *
835de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel * http://www.opensource.org/licenses/gpl-license.html
935de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel * http://www.gnu.org/copyleft/gpl.html
1035de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel */
1135de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel#include <linux/export.h>
1235de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel#include <linux/types.h>
1335de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel#include <linux/init.h>
1435de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel#include <linux/io.h>
1535de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel#include <linux/errno.h>
1635de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel#include <linux/spinlock.h>
1735de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel#include <linux/delay.h>
1835de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel#include <linux/clk.h>
1935de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel#include <video/imx-ipu-v3.h>
2035de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
2135de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel#include "ipu-prv.h"
2235de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
237fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeamstruct ipu_smfc {
247fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	struct ipu_smfc_priv *priv;
257fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	int chno;
267fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	bool inuse;
277fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam};
287fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
2935de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabelstruct ipu_smfc_priv {
3035de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel	void __iomem *base;
3135de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel	spinlock_t lock;
327fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	struct ipu_soc *ipu;
337fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	struct ipu_smfc channel[4];
347fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	int use_count;
3535de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel};
3635de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
3735de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel/*SMFC Registers */
3835de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel#define SMFC_MAP	0x0000
3935de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel#define SMFC_WMC	0x0004
4035de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel#define SMFC_BS		0x0008
4135de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
427fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeamint ipu_smfc_set_burstsize(struct ipu_smfc *smfc, int burstsize)
4335de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel{
447fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	struct ipu_smfc_priv *priv = smfc->priv;
4535de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel	unsigned long flags;
4635de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel	u32 val, shift;
4735de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
487fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	spin_lock_irqsave(&priv->lock, flags);
4935de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
507fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	shift = smfc->chno * 4;
517fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	val = readl(priv->base + SMFC_BS);
5235de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel	val &= ~(0xf << shift);
5335de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel	val |= burstsize << shift;
547fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	writel(val, priv->base + SMFC_BS);
5535de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
567fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	spin_unlock_irqrestore(&priv->lock, flags);
5735de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
5835de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel	return 0;
5935de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel}
6035de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp ZabelEXPORT_SYMBOL_GPL(ipu_smfc_set_burstsize);
6135de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
627fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeamint ipu_smfc_map_channel(struct ipu_smfc *smfc, int csi_id, int mipi_id)
6335de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel{
647fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	struct ipu_smfc_priv *priv = smfc->priv;
6535de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel	unsigned long flags;
6635de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel	u32 val, shift;
6735de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
687fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	spin_lock_irqsave(&priv->lock, flags);
6935de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
707fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	shift = smfc->chno * 3;
717fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	val = readl(priv->base + SMFC_MAP);
7235de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel	val &= ~(0x7 << shift);
7335de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel	val |= ((csi_id << 2) | mipi_id) << shift;
747fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	writel(val, priv->base + SMFC_MAP);
7535de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
767fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	spin_unlock_irqrestore(&priv->lock, flags);
7735de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
7835de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel	return 0;
7935de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel}
8035de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp ZabelEXPORT_SYMBOL_GPL(ipu_smfc_map_channel);
8135de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
82a2be35e3320b27c84488729e9fb56a62e74d65faSteve Longerbeamint ipu_smfc_set_watermark(struct ipu_smfc *smfc, u32 set_level, u32 clr_level)
83a2be35e3320b27c84488729e9fb56a62e74d65faSteve Longerbeam{
84a2be35e3320b27c84488729e9fb56a62e74d65faSteve Longerbeam	struct ipu_smfc_priv *priv = smfc->priv;
85a2be35e3320b27c84488729e9fb56a62e74d65faSteve Longerbeam	unsigned long flags;
86a2be35e3320b27c84488729e9fb56a62e74d65faSteve Longerbeam	u32 val, shift;
87a2be35e3320b27c84488729e9fb56a62e74d65faSteve Longerbeam
88a2be35e3320b27c84488729e9fb56a62e74d65faSteve Longerbeam	spin_lock_irqsave(&priv->lock, flags);
89a2be35e3320b27c84488729e9fb56a62e74d65faSteve Longerbeam
90a2be35e3320b27c84488729e9fb56a62e74d65faSteve Longerbeam	shift = smfc->chno * 6 + (smfc->chno > 1 ? 4 : 0);
91a2be35e3320b27c84488729e9fb56a62e74d65faSteve Longerbeam	val = readl(priv->base + SMFC_WMC);
92a2be35e3320b27c84488729e9fb56a62e74d65faSteve Longerbeam	val &= ~(0x3f << shift);
93a2be35e3320b27c84488729e9fb56a62e74d65faSteve Longerbeam	val |= ((clr_level << 3) | set_level) << shift;
94a2be35e3320b27c84488729e9fb56a62e74d65faSteve Longerbeam	writel(val, priv->base + SMFC_WMC);
95a2be35e3320b27c84488729e9fb56a62e74d65faSteve Longerbeam
96a2be35e3320b27c84488729e9fb56a62e74d65faSteve Longerbeam	spin_unlock_irqrestore(&priv->lock, flags);
97a2be35e3320b27c84488729e9fb56a62e74d65faSteve Longerbeam
98a2be35e3320b27c84488729e9fb56a62e74d65faSteve Longerbeam	return 0;
99a2be35e3320b27c84488729e9fb56a62e74d65faSteve Longerbeam}
100a2be35e3320b27c84488729e9fb56a62e74d65faSteve LongerbeamEXPORT_SYMBOL_GPL(ipu_smfc_set_watermark);
101a2be35e3320b27c84488729e9fb56a62e74d65faSteve Longerbeam
1027fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeamint ipu_smfc_enable(struct ipu_smfc *smfc)
103fc4353559e587f5962f22c24ca7e015bdbea1e49Steve Longerbeam{
1047fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	struct ipu_smfc_priv *priv = smfc->priv;
1057fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	unsigned long flags;
1067fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
1077fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	spin_lock_irqsave(&priv->lock, flags);
1087fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
1097fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	if (!priv->use_count)
1107fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam		ipu_module_enable(priv->ipu, IPU_CONF_SMFC_EN);
1117fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
1127fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	priv->use_count++;
1137fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
1147fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	spin_unlock_irqrestore(&priv->lock, flags);
1157fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
1167fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	return 0;
117fc4353559e587f5962f22c24ca7e015bdbea1e49Steve Longerbeam}
118fc4353559e587f5962f22c24ca7e015bdbea1e49Steve LongerbeamEXPORT_SYMBOL_GPL(ipu_smfc_enable);
119fc4353559e587f5962f22c24ca7e015bdbea1e49Steve Longerbeam
1207fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeamint ipu_smfc_disable(struct ipu_smfc *smfc)
121fc4353559e587f5962f22c24ca7e015bdbea1e49Steve Longerbeam{
1227fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	struct ipu_smfc_priv *priv = smfc->priv;
1237fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	unsigned long flags;
1247fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
1257fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	spin_lock_irqsave(&priv->lock, flags);
1267fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
1277fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	priv->use_count--;
1287fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
1297fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	if (!priv->use_count)
1307fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam		ipu_module_disable(priv->ipu, IPU_CONF_SMFC_EN);
1317fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
1327fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	if (priv->use_count < 0)
1337fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam		priv->use_count = 0;
1347fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
1357fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	spin_unlock_irqrestore(&priv->lock, flags);
1367fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
1377fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	return 0;
138fc4353559e587f5962f22c24ca7e015bdbea1e49Steve Longerbeam}
139fc4353559e587f5962f22c24ca7e015bdbea1e49Steve LongerbeamEXPORT_SYMBOL_GPL(ipu_smfc_disable);
140fc4353559e587f5962f22c24ca7e015bdbea1e49Steve Longerbeam
1417fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeamstruct ipu_smfc *ipu_smfc_get(struct ipu_soc *ipu, unsigned int chno)
1427fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam{
1437fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	struct ipu_smfc_priv *priv = ipu->smfc_priv;
1447fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	struct ipu_smfc *smfc, *ret;
1457fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	unsigned long flags;
1467fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
1477fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	if (chno >= 4)
1487fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam		return ERR_PTR(-EINVAL);
1497fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
1507fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	smfc = &priv->channel[chno];
1517fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	ret = smfc;
1527fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
1537fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	spin_lock_irqsave(&priv->lock, flags);
1547fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
1557fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	if (smfc->inuse) {
1567fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam		ret = ERR_PTR(-EBUSY);
1577fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam		goto unlock;
1587fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	}
1597fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
1607fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	smfc->inuse = true;
1617fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeamunlock:
1627fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	spin_unlock_irqrestore(&priv->lock, flags);
1637fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	return ret;
1647fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam}
1657fafa8f06f9bdf32b806b4612bfe387de8e34125Steve LongerbeamEXPORT_SYMBOL_GPL(ipu_smfc_get);
1667fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
1677fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeamvoid ipu_smfc_put(struct ipu_smfc *smfc)
1687fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam{
1697fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	struct ipu_smfc_priv *priv = smfc->priv;
1707fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	unsigned long flags;
1717fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
1727fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	spin_lock_irqsave(&priv->lock, flags);
1737fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	smfc->inuse = false;
1747fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	spin_unlock_irqrestore(&priv->lock, flags);
1757fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam}
1767fafa8f06f9bdf32b806b4612bfe387de8e34125Steve LongerbeamEXPORT_SYMBOL_GPL(ipu_smfc_put);
1777fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
17835de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabelint ipu_smfc_init(struct ipu_soc *ipu, struct device *dev,
17935de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel		  unsigned long base)
18035de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel{
1817fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	struct ipu_smfc_priv *priv;
1827fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	int i;
18335de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
1847fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
1857fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	if (!priv)
18635de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel		return -ENOMEM;
18735de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
1887fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	ipu->smfc_priv = priv;
1897fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	spin_lock_init(&priv->lock);
1907fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	priv->ipu = ipu;
19135de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
1927fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	priv->base = devm_ioremap(dev, base, PAGE_SIZE);
1937fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	if (!priv->base)
19435de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel		return -ENOMEM;
19535de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
1967fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	for (i = 0; i < 4; i++) {
1977fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam		priv->channel[i].priv = priv;
1987fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam		priv->channel[i].chno = i;
1997fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	}
2007fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam
2017fafa8f06f9bdf32b806b4612bfe387de8e34125Steve Longerbeam	pr_debug("%s: ioremap 0x%08lx -> %p\n", __func__, base, priv->base);
20235de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
20335de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel	return 0;
20435de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel}
20535de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel
20635de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabelvoid ipu_smfc_exit(struct ipu_soc *ipu)
20735de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel{
20835de925ffaa67971e073d3ebf1e0600be0d0d3f1Philipp Zabel}
209