Loading...
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) 2016 BayLibre, SAS
4 * Author: Neil Armstrong <narmstrong@baylibre.com>
5 * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
6 * Copyright (C) 2014 Endless Mobile
7 */
8
9#include <linux/export.h>
10
11#include "meson_drv.h"
12#include "meson_viu.h"
13#include "meson_registers.h"
14
15/**
16 * DOC: Video Input Unit
17 *
18 * VIU Handles the Pixel scanout and the basic Colorspace conversions
19 * We handle the following features :
20 *
21 * - OSD1 RGB565/RGB888/xRGB8888 scanout
22 * - RGB conversion to x/cb/cr
23 * - Progressive or Interlace buffer scanout
24 * - OSD1 Commit on Vsync
25 * - HDR OSD matrix for GXL/GXM
26 *
27 * What is missing :
28 *
29 * - BGR888/xBGR8888/BGRx8888/BGRx8888 modes
30 * - YUV4:2:2 Y0CbY1Cr scanout
31 * - Conversion to YUV 4:4:4 from 4:2:2 input
32 * - Colorkey Alpha matching
33 * - Big endian scanout
34 * - X/Y reverse scanout
35 * - Global alpha setup
36 * - OSD2 support, would need interlace switching on vsync
37 * - OSD1 full scaling to support TV overscan
38 */
39
40/* OSD csc defines */
41
42enum viu_matrix_sel_e {
43 VIU_MATRIX_OSD_EOTF = 0,
44 VIU_MATRIX_OSD,
45};
46
47enum viu_lut_sel_e {
48 VIU_LUT_OSD_EOTF = 0,
49 VIU_LUT_OSD_OETF,
50};
51
52#define COEFF_NORM(a) ((int)((((a) * 2048.0) + 1) / 2))
53#define MATRIX_5X3_COEF_SIZE 24
54
55#define EOTF_COEFF_NORM(a) ((int)((((a) * 4096.0) + 1) / 2))
56#define EOTF_COEFF_SIZE 10
57#define EOTF_COEFF_RIGHTSHIFT 1
58
59static int RGB709_to_YUV709l_coeff[MATRIX_5X3_COEF_SIZE] = {
60 0, 0, 0, /* pre offset */
61 COEFF_NORM(0.181873), COEFF_NORM(0.611831), COEFF_NORM(0.061765),
62 COEFF_NORM(-0.100251), COEFF_NORM(-0.337249), COEFF_NORM(0.437500),
63 COEFF_NORM(0.437500), COEFF_NORM(-0.397384), COEFF_NORM(-0.040116),
64 0, 0, 0, /* 10'/11'/12' */
65 0, 0, 0, /* 20'/21'/22' */
66 64, 512, 512, /* offset */
67 0, 0, 0 /* mode, right_shift, clip_en */
68};
69
70/* eotf matrix: bypass */
71static int eotf_bypass_coeff[EOTF_COEFF_SIZE] = {
72 EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0),
73 EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0),
74 EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0),
75 EOTF_COEFF_RIGHTSHIFT /* right shift */
76};
77
78static void meson_viu_set_g12a_osd1_matrix(struct meson_drm *priv,
79 int *m, bool csc_on)
80{
81 /* VPP WRAP OSD1 matrix */
82 writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff),
83 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_PRE_OFFSET0_1));
84 writel(m[2] & 0xfff,
85 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_PRE_OFFSET2));
86 writel(((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff),
87 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF00_01));
88 writel(((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff),
89 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF02_10));
90 writel(((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff),
91 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF11_12));
92 writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff),
93 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF20_21));
94 writel((m[11] & 0x1fff) << 16,
95 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF22));
96
97 writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff),
98 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_OFFSET0_1));
99 writel(m[20] & 0xfff,
100 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_OFFSET2));
101
102 writel_bits_relaxed(BIT(0), csc_on ? BIT(0) : 0,
103 priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_EN_CTRL));
104}
105
106static void meson_viu_set_osd_matrix(struct meson_drm *priv,
107 enum viu_matrix_sel_e m_select,
108 int *m, bool csc_on)
109{
110 if (m_select == VIU_MATRIX_OSD) {
111 /* osd matrix, VIU_MATRIX_0 */
112 writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff),
113 priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET0_1));
114 writel(m[2] & 0xfff,
115 priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET2));
116 writel(((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff),
117 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF00_01));
118 writel(((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff),
119 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF02_10));
120 writel(((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff),
121 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF11_12));
122 writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff),
123 priv->io_base + _REG(VIU_OSD1_MATRIX_COEF20_21));
124
125 if (m[21]) {
126 writel(((m[11] & 0x1fff) << 16) | (m[12] & 0x1fff),
127 priv->io_base +
128 _REG(VIU_OSD1_MATRIX_COEF22_30));
129 writel(((m[13] & 0x1fff) << 16) | (m[14] & 0x1fff),
130 priv->io_base +
131 _REG(VIU_OSD1_MATRIX_COEF31_32));
132 writel(((m[15] & 0x1fff) << 16) | (m[16] & 0x1fff),
133 priv->io_base +
134 _REG(VIU_OSD1_MATRIX_COEF40_41));
135 writel(m[17] & 0x1fff, priv->io_base +
136 _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
137 } else
138 writel((m[11] & 0x1fff) << 16, priv->io_base +
139 _REG(VIU_OSD1_MATRIX_COEF22_30));
140
141 writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff),
142 priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET0_1));
143 writel(m[20] & 0xfff,
144 priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET2));
145
146 writel_bits_relaxed(3 << 30, m[21] << 30,
147 priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
148 writel_bits_relaxed(7 << 16, m[22] << 16,
149 priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
150
151 /* 23 reserved for clipping control */
152 writel_bits_relaxed(BIT(0), csc_on ? BIT(0) : 0,
153 priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL));
154 writel_bits_relaxed(BIT(1), 0,
155 priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL));
156 } else if (m_select == VIU_MATRIX_OSD_EOTF) {
157 int i;
158
159 /* osd eotf matrix, VIU_MATRIX_OSD_EOTF */
160 for (i = 0; i < 5; i++)
161 writel(((m[i * 2] & 0x1fff) << 16) |
162 (m[i * 2 + 1] & 0x1fff), priv->io_base +
163 _REG(VIU_OSD1_EOTF_CTL + i + 1));
164
165 writel_bits_relaxed(BIT(30), csc_on ? BIT(30) : 0,
166 priv->io_base + _REG(VIU_OSD1_EOTF_CTL));
167 writel_bits_relaxed(BIT(31), csc_on ? BIT(31) : 0,
168 priv->io_base + _REG(VIU_OSD1_EOTF_CTL));
169 }
170}
171
172#define OSD_EOTF_LUT_SIZE 33
173#define OSD_OETF_LUT_SIZE 41
174
175static void
176meson_viu_set_osd_lut(struct meson_drm *priv, enum viu_lut_sel_e lut_sel,
177 unsigned int *r_map, unsigned int *g_map,
178 unsigned int *b_map, bool csc_on)
179{
180 unsigned int addr_port;
181 unsigned int data_port;
182 unsigned int ctrl_port;
183 int i;
184
185 if (lut_sel == VIU_LUT_OSD_EOTF) {
186 addr_port = VIU_OSD1_EOTF_LUT_ADDR_PORT;
187 data_port = VIU_OSD1_EOTF_LUT_DATA_PORT;
188 ctrl_port = VIU_OSD1_EOTF_CTL;
189 } else if (lut_sel == VIU_LUT_OSD_OETF) {
190 addr_port = VIU_OSD1_OETF_LUT_ADDR_PORT;
191 data_port = VIU_OSD1_OETF_LUT_DATA_PORT;
192 ctrl_port = VIU_OSD1_OETF_CTL;
193 } else
194 return;
195
196 if (lut_sel == VIU_LUT_OSD_OETF) {
197 writel(0, priv->io_base + _REG(addr_port));
198
199 for (i = 0; i < (OSD_OETF_LUT_SIZE / 2); i++)
200 writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16),
201 priv->io_base + _REG(data_port));
202
203 writel(r_map[OSD_OETF_LUT_SIZE - 1] | (g_map[0] << 16),
204 priv->io_base + _REG(data_port));
205
206 for (i = 0; i < (OSD_OETF_LUT_SIZE / 2); i++)
207 writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16),
208 priv->io_base + _REG(data_port));
209
210 for (i = 0; i < (OSD_OETF_LUT_SIZE / 2); i++)
211 writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16),
212 priv->io_base + _REG(data_port));
213
214 writel(b_map[OSD_OETF_LUT_SIZE - 1],
215 priv->io_base + _REG(data_port));
216
217 if (csc_on)
218 writel_bits_relaxed(0x7 << 29, 7 << 29,
219 priv->io_base + _REG(ctrl_port));
220 else
221 writel_bits_relaxed(0x7 << 29, 0,
222 priv->io_base + _REG(ctrl_port));
223 } else if (lut_sel == VIU_LUT_OSD_EOTF) {
224 writel(0, priv->io_base + _REG(addr_port));
225
226 for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); i++)
227 writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16),
228 priv->io_base + _REG(data_port));
229
230 writel(r_map[OSD_EOTF_LUT_SIZE - 1] | (g_map[0] << 16),
231 priv->io_base + _REG(data_port));
232
233 for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); i++)
234 writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16),
235 priv->io_base + _REG(data_port));
236
237 for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); i++)
238 writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16),
239 priv->io_base + _REG(data_port));
240
241 writel(b_map[OSD_EOTF_LUT_SIZE - 1],
242 priv->io_base + _REG(data_port));
243
244 if (csc_on)
245 writel_bits_relaxed(7 << 27, 7 << 27,
246 priv->io_base + _REG(ctrl_port));
247 else
248 writel_bits_relaxed(7 << 27, 0,
249 priv->io_base + _REG(ctrl_port));
250
251 writel_bits_relaxed(BIT(31), BIT(31),
252 priv->io_base + _REG(ctrl_port));
253 }
254}
255
256/* eotf lut: linear */
257static unsigned int eotf_33_linear_mapping[OSD_EOTF_LUT_SIZE] = {
258 0x0000, 0x0200, 0x0400, 0x0600,
259 0x0800, 0x0a00, 0x0c00, 0x0e00,
260 0x1000, 0x1200, 0x1400, 0x1600,
261 0x1800, 0x1a00, 0x1c00, 0x1e00,
262 0x2000, 0x2200, 0x2400, 0x2600,
263 0x2800, 0x2a00, 0x2c00, 0x2e00,
264 0x3000, 0x3200, 0x3400, 0x3600,
265 0x3800, 0x3a00, 0x3c00, 0x3e00,
266 0x4000
267};
268
269/* osd oetf lut: linear */
270static unsigned int oetf_41_linear_mapping[OSD_OETF_LUT_SIZE] = {
271 0, 0, 0, 0,
272 0, 32, 64, 96,
273 128, 160, 196, 224,
274 256, 288, 320, 352,
275 384, 416, 448, 480,
276 512, 544, 576, 608,
277 640, 672, 704, 736,
278 768, 800, 832, 864,
279 896, 928, 960, 992,
280 1023, 1023, 1023, 1023,
281 1023
282};
283
284static void meson_viu_load_matrix(struct meson_drm *priv)
285{
286 /* eotf lut bypass */
287 meson_viu_set_osd_lut(priv, VIU_LUT_OSD_EOTF,
288 eotf_33_linear_mapping, /* R */
289 eotf_33_linear_mapping, /* G */
290 eotf_33_linear_mapping, /* B */
291 false);
292
293 /* eotf matrix bypass */
294 meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD_EOTF,
295 eotf_bypass_coeff,
296 false);
297
298 /* oetf lut bypass */
299 meson_viu_set_osd_lut(priv, VIU_LUT_OSD_OETF,
300 oetf_41_linear_mapping, /* R */
301 oetf_41_linear_mapping, /* G */
302 oetf_41_linear_mapping, /* B */
303 false);
304
305 /* osd matrix RGB709 to YUV709 limit */
306 meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD,
307 RGB709_to_YUV709l_coeff,
308 true);
309}
310
311/* VIU OSD1 Reset as workaround for GXL+ Alpha OSD Bug */
312void meson_viu_osd1_reset(struct meson_drm *priv)
313{
314 uint32_t osd1_fifo_ctrl_stat, osd1_ctrl_stat2;
315
316 /* Save these 2 registers state */
317 osd1_fifo_ctrl_stat = readl_relaxed(
318 priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT));
319 osd1_ctrl_stat2 = readl_relaxed(
320 priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
321
322 /* Reset OSD1 */
323 writel_bits_relaxed(VIU_SW_RESET_OSD1, VIU_SW_RESET_OSD1,
324 priv->io_base + _REG(VIU_SW_RESET));
325 writel_bits_relaxed(VIU_SW_RESET_OSD1, 0,
326 priv->io_base + _REG(VIU_SW_RESET));
327
328 /* Rewrite these registers state lost in the reset */
329 writel_relaxed(osd1_fifo_ctrl_stat,
330 priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT));
331 writel_relaxed(osd1_ctrl_stat2,
332 priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
333
334 /* Reload the conversion matrix */
335 meson_viu_load_matrix(priv);
336}
337
338static inline uint32_t meson_viu_osd_burst_length_reg(uint32_t length)
339{
340 uint32_t val = (((length & 0x80) % 24) / 12);
341
342 return (((val & 0x3) << 10) | (((val & 0x4) >> 2) << 31));
343}
344
345void meson_viu_init(struct meson_drm *priv)
346{
347 uint32_t reg;
348
349 /* Disable OSDs */
350 writel_bits_relaxed(VIU_OSD1_OSD_BLK_ENABLE | VIU_OSD1_OSD_ENABLE, 0,
351 priv->io_base + _REG(VIU_OSD1_CTRL_STAT));
352 writel_bits_relaxed(VIU_OSD1_OSD_BLK_ENABLE | VIU_OSD1_OSD_ENABLE, 0,
353 priv->io_base + _REG(VIU_OSD2_CTRL_STAT));
354
355 /* On GXL/GXM, Use the 10bit HDR conversion matrix */
356 if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
357 meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL))
358 meson_viu_load_matrix(priv);
359 else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
360 meson_viu_set_g12a_osd1_matrix(priv, RGB709_to_YUV709l_coeff,
361 true);
362
363 /* Initialize OSD1 fifo control register */
364 reg = VIU_OSD_DDR_PRIORITY_URGENT |
365 VIU_OSD_HOLD_FIFO_LINES(4) |
366 VIU_OSD_FIFO_DEPTH_VAL(32) | /* fifo_depth_val: 32*8=256 */
367 VIU_OSD_WORDS_PER_BURST(4) | /* 4 words in 1 burst */
368 VIU_OSD_FIFO_LIMITS(2); /* fifo_lim: 2*16=32 */
369
370 if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
371 reg |= meson_viu_osd_burst_length_reg(32);
372 else
373 reg |= meson_viu_osd_burst_length_reg(64);
374
375 writel_relaxed(reg, priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT));
376 writel_relaxed(reg, priv->io_base + _REG(VIU_OSD2_FIFO_CTRL_STAT));
377
378 /* Set OSD alpha replace value */
379 writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT,
380 0xff << OSD_REPLACE_SHIFT,
381 priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
382 writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT,
383 0xff << OSD_REPLACE_SHIFT,
384 priv->io_base + _REG(VIU_OSD2_CTRL_STAT2));
385
386 /* Disable VD1 AFBC */
387 /* di_mif0_en=0 mif0_to_vpp_en=0 di_mad_en=0 and afbc vd1 set=0*/
388 writel_bits_relaxed(VIU_CTRL0_VD1_AFBC_MASK, 0,
389 priv->io_base + _REG(VIU_MISC_CTRL0));
390 writel_relaxed(0, priv->io_base + _REG(AFBC_ENABLE));
391
392 writel_relaxed(0x00FF00C0,
393 priv->io_base + _REG(VD1_IF0_LUMA_FIFO_SIZE));
394 writel_relaxed(0x00FF00C0,
395 priv->io_base + _REG(VD2_IF0_LUMA_FIFO_SIZE));
396
397 if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
398 writel_relaxed(VIU_OSD_BLEND_REORDER(0, 1) |
399 VIU_OSD_BLEND_REORDER(1, 0) |
400 VIU_OSD_BLEND_REORDER(2, 0) |
401 VIU_OSD_BLEND_REORDER(3, 0) |
402 VIU_OSD_BLEND_DIN_EN(1) |
403 VIU_OSD_BLEND1_DIN3_BYPASS_TO_DOUT1 |
404 VIU_OSD_BLEND1_DOUT_BYPASS_TO_BLEND2 |
405 VIU_OSD_BLEND_DIN0_BYPASS_TO_DOUT0 |
406 VIU_OSD_BLEND_BLEN2_PREMULT_EN(1) |
407 VIU_OSD_BLEND_HOLD_LINES(4),
408 priv->io_base + _REG(VIU_OSD_BLEND_CTRL));
409
410 writel_relaxed(OSD_BLEND_PATH_SEL_ENABLE,
411 priv->io_base + _REG(OSD1_BLEND_SRC_CTRL));
412 writel_relaxed(OSD_BLEND_PATH_SEL_ENABLE,
413 priv->io_base + _REG(OSD2_BLEND_SRC_CTRL));
414 writel_relaxed(0, priv->io_base + _REG(VD1_BLEND_SRC_CTRL));
415 writel_relaxed(0, priv->io_base + _REG(VD2_BLEND_SRC_CTRL));
416 writel_relaxed(0,
417 priv->io_base + _REG(VIU_OSD_BLEND_DUMMY_DATA0));
418 writel_relaxed(0,
419 priv->io_base + _REG(VIU_OSD_BLEND_DUMMY_ALPHA));
420
421 writel_bits_relaxed(DOLBY_BYPASS_EN(0xc), DOLBY_BYPASS_EN(0xc),
422 priv->io_base + _REG(DOLBY_PATH_CTRL));
423 }
424
425 priv->viu.osd1_enabled = false;
426 priv->viu.osd1_commit = false;
427 priv->viu.osd1_interlace = false;
428}
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}