diff options
Diffstat (limited to 'drivers/gpu/ipu-v3/ipu-dmfc.c')
-rw-r--r-- | drivers/gpu/ipu-v3/ipu-dmfc.c | 415 |
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 | |||
54 | struct 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 | |||
62 | static 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 | |||
98 | struct ipu_dmfc_priv; | ||
99 | |||
100 | struct 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 | |||
110 | struct 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 | |||
120 | int 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 | } | ||
134 | EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel); | ||
135 | |||
136 | void 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 | } | ||
152 | EXPORT_SYMBOL_GPL(ipu_dmfc_disable_channel); | ||
153 | |||
154 | static 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 | |||
213 | static 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 | |||
224 | static 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 | |||
245 | void 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 | } | ||
282 | out: | ||
283 | mutex_unlock(&priv->mutex); | ||
284 | } | ||
285 | EXPORT_SYMBOL_GPL(ipu_dmfc_free_bandwidth); | ||
286 | |||
287 | int 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 | |||
325 | out: | ||
326 | mutex_unlock(&priv->mutex); | ||
327 | |||
328 | return ret; | ||
329 | } | ||
330 | EXPORT_SYMBOL_GPL(ipu_dmfc_alloc_bandwidth); | ||
331 | |||
332 | int 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 | } | ||
348 | EXPORT_SYMBOL_GPL(ipu_dmfc_init_channel); | ||
349 | |||
350 | struct 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 | } | ||
360 | EXPORT_SYMBOL_GPL(ipu_dmfc_get); | ||
361 | |||
362 | void ipu_dmfc_put(struct dmfc_channel *dmfc) | ||
363 | { | ||
364 | ipu_dmfc_free_bandwidth(dmfc); | ||
365 | } | ||
366 | EXPORT_SYMBOL_GPL(ipu_dmfc_put); | ||
367 | |||
368 | int 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 | |||
413 | void ipu_dmfc_exit(struct ipu_soc *ipu) | ||
414 | { | ||
415 | } | ||