1 /* 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 6 * 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. 11 * 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. 16 * 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/>. 19 */ 20 21 #include <linux/kernel.h> 22 #include <linux/module.h> 23 #include <drm/drmP.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" 29 30 /** 31 * DOC: Video Input Unit 32 * 33 * VIU Handles the Pixel scanout and the basic Colorspace conversions 34 * We handle the following features : 35 * 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 41 * 42 * What is missing : 43 * 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 53 */ 54 55 /* OSD csc defines */ 56 57 enum viu_matrix_sel_e { 58 VIU_MATRIX_OSD_EOTF = 0, 59 VIU_MATRIX_OSD, 60 }; 61 62 enum viu_lut_sel_e { 63 VIU_LUT_OSD_EOTF = 0, 64 VIU_LUT_OSD_OETF, 65 }; 66 67 #define COEFF_NORM(a) ((int)((((a) * 2048.0) + 1) / 2)) 68 #define MATRIX_5X3_COEF_SIZE 24 69 70 #define EOTF_COEFF_NORM(a) ((int)((((a) * 4096.0) + 1) / 2)) 71 #define EOTF_COEFF_SIZE 10 72 #define EOTF_COEFF_RIGHTSHIFT 1 73 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 */ 83 }; 84 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 */ 91 }; 92 93 void meson_viu_set_osd_matrix(struct meson_drm *priv, 94 enum viu_matrix_sel_e m_select, 95 int *m, bool csc_on) 96 { 97 if (m_select == VIU_MATRIX_OSD) { 98 /* osd matrix, VIU_MATRIX_0 */ 99 writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff), 100 priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET0_1)); 101 writel(m[2] & 0xfff, 102 priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET2)); 103 writel(((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff), 104 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF00_01)); 105 writel(((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff), 106 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF02_10)); 107 writel(((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff), 108 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF11_12)); 109 writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff), 110 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF20_21)); 111 112 if (m[21]) { 113 writel(((m[11] & 0x1fff) << 16) | (m[12] & 0x1fff), 114 priv->io_base + 115 _REG(VIU_OSD1_MATRIX_COEF22_30)); 116 writel(((m[13] & 0x1fff) << 16) | (m[14] & 0x1fff), 117 priv->io_base + 118 _REG(VIU_OSD1_MATRIX_COEF31_32)); 119 writel(((m[15] & 0x1fff) << 16) | (m[16] & 0x1fff), 120 priv->io_base + 121 _REG(VIU_OSD1_MATRIX_COEF40_41)); 122 writel(m[17] & 0x1fff, priv->io_base + 123 _REG(VIU_OSD1_MATRIX_COLMOD_COEF42)); 124 } else 125 writel((m[11] & 0x1fff) << 16, priv->io_base + 126 _REG(VIU_OSD1_MATRIX_COEF22_30)); 127 128 writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff), 129 priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET0_1)); 130 writel(m[20] & 0xfff, 131 priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET2)); 132 133 writel_bits_relaxed(3 << 30, m[21] << 30, 134 priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42)); 135 writel_bits_relaxed(7 << 16, m[22] << 16, 136 priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42)); 137 138 /* 23 reserved for clipping control */ 139 writel_bits_relaxed(BIT(0), csc_on ? BIT(0) : 0, 140 priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL)); 141 writel_bits_relaxed(BIT(1), 0, 142 priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL)); 143 } else if (m_select == VIU_MATRIX_OSD_EOTF) { 144 int i; 145 146 /* osd eotf matrix, VIU_MATRIX_OSD_EOTF */ 147 for (i = 0; i < 5; i++) 148 writel(((m[i * 2] & 0x1fff) << 16) | 149 (m[i * 2 + 1] & 0x1fff), priv->io_base + 150 _REG(VIU_OSD1_EOTF_CTL + i + 1)); 151 152 writel_bits_relaxed(BIT(30), csc_on ? BIT(30) : 0, 153 priv->io_base + _REG(VIU_OSD1_EOTF_CTL)); 154 writel_bits_relaxed(BIT(31), csc_on ? BIT(31) : 0, 155 priv->io_base + _REG(VIU_OSD1_EOTF_CTL)); 156 } 157 } 158 159 #define OSD_EOTF_LUT_SIZE 33 160 #define OSD_OETF_LUT_SIZE 41 161 162 void meson_viu_set_osd_lut(struct meson_drm *priv, enum viu_lut_sel_e lut_sel, 163 unsigned int *r_map, unsigned int *g_map, 164 unsigned int *b_map, 165 bool csc_on) 166 { 167 unsigned int addr_port; 168 unsigned int data_port; 169 unsigned int ctrl_port; 170 int i; 171 172 if (lut_sel == VIU_LUT_OSD_EOTF) { 173 addr_port = VIU_OSD1_EOTF_LUT_ADDR_PORT; 174 data_port = VIU_OSD1_EOTF_LUT_DATA_PORT; 175 ctrl_port = VIU_OSD1_EOTF_CTL; 176 } else if (lut_sel == VIU_LUT_OSD_OETF) { 177 addr_port = VIU_OSD1_OETF_LUT_ADDR_PORT; 178 data_port = VIU_OSD1_OETF_LUT_DATA_PORT; 179 ctrl_port = VIU_OSD1_OETF_CTL; 180 } else 181 return; 182 183 if (lut_sel == VIU_LUT_OSD_OETF) { 184 writel(0, priv->io_base + _REG(addr_port)); 185 186 for (i = 0; i < (OSD_OETF_LUT_SIZE / 2); i++) 187 writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16), 188 priv->io_base + _REG(data_port)); 189 190 writel(r_map[OSD_OETF_LUT_SIZE - 1] | (g_map[0] << 16), 191 priv->io_base + _REG(data_port)); 192 193 for (i = 0; i < (OSD_OETF_LUT_SIZE / 2); i++) 194 writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16), 195 priv->io_base + _REG(data_port)); 196 197 for (i = 0; i < (OSD_OETF_LUT_SIZE / 2); i++) 198 writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16), 199 priv->io_base + _REG(data_port)); 200 201 writel(b_map[OSD_OETF_LUT_SIZE - 1], 202 priv->io_base + _REG(data_port)); 203 204 if (csc_on) 205 writel_bits_relaxed(0x7 << 29, 7 << 29, 206 priv->io_base + _REG(ctrl_port)); 207 else 208 writel_bits_relaxed(0x7 << 29, 0, 209 priv->io_base + _REG(ctrl_port)); 210 } else if (lut_sel == VIU_LUT_OSD_EOTF) { 211 writel(0, priv->io_base + _REG(addr_port)); 212 213 for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); 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_EOTF_LUT_SIZE - 1] | (g_map[0] << 16), 218 priv->io_base + _REG(data_port)); 219 220 for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); 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 < (OSD_EOTF_LUT_SIZE / 2); 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_EOTF_LUT_SIZE - 1], 229 priv->io_base + _REG(data_port)); 230 231 if (csc_on) 232 writel_bits_relaxed(7 << 27, 7 << 27, 233 priv->io_base + _REG(ctrl_port)); 234 else 235 writel_bits_relaxed(7 << 27, 0, 236 priv->io_base + _REG(ctrl_port)); 237 238 writel_bits_relaxed(BIT(31), BIT(31), 239 priv->io_base + _REG(ctrl_port)); 240 } 241 } 242 243 /* eotf lut: linear */ 244 static unsigned int eotf_33_linear_mapping[OSD_EOTF_LUT_SIZE] = { 245 0x0000, 0x0200, 0x0400, 0x0600, 246 0x0800, 0x0a00, 0x0c00, 0x0e00, 247 0x1000, 0x1200, 0x1400, 0x1600, 248 0x1800, 0x1a00, 0x1c00, 0x1e00, 249 0x2000, 0x2200, 0x2400, 0x2600, 250 0x2800, 0x2a00, 0x2c00, 0x2e00, 251 0x3000, 0x3200, 0x3400, 0x3600, 252 0x3800, 0x3a00, 0x3c00, 0x3e00, 253 0x4000 254 }; 255 256 /* osd oetf lut: linear */ 257 static unsigned int oetf_41_linear_mapping[OSD_OETF_LUT_SIZE] = { 258 0, 0, 0, 0, 259 0, 32, 64, 96, 260 128, 160, 196, 224, 261 256, 288, 320, 352, 262 384, 416, 448, 480, 263 512, 544, 576, 608, 264 640, 672, 704, 736, 265 768, 800, 832, 864, 266 896, 928, 960, 992, 267 1023, 1023, 1023, 1023, 268 1023 269 }; 270 271 static void meson_viu_load_matrix(struct meson_drm *priv) 272 { 273 /* eotf lut bypass */ 274 meson_viu_set_osd_lut(priv, VIU_LUT_OSD_EOTF, 275 eotf_33_linear_mapping, /* R */ 276 eotf_33_linear_mapping, /* G */ 277 eotf_33_linear_mapping, /* B */ 278 false); 279 280 /* eotf matrix bypass */ 281 meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD_EOTF, 282 eotf_bypass_coeff, 283 false); 284 285 /* oetf lut bypass */ 286 meson_viu_set_osd_lut(priv, VIU_LUT_OSD_OETF, 287 oetf_41_linear_mapping, /* R */ 288 oetf_41_linear_mapping, /* G */ 289 oetf_41_linear_mapping, /* B */ 290 false); 291 292 /* osd matrix RGB709 to YUV709 limit */ 293 meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD, 294 RGB709_to_YUV709l_coeff, 295 true); 296 } 297 298 /* VIU OSD1 Reset as workaround for GXL+ Alpha OSD Bug */ 299 void meson_viu_osd1_reset(struct meson_drm *priv) 300 { 301 uint32_t osd1_fifo_ctrl_stat, osd1_ctrl_stat2; 302 303 /* Save these 2 registers state */ 304 osd1_fifo_ctrl_stat = readl_relaxed( 305 priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT)); 306 osd1_ctrl_stat2 = readl_relaxed( 307 priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); 308 309 /* Reset OSD1 */ 310 writel_bits_relaxed(BIT(0), BIT(0), 311 priv->io_base + _REG(VIU_SW_RESET)); 312 writel_bits_relaxed(BIT(0), 0, 313 priv->io_base + _REG(VIU_SW_RESET)); 314 315 /* Rewrite these registers state lost in the reset */ 316 writel_relaxed(osd1_fifo_ctrl_stat, 317 priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT)); 318 writel_relaxed(osd1_ctrl_stat2, 319 priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); 320 321 /* Reload the conversion matrix */ 322 meson_viu_load_matrix(priv); 323 } 324 325 void meson_viu_init(struct meson_drm *priv) 326 { 327 uint32_t reg; 328 329 /* Disable OSDs */ 330 writel_bits_relaxed(BIT(0) | BIT(21), 0, 331 priv->io_base + _REG(VIU_OSD1_CTRL_STAT)); 332 writel_bits_relaxed(BIT(0) | BIT(21), 0, 333 priv->io_base + _REG(VIU_OSD2_CTRL_STAT)); 334 335 /* On GXL/GXM, Use the 10bit HDR conversion matrix */ 336 if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || 337 meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) 338 meson_viu_load_matrix(priv); 339 340 /* Initialize OSD1 fifo control register */ 341 reg = BIT(0) | /* Urgent DDR request priority */ 342 (4 << 5) | /* hold_fifo_lines */ 343 (3 << 10) | /* burst length 64 */ 344 (32 << 12) | /* fifo_depth_val: 32*8=256 */ 345 (2 << 22) | /* 4 words in 1 burst */ 346 (2 << 24); 347 writel_relaxed(reg, priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT)); 348 writel_relaxed(reg, priv->io_base + _REG(VIU_OSD2_FIFO_CTRL_STAT)); 349 350 /* Set OSD alpha replace value */ 351 writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT, 352 0xff << OSD_REPLACE_SHIFT, 353 priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); 354 writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT, 355 0xff << OSD_REPLACE_SHIFT, 356 priv->io_base + _REG(VIU_OSD2_CTRL_STAT2)); 357 358 /* Disable VD1 AFBC */ 359 /* di_mif0_en=0 mif0_to_vpp_en=0 di_mad_en=0 */ 360 writel_bits_relaxed(0x7 << 16, 0, 361 priv->io_base + _REG(VIU_MISC_CTRL0)); 362 /* afbc vd1 set=0 */ 363 writel_bits_relaxed(BIT(20), 0, 364 priv->io_base + _REG(VIU_MISC_CTRL0)); 365 writel_relaxed(0, priv->io_base + _REG(AFBC_ENABLE)); 366 367 writel_relaxed(0x00FF00C0, 368 priv->io_base + _REG(VD1_IF0_LUMA_FIFO_SIZE)); 369 writel_relaxed(0x00FF00C0, 370 priv->io_base + _REG(VD2_IF0_LUMA_FIFO_SIZE)); 371 372 373 priv->viu.osd1_enabled = false; 374 priv->viu.osd1_commit = false; 375 priv->viu.osd1_interlace = false; 376 } 377