1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Amlogic Meson Video Processing Unit driver 4 * 5 * Copyright (c) 2018 BayLibre, SAS. 6 * Author: Neil Armstrong <narmstrong@baylibre.com> 7 */ 8 9 #define DEBUG 10 11 #include "meson_vpu.h" 12 13 /* HHI Registers */ 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 */ 17 18 /* OSDx_CTRL_STAT2 */ 19 #define OSD_REPLACE_EN BIT(14) 20 #define OSD_REPLACE_SHIFT 6 21 22 void meson_vpp_setup_mux(struct meson_vpu_priv *priv, unsigned int mux) 23 { 24 writel(mux, priv->io_base + _REG(VPU_VIU_VENC_MUX_CTRL)); 25 } 26 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, 36 0x033d3d03 37 }; 38 39 static void meson_vpp_write_scaling_filter_coefs(struct meson_vpu_priv *priv, 40 const unsigned int *coefs, 41 bool is_horizontal) 42 { 43 int i; 44 45 writel(is_horizontal ? BIT(8) : 0, 46 priv->io_base + _REG(VPP_OSD_SCALE_COEF_IDX)); 47 for (i = 0; i < 33; i++) 48 writel(coefs[i], 49 priv->io_base + _REG(VPP_OSD_SCALE_COEF)); 50 } 51 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, 61 0xf84848f8 62 }; 63 64 static void meson_vpp_write_vd_scaling_filter_coefs(struct meson_vpu_priv *priv, 65 const unsigned int *coefs, 66 bool is_horizontal) 67 { 68 int i; 69 70 writel(is_horizontal ? BIT(8) : 0, 71 priv->io_base + _REG(VPP_SCALE_COEF_IDX)); 72 for (i = 0; i < 33; i++) 73 writel(coefs[i], 74 priv->io_base + _REG(VPP_SCALE_COEF)); 75 } 76 77 /* OSD csc defines */ 78 79 enum viu_matrix_sel_e { 80 VIU_MATRIX_OSD_EOTF = 0, 81 VIU_MATRIX_OSD, 82 }; 83 84 enum viu_lut_sel_e { 85 VIU_LUT_OSD_EOTF = 0, 86 VIU_LUT_OSD_OETF, 87 }; 88 89 #define COEFF_NORM(a) ((int)((((a) * 2048.0) + 1) / 2)) 90 #define MATRIX_5X3_COEF_SIZE 24 91 92 #define EOTF_COEFF_NORM(a) ((int)((((a) * 4096.0) + 1) / 2)) 93 #define EOTF_COEFF_SIZE 10 94 #define EOTF_COEFF_RIGHTSHIFT 1 95 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 */ 105 }; 106 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 */ 113 }; 114 115 static void meson_viu_set_osd_matrix(struct meson_vpu_priv *priv, 116 enum viu_matrix_sel_e m_select, 117 int *m, bool csc_on) 118 { 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)); 123 writel(m[2] & 0xfff, 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)); 133 134 if (m[21]) { 135 writel(((m[11] & 0x1fff) << 16) | (m[12] & 0x1fff), 136 priv->io_base + 137 _REG(VIU_OSD1_MATRIX_COEF22_30)); 138 writel(((m[13] & 0x1fff) << 16) | (m[14] & 0x1fff), 139 priv->io_base + 140 _REG(VIU_OSD1_MATRIX_COEF31_32)); 141 writel(((m[15] & 0x1fff) << 16) | (m[16] & 0x1fff), 142 priv->io_base + 143 _REG(VIU_OSD1_MATRIX_COEF40_41)); 144 writel(m[17] & 0x1fff, priv->io_base + 145 _REG(VIU_OSD1_MATRIX_COLMOD_COEF42)); 146 } else { 147 writel((m[11] & 0x1fff) << 16, priv->io_base + 148 _REG(VIU_OSD1_MATRIX_COEF22_30)); 149 } 150 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)); 155 156 writel_bits(3 << 30, m[21] << 30, 157 priv->io_base + 158 _REG(VIU_OSD1_MATRIX_COLMOD_COEF42)); 159 writel_bits(7 << 16, m[22] << 16, 160 priv->io_base + 161 _REG(VIU_OSD1_MATRIX_COLMOD_COEF42)); 162 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) { 169 int i; 170 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)); 176 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)); 181 } 182 } 183 184 #define OSD_EOTF_LUT_SIZE 33 185 #define OSD_OETF_LUT_SIZE 41 186 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, 190 unsigned int *b_map, 191 bool csc_on) 192 { 193 unsigned int addr_port; 194 unsigned int data_port; 195 unsigned int ctrl_port; 196 int i; 197 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; 206 } else { 207 return; 208 } 209 210 if (lut_sel == VIU_LUT_OSD_OETF) { 211 writel(0, priv->io_base + _REG(addr_port)); 212 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)); 216 217 writel(r_map[OSD_OETF_LUT_SIZE - 1] | (g_map[0] << 16), 218 priv->io_base + _REG(data_port)); 219 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)); 223 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)); 227 228 writel(b_map[OSD_OETF_LUT_SIZE - 1], 229 priv->io_base + _REG(data_port)); 230 231 if (csc_on) 232 writel_bits(0x7 << 29, 7 << 29, 233 priv->io_base + _REG(ctrl_port)); 234 else 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)); 239 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)); 243 244 writel(r_map[OSD_EOTF_LUT_SIZE - 1] | (g_map[0] << 16), 245 priv->io_base + _REG(data_port)); 246 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)); 250 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)); 254 255 writel(b_map[OSD_EOTF_LUT_SIZE - 1], 256 priv->io_base + _REG(data_port)); 257 258 if (csc_on) 259 writel_bits(7 << 27, 7 << 27, 260 priv->io_base + _REG(ctrl_port)); 261 else 262 writel_bits(7 << 27, 0, 263 priv->io_base + _REG(ctrl_port)); 264 265 writel_bits(BIT(31), BIT(31), 266 priv->io_base + _REG(ctrl_port)); 267 } 268 } 269 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, 280 0x4000 281 }; 282 283 /* osd oetf lut: linear */ 284 static unsigned int oetf_41_linear_mapping[OSD_OETF_LUT_SIZE] = { 285 0, 0, 0, 0, 286 0, 32, 64, 96, 287 128, 160, 196, 224, 288 256, 288, 320, 352, 289 384, 416, 448, 480, 290 512, 544, 576, 608, 291 640, 672, 704, 736, 292 768, 800, 832, 864, 293 896, 928, 960, 992, 294 1023, 1023, 1023, 1023, 295 1023 296 }; 297 298 static void meson_viu_load_matrix(struct meson_vpu_priv *priv) 299 { 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 */ 305 false); 306 307 /* eotf matrix bypass */ 308 meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD_EOTF, 309 eotf_bypass_coeff, 310 false); 311 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 */ 317 false); 318 319 /* osd matrix RGB709 to YUV709 limit */ 320 meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD, 321 RGB709_to_YUV709l_coeff, 322 true); 323 } 324 325 void meson_vpu_init(struct udevice *dev) 326 { 327 struct meson_vpu_priv *priv = dev_get_priv(dev); 328 u32 reg; 329 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)); 335 336 /* Disable CVBS VDAC */ 337 hhi_write(HHI_VDAC_CNTL0, 0); 338 hhi_write(HHI_VDAC_CNTL1, 8); 339 340 /* Power Down Dacs */ 341 writel(0xff, priv->io_base + _REG(VENC_VDAC_SETTING)); 342 343 /* Disable HDMI PHY */ 344 hhi_write(HHI_HDMI_PHY_CNTL0, 0); 345 346 /* Disable HDMI */ 347 writel_bits(0x3, 0, priv->io_base + _REG(VPU_HDMI_SETTING)); 348 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)); 353 354 /* Disable VSync IRQ */ 355 writel(0, priv->io_base + _REG(VENC_INTCTRL)); 356 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)); 364 writel(0x1020080, 365 priv->io_base + _REG(VPP_DUMMY_DATA1)); 366 } 367 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)); 372 373 /* Turn off preblend */ 374 writel_bits(VPP_PREBLEND_ENABLE, 0, 375 priv->io_base + _REG(VPP_MISC)); 376 377 /* Turn off POSTBLEND */ 378 writel_bits(VPP_POSTBLEND_ENABLE, 0, 379 priv->io_base + _REG(VPP_MISC)); 380 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)); 386 387 /* Setup default VD settings */ 388 writel(4096, 389 priv->io_base + _REG(VPP_PREBLEND_VD1_H_START_END)); 390 writel(4096, 391 priv->io_base + _REG(VPP_BLEND_VD2_H_START_END)); 392 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)); 399 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); 405 406 /* Write the VD proper filter coefficients. */ 407 meson_vpp_write_vd_scaling_filter_coefs(priv, vpp_filter_coefs_bicubic, 408 false); 409 meson_vpp_write_vd_scaling_filter_coefs(priv, vpp_filter_coefs_bicubic, 410 true); 411 412 /* Disable OSDs */ 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)); 417 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); 422 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 */ 429 (2 << 24); 430 writel(reg, priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT)); 431 writel(reg, priv->io_base + _REG(VIU_OSD2_FIFO_CTRL_STAT)); 432 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)); 440 } 441