1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
4 * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
6 #include <linux/export.h>
7 #include <linux/kernel.h>
8 #include <linux/types.h>
9 #include <linux/errno.h>
11 #include <linux/err.h>
13 #include <video/imx-ipu-v3.h>
17 #define DP_ASYNC0 0x60
18 #define DP_ASYNC1 0xBC
20 #define DP_COM_CONF 0x0
21 #define DP_GRAPH_WIND_CTRL 0x0004
22 #define DP_FG_POS 0x0008
23 #define DP_CSC_A_0 0x0044
24 #define DP_CSC_A_1 0x0048
25 #define DP_CSC_A_2 0x004C
26 #define DP_CSC_A_3 0x0050
27 #define DP_CSC_0 0x0054
28 #define DP_CSC_1 0x0058
30 #define DP_COM_CONF_FG_EN (1 << 0)
31 #define DP_COM_CONF_GWSEL (1 << 1)
32 #define DP_COM_CONF_GWAM (1 << 2)
33 #define DP_COM_CONF_GWCKE (1 << 3)
34 #define DP_COM_CONF_CSC_DEF_MASK (3 << 8)
35 #define DP_COM_CONF_CSC_DEF_OFFSET 8
36 #define DP_COM_CONF_CSC_DEF_FG (3 << 8)
37 #define DP_COM_CONF_CSC_DEF_BG (2 << 8)
38 #define DP_COM_CONF_CSC_DEF_BOTH (1 << 8)
40 #define IPUV3_NUM_FLOWS 3
48 enum ipu_color_space in_cs
;
52 struct ipu_dp foreground
;
53 struct ipu_dp background
;
54 enum ipu_color_space out_cs
;
56 struct ipu_dp_priv
*priv
;
63 struct ipu_flow flow
[IPUV3_NUM_FLOWS
];
68 static u32 ipu_dp_flow_base
[] = {DP_SYNC
, DP_ASYNC0
, DP_ASYNC1
};
70 static inline struct ipu_flow
*to_flow(struct ipu_dp
*dp
)
73 return container_of(dp
, struct ipu_flow
, foreground
);
75 return container_of(dp
, struct ipu_flow
, background
);
78 int ipu_dp_set_global_alpha(struct ipu_dp
*dp
, bool enable
,
79 u8 alpha
, bool bg_chan
)
81 struct ipu_flow
*flow
= to_flow(dp
);
82 struct ipu_dp_priv
*priv
= flow
->priv
;
85 mutex_lock(&priv
->mutex
);
87 reg
= readl(flow
->base
+ DP_COM_CONF
);
89 reg
&= ~DP_COM_CONF_GWSEL
;
91 reg
|= DP_COM_CONF_GWSEL
;
92 writel(reg
, flow
->base
+ DP_COM_CONF
);
95 reg
= readl(flow
->base
+ DP_GRAPH_WIND_CTRL
) & 0x00FFFFFFL
;
96 writel(reg
| ((u32
) alpha
<< 24),
97 flow
->base
+ DP_GRAPH_WIND_CTRL
);
99 reg
= readl(flow
->base
+ DP_COM_CONF
);
100 writel(reg
| DP_COM_CONF_GWAM
, flow
->base
+ DP_COM_CONF
);
102 reg
= readl(flow
->base
+ DP_COM_CONF
);
103 writel(reg
& ~DP_COM_CONF_GWAM
, flow
->base
+ DP_COM_CONF
);
106 ipu_srm_dp_update(priv
->ipu
, true);
108 mutex_unlock(&priv
->mutex
);
112 EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha
);
114 int ipu_dp_set_window_pos(struct ipu_dp
*dp
, u16 x_pos
, u16 y_pos
)
116 struct ipu_flow
*flow
= to_flow(dp
);
117 struct ipu_dp_priv
*priv
= flow
->priv
;
119 writel((x_pos
<< 16) | y_pos
, flow
->base
+ DP_FG_POS
);
121 ipu_srm_dp_update(priv
->ipu
, true);
125 EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos
);
127 static void ipu_dp_csc_init(struct ipu_flow
*flow
,
128 enum ipu_color_space in
,
129 enum ipu_color_space out
,
134 reg
= readl(flow
->base
+ DP_COM_CONF
);
135 reg
&= ~DP_COM_CONF_CSC_DEF_MASK
;
138 writel(reg
, flow
->base
+ DP_COM_CONF
);
142 if (in
== IPUV3_COLORSPACE_RGB
&& out
== IPUV3_COLORSPACE_YUV
) {
143 writel(0x099 | (0x12d << 16), flow
->base
+ DP_CSC_A_0
);
144 writel(0x03a | (0x3a9 << 16), flow
->base
+ DP_CSC_A_1
);
145 writel(0x356 | (0x100 << 16), flow
->base
+ DP_CSC_A_2
);
146 writel(0x100 | (0x329 << 16), flow
->base
+ DP_CSC_A_3
);
147 writel(0x3d6 | (0x0000 << 16) | (2 << 30),
148 flow
->base
+ DP_CSC_0
);
149 writel(0x200 | (2 << 14) | (0x200 << 16) | (2 << 30),
150 flow
->base
+ DP_CSC_1
);
152 writel(0x095 | (0x000 << 16), flow
->base
+ DP_CSC_A_0
);
153 writel(0x0cc | (0x095 << 16), flow
->base
+ DP_CSC_A_1
);
154 writel(0x3ce | (0x398 << 16), flow
->base
+ DP_CSC_A_2
);
155 writel(0x095 | (0x0ff << 16), flow
->base
+ DP_CSC_A_3
);
156 writel(0x000 | (0x3e42 << 16) | (1 << 30),
157 flow
->base
+ DP_CSC_0
);
158 writel(0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30),
159 flow
->base
+ DP_CSC_1
);
164 writel(reg
, flow
->base
+ DP_COM_CONF
);
167 int ipu_dp_setup_channel(struct ipu_dp
*dp
,
168 enum ipu_color_space in
,
169 enum ipu_color_space out
)
171 struct ipu_flow
*flow
= to_flow(dp
);
172 struct ipu_dp_priv
*priv
= flow
->priv
;
174 mutex_lock(&priv
->mutex
);
181 if (flow
->foreground
.in_cs
== flow
->background
.in_cs
) {
183 * foreground and background are of same colorspace, put
184 * colorspace converter after combining unit.
186 ipu_dp_csc_init(flow
, flow
->foreground
.in_cs
, flow
->out_cs
,
187 DP_COM_CONF_CSC_DEF_BOTH
);
189 if (flow
->foreground
.in_cs
== IPUV3_COLORSPACE_UNKNOWN
||
190 flow
->foreground
.in_cs
== flow
->out_cs
)
192 * foreground identical to output, apply color
193 * conversion on background
195 ipu_dp_csc_init(flow
, flow
->background
.in_cs
,
196 flow
->out_cs
, DP_COM_CONF_CSC_DEF_BG
);
198 ipu_dp_csc_init(flow
, flow
->foreground
.in_cs
,
199 flow
->out_cs
, DP_COM_CONF_CSC_DEF_FG
);
202 ipu_srm_dp_update(priv
->ipu
, true);
204 mutex_unlock(&priv
->mutex
);
208 EXPORT_SYMBOL_GPL(ipu_dp_setup_channel
);
210 int ipu_dp_enable(struct ipu_soc
*ipu
)
212 struct ipu_dp_priv
*priv
= ipu
->dp_priv
;
214 mutex_lock(&priv
->mutex
);
216 if (!priv
->use_count
)
217 ipu_module_enable(priv
->ipu
, IPU_CONF_DP_EN
);
221 mutex_unlock(&priv
->mutex
);
225 EXPORT_SYMBOL_GPL(ipu_dp_enable
);
227 int ipu_dp_enable_channel(struct ipu_dp
*dp
)
229 struct ipu_flow
*flow
= to_flow(dp
);
230 struct ipu_dp_priv
*priv
= flow
->priv
;
236 mutex_lock(&priv
->mutex
);
238 reg
= readl(flow
->base
+ DP_COM_CONF
);
239 reg
|= DP_COM_CONF_FG_EN
;
240 writel(reg
, flow
->base
+ DP_COM_CONF
);
242 ipu_srm_dp_update(priv
->ipu
, true);
244 mutex_unlock(&priv
->mutex
);
248 EXPORT_SYMBOL_GPL(ipu_dp_enable_channel
);
250 void ipu_dp_disable_channel(struct ipu_dp
*dp
, bool sync
)
252 struct ipu_flow
*flow
= to_flow(dp
);
253 struct ipu_dp_priv
*priv
= flow
->priv
;
256 dp
->in_cs
= IPUV3_COLORSPACE_UNKNOWN
;
261 mutex_lock(&priv
->mutex
);
263 reg
= readl(flow
->base
+ DP_COM_CONF
);
264 csc
= reg
& DP_COM_CONF_CSC_DEF_MASK
;
265 reg
&= ~DP_COM_CONF_CSC_DEF_MASK
;
266 if (csc
== DP_COM_CONF_CSC_DEF_BOTH
|| csc
== DP_COM_CONF_CSC_DEF_BG
)
267 reg
|= DP_COM_CONF_CSC_DEF_BG
;
269 reg
&= ~DP_COM_CONF_FG_EN
;
270 writel(reg
, flow
->base
+ DP_COM_CONF
);
272 writel(0, flow
->base
+ DP_FG_POS
);
273 ipu_srm_dp_update(priv
->ipu
, sync
);
275 mutex_unlock(&priv
->mutex
);
277 EXPORT_SYMBOL_GPL(ipu_dp_disable_channel
);
279 void ipu_dp_disable(struct ipu_soc
*ipu
)
281 struct ipu_dp_priv
*priv
= ipu
->dp_priv
;
283 mutex_lock(&priv
->mutex
);
287 if (!priv
->use_count
)
288 ipu_module_disable(priv
->ipu
, IPU_CONF_DP_EN
);
290 if (priv
->use_count
< 0)
293 mutex_unlock(&priv
->mutex
);
295 EXPORT_SYMBOL_GPL(ipu_dp_disable
);
297 struct ipu_dp
*ipu_dp_get(struct ipu_soc
*ipu
, unsigned int flow
)
299 struct ipu_dp_priv
*priv
= ipu
->dp_priv
;
302 if ((flow
>> 1) >= IPUV3_NUM_FLOWS
)
303 return ERR_PTR(-EINVAL
);
306 dp
= &priv
->flow
[flow
>> 1].foreground
;
308 dp
= &priv
->flow
[flow
>> 1].background
;
311 return ERR_PTR(-EBUSY
);
317 EXPORT_SYMBOL_GPL(ipu_dp_get
);
319 void ipu_dp_put(struct ipu_dp
*dp
)
323 EXPORT_SYMBOL_GPL(ipu_dp_put
);
325 int ipu_dp_init(struct ipu_soc
*ipu
, struct device
*dev
, unsigned long base
)
327 struct ipu_dp_priv
*priv
;
330 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
338 priv
->base
= devm_ioremap(dev
, base
, PAGE_SIZE
);
342 mutex_init(&priv
->mutex
);
344 for (i
= 0; i
< IPUV3_NUM_FLOWS
; i
++) {
345 priv
->flow
[i
].background
.in_cs
= IPUV3_COLORSPACE_UNKNOWN
;
346 priv
->flow
[i
].foreground
.in_cs
= IPUV3_COLORSPACE_UNKNOWN
;
347 priv
->flow
[i
].foreground
.foreground
= true;
348 priv
->flow
[i
].base
= priv
->base
+ ipu_dp_flow_base
[i
];
349 priv
->flow
[i
].priv
= priv
;
355 void ipu_dp_exit(struct ipu_soc
*ipu
)