1/*
2 * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
3 *
4 * The code contained herein is licensed under the GNU General Public
5 * License. You may obtain a copy of the GNU General Public License
6 * Version 2 or later at the following locations:
7 *
8 * http://www.opensource.org/licenses/gpl-license.html
9 * http://www.gnu.org/copyleft/gpl.html
10 */
11#include <linux/export.h>
12#include <linux/types.h>
13#include <linux/init.h>
14#include <linux/io.h>
15#include <linux/errno.h>
16#include <linux/spinlock.h>
17#include <linux/delay.h>
18#include <linux/clk.h>
19#include <video/imx-ipu-v3.h>
20
21#include "ipu-prv.h"
22
23struct ipu_smfc {
24	struct ipu_smfc_priv *priv;
25	int chno;
26	bool inuse;
27};
28
29struct ipu_smfc_priv {
30	void __iomem *base;
31	spinlock_t lock;
32	struct ipu_soc *ipu;
33	struct ipu_smfc channel[4];
34	int use_count;
35};
36
37/*SMFC Registers */
38#define SMFC_MAP	0x0000
39#define SMFC_WMC	0x0004
40#define SMFC_BS		0x0008
41
42int ipu_smfc_set_burstsize(struct ipu_smfc *smfc, int burstsize)
43{
44	struct ipu_smfc_priv *priv = smfc->priv;
45	unsigned long flags;
46	u32 val, shift;
47
48	spin_lock_irqsave(&priv->lock, flags);
49
50	shift = smfc->chno * 4;
51	val = readl(priv->base + SMFC_BS);
52	val &= ~(0xf << shift);
53	val |= burstsize << shift;
54	writel(val, priv->base + SMFC_BS);
55
56	spin_unlock_irqrestore(&priv->lock, flags);
57
58	return 0;
59}
60EXPORT_SYMBOL_GPL(ipu_smfc_set_burstsize);
61
62int ipu_smfc_map_channel(struct ipu_smfc *smfc, int csi_id, int mipi_id)
63{
64	struct ipu_smfc_priv *priv = smfc->priv;
65	unsigned long flags;
66	u32 val, shift;
67
68	spin_lock_irqsave(&priv->lock, flags);
69
70	shift = smfc->chno * 3;
71	val = readl(priv->base + SMFC_MAP);
72	val &= ~(0x7 << shift);
73	val |= ((csi_id << 2) | mipi_id) << shift;
74	writel(val, priv->base + SMFC_MAP);
75
76	spin_unlock_irqrestore(&priv->lock, flags);
77
78	return 0;
79}
80EXPORT_SYMBOL_GPL(ipu_smfc_map_channel);
81
82int ipu_smfc_set_watermark(struct ipu_smfc *smfc, u32 set_level, u32 clr_level)
83{
84	struct ipu_smfc_priv *priv = smfc->priv;
85	unsigned long flags;
86	u32 val, shift;
87
88	spin_lock_irqsave(&priv->lock, flags);
89
90	shift = smfc->chno * 6 + (smfc->chno > 1 ? 4 : 0);
91	val = readl(priv->base + SMFC_WMC);
92	val &= ~(0x3f << shift);
93	val |= ((clr_level << 3) | set_level) << shift;
94	writel(val, priv->base + SMFC_WMC);
95
96	spin_unlock_irqrestore(&priv->lock, flags);
97
98	return 0;
99}
100EXPORT_SYMBOL_GPL(ipu_smfc_set_watermark);
101
102int ipu_smfc_enable(struct ipu_smfc *smfc)
103{
104	struct ipu_smfc_priv *priv = smfc->priv;
105	unsigned long flags;
106
107	spin_lock_irqsave(&priv->lock, flags);
108
109	if (!priv->use_count)
110		ipu_module_enable(priv->ipu, IPU_CONF_SMFC_EN);
111
112	priv->use_count++;
113
114	spin_unlock_irqrestore(&priv->lock, flags);
115
116	return 0;
117}
118EXPORT_SYMBOL_GPL(ipu_smfc_enable);
119
120int ipu_smfc_disable(struct ipu_smfc *smfc)
121{
122	struct ipu_smfc_priv *priv = smfc->priv;
123	unsigned long flags;
124
125	spin_lock_irqsave(&priv->lock, flags);
126
127	priv->use_count--;
128
129	if (!priv->use_count)
130		ipu_module_disable(priv->ipu, IPU_CONF_SMFC_EN);
131
132	if (priv->use_count < 0)
133		priv->use_count = 0;
134
135	spin_unlock_irqrestore(&priv->lock, flags);
136
137	return 0;
138}
139EXPORT_SYMBOL_GPL(ipu_smfc_disable);
140
141struct ipu_smfc *ipu_smfc_get(struct ipu_soc *ipu, unsigned int chno)
142{
143	struct ipu_smfc_priv *priv = ipu->smfc_priv;
144	struct ipu_smfc *smfc, *ret;
145	unsigned long flags;
146
147	if (chno >= 4)
148		return ERR_PTR(-EINVAL);
149
150	smfc = &priv->channel[chno];
151	ret = smfc;
152
153	spin_lock_irqsave(&priv->lock, flags);
154
155	if (smfc->inuse) {
156		ret = ERR_PTR(-EBUSY);
157		goto unlock;
158	}
159
160	smfc->inuse = true;
161unlock:
162	spin_unlock_irqrestore(&priv->lock, flags);
163	return ret;
164}
165EXPORT_SYMBOL_GPL(ipu_smfc_get);
166
167void ipu_smfc_put(struct ipu_smfc *smfc)
168{
169	struct ipu_smfc_priv *priv = smfc->priv;
170	unsigned long flags;
171
172	spin_lock_irqsave(&priv->lock, flags);
173	smfc->inuse = false;
174	spin_unlock_irqrestore(&priv->lock, flags);
175}
176EXPORT_SYMBOL_GPL(ipu_smfc_put);
177
178int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev,
179		  unsigned long base)
180{
181	struct ipu_smfc_priv *priv;
182	int i;
183
184	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
185	if (!priv)
186		return -ENOMEM;
187
188	ipu->smfc_priv = priv;
189	spin_lock_init(&priv->lock);
190	priv->ipu = ipu;
191
192	priv->base = devm_ioremap(dev, base, PAGE_SIZE);
193	if (!priv->base)
194		return -ENOMEM;
195
196	for (i = 0; i < 4; i++) {
197		priv->channel[i].priv = priv;
198		priv->channel[i].chno = i;
199	}
200
201	pr_debug("%s: ioremap 0x%08lx -> %p\n", __func__, base, priv->base);
202
203	return 0;
204}
205
206void ipu_smfc_exit(struct ipu_soc *ipu)
207{
208}
209