1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2016 BayLibre, SAS
5 * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
6 * Copyright (C) 2014 Endless Mobile
9 #include <linux/export.h>
10 #include <linux/bitfield.h>
12 #include <drm/drm_fourcc.h>
14 #include "meson_drv.h"
15 #include "meson_viu.h"
16 #include "meson_registers.h"
19 * DOC: Video Input Unit
21 * VIU Handles the Pixel scanout and the basic Colorspace conversions
22 * We handle the following features :
24 * - OSD1 RGB565/RGB888/xRGB8888 scanout
25 * - RGB conversion to x/cb/cr
26 * - Progressive or Interlace buffer scanout
27 * - OSD1 Commit on Vsync
28 * - HDR OSD matrix for GXL/GXM
32 * - BGR888/xBGR8888/BGRx8888/BGRx8888 modes
33 * - YUV4:2:2 Y0CbY1Cr scanout
34 * - Conversion to YUV 4:4:4 from 4:2:2 input
35 * - Colorkey Alpha matching
36 * - Big endian scanout
37 * - X/Y reverse scanout
38 * - Global alpha setup
39 * - OSD2 support, would need interlace switching on vsync
40 * - OSD1 full scaling to support TV overscan
45 enum viu_matrix_sel_e {
46 VIU_MATRIX_OSD_EOTF = 0,
55 #define COEFF_NORM(a) ((int)((((a) * 2048.0) + 1) / 2))
56 #define MATRIX_5X3_COEF_SIZE 24
58 #define EOTF_COEFF_NORM(a) ((int)((((a) * 4096.0) + 1) / 2))
59 #define EOTF_COEFF_SIZE 10
60 #define EOTF_COEFF_RIGHTSHIFT 1
62 static int RGB709_to_YUV709l_coeff[MATRIX_5X3_COEF_SIZE] = {
63 0, 0, 0, /* pre offset */
64 COEFF_NORM(0.181873), COEFF_NORM(0.611831), COEFF_NORM(0.061765),
65 COEFF_NORM(-0.100251), COEFF_NORM(-0.337249), COEFF_NORM(0.437500),
66 COEFF_NORM(0.437500), COEFF_NORM(-0.397384), COEFF_NORM(-0.040116),
67 0, 0, 0, /* 10'/11'/12' */
68 0, 0, 0, /* 20'/21'/22' */
69 64, 512, 512, /* offset */
70 0, 0, 0 /* mode, right_shift, clip_en */
73 /* eotf matrix: bypass */
74 static int eotf_bypass_coeff[EOTF_COEFF_SIZE] = {
75 EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0),
76 EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0),
77 EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0),
78 EOTF_COEFF_RIGHTSHIFT /* right shift */
81 static void meson_viu_set_g12a_osd1_matrix(struct meson_drm *priv,
84 /* VPP WRAP OSD1 matrix */
85 writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff),
86 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_PRE_OFFSET0_1));
88 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_PRE_OFFSET2));
89 writel(((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff),
90 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF00_01));
91 writel(((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff),
92 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF02_10));
93 writel(((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff),
94 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF11_12));
95 writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff),
96 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF20_21));
97 writel((m[11] & 0x1fff) << 16,
98 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF22));
100 writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff),
101 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_OFFSET0_1));
102 writel(m[20] & 0xfff,
103 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_OFFSET2));
105 writel_bits_relaxed(BIT(0), csc_on ? BIT(0) : 0,
106 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_EN_CTRL));
109 static void meson_viu_set_osd_matrix(struct meson_drm *priv,
110 enum viu_matrix_sel_e m_select,
113 if (m_select == VIU_MATRIX_OSD) {
114 /* osd matrix, VIU_MATRIX_0 */
115 writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff),
116 priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET0_1));
118 priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET2));
119 writel(((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff),
120 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF00_01));
121 writel(((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff),
122 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF02_10));
123 writel(((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff),
124 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF11_12));
125 writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff),
126 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF20_21));
129 writel(((m[11] & 0x1fff) << 16) | (m[12] & 0x1fff),
131 _REG(VIU_OSD1_MATRIX_COEF22_30));
132 writel(((m[13] & 0x1fff) << 16) | (m[14] & 0x1fff),
134 _REG(VIU_OSD1_MATRIX_COEF31_32));
135 writel(((m[15] & 0x1fff) << 16) | (m[16] & 0x1fff),
137 _REG(VIU_OSD1_MATRIX_COEF40_41));
138 writel(m[17] & 0x1fff, priv->io_base +
139 _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
141 writel((m[11] & 0x1fff) << 16, priv->io_base +
142 _REG(VIU_OSD1_MATRIX_COEF22_30));
144 writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff),
145 priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET0_1));
146 writel(m[20] & 0xfff,
147 priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET2));
149 writel_bits_relaxed(3 << 30, m[21] << 30,
150 priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
151 writel_bits_relaxed(7 << 16, m[22] << 16,
152 priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
154 /* 23 reserved for clipping control */
155 writel_bits_relaxed(BIT(0), csc_on ? BIT(0) : 0,
156 priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL));
157 writel_bits_relaxed(BIT(1), 0,
158 priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL));
159 } else if (m_select == VIU_MATRIX_OSD_EOTF) {
162 /* osd eotf matrix, VIU_MATRIX_OSD_EOTF */
163 for (i = 0; i < 5; i++)
164 writel(((m[i * 2] & 0x1fff) << 16) |
165 (m[i * 2 + 1] & 0x1fff), priv->io_base +
166 _REG(VIU_OSD1_EOTF_CTL + i + 1));
168 writel_bits_relaxed(BIT(30), csc_on ? BIT(30) : 0,
169 priv->io_base + _REG(VIU_OSD1_EOTF_CTL));
170 writel_bits_relaxed(BIT(31), csc_on ? BIT(31) : 0,
171 priv->io_base + _REG(VIU_OSD1_EOTF_CTL));
175 #define OSD_EOTF_LUT_SIZE 33
176 #define OSD_OETF_LUT_SIZE 41
179 meson_viu_set_osd_lut(struct meson_drm *priv, enum viu_lut_sel_e lut_sel,
180 unsigned int *r_map, unsigned int *g_map,
181 unsigned int *b_map, bool csc_on)
183 unsigned int addr_port;
184 unsigned int data_port;
185 unsigned int ctrl_port;
188 if (lut_sel == VIU_LUT_OSD_EOTF) {
189 addr_port = VIU_OSD1_EOTF_LUT_ADDR_PORT;
190 data_port = VIU_OSD1_EOTF_LUT_DATA_PORT;
191 ctrl_port = VIU_OSD1_EOTF_CTL;
192 } else if (lut_sel == VIU_LUT_OSD_OETF) {
193 addr_port = VIU_OSD1_OETF_LUT_ADDR_PORT;
194 data_port = VIU_OSD1_OETF_LUT_DATA_PORT;
195 ctrl_port = VIU_OSD1_OETF_CTL;
199 if (lut_sel == VIU_LUT_OSD_OETF) {
200 writel(0, priv->io_base + _REG(addr_port));
202 for (i = 0; i < (OSD_OETF_LUT_SIZE / 2); i++)
203 writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16),
204 priv->io_base + _REG(data_port));
206 writel(r_map[OSD_OETF_LUT_SIZE - 1] | (g_map[0] << 16),
207 priv->io_base + _REG(data_port));
209 for (i = 0; i < (OSD_OETF_LUT_SIZE / 2); i++)
210 writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16),
211 priv->io_base + _REG(data_port));
213 for (i = 0; i < (OSD_OETF_LUT_SIZE / 2); i++)
214 writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16),
215 priv->io_base + _REG(data_port));
217 writel(b_map[OSD_OETF_LUT_SIZE - 1],
218 priv->io_base + _REG(data_port));
221 writel_bits_relaxed(0x7 << 29, 7 << 29,
222 priv->io_base + _REG(ctrl_port));
224 writel_bits_relaxed(0x7 << 29, 0,
225 priv->io_base + _REG(ctrl_port));
226 } else if (lut_sel == VIU_LUT_OSD_EOTF) {
227 writel(0, priv->io_base + _REG(addr_port));
229 for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); i++)
230 writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16),
231 priv->io_base + _REG(data_port));
233 writel(r_map[OSD_EOTF_LUT_SIZE - 1] | (g_map[0] << 16),
234 priv->io_base + _REG(data_port));
236 for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); i++)
237 writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16),
238 priv->io_base + _REG(data_port));
240 for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); i++)
241 writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16),
242 priv->io_base + _REG(data_port));
244 writel(b_map[OSD_EOTF_LUT_SIZE - 1],
245 priv->io_base + _REG(data_port));
248 writel_bits_relaxed(7 << 27, 7 << 27,
249 priv->io_base + _REG(ctrl_port));
251 writel_bits_relaxed(7 << 27, 0,
252 priv->io_base + _REG(ctrl_port));
254 writel_bits_relaxed(BIT(31), BIT(31),
255 priv->io_base + _REG(ctrl_port));
259 /* eotf lut: linear */
260 static unsigned int eotf_33_linear_mapping[OSD_EOTF_LUT_SIZE] = {
261 0x0000, 0x0200, 0x0400, 0x0600,
262 0x0800, 0x0a00, 0x0c00, 0x0e00,
263 0x1000, 0x1200, 0x1400, 0x1600,
264 0x1800, 0x1a00, 0x1c00, 0x1e00,
265 0x2000, 0x2200, 0x2400, 0x2600,
266 0x2800, 0x2a00, 0x2c00, 0x2e00,
267 0x3000, 0x3200, 0x3400, 0x3600,
268 0x3800, 0x3a00, 0x3c00, 0x3e00,
272 /* osd oetf lut: linear */
273 static unsigned int oetf_41_linear_mapping[OSD_OETF_LUT_SIZE] = {
283 1023, 1023, 1023, 1023,
287 static void meson_viu_load_matrix(struct meson_drm *priv)
289 /* eotf lut bypass */
290 meson_viu_set_osd_lut(priv, VIU_LUT_OSD_EOTF,
291 eotf_33_linear_mapping, /* R */
292 eotf_33_linear_mapping, /* G */
293 eotf_33_linear_mapping, /* B */
296 /* eotf matrix bypass */
297 meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD_EOTF,
301 /* oetf lut bypass */
302 meson_viu_set_osd_lut(priv, VIU_LUT_OSD_OETF,
303 oetf_41_linear_mapping, /* R */
304 oetf_41_linear_mapping, /* G */
305 oetf_41_linear_mapping, /* B */
308 /* osd matrix RGB709 to YUV709 limit */
309 meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD,
310 RGB709_to_YUV709l_coeff,
314 /* VIU OSD1 Reset as workaround for GXL+ Alpha OSD Bug */
315 void meson_viu_osd1_reset(struct meson_drm *priv)
317 uint32_t osd1_fifo_ctrl_stat, osd1_ctrl_stat2;
319 /* Save these 2 registers state */
320 osd1_fifo_ctrl_stat = readl_relaxed(
321 priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT));
322 osd1_ctrl_stat2 = readl_relaxed(
323 priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
326 writel_bits_relaxed(VIU_SW_RESET_OSD1, VIU_SW_RESET_OSD1,
327 priv->io_base + _REG(VIU_SW_RESET));
328 writel_bits_relaxed(VIU_SW_RESET_OSD1, 0,
329 priv->io_base + _REG(VIU_SW_RESET));
331 /* Rewrite these registers state lost in the reset */
332 writel_relaxed(osd1_fifo_ctrl_stat,
333 priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT));
334 writel_relaxed(osd1_ctrl_stat2,
335 priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
337 /* Reload the conversion matrix */
338 meson_viu_load_matrix(priv);
341 #define OSD1_MALI_ORDER_ABGR \
342 (FIELD_PREP(VIU_OSD1_MALI_AFBCD_A_REORDER, \
343 VIU_OSD1_MALI_REORDER_A) | \
344 FIELD_PREP(VIU_OSD1_MALI_AFBCD_B_REORDER, \
345 VIU_OSD1_MALI_REORDER_B) | \
346 FIELD_PREP(VIU_OSD1_MALI_AFBCD_G_REORDER, \
347 VIU_OSD1_MALI_REORDER_G) | \
348 FIELD_PREP(VIU_OSD1_MALI_AFBCD_R_REORDER, \
349 VIU_OSD1_MALI_REORDER_R))
351 #define OSD1_MALI_ORDER_ARGB \
352 (FIELD_PREP(VIU_OSD1_MALI_AFBCD_A_REORDER, \
353 VIU_OSD1_MALI_REORDER_A) | \
354 FIELD_PREP(VIU_OSD1_MALI_AFBCD_B_REORDER, \
355 VIU_OSD1_MALI_REORDER_R) | \
356 FIELD_PREP(VIU_OSD1_MALI_AFBCD_G_REORDER, \
357 VIU_OSD1_MALI_REORDER_G) | \
358 FIELD_PREP(VIU_OSD1_MALI_AFBCD_R_REORDER, \
359 VIU_OSD1_MALI_REORDER_B))
361 void meson_viu_g12a_enable_osd1_afbc(struct meson_drm *priv)
363 u32 afbc_order = OSD1_MALI_ORDER_ARGB;
365 /* Enable Mali AFBC Unpack */
366 writel_bits_relaxed(VIU_OSD1_MALI_UNPACK_EN,
367 VIU_OSD1_MALI_UNPACK_EN,
368 priv->io_base + _REG(VIU_OSD1_MALI_UNPACK_CTRL));
370 switch (priv->afbcd.format) {
371 case DRM_FORMAT_XBGR8888:
372 case DRM_FORMAT_ABGR8888:
373 afbc_order = OSD1_MALI_ORDER_ABGR;
377 /* Setup RGBA Reordering */
378 writel_bits_relaxed(VIU_OSD1_MALI_AFBCD_A_REORDER |
379 VIU_OSD1_MALI_AFBCD_B_REORDER |
380 VIU_OSD1_MALI_AFBCD_G_REORDER |
381 VIU_OSD1_MALI_AFBCD_R_REORDER,
383 priv->io_base + _REG(VIU_OSD1_MALI_UNPACK_CTRL));
385 /* Select AFBCD path for OSD1 */
386 writel_bits_relaxed(OSD_PATH_OSD_AXI_SEL_OSD1_AFBCD,
387 OSD_PATH_OSD_AXI_SEL_OSD1_AFBCD,
388 priv->io_base + _REG(OSD_PATH_MISC_CTRL));
391 void meson_viu_g12a_disable_osd1_afbc(struct meson_drm *priv)
393 /* Disable AFBCD path for OSD1 */
394 writel_bits_relaxed(OSD_PATH_OSD_AXI_SEL_OSD1_AFBCD, 0,
395 priv->io_base + _REG(OSD_PATH_MISC_CTRL));
397 /* Disable AFBCD unpack */
398 writel_bits_relaxed(VIU_OSD1_MALI_UNPACK_EN, 0,
399 priv->io_base + _REG(VIU_OSD1_MALI_UNPACK_CTRL));
402 void meson_viu_gxm_enable_osd1_afbc(struct meson_drm *priv)
404 writel_bits_relaxed(MALI_AFBC_MISC, FIELD_PREP(MALI_AFBC_MISC, 0x90),
405 priv->io_base + _REG(VIU_MISC_CTRL1));
408 void meson_viu_gxm_disable_osd1_afbc(struct meson_drm *priv)
410 writel_bits_relaxed(MALI_AFBC_MISC, FIELD_PREP(MALI_AFBC_MISC, 0x00),
411 priv->io_base + _REG(VIU_MISC_CTRL1));
414 void meson_viu_init(struct meson_drm *priv)
419 writel_bits_relaxed(VIU_OSD1_OSD_BLK_ENABLE | VIU_OSD1_OSD_ENABLE, 0,
420 priv->io_base + _REG(VIU_OSD1_CTRL_STAT));
421 writel_bits_relaxed(VIU_OSD1_OSD_BLK_ENABLE | VIU_OSD1_OSD_ENABLE, 0,
422 priv->io_base + _REG(VIU_OSD2_CTRL_STAT));
424 /* On GXL/GXM, Use the 10bit HDR conversion matrix */
425 if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
426 meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL))
427 meson_viu_load_matrix(priv);
428 else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
429 meson_viu_set_g12a_osd1_matrix(priv, RGB709_to_YUV709l_coeff,
432 /* Initialize OSD1 fifo control register */
433 reg = VIU_OSD_DDR_PRIORITY_URGENT |
434 VIU_OSD_HOLD_FIFO_LINES(31) |
435 VIU_OSD_FIFO_DEPTH_VAL(32) | /* fifo_depth_val: 32*8=256 */
436 VIU_OSD_WORDS_PER_BURST(4) | /* 4 words in 1 burst */
437 VIU_OSD_FIFO_LIMITS(2); /* fifo_lim: 2*16=32 */
439 if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
440 reg |= VIU_OSD_BURST_LENGTH_32;
442 reg |= VIU_OSD_BURST_LENGTH_64;
444 writel_relaxed(reg, priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT));
445 writel_relaxed(reg, priv->io_base + _REG(VIU_OSD2_FIFO_CTRL_STAT));
447 /* Set OSD alpha replace value */
448 writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT,
449 0xff << OSD_REPLACE_SHIFT,
450 priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
451 writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT,
452 0xff << OSD_REPLACE_SHIFT,
453 priv->io_base + _REG(VIU_OSD2_CTRL_STAT2));
455 /* Disable VD1 AFBC */
456 /* di_mif0_en=0 mif0_to_vpp_en=0 di_mad_en=0 and afbc vd1 set=0*/
457 writel_bits_relaxed(VIU_CTRL0_VD1_AFBC_MASK, 0,
458 priv->io_base + _REG(VIU_MISC_CTRL0));
459 writel_relaxed(0, priv->io_base + _REG(AFBC_ENABLE));
461 writel_relaxed(0x00FF00C0,
462 priv->io_base + _REG(VD1_IF0_LUMA_FIFO_SIZE));
463 writel_relaxed(0x00FF00C0,
464 priv->io_base + _REG(VD2_IF0_LUMA_FIFO_SIZE));
466 if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
467 writel_relaxed(VIU_OSD_BLEND_REORDER(0, 1) |
468 VIU_OSD_BLEND_REORDER(1, 0) |
469 VIU_OSD_BLEND_REORDER(2, 0) |
470 VIU_OSD_BLEND_REORDER(3, 0) |
471 VIU_OSD_BLEND_DIN_EN(1) |
472 VIU_OSD_BLEND1_DIN3_BYPASS_TO_DOUT1 |
473 VIU_OSD_BLEND1_DOUT_BYPASS_TO_BLEND2 |
474 VIU_OSD_BLEND_DIN0_BYPASS_TO_DOUT0 |
475 VIU_OSD_BLEND_BLEN2_PREMULT_EN(1) |
476 VIU_OSD_BLEND_HOLD_LINES(4),
477 priv->io_base + _REG(VIU_OSD_BLEND_CTRL));
479 writel_relaxed(OSD_BLEND_PATH_SEL_ENABLE,
480 priv->io_base + _REG(OSD1_BLEND_SRC_CTRL));
481 writel_relaxed(OSD_BLEND_PATH_SEL_ENABLE,
482 priv->io_base + _REG(OSD2_BLEND_SRC_CTRL));
483 writel_relaxed(0, priv->io_base + _REG(VD1_BLEND_SRC_CTRL));
484 writel_relaxed(0, priv->io_base + _REG(VD2_BLEND_SRC_CTRL));
486 priv->io_base + _REG(VIU_OSD_BLEND_DUMMY_DATA0));
488 priv->io_base + _REG(VIU_OSD_BLEND_DUMMY_ALPHA));
490 writel_bits_relaxed(DOLBY_BYPASS_EN(0xc), DOLBY_BYPASS_EN(0xc),
491 priv->io_base + _REG(DOLBY_PATH_CTRL));
493 meson_viu_g12a_disable_osd1_afbc(priv);
496 if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM))
497 meson_viu_gxm_disable_osd1_afbc(priv);
499 priv->viu.osd1_enabled = false;
500 priv->viu.osd1_commit = false;
501 priv->viu.osd1_interlace = false;