aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/ipu-v3/ipu-dmfc.c
diff options
context:
space:
mode:
authorPhilipp Zabel <p.zabel@pengutronix.de>2013-09-30 10:13:39 -0400
committerPhilipp Zabel <p.zabel@pengutronix.de>2014-06-04 05:06:52 -0400
commit39b9004d1f626b88b775c7655d3f286e135dfec6 (patch)
tree3d439afd8cff80424b05b78aebe00e23b0ed6b7f /drivers/gpu/ipu-v3/ipu-dmfc.c
parentd1db0eea852497762cab43b905b879dfcd3b8987 (diff)
gpu: ipu-v3: Move i.MX IPUv3 core driver out of staging
The i.MX Image Processing Unit (IPU) contains a number of image processing blocks that sit right in the middle between DRM and V4L2. Some of the modules, such as Display Controller, Processor, and Interface (DC, DP, DI) or CMOS Sensor Interface (CSI) and their FIFOs could be assigned to either framework, but others, such as the dma controller (IDMAC) and image converter (IC) can be used by both. The IPUv3 core driver provides an internal API to access the modules, to be used by both DRM and V4L2 IPUv3 drivers. Signed-off-by: Lucas Stach <l.stach@pengutronix.de> Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/gpu/ipu-v3/ipu-dmfc.c')
-rw-r--r--drivers/gpu/ipu-v3/ipu-dmfc.c415
1 files changed, 415 insertions, 0 deletions
diff --git a/drivers/gpu/ipu-v3/ipu-dmfc.c b/drivers/gpu/ipu-v3/ipu-dmfc.c
new file mode 100644
index 000000000000..e1493ab36ca2
--- /dev/null
+++ b/drivers/gpu/ipu-v3/ipu-dmfc.c
@@ -0,0 +1,415 @@
1/*
2 * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
3 * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15#include <linux/export.h>
16#include <linux/types.h>
17#include <linux/errno.h>
18#include <linux/io.h>
19
20#include <video/imx-ipu-v3.h>
21#include "ipu-prv.h"
22
23#define DMFC_RD_CHAN 0x0000
24#define DMFC_WR_CHAN 0x0004
25#define DMFC_WR_CHAN_DEF 0x0008
26#define DMFC_DP_CHAN 0x000c
27#define DMFC_DP_CHAN_DEF 0x0010
28#define DMFC_GENERAL1 0x0014
29#define DMFC_GENERAL2 0x0018
30#define DMFC_IC_CTRL 0x001c
31#define DMFC_STAT 0x0020
32
33#define DMFC_WR_CHAN_1_28 0
34#define DMFC_WR_CHAN_2_41 8
35#define DMFC_WR_CHAN_1C_42 16
36#define DMFC_WR_CHAN_2C_43 24
37
38#define DMFC_DP_CHAN_5B_23 0
39#define DMFC_DP_CHAN_5F_27 8
40#define DMFC_DP_CHAN_6B_24 16
41#define DMFC_DP_CHAN_6F_29 24
42
43#define DMFC_FIFO_SIZE_64 (3 << 3)
44#define DMFC_FIFO_SIZE_128 (2 << 3)
45#define DMFC_FIFO_SIZE_256 (1 << 3)
46#define DMFC_FIFO_SIZE_512 (0 << 3)
47
48#define DMFC_SEGMENT(x) ((x & 0x7) << 0)
49#define DMFC_BURSTSIZE_128 (0 << 6)
50#define DMFC_BURSTSIZE_64 (1 << 6)
51#define DMFC_BURSTSIZE_32 (2 << 6)
52#define DMFC_BURSTSIZE_16 (3 << 6)
53
54struct dmfc_channel_data {
55 int ipu_channel;
56 unsigned long channel_reg;
57 unsigned long shift;
58 unsigned eot_shift;
59 unsigned max_fifo_lines;
60};
61
62static const struct dmfc_channel_data dmfcdata[] = {
63 {
64 .ipu_channel = IPUV3_CHANNEL_MEM_BG_SYNC,
65 .channel_reg = DMFC_DP_CHAN,
66 .shift = DMFC_DP_CHAN_5B_23,
67 .eot_shift = 20,
68 .max_fifo_lines = 3,
69 }, {
70 .ipu_channel = 24,
71 .channel_reg = DMFC_DP_CHAN,
72 .shift = DMFC_DP_CHAN_6B_24,
73 .eot_shift = 22,
74 .max_fifo_lines = 1,
75 }, {
76 .ipu_channel = IPUV3_CHANNEL_MEM_FG_SYNC,
77 .channel_reg = DMFC_DP_CHAN,
78 .shift = DMFC_DP_CHAN_5F_27,
79 .eot_shift = 21,
80 .max_fifo_lines = 2,
81 }, {
82 .ipu_channel = IPUV3_CHANNEL_MEM_DC_SYNC,
83 .channel_reg = DMFC_WR_CHAN,
84 .shift = DMFC_WR_CHAN_1_28,
85 .eot_shift = 16,
86 .max_fifo_lines = 2,
87 }, {
88 .ipu_channel = 29,
89 .channel_reg = DMFC_DP_CHAN,
90 .shift = DMFC_DP_CHAN_6F_29,
91 .eot_shift = 23,
92 .max_fifo_lines = 1,
93 },
94};
95
96#define DMFC_NUM_CHANNELS ARRAY_SIZE(dmfcdata)
97
98struct ipu_dmfc_priv;
99
100struct dmfc_channel {
101 unsigned slots;
102 unsigned slotmask;
103 unsigned segment;
104 int burstsize;
105 struct ipu_soc *ipu;
106 struct ipu_dmfc_priv *priv;
107 const struct dmfc_channel_data *data;
108};
109
110struct ipu_dmfc_priv {
111 struct ipu_soc *ipu;
112 struct device *dev;
113 struct dmfc_channel channels[DMFC_NUM_CHANNELS];
114 struct mutex mutex;
115 unsigned long bandwidth_per_slot;
116 void __iomem *base;
117 int use_count;
118};
119
120int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc)
121{
122 struct ipu_dmfc_priv *priv = dmfc->priv;
123 mutex_lock(&priv->mutex);
124
125 if (!priv->use_count)
126 ipu_module_enable(priv->ipu, IPU_CONF_DMFC_EN);
127
128 priv->use_count++;
129
130 mutex_unlock(&priv->mutex);
131
132 return 0;
133}
134EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel);
135
136void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc)
137{
138 struct ipu_dmfc_priv *priv = dmfc->priv;
139
140 mutex_lock(&priv->mutex);
141
142 priv->use_count--;
143
144 if (!priv->use_count)
145 ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN);
146
147 if (priv->use_count < 0)
148 priv->use_count = 0;
149
150 mutex_unlock(&priv->mutex);
151}
152EXPORT_SYMBOL_GPL(ipu_dmfc_disable_channel);
153
154static int ipu_dmfc_setup_channel(struct dmfc_channel *dmfc, int slots,
155 int segment, int burstsize)
156{
157 struct ipu_dmfc_priv *priv = dmfc->priv;
158 u32 val, field;
159
160 dev_dbg(priv->dev,
161 "dmfc: using %d slots starting from segment %d for IPU channel %d\n",
162 slots, segment, dmfc->data->ipu_channel);
163
164 switch (slots) {
165 case 1:
166 field = DMFC_FIFO_SIZE_64;
167 break;
168 case 2:
169 field = DMFC_FIFO_SIZE_128;
170 break;
171 case 4:
172 field = DMFC_FIFO_SIZE_256;
173 break;
174 case 8:
175 field = DMFC_FIFO_SIZE_512;
176 break;
177 default:
178 return -EINVAL;
179 }
180
181 switch (burstsize) {
182 case 16:
183 field |= DMFC_BURSTSIZE_16;
184 break;
185 case 32:
186 field |= DMFC_BURSTSIZE_32;
187 break;
188 case 64:
189 field |= DMFC_BURSTSIZE_64;
190 break;
191 case 128:
192 field |= DMFC_BURSTSIZE_128;
193 break;
194 }
195
196 field |= DMFC_SEGMENT(segment);
197
198 val = readl(priv->base + dmfc->data->channel_reg);
199
200 val &= ~(0xff << dmfc->data->shift);
201 val |= field << dmfc->data->shift;
202
203 writel(val, priv->base + dmfc->data->channel_reg);
204
205 dmfc->slots = slots;
206 dmfc->segment = segment;
207 dmfc->burstsize = burstsize;
208 dmfc->slotmask = ((1 << slots) - 1) << segment;
209
210 return 0;
211}
212
213static int dmfc_bandwidth_to_slots(struct ipu_dmfc_priv *priv,
214 unsigned long bandwidth)
215{
216 int slots = 1;
217
218 while (slots * priv->bandwidth_per_slot < bandwidth)
219 slots *= 2;
220
221 return slots;
222}
223
224static int dmfc_find_slots(struct ipu_dmfc_priv *priv, int slots)
225{
226 unsigned slotmask_need, slotmask_used = 0;
227 int i, segment = 0;
228
229 slotmask_need = (1 << slots) - 1;
230
231 for (i = 0; i < DMFC_NUM_CHANNELS; i++)
232 slotmask_used |= priv->channels[i].slotmask;
233
234 while (slotmask_need <= 0xff) {
235 if (!(slotmask_used & slotmask_need))
236 return segment;
237
238 slotmask_need <<= 1;
239 segment++;
240 }
241
242 return -EBUSY;
243}
244
245void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc)
246{
247 struct ipu_dmfc_priv *priv = dmfc->priv;
248 int i;
249
250 dev_dbg(priv->dev, "dmfc: freeing %d slots starting from segment %d\n",
251 dmfc->slots, dmfc->segment);
252
253 mutex_lock(&priv->mutex);
254
255 if (!dmfc->slots)
256 goto out;
257
258 dmfc->slotmask = 0;
259 dmfc->slots = 0;
260 dmfc->segment = 0;
261
262 for (i = 0; i < DMFC_NUM_CHANNELS; i++)
263 priv->channels[i].slotmask = 0;
264
265 for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
266 if (priv->channels[i].slots > 0) {
267 priv->channels[i].segment =
268 dmfc_find_slots(priv, priv->channels[i].slots);
269 priv->channels[i].slotmask =
270 ((1 << priv->channels[i].slots) - 1) <<
271 priv->channels[i].segment;
272 }
273 }
274
275 for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
276 if (priv->channels[i].slots > 0)
277 ipu_dmfc_setup_channel(&priv->channels[i],
278 priv->channels[i].slots,
279 priv->channels[i].segment,
280 priv->channels[i].burstsize);
281 }
282out:
283 mutex_unlock(&priv->mutex);
284}
285EXPORT_SYMBOL_GPL(ipu_dmfc_free_bandwidth);
286
287int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc,
288 unsigned long bandwidth_pixel_per_second, int burstsize)
289{
290 struct ipu_dmfc_priv *priv = dmfc->priv;
291 int slots = dmfc_bandwidth_to_slots(priv, bandwidth_pixel_per_second);
292 int segment = -1, ret = 0;
293
294 dev_dbg(priv->dev, "dmfc: trying to allocate %ldMpixel/s for IPU channel %d\n",
295 bandwidth_pixel_per_second / 1000000,
296 dmfc->data->ipu_channel);
297
298 ipu_dmfc_free_bandwidth(dmfc);
299
300 mutex_lock(&priv->mutex);
301
302 if (slots > 8) {
303 ret = -EBUSY;
304 goto out;
305 }
306
307 /* For the MEM_BG channel, first try to allocate twice the slots */
308 if (dmfc->data->ipu_channel == IPUV3_CHANNEL_MEM_BG_SYNC)
309 segment = dmfc_find_slots(priv, slots * 2);
310 else if (slots < 2)
311 /* Always allocate at least 128*4 bytes (2 slots) */
312 slots = 2;
313
314 if (segment >= 0)
315 slots *= 2;
316 else
317 segment = dmfc_find_slots(priv, slots);
318 if (segment < 0) {
319 ret = -EBUSY;
320 goto out;
321 }
322
323 ipu_dmfc_setup_channel(dmfc, slots, segment, burstsize);
324
325out:
326 mutex_unlock(&priv->mutex);
327
328 return ret;
329}
330EXPORT_SYMBOL_GPL(ipu_dmfc_alloc_bandwidth);
331
332int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width)
333{
334 struct ipu_dmfc_priv *priv = dmfc->priv;
335 u32 dmfc_gen1;
336
337 dmfc_gen1 = readl(priv->base + DMFC_GENERAL1);
338
339 if ((dmfc->slots * 64 * 4) / width > dmfc->data->max_fifo_lines)
340 dmfc_gen1 |= 1 << dmfc->data->eot_shift;
341 else
342 dmfc_gen1 &= ~(1 << dmfc->data->eot_shift);
343
344 writel(dmfc_gen1, priv->base + DMFC_GENERAL1);
345
346 return 0;
347}
348EXPORT_SYMBOL_GPL(ipu_dmfc_init_channel);
349
350struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipu_channel)
351{
352 struct ipu_dmfc_priv *priv = ipu->dmfc_priv;
353 int i;
354
355 for (i = 0; i < DMFC_NUM_CHANNELS; i++)
356 if (dmfcdata[i].ipu_channel == ipu_channel)
357 return &priv->channels[i];
358 return ERR_PTR(-ENODEV);
359}
360EXPORT_SYMBOL_GPL(ipu_dmfc_get);
361
362void ipu_dmfc_put(struct dmfc_channel *dmfc)
363{
364 ipu_dmfc_free_bandwidth(dmfc);
365}
366EXPORT_SYMBOL_GPL(ipu_dmfc_put);
367
368int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base,
369 struct clk *ipu_clk)
370{
371 struct ipu_dmfc_priv *priv;
372 int i;
373
374 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
375 if (!priv)
376 return -ENOMEM;
377
378 priv->base = devm_ioremap(dev, base, PAGE_SIZE);
379 if (!priv->base)
380 return -ENOMEM;
381
382 priv->dev = dev;
383 priv->ipu = ipu;
384 mutex_init(&priv->mutex);
385
386 ipu->dmfc_priv = priv;
387
388 for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
389 priv->channels[i].priv = priv;
390 priv->channels[i].ipu = ipu;
391 priv->channels[i].data = &dmfcdata[i];
392 }
393
394 writel(0x0, priv->base + DMFC_WR_CHAN);
395 writel(0x0, priv->base + DMFC_DP_CHAN);
396
397 /*
398 * We have a total bandwidth of clkrate * 4pixel divided
399 * into 8 slots.
400 */
401 priv->bandwidth_per_slot = clk_get_rate(ipu_clk) * 4 / 8;
402
403 dev_dbg(dev, "dmfc: 8 slots with %ldMpixel/s bandwidth each\n",
404 priv->bandwidth_per_slot / 1000000);
405
406 writel(0x202020f6, priv->base + DMFC_WR_CHAN_DEF);
407 writel(0x2020f6f6, priv->base + DMFC_DP_CHAN_DEF);
408 writel(0x00000003, priv->base + DMFC_GENERAL1);
409
410 return 0;
411}
412
413void ipu_dmfc_exit(struct ipu_soc *ipu)
414{
415}