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