1 // SPDX-License-Identifier: GPL-2.0
3 * Amlogic Meson Video Processing Unit driver
5 * Copyright (c) 2018 BayLibre, SAS.
6 * Author: Neil Armstrong <narmstrong@baylibre.com>
11 #include "meson_vpu.h"
14 #define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */
15 #define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */
16 #define HHI_HDMI_PHY_CNTL0 0x3a0 /* 0xe8 offset in data sheet */
19 #define OSD_REPLACE_EN BIT(14)
20 #define OSD_REPLACE_SHIFT 6
22 void meson_vpp_setup_mux(struct meson_vpu_priv *priv, unsigned int mux)
24 writel(mux, priv->io_base + _REG(VPU_VIU_VENC_MUX_CTRL));
27 static unsigned int vpp_filter_coefs_4point_bspline[] = {
28 0x15561500, 0x14561600, 0x13561700, 0x12561800,
29 0x11551a00, 0x11541b00, 0x10541c00, 0x0f541d00,
30 0x0f531e00, 0x0e531f00, 0x0d522100, 0x0c522200,
31 0x0b522300, 0x0b512400, 0x0a502600, 0x0a4f2700,
32 0x094e2900, 0x084e2a00, 0x084d2b00, 0x074c2c01,
33 0x074b2d01, 0x064a2f01, 0x06493001, 0x05483201,
34 0x05473301, 0x05463401, 0x04453601, 0x04433702,
35 0x04423802, 0x03413a02, 0x03403b02, 0x033f3c02,
39 static void meson_vpp_write_scaling_filter_coefs(struct meson_vpu_priv *priv,
40 const unsigned int *coefs,
45 writel(is_horizontal ? BIT(8) : 0,
46 priv->io_base + _REG(VPP_OSD_SCALE_COEF_IDX));
47 for (i = 0; i < 33; i++)
49 priv->io_base + _REG(VPP_OSD_SCALE_COEF));
52 static const u32 vpp_filter_coefs_bicubic[] = {
53 0x00800000, 0x007f0100, 0xff7f0200, 0xfe7f0300,
54 0xfd7e0500, 0xfc7e0600, 0xfb7d0800, 0xfb7c0900,
55 0xfa7b0b00, 0xfa7a0dff, 0xf9790fff, 0xf97711ff,
56 0xf87613ff, 0xf87416fe, 0xf87218fe, 0xf8701afe,
57 0xf76f1dfd, 0xf76d1ffd, 0xf76b21fd, 0xf76824fd,
58 0xf76627fc, 0xf76429fc, 0xf7612cfc, 0xf75f2ffb,
59 0xf75d31fb, 0xf75a34fb, 0xf75837fa, 0xf7553afa,
60 0xf8523cfa, 0xf8503ff9, 0xf84d42f9, 0xf84a45f9,
64 static void meson_vpp_write_vd_scaling_filter_coefs(struct meson_vpu_priv *priv,
65 const unsigned int *coefs,
70 writel(is_horizontal ? BIT(8) : 0,
71 priv->io_base + _REG(VPP_SCALE_COEF_IDX));
72 for (i = 0; i < 33; i++)
74 priv->io_base + _REG(VPP_SCALE_COEF));
79 enum viu_matrix_sel_e {
80 VIU_MATRIX_OSD_EOTF = 0,
89 #define COEFF_NORM(a) ((int)((((a) * 2048.0) + 1) / 2))
90 #define MATRIX_5X3_COEF_SIZE 24
92 #define EOTF_COEFF_NORM(a) ((int)((((a) * 4096.0) + 1) / 2))
93 #define EOTF_COEFF_SIZE 10
94 #define EOTF_COEFF_RIGHTSHIFT 1
96 static int RGB709_to_YUV709l_coeff[MATRIX_5X3_COEF_SIZE] = {
97 0, 0, 0, /* pre offset */
98 COEFF_NORM(0.181873), COEFF_NORM(0.611831), COEFF_NORM(0.061765),
99 COEFF_NORM(-0.100251), COEFF_NORM(-0.337249), COEFF_NORM(0.437500),
100 COEFF_NORM(0.437500), COEFF_NORM(-0.397384), COEFF_NORM(-0.040116),
101 0, 0, 0, /* 10'/11'/12' */
102 0, 0, 0, /* 20'/21'/22' */
103 64, 512, 512, /* offset */
104 0, 0, 0 /* mode, right_shift, clip_en */
107 /* eotf matrix: bypass */
108 static int eotf_bypass_coeff[EOTF_COEFF_SIZE] = {
109 EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0),
110 EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0),
111 EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0),
112 EOTF_COEFF_RIGHTSHIFT /* right shift */
115 static void meson_viu_set_osd_matrix(struct meson_vpu_priv *priv,
116 enum viu_matrix_sel_e m_select,
119 if (m_select == VIU_MATRIX_OSD) {
120 /* osd matrix, VIU_MATRIX_0 */
121 writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff),
122 priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET0_1));
124 priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET2));
125 writel(((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff),
126 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF00_01));
127 writel(((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff),
128 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF02_10));
129 writel(((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff),
130 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF11_12));
131 writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff),
132 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF20_21));
135 writel(((m[11] & 0x1fff) << 16) | (m[12] & 0x1fff),
137 _REG(VIU_OSD1_MATRIX_COEF22_30));
138 writel(((m[13] & 0x1fff) << 16) | (m[14] & 0x1fff),
140 _REG(VIU_OSD1_MATRIX_COEF31_32));
141 writel(((m[15] & 0x1fff) << 16) | (m[16] & 0x1fff),
143 _REG(VIU_OSD1_MATRIX_COEF40_41));
144 writel(m[17] & 0x1fff, priv->io_base +
145 _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
147 writel((m[11] & 0x1fff) << 16, priv->io_base +
148 _REG(VIU_OSD1_MATRIX_COEF22_30));
151 writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff),
152 priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET0_1));
153 writel(m[20] & 0xfff,
154 priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET2));
156 writel_bits(3 << 30, m[21] << 30,
158 _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
159 writel_bits(7 << 16, m[22] << 16,
161 _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
163 /* 23 reserved for clipping control */
164 writel_bits(BIT(0), csc_on ? BIT(0) : 0,
165 priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL));
166 writel_bits(BIT(1), 0,
167 priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL));
168 } else if (m_select == VIU_MATRIX_OSD_EOTF) {
171 /* osd eotf matrix, VIU_MATRIX_OSD_EOTF */
172 for (i = 0; i < 5; i++)
173 writel(((m[i * 2] & 0x1fff) << 16) |
174 (m[i * 2 + 1] & 0x1fff), priv->io_base +
175 _REG(VIU_OSD1_EOTF_CTL + i + 1));
177 writel_bits(BIT(30), csc_on ? BIT(30) : 0,
178 priv->io_base + _REG(VIU_OSD1_EOTF_CTL));
179 writel_bits(BIT(31), csc_on ? BIT(31) : 0,
180 priv->io_base + _REG(VIU_OSD1_EOTF_CTL));
184 #define OSD_EOTF_LUT_SIZE 33
185 #define OSD_OETF_LUT_SIZE 41
187 static void meson_viu_set_osd_lut(struct meson_vpu_priv *priv,
188 enum viu_lut_sel_e lut_sel,
189 unsigned int *r_map, unsigned int *g_map,
193 unsigned int addr_port;
194 unsigned int data_port;
195 unsigned int ctrl_port;
198 if (lut_sel == VIU_LUT_OSD_EOTF) {
199 addr_port = VIU_OSD1_EOTF_LUT_ADDR_PORT;
200 data_port = VIU_OSD1_EOTF_LUT_DATA_PORT;
201 ctrl_port = VIU_OSD1_EOTF_CTL;
202 } else if (lut_sel == VIU_LUT_OSD_OETF) {
203 addr_port = VIU_OSD1_OETF_LUT_ADDR_PORT;
204 data_port = VIU_OSD1_OETF_LUT_DATA_PORT;
205 ctrl_port = VIU_OSD1_OETF_CTL;
210 if (lut_sel == VIU_LUT_OSD_OETF) {
211 writel(0, priv->io_base + _REG(addr_port));
213 for (i = 0; i < 20; i++)
214 writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16),
215 priv->io_base + _REG(data_port));
217 writel(r_map[OSD_OETF_LUT_SIZE - 1] | (g_map[0] << 16),
218 priv->io_base + _REG(data_port));
220 for (i = 0; i < 20; i++)
221 writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16),
222 priv->io_base + _REG(data_port));
224 for (i = 0; i < 20; i++)
225 writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16),
226 priv->io_base + _REG(data_port));
228 writel(b_map[OSD_OETF_LUT_SIZE - 1],
229 priv->io_base + _REG(data_port));
232 writel_bits(0x7 << 29, 7 << 29,
233 priv->io_base + _REG(ctrl_port));
235 writel_bits(0x7 << 29, 0,
236 priv->io_base + _REG(ctrl_port));
237 } else if (lut_sel == VIU_LUT_OSD_EOTF) {
238 writel(0, priv->io_base + _REG(addr_port));
240 for (i = 0; i < 20; i++)
241 writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16),
242 priv->io_base + _REG(data_port));
244 writel(r_map[OSD_EOTF_LUT_SIZE - 1] | (g_map[0] << 16),
245 priv->io_base + _REG(data_port));
247 for (i = 0; i < 20; i++)
248 writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16),
249 priv->io_base + _REG(data_port));
251 for (i = 0; i < 20; i++)
252 writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16),
253 priv->io_base + _REG(data_port));
255 writel(b_map[OSD_EOTF_LUT_SIZE - 1],
256 priv->io_base + _REG(data_port));
259 writel_bits(7 << 27, 7 << 27,
260 priv->io_base + _REG(ctrl_port));
262 writel_bits(7 << 27, 0,
263 priv->io_base + _REG(ctrl_port));
265 writel_bits(BIT(31), BIT(31),
266 priv->io_base + _REG(ctrl_port));
270 /* eotf lut: linear */
271 static unsigned int eotf_33_linear_mapping[OSD_EOTF_LUT_SIZE] = {
272 0x0000, 0x0200, 0x0400, 0x0600,
273 0x0800, 0x0a00, 0x0c00, 0x0e00,
274 0x1000, 0x1200, 0x1400, 0x1600,
275 0x1800, 0x1a00, 0x1c00, 0x1e00,
276 0x2000, 0x2200, 0x2400, 0x2600,
277 0x2800, 0x2a00, 0x2c00, 0x2e00,
278 0x3000, 0x3200, 0x3400, 0x3600,
279 0x3800, 0x3a00, 0x3c00, 0x3e00,
283 /* osd oetf lut: linear */
284 static unsigned int oetf_41_linear_mapping[OSD_OETF_LUT_SIZE] = {
294 1023, 1023, 1023, 1023,
298 static void meson_viu_load_matrix(struct meson_vpu_priv *priv)
300 /* eotf lut bypass */
301 meson_viu_set_osd_lut(priv, VIU_LUT_OSD_EOTF,
302 eotf_33_linear_mapping, /* R */
303 eotf_33_linear_mapping, /* G */
304 eotf_33_linear_mapping, /* B */
307 /* eotf matrix bypass */
308 meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD_EOTF,
312 /* oetf lut bypass */
313 meson_viu_set_osd_lut(priv, VIU_LUT_OSD_OETF,
314 oetf_41_linear_mapping, /* R */
315 oetf_41_linear_mapping, /* G */
316 oetf_41_linear_mapping, /* B */
319 /* osd matrix RGB709 to YUV709 limit */
320 meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD,
321 RGB709_to_YUV709l_coeff,
325 void meson_vpu_init(struct udevice *dev)
327 struct meson_vpu_priv *priv = dev_get_priv(dev);
330 /* vpu initialization */
331 writel(0x210000, priv->io_base + _REG(VPU_RDARB_MODE_L1C1));
332 writel(0x10000, priv->io_base + _REG(VPU_RDARB_MODE_L1C2));
333 writel(0x900000, priv->io_base + _REG(VPU_RDARB_MODE_L2C1));
334 writel(0x20000, priv->io_base + _REG(VPU_WRARB_MODE_L2C1));
336 /* Disable CVBS VDAC */
337 hhi_write(HHI_VDAC_CNTL0, 0);
338 hhi_write(HHI_VDAC_CNTL1, 8);
340 /* Power Down Dacs */
341 writel(0xff, priv->io_base + _REG(VENC_VDAC_SETTING));
343 /* Disable HDMI PHY */
344 hhi_write(HHI_HDMI_PHY_CNTL0, 0);
347 writel_bits(0x3, 0, priv->io_base + _REG(VPU_HDMI_SETTING));
349 /* Disable all encoders */
350 writel(0, priv->io_base + _REG(ENCI_VIDEO_EN));
351 writel(0, priv->io_base + _REG(ENCP_VIDEO_EN));
352 writel(0, priv->io_base + _REG(ENCL_VIDEO_EN));
354 /* Disable VSync IRQ */
355 writel(0, priv->io_base + _REG(VENC_INTCTRL));
357 /* set dummy data default YUV black */
358 if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) {
359 writel(0x108080, priv->io_base + _REG(VPP_DUMMY_DATA1));
360 } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM)) {
361 writel_bits(0xff << 16, 0xff << 16,
362 priv->io_base + _REG(VIU_MISC_CTRL1));
363 writel(0x20000, priv->io_base + _REG(VPP_DOLBY_CTRL));
365 priv->io_base + _REG(VPP_DUMMY_DATA1));
368 /* Initialize vpu fifo control registers */
369 writel(readl(priv->io_base + _REG(VPP_OFIFO_SIZE)) |
370 0x77f, priv->io_base + _REG(VPP_OFIFO_SIZE));
371 writel(0x08080808, priv->io_base + _REG(VPP_HOLD_LINES));
373 /* Turn off preblend */
374 writel_bits(VPP_PREBLEND_ENABLE, 0,
375 priv->io_base + _REG(VPP_MISC));
377 /* Turn off POSTBLEND */
378 writel_bits(VPP_POSTBLEND_ENABLE, 0,
379 priv->io_base + _REG(VPP_MISC));
381 /* Force all planes off */
382 writel_bits(VPP_OSD1_POSTBLEND | VPP_OSD2_POSTBLEND |
383 VPP_VD1_POSTBLEND | VPP_VD2_POSTBLEND |
384 VPP_VD1_PREBLEND | VPP_VD2_PREBLEND, 0,
385 priv->io_base + _REG(VPP_MISC));
387 /* Setup default VD settings */
389 priv->io_base + _REG(VPP_PREBLEND_VD1_H_START_END));
391 priv->io_base + _REG(VPP_BLEND_VD2_H_START_END));
393 /* Disable Scalers */
394 writel(0, priv->io_base + _REG(VPP_OSD_SC_CTRL0));
395 writel(0, priv->io_base + _REG(VPP_OSD_VSC_CTRL0));
396 writel(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0));
397 writel(4 | (4 << 8) | BIT(15),
398 priv->io_base + _REG(VPP_SC_MISC));
400 /* Write in the proper filter coefficients. */
401 meson_vpp_write_scaling_filter_coefs(priv,
402 vpp_filter_coefs_4point_bspline, false);
403 meson_vpp_write_scaling_filter_coefs(priv,
404 vpp_filter_coefs_4point_bspline, true);
406 /* Write the VD proper filter coefficients. */
407 meson_vpp_write_vd_scaling_filter_coefs(priv, vpp_filter_coefs_bicubic,
409 meson_vpp_write_vd_scaling_filter_coefs(priv, vpp_filter_coefs_bicubic,
413 writel_bits(BIT(0) | BIT(21), 0,
414 priv->io_base + _REG(VIU_OSD1_CTRL_STAT));
415 writel_bits(BIT(0) | BIT(21), 0,
416 priv->io_base + _REG(VIU_OSD2_CTRL_STAT));
418 /* On GXL/GXM, Use the 10bit HDR conversion matrix */
419 if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
420 meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL))
421 meson_viu_load_matrix(priv);
423 /* Initialize OSD1 fifo control register */
424 reg = BIT(0) | /* Urgent DDR request priority */
425 (4 << 5) | /* hold_fifo_lines */
426 (3 << 10) | /* burst length 64 */
427 (32 << 12) | /* fifo_depth_val: 32*8=256 */
428 (2 << 22) | /* 4 words in 1 burst */
430 writel(reg, priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT));
431 writel(reg, priv->io_base + _REG(VIU_OSD2_FIFO_CTRL_STAT));
433 /* Set OSD alpha replace value */
434 writel_bits(0xff << OSD_REPLACE_SHIFT,
435 0xff << OSD_REPLACE_SHIFT,
436 priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
437 writel_bits(0xff << OSD_REPLACE_SHIFT,
438 0xff << OSD_REPLACE_SHIFT,
439 priv->io_base + _REG(VIU_OSD2_CTRL_STAT2));