Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
 54enum viu_matrix_sel_e {
 55	VIU_MATRIX_OSD_EOTF = 0,
 56	VIU_MATRIX_OSD,
 57};
 58
 59enum 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
 71static 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 */
 83static 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
 90void 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
159void 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 */
241static 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 */
254static 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
268static 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
295void 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}