Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[linux-2.6-microblaze.git] / drivers / gpu / ipu-v3 / ipu-dp.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
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 }