// 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; }