Merge tag 'drm-intel-next-2014-09-05' of git://anongit.freedesktop.org/drm-intel...
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / ipu-v3 / ipu-smfc.c
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 }