xref: /openbmc/linux/drivers/gpu/ipu-v3/ipu-smfc.c (revision bc5aa3a0)
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 
23 struct ipu_smfc {
24 	struct ipu_smfc_priv *priv;
25 	int chno;
26 	bool inuse;
27 };
28 
29 struct 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 
42 int 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 }
60 EXPORT_SYMBOL_GPL(ipu_smfc_set_burstsize);
61 
62 int 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 }
80 EXPORT_SYMBOL_GPL(ipu_smfc_map_channel);
81 
82 int 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 }
100 EXPORT_SYMBOL_GPL(ipu_smfc_set_watermark);
101 
102 int 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 }
118 EXPORT_SYMBOL_GPL(ipu_smfc_enable);
119 
120 int 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 }
139 EXPORT_SYMBOL_GPL(ipu_smfc_disable);
140 
141 struct 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;
161 unlock:
162 	spin_unlock_irqrestore(&priv->lock, flags);
163 	return ret;
164 }
165 EXPORT_SYMBOL_GPL(ipu_smfc_get);
166 
167 void 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 }
176 EXPORT_SYMBOL_GPL(ipu_smfc_put);
177 
178 int 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 
206 void ipu_smfc_exit(struct ipu_soc *ipu)
207 {
208 }
209