277 lines
6.8 KiB
C
277 lines
6.8 KiB
C
/*
|
|
* (C) Copyright 2023 Rockchip Electronics Co., Ltd
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*/
|
|
|
|
#include <linux/hdmi.h>
|
|
#include <linux/compat.h>
|
|
#include "rockchip_display.h"
|
|
#include <spl_display.h>
|
|
|
|
#define RK_BLK_SIZE 512
|
|
#define BMP_PROCESSED_FLAG 8399
|
|
|
|
static uint32_t crc32_table[256];
|
|
|
|
void rockchip_display_make_crc32_table(void)
|
|
{
|
|
uint32_t c;
|
|
int n, k;
|
|
unsigned long poly; /* polynomial exclusive-or pattern */
|
|
/* terms of polynomial defining this crc (except x^32): */
|
|
static const char p[] = {0, 1, 2, 4, 5, 7, 8, 10, 11, 12, 16, 22, 23, 26};
|
|
|
|
/* make exclusive-or pattern from polynomial (0xedb88320L) */
|
|
poly = 0L;
|
|
for (n = 0; n < sizeof(p) / sizeof(char); n++)
|
|
poly |= 1L << (31 - p[n]);
|
|
|
|
for (n = 0; n < 256; n++) {
|
|
c = (unsigned long)n;
|
|
for (k = 0; k < 8; k++)
|
|
c = c & 1 ? poly ^ (c >> 1) : c >> 1;
|
|
crc32_table[n] = cpu_to_le32(c);
|
|
}
|
|
}
|
|
|
|
uint32_t rockchip_display_crc32c_cal(unsigned char *data, int length)
|
|
{
|
|
int i;
|
|
uint32_t crc;
|
|
crc = 0xFFFFFFFF;
|
|
|
|
for (i = 0; i < length; i++) {
|
|
crc = crc32_table[(crc ^ *data) & 0xff] ^ (crc >> 8);
|
|
data++;
|
|
}
|
|
|
|
return crc ^ 0xffffffff;
|
|
}
|
|
|
|
/**
|
|
* drm_mode_max_resolution_filter - mark modes out of vop max resolution
|
|
* @edid_data: structure store mode list
|
|
* @max_output: vop max output resolution
|
|
*/
|
|
void drm_mode_max_resolution_filter(struct hdmi_edid_data *edid_data,
|
|
struct vop_rect *max_output)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < edid_data->modes; i++) {
|
|
if (edid_data->mode_buf[i].hdisplay > max_output->width ||
|
|
edid_data->mode_buf[i].vdisplay > max_output->height)
|
|
edid_data->mode_buf[i].invalid = true;
|
|
}
|
|
}
|
|
|
|
int drm_mode_vrefresh(const struct drm_display_mode *mode)
|
|
{
|
|
int refresh = 0;
|
|
unsigned int calc_val;
|
|
|
|
if (mode->vrefresh > 0) {
|
|
refresh = mode->vrefresh;
|
|
} else if (mode->htotal > 0 && mode->vtotal > 0) {
|
|
int vtotal;
|
|
|
|
vtotal = mode->vtotal;
|
|
/* work out vrefresh the value will be x1000 */
|
|
calc_val = (mode->clock * 1000);
|
|
calc_val /= mode->htotal;
|
|
refresh = (calc_val + vtotal / 2) / vtotal;
|
|
|
|
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
|
|
refresh *= 2;
|
|
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
|
refresh /= 2;
|
|
if (mode->vscan > 1)
|
|
refresh /= mode->vscan;
|
|
}
|
|
return refresh;
|
|
}
|
|
|
|
/**
|
|
* drm_mode_set_crtcinfo - set CRTC modesetting timing parameters
|
|
* @p: mode
|
|
* @adjust_flags: a combination of adjustment flags
|
|
*
|
|
* Setup the CRTC modesetting timing parameters for @p, adjusting if necessary.
|
|
*
|
|
* - The CRTC_INTERLACE_HALVE_V flag can be used to halve vertical timings of
|
|
* interlaced modes.
|
|
* - The CRTC_STEREO_DOUBLE flag can be used to compute the timings for
|
|
* buffers containing two eyes (only adjust the timings when needed, eg. for
|
|
* "frame packing" or "side by side full").
|
|
* - The CRTC_NO_DBLSCAN and CRTC_NO_VSCAN flags request that adjustment *not*
|
|
* be performed for doublescan and vscan > 1 modes respectively.
|
|
*/
|
|
void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
|
|
{
|
|
if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) == DRM_MODE_TYPE_BUILTIN))
|
|
return;
|
|
|
|
p->crtc_clock = p->clock;
|
|
p->crtc_hdisplay = p->hdisplay;
|
|
p->crtc_hsync_start = p->hsync_start;
|
|
p->crtc_hsync_end = p->hsync_end;
|
|
p->crtc_htotal = p->htotal;
|
|
p->crtc_hskew = p->hskew;
|
|
p->crtc_vdisplay = p->vdisplay;
|
|
p->crtc_vsync_start = p->vsync_start;
|
|
p->crtc_vsync_end = p->vsync_end;
|
|
p->crtc_vtotal = p->vtotal;
|
|
|
|
if (p->flags & DRM_MODE_FLAG_INTERLACE) {
|
|
if (adjust_flags & CRTC_INTERLACE_HALVE_V) {
|
|
p->crtc_vdisplay /= 2;
|
|
p->crtc_vsync_start /= 2;
|
|
p->crtc_vsync_end /= 2;
|
|
p->crtc_vtotal /= 2;
|
|
}
|
|
}
|
|
|
|
if (!(adjust_flags & CRTC_NO_DBLSCAN)) {
|
|
if (p->flags & DRM_MODE_FLAG_DBLSCAN) {
|
|
p->crtc_vdisplay *= 2;
|
|
p->crtc_vsync_start *= 2;
|
|
p->crtc_vsync_end *= 2;
|
|
p->crtc_vtotal *= 2;
|
|
}
|
|
}
|
|
|
|
if (!(adjust_flags & CRTC_NO_VSCAN)) {
|
|
if (p->vscan > 1) {
|
|
p->crtc_vdisplay *= p->vscan;
|
|
p->crtc_vsync_start *= p->vscan;
|
|
p->crtc_vsync_end *= p->vscan;
|
|
p->crtc_vtotal *= p->vscan;
|
|
}
|
|
}
|
|
|
|
if (adjust_flags & CRTC_STEREO_DOUBLE) {
|
|
unsigned int layout = p->flags & DRM_MODE_FLAG_3D_MASK;
|
|
|
|
switch (layout) {
|
|
case DRM_MODE_FLAG_3D_FRAME_PACKING:
|
|
p->crtc_clock *= 2;
|
|
p->crtc_vdisplay += p->crtc_vtotal;
|
|
p->crtc_vsync_start += p->crtc_vtotal;
|
|
p->crtc_vsync_end += p->crtc_vtotal;
|
|
p->crtc_vtotal += p->crtc_vtotal;
|
|
break;
|
|
}
|
|
}
|
|
|
|
p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay);
|
|
p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal);
|
|
p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay);
|
|
p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal);
|
|
}
|
|
|
|
/**
|
|
* drm_mode_is_420_only - if a given videomode can be only supported in YCBCR420
|
|
* output format
|
|
*
|
|
* @connector: drm connector under action.
|
|
* @mode: video mode to be tested.
|
|
*
|
|
* Returns:
|
|
* true if the mode can be supported in YCBCR420 format
|
|
* false if not.
|
|
*/
|
|
static bool drm_mode_is_420_only(const struct drm_display_info *display,
|
|
struct drm_display_mode *mode)
|
|
{
|
|
u8 vic = drm_match_cea_mode(mode);
|
|
|
|
return test_bit(vic, display->hdmi.y420_vdb_modes);
|
|
}
|
|
|
|
/**
|
|
* drm_mode_is_420_also - if a given videomode can be supported in YCBCR420
|
|
* output format also (along with RGB/YCBCR444/422)
|
|
*
|
|
* @display: display under action.
|
|
* @mode: video mode to be tested.
|
|
*
|
|
* Returns:
|
|
* true if the mode can be support YCBCR420 format
|
|
* false if not.
|
|
*/
|
|
static bool drm_mode_is_420_also(const struct drm_display_info *display,
|
|
struct drm_display_mode *mode)
|
|
{
|
|
u8 vic = drm_match_cea_mode(mode);
|
|
|
|
return test_bit(vic, display->hdmi.y420_cmdb_modes);
|
|
}
|
|
|
|
/**
|
|
* drm_mode_is_420 - if a given videomode can be supported in YCBCR420
|
|
* output format
|
|
*
|
|
* @display: display under action.
|
|
* @mode: video mode to be tested.
|
|
*
|
|
* Returns:
|
|
* true if the mode can be supported in YCBCR420 format
|
|
* false if not.
|
|
*/
|
|
bool drm_mode_is_420(const struct drm_display_info *display,
|
|
struct drm_display_mode *mode)
|
|
{
|
|
return drm_mode_is_420_only(display, mode) ||
|
|
drm_mode_is_420_also(display, mode);
|
|
}
|
|
|
|
static int display_rect_calc_scale(int src, int dst)
|
|
{
|
|
int scale = 0;
|
|
|
|
if (WARN_ON(src < 0 || dst < 0))
|
|
return -EINVAL;
|
|
|
|
if (dst == 0)
|
|
return 0;
|
|
|
|
src <<= 16;
|
|
|
|
if (src > (dst << 16))
|
|
return DIV_ROUND_UP(src, dst);
|
|
else
|
|
scale = src / dst;
|
|
|
|
return scale;
|
|
}
|
|
|
|
int display_rect_calc_hscale(struct display_rect *src, struct display_rect *dst,
|
|
int min_hscale, int max_hscale)
|
|
{
|
|
int hscale = display_rect_calc_scale(src->w, dst->w);
|
|
|
|
if (hscale < 0 || dst->w == 0)
|
|
return hscale;
|
|
|
|
if (hscale < min_hscale || hscale > max_hscale)
|
|
return -ERANGE;
|
|
|
|
return hscale;
|
|
}
|
|
|
|
int display_rect_calc_vscale(struct display_rect *src, struct display_rect *dst,
|
|
int min_vscale, int max_vscale)
|
|
{
|
|
int vscale = display_rect_calc_scale(src->h, dst->h);
|
|
|
|
if (vscale < 0 || dst->h == 0)
|
|
return vscale;
|
|
|
|
if (vscale < min_vscale || vscale > max_vscale)
|
|
return -ERANGE;
|
|
|
|
return vscale;
|
|
}
|
|
|