xref: /openbmc/linux/drivers/gpu/ipu-v3/ipu-smfc.c (revision fcaf2036)
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 
17 struct ipu_smfc {
18 	struct ipu_smfc_priv *priv;
19 	int chno;
20 	bool inuse;
21 };
22 
23 struct 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 
ipu_smfc_set_burstsize(struct ipu_smfc * smfc,int burstsize)36 int 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 }
54 EXPORT_SYMBOL_GPL(ipu_smfc_set_burstsize);
55 
ipu_smfc_map_channel(struct ipu_smfc * smfc,int csi_id,int mipi_id)56 int 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 }
74 EXPORT_SYMBOL_GPL(ipu_smfc_map_channel);
75 
ipu_smfc_set_watermark(struct ipu_smfc * smfc,u32 set_level,u32 clr_level)76 int 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 }
94 EXPORT_SYMBOL_GPL(ipu_smfc_set_watermark);
95 
ipu_smfc_enable(struct ipu_smfc * smfc)96 int 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 }
112 EXPORT_SYMBOL_GPL(ipu_smfc_enable);
113 
ipu_smfc_disable(struct ipu_smfc * smfc)114 int 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 }
133 EXPORT_SYMBOL_GPL(ipu_smfc_disable);
134 
ipu_smfc_get(struct ipu_soc * ipu,unsigned int chno)135 struct 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;
155 unlock:
156 	spin_unlock_irqrestore(&priv->lock, flags);
157 	return ret;
158 }
159 EXPORT_SYMBOL_GPL(ipu_smfc_get);
160 
ipu_smfc_put(struct ipu_smfc * smfc)161 void 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 }
170 EXPORT_SYMBOL_GPL(ipu_smfc_put);
171 
ipu_smfc_init(struct ipu_soc * ipu,struct device * dev,unsigned long base)172 int 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 
ipu_smfc_exit(struct ipu_soc * ipu)200 void ipu_smfc_exit(struct ipu_soc *ipu)
201 {
202 }
203