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_canvas.h" 29 #include "meson_registers.h" 30 31 /* 32 * VIU Handles the Pixel scanout and the basic Colorspace conversions 33 * We handle the following features : 34 * - OSD1 RGB565/RGB888/xRGB8888 scanout 35 * - RGB conversion to x/cb/cr 36 * - Progressive or Interlace buffer scanout 37 * - OSD1 Commit on Vsync 38 * - HDR OSD matrix for GXL/GXM 39 * 40 * What is missing : 41 * - BGR888/xBGR8888/BGRx8888/BGRx8888 modes 42 * - YUV4:2:2 Y0CbY1Cr scanout 43 * - Conversion to YUV 4:4:4 from 4:2:2 input 44 * - Colorkey Alpha matching 45 * - Big endian scanout 46 * - X/Y reverse scanout 47 * - Global alpha setup 48 * - OSD2 support, would need interlace switching on vsync 49 * - OSD1 full scaling to support TV overscan 50 */ 51 52 /* OSD csc defines */ 53 54 enum viu_matrix_sel_e { 55 VIU_MATRIX_OSD_EOTF = 0, 56 VIU_MATRIX_OSD, 57 }; 58 59 enum viu_lut_sel_e { 60 VIU_LUT_OSD_EOTF = 0, 61 VIU_LUT_OSD_OETF, 62 }; 63 64 #define COEFF_NORM(a) ((int)((((a) * 2048.0) + 1) / 2)) 65 #define MATRIX_5X3_COEF_SIZE 24 66 67 #define EOTF_COEFF_NORM(a) ((int)((((a) * 4096.0) + 1) / 2)) 68 #define EOTF_COEFF_SIZE 10 69 #define EOTF_COEFF_RIGHTSHIFT 1 70 71 static int RGB709_to_YUV709l_coeff[MATRIX_5X3_COEF_SIZE] = { 72 0, 0, 0, /* pre offset */ 73 COEFF_NORM(0.181873), COEFF_NORM(0.611831), COEFF_NORM(0.061765), 74 COEFF_NORM(-0.100251), COEFF_NORM(-0.337249), COEFF_NORM(0.437500), 75 COEFF_NORM(0.437500), COEFF_NORM(-0.397384), COEFF_NORM(-0.040116), 76 0, 0, 0, /* 10'/11'/12' */ 77 0, 0, 0, /* 20'/21'/22' */ 78 64, 512, 512, /* offset */ 79 0, 0, 0 /* mode, right_shift, clip_en */ 80 }; 81 82 /* eotf matrix: bypass */ 83 static int eotf_bypass_coeff[EOTF_COEFF_SIZE] = { 84 EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0), 85 EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0), 86 EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0), 87 EOTF_COEFF_RIGHTSHIFT /* right shift */ 88 }; 89 90 void meson_viu_set_osd_matrix(struct meson_drm *priv, 91 enum viu_matrix_sel_e m_select, 92 int *m, bool csc_on) 93 { 94 if (m_select == VIU_MATRIX_OSD) { 95 /* osd matrix, VIU_MATRIX_0 */ 96 writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff), 97 priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET0_1)); 98 writel(m[2] & 0xfff, 99 priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET2)); 100 writel(((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff), 101 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF00_01)); 102 writel(((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff), 103 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF02_10)); 104 writel(((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff), 105 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF11_12)); 106 writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff), 107 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF20_21)); 108 109 if (m[21]) { 110 writel(((m[11] & 0x1fff) << 16) | (m[12] & 0x1fff), 111 priv->io_base + 112 _REG(VIU_OSD1_MATRIX_COEF22_30)); 113 writel(((m[13] & 0x1fff) << 16) | (m[14] & 0x1fff), 114 priv->io_base + 115 _REG(VIU_OSD1_MATRIX_COEF31_32)); 116 writel(((m[15] & 0x1fff) << 16) | (m[16] & 0x1fff), 117 priv->io_base + 118 _REG(VIU_OSD1_MATRIX_COEF40_41)); 119 writel(m[17] & 0x1fff, priv->io_base + 120 _REG(VIU_OSD1_MATRIX_COLMOD_COEF42)); 121 } else 122 writel((m[11] & 0x1fff) << 16, priv->io_base + 123 _REG(VIU_OSD1_MATRIX_COEF22_30)); 124 125 writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff), 126 priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET0_1)); 127 writel(m[20] & 0xfff, 128 priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET2)); 129 130 writel_bits_relaxed(3 << 30, m[21] << 30, 131 priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42)); 132 writel_bits_relaxed(7 << 16, m[22] << 16, 133 priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42)); 134 135 /* 23 reserved for clipping control */ 136 writel_bits_relaxed(BIT(0), csc_on ? BIT(0) : 0, 137 priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL)); 138 writel_bits_relaxed(BIT(1), 0, 139 priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL)); 140 } else if (m_select == VIU_MATRIX_OSD_EOTF) { 141 int i; 142 143 /* osd eotf matrix, VIU_MATRIX_OSD_EOTF */ 144 for (i = 0; i < 5; i++) 145 writel(((m[i * 2] & 0x1fff) << 16) | 146 (m[i * 2 + 1] & 0x1fff), priv->io_base + 147 _REG(VIU_OSD1_EOTF_CTL + i + 1)); 148 149 writel_bits_relaxed(BIT(30), csc_on ? BIT(30) : 0, 150 priv->io_base + _REG(VIU_OSD1_EOTF_CTL)); 151 writel_bits_relaxed(BIT(31), csc_on ? BIT(31) : 0, 152 priv->io_base + _REG(VIU_OSD1_EOTF_CTL)); 153 } 154 } 155 156 #define OSD_EOTF_LUT_SIZE 33 157 #define OSD_OETF_LUT_SIZE 41 158 159 void meson_viu_set_osd_lut(struct meson_drm *priv, enum viu_lut_sel_e lut_sel, 160 unsigned int *r_map, unsigned int *g_map, 161 unsigned int *b_map, 162 bool csc_on) 163 { 164 unsigned int addr_port; 165 unsigned int data_port; 166 unsigned int ctrl_port; 167 int i; 168 169 if (lut_sel == VIU_LUT_OSD_EOTF) { 170 addr_port = VIU_OSD1_EOTF_LUT_ADDR_PORT; 171 data_port = VIU_OSD1_EOTF_LUT_DATA_PORT; 172 ctrl_port = VIU_OSD1_EOTF_CTL; 173 } else if (lut_sel == VIU_LUT_OSD_OETF) { 174 addr_port = VIU_OSD1_OETF_LUT_ADDR_PORT; 175 data_port = VIU_OSD1_OETF_LUT_DATA_PORT; 176 ctrl_port = VIU_OSD1_OETF_CTL; 177 } else 178 return; 179 180 if (lut_sel == VIU_LUT_OSD_OETF) { 181 writel(0, priv->io_base + _REG(addr_port)); 182 183 for (i = 0; i < 20; i++) 184 writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16), 185 priv->io_base + _REG(data_port)); 186 187 writel(r_map[OSD_OETF_LUT_SIZE - 1] | (g_map[0] << 16), 188 priv->io_base + _REG(data_port)); 189 190 for (i = 0; i < 20; i++) 191 writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16), 192 priv->io_base + _REG(data_port)); 193 194 for (i = 0; i < 20; i++) 195 writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16), 196 priv->io_base + _REG(data_port)); 197 198 writel(b_map[OSD_OETF_LUT_SIZE - 1], 199 priv->io_base + _REG(data_port)); 200 201 if (csc_on) 202 writel_bits_relaxed(0x7 << 29, 7 << 29, 203 priv->io_base + _REG(ctrl_port)); 204 else 205 writel_bits_relaxed(0x7 << 29, 0, 206 priv->io_base + _REG(ctrl_port)); 207 } else if (lut_sel == VIU_LUT_OSD_EOTF) { 208 writel(0, priv->io_base + _REG(addr_port)); 209 210 for (i = 0; i < 20; i++) 211 writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16), 212 priv->io_base + _REG(data_port)); 213 214 writel(r_map[OSD_EOTF_LUT_SIZE - 1] | (g_map[0] << 16), 215 priv->io_base + _REG(data_port)); 216 217 for (i = 0; i < 20; i++) 218 writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16), 219 priv->io_base + _REG(data_port)); 220 221 for (i = 0; i < 20; i++) 222 writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16), 223 priv->io_base + _REG(data_port)); 224 225 writel(b_map[OSD_EOTF_LUT_SIZE - 1], 226 priv->io_base + _REG(data_port)); 227 228 if (csc_on) 229 writel_bits_relaxed(7 << 27, 7 << 27, 230 priv->io_base + _REG(ctrl_port)); 231 else 232 writel_bits_relaxed(7 << 27, 0, 233 priv->io_base + _REG(ctrl_port)); 234 235 writel_bits_relaxed(BIT(31), BIT(31), 236 priv->io_base + _REG(ctrl_port)); 237 } 238 } 239 240 /* eotf lut: linear */ 241 static unsigned int eotf_33_linear_mapping[OSD_EOTF_LUT_SIZE] = { 242 0x0000, 0x0200, 0x0400, 0x0600, 243 0x0800, 0x0a00, 0x0c00, 0x0e00, 244 0x1000, 0x1200, 0x1400, 0x1600, 245 0x1800, 0x1a00, 0x1c00, 0x1e00, 246 0x2000, 0x2200, 0x2400, 0x2600, 247 0x2800, 0x2a00, 0x2c00, 0x2e00, 248 0x3000, 0x3200, 0x3400, 0x3600, 249 0x3800, 0x3a00, 0x3c00, 0x3e00, 250 0x4000 251 }; 252 253 /* osd oetf lut: linear */ 254 static unsigned int oetf_41_linear_mapping[OSD_OETF_LUT_SIZE] = { 255 0, 0, 0, 0, 256 0, 32, 64, 96, 257 128, 160, 196, 224, 258 256, 288, 320, 352, 259 384, 416, 448, 480, 260 512, 544, 576, 608, 261 640, 672, 704, 736, 262 768, 800, 832, 864, 263 896, 928, 960, 992, 264 1023, 1023, 1023, 1023, 265 1023 266 }; 267 268 static void meson_viu_load_matrix(struct meson_drm *priv) 269 { 270 /* eotf lut bypass */ 271 meson_viu_set_osd_lut(priv, VIU_LUT_OSD_EOTF, 272 eotf_33_linear_mapping, /* R */ 273 eotf_33_linear_mapping, /* G */ 274 eotf_33_linear_mapping, /* B */ 275 false); 276 277 /* eotf matrix bypass */ 278 meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD_EOTF, 279 eotf_bypass_coeff, 280 false); 281 282 /* oetf lut bypass */ 283 meson_viu_set_osd_lut(priv, VIU_LUT_OSD_OETF, 284 oetf_41_linear_mapping, /* R */ 285 oetf_41_linear_mapping, /* G */ 286 oetf_41_linear_mapping, /* B */ 287 false); 288 289 /* osd matrix RGB709 to YUV709 limit */ 290 meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD, 291 RGB709_to_YUV709l_coeff, 292 true); 293 } 294 295 void meson_viu_init(struct meson_drm *priv) 296 { 297 uint32_t reg; 298 299 /* Disable OSDs */ 300 writel_bits_relaxed(BIT(0) | BIT(21), 0, 301 priv->io_base + _REG(VIU_OSD1_CTRL_STAT)); 302 writel_bits_relaxed(BIT(0) | BIT(21), 0, 303 priv->io_base + _REG(VIU_OSD2_CTRL_STAT)); 304 305 /* On GXL/GXM, Use the 10bit HDR conversion matrix */ 306 if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || 307 meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) 308 meson_viu_load_matrix(priv); 309 310 /* Initialize OSD1 fifo control register */ 311 reg = BIT(0) | /* Urgent DDR request priority */ 312 (4 << 5) | /* hold_fifo_lines */ 313 (3 << 10) | /* burst length 64 */ 314 (32 << 12) | /* fifo_depth_val: 32*8=256 */ 315 (2 << 22) | /* 4 words in 1 burst */ 316 (2 << 24); 317 writel_relaxed(reg, priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT)); 318 writel_relaxed(reg, priv->io_base + _REG(VIU_OSD2_FIFO_CTRL_STAT)); 319 320 /* Set OSD alpha replace value */ 321 writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT, 322 0xff << OSD_REPLACE_SHIFT, 323 priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); 324 writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT, 325 0xff << OSD_REPLACE_SHIFT, 326 priv->io_base + _REG(VIU_OSD2_CTRL_STAT2)); 327 328 priv->viu.osd1_enabled = false; 329 priv->viu.osd1_commit = false; 330 priv->viu.osd1_interlace = false; 331 } 332