2 * Copyright (C) 2016 BayLibre, SAS
3 * Author: Neil Armstrong <narmstrong@baylibre.com>
4 * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
5 * Copyright (C) 2014 Endless Mobile
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include <linux/kernel.h>
22 #include <linux/module.h>
24 #include "meson_drv.h"
25 #include "meson_viu.h"
26 #include "meson_vpp.h"
27 #include "meson_venc.h"
28 #include "meson_canvas.h"
29 #include "meson_registers.h"
32 * DOC: Video Input Unit
34 * VIU Handles the Pixel scanout and the basic Colorspace conversions
35 * We handle the following features :
37 * - OSD1 RGB565/RGB888/xRGB8888 scanout
38 * - RGB conversion to x/cb/cr
39 * - Progressive or Interlace buffer scanout
40 * - OSD1 Commit on Vsync
41 * - HDR OSD matrix for GXL/GXM
45 * - BGR888/xBGR8888/BGRx8888/BGRx8888 modes
46 * - YUV4:2:2 Y0CbY1Cr scanout
47 * - Conversion to YUV 4:4:4 from 4:2:2 input
48 * - Colorkey Alpha matching
49 * - Big endian scanout
50 * - X/Y reverse scanout
51 * - Global alpha setup
52 * - OSD2 support, would need interlace switching on vsync
53 * - OSD1 full scaling to support TV overscan
58 enum viu_matrix_sel_e
{
59 VIU_MATRIX_OSD_EOTF
= 0,
68 #define COEFF_NORM(a) ((int)((((a) * 2048.0) + 1) / 2))
69 #define MATRIX_5X3_COEF_SIZE 24
71 #define EOTF_COEFF_NORM(a) ((int)((((a) * 4096.0) + 1) / 2))
72 #define EOTF_COEFF_SIZE 10
73 #define EOTF_COEFF_RIGHTSHIFT 1
75 static int RGB709_to_YUV709l_coeff
[MATRIX_5X3_COEF_SIZE
] = {
76 0, 0, 0, /* pre offset */
77 COEFF_NORM(0.181873), COEFF_NORM(0.611831), COEFF_NORM(0.061765),
78 COEFF_NORM(-0.100251), COEFF_NORM(-0.337249), COEFF_NORM(0.437500),
79 COEFF_NORM(0.437500), COEFF_NORM(-0.397384), COEFF_NORM(-0.040116),
80 0, 0, 0, /* 10'/11'/12' */
81 0, 0, 0, /* 20'/21'/22' */
82 64, 512, 512, /* offset */
83 0, 0, 0 /* mode, right_shift, clip_en */
86 /* eotf matrix: bypass */
87 static int eotf_bypass_coeff
[EOTF_COEFF_SIZE
] = {
88 EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0),
89 EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0),
90 EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0),
91 EOTF_COEFF_RIGHTSHIFT
/* right shift */
94 void meson_viu_set_osd_matrix(struct meson_drm
*priv
,
95 enum viu_matrix_sel_e m_select
,
98 if (m_select
== VIU_MATRIX_OSD
) {
99 /* osd matrix, VIU_MATRIX_0 */
100 writel(((m
[0] & 0xfff) << 16) | (m
[1] & 0xfff),
101 priv
->io_base
+ _REG(VIU_OSD1_MATRIX_PRE_OFFSET0_1
));
103 priv
->io_base
+ _REG(VIU_OSD1_MATRIX_PRE_OFFSET2
));
104 writel(((m
[3] & 0x1fff) << 16) | (m
[4] & 0x1fff),
105 priv
->io_base
+ _REG(VIU_OSD1_MATRIX_COEF00_01
));
106 writel(((m
[5] & 0x1fff) << 16) | (m
[6] & 0x1fff),
107 priv
->io_base
+ _REG(VIU_OSD1_MATRIX_COEF02_10
));
108 writel(((m
[7] & 0x1fff) << 16) | (m
[8] & 0x1fff),
109 priv
->io_base
+ _REG(VIU_OSD1_MATRIX_COEF11_12
));
110 writel(((m
[9] & 0x1fff) << 16) | (m
[10] & 0x1fff),
111 priv
->io_base
+ _REG(VIU_OSD1_MATRIX_COEF20_21
));
114 writel(((m
[11] & 0x1fff) << 16) | (m
[12] & 0x1fff),
116 _REG(VIU_OSD1_MATRIX_COEF22_30
));
117 writel(((m
[13] & 0x1fff) << 16) | (m
[14] & 0x1fff),
119 _REG(VIU_OSD1_MATRIX_COEF31_32
));
120 writel(((m
[15] & 0x1fff) << 16) | (m
[16] & 0x1fff),
122 _REG(VIU_OSD1_MATRIX_COEF40_41
));
123 writel(m
[17] & 0x1fff, priv
->io_base
+
124 _REG(VIU_OSD1_MATRIX_COLMOD_COEF42
));
126 writel((m
[11] & 0x1fff) << 16, priv
->io_base
+
127 _REG(VIU_OSD1_MATRIX_COEF22_30
));
129 writel(((m
[18] & 0xfff) << 16) | (m
[19] & 0xfff),
130 priv
->io_base
+ _REG(VIU_OSD1_MATRIX_OFFSET0_1
));
131 writel(m
[20] & 0xfff,
132 priv
->io_base
+ _REG(VIU_OSD1_MATRIX_OFFSET2
));
134 writel_bits_relaxed(3 << 30, m
[21] << 30,
135 priv
->io_base
+ _REG(VIU_OSD1_MATRIX_COLMOD_COEF42
));
136 writel_bits_relaxed(7 << 16, m
[22] << 16,
137 priv
->io_base
+ _REG(VIU_OSD1_MATRIX_COLMOD_COEF42
));
139 /* 23 reserved for clipping control */
140 writel_bits_relaxed(BIT(0), csc_on
? BIT(0) : 0,
141 priv
->io_base
+ _REG(VIU_OSD1_MATRIX_CTRL
));
142 writel_bits_relaxed(BIT(1), 0,
143 priv
->io_base
+ _REG(VIU_OSD1_MATRIX_CTRL
));
144 } else if (m_select
== VIU_MATRIX_OSD_EOTF
) {
147 /* osd eotf matrix, VIU_MATRIX_OSD_EOTF */
148 for (i
= 0; i
< 5; i
++)
149 writel(((m
[i
* 2] & 0x1fff) << 16) |
150 (m
[i
* 2 + 1] & 0x1fff), priv
->io_base
+
151 _REG(VIU_OSD1_EOTF_CTL
+ i
+ 1));
153 writel_bits_relaxed(BIT(30), csc_on
? BIT(30) : 0,
154 priv
->io_base
+ _REG(VIU_OSD1_EOTF_CTL
));
155 writel_bits_relaxed(BIT(31), csc_on
? BIT(31) : 0,
156 priv
->io_base
+ _REG(VIU_OSD1_EOTF_CTL
));
160 #define OSD_EOTF_LUT_SIZE 33
161 #define OSD_OETF_LUT_SIZE 41
163 void meson_viu_set_osd_lut(struct meson_drm
*priv
, enum viu_lut_sel_e lut_sel
,
164 unsigned int *r_map
, unsigned int *g_map
,
168 unsigned int addr_port
;
169 unsigned int data_port
;
170 unsigned int ctrl_port
;
173 if (lut_sel
== VIU_LUT_OSD_EOTF
) {
174 addr_port
= VIU_OSD1_EOTF_LUT_ADDR_PORT
;
175 data_port
= VIU_OSD1_EOTF_LUT_DATA_PORT
;
176 ctrl_port
= VIU_OSD1_EOTF_CTL
;
177 } else if (lut_sel
== VIU_LUT_OSD_OETF
) {
178 addr_port
= VIU_OSD1_OETF_LUT_ADDR_PORT
;
179 data_port
= VIU_OSD1_OETF_LUT_DATA_PORT
;
180 ctrl_port
= VIU_OSD1_OETF_CTL
;
184 if (lut_sel
== VIU_LUT_OSD_OETF
) {
185 writel(0, priv
->io_base
+ _REG(addr_port
));
187 for (i
= 0; i
< (OSD_OETF_LUT_SIZE
/ 2); i
++)
188 writel(r_map
[i
* 2] | (r_map
[i
* 2 + 1] << 16),
189 priv
->io_base
+ _REG(data_port
));
191 writel(r_map
[OSD_OETF_LUT_SIZE
- 1] | (g_map
[0] << 16),
192 priv
->io_base
+ _REG(data_port
));
194 for (i
= 0; i
< (OSD_OETF_LUT_SIZE
/ 2); i
++)
195 writel(g_map
[i
* 2 + 1] | (g_map
[i
* 2 + 2] << 16),
196 priv
->io_base
+ _REG(data_port
));
198 for (i
= 0; i
< (OSD_OETF_LUT_SIZE
/ 2); i
++)
199 writel(b_map
[i
* 2] | (b_map
[i
* 2 + 1] << 16),
200 priv
->io_base
+ _REG(data_port
));
202 writel(b_map
[OSD_OETF_LUT_SIZE
- 1],
203 priv
->io_base
+ _REG(data_port
));
206 writel_bits_relaxed(0x7 << 29, 7 << 29,
207 priv
->io_base
+ _REG(ctrl_port
));
209 writel_bits_relaxed(0x7 << 29, 0,
210 priv
->io_base
+ _REG(ctrl_port
));
211 } else if (lut_sel
== VIU_LUT_OSD_EOTF
) {
212 writel(0, priv
->io_base
+ _REG(addr_port
));
214 for (i
= 0; i
< (OSD_EOTF_LUT_SIZE
/ 2); i
++)
215 writel(r_map
[i
* 2] | (r_map
[i
* 2 + 1] << 16),
216 priv
->io_base
+ _REG(data_port
));
218 writel(r_map
[OSD_EOTF_LUT_SIZE
- 1] | (g_map
[0] << 16),
219 priv
->io_base
+ _REG(data_port
));
221 for (i
= 0; i
< (OSD_EOTF_LUT_SIZE
/ 2); i
++)
222 writel(g_map
[i
* 2 + 1] | (g_map
[i
* 2 + 2] << 16),
223 priv
->io_base
+ _REG(data_port
));
225 for (i
= 0; i
< (OSD_EOTF_LUT_SIZE
/ 2); i
++)
226 writel(b_map
[i
* 2] | (b_map
[i
* 2 + 1] << 16),
227 priv
->io_base
+ _REG(data_port
));
229 writel(b_map
[OSD_EOTF_LUT_SIZE
- 1],
230 priv
->io_base
+ _REG(data_port
));
233 writel_bits_relaxed(7 << 27, 7 << 27,
234 priv
->io_base
+ _REG(ctrl_port
));
236 writel_bits_relaxed(7 << 27, 0,
237 priv
->io_base
+ _REG(ctrl_port
));
239 writel_bits_relaxed(BIT(31), BIT(31),
240 priv
->io_base
+ _REG(ctrl_port
));
244 /* eotf lut: linear */
245 static unsigned int eotf_33_linear_mapping
[OSD_EOTF_LUT_SIZE
] = {
246 0x0000, 0x0200, 0x0400, 0x0600,
247 0x0800, 0x0a00, 0x0c00, 0x0e00,
248 0x1000, 0x1200, 0x1400, 0x1600,
249 0x1800, 0x1a00, 0x1c00, 0x1e00,
250 0x2000, 0x2200, 0x2400, 0x2600,
251 0x2800, 0x2a00, 0x2c00, 0x2e00,
252 0x3000, 0x3200, 0x3400, 0x3600,
253 0x3800, 0x3a00, 0x3c00, 0x3e00,
257 /* osd oetf lut: linear */
258 static unsigned int oetf_41_linear_mapping
[OSD_OETF_LUT_SIZE
] = {
268 1023, 1023, 1023, 1023,
272 static void meson_viu_load_matrix(struct meson_drm
*priv
)
274 /* eotf lut bypass */
275 meson_viu_set_osd_lut(priv
, VIU_LUT_OSD_EOTF
,
276 eotf_33_linear_mapping
, /* R */
277 eotf_33_linear_mapping
, /* G */
278 eotf_33_linear_mapping
, /* B */
281 /* eotf matrix bypass */
282 meson_viu_set_osd_matrix(priv
, VIU_MATRIX_OSD_EOTF
,
286 /* oetf lut bypass */
287 meson_viu_set_osd_lut(priv
, VIU_LUT_OSD_OETF
,
288 oetf_41_linear_mapping
, /* R */
289 oetf_41_linear_mapping
, /* G */
290 oetf_41_linear_mapping
, /* B */
293 /* osd matrix RGB709 to YUV709 limit */
294 meson_viu_set_osd_matrix(priv
, VIU_MATRIX_OSD
,
295 RGB709_to_YUV709l_coeff
,
299 void meson_viu_init(struct meson_drm
*priv
)
304 writel_bits_relaxed(BIT(0) | BIT(21), 0,
305 priv
->io_base
+ _REG(VIU_OSD1_CTRL_STAT
));
306 writel_bits_relaxed(BIT(0) | BIT(21), 0,
307 priv
->io_base
+ _REG(VIU_OSD2_CTRL_STAT
));
309 /* On GXL/GXM, Use the 10bit HDR conversion matrix */
310 if (meson_vpu_is_compatible(priv
, "amlogic,meson-gxm-vpu") ||
311 meson_vpu_is_compatible(priv
, "amlogic,meson-gxl-vpu"))
312 meson_viu_load_matrix(priv
);
314 /* Initialize OSD1 fifo control register */
315 reg
= BIT(0) | /* Urgent DDR request priority */
316 (4 << 5) | /* hold_fifo_lines */
317 (3 << 10) | /* burst length 64 */
318 (32 << 12) | /* fifo_depth_val: 32*8=256 */
319 (2 << 22) | /* 4 words in 1 burst */
321 writel_relaxed(reg
, priv
->io_base
+ _REG(VIU_OSD1_FIFO_CTRL_STAT
));
322 writel_relaxed(reg
, priv
->io_base
+ _REG(VIU_OSD2_FIFO_CTRL_STAT
));
324 /* Set OSD alpha replace value */
325 writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT
,
326 0xff << OSD_REPLACE_SHIFT
,
327 priv
->io_base
+ _REG(VIU_OSD1_CTRL_STAT2
));
328 writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT
,
329 0xff << OSD_REPLACE_SHIFT
,
330 priv
->io_base
+ _REG(VIU_OSD2_CTRL_STAT2
));
332 priv
->viu
.osd1_enabled
= false;
333 priv
->viu
.osd1_commit
= false;
334 priv
->viu
.osd1_interlace
= false;