xref: /rk3399_rockchip-uboot/drivers/video/drm/rockchip_display_helper.c (revision 690e9ed17aeae22edefd790be3aa69d5fb401bae)
1*690e9ed1SSandy Huang /*
2*690e9ed1SSandy Huang  * (C) Copyright 2023 Rockchip Electronics Co., Ltd
3*690e9ed1SSandy Huang  *
4*690e9ed1SSandy Huang  * SPDX-License-Identifier:	GPL-2.0+
5*690e9ed1SSandy Huang  */
6*690e9ed1SSandy Huang 
7*690e9ed1SSandy Huang #include <linux/hdmi.h>
8*690e9ed1SSandy Huang #include <linux/compat.h>
9*690e9ed1SSandy Huang #include "rockchip_display.h"
10*690e9ed1SSandy Huang #include <spl_display.h>
11*690e9ed1SSandy Huang 
12*690e9ed1SSandy Huang #define RK_BLK_SIZE 512
13*690e9ed1SSandy Huang #define BMP_PROCESSED_FLAG 8399
14*690e9ed1SSandy Huang 
15*690e9ed1SSandy Huang static uint32_t crc32_table[256];
16*690e9ed1SSandy Huang 
17*690e9ed1SSandy Huang void rockchip_display_make_crc32_table(void)
18*690e9ed1SSandy Huang {
19*690e9ed1SSandy Huang 	uint32_t c;
20*690e9ed1SSandy Huang 	int n, k;
21*690e9ed1SSandy Huang 	unsigned long poly;		/* polynomial exclusive-or pattern */
22*690e9ed1SSandy Huang 	/* terms of polynomial defining this crc (except x^32): */
23*690e9ed1SSandy Huang 	static const char p[] = {0, 1, 2, 4, 5, 7, 8, 10, 11, 12, 16, 22, 23, 26};
24*690e9ed1SSandy Huang 
25*690e9ed1SSandy Huang 	/* make exclusive-or pattern from polynomial (0xedb88320L) */
26*690e9ed1SSandy Huang 	poly = 0L;
27*690e9ed1SSandy Huang 	for (n = 0; n < sizeof(p) / sizeof(char); n++)
28*690e9ed1SSandy Huang 		poly |= 1L << (31 - p[n]);
29*690e9ed1SSandy Huang 
30*690e9ed1SSandy Huang 	for (n = 0; n < 256; n++) {
31*690e9ed1SSandy Huang 		c = (unsigned long)n;
32*690e9ed1SSandy Huang 		for (k = 0; k < 8; k++)
33*690e9ed1SSandy Huang 		c = c & 1 ? poly ^ (c >> 1) : c >> 1;
34*690e9ed1SSandy Huang 		crc32_table[n] = cpu_to_le32(c);
35*690e9ed1SSandy Huang 	}
36*690e9ed1SSandy Huang }
37*690e9ed1SSandy Huang 
38*690e9ed1SSandy Huang uint32_t rockchip_display_crc32c_cal(unsigned char *data, int length)
39*690e9ed1SSandy Huang {
40*690e9ed1SSandy Huang 	int i;
41*690e9ed1SSandy Huang 	uint32_t crc;
42*690e9ed1SSandy Huang 	crc = 0xFFFFFFFF;
43*690e9ed1SSandy Huang 
44*690e9ed1SSandy Huang 	for (i = 0; i < length; i++) {
45*690e9ed1SSandy Huang 		crc = crc32_table[(crc ^ *data) & 0xff] ^ (crc >> 8);
46*690e9ed1SSandy Huang 		data++;
47*690e9ed1SSandy Huang 	}
48*690e9ed1SSandy Huang 
49*690e9ed1SSandy Huang 	return crc ^ 0xffffffff;
50*690e9ed1SSandy Huang }
51*690e9ed1SSandy Huang 
52*690e9ed1SSandy Huang /**
53*690e9ed1SSandy Huang  * drm_mode_max_resolution_filter - mark modes out of vop max resolution
54*690e9ed1SSandy Huang  * @edid_data: structure store mode list
55*690e9ed1SSandy Huang  * @max_output: vop max output resolution
56*690e9ed1SSandy Huang  */
57*690e9ed1SSandy Huang void drm_mode_max_resolution_filter(struct hdmi_edid_data *edid_data,
58*690e9ed1SSandy Huang 				    struct vop_rect *max_output)
59*690e9ed1SSandy Huang {
60*690e9ed1SSandy Huang 	int i;
61*690e9ed1SSandy Huang 
62*690e9ed1SSandy Huang 	for (i = 0; i < edid_data->modes; i++) {
63*690e9ed1SSandy Huang 		if (edid_data->mode_buf[i].hdisplay > max_output->width ||
64*690e9ed1SSandy Huang 		    edid_data->mode_buf[i].vdisplay > max_output->height)
65*690e9ed1SSandy Huang 			edid_data->mode_buf[i].invalid = true;
66*690e9ed1SSandy Huang 	}
67*690e9ed1SSandy Huang }
68*690e9ed1SSandy Huang 
69*690e9ed1SSandy Huang int drm_mode_vrefresh(const struct drm_display_mode *mode)
70*690e9ed1SSandy Huang {
71*690e9ed1SSandy Huang 	int refresh = 0;
72*690e9ed1SSandy Huang 	unsigned int calc_val;
73*690e9ed1SSandy Huang 
74*690e9ed1SSandy Huang 	if (mode->vrefresh > 0) {
75*690e9ed1SSandy Huang 		refresh = mode->vrefresh;
76*690e9ed1SSandy Huang 	} else if (mode->htotal > 0 && mode->vtotal > 0) {
77*690e9ed1SSandy Huang 		int vtotal;
78*690e9ed1SSandy Huang 
79*690e9ed1SSandy Huang 		vtotal = mode->vtotal;
80*690e9ed1SSandy Huang 		/* work out vrefresh the value will be x1000 */
81*690e9ed1SSandy Huang 		calc_val = (mode->clock * 1000);
82*690e9ed1SSandy Huang 		calc_val /= mode->htotal;
83*690e9ed1SSandy Huang 		refresh = (calc_val + vtotal / 2) / vtotal;
84*690e9ed1SSandy Huang 
85*690e9ed1SSandy Huang 		if (mode->flags & DRM_MODE_FLAG_INTERLACE)
86*690e9ed1SSandy Huang 		refresh *= 2;
87*690e9ed1SSandy Huang 		if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
88*690e9ed1SSandy Huang 		refresh /= 2;
89*690e9ed1SSandy Huang 		if (mode->vscan > 1)
90*690e9ed1SSandy Huang 		refresh /= mode->vscan;
91*690e9ed1SSandy Huang 	}
92*690e9ed1SSandy Huang 	return refresh;
93*690e9ed1SSandy Huang }
94*690e9ed1SSandy Huang 
95*690e9ed1SSandy Huang /**
96*690e9ed1SSandy Huang  * drm_mode_set_crtcinfo - set CRTC modesetting timing parameters
97*690e9ed1SSandy Huang  * @p: mode
98*690e9ed1SSandy Huang  * @adjust_flags: a combination of adjustment flags
99*690e9ed1SSandy Huang  *
100*690e9ed1SSandy Huang  * Setup the CRTC modesetting timing parameters for @p, adjusting if necessary.
101*690e9ed1SSandy Huang  *
102*690e9ed1SSandy Huang  * - The CRTC_INTERLACE_HALVE_V flag can be used to halve vertical timings of
103*690e9ed1SSandy Huang  *   interlaced modes.
104*690e9ed1SSandy Huang  * - The CRTC_STEREO_DOUBLE flag can be used to compute the timings for
105*690e9ed1SSandy Huang  *   buffers containing two eyes (only adjust the timings when needed, eg. for
106*690e9ed1SSandy Huang  *   "frame packing" or "side by side full").
107*690e9ed1SSandy Huang  * - The CRTC_NO_DBLSCAN and CRTC_NO_VSCAN flags request that adjustment *not*
108*690e9ed1SSandy Huang  *   be performed for doublescan and vscan > 1 modes respectively.
109*690e9ed1SSandy Huang  */
110*690e9ed1SSandy Huang void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
111*690e9ed1SSandy Huang {
112*690e9ed1SSandy Huang 	if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) == DRM_MODE_TYPE_BUILTIN))
113*690e9ed1SSandy Huang 		return;
114*690e9ed1SSandy Huang 
115*690e9ed1SSandy Huang 	if (p->flags & DRM_MODE_FLAG_DBLCLK)
116*690e9ed1SSandy Huang 		p->crtc_clock = 2 * p->clock;
117*690e9ed1SSandy Huang 	else
118*690e9ed1SSandy Huang 		p->crtc_clock = p->clock;
119*690e9ed1SSandy Huang 	p->crtc_hdisplay = p->hdisplay;
120*690e9ed1SSandy Huang 	p->crtc_hsync_start = p->hsync_start;
121*690e9ed1SSandy Huang 	p->crtc_hsync_end = p->hsync_end;
122*690e9ed1SSandy Huang 	p->crtc_htotal = p->htotal;
123*690e9ed1SSandy Huang 	p->crtc_hskew = p->hskew;
124*690e9ed1SSandy Huang 	p->crtc_vdisplay = p->vdisplay;
125*690e9ed1SSandy Huang 	p->crtc_vsync_start = p->vsync_start;
126*690e9ed1SSandy Huang 	p->crtc_vsync_end = p->vsync_end;
127*690e9ed1SSandy Huang 	p->crtc_vtotal = p->vtotal;
128*690e9ed1SSandy Huang 
129*690e9ed1SSandy Huang 	if (p->flags & DRM_MODE_FLAG_INTERLACE) {
130*690e9ed1SSandy Huang 		if (adjust_flags & CRTC_INTERLACE_HALVE_V) {
131*690e9ed1SSandy Huang 			p->crtc_vdisplay /= 2;
132*690e9ed1SSandy Huang 			p->crtc_vsync_start /= 2;
133*690e9ed1SSandy Huang 			p->crtc_vsync_end /= 2;
134*690e9ed1SSandy Huang 			p->crtc_vtotal /= 2;
135*690e9ed1SSandy Huang 		}
136*690e9ed1SSandy Huang 	}
137*690e9ed1SSandy Huang 
138*690e9ed1SSandy Huang 	if (!(adjust_flags & CRTC_NO_DBLSCAN)) {
139*690e9ed1SSandy Huang 		if (p->flags & DRM_MODE_FLAG_DBLSCAN) {
140*690e9ed1SSandy Huang 			p->crtc_vdisplay *= 2;
141*690e9ed1SSandy Huang 			p->crtc_vsync_start *= 2;
142*690e9ed1SSandy Huang 			p->crtc_vsync_end *= 2;
143*690e9ed1SSandy Huang 			p->crtc_vtotal *= 2;
144*690e9ed1SSandy Huang 		}
145*690e9ed1SSandy Huang 	}
146*690e9ed1SSandy Huang 
147*690e9ed1SSandy Huang 	if (!(adjust_flags & CRTC_NO_VSCAN)) {
148*690e9ed1SSandy Huang 		if (p->vscan > 1) {
149*690e9ed1SSandy Huang 			p->crtc_vdisplay *= p->vscan;
150*690e9ed1SSandy Huang 			p->crtc_vsync_start *= p->vscan;
151*690e9ed1SSandy Huang 			p->crtc_vsync_end *= p->vscan;
152*690e9ed1SSandy Huang 			p->crtc_vtotal *= p->vscan;
153*690e9ed1SSandy Huang 		}
154*690e9ed1SSandy Huang 	}
155*690e9ed1SSandy Huang 
156*690e9ed1SSandy Huang 	if (adjust_flags & CRTC_STEREO_DOUBLE) {
157*690e9ed1SSandy Huang 		unsigned int layout = p->flags & DRM_MODE_FLAG_3D_MASK;
158*690e9ed1SSandy Huang 
159*690e9ed1SSandy Huang 		switch (layout) {
160*690e9ed1SSandy Huang 		case DRM_MODE_FLAG_3D_FRAME_PACKING:
161*690e9ed1SSandy Huang 			p->crtc_clock *= 2;
162*690e9ed1SSandy Huang 			p->crtc_vdisplay += p->crtc_vtotal;
163*690e9ed1SSandy Huang 			p->crtc_vsync_start += p->crtc_vtotal;
164*690e9ed1SSandy Huang 			p->crtc_vsync_end += p->crtc_vtotal;
165*690e9ed1SSandy Huang 			p->crtc_vtotal += p->crtc_vtotal;
166*690e9ed1SSandy Huang 			break;
167*690e9ed1SSandy Huang 		}
168*690e9ed1SSandy Huang 	}
169*690e9ed1SSandy Huang 
170*690e9ed1SSandy Huang 	p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay);
171*690e9ed1SSandy Huang 	p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal);
172*690e9ed1SSandy Huang 	p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay);
173*690e9ed1SSandy Huang 	p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal);
174*690e9ed1SSandy Huang }
175*690e9ed1SSandy Huang 
176*690e9ed1SSandy Huang /**
177*690e9ed1SSandy Huang  * drm_mode_is_420_only - if a given videomode can be only supported in YCBCR420
178*690e9ed1SSandy Huang  * output format
179*690e9ed1SSandy Huang  *
180*690e9ed1SSandy Huang  * @connector: drm connector under action.
181*690e9ed1SSandy Huang  * @mode: video mode to be tested.
182*690e9ed1SSandy Huang  *
183*690e9ed1SSandy Huang  * Returns:
184*690e9ed1SSandy Huang  * true if the mode can be supported in YCBCR420 format
185*690e9ed1SSandy Huang  * false if not.
186*690e9ed1SSandy Huang  */
187*690e9ed1SSandy Huang static bool drm_mode_is_420_only(const struct drm_display_info *display,
188*690e9ed1SSandy Huang 			  struct drm_display_mode *mode)
189*690e9ed1SSandy Huang {
190*690e9ed1SSandy Huang 	u8 vic = drm_match_cea_mode(mode);
191*690e9ed1SSandy Huang 
192*690e9ed1SSandy Huang 	return test_bit(vic, display->hdmi.y420_vdb_modes);
193*690e9ed1SSandy Huang }
194*690e9ed1SSandy Huang 
195*690e9ed1SSandy Huang /**
196*690e9ed1SSandy Huang  * drm_mode_is_420_also - if a given videomode can be supported in YCBCR420
197*690e9ed1SSandy Huang  * output format also (along with RGB/YCBCR444/422)
198*690e9ed1SSandy Huang  *
199*690e9ed1SSandy Huang  * @display: display under action.
200*690e9ed1SSandy Huang  * @mode: video mode to be tested.
201*690e9ed1SSandy Huang  *
202*690e9ed1SSandy Huang  * Returns:
203*690e9ed1SSandy Huang  * true if the mode can be support YCBCR420 format
204*690e9ed1SSandy Huang  * false if not.
205*690e9ed1SSandy Huang  */
206*690e9ed1SSandy Huang static bool drm_mode_is_420_also(const struct drm_display_info *display,
207*690e9ed1SSandy Huang 			  struct drm_display_mode *mode)
208*690e9ed1SSandy Huang {
209*690e9ed1SSandy Huang 	u8 vic = drm_match_cea_mode(mode);
210*690e9ed1SSandy Huang 
211*690e9ed1SSandy Huang 	return test_bit(vic, display->hdmi.y420_cmdb_modes);
212*690e9ed1SSandy Huang }
213*690e9ed1SSandy Huang 
214*690e9ed1SSandy Huang /**
215*690e9ed1SSandy Huang  * drm_mode_is_420 - if a given videomode can be supported in YCBCR420
216*690e9ed1SSandy Huang  * output format
217*690e9ed1SSandy Huang  *
218*690e9ed1SSandy Huang  * @display: display under action.
219*690e9ed1SSandy Huang  * @mode: video mode to be tested.
220*690e9ed1SSandy Huang  *
221*690e9ed1SSandy Huang  * Returns:
222*690e9ed1SSandy Huang  * true if the mode can be supported in YCBCR420 format
223*690e9ed1SSandy Huang  * false if not.
224*690e9ed1SSandy Huang  */
225*690e9ed1SSandy Huang bool drm_mode_is_420(const struct drm_display_info *display,
226*690e9ed1SSandy Huang 		     struct drm_display_mode *mode)
227*690e9ed1SSandy Huang {
228*690e9ed1SSandy Huang 	return drm_mode_is_420_only(display, mode) ||
229*690e9ed1SSandy Huang 		drm_mode_is_420_also(display, mode);
230*690e9ed1SSandy Huang }
231*690e9ed1SSandy Huang 
232*690e9ed1SSandy Huang static int display_rect_calc_scale(int src, int dst)
233*690e9ed1SSandy Huang {
234*690e9ed1SSandy Huang 	int scale = 0;
235*690e9ed1SSandy Huang 
236*690e9ed1SSandy Huang 	if (WARN_ON(src < 0 || dst < 0))
237*690e9ed1SSandy Huang 		return -EINVAL;
238*690e9ed1SSandy Huang 
239*690e9ed1SSandy Huang 	if (dst == 0)
240*690e9ed1SSandy Huang 		return 0;
241*690e9ed1SSandy Huang 
242*690e9ed1SSandy Huang 	src <<= 16;
243*690e9ed1SSandy Huang 
244*690e9ed1SSandy Huang 	if (src > (dst << 16))
245*690e9ed1SSandy Huang 		return DIV_ROUND_UP(src, dst);
246*690e9ed1SSandy Huang 	else
247*690e9ed1SSandy Huang 		scale = src / dst;
248*690e9ed1SSandy Huang 
249*690e9ed1SSandy Huang 	return scale;
250*690e9ed1SSandy Huang }
251*690e9ed1SSandy Huang 
252*690e9ed1SSandy Huang int display_rect_calc_hscale(struct display_rect *src, struct display_rect *dst,
253*690e9ed1SSandy Huang 			     int min_hscale, int max_hscale)
254*690e9ed1SSandy Huang {
255*690e9ed1SSandy Huang 	int hscale = display_rect_calc_scale(src->w, dst->w);
256*690e9ed1SSandy Huang 
257*690e9ed1SSandy Huang 	if (hscale < 0 || dst->w == 0)
258*690e9ed1SSandy Huang 		return hscale;
259*690e9ed1SSandy Huang 
260*690e9ed1SSandy Huang 	if (hscale < min_hscale || hscale > max_hscale)
261*690e9ed1SSandy Huang 		return -ERANGE;
262*690e9ed1SSandy Huang 
263*690e9ed1SSandy Huang 	return hscale;
264*690e9ed1SSandy Huang }
265*690e9ed1SSandy Huang 
266*690e9ed1SSandy Huang int display_rect_calc_vscale(struct display_rect *src, struct display_rect *dst,
267*690e9ed1SSandy Huang 			     int min_vscale, int max_vscale)
268*690e9ed1SSandy Huang {
269*690e9ed1SSandy Huang 	int vscale = display_rect_calc_scale(src->h, dst->h);
270*690e9ed1SSandy Huang 
271*690e9ed1SSandy Huang 	if (vscale < 0 || dst->h == 0)
272*690e9ed1SSandy Huang 		return vscale;
273*690e9ed1SSandy Huang 
274*690e9ed1SSandy Huang 	if (vscale < min_vscale || vscale > max_vscale)
275*690e9ed1SSandy Huang 		return -ERANGE;
276*690e9ed1SSandy Huang 
277*690e9ed1SSandy Huang 	return vscale;
278*690e9ed1SSandy Huang }
279*690e9ed1SSandy Huang 
280