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_vpp.h" 26 #include "meson_registers.h" 27 28 /** 29 * DOC: Video Post Processing 30 * 31 * VPP Handles all the Post Processing after the Scanout from the VIU 32 * We handle the following post processings : 33 * 34 * - Postblend, Blends the OSD1 only 35 * We exclude OSD2, VS1, VS1 and Preblend output 36 * - Vertical OSD Scaler for OSD1 only, we disable vertical scaler and 37 * use it only for interlace scanout 38 * - Intermediate FIFO with default Amlogic values 39 * 40 * What is missing : 41 * 42 * - Preblend for video overlay pre-scaling 43 * - OSD2 support for cursor framebuffer 44 * - Video pre-scaling before postblend 45 * - Full Vertical/Horizontal OSD scaling to support TV overscan 46 * - HDR conversion 47 */ 48 49 void meson_vpp_setup_mux(struct meson_drm *priv, unsigned int mux) 50 { 51 writel(mux, priv->io_base + _REG(VPU_VIU_VENC_MUX_CTRL)); 52 } 53 54 /* 55 * When the output is interlaced, the OSD must switch between 56 * each field using the INTERLACE_SEL_ODD (0) of VIU_OSD1_BLK0_CFG_W0 57 * at each vsync. 58 * But the vertical scaler can provide such funtionnality if 59 * is configured for 2:1 scaling with interlace options enabled. 60 */ 61 void meson_vpp_setup_interlace_vscaler_osd1(struct meson_drm *priv, 62 struct drm_rect *input) 63 { 64 writel_relaxed(BIT(3) /* Enable scaler */ | 65 BIT(2), /* Select OSD1 */ 66 priv->io_base + _REG(VPP_OSD_SC_CTRL0)); 67 68 writel_relaxed(((drm_rect_width(input) - 1) << 16) | 69 (drm_rect_height(input) - 1), 70 priv->io_base + _REG(VPP_OSD_SCI_WH_M1)); 71 /* 2:1 scaling */ 72 writel_relaxed(((input->x1) << 16) | (input->x2), 73 priv->io_base + _REG(VPP_OSD_SCO_H_START_END)); 74 writel_relaxed(((input->y1 >> 1) << 16) | (input->y2 >> 1), 75 priv->io_base + _REG(VPP_OSD_SCO_V_START_END)); 76 77 /* 2:1 scaling values */ 78 writel_relaxed(BIT(16), priv->io_base + _REG(VPP_OSD_VSC_INI_PHASE)); 79 writel_relaxed(BIT(25), priv->io_base + _REG(VPP_OSD_VSC_PHASE_STEP)); 80 81 writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0)); 82 83 writel_relaxed((4 << 0) /* osd_vsc_bank_length */ | 84 (4 << 3) /* osd_vsc_top_ini_rcv_num0 */ | 85 (1 << 8) /* osd_vsc_top_rpt_p0_num0 */ | 86 (6 << 11) /* osd_vsc_bot_ini_rcv_num0 */ | 87 (2 << 16) /* osd_vsc_bot_rpt_p0_num0 */ | 88 BIT(23) /* osd_prog_interlace */ | 89 BIT(24), /* Enable vertical scaler */ 90 priv->io_base + _REG(VPP_OSD_VSC_CTRL0)); 91 } 92 93 void meson_vpp_disable_interlace_vscaler_osd1(struct meson_drm *priv) 94 { 95 writel_relaxed(0, priv->io_base + _REG(VPP_OSD_SC_CTRL0)); 96 writel_relaxed(0, priv->io_base + _REG(VPP_OSD_VSC_CTRL0)); 97 writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0)); 98 } 99 100 static unsigned int vpp_filter_coefs_4point_bspline[] = { 101 0x15561500, 0x14561600, 0x13561700, 0x12561800, 102 0x11551a00, 0x11541b00, 0x10541c00, 0x0f541d00, 103 0x0f531e00, 0x0e531f00, 0x0d522100, 0x0c522200, 104 0x0b522300, 0x0b512400, 0x0a502600, 0x0a4f2700, 105 0x094e2900, 0x084e2a00, 0x084d2b00, 0x074c2c01, 106 0x074b2d01, 0x064a2f01, 0x06493001, 0x05483201, 107 0x05473301, 0x05463401, 0x04453601, 0x04433702, 108 0x04423802, 0x03413a02, 0x03403b02, 0x033f3c02, 109 0x033d3d03 110 }; 111 112 static void meson_vpp_write_scaling_filter_coefs(struct meson_drm *priv, 113 const unsigned int *coefs, 114 bool is_horizontal) 115 { 116 int i; 117 118 writel_relaxed(is_horizontal ? BIT(8) : 0, 119 priv->io_base + _REG(VPP_OSD_SCALE_COEF_IDX)); 120 for (i = 0; i < 33; i++) 121 writel_relaxed(coefs[i], 122 priv->io_base + _REG(VPP_OSD_SCALE_COEF)); 123 } 124 125 static const uint32_t vpp_filter_coefs_bicubic[] = { 126 0x00800000, 0x007f0100, 0xff7f0200, 0xfe7f0300, 127 0xfd7e0500, 0xfc7e0600, 0xfb7d0800, 0xfb7c0900, 128 0xfa7b0b00, 0xfa7a0dff, 0xf9790fff, 0xf97711ff, 129 0xf87613ff, 0xf87416fe, 0xf87218fe, 0xf8701afe, 130 0xf76f1dfd, 0xf76d1ffd, 0xf76b21fd, 0xf76824fd, 131 0xf76627fc, 0xf76429fc, 0xf7612cfc, 0xf75f2ffb, 132 0xf75d31fb, 0xf75a34fb, 0xf75837fa, 0xf7553afa, 133 0xf8523cfa, 0xf8503ff9, 0xf84d42f9, 0xf84a45f9, 134 0xf84848f8 135 }; 136 137 static void meson_vpp_write_vd_scaling_filter_coefs(struct meson_drm *priv, 138 const unsigned int *coefs, 139 bool is_horizontal) 140 { 141 int i; 142 143 writel_relaxed(is_horizontal ? BIT(8) : 0, 144 priv->io_base + _REG(VPP_SCALE_COEF_IDX)); 145 for (i = 0; i < 33; i++) 146 writel_relaxed(coefs[i], 147 priv->io_base + _REG(VPP_SCALE_COEF)); 148 } 149 150 void meson_vpp_init(struct meson_drm *priv) 151 { 152 /* set dummy data default YUV black */ 153 if (meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) 154 writel_relaxed(0x108080, priv->io_base + _REG(VPP_DUMMY_DATA1)); 155 else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu")) { 156 writel_bits_relaxed(0xff << 16, 0xff << 16, 157 priv->io_base + _REG(VIU_MISC_CTRL1)); 158 writel_relaxed(0x20000, priv->io_base + _REG(VPP_DOLBY_CTRL)); 159 writel_relaxed(0x1020080, 160 priv->io_base + _REG(VPP_DUMMY_DATA1)); 161 } 162 163 /* Initialize vpu fifo control registers */ 164 writel_relaxed(readl_relaxed(priv->io_base + _REG(VPP_OFIFO_SIZE)) | 165 0x77f, priv->io_base + _REG(VPP_OFIFO_SIZE)); 166 writel_relaxed(0x08080808, priv->io_base + _REG(VPP_HOLD_LINES)); 167 168 /* Turn off preblend */ 169 writel_bits_relaxed(VPP_PREBLEND_ENABLE, 0, 170 priv->io_base + _REG(VPP_MISC)); 171 172 /* Turn off POSTBLEND */ 173 writel_bits_relaxed(VPP_POSTBLEND_ENABLE, 0, 174 priv->io_base + _REG(VPP_MISC)); 175 176 /* Force all planes off */ 177 writel_bits_relaxed(VPP_OSD1_POSTBLEND | VPP_OSD2_POSTBLEND | 178 VPP_VD1_POSTBLEND | VPP_VD2_POSTBLEND | 179 VPP_VD1_PREBLEND | VPP_VD2_PREBLEND, 0, 180 priv->io_base + _REG(VPP_MISC)); 181 182 /* Setup default VD settings */ 183 writel_relaxed(4096, 184 priv->io_base + _REG(VPP_PREBLEND_VD1_H_START_END)); 185 writel_relaxed(4096, 186 priv->io_base + _REG(VPP_BLEND_VD2_H_START_END)); 187 188 /* Disable Scalers */ 189 writel_relaxed(0, priv->io_base + _REG(VPP_OSD_SC_CTRL0)); 190 writel_relaxed(0, priv->io_base + _REG(VPP_OSD_VSC_CTRL0)); 191 writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0)); 192 writel_relaxed(4 | (4 << 8) | BIT(15), 193 priv->io_base + _REG(VPP_SC_MISC)); 194 195 writel_relaxed(1, priv->io_base + _REG(VPP_VADJ_CTRL)); 196 197 /* Write in the proper filter coefficients. */ 198 meson_vpp_write_scaling_filter_coefs(priv, 199 vpp_filter_coefs_4point_bspline, false); 200 meson_vpp_write_scaling_filter_coefs(priv, 201 vpp_filter_coefs_4point_bspline, true); 202 203 /* Write the VD proper filter coefficients. */ 204 meson_vpp_write_vd_scaling_filter_coefs(priv, vpp_filter_coefs_bicubic, 205 false); 206 meson_vpp_write_vd_scaling_filter_coefs(priv, vpp_filter_coefs_bicubic, 207 true); 208 } 209