1690e9ed1SSandy Huang /*
2690e9ed1SSandy Huang * (C) Copyright 2023 Rockchip Electronics Co., Ltd
3690e9ed1SSandy Huang *
4690e9ed1SSandy Huang * SPDX-License-Identifier: GPL-2.0+
5690e9ed1SSandy Huang */
6690e9ed1SSandy Huang
7690e9ed1SSandy Huang #include <linux/hdmi.h>
8690e9ed1SSandy Huang #include <linux/compat.h>
9690e9ed1SSandy Huang #include "rockchip_display.h"
10690e9ed1SSandy Huang #include <spl_display.h>
11690e9ed1SSandy Huang
12690e9ed1SSandy Huang #define RK_BLK_SIZE 512
13690e9ed1SSandy Huang #define BMP_PROCESSED_FLAG 8399
14690e9ed1SSandy Huang
15690e9ed1SSandy Huang static uint32_t crc32_table[256];
16690e9ed1SSandy Huang
rockchip_display_make_crc32_table(void)17690e9ed1SSandy Huang void rockchip_display_make_crc32_table(void)
18690e9ed1SSandy Huang {
19690e9ed1SSandy Huang uint32_t c;
20690e9ed1SSandy Huang int n, k;
21690e9ed1SSandy Huang unsigned long poly; /* polynomial exclusive-or pattern */
22690e9ed1SSandy Huang /* terms of polynomial defining this crc (except x^32): */
23690e9ed1SSandy Huang static const char p[] = {0, 1, 2, 4, 5, 7, 8, 10, 11, 12, 16, 22, 23, 26};
24690e9ed1SSandy Huang
25690e9ed1SSandy Huang /* make exclusive-or pattern from polynomial (0xedb88320L) */
26690e9ed1SSandy Huang poly = 0L;
27690e9ed1SSandy Huang for (n = 0; n < sizeof(p) / sizeof(char); n++)
28690e9ed1SSandy Huang poly |= 1L << (31 - p[n]);
29690e9ed1SSandy Huang
30690e9ed1SSandy Huang for (n = 0; n < 256; n++) {
31690e9ed1SSandy Huang c = (unsigned long)n;
32690e9ed1SSandy Huang for (k = 0; k < 8; k++)
33690e9ed1SSandy Huang c = c & 1 ? poly ^ (c >> 1) : c >> 1;
34690e9ed1SSandy Huang crc32_table[n] = cpu_to_le32(c);
35690e9ed1SSandy Huang }
36690e9ed1SSandy Huang }
37690e9ed1SSandy Huang
rockchip_display_crc32c_cal(unsigned char * data,int length)38690e9ed1SSandy Huang uint32_t rockchip_display_crc32c_cal(unsigned char *data, int length)
39690e9ed1SSandy Huang {
40690e9ed1SSandy Huang int i;
41690e9ed1SSandy Huang uint32_t crc;
42690e9ed1SSandy Huang crc = 0xFFFFFFFF;
43690e9ed1SSandy Huang
44690e9ed1SSandy Huang for (i = 0; i < length; i++) {
45690e9ed1SSandy Huang crc = crc32_table[(crc ^ *data) & 0xff] ^ (crc >> 8);
46690e9ed1SSandy Huang data++;
47690e9ed1SSandy Huang }
48690e9ed1SSandy Huang
49690e9ed1SSandy Huang return crc ^ 0xffffffff;
50690e9ed1SSandy Huang }
51690e9ed1SSandy Huang
52690e9ed1SSandy Huang /**
53690e9ed1SSandy Huang * drm_mode_max_resolution_filter - mark modes out of vop max resolution
54690e9ed1SSandy Huang * @edid_data: structure store mode list
55690e9ed1SSandy Huang * @max_output: vop max output resolution
56690e9ed1SSandy Huang */
drm_mode_max_resolution_filter(struct hdmi_edid_data * edid_data,struct vop_rect * max_output)57690e9ed1SSandy Huang void drm_mode_max_resolution_filter(struct hdmi_edid_data *edid_data,
58690e9ed1SSandy Huang struct vop_rect *max_output)
59690e9ed1SSandy Huang {
60690e9ed1SSandy Huang int i;
61690e9ed1SSandy Huang
62690e9ed1SSandy Huang for (i = 0; i < edid_data->modes; i++) {
63690e9ed1SSandy Huang if (edid_data->mode_buf[i].hdisplay > max_output->width ||
64690e9ed1SSandy Huang edid_data->mode_buf[i].vdisplay > max_output->height)
65690e9ed1SSandy Huang edid_data->mode_buf[i].invalid = true;
66690e9ed1SSandy Huang }
67690e9ed1SSandy Huang }
68690e9ed1SSandy Huang
drm_mode_vrefresh(const struct drm_display_mode * mode)69690e9ed1SSandy Huang int drm_mode_vrefresh(const struct drm_display_mode *mode)
70690e9ed1SSandy Huang {
71690e9ed1SSandy Huang int refresh = 0;
72690e9ed1SSandy Huang unsigned int calc_val;
73690e9ed1SSandy Huang
74690e9ed1SSandy Huang if (mode->vrefresh > 0) {
75690e9ed1SSandy Huang refresh = mode->vrefresh;
76690e9ed1SSandy Huang } else if (mode->htotal > 0 && mode->vtotal > 0) {
77690e9ed1SSandy Huang int vtotal;
78690e9ed1SSandy Huang
79690e9ed1SSandy Huang vtotal = mode->vtotal;
80690e9ed1SSandy Huang /* work out vrefresh the value will be x1000 */
81690e9ed1SSandy Huang calc_val = (mode->clock * 1000);
82690e9ed1SSandy Huang calc_val /= mode->htotal;
83690e9ed1SSandy Huang refresh = (calc_val + vtotal / 2) / vtotal;
84690e9ed1SSandy Huang
85690e9ed1SSandy Huang if (mode->flags & DRM_MODE_FLAG_INTERLACE)
86690e9ed1SSandy Huang refresh *= 2;
87690e9ed1SSandy Huang if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
88690e9ed1SSandy Huang refresh /= 2;
89690e9ed1SSandy Huang if (mode->vscan > 1)
90690e9ed1SSandy Huang refresh /= mode->vscan;
91690e9ed1SSandy Huang }
92690e9ed1SSandy Huang return refresh;
93690e9ed1SSandy Huang }
94690e9ed1SSandy Huang
95690e9ed1SSandy Huang /**
96690e9ed1SSandy Huang * drm_mode_set_crtcinfo - set CRTC modesetting timing parameters
97690e9ed1SSandy Huang * @p: mode
98690e9ed1SSandy Huang * @adjust_flags: a combination of adjustment flags
99690e9ed1SSandy Huang *
100690e9ed1SSandy Huang * Setup the CRTC modesetting timing parameters for @p, adjusting if necessary.
101690e9ed1SSandy Huang *
102690e9ed1SSandy Huang * - The CRTC_INTERLACE_HALVE_V flag can be used to halve vertical timings of
103690e9ed1SSandy Huang * interlaced modes.
104690e9ed1SSandy Huang * - The CRTC_STEREO_DOUBLE flag can be used to compute the timings for
105690e9ed1SSandy Huang * buffers containing two eyes (only adjust the timings when needed, eg. for
106690e9ed1SSandy Huang * "frame packing" or "side by side full").
107690e9ed1SSandy Huang * - The CRTC_NO_DBLSCAN and CRTC_NO_VSCAN flags request that adjustment *not*
108690e9ed1SSandy Huang * be performed for doublescan and vscan > 1 modes respectively.
109690e9ed1SSandy Huang */
drm_mode_set_crtcinfo(struct drm_display_mode * p,int adjust_flags)110690e9ed1SSandy Huang void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
111690e9ed1SSandy Huang {
112690e9ed1SSandy Huang if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) == DRM_MODE_TYPE_BUILTIN))
113690e9ed1SSandy Huang return;
114690e9ed1SSandy Huang
115690e9ed1SSandy Huang p->crtc_clock = p->clock;
116690e9ed1SSandy Huang p->crtc_hdisplay = p->hdisplay;
117690e9ed1SSandy Huang p->crtc_hsync_start = p->hsync_start;
118690e9ed1SSandy Huang p->crtc_hsync_end = p->hsync_end;
119690e9ed1SSandy Huang p->crtc_htotal = p->htotal;
120690e9ed1SSandy Huang p->crtc_hskew = p->hskew;
121690e9ed1SSandy Huang p->crtc_vdisplay = p->vdisplay;
122690e9ed1SSandy Huang p->crtc_vsync_start = p->vsync_start;
123690e9ed1SSandy Huang p->crtc_vsync_end = p->vsync_end;
124690e9ed1SSandy Huang p->crtc_vtotal = p->vtotal;
125690e9ed1SSandy Huang
126690e9ed1SSandy Huang if (p->flags & DRM_MODE_FLAG_INTERLACE) {
127690e9ed1SSandy Huang if (adjust_flags & CRTC_INTERLACE_HALVE_V) {
128690e9ed1SSandy Huang p->crtc_vdisplay /= 2;
129690e9ed1SSandy Huang p->crtc_vsync_start /= 2;
130690e9ed1SSandy Huang p->crtc_vsync_end /= 2;
131690e9ed1SSandy Huang p->crtc_vtotal /= 2;
132690e9ed1SSandy Huang }
133690e9ed1SSandy Huang }
134690e9ed1SSandy Huang
135690e9ed1SSandy Huang if (!(adjust_flags & CRTC_NO_DBLSCAN)) {
136690e9ed1SSandy Huang if (p->flags & DRM_MODE_FLAG_DBLSCAN) {
137690e9ed1SSandy Huang p->crtc_vdisplay *= 2;
138690e9ed1SSandy Huang p->crtc_vsync_start *= 2;
139690e9ed1SSandy Huang p->crtc_vsync_end *= 2;
140690e9ed1SSandy Huang p->crtc_vtotal *= 2;
141690e9ed1SSandy Huang }
142690e9ed1SSandy Huang }
143690e9ed1SSandy Huang
144690e9ed1SSandy Huang if (!(adjust_flags & CRTC_NO_VSCAN)) {
145690e9ed1SSandy Huang if (p->vscan > 1) {
146690e9ed1SSandy Huang p->crtc_vdisplay *= p->vscan;
147690e9ed1SSandy Huang p->crtc_vsync_start *= p->vscan;
148690e9ed1SSandy Huang p->crtc_vsync_end *= p->vscan;
149690e9ed1SSandy Huang p->crtc_vtotal *= p->vscan;
150690e9ed1SSandy Huang }
151690e9ed1SSandy Huang }
152690e9ed1SSandy Huang
153690e9ed1SSandy Huang if (adjust_flags & CRTC_STEREO_DOUBLE) {
154690e9ed1SSandy Huang unsigned int layout = p->flags & DRM_MODE_FLAG_3D_MASK;
155690e9ed1SSandy Huang
156690e9ed1SSandy Huang switch (layout) {
157690e9ed1SSandy Huang case DRM_MODE_FLAG_3D_FRAME_PACKING:
158690e9ed1SSandy Huang p->crtc_clock *= 2;
159690e9ed1SSandy Huang p->crtc_vdisplay += p->crtc_vtotal;
160690e9ed1SSandy Huang p->crtc_vsync_start += p->crtc_vtotal;
161690e9ed1SSandy Huang p->crtc_vsync_end += p->crtc_vtotal;
162690e9ed1SSandy Huang p->crtc_vtotal += p->crtc_vtotal;
163690e9ed1SSandy Huang break;
164690e9ed1SSandy Huang }
165690e9ed1SSandy Huang }
166690e9ed1SSandy Huang
167690e9ed1SSandy Huang p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay);
168690e9ed1SSandy Huang p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal);
169690e9ed1SSandy Huang p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay);
170690e9ed1SSandy Huang p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal);
171690e9ed1SSandy Huang }
172690e9ed1SSandy Huang
drm_mode_convert_to_origin_mode(struct drm_display_mode * mode)1732b992d78SDamon Ding void drm_mode_convert_to_origin_mode(struct drm_display_mode *mode)
1742b992d78SDamon Ding {
1752b992d78SDamon Ding u16 hactive, hfp, hsync, hbp;
1762b992d78SDamon Ding
1772b992d78SDamon Ding hactive = mode->hdisplay;
1782b992d78SDamon Ding hfp = mode->hsync_start - mode->hdisplay;
1792b992d78SDamon Ding hsync = mode->hsync_end - mode->hsync_start;
1802b992d78SDamon Ding hbp = mode->htotal - mode->hsync_end;
1812b992d78SDamon Ding
1822b992d78SDamon Ding mode->clock /= 2;
1832b992d78SDamon Ding mode->crtc_clock /= 2;
1842b992d78SDamon Ding mode->hdisplay = hactive / 2;
1852b992d78SDamon Ding mode->hsync_start = mode->hdisplay + hfp / 2;
1862b992d78SDamon Ding mode->hsync_end = mode->hsync_start + hsync / 2;
1872b992d78SDamon Ding mode->htotal = mode->hsync_end + hbp / 2;
1882b992d78SDamon Ding }
1892b992d78SDamon Ding
drm_mode_convert_to_split_mode(struct drm_display_mode * mode)1902b992d78SDamon Ding void drm_mode_convert_to_split_mode(struct drm_display_mode *mode)
1912b992d78SDamon Ding {
1922b992d78SDamon Ding u16 hactive, hfp, hsync, hbp;
1932b992d78SDamon Ding
1942b992d78SDamon Ding hactive = mode->hdisplay;
1952b992d78SDamon Ding hfp = mode->hsync_start - mode->hdisplay;
1962b992d78SDamon Ding hsync = mode->hsync_end - mode->hsync_start;
1972b992d78SDamon Ding hbp = mode->htotal - mode->hsync_end;
1982b992d78SDamon Ding
1992b992d78SDamon Ding mode->clock *= 2;
2002b992d78SDamon Ding mode->crtc_clock *= 2;
2012b992d78SDamon Ding mode->hdisplay = hactive * 2;
2022b992d78SDamon Ding mode->hsync_start = mode->hdisplay + hfp * 2;
2032b992d78SDamon Ding mode->hsync_end = mode->hsync_start + hsync * 2;
2042b992d78SDamon Ding mode->htotal = mode->hsync_end + hbp * 2;
2052b992d78SDamon Ding }
2062b992d78SDamon Ding
207690e9ed1SSandy Huang /**
208690e9ed1SSandy Huang * drm_mode_is_420_only - if a given videomode can be only supported in YCBCR420
209690e9ed1SSandy Huang * output format
210690e9ed1SSandy Huang *
211690e9ed1SSandy Huang * @connector: drm connector under action.
212690e9ed1SSandy Huang * @mode: video mode to be tested.
213690e9ed1SSandy Huang *
214690e9ed1SSandy Huang * Returns:
215690e9ed1SSandy Huang * true if the mode can be supported in YCBCR420 format
216690e9ed1SSandy Huang * false if not.
217690e9ed1SSandy Huang */
drm_mode_is_420_only(const struct drm_display_info * display,struct drm_display_mode * mode)218*038b8732SAlgea Cao bool drm_mode_is_420_only(const struct drm_display_info *display,
219690e9ed1SSandy Huang struct drm_display_mode *mode)
220690e9ed1SSandy Huang {
221690e9ed1SSandy Huang u8 vic = drm_match_cea_mode(mode);
222690e9ed1SSandy Huang
223690e9ed1SSandy Huang return test_bit(vic, display->hdmi.y420_vdb_modes);
224690e9ed1SSandy Huang }
225690e9ed1SSandy Huang
226690e9ed1SSandy Huang /**
227690e9ed1SSandy Huang * drm_mode_is_420_also - if a given videomode can be supported in YCBCR420
228690e9ed1SSandy Huang * output format also (along with RGB/YCBCR444/422)
229690e9ed1SSandy Huang *
230690e9ed1SSandy Huang * @display: display under action.
231690e9ed1SSandy Huang * @mode: video mode to be tested.
232690e9ed1SSandy Huang *
233690e9ed1SSandy Huang * Returns:
234690e9ed1SSandy Huang * true if the mode can be support YCBCR420 format
235690e9ed1SSandy Huang * false if not.
236690e9ed1SSandy Huang */
drm_mode_is_420_also(const struct drm_display_info * display,struct drm_display_mode * mode)237*038b8732SAlgea Cao bool drm_mode_is_420_also(const struct drm_display_info *display,
238690e9ed1SSandy Huang struct drm_display_mode *mode)
239690e9ed1SSandy Huang {
240690e9ed1SSandy Huang u8 vic = drm_match_cea_mode(mode);
241690e9ed1SSandy Huang
242690e9ed1SSandy Huang return test_bit(vic, display->hdmi.y420_cmdb_modes);
243690e9ed1SSandy Huang }
244690e9ed1SSandy Huang
245690e9ed1SSandy Huang /**
246690e9ed1SSandy Huang * drm_mode_is_420 - if a given videomode can be supported in YCBCR420
247690e9ed1SSandy Huang * output format
248690e9ed1SSandy Huang *
249690e9ed1SSandy Huang * @display: display under action.
250690e9ed1SSandy Huang * @mode: video mode to be tested.
251690e9ed1SSandy Huang *
252690e9ed1SSandy Huang * Returns:
253690e9ed1SSandy Huang * true if the mode can be supported in YCBCR420 format
254690e9ed1SSandy Huang * false if not.
255690e9ed1SSandy Huang */
drm_mode_is_420(const struct drm_display_info * display,struct drm_display_mode * mode)256690e9ed1SSandy Huang bool drm_mode_is_420(const struct drm_display_info *display,
257690e9ed1SSandy Huang struct drm_display_mode *mode)
258690e9ed1SSandy Huang {
259690e9ed1SSandy Huang return drm_mode_is_420_only(display, mode) ||
260690e9ed1SSandy Huang drm_mode_is_420_also(display, mode);
261690e9ed1SSandy Huang }
262690e9ed1SSandy Huang
display_rect_calc_scale(int src,int dst)263690e9ed1SSandy Huang static int display_rect_calc_scale(int src, int dst)
264690e9ed1SSandy Huang {
265690e9ed1SSandy Huang int scale = 0;
266690e9ed1SSandy Huang
267690e9ed1SSandy Huang if (WARN_ON(src < 0 || dst < 0))
268690e9ed1SSandy Huang return -EINVAL;
269690e9ed1SSandy Huang
270690e9ed1SSandy Huang if (dst == 0)
271690e9ed1SSandy Huang return 0;
272690e9ed1SSandy Huang
273690e9ed1SSandy Huang src <<= 16;
274690e9ed1SSandy Huang
275690e9ed1SSandy Huang if (src > (dst << 16))
276690e9ed1SSandy Huang return DIV_ROUND_UP(src, dst);
277690e9ed1SSandy Huang else
278690e9ed1SSandy Huang scale = src / dst;
279690e9ed1SSandy Huang
280690e9ed1SSandy Huang return scale;
281690e9ed1SSandy Huang }
282690e9ed1SSandy Huang
display_rect_calc_hscale(struct display_rect * src,struct display_rect * dst,int min_hscale,int max_hscale)283690e9ed1SSandy Huang int display_rect_calc_hscale(struct display_rect *src, struct display_rect *dst,
284690e9ed1SSandy Huang int min_hscale, int max_hscale)
285690e9ed1SSandy Huang {
286690e9ed1SSandy Huang int hscale = display_rect_calc_scale(src->w, dst->w);
287690e9ed1SSandy Huang
288690e9ed1SSandy Huang if (hscale < 0 || dst->w == 0)
289690e9ed1SSandy Huang return hscale;
290690e9ed1SSandy Huang
291690e9ed1SSandy Huang if (hscale < min_hscale || hscale > max_hscale)
292690e9ed1SSandy Huang return -ERANGE;
293690e9ed1SSandy Huang
294690e9ed1SSandy Huang return hscale;
295690e9ed1SSandy Huang }
296690e9ed1SSandy Huang
display_rect_calc_vscale(struct display_rect * src,struct display_rect * dst,int min_vscale,int max_vscale)297690e9ed1SSandy Huang int display_rect_calc_vscale(struct display_rect *src, struct display_rect *dst,
298690e9ed1SSandy Huang int min_vscale, int max_vscale)
299690e9ed1SSandy Huang {
300690e9ed1SSandy Huang int vscale = display_rect_calc_scale(src->h, dst->h);
301690e9ed1SSandy Huang
302690e9ed1SSandy Huang if (vscale < 0 || dst->h == 0)
303690e9ed1SSandy Huang return vscale;
304690e9ed1SSandy Huang
305690e9ed1SSandy Huang if (vscale < min_vscale || vscale > max_vscale)
306690e9ed1SSandy Huang return -ERANGE;
307690e9ed1SSandy Huang
308690e9ed1SSandy Huang return vscale;
309690e9ed1SSandy Huang }
310690e9ed1SSandy Huang
311