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_sync_update(priv
->ipu
);
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_sync_update(priv
->ipu
);
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
== flow
->out_cs
)
200 * foreground identical to output, apply color
201 * conversion on background
203 ipu_dp_csc_init(flow
, flow
->background
.in_cs
,
204 flow
->out_cs
, DP_COM_CONF_CSC_DEF_BG
);
206 ipu_dp_csc_init(flow
, flow
->foreground
.in_cs
,
207 flow
->out_cs
, DP_COM_CONF_CSC_DEF_FG
);
210 ipu_srm_dp_sync_update(priv
->ipu
);
212 mutex_unlock(&priv
->mutex
);
216 EXPORT_SYMBOL_GPL(ipu_dp_setup_channel
);
218 int ipu_dp_enable(struct ipu_soc
*ipu
)
220 struct ipu_dp_priv
*priv
= ipu
->dp_priv
;
222 mutex_lock(&priv
->mutex
);
224 if (!priv
->use_count
)
225 ipu_module_enable(priv
->ipu
, IPU_CONF_DP_EN
);
229 mutex_unlock(&priv
->mutex
);
233 EXPORT_SYMBOL_GPL(ipu_dp_enable
);
235 int ipu_dp_enable_channel(struct ipu_dp
*dp
)
237 struct ipu_flow
*flow
= to_flow(dp
);
238 struct ipu_dp_priv
*priv
= flow
->priv
;
244 mutex_lock(&priv
->mutex
);
246 reg
= readl(flow
->base
+ DP_COM_CONF
);
247 reg
|= DP_COM_CONF_FG_EN
;
248 writel(reg
, flow
->base
+ DP_COM_CONF
);
250 ipu_srm_dp_sync_update(priv
->ipu
);
252 mutex_unlock(&priv
->mutex
);
256 EXPORT_SYMBOL_GPL(ipu_dp_enable_channel
);
258 void ipu_dp_disable_channel(struct ipu_dp
*dp
)
260 struct ipu_flow
*flow
= to_flow(dp
);
261 struct ipu_dp_priv
*priv
= flow
->priv
;
267 mutex_lock(&priv
->mutex
);
269 reg
= readl(flow
->base
+ DP_COM_CONF
);
270 csc
= reg
& DP_COM_CONF_CSC_DEF_MASK
;
271 if (csc
== DP_COM_CONF_CSC_DEF_FG
)
272 reg
&= ~DP_COM_CONF_CSC_DEF_MASK
;
274 reg
&= ~DP_COM_CONF_FG_EN
;
275 writel(reg
, flow
->base
+ DP_COM_CONF
);
277 writel(0, flow
->base
+ DP_FG_POS
);
278 ipu_srm_dp_sync_update(priv
->ipu
);
280 if (ipu_idmac_channel_busy(priv
->ipu
, IPUV3_CHANNEL_MEM_BG_SYNC
))
281 ipu_wait_interrupt(priv
->ipu
, IPU_IRQ_DP_SF_END
, 50);
283 mutex_unlock(&priv
->mutex
);
285 EXPORT_SYMBOL_GPL(ipu_dp_disable_channel
);
287 void ipu_dp_disable(struct ipu_soc
*ipu
)
289 struct ipu_dp_priv
*priv
= ipu
->dp_priv
;
291 mutex_lock(&priv
->mutex
);
295 if (!priv
->use_count
)
296 ipu_module_disable(priv
->ipu
, IPU_CONF_DP_EN
);
298 if (priv
->use_count
< 0)
301 mutex_unlock(&priv
->mutex
);
303 EXPORT_SYMBOL_GPL(ipu_dp_disable
);
305 struct ipu_dp
*ipu_dp_get(struct ipu_soc
*ipu
, unsigned int flow
)
307 struct ipu_dp_priv
*priv
= ipu
->dp_priv
;
310 if ((flow
>> 1) >= IPUV3_NUM_FLOWS
)
311 return ERR_PTR(-EINVAL
);
314 dp
= &priv
->flow
[flow
>> 1].foreground
;
316 dp
= &priv
->flow
[flow
>> 1].background
;
319 return ERR_PTR(-EBUSY
);
325 EXPORT_SYMBOL_GPL(ipu_dp_get
);
327 void ipu_dp_put(struct ipu_dp
*dp
)
331 EXPORT_SYMBOL_GPL(ipu_dp_put
);
333 int ipu_dp_init(struct ipu_soc
*ipu
, struct device
*dev
, unsigned long base
)
335 struct ipu_dp_priv
*priv
;
338 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
346 priv
->base
= devm_ioremap(dev
, base
, PAGE_SIZE
);
350 mutex_init(&priv
->mutex
);
352 for (i
= 0; i
< IPUV3_NUM_FLOWS
; i
++) {
353 priv
->flow
[i
].foreground
.foreground
= true;
354 priv
->flow
[i
].base
= priv
->base
+ ipu_dp_flow_base
[i
];
355 priv
->flow
[i
].priv
= priv
;
361 void ipu_dp_exit(struct ipu_soc
*ipu
)