unplugged-kernel/drivers/misc/mediatek/video/mt6779/dispsys/ddp_ovl_wcg.c

375 lines
8.0 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include "ddp_ovl_wcg.h"
#include "primary_display.h"
#include "disp_lcm.h"
#include "disp_helper.h"
#include "ddp_reg.h"
#include "disp_drv_log.h"
#define CSC_COEF_NUM 9
static u32 sRGB_to_DCI_P3[CSC_COEF_NUM] = {
215603, 46541, 0,
8702, 253442, 0,
4478, 18979, 238687
};
static u32 DCI_P3_to_sRGB[CSC_COEF_NUM] = {
321111, -58967, 0,
-11025, 273169, 0,
-5148, -20614, 287906
};
enum ovl_colorspace {
OVL_SRGB = 0,
OVL_P3,
OVL_CS_NUM,
OVL_CS_UNKNOWN,
};
enum ovl_transfer {
OVL_GAMMA2 = 0,
OVL_GAMMA2_2,
OVL_LINEAR,
OVL_GAMMA_NUM,
OVL_GAMMA_UNKNOWN,
};
static char *ovl_colorspace_str(enum ovl_colorspace cs)
{
switch (cs) {
case OVL_SRGB:
return "OVL_SRGB";
case OVL_P3:
return "OVL_P3";
default:
break;
}
return "unknown ovl colorspace";
}
static char *ovl_transfer_str(enum ovl_transfer xfr)
{
switch (xfr) {
case OVL_GAMMA2:
return "OVL_GAMMA2";
case OVL_GAMMA2_2:
return "OVL_GAMMA2_2";
case OVL_LINEAR:
return "OVL_LINEAR";
default:
break;
}
return "unknown ovl transfer";
}
char *lcm_color_mode_str(enum android_color_mode cm)
{
switch (cm) {
case HAL_COLOR_MODE_NATIVE:
return "NATIVE";
case HAL_COLOR_MODE_STANDARD_BT601_625:
return "BT601_625";
case HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED:
return "BT601_625_UNADJUSTED";
case HAL_COLOR_MODE_STANDARD_BT601_525:
return "BT601_525";
case HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED:
return "BT601_525_UNADJUSTED";
case HAL_COLOR_MODE_STANDARD_BT709:
return "BT709";
case HAL_COLOR_MODE_DCI_P3:
return "DCI_P3";
case HAL_COLOR_MODE_SRGB:
return "SRGB";
case HAL_COLOR_MODE_ADOBE_RGB:
return "ADOBE_RGB";
case HAL_COLOR_MODE_DISPLAY_P3:
return "DISPLAY_P3";
default:
break;
}
return "unknown lcm color mode";
}
static enum android_dataspace ovl_map_lcm_color_mode(enum android_color_mode cm)
{
enum android_dataspace ds = HAL_DATASPACE_SRGB;
switch (cm) {
case HAL_COLOR_MODE_DISPLAY_P3:
ds = HAL_DATASPACE_DISPLAY_P3;
break;
default:
ds = HAL_DATASPACE_SRGB;
break;
}
return ds;
}
static enum ovl_colorspace ovl_map_cs(enum android_dataspace ds)
{
enum ovl_colorspace cs = OVL_SRGB;
switch (ds & HAL_DATASPACE_STANDARD_MASK) {
case HAL_DATASPACE_STANDARD_DCI_P3:
cs = OVL_P3;
break;
case HAL_DATASPACE_STANDARD_ADOBE_RGB:
DISP_PR_ERR("%s: ovl get cs ADOBE_RGB\n", __func__);
/* fall through */
case HAL_DATASPACE_STANDARD_BT2020:
DISP_PR_ERR("%s: ovl does not support BT2020\n", __func__);
/* fall through */
default:
cs = OVL_SRGB;
break;
}
return cs;
}
static enum ovl_transfer ovl_map_transfer(enum android_dataspace ds)
{
enum ovl_transfer xfr = OVL_GAMMA_UNKNOWN;
switch (ds & HAL_DATASPACE_TRANSFER_MASK) {
case HAL_DATASPACE_TRANSFER_LINEAR:
xfr = OVL_LINEAR;
break;
case HAL_DATASPACE_TRANSFER_GAMMA2_6:
case HAL_DATASPACE_TRANSFER_GAMMA2_8:
DISP_PR_ERR("%s: ovl does not support gamma 2.6/2.8\n",
__func__);
/* fall through */
case HAL_DATASPACE_TRANSFER_ST2084:
case HAL_DATASPACE_TRANSFER_HLG:
DISP_PR_ERR("%s: HDR transfer\n", __func__);
/* fall through */
default:
xfr = OVL_GAMMA2_2;
break;
}
return xfr;
}
static u32 *get_ovl_csc(enum ovl_colorspace in, enum ovl_colorspace out)
{
static u32 *ovl_csc[OVL_CS_NUM][OVL_CS_NUM];
static bool inited;
if ((unsigned int)in >= OVL_CS_UNKNOWN ||
(unsigned int)out >= OVL_CS_UNKNOWN)
return 0;
if (inited)
goto done;
ovl_csc[OVL_SRGB][OVL_P3] = sRGB_to_DCI_P3;
ovl_csc[OVL_P3][OVL_SRGB] = DCI_P3_to_sRGB;
inited = true;
done:
return ovl_csc[in][out];
}
bool is_ovl_standard(enum android_dataspace ds)
{
enum android_dataspace std = ds & HAL_DATASPACE_STANDARD_MASK;
bool ret = false;
if (!disp_helper_get_option(DISP_OPT_OVL_WCG) && is_ovl_wcg(ds))
return ret;
switch (std) {
case HAL_DATASPACE_STANDARD_BT2020:
case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
ret = false;
break;
default:
ret = true;
break;
}
return ret;
}
bool is_ovl_wcg(enum android_dataspace ds)
{
bool ret = false;
switch (ds) {
case HAL_DATASPACE_V0_SCRGB:
case HAL_DATASPACE_V0_SCRGB_LINEAR:
case HAL_DATASPACE_DISPLAY_P3:
ret = true;
break;
default:
ret = false;
break;
}
return ret;
}
bool frame_has_wcg(struct disp_ddp_path_config *pconfig)
{
s32 i = 0;
struct OVL_CONFIG_STRUCT *c = NULL;
for (i = 0; i < TOTAL_OVL_LAYER_NUM; i++) {
c = &pconfig->ovl_config[i];
if (!c->layer_en)
continue;
if (is_ovl_wcg(c->ds))
return true;
}
return false;
}
static int ovl_do_csc(struct OVL_CONFIG_STRUCT *c,
enum android_dataspace lcm_ds, u32 *wcg_en, void *qhandle)
{
enum ovl_colorspace in = OVL_SRGB, out = OVL_SRGB;
unsigned int fld = 0;
unsigned long reg = 0;
unsigned long baddr = ovl_base_addr(c->module);
s32 i = 0;
u32 *csc = NULL;
bool en = false;
in = ovl_map_cs(c->ds);
out = ovl_map_cs(lcm_ds);
DISPDBG("%s:L%d:%s->%s\n", __func__, c->layer,
ovl_colorspace_str(in), ovl_colorspace_str(out));
en = in != out;
if (c->ext_layer != -1)
fld = FLD_ELn_CSC_EN(c->ext_layer);
else
fld = FLD_Ln_CSC_EN(c->phy_layer);
*wcg_en |= REG_FLD_VAL(fld, en);
if (!en)
return 0;
csc = get_ovl_csc(in, out);
if (!csc) {
DISP_PR_ERR("%s:L%d:no ovl csc %s to %s, disable csc\n",
__func__, c->layer, ovl_colorspace_str(in),
ovl_colorspace_str(out));
*wcg_en &= ~REG_FLD_VAL(fld, en);
return 0;
}
if (c->ext_layer != -1)
reg = DISP_REG_OVL_ELn_R2R_PARA(c->ext_layer);
else
reg = DISP_REG_OVL_Ln_R2R_PARA(c->phy_layer);
for (i = 0; i < CSC_COEF_NUM; i++)
DISP_REG_SET(qhandle, baddr + reg + 4 * i, csc[i]);
return 0;
}
static int ovl_do_transfer(struct OVL_CONFIG_STRUCT *c,
enum android_dataspace lcm_ds,
u32 *wcg_en, u32 *gamma_sel)
{
enum ovl_transfer xfr_in = OVL_GAMMA2_2, xfr_out = OVL_GAMMA2_2;
enum ovl_colorspace cs_in = OVL_CS_UNKNOWN, cs_out = OVL_CS_UNKNOWN;
unsigned int fld = 0;
bool en = false;
xfr_in = ovl_map_transfer(c->ds);
xfr_out = ovl_map_transfer(lcm_ds);
cs_in = ovl_map_cs(c->ds);
cs_out = ovl_map_cs(lcm_ds);
DISPDBG("%s:L%d:%s->%s\n", __func__, c->layer,
ovl_transfer_str(xfr_in), ovl_transfer_str(xfr_out));
en = xfr_in != OVL_LINEAR && (xfr_in != xfr_out || cs_in != cs_out);
if (c->ext_layer != -1)
fld = FLD_ELn_IGAMMA_EN(c->ext_layer);
else
fld = FLD_Ln_IGAMMA_EN(c->phy_layer);
*wcg_en |= REG_FLD_VAL(fld, en);
if (en) {
if (c->ext_layer != -1)
fld = FLD_ELn_IGAMMA_SEL(c->ext_layer);
else
fld = FLD_Ln_IGAMMA_SEL(c->phy_layer);
*gamma_sel |= REG_FLD_VAL(fld, xfr_in);
}
en = xfr_out != OVL_LINEAR && (xfr_in != xfr_out || cs_in != cs_out);
if (c->ext_layer != -1)
fld = FLD_ELn_GAMMA_EN(c->ext_layer);
else
fld = FLD_Ln_GAMMA_EN(c->phy_layer);
*wcg_en |= REG_FLD_VAL(fld, en);
if (en) {
if (c->ext_layer != -1)
fld = FLD_ELn_GAMMA_SEL(c->ext_layer);
else
fld = FLD_Ln_GAMMA_SEL(c->phy_layer);
*gamma_sel |= REG_FLD_VAL(fld, xfr_out);
}
return 0;
}
enum android_color_mode ovl_get_lcm_color_mode(struct disp_lcm_handle *plcm)
{
if (!(plcm && plcm->params))
return HAL_COLOR_MODE_NATIVE;
return plcm->params->lcm_color_mode;
}
int ovl_color_manage(enum DISP_MODULE_ENUM module,
struct disp_ddp_path_config *pconfig, void *qhandle)
{
s32 i = 0;
struct OVL_CONFIG_STRUCT *c = NULL;
u32 wcg_en = 0, gamma_sel = 0;
enum android_color_mode lcm_cm;
enum android_dataspace lcm_ds;
unsigned long baddr = ovl_base_addr(module);
if (!disp_helper_get_option(DISP_OPT_OVL_WCG))
goto done;
lcm_cm = ovl_get_lcm_color_mode(primary_get_lcm());
lcm_ds = ovl_map_lcm_color_mode(lcm_cm);
for (i = 0; i < TOTAL_OVL_LAYER_NUM; i++) {
c = &pconfig->ovl_config[i];
if (!c->layer_en)
continue;
if (c->module != module)
continue;
DISPDBG("%s:L%d:0x%08x->0x%08x\n",
__func__, c->layer, c->ds, lcm_ds);
ovl_do_transfer(c, lcm_ds, &wcg_en, &gamma_sel);
ovl_do_csc(c, lcm_ds, &wcg_en, qhandle);
}
done:
DISP_REG_SET(qhandle, baddr + DISP_REG_OVL_WCG_CFG1, wcg_en);
DISP_REG_SET(qhandle, baddr + DISP_REG_OVL_WCG_CFG2, gamma_sel);
return 0;
}