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