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