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_registers.h"
31 * DOC: Video Input Unit
33 * VIU Handles the Pixel scanout and the basic Colorspace conversions
34 * We handle the following features :
36 * - OSD1 RGB565/RGB888/xRGB8888 scanout
37 * - RGB conversion to x/cb/cr
38 * - Progressive or Interlace buffer scanout
39 * - OSD1 Commit on Vsync
40 * - HDR OSD matrix for GXL/GXM
44 * - BGR888/xBGR8888/BGRx8888/BGRx8888 modes
45 * - YUV4:2:2 Y0CbY1Cr scanout
46 * - Conversion to YUV 4:4:4 from 4:2:2 input
47 * - Colorkey Alpha matching
48 * - Big endian scanout
49 * - X/Y reverse scanout
50 * - Global alpha setup
51 * - OSD2 support, would need interlace switching on vsync
52 * - OSD1 full scaling to support TV overscan
57 enum viu_matrix_sel_e {
58 VIU_MATRIX_OSD_EOTF = 0,
67 #define COEFF_NORM(a) ((int)((((a) * 2048.0) + 1) / 2))
68 #define MATRIX_5X3_COEF_SIZE 24
70 #define EOTF_COEFF_NORM(a) ((int)((((a) * 4096.0) + 1) / 2))
71 #define EOTF_COEFF_SIZE 10
72 #define EOTF_COEFF_RIGHTSHIFT 1
74 static int RGB709_to_YUV709l_coeff[MATRIX_5X3_COEF_SIZE] = {
75 0, 0, 0, /* pre offset */
76 COEFF_NORM(0.181873), COEFF_NORM(0.611831), COEFF_NORM(0.061765),
77 COEFF_NORM(-0.100251), COEFF_NORM(-0.337249), COEFF_NORM(0.437500),
78 COEFF_NORM(0.437500), COEFF_NORM(-0.397384), COEFF_NORM(-0.040116),
79 0, 0, 0, /* 10'/11'/12' */
80 0, 0, 0, /* 20'/21'/22' */
81 64, 512, 512, /* offset */
82 0, 0, 0 /* mode, right_shift, clip_en */
85 /* eotf matrix: bypass */
86 static int eotf_bypass_coeff[EOTF_COEFF_SIZE] = {
87 EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0),
88 EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0),
89 EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0),
90 EOTF_COEFF_RIGHTSHIFT /* right shift */
93 static void meson_viu_set_g12a_osd1_matrix(struct meson_drm *priv,
96 /* VPP WRAP OSD1 matrix */
97 writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff),
98 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_PRE_OFFSET0_1));
100 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_PRE_OFFSET2));
101 writel(((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff),
102 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF00_01));
103 writel(((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff),
104 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF02_10));
105 writel(((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff),
106 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF11_12));
107 writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff),
108 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF20_21));
109 writel((m[11] & 0x1fff) << 16,
110 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF22));
112 writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff),
113 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_OFFSET0_1));
114 writel(m[20] & 0xfff,
115 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_OFFSET2));
117 writel_bits_relaxed(BIT(0), csc_on ? BIT(0) : 0,
118 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_EN_CTRL));
121 static void meson_viu_set_osd_matrix(struct meson_drm *priv,
122 enum viu_matrix_sel_e m_select,
125 if (m_select == VIU_MATRIX_OSD) {
126 /* osd matrix, VIU_MATRIX_0 */
127 writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff),
128 priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET0_1));
130 priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET2));
131 writel(((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff),
132 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF00_01));
133 writel(((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff),
134 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF02_10));
135 writel(((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff),
136 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF11_12));
137 writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff),
138 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF20_21));
141 writel(((m[11] & 0x1fff) << 16) | (m[12] & 0x1fff),
143 _REG(VIU_OSD1_MATRIX_COEF22_30));
144 writel(((m[13] & 0x1fff) << 16) | (m[14] & 0x1fff),
146 _REG(VIU_OSD1_MATRIX_COEF31_32));
147 writel(((m[15] & 0x1fff) << 16) | (m[16] & 0x1fff),
149 _REG(VIU_OSD1_MATRIX_COEF40_41));
150 writel(m[17] & 0x1fff, priv->io_base +
151 _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
153 writel((m[11] & 0x1fff) << 16, priv->io_base +
154 _REG(VIU_OSD1_MATRIX_COEF22_30));
156 writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff),
157 priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET0_1));
158 writel(m[20] & 0xfff,
159 priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET2));
161 writel_bits_relaxed(3 << 30, m[21] << 30,
162 priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
163 writel_bits_relaxed(7 << 16, m[22] << 16,
164 priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
166 /* 23 reserved for clipping control */
167 writel_bits_relaxed(BIT(0), csc_on ? BIT(0) : 0,
168 priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL));
169 writel_bits_relaxed(BIT(1), 0,
170 priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL));
171 } else if (m_select == VIU_MATRIX_OSD_EOTF) {
174 /* osd eotf matrix, VIU_MATRIX_OSD_EOTF */
175 for (i = 0; i < 5; i++)
176 writel(((m[i * 2] & 0x1fff) << 16) |
177 (m[i * 2 + 1] & 0x1fff), priv->io_base +
178 _REG(VIU_OSD1_EOTF_CTL + i + 1));
180 writel_bits_relaxed(BIT(30), csc_on ? BIT(30) : 0,
181 priv->io_base + _REG(VIU_OSD1_EOTF_CTL));
182 writel_bits_relaxed(BIT(31), csc_on ? BIT(31) : 0,
183 priv->io_base + _REG(VIU_OSD1_EOTF_CTL));
187 #define OSD_EOTF_LUT_SIZE 33
188 #define OSD_OETF_LUT_SIZE 41
191 meson_viu_set_osd_lut(struct meson_drm *priv, enum viu_lut_sel_e lut_sel,
192 unsigned int *r_map, unsigned int *g_map,
193 unsigned int *b_map, bool csc_on)
195 unsigned int addr_port;
196 unsigned int data_port;
197 unsigned int ctrl_port;
200 if (lut_sel == VIU_LUT_OSD_EOTF) {
201 addr_port = VIU_OSD1_EOTF_LUT_ADDR_PORT;
202 data_port = VIU_OSD1_EOTF_LUT_DATA_PORT;
203 ctrl_port = VIU_OSD1_EOTF_CTL;
204 } else if (lut_sel == VIU_LUT_OSD_OETF) {
205 addr_port = VIU_OSD1_OETF_LUT_ADDR_PORT;
206 data_port = VIU_OSD1_OETF_LUT_DATA_PORT;
207 ctrl_port = VIU_OSD1_OETF_CTL;
211 if (lut_sel == VIU_LUT_OSD_OETF) {
212 writel(0, priv->io_base + _REG(addr_port));
214 for (i = 0; i < (OSD_OETF_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_OETF_LUT_SIZE - 1] | (g_map[0] << 16),
219 priv->io_base + _REG(data_port));
221 for (i = 0; i < (OSD_OETF_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_OETF_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_OETF_LUT_SIZE - 1],
230 priv->io_base + _REG(data_port));
233 writel_bits_relaxed(0x7 << 29, 7 << 29,
234 priv->io_base + _REG(ctrl_port));
236 writel_bits_relaxed(0x7 << 29, 0,
237 priv->io_base + _REG(ctrl_port));
238 } else if (lut_sel == VIU_LUT_OSD_EOTF) {
239 writel(0, priv->io_base + _REG(addr_port));
241 for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); i++)
242 writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16),
243 priv->io_base + _REG(data_port));
245 writel(r_map[OSD_EOTF_LUT_SIZE - 1] | (g_map[0] << 16),
246 priv->io_base + _REG(data_port));
248 for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); i++)
249 writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16),
250 priv->io_base + _REG(data_port));
252 for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); i++)
253 writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16),
254 priv->io_base + _REG(data_port));
256 writel(b_map[OSD_EOTF_LUT_SIZE - 1],
257 priv->io_base + _REG(data_port));
260 writel_bits_relaxed(7 << 27, 7 << 27,
261 priv->io_base + _REG(ctrl_port));
263 writel_bits_relaxed(7 << 27, 0,
264 priv->io_base + _REG(ctrl_port));
266 writel_bits_relaxed(BIT(31), BIT(31),
267 priv->io_base + _REG(ctrl_port));
271 /* eotf lut: linear */
272 static unsigned int eotf_33_linear_mapping[OSD_EOTF_LUT_SIZE] = {
273 0x0000, 0x0200, 0x0400, 0x0600,
274 0x0800, 0x0a00, 0x0c00, 0x0e00,
275 0x1000, 0x1200, 0x1400, 0x1600,
276 0x1800, 0x1a00, 0x1c00, 0x1e00,
277 0x2000, 0x2200, 0x2400, 0x2600,
278 0x2800, 0x2a00, 0x2c00, 0x2e00,
279 0x3000, 0x3200, 0x3400, 0x3600,
280 0x3800, 0x3a00, 0x3c00, 0x3e00,
284 /* osd oetf lut: linear */
285 static unsigned int oetf_41_linear_mapping[OSD_OETF_LUT_SIZE] = {
295 1023, 1023, 1023, 1023,
299 static void meson_viu_load_matrix(struct meson_drm *priv)
301 /* eotf lut bypass */
302 meson_viu_set_osd_lut(priv, VIU_LUT_OSD_EOTF,
303 eotf_33_linear_mapping, /* R */
304 eotf_33_linear_mapping, /* G */
305 eotf_33_linear_mapping, /* B */
308 /* eotf matrix bypass */
309 meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD_EOTF,
313 /* oetf lut bypass */
314 meson_viu_set_osd_lut(priv, VIU_LUT_OSD_OETF,
315 oetf_41_linear_mapping, /* R */
316 oetf_41_linear_mapping, /* G */
317 oetf_41_linear_mapping, /* B */
320 /* osd matrix RGB709 to YUV709 limit */
321 meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD,
322 RGB709_to_YUV709l_coeff,
326 /* VIU OSD1 Reset as workaround for GXL+ Alpha OSD Bug */
327 void meson_viu_osd1_reset(struct meson_drm *priv)
329 uint32_t osd1_fifo_ctrl_stat, osd1_ctrl_stat2;
331 /* Save these 2 registers state */
332 osd1_fifo_ctrl_stat = readl_relaxed(
333 priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT));
334 osd1_ctrl_stat2 = readl_relaxed(
335 priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
338 writel_bits_relaxed(BIT(0), BIT(0),
339 priv->io_base + _REG(VIU_SW_RESET));
340 writel_bits_relaxed(BIT(0), 0,
341 priv->io_base + _REG(VIU_SW_RESET));
343 /* Rewrite these registers state lost in the reset */
344 writel_relaxed(osd1_fifo_ctrl_stat,
345 priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT));
346 writel_relaxed(osd1_ctrl_stat2,
347 priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
349 /* Reload the conversion matrix */
350 meson_viu_load_matrix(priv);
353 void meson_viu_init(struct meson_drm *priv)
358 writel_bits_relaxed(BIT(0) | BIT(21), 0,
359 priv->io_base + _REG(VIU_OSD1_CTRL_STAT));
360 writel_bits_relaxed(BIT(0) | BIT(21), 0,
361 priv->io_base + _REG(VIU_OSD2_CTRL_STAT));
363 /* On GXL/GXM, Use the 10bit HDR conversion matrix */
364 if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
365 meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
366 meson_viu_load_matrix(priv);
367 else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
368 meson_viu_set_g12a_osd1_matrix(priv, RGB709_to_YUV709l_coeff,
371 /* Initialize OSD1 fifo control register */
372 reg = BIT(0) | /* Urgent DDR request priority */
373 (4 << 5); /* hold_fifo_lines */
374 if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
375 reg |= (1 << 10) | /* burst length 32 */
376 (32 << 12) | /* fifo_depth_val: 32*8=256 */
377 (2 << 22) | /* 4 words in 1 burst */
381 reg |= (3 << 10) | /* burst length 64 */
382 (32 << 12) | /* fifo_depth_val: 32*8=256 */
383 (2 << 22) | /* 4 words in 1 burst */
385 writel_relaxed(reg, priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT));
386 writel_relaxed(reg, priv->io_base + _REG(VIU_OSD2_FIFO_CTRL_STAT));
388 /* Set OSD alpha replace value */
389 writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT,
390 0xff << OSD_REPLACE_SHIFT,
391 priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
392 writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT,
393 0xff << OSD_REPLACE_SHIFT,
394 priv->io_base + _REG(VIU_OSD2_CTRL_STAT2));
396 /* Disable VD1 AFBC */
397 /* di_mif0_en=0 mif0_to_vpp_en=0 di_mad_en=0 */
398 writel_bits_relaxed(0x7 << 16, 0,
399 priv->io_base + _REG(VIU_MISC_CTRL0));
401 writel_bits_relaxed(BIT(20), 0,
402 priv->io_base + _REG(VIU_MISC_CTRL0));
403 writel_relaxed(0, priv->io_base + _REG(AFBC_ENABLE));
405 writel_relaxed(0x00FF00C0,
406 priv->io_base + _REG(VD1_IF0_LUMA_FIFO_SIZE));
407 writel_relaxed(0x00FF00C0,
408 priv->io_base + _REG(VD2_IF0_LUMA_FIFO_SIZE));
410 if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
411 writel_relaxed(4 << 29 |
413 1 << 26 | /* blend_din0 input to blend0 */
414 1 << 25 | /* blend1_dout to blend2 */
415 1 << 24 | /* blend1_din3 input to blend1 */
419 priv->io_base + _REG(VIU_OSD_BLEND_CTRL));
420 writel_relaxed(3 << 8 |
422 priv->io_base + _REG(OSD1_BLEND_SRC_CTRL));
423 writel_relaxed(1 << 20,
424 priv->io_base + _REG(OSD2_BLEND_SRC_CTRL));
425 writel_relaxed(0, priv->io_base + _REG(VD1_BLEND_SRC_CTRL));
426 writel_relaxed(0, priv->io_base + _REG(VD2_BLEND_SRC_CTRL));
428 priv->io_base + _REG(VIU_OSD_BLEND_DUMMY_DATA0));
430 priv->io_base + _REG(VIU_OSD_BLEND_DUMMY_ALPHA));
431 writel_bits_relaxed(0x3 << 2, 0x3 << 2,
432 priv->io_base + _REG(DOLBY_PATH_CTRL));
435 priv->viu.osd1_enabled = false;
436 priv->viu.osd1_commit = false;
437 priv->viu.osd1_interlace = false;