xref: /openbmc/linux/drivers/gpu/drm/meson/meson_vpp.c (revision f9a23481)
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