2 * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
3 * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
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.
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
15 #include <linux/export.h>
16 #include <linux/kernel.h>
17 #include <linux/types.h>
18 #include <linux/errno.h>
20 #include <linux/err.h>
22 #include <video/imx-ipu-v3.h>
26 #define DP_ASYNC0 0x60
27 #define DP_ASYNC1 0xBC
29 #define DP_COM_CONF 0x0
30 #define DP_GRAPH_WIND_CTRL 0x0004
31 #define DP_FG_POS 0x0008
32 #define DP_CSC_A_0 0x0044
33 #define DP_CSC_A_1 0x0048
34 #define DP_CSC_A_2 0x004C
35 #define DP_CSC_A_3 0x0050
36 #define DP_CSC_0 0x0054
37 #define DP_CSC_1 0x0058
39 #define DP_COM_CONF_FG_EN (1 << 0)
40 #define DP_COM_CONF_GWSEL (1 << 1)
41 #define DP_COM_CONF_GWAM (1 << 2)
42 #define DP_COM_CONF_GWCKE (1 << 3)
43 #define DP_COM_CONF_CSC_DEF_MASK (3 << 8)
44 #define DP_COM_CONF_CSC_DEF_OFFSET 8
45 #define DP_COM_CONF_CSC_DEF_FG (3 << 8)
46 #define DP_COM_CONF_CSC_DEF_BG (2 << 8)
47 #define DP_COM_CONF_CSC_DEF_BOTH (1 << 8)
49 #define IPUV3_NUM_FLOWS 3
57 enum ipu_color_space in_cs
;
61 struct ipu_dp foreground
;
62 struct ipu_dp background
;
63 enum ipu_color_space out_cs
;
65 struct ipu_dp_priv
*priv
;
72 struct ipu_flow flow
[IPUV3_NUM_FLOWS
];
77 static u32 ipu_dp_flow_base
[] = {DP_SYNC
, DP_ASYNC0
, DP_ASYNC1
};
79 static inline struct ipu_flow
*to_flow(struct ipu_dp
*dp
)
82 return container_of(dp
, struct ipu_flow
, foreground
);
84 return container_of(dp
, struct ipu_flow
, background
);
87 int ipu_dp_set_global_alpha(struct ipu_dp
*dp
, bool enable
,
88 u8 alpha
, bool bg_chan
)
90 struct ipu_flow
*flow
= to_flow(dp
);
91 struct ipu_dp_priv
*priv
= flow
->priv
;
94 mutex_lock(&priv
->mutex
);
96 reg
= readl(flow
->base
+ DP_COM_CONF
);
98 reg
&= ~DP_COM_CONF_GWSEL
;
100 reg
|= DP_COM_CONF_GWSEL
;
101 writel(reg
, flow
->base
+ DP_COM_CONF
);
104 reg
= readl(flow
->base
+ DP_GRAPH_WIND_CTRL
) & 0x00FFFFFFL
;
105 writel(reg
| ((u32
) alpha
<< 24),
106 flow
->base
+ DP_GRAPH_WIND_CTRL
);
108 reg
= readl(flow
->base
+ DP_COM_CONF
);
109 writel(reg
| DP_COM_CONF_GWAM
, flow
->base
+ DP_COM_CONF
);
111 reg
= readl(flow
->base
+ DP_COM_CONF
);
112 writel(reg
& ~DP_COM_CONF_GWAM
, flow
->base
+ DP_COM_CONF
);
115 ipu_srm_dp_update(priv
->ipu
, true);
117 mutex_unlock(&priv
->mutex
);
121 EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha
);
123 int ipu_dp_set_window_pos(struct ipu_dp
*dp
, u16 x_pos
, u16 y_pos
)
125 struct ipu_flow
*flow
= to_flow(dp
);
126 struct ipu_dp_priv
*priv
= flow
->priv
;
128 writel((x_pos
<< 16) | y_pos
, flow
->base
+ DP_FG_POS
);
130 ipu_srm_dp_update(priv
->ipu
, true);
134 EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos
);
136 static void ipu_dp_csc_init(struct ipu_flow
*flow
,
137 enum ipu_color_space in
,
138 enum ipu_color_space out
,
143 reg
= readl(flow
->base
+ DP_COM_CONF
);
144 reg
&= ~DP_COM_CONF_CSC_DEF_MASK
;
147 writel(reg
, flow
->base
+ DP_COM_CONF
);
151 if (in
== IPUV3_COLORSPACE_RGB
&& out
== IPUV3_COLORSPACE_YUV
) {
152 writel(0x099 | (0x12d << 16), flow
->base
+ DP_CSC_A_0
);
153 writel(0x03a | (0x3a9 << 16), flow
->base
+ DP_CSC_A_1
);
154 writel(0x356 | (0x100 << 16), flow
->base
+ DP_CSC_A_2
);
155 writel(0x100 | (0x329 << 16), flow
->base
+ DP_CSC_A_3
);
156 writel(0x3d6 | (0x0000 << 16) | (2 << 30),
157 flow
->base
+ DP_CSC_0
);
158 writel(0x200 | (2 << 14) | (0x200 << 16) | (2 << 30),
159 flow
->base
+ DP_CSC_1
);
161 writel(0x095 | (0x000 << 16), flow
->base
+ DP_CSC_A_0
);
162 writel(0x0cc | (0x095 << 16), flow
->base
+ DP_CSC_A_1
);
163 writel(0x3ce | (0x398 << 16), flow
->base
+ DP_CSC_A_2
);
164 writel(0x095 | (0x0ff << 16), flow
->base
+ DP_CSC_A_3
);
165 writel(0x000 | (0x3e42 << 16) | (1 << 30),
166 flow
->base
+ DP_CSC_0
);
167 writel(0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30),
168 flow
->base
+ DP_CSC_1
);
173 writel(reg
, flow
->base
+ DP_COM_CONF
);
176 int ipu_dp_setup_channel(struct ipu_dp
*dp
,
177 enum ipu_color_space in
,
178 enum ipu_color_space out
)
180 struct ipu_flow
*flow
= to_flow(dp
);
181 struct ipu_dp_priv
*priv
= flow
->priv
;
183 mutex_lock(&priv
->mutex
);
190 if (flow
->foreground
.in_cs
== flow
->background
.in_cs
) {
192 * foreground and background are of same colorspace, put
193 * colorspace converter after combining unit.
195 ipu_dp_csc_init(flow
, flow
->foreground
.in_cs
, flow
->out_cs
,
196 DP_COM_CONF_CSC_DEF_BOTH
);
198 if (flow
->foreground
.in_cs
== IPUV3_COLORSPACE_UNKNOWN
||
199 flow
->foreground
.in_cs
== flow
->out_cs
)
201 * foreground identical to output, apply color
202 * conversion on background
204 ipu_dp_csc_init(flow
, flow
->background
.in_cs
,
205 flow
->out_cs
, DP_COM_CONF_CSC_DEF_BG
);
207 ipu_dp_csc_init(flow
, flow
->foreground
.in_cs
,
208 flow
->out_cs
, DP_COM_CONF_CSC_DEF_FG
);
211 ipu_srm_dp_update(priv
->ipu
, true);
213 mutex_unlock(&priv
->mutex
);
217 EXPORT_SYMBOL_GPL(ipu_dp_setup_channel
);
219 int ipu_dp_enable(struct ipu_soc
*ipu
)
221 struct ipu_dp_priv
*priv
= ipu
->dp_priv
;
223 mutex_lock(&priv
->mutex
);
225 if (!priv
->use_count
)
226 ipu_module_enable(priv
->ipu
, IPU_CONF_DP_EN
);
230 mutex_unlock(&priv
->mutex
);
234 EXPORT_SYMBOL_GPL(ipu_dp_enable
);
236 int ipu_dp_enable_channel(struct ipu_dp
*dp
)
238 struct ipu_flow
*flow
= to_flow(dp
);
239 struct ipu_dp_priv
*priv
= flow
->priv
;
245 mutex_lock(&priv
->mutex
);
247 reg
= readl(flow
->base
+ DP_COM_CONF
);
248 reg
|= DP_COM_CONF_FG_EN
;
249 writel(reg
, flow
->base
+ DP_COM_CONF
);
251 ipu_srm_dp_update(priv
->ipu
, true);
253 mutex_unlock(&priv
->mutex
);
257 EXPORT_SYMBOL_GPL(ipu_dp_enable_channel
);
259 void ipu_dp_disable_channel(struct ipu_dp
*dp
, bool sync
)
261 struct ipu_flow
*flow
= to_flow(dp
);
262 struct ipu_dp_priv
*priv
= flow
->priv
;
265 dp
->in_cs
= IPUV3_COLORSPACE_UNKNOWN
;
270 mutex_lock(&priv
->mutex
);
272 reg
= readl(flow
->base
+ DP_COM_CONF
);
273 csc
= reg
& DP_COM_CONF_CSC_DEF_MASK
;
274 reg
&= ~DP_COM_CONF_CSC_DEF_MASK
;
275 if (csc
== DP_COM_CONF_CSC_DEF_BOTH
|| csc
== DP_COM_CONF_CSC_DEF_BG
)
276 reg
|= DP_COM_CONF_CSC_DEF_BG
;
278 reg
&= ~DP_COM_CONF_FG_EN
;
279 writel(reg
, flow
->base
+ DP_COM_CONF
);
281 writel(0, flow
->base
+ DP_FG_POS
);
282 ipu_srm_dp_update(priv
->ipu
, sync
);
284 mutex_unlock(&priv
->mutex
);
286 EXPORT_SYMBOL_GPL(ipu_dp_disable_channel
);
288 void ipu_dp_disable(struct ipu_soc
*ipu
)
290 struct ipu_dp_priv
*priv
= ipu
->dp_priv
;
292 mutex_lock(&priv
->mutex
);
296 if (!priv
->use_count
)
297 ipu_module_disable(priv
->ipu
, IPU_CONF_DP_EN
);
299 if (priv
->use_count
< 0)
302 mutex_unlock(&priv
->mutex
);
304 EXPORT_SYMBOL_GPL(ipu_dp_disable
);
306 struct ipu_dp
*ipu_dp_get(struct ipu_soc
*ipu
, unsigned int flow
)
308 struct ipu_dp_priv
*priv
= ipu
->dp_priv
;
311 if ((flow
>> 1) >= IPUV3_NUM_FLOWS
)
312 return ERR_PTR(-EINVAL
);
315 dp
= &priv
->flow
[flow
>> 1].foreground
;
317 dp
= &priv
->flow
[flow
>> 1].background
;
320 return ERR_PTR(-EBUSY
);
326 EXPORT_SYMBOL_GPL(ipu_dp_get
);
328 void ipu_dp_put(struct ipu_dp
*dp
)
332 EXPORT_SYMBOL_GPL(ipu_dp_put
);
334 int ipu_dp_init(struct ipu_soc
*ipu
, struct device
*dev
, unsigned long base
)
336 struct ipu_dp_priv
*priv
;
339 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
347 priv
->base
= devm_ioremap(dev
, base
, PAGE_SIZE
);
351 mutex_init(&priv
->mutex
);
353 for (i
= 0; i
< IPUV3_NUM_FLOWS
; i
++) {
354 priv
->flow
[i
].background
.in_cs
= IPUV3_COLORSPACE_UNKNOWN
;
355 priv
->flow
[i
].foreground
.in_cs
= IPUV3_COLORSPACE_UNKNOWN
;
356 priv
->flow
[i
].foreground
.foreground
= true;
357 priv
->flow
[i
].base
= priv
->base
+ ipu_dp_flow_base
[i
];
358 priv
->flow
[i
].priv
= priv
;
364 void ipu_dp_exit(struct ipu_soc
*ipu
)