GNU Linux-libre 4.14.254-gnu1
[releases.git] / drivers / gpu / drm / meson / meson_vpp.c
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 void meson_vpp_init(struct meson_drm *priv)
126 {
127         /* set dummy data default YUV black */
128         if (meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
129                 writel_relaxed(0x108080, priv->io_base + _REG(VPP_DUMMY_DATA1));
130         else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu")) {
131                 writel_bits_relaxed(0xff << 16, 0xff << 16,
132                                     priv->io_base + _REG(VIU_MISC_CTRL1));
133                 writel_relaxed(0x20000, priv->io_base + _REG(VPP_DOLBY_CTRL));
134                 writel_relaxed(0x1020080,
135                                 priv->io_base + _REG(VPP_DUMMY_DATA1));
136         }
137
138         /* Initialize vpu fifo control registers */
139         writel_relaxed(readl_relaxed(priv->io_base + _REG(VPP_OFIFO_SIZE)) |
140                         0x77f, priv->io_base + _REG(VPP_OFIFO_SIZE));
141         writel_relaxed(0x08080808, priv->io_base + _REG(VPP_HOLD_LINES));
142
143         /* Turn off preblend */
144         writel_bits_relaxed(VPP_PREBLEND_ENABLE, 0,
145                             priv->io_base + _REG(VPP_MISC));
146
147         /* Turn off POSTBLEND */
148         writel_bits_relaxed(VPP_POSTBLEND_ENABLE, 0,
149                             priv->io_base + _REG(VPP_MISC));
150
151         /* Force all planes off */
152         writel_bits_relaxed(VPP_OSD1_POSTBLEND | VPP_OSD2_POSTBLEND |
153                             VPP_VD1_POSTBLEND | VPP_VD2_POSTBLEND, 0,
154                             priv->io_base + _REG(VPP_MISC));
155
156         /* Disable Scalers */
157         writel_relaxed(0, priv->io_base + _REG(VPP_OSD_SC_CTRL0));
158         writel_relaxed(0, priv->io_base + _REG(VPP_OSD_VSC_CTRL0));
159         writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0));
160
161         /* Write in the proper filter coefficients. */
162         meson_vpp_write_scaling_filter_coefs(priv,
163                                 vpp_filter_coefs_4point_bspline, false);
164         meson_vpp_write_scaling_filter_coefs(priv,
165                                 vpp_filter_coefs_4point_bspline, true);
166 }