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