Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/ipu-v3/ipu-dp.c
26428 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Copyright (c) 2010 Sascha Hauer <[email protected]>
4
* Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
5
*/
6
#include <linux/export.h>
7
#include <linux/kernel.h>
8
#include <linux/types.h>
9
#include <linux/errno.h>
10
#include <linux/io.h>
11
#include <linux/err.h>
12
13
#include <drm/drm_color_mgmt.h>
14
#include <video/imx-ipu-v3.h>
15
#include "ipu-prv.h"
16
17
#define DP_SYNC 0
18
#define DP_ASYNC0 0x60
19
#define DP_ASYNC1 0xBC
20
21
#define DP_COM_CONF 0x0
22
#define DP_GRAPH_WIND_CTRL 0x0004
23
#define DP_FG_POS 0x0008
24
#define DP_CSC_A_0 0x0044
25
#define DP_CSC_A_1 0x0048
26
#define DP_CSC_A_2 0x004C
27
#define DP_CSC_A_3 0x0050
28
#define DP_CSC_0 0x0054
29
#define DP_CSC_1 0x0058
30
31
#define DP_COM_CONF_FG_EN (1 << 0)
32
#define DP_COM_CONF_GWSEL (1 << 1)
33
#define DP_COM_CONF_GWAM (1 << 2)
34
#define DP_COM_CONF_GWCKE (1 << 3)
35
#define DP_COM_CONF_CSC_DEF_MASK (3 << 8)
36
#define DP_COM_CONF_CSC_DEF_OFFSET 8
37
#define DP_COM_CONF_CSC_DEF_FG (3 << 8)
38
#define DP_COM_CONF_CSC_DEF_BG (2 << 8)
39
#define DP_COM_CONF_CSC_DEF_BOTH (1 << 8)
40
41
#define IPUV3_NUM_FLOWS 3
42
43
struct ipu_dp_priv;
44
45
struct ipu_dp {
46
u32 flow;
47
bool in_use;
48
bool foreground;
49
enum ipu_color_space in_cs;
50
};
51
52
struct ipu_flow {
53
struct ipu_dp foreground;
54
struct ipu_dp background;
55
enum ipu_color_space out_cs;
56
void __iomem *base;
57
struct ipu_dp_priv *priv;
58
};
59
60
struct ipu_dp_priv {
61
struct ipu_soc *ipu;
62
struct device *dev;
63
void __iomem *base;
64
struct ipu_flow flow[IPUV3_NUM_FLOWS];
65
struct mutex mutex;
66
int use_count;
67
};
68
69
static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1};
70
71
static inline struct ipu_flow *to_flow(struct ipu_dp *dp)
72
{
73
if (dp->foreground)
74
return container_of(dp, struct ipu_flow, foreground);
75
else
76
return container_of(dp, struct ipu_flow, background);
77
}
78
79
int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable,
80
u8 alpha, bool bg_chan)
81
{
82
struct ipu_flow *flow = to_flow(dp);
83
struct ipu_dp_priv *priv = flow->priv;
84
u32 reg;
85
86
mutex_lock(&priv->mutex);
87
88
reg = readl(flow->base + DP_COM_CONF);
89
if (bg_chan)
90
reg &= ~DP_COM_CONF_GWSEL;
91
else
92
reg |= DP_COM_CONF_GWSEL;
93
writel(reg, flow->base + DP_COM_CONF);
94
95
if (enable) {
96
reg = readl(flow->base + DP_GRAPH_WIND_CTRL) & 0x00FFFFFFL;
97
writel(reg | ((u32) alpha << 24),
98
flow->base + DP_GRAPH_WIND_CTRL);
99
100
reg = readl(flow->base + DP_COM_CONF);
101
writel(reg | DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
102
} else {
103
reg = readl(flow->base + DP_COM_CONF);
104
writel(reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
105
}
106
107
ipu_srm_dp_update(priv->ipu, true);
108
109
mutex_unlock(&priv->mutex);
110
111
return 0;
112
}
113
EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha);
114
115
int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos)
116
{
117
struct ipu_flow *flow = to_flow(dp);
118
struct ipu_dp_priv *priv = flow->priv;
119
120
writel((x_pos << 16) | y_pos, flow->base + DP_FG_POS);
121
122
ipu_srm_dp_update(priv->ipu, true);
123
124
return 0;
125
}
126
EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos);
127
128
static void ipu_dp_csc_init(struct ipu_flow *flow,
129
enum drm_color_encoding ycbcr_enc,
130
enum drm_color_range range,
131
enum ipu_color_space in,
132
enum ipu_color_space out,
133
u32 place)
134
{
135
u32 reg;
136
137
reg = readl(flow->base + DP_COM_CONF);
138
reg &= ~DP_COM_CONF_CSC_DEF_MASK;
139
140
if (in == out) {
141
writel(reg, flow->base + DP_COM_CONF);
142
return;
143
}
144
145
if (in == IPUV3_COLORSPACE_RGB && out == IPUV3_COLORSPACE_YUV) {
146
writel(0x099 | (0x12d << 16), flow->base + DP_CSC_A_0);
147
writel(0x03a | (0x3a9 << 16), flow->base + DP_CSC_A_1);
148
writel(0x356 | (0x100 << 16), flow->base + DP_CSC_A_2);
149
writel(0x100 | (0x329 << 16), flow->base + DP_CSC_A_3);
150
writel(0x3d6 | (0x0000 << 16) | (2 << 30),
151
flow->base + DP_CSC_0);
152
writel(0x200 | (2 << 14) | (0x200 << 16) | (2 << 30),
153
flow->base + DP_CSC_1);
154
} else if (ycbcr_enc == DRM_COLOR_YCBCR_BT709) {
155
/* Rec.709 limited range */
156
writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0);
157
writel(0x0e5 | (0x095 << 16), flow->base + DP_CSC_A_1);
158
writel(0x3e5 | (0x3bc << 16), flow->base + DP_CSC_A_2);
159
writel(0x095 | (0x10e << 16), flow->base + DP_CSC_A_3);
160
writel(0x000 | (0x3e10 << 16) | (1 << 30),
161
flow->base + DP_CSC_0);
162
writel(0x09a | (1 << 14) | (0x3dbe << 16) | (1 << 30),
163
flow->base + DP_CSC_1);
164
} else {
165
/* BT.601 limited range */
166
writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0);
167
writel(0x0cc | (0x095 << 16), flow->base + DP_CSC_A_1);
168
writel(0x3ce | (0x398 << 16), flow->base + DP_CSC_A_2);
169
writel(0x095 | (0x0ff << 16), flow->base + DP_CSC_A_3);
170
writel(0x000 | (0x3e42 << 16) | (1 << 30),
171
flow->base + DP_CSC_0);
172
writel(0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30),
173
flow->base + DP_CSC_1);
174
}
175
176
reg |= place;
177
178
writel(reg, flow->base + DP_COM_CONF);
179
}
180
181
int ipu_dp_setup_channel(struct ipu_dp *dp,
182
enum drm_color_encoding ycbcr_enc,
183
enum drm_color_range range,
184
enum ipu_color_space in,
185
enum ipu_color_space out)
186
{
187
struct ipu_flow *flow = to_flow(dp);
188
struct ipu_dp_priv *priv = flow->priv;
189
190
mutex_lock(&priv->mutex);
191
192
dp->in_cs = in;
193
194
if (!dp->foreground)
195
flow->out_cs = out;
196
197
if (flow->foreground.in_cs == flow->background.in_cs) {
198
/*
199
* foreground and background are of same colorspace, put
200
* colorspace converter after combining unit.
201
*/
202
ipu_dp_csc_init(flow, ycbcr_enc, range,
203
flow->foreground.in_cs, flow->out_cs,
204
DP_COM_CONF_CSC_DEF_BOTH);
205
} else {
206
if (flow->foreground.in_cs == IPUV3_COLORSPACE_UNKNOWN ||
207
flow->foreground.in_cs == flow->out_cs)
208
/*
209
* foreground identical to output, apply color
210
* conversion on background
211
*/
212
ipu_dp_csc_init(flow, ycbcr_enc, range,
213
flow->background.in_cs,
214
flow->out_cs, DP_COM_CONF_CSC_DEF_BG);
215
else
216
ipu_dp_csc_init(flow, ycbcr_enc, range,
217
flow->foreground.in_cs,
218
flow->out_cs, DP_COM_CONF_CSC_DEF_FG);
219
}
220
221
ipu_srm_dp_update(priv->ipu, true);
222
223
mutex_unlock(&priv->mutex);
224
225
return 0;
226
}
227
EXPORT_SYMBOL_GPL(ipu_dp_setup_channel);
228
229
int ipu_dp_enable(struct ipu_soc *ipu)
230
{
231
struct ipu_dp_priv *priv = ipu->dp_priv;
232
233
mutex_lock(&priv->mutex);
234
235
if (!priv->use_count)
236
ipu_module_enable(priv->ipu, IPU_CONF_DP_EN);
237
238
priv->use_count++;
239
240
mutex_unlock(&priv->mutex);
241
242
return 0;
243
}
244
EXPORT_SYMBOL_GPL(ipu_dp_enable);
245
246
int ipu_dp_enable_channel(struct ipu_dp *dp)
247
{
248
struct ipu_flow *flow = to_flow(dp);
249
struct ipu_dp_priv *priv = flow->priv;
250
u32 reg;
251
252
if (!dp->foreground)
253
return 0;
254
255
mutex_lock(&priv->mutex);
256
257
reg = readl(flow->base + DP_COM_CONF);
258
reg |= DP_COM_CONF_FG_EN;
259
writel(reg, flow->base + DP_COM_CONF);
260
261
ipu_srm_dp_update(priv->ipu, true);
262
263
mutex_unlock(&priv->mutex);
264
265
return 0;
266
}
267
EXPORT_SYMBOL_GPL(ipu_dp_enable_channel);
268
269
void ipu_dp_disable_channel(struct ipu_dp *dp, bool sync)
270
{
271
struct ipu_flow *flow = to_flow(dp);
272
struct ipu_dp_priv *priv = flow->priv;
273
u32 reg, csc;
274
275
dp->in_cs = IPUV3_COLORSPACE_UNKNOWN;
276
277
if (!dp->foreground)
278
return;
279
280
mutex_lock(&priv->mutex);
281
282
reg = readl(flow->base + DP_COM_CONF);
283
csc = reg & DP_COM_CONF_CSC_DEF_MASK;
284
reg &= ~DP_COM_CONF_CSC_DEF_MASK;
285
if (csc == DP_COM_CONF_CSC_DEF_BOTH || csc == DP_COM_CONF_CSC_DEF_BG)
286
reg |= DP_COM_CONF_CSC_DEF_BG;
287
288
reg &= ~DP_COM_CONF_FG_EN;
289
writel(reg, flow->base + DP_COM_CONF);
290
291
writel(0, flow->base + DP_FG_POS);
292
ipu_srm_dp_update(priv->ipu, sync);
293
294
mutex_unlock(&priv->mutex);
295
}
296
EXPORT_SYMBOL_GPL(ipu_dp_disable_channel);
297
298
void ipu_dp_disable(struct ipu_soc *ipu)
299
{
300
struct ipu_dp_priv *priv = ipu->dp_priv;
301
302
mutex_lock(&priv->mutex);
303
304
priv->use_count--;
305
306
if (!priv->use_count)
307
ipu_module_disable(priv->ipu, IPU_CONF_DP_EN);
308
309
if (priv->use_count < 0)
310
priv->use_count = 0;
311
312
mutex_unlock(&priv->mutex);
313
}
314
EXPORT_SYMBOL_GPL(ipu_dp_disable);
315
316
struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow)
317
{
318
struct ipu_dp_priv *priv = ipu->dp_priv;
319
struct ipu_dp *dp;
320
321
if ((flow >> 1) >= IPUV3_NUM_FLOWS)
322
return ERR_PTR(-EINVAL);
323
324
if (flow & 1)
325
dp = &priv->flow[flow >> 1].foreground;
326
else
327
dp = &priv->flow[flow >> 1].background;
328
329
if (dp->in_use)
330
return ERR_PTR(-EBUSY);
331
332
dp->in_use = true;
333
334
return dp;
335
}
336
EXPORT_SYMBOL_GPL(ipu_dp_get);
337
338
void ipu_dp_put(struct ipu_dp *dp)
339
{
340
dp->in_use = false;
341
}
342
EXPORT_SYMBOL_GPL(ipu_dp_put);
343
344
int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base)
345
{
346
struct ipu_dp_priv *priv;
347
int i;
348
349
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
350
if (!priv)
351
return -ENOMEM;
352
priv->dev = dev;
353
priv->ipu = ipu;
354
355
ipu->dp_priv = priv;
356
357
priv->base = devm_ioremap(dev, base, PAGE_SIZE);
358
if (!priv->base)
359
return -ENOMEM;
360
361
mutex_init(&priv->mutex);
362
363
for (i = 0; i < IPUV3_NUM_FLOWS; i++) {
364
priv->flow[i].background.in_cs = IPUV3_COLORSPACE_UNKNOWN;
365
priv->flow[i].foreground.in_cs = IPUV3_COLORSPACE_UNKNOWN;
366
priv->flow[i].foreground.foreground = true;
367
priv->flow[i].base = priv->base + ipu_dp_flow_base[i];
368
priv->flow[i].priv = priv;
369
}
370
371
return 0;
372
}
373
374
void ipu_dp_exit(struct ipu_soc *ipu)
375
{
376
}
377
378