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