1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2016 BayLibre, SAS
4 * Author: Neil Armstrong <narmstrong@baylibre.com>
5 * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
6 * Copyright (C) 2014 Endless Mobile
9 #include <linux/export.h>
11 #include "meson_drv.h"
12 #include "meson_viu.h"
13 #include "meson_registers.h"
16 * DOC: Video Input Unit
18 * VIU Handles the Pixel scanout and the basic Colorspace conversions
19 * We handle the following features :
21 * - OSD1 RGB565/RGB888/xRGB8888 scanout
22 * - RGB conversion to x/cb/cr
23 * - Progressive or Interlace buffer scanout
24 * - OSD1 Commit on Vsync
25 * - HDR OSD matrix for GXL/GXM
29 * - BGR888/xBGR8888/BGRx8888/BGRx8888 modes
30 * - YUV4:2:2 Y0CbY1Cr scanout
31 * - Conversion to YUV 4:4:4 from 4:2:2 input
32 * - Colorkey Alpha matching
33 * - Big endian scanout
34 * - X/Y reverse scanout
35 * - Global alpha setup
36 * - OSD2 support, would need interlace switching on vsync
37 * - OSD1 full scaling to support TV overscan
42 enum viu_matrix_sel_e {
43 VIU_MATRIX_OSD_EOTF = 0,
52 #define COEFF_NORM(a) ((int)((((a) * 2048.0) + 1) / 2))
53 #define MATRIX_5X3_COEF_SIZE 24
55 #define EOTF_COEFF_NORM(a) ((int)((((a) * 4096.0) + 1) / 2))
56 #define EOTF_COEFF_SIZE 10
57 #define EOTF_COEFF_RIGHTSHIFT 1
59 static int RGB709_to_YUV709l_coeff[MATRIX_5X3_COEF_SIZE] = {
60 0, 0, 0, /* pre offset */
61 COEFF_NORM(0.181873), COEFF_NORM(0.611831), COEFF_NORM(0.061765),
62 COEFF_NORM(-0.100251), COEFF_NORM(-0.337249), COEFF_NORM(0.437500),
63 COEFF_NORM(0.437500), COEFF_NORM(-0.397384), COEFF_NORM(-0.040116),
64 0, 0, 0, /* 10'/11'/12' */
65 0, 0, 0, /* 20'/21'/22' */
66 64, 512, 512, /* offset */
67 0, 0, 0 /* mode, right_shift, clip_en */
70 /* eotf matrix: bypass */
71 static int eotf_bypass_coeff[EOTF_COEFF_SIZE] = {
72 EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0),
73 EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0),
74 EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0),
75 EOTF_COEFF_RIGHTSHIFT /* right shift */
78 static void meson_viu_set_g12a_osd1_matrix(struct meson_drm *priv,
81 /* VPP WRAP OSD1 matrix */
82 writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff),
83 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_PRE_OFFSET0_1));
85 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_PRE_OFFSET2));
86 writel(((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff),
87 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF00_01));
88 writel(((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff),
89 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF02_10));
90 writel(((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff),
91 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF11_12));
92 writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff),
93 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF20_21));
94 writel((m[11] & 0x1fff) << 16,
95 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF22));
97 writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff),
98 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_OFFSET0_1));
100 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_OFFSET2));
102 writel_bits_relaxed(BIT(0), csc_on ? BIT(0) : 0,
103 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_EN_CTRL));
106 static void meson_viu_set_osd_matrix(struct meson_drm *priv,
107 enum viu_matrix_sel_e m_select,
110 if (m_select == VIU_MATRIX_OSD) {
111 /* osd matrix, VIU_MATRIX_0 */
112 writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff),
113 priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET0_1));
115 priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET2));
116 writel(((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff),
117 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF00_01));
118 writel(((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff),
119 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF02_10));
120 writel(((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff),
121 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF11_12));
122 writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff),
123 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF20_21));
126 writel(((m[11] & 0x1fff) << 16) | (m[12] & 0x1fff),
128 _REG(VIU_OSD1_MATRIX_COEF22_30));
129 writel(((m[13] & 0x1fff) << 16) | (m[14] & 0x1fff),
131 _REG(VIU_OSD1_MATRIX_COEF31_32));
132 writel(((m[15] & 0x1fff) << 16) | (m[16] & 0x1fff),
134 _REG(VIU_OSD1_MATRIX_COEF40_41));
135 writel(m[17] & 0x1fff, priv->io_base +
136 _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
138 writel((m[11] & 0x1fff) << 16, priv->io_base +
139 _REG(VIU_OSD1_MATRIX_COEF22_30));
141 writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff),
142 priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET0_1));
143 writel(m[20] & 0xfff,
144 priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET2));
146 writel_bits_relaxed(3 << 30, m[21] << 30,
147 priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
148 writel_bits_relaxed(7 << 16, m[22] << 16,
149 priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
151 /* 23 reserved for clipping control */
152 writel_bits_relaxed(BIT(0), csc_on ? BIT(0) : 0,
153 priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL));
154 writel_bits_relaxed(BIT(1), 0,
155 priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL));
156 } else if (m_select == VIU_MATRIX_OSD_EOTF) {
159 /* osd eotf matrix, VIU_MATRIX_OSD_EOTF */
160 for (i = 0; i < 5; i++)
161 writel(((m[i * 2] & 0x1fff) << 16) |
162 (m[i * 2 + 1] & 0x1fff), priv->io_base +
163 _REG(VIU_OSD1_EOTF_CTL + i + 1));
165 writel_bits_relaxed(BIT(30), csc_on ? BIT(30) : 0,
166 priv->io_base + _REG(VIU_OSD1_EOTF_CTL));
167 writel_bits_relaxed(BIT(31), csc_on ? BIT(31) : 0,
168 priv->io_base + _REG(VIU_OSD1_EOTF_CTL));
172 #define OSD_EOTF_LUT_SIZE 33
173 #define OSD_OETF_LUT_SIZE 41
176 meson_viu_set_osd_lut(struct meson_drm *priv, enum viu_lut_sel_e lut_sel,
177 unsigned int *r_map, unsigned int *g_map,
178 unsigned int *b_map, bool csc_on)
180 unsigned int addr_port;
181 unsigned int data_port;
182 unsigned int ctrl_port;
185 if (lut_sel == VIU_LUT_OSD_EOTF) {
186 addr_port = VIU_OSD1_EOTF_LUT_ADDR_PORT;
187 data_port = VIU_OSD1_EOTF_LUT_DATA_PORT;
188 ctrl_port = VIU_OSD1_EOTF_CTL;
189 } else if (lut_sel == VIU_LUT_OSD_OETF) {
190 addr_port = VIU_OSD1_OETF_LUT_ADDR_PORT;
191 data_port = VIU_OSD1_OETF_LUT_DATA_PORT;
192 ctrl_port = VIU_OSD1_OETF_CTL;
196 if (lut_sel == VIU_LUT_OSD_OETF) {
197 writel(0, priv->io_base + _REG(addr_port));
199 for (i = 0; i < (OSD_OETF_LUT_SIZE / 2); i++)
200 writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16),
201 priv->io_base + _REG(data_port));
203 writel(r_map[OSD_OETF_LUT_SIZE - 1] | (g_map[0] << 16),
204 priv->io_base + _REG(data_port));
206 for (i = 0; i < (OSD_OETF_LUT_SIZE / 2); i++)
207 writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16),
208 priv->io_base + _REG(data_port));
210 for (i = 0; i < (OSD_OETF_LUT_SIZE / 2); i++)
211 writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16),
212 priv->io_base + _REG(data_port));
214 writel(b_map[OSD_OETF_LUT_SIZE - 1],
215 priv->io_base + _REG(data_port));
218 writel_bits_relaxed(0x7 << 29, 7 << 29,
219 priv->io_base + _REG(ctrl_port));
221 writel_bits_relaxed(0x7 << 29, 0,
222 priv->io_base + _REG(ctrl_port));
223 } else if (lut_sel == VIU_LUT_OSD_EOTF) {
224 writel(0, priv->io_base + _REG(addr_port));
226 for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); i++)
227 writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16),
228 priv->io_base + _REG(data_port));
230 writel(r_map[OSD_EOTF_LUT_SIZE - 1] | (g_map[0] << 16),
231 priv->io_base + _REG(data_port));
233 for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); i++)
234 writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16),
235 priv->io_base + _REG(data_port));
237 for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); i++)
238 writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16),
239 priv->io_base + _REG(data_port));
241 writel(b_map[OSD_EOTF_LUT_SIZE - 1],
242 priv->io_base + _REG(data_port));
245 writel_bits_relaxed(7 << 27, 7 << 27,
246 priv->io_base + _REG(ctrl_port));
248 writel_bits_relaxed(7 << 27, 0,
249 priv->io_base + _REG(ctrl_port));
251 writel_bits_relaxed(BIT(31), BIT(31),
252 priv->io_base + _REG(ctrl_port));
256 /* eotf lut: linear */
257 static unsigned int eotf_33_linear_mapping[OSD_EOTF_LUT_SIZE] = {
258 0x0000, 0x0200, 0x0400, 0x0600,
259 0x0800, 0x0a00, 0x0c00, 0x0e00,
260 0x1000, 0x1200, 0x1400, 0x1600,
261 0x1800, 0x1a00, 0x1c00, 0x1e00,
262 0x2000, 0x2200, 0x2400, 0x2600,
263 0x2800, 0x2a00, 0x2c00, 0x2e00,
264 0x3000, 0x3200, 0x3400, 0x3600,
265 0x3800, 0x3a00, 0x3c00, 0x3e00,
269 /* osd oetf lut: linear */
270 static unsigned int oetf_41_linear_mapping[OSD_OETF_LUT_SIZE] = {
280 1023, 1023, 1023, 1023,
284 static void meson_viu_load_matrix(struct meson_drm *priv)
286 /* eotf lut bypass */
287 meson_viu_set_osd_lut(priv, VIU_LUT_OSD_EOTF,
288 eotf_33_linear_mapping, /* R */
289 eotf_33_linear_mapping, /* G */
290 eotf_33_linear_mapping, /* B */
293 /* eotf matrix bypass */
294 meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD_EOTF,
298 /* oetf lut bypass */
299 meson_viu_set_osd_lut(priv, VIU_LUT_OSD_OETF,
300 oetf_41_linear_mapping, /* R */
301 oetf_41_linear_mapping, /* G */
302 oetf_41_linear_mapping, /* B */
305 /* osd matrix RGB709 to YUV709 limit */
306 meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD,
307 RGB709_to_YUV709l_coeff,
311 /* VIU OSD1 Reset as workaround for GXL+ Alpha OSD Bug */
312 void meson_viu_osd1_reset(struct meson_drm *priv)
314 uint32_t osd1_fifo_ctrl_stat, osd1_ctrl_stat2;
316 /* Save these 2 registers state */
317 osd1_fifo_ctrl_stat = readl_relaxed(
318 priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT));
319 osd1_ctrl_stat2 = readl_relaxed(
320 priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
323 writel_bits_relaxed(BIT(0), BIT(0),
324 priv->io_base + _REG(VIU_SW_RESET));
325 writel_bits_relaxed(BIT(0), 0,
326 priv->io_base + _REG(VIU_SW_RESET));
328 /* Rewrite these registers state lost in the reset */
329 writel_relaxed(osd1_fifo_ctrl_stat,
330 priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT));
331 writel_relaxed(osd1_ctrl_stat2,
332 priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
334 /* Reload the conversion matrix */
335 meson_viu_load_matrix(priv);
338 void meson_viu_init(struct meson_drm *priv)
343 writel_bits_relaxed(BIT(0) | BIT(21), 0,
344 priv->io_base + _REG(VIU_OSD1_CTRL_STAT));
345 writel_bits_relaxed(BIT(0) | BIT(21), 0,
346 priv->io_base + _REG(VIU_OSD2_CTRL_STAT));
348 /* On GXL/GXM, Use the 10bit HDR conversion matrix */
349 if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
350 meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
351 meson_viu_load_matrix(priv);
352 else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
353 meson_viu_set_g12a_osd1_matrix(priv, RGB709_to_YUV709l_coeff,
356 /* Initialize OSD1 fifo control register */
357 reg = BIT(0) | /* Urgent DDR request priority */
358 (4 << 5); /* hold_fifo_lines */
359 if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
360 reg |= (1 << 10) | /* burst length 32 */
361 (32 << 12) | /* fifo_depth_val: 32*8=256 */
362 (2 << 22) | /* 4 words in 1 burst */
366 reg |= (3 << 10) | /* burst length 64 */
367 (32 << 12) | /* fifo_depth_val: 32*8=256 */
368 (2 << 22) | /* 4 words in 1 burst */
370 writel_relaxed(reg, priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT));
371 writel_relaxed(reg, priv->io_base + _REG(VIU_OSD2_FIFO_CTRL_STAT));
373 /* Set OSD alpha replace value */
374 writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT,
375 0xff << OSD_REPLACE_SHIFT,
376 priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
377 writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT,
378 0xff << OSD_REPLACE_SHIFT,
379 priv->io_base + _REG(VIU_OSD2_CTRL_STAT2));
381 /* Disable VD1 AFBC */
382 /* di_mif0_en=0 mif0_to_vpp_en=0 di_mad_en=0 */
383 writel_bits_relaxed(0x7 << 16, 0,
384 priv->io_base + _REG(VIU_MISC_CTRL0));
386 writel_bits_relaxed(BIT(20), 0,
387 priv->io_base + _REG(VIU_MISC_CTRL0));
388 writel_relaxed(0, priv->io_base + _REG(AFBC_ENABLE));
390 writel_relaxed(0x00FF00C0,
391 priv->io_base + _REG(VD1_IF0_LUMA_FIFO_SIZE));
392 writel_relaxed(0x00FF00C0,
393 priv->io_base + _REG(VD2_IF0_LUMA_FIFO_SIZE));
395 if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
396 writel_relaxed(4 << 29 |
398 1 << 26 | /* blend_din0 input to blend0 */
399 1 << 25 | /* blend1_dout to blend2 */
400 1 << 24 | /* blend1_din3 input to blend1 */
404 priv->io_base + _REG(VIU_OSD_BLEND_CTRL));
405 writel_relaxed(1 << 20,
406 priv->io_base + _REG(OSD1_BLEND_SRC_CTRL));
407 writel_relaxed(1 << 20,
408 priv->io_base + _REG(OSD2_BLEND_SRC_CTRL));
409 writel_relaxed(0, priv->io_base + _REG(VD1_BLEND_SRC_CTRL));
410 writel_relaxed(0, priv->io_base + _REG(VD2_BLEND_SRC_CTRL));
412 priv->io_base + _REG(VIU_OSD_BLEND_DUMMY_DATA0));
414 priv->io_base + _REG(VIU_OSD_BLEND_DUMMY_ALPHA));
415 writel_bits_relaxed(0x3 << 2, 0x3 << 2,
416 priv->io_base + _REG(DOLBY_PATH_CTRL));
419 priv->viu.osd1_enabled = false;
420 priv->viu.osd1_commit = false;
421 priv->viu.osd1_interlace = false;