// SPDX-License-Identifier: MIT
/*
* Copyright © 2018 Intel Corporation
*
* Author: Gaurav K Singh <gaurav.k.singh@intel.com>
* Manasi Navare <manasi.d.navare@intel.com>
*/
#include <linux/limits.h>
#include <drm/display/drm_dsc_helper.h>
#include "i915_drv.h"
#include "i915_reg.h"
#include "intel_crtc.h"
#include "intel_de.h"
#include "intel_display_types.h"
#include "intel_dsi.h"
#include "intel_qp_tables.h"
#include "intel_vdsc.h"
#include "intel_vdsc_regs.h"
bool intel_dsc_source_support(const struct intel_crtc_state *crtc_state)
{
const struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
struct drm_i915_private *i915 = to_i915(crtc->base.dev);
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
if (!HAS_DSC(i915))
return false;
if (DISPLAY_VER(i915) == 11 && cpu_transcoder == TRANSCODER_A)
return false;
return true;
}
static bool is_pipe_dsc(struct intel_crtc *crtc, enum transcoder cpu_transcoder)
{
struct drm_i915_private *i915 = to_i915(crtc->base.dev);
if (DISPLAY_VER(i915) >= 12)
return true;
if (cpu_transcoder == TRANSCODER_EDP ||
cpu_transcoder == TRANSCODER_DSI_0 ||
cpu_transcoder == TRANSCODER_DSI_1)
return false;
/* There's no pipe A DSC engine on ICL */
drm_WARN_ON(&i915->drm, crtc->pipe == PIPE_A);
return true;
}
static void
calculate_rc_params(struct drm_dsc_config *vdsc_cfg)
{
int bpc = vdsc_cfg->bits_per_component;
int bpp = vdsc_cfg->bits_per_pixel >> 4;
static const s8 ofs_und6[] = {
0, -2, -2, -4, -6, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12
};
static const s8 ofs_und8[] = {
2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -10, -12, -12, -12
};
static const s8 ofs_und12[] = {
2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -10, -12, -12, -12
};
static const s8 ofs_und15[] = {
10, 8, 6, 4, 2, 0, -2, -4, -6, -8, -10, -10, -12, -12, -12
};
int qp_bpc_modifier = (bpc - 8) * 2;
u32 res, buf_i, bpp_i;
if (vdsc_cfg->slice_height >= 8)
vdsc_cfg->first_line_bpg_offset =
12 + DIV_ROUND_UP((9 * min(34, vdsc_cfg->slice_height - 8)), 100);
else
vdsc_cfg->first_line_bpg_offset = 2 * (vdsc_cfg->slice_height - 1);
/* Our hw supports only 444 modes as of today */
if (bpp >= 12)
vdsc_cfg->initial_offset = 2048;
else if (bpp >= 10)
vdsc_cfg->initial_offset = 5632 - DIV_ROUND_UP(((bpp - 10) * 3584), 2);
else if (bpp >= 8)
vdsc_cfg->initial_offset = 6144 - DIV_ROUND_UP(((bpp - 8) * 512), 2);
else
vdsc_cfg->initial_offset = 6144;
/* initial_xmit_delay = rc_model_size/2/compression_bpp */
vdsc_cfg->initial_xmit_delay = DIV_ROUND_UP(DSC_RC_MODEL_SIZE_CONST, 2 * bpp);
vdsc_cfg->flatness_min_qp = 3 + qp_bpc_modifier;
vdsc_cfg->flatness_max_qp = 12 + qp_bpc_modifier;
vdsc_cfg->rc_quant_incr_limit0 = 11 + qp_bpc_modifier;
vdsc_cfg->rc_quant_incr_limit1 = 11 + qp_bpc_modifier;
bpp_i = (2 * (bpp - 6));
for (buf_i = 0; buf_i < DSC_NUM_BUF_RANGES; buf_i++) {
u8 range_bpg_offset;
/* Read range_minqp and range_max_qp from qp tables */
vdsc_cfg->rc_range_params[buf_i].range_min_qp =
intel_lookup_range_min_qp(bpc, buf_i, bpp_i, vdsc_cfg->native_420);
vdsc_cfg->rc_range_params[buf_i].range_max_qp =
intel_lookup_range_max_qp(bpc, buf_i, bpp_i, vdsc_cfg->native_420);
/* Calculate range_bpg_offset */
if (bpp <= 6) {
range_bpg_offset = ofs_und6[buf_i];
} else if (bpp <= 8) {
res = DIV_ROUND_UP(((bpp - 6) * (ofs_und8[buf_i] - ofs_und6[buf_i])), 2);
range_bpg_offset = ofs_und6[buf_i] + res;
} else if (bpp <= 12) {
range_bpg_offset = ofs_und8[buf_i];
} else if (bpp <= 15) {
res = DIV_ROUND_UP(((bpp - 12) * (ofs_und15[buf_i] - ofs_und12[buf_i])), 3);
range_bpg_offset = ofs_und12[buf_i] + res;
} else {
range_bpg_offset = ofs_und15[buf_i];
}
vdsc_cfg->rc_range_params[buf_i].range_bpg_offset =
range_bpg_offset & DSC_RANGE_BPG_OFFSET_MASK;
}
}
static int intel_dsc_slice_dimensions_valid(struct intel_crtc_state *pipe_config,
struct drm_dsc_config *vdsc_cfg)
{
if (pipe_config->