Commit | Line | Data |
---|---|---|
ea9c2605 LS |
1 | /* |
2 | * Copyright (c) 2016-2017 Lucas Stach, Pengutronix | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify it | |
5 | * under the terms and conditions of the GNU General Public License, | |
6 | * version 2, as published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope it will be useful, but WITHOUT | |
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
11 | * more details. | |
12 | */ | |
13 | ||
14 | #include <drm/drm_fourcc.h> | |
15 | #include <linux/clk.h> | |
16 | #include <linux/err.h> | |
263c3b80 | 17 | #include <linux/iopoll.h> |
ea9c2605 LS |
18 | #include <linux/mfd/syscon.h> |
19 | #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> | |
20 | #include <linux/module.h> | |
21 | #include <linux/of.h> | |
22 | #include <linux/platform_device.h> | |
72944089 | 23 | #include <linux/pm_runtime.h> |
ea9c2605 LS |
24 | #include <linux/regmap.h> |
25 | #include <video/imx-ipu-v3.h> | |
26 | ||
27 | #include "ipu-prv.h" | |
28 | ||
29 | #define IPU_PRG_CTL 0x00 | |
30 | #define IPU_PRG_CTL_BYPASS(i) (1 << (0 + i)) | |
31 | #define IPU_PRG_CTL_SOFT_ARID_MASK 0x3 | |
32 | #define IPU_PRG_CTL_SOFT_ARID_SHIFT(i) (8 + i * 2) | |
33 | #define IPU_PRG_CTL_SOFT_ARID(i, v) ((v & 0x3) << (8 + 2 * i)) | |
34 | #define IPU_PRG_CTL_SO(i) (1 << (16 + i)) | |
35 | #define IPU_PRG_CTL_VFLIP(i) (1 << (19 + i)) | |
36 | #define IPU_PRG_CTL_BLOCK_MODE(i) (1 << (22 + i)) | |
37 | #define IPU_PRG_CTL_CNT_LOAD_EN(i) (1 << (25 + i)) | |
38 | #define IPU_PRG_CTL_SOFTRST (1 << 30) | |
39 | #define IPU_PRG_CTL_SHADOW_EN (1 << 31) | |
40 | ||
41 | #define IPU_PRG_STATUS 0x04 | |
42 | #define IPU_PRG_STATUS_BUFFER0_READY(i) (1 << (0 + i * 2)) | |
43 | #define IPU_PRG_STATUS_BUFFER1_READY(i) (1 << (1 + i * 2)) | |
44 | ||
45 | #define IPU_PRG_QOS 0x08 | |
46 | #define IPU_PRG_QOS_ARID_MASK 0xf | |
47 | #define IPU_PRG_QOS_ARID_SHIFT(i) (0 + i * 4) | |
48 | ||
49 | #define IPU_PRG_REG_UPDATE 0x0c | |
50 | #define IPU_PRG_REG_UPDATE_REG_UPDATE (1 << 0) | |
51 | ||
52 | #define IPU_PRG_STRIDE(i) (0x10 + i * 0x4) | |
53 | #define IPU_PRG_STRIDE_STRIDE_MASK 0x3fff | |
54 | ||
55 | #define IPU_PRG_CROP_LINE 0x1c | |
56 | ||
57 | #define IPU_PRG_THD 0x20 | |
58 | ||
59 | #define IPU_PRG_BADDR(i) (0x24 + i * 0x4) | |
60 | ||
61 | #define IPU_PRG_OFFSET(i) (0x30 + i * 0x4) | |
62 | ||
63 | #define IPU_PRG_ILO(i) (0x3c + i * 0x4) | |
64 | ||
65 | #define IPU_PRG_HEIGHT(i) (0x48 + i * 0x4) | |
66 | #define IPU_PRG_HEIGHT_PRE_HEIGHT_MASK 0xfff | |
67 | #define IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT 0 | |
68 | #define IPU_PRG_HEIGHT_IPU_HEIGHT_MASK 0xfff | |
69 | #define IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT 16 | |
70 | ||
71 | struct ipu_prg_channel { | |
72 | bool enabled; | |
73 | int used_pre; | |
74 | }; | |
75 | ||
76 | struct ipu_prg { | |
77 | struct list_head list; | |
78 | struct device *dev; | |
79 | int id; | |
80 | ||
81 | void __iomem *regs; | |
82 | struct clk *clk_ipg, *clk_axi; | |
83 | struct regmap *iomuxc_gpr; | |
84 | struct ipu_pre *pres[3]; | |
85 | ||
86 | struct ipu_prg_channel chan[3]; | |
87 | }; | |
88 | ||
89 | static DEFINE_MUTEX(ipu_prg_list_mutex); | |
90 | static LIST_HEAD(ipu_prg_list); | |
91 | ||
92 | struct ipu_prg * | |
93 | ipu_prg_lookup_by_phandle(struct device *dev, const char *name, int ipu_id) | |
94 | { | |
95 | struct device_node *prg_node = of_parse_phandle(dev->of_node, | |
96 | name, 0); | |
97 | struct ipu_prg *prg; | |
98 | ||
99 | mutex_lock(&ipu_prg_list_mutex); | |
100 | list_for_each_entry(prg, &ipu_prg_list, list) { | |
101 | if (prg_node == prg->dev->of_node) { | |
102 | mutex_unlock(&ipu_prg_list_mutex); | |
e88728f4 VG |
103 | device_link_add(dev, prg->dev, |
104 | DL_FLAG_AUTOREMOVE_CONSUMER); | |
ea9c2605 | 105 | prg->id = ipu_id; |
3addaba8 | 106 | of_node_put(prg_node); |
ea9c2605 LS |
107 | return prg; |
108 | } | |
109 | } | |
110 | mutex_unlock(&ipu_prg_list_mutex); | |
111 | ||
3addaba8 TJ |
112 | of_node_put(prg_node); |
113 | ||
ea9c2605 LS |
114 | return NULL; |
115 | } | |
116 | ||
117 | int ipu_prg_max_active_channels(void) | |
118 | { | |
119 | return ipu_pre_get_available_count(); | |
120 | } | |
121 | EXPORT_SYMBOL_GPL(ipu_prg_max_active_channels); | |
122 | ||
123 | bool ipu_prg_present(struct ipu_soc *ipu) | |
124 | { | |
125 | if (ipu->prg_priv) | |
126 | return true; | |
127 | ||
128 | return false; | |
129 | } | |
130 | EXPORT_SYMBOL_GPL(ipu_prg_present); | |
131 | ||
132 | bool ipu_prg_format_supported(struct ipu_soc *ipu, uint32_t format, | |
133 | uint64_t modifier) | |
134 | { | |
135 | const struct drm_format_info *info = drm_format_info(format); | |
136 | ||
137 | if (info->num_planes != 1) | |
138 | return false; | |
139 | ||
a2ceec52 LS |
140 | switch (modifier) { |
141 | case DRM_FORMAT_MOD_LINEAR: | |
142 | case DRM_FORMAT_MOD_VIVANTE_TILED: | |
143 | case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED: | |
144 | return true; | |
145 | default: | |
146 | return false; | |
147 | } | |
ea9c2605 LS |
148 | } |
149 | EXPORT_SYMBOL_GPL(ipu_prg_format_supported); | |
150 | ||
151 | int ipu_prg_enable(struct ipu_soc *ipu) | |
152 | { | |
153 | struct ipu_prg *prg = ipu->prg_priv; | |
ea9c2605 LS |
154 | |
155 | if (!prg) | |
156 | return 0; | |
157 | ||
72944089 | 158 | return pm_runtime_get_sync(prg->dev); |
ea9c2605 LS |
159 | } |
160 | EXPORT_SYMBOL_GPL(ipu_prg_enable); | |
161 | ||
162 | void ipu_prg_disable(struct ipu_soc *ipu) | |
163 | { | |
164 | struct ipu_prg *prg = ipu->prg_priv; | |
165 | ||
166 | if (!prg) | |
167 | return; | |
168 | ||
72944089 | 169 | pm_runtime_put(prg->dev); |
ea9c2605 LS |
170 | } |
171 | EXPORT_SYMBOL_GPL(ipu_prg_disable); | |
172 | ||
173 | /* | |
174 | * The channel configuartion functions below are not thread safe, as they | |
175 | * must be only called from the atomic commit path in the DRM driver, which | |
176 | * is properly serialized. | |
177 | */ | |
178 | static int ipu_prg_ipu_to_prg_chan(int ipu_chan) | |
179 | { | |
180 | /* | |
181 | * This isn't clearly documented in the RM, but IPU to PRG channel | |
182 | * assignment is fixed, as only with this mapping the control signals | |
183 | * match up. | |
184 | */ | |
185 | switch (ipu_chan) { | |
186 | case IPUV3_CHANNEL_MEM_BG_SYNC: | |
187 | return 0; | |
188 | case IPUV3_CHANNEL_MEM_FG_SYNC: | |
189 | return 1; | |
190 | case IPUV3_CHANNEL_MEM_DC_SYNC: | |
191 | return 2; | |
192 | default: | |
193 | return -EINVAL; | |
194 | } | |
195 | } | |
196 | ||
197 | static int ipu_prg_get_pre(struct ipu_prg *prg, int prg_chan) | |
198 | { | |
199 | int i, ret; | |
200 | ||
201 | /* channel 0 is special as it is hardwired to one of the PREs */ | |
202 | if (prg_chan == 0) { | |
203 | ret = ipu_pre_get(prg->pres[0]); | |
204 | if (ret) | |
205 | goto fail; | |
206 | prg->chan[prg_chan].used_pre = 0; | |
207 | return 0; | |
208 | } | |
209 | ||
210 | for (i = 1; i < 3; i++) { | |
211 | ret = ipu_pre_get(prg->pres[i]); | |
212 | if (!ret) { | |
213 | u32 val, mux; | |
214 | int shift; | |
215 | ||
216 | prg->chan[prg_chan].used_pre = i; | |
217 | ||
218 | /* configure the PRE to PRG channel mux */ | |
219 | shift = (i == 1) ? 12 : 14; | |
220 | mux = (prg->id << 1) | (prg_chan - 1); | |
221 | regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5, | |
222 | 0x3 << shift, mux << shift); | |
223 | ||
224 | /* check other mux, must not point to same channel */ | |
225 | shift = (i == 1) ? 14 : 12; | |
226 | regmap_read(prg->iomuxc_gpr, IOMUXC_GPR5, &val); | |
227 | if (((val >> shift) & 0x3) == mux) { | |
228 | regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5, | |
229 | 0x3 << shift, | |
230 | (mux ^ 0x1) << shift); | |
231 | } | |
232 | ||
233 | return 0; | |
234 | } | |
235 | } | |
236 | ||
237 | fail: | |
238 | dev_err(prg->dev, "could not get PRE for PRG chan %d", prg_chan); | |
239 | return ret; | |
240 | } | |
241 | ||
242 | static void ipu_prg_put_pre(struct ipu_prg *prg, int prg_chan) | |
243 | { | |
244 | struct ipu_prg_channel *chan = &prg->chan[prg_chan]; | |
245 | ||
246 | ipu_pre_put(prg->pres[chan->used_pre]); | |
247 | chan->used_pre = -1; | |
248 | } | |
249 | ||
250 | void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan) | |
251 | { | |
252 | int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); | |
253 | struct ipu_prg *prg = ipu_chan->ipu->prg_priv; | |
746d024c | 254 | struct ipu_prg_channel *chan; |
ea9c2605 LS |
255 | u32 val; |
256 | ||
746d024c AB |
257 | if (prg_chan < 0) |
258 | return; | |
259 | ||
260 | chan = &prg->chan[prg_chan]; | |
261 | if (!chan->enabled) | |
ea9c2605 LS |
262 | return; |
263 | ||
72944089 | 264 | pm_runtime_get_sync(prg->dev); |
ea9c2605 LS |
265 | |
266 | val = readl(prg->regs + IPU_PRG_CTL); | |
267 | val |= IPU_PRG_CTL_BYPASS(prg_chan); | |
268 | writel(val, prg->regs + IPU_PRG_CTL); | |
269 | ||
270 | val = IPU_PRG_REG_UPDATE_REG_UPDATE; | |
271 | writel(val, prg->regs + IPU_PRG_REG_UPDATE); | |
272 | ||
72944089 | 273 | pm_runtime_put(prg->dev); |
ea9c2605 LS |
274 | |
275 | ipu_prg_put_pre(prg, prg_chan); | |
276 | ||
277 | chan->enabled = false; | |
278 | } | |
279 | EXPORT_SYMBOL_GPL(ipu_prg_channel_disable); | |
280 | ||
281 | int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan, | |
282 | unsigned int axi_id, unsigned int width, | |
283 | unsigned int height, unsigned int stride, | |
a2ceec52 | 284 | u32 format, uint64_t modifier, unsigned long *eba) |
ea9c2605 LS |
285 | { |
286 | int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); | |
287 | struct ipu_prg *prg = ipu_chan->ipu->prg_priv; | |
746d024c | 288 | struct ipu_prg_channel *chan; |
ea9c2605 LS |
289 | u32 val; |
290 | int ret; | |
291 | ||
292 | if (prg_chan < 0) | |
293 | return prg_chan; | |
294 | ||
746d024c AB |
295 | chan = &prg->chan[prg_chan]; |
296 | ||
ea9c2605 LS |
297 | if (chan->enabled) { |
298 | ipu_pre_update(prg->pres[chan->used_pre], *eba); | |
299 | return 0; | |
300 | } | |
301 | ||
302 | ret = ipu_prg_get_pre(prg, prg_chan); | |
303 | if (ret) | |
304 | return ret; | |
305 | ||
306 | ipu_pre_configure(prg->pres[chan->used_pre], | |
a2ceec52 | 307 | width, height, stride, format, modifier, *eba); |
ea9c2605 LS |
308 | |
309 | ||
72944089 | 310 | pm_runtime_get_sync(prg->dev); |
ea9c2605 LS |
311 | |
312 | val = (stride - 1) & IPU_PRG_STRIDE_STRIDE_MASK; | |
313 | writel(val, prg->regs + IPU_PRG_STRIDE(prg_chan)); | |
314 | ||
315 | val = ((height & IPU_PRG_HEIGHT_PRE_HEIGHT_MASK) << | |
316 | IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT) | | |
317 | ((height & IPU_PRG_HEIGHT_IPU_HEIGHT_MASK) << | |
318 | IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT); | |
319 | writel(val, prg->regs + IPU_PRG_HEIGHT(prg_chan)); | |
320 | ||
321 | val = ipu_pre_get_baddr(prg->pres[chan->used_pre]); | |
322 | *eba = val; | |
323 | writel(val, prg->regs + IPU_PRG_BADDR(prg_chan)); | |
324 | ||
325 | val = readl(prg->regs + IPU_PRG_CTL); | |
ea9c2605 LS |
326 | /* config AXI ID */ |
327 | val &= ~(IPU_PRG_CTL_SOFT_ARID_MASK << | |
328 | IPU_PRG_CTL_SOFT_ARID_SHIFT(prg_chan)); | |
329 | val |= IPU_PRG_CTL_SOFT_ARID(prg_chan, axi_id); | |
330 | /* enable channel */ | |
331 | val &= ~IPU_PRG_CTL_BYPASS(prg_chan); | |
332 | writel(val, prg->regs + IPU_PRG_CTL); | |
333 | ||
334 | val = IPU_PRG_REG_UPDATE_REG_UPDATE; | |
335 | writel(val, prg->regs + IPU_PRG_REG_UPDATE); | |
336 | ||
263c3b80 LS |
337 | /* wait for both double buffers to be filled */ |
338 | readl_poll_timeout(prg->regs + IPU_PRG_STATUS, val, | |
339 | (val & IPU_PRG_STATUS_BUFFER0_READY(prg_chan)) && | |
340 | (val & IPU_PRG_STATUS_BUFFER1_READY(prg_chan)), | |
341 | 5, 1000); | |
342 | ||
72944089 | 343 | pm_runtime_put(prg->dev); |
ea9c2605 LS |
344 | |
345 | chan->enabled = true; | |
346 | return 0; | |
347 | } | |
348 | EXPORT_SYMBOL_GPL(ipu_prg_channel_configure); | |
349 | ||
4bfbd561 LS |
350 | bool ipu_prg_channel_configure_pending(struct ipuv3_channel *ipu_chan) |
351 | { | |
352 | int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); | |
353 | struct ipu_prg *prg = ipu_chan->ipu->prg_priv; | |
354 | struct ipu_prg_channel *chan; | |
355 | ||
356 | if (prg_chan < 0) | |
357 | return false; | |
358 | ||
359 | chan = &prg->chan[prg_chan]; | |
360 | WARN_ON(!chan->enabled); | |
361 | ||
362 | return ipu_pre_update_pending(prg->pres[chan->used_pre]); | |
363 | } | |
364 | EXPORT_SYMBOL_GPL(ipu_prg_channel_configure_pending); | |
365 | ||
ea9c2605 LS |
366 | static int ipu_prg_probe(struct platform_device *pdev) |
367 | { | |
368 | struct device *dev = &pdev->dev; | |
369 | struct resource *res; | |
370 | struct ipu_prg *prg; | |
371 | u32 val; | |
372 | int i, ret; | |
373 | ||
374 | prg = devm_kzalloc(dev, sizeof(*prg), GFP_KERNEL); | |
375 | if (!prg) | |
376 | return -ENOMEM; | |
377 | ||
378 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
379 | prg->regs = devm_ioremap_resource(&pdev->dev, res); | |
380 | if (IS_ERR(prg->regs)) | |
381 | return PTR_ERR(prg->regs); | |
382 | ||
383 | ||
384 | prg->clk_ipg = devm_clk_get(dev, "ipg"); | |
385 | if (IS_ERR(prg->clk_ipg)) | |
386 | return PTR_ERR(prg->clk_ipg); | |
387 | ||
388 | prg->clk_axi = devm_clk_get(dev, "axi"); | |
389 | if (IS_ERR(prg->clk_axi)) | |
390 | return PTR_ERR(prg->clk_axi); | |
391 | ||
392 | prg->iomuxc_gpr = | |
393 | syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); | |
394 | if (IS_ERR(prg->iomuxc_gpr)) | |
395 | return PTR_ERR(prg->iomuxc_gpr); | |
396 | ||
397 | for (i = 0; i < 3; i++) { | |
398 | prg->pres[i] = ipu_pre_lookup_by_phandle(dev, "fsl,pres", i); | |
399 | if (!prg->pres[i]) | |
400 | return -EPROBE_DEFER; | |
401 | } | |
402 | ||
403 | ret = clk_prepare_enable(prg->clk_ipg); | |
404 | if (ret) | |
405 | return ret; | |
406 | ||
72944089 LS |
407 | ret = clk_prepare_enable(prg->clk_axi); |
408 | if (ret) { | |
409 | clk_disable_unprepare(prg->clk_ipg); | |
410 | return ret; | |
411 | } | |
412 | ||
ea9c2605 LS |
413 | /* init to free running mode */ |
414 | val = readl(prg->regs + IPU_PRG_CTL); | |
415 | val |= IPU_PRG_CTL_SHADOW_EN; | |
416 | writel(val, prg->regs + IPU_PRG_CTL); | |
417 | ||
418 | /* disable address threshold */ | |
419 | writel(0xffffffff, prg->regs + IPU_PRG_THD); | |
420 | ||
72944089 LS |
421 | pm_runtime_set_active(dev); |
422 | pm_runtime_enable(dev); | |
ea9c2605 LS |
423 | |
424 | prg->dev = dev; | |
425 | platform_set_drvdata(pdev, prg); | |
426 | mutex_lock(&ipu_prg_list_mutex); | |
427 | list_add(&prg->list, &ipu_prg_list); | |
428 | mutex_unlock(&ipu_prg_list_mutex); | |
429 | ||
430 | return 0; | |
431 | } | |
432 | ||
433 | static int ipu_prg_remove(struct platform_device *pdev) | |
434 | { | |
435 | struct ipu_prg *prg = platform_get_drvdata(pdev); | |
436 | ||
437 | mutex_lock(&ipu_prg_list_mutex); | |
438 | list_del(&prg->list); | |
439 | mutex_unlock(&ipu_prg_list_mutex); | |
440 | ||
441 | return 0; | |
442 | } | |
443 | ||
72944089 LS |
444 | #ifdef CONFIG_PM |
445 | static int prg_suspend(struct device *dev) | |
446 | { | |
447 | struct ipu_prg *prg = dev_get_drvdata(dev); | |
448 | ||
449 | clk_disable_unprepare(prg->clk_axi); | |
450 | clk_disable_unprepare(prg->clk_ipg); | |
451 | ||
452 | return 0; | |
453 | } | |
454 | ||
455 | static int prg_resume(struct device *dev) | |
456 | { | |
457 | struct ipu_prg *prg = dev_get_drvdata(dev); | |
458 | int ret; | |
459 | ||
460 | ret = clk_prepare_enable(prg->clk_ipg); | |
461 | if (ret) | |
462 | return ret; | |
463 | ||
464 | ret = clk_prepare_enable(prg->clk_axi); | |
465 | if (ret) { | |
466 | clk_disable_unprepare(prg->clk_ipg); | |
467 | return ret; | |
468 | } | |
469 | ||
470 | return 0; | |
471 | } | |
472 | #endif | |
473 | ||
474 | static const struct dev_pm_ops prg_pm_ops = { | |
475 | SET_RUNTIME_PM_OPS(prg_suspend, prg_resume, NULL) | |
476 | }; | |
477 | ||
ea9c2605 LS |
478 | static const struct of_device_id ipu_prg_dt_ids[] = { |
479 | { .compatible = "fsl,imx6qp-prg", }, | |
480 | { /* sentinel */ }, | |
481 | }; | |
482 | ||
483 | struct platform_driver ipu_prg_drv = { | |
484 | .probe = ipu_prg_probe, | |
485 | .remove = ipu_prg_remove, | |
486 | .driver = { | |
487 | .name = "imx-ipu-prg", | |
72944089 | 488 | .pm = &prg_pm_ops, |
ea9c2605 LS |
489 | .of_match_table = ipu_prg_dt_ids, |
490 | }, | |
491 | }; |