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