2 * Copyright (C) 2016 BayLibre, SAS
4 * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
5 * Copyright (C) 2014 Endless Mobile
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.
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.
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/>.
21 #include <linux/kernel.h>
22 #include <linux/module.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"
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
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
54 enum viu_matrix_sel_e {
55 VIU_MATRIX_OSD_EOTF = 0,
64 #define COEFF_NORM(a) ((int)((((a) * 2048.0) + 1) / 2))
65 #define MATRIX_5X3_COEF_SIZE 24
67 #define EOTF_COEFF_NORM(a) ((int)((((a) * 4096.0) + 1) / 2))
68 #define EOTF_COEFF_SIZE 10
69 #define EOTF_COEFF_RIGHTSHIFT 1
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 */
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 */
90 void meson_viu_set_osd_matrix(struct meson_drm *priv,
91 enum viu_matrix_sel_e m_select,
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));
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));
110 writel(((m[11] & 0x1fff) << 16) | (m[12] & 0x1fff),
112 _REG(VIU_OSD1_MATRIX_COEF22_30));
113 writel(((m[13] & 0x1fff) << 16) | (m[14] & 0x1fff),
115 _REG(VIU_OSD1_MATRIX_COEF31_32));
116 writel(((m[15] & 0x1fff) << 16) | (m[16] & 0x1fff),
118 _REG(VIU_OSD1_MATRIX_COEF40_41));
119 writel(m[17] & 0x1fff, priv->io_base +
120 _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
122 writel((m[11] & 0x1fff) << 16, priv->io_base +
123 _REG(VIU_OSD1_MATRIX_COEF22_30));
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));
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));
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) {
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));
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));
156 #define OSD_EOTF_LUT_SIZE 33
157 #define OSD_OETF_LUT_SIZE 41
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,
164 unsigned int addr_port;
165 unsigned int data_port;
166 unsigned int ctrl_port;
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;
180 if (lut_sel == VIU_LUT_OSD_OETF) {
181 writel(0, priv->io_base + _REG(addr_port));
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));
187 writel(r_map[OSD_OETF_LUT_SIZE - 1] | (g_map[0] << 16),
188 priv->io_base + _REG(data_port));
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));
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));
198 writel(b_map[OSD_OETF_LUT_SIZE - 1],
199 priv->io_base + _REG(data_port));
202 writel_bits_relaxed(0x7 << 29, 7 << 29,
203 priv->io_base + _REG(ctrl_port));
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));
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));
214 writel(r_map[OSD_EOTF_LUT_SIZE - 1] | (g_map[0] << 16),
215 priv->io_base + _REG(data_port));
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));
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));
225 writel(b_map[OSD_EOTF_LUT_SIZE - 1],
226 priv->io_base + _REG(data_port));
229 writel_bits_relaxed(7 << 27, 7 << 27,
230 priv->io_base + _REG(ctrl_port));
232 writel_bits_relaxed(7 << 27, 0,
233 priv->io_base + _REG(ctrl_port));
235 writel_bits_relaxed(BIT(31), BIT(31),
236 priv->io_base + _REG(ctrl_port));
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,
253 /* osd oetf lut: linear */
254 static unsigned int oetf_41_linear_mapping[OSD_OETF_LUT_SIZE] = {
264 1023, 1023, 1023, 1023,
268 static void meson_viu_load_matrix(struct meson_drm *priv)
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 */
277 /* eotf matrix bypass */
278 meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD_EOTF,
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 */
289 /* osd matrix RGB709 to YUV709 limit */
290 meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD,
291 RGB709_to_YUV709l_coeff,
295 void meson_viu_init(struct meson_drm *priv)
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));
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);
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 */
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));
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));
328 priv->viu.osd1_enabled = false;
329 priv->viu.osd1_commit = false;
330 priv->viu.osd1_interlace = false;