From 921f8a026f4d6b8d497a27f8d12aea74a02d412f Mon Sep 17 00:00:00 2001 From: ChiHun Won Date: Mon, 26 Nov 2018 16:16:43 +0900 Subject: [PATCH] [RAMEN9610-9076] fbdev: dpu20: added color transform for night light Change-Id: I823222e9227cd0045df614e7b79eeac5a2f357ba Signed-off-by: ChiHun Won --- drivers/video/fbdev/exynos/dpu20/decon.h | 88 ++++++++ drivers/video/fbdev/exynos/dpu20/decon_core.c | 194 ++++++++++++++++++ drivers/video/fbdev/exynos/dpu20/dqe.h | 4 + drivers/video/fbdev/exynos/dpu20/dqe_drv.c | 110 ++++++++++ 4 files changed, 396 insertions(+) diff --git a/drivers/video/fbdev/exynos/dpu20/decon.h b/drivers/video/fbdev/exynos/dpu20/decon.h index a14945128e64..010ae05d47fb 100644 --- a/drivers/video/fbdev/exynos/dpu20/decon.h +++ b/drivers/video/fbdev/exynos/dpu20/decon.h @@ -309,6 +309,82 @@ enum dpp_hdr_standard { DPP_HDR_HLG, }; +/* HAL color mode */ +enum HAL_color_mode { + HAL_COLOR_MODE_NATIVE = 0, + HAL_COLOR_MODE_STANDARD_BT601_625 = 1, + HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED = 2, + HAL_COLOR_MODE_STANDARD_BT601_525 = 3, + HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED = 4, + HAL_COLOR_MODE_STANDARD_BT709 = 5, + HAL_COLOR_MODE_DCI_P3 = 6, + HAL_COLOR_MODE_SRGB = 7, + HAL_COLOR_MODE_ADOBE_RGB = 8, + HAL_COLOR_MODE_DISPLAY_P3 = 9, +}; + +/* HAL intent info */ +enum HAL_intent_info{ + HAL_RENDER_INTENT_COLORIMETRIC = 0, + HAL_RENDER_INTENT_ENHANCE = 1, + HAL_RENDER_INTENT_TONE_MAP_COLORIMETRIC = 2, + HAL_RENDER_INTENT_TONE_MAP_ENHANCE = 3, +}; + +/* HAL color transform*/ +enum HAL_color_transform{ + HAL_COLOR_TRANSFORM_IDENTITY = 0, + HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX = 1, + HAL_COLOR_TRANSFORM_VALUE_INVERSE = 2, + HAL_COLOR_TRANSFORM_GRAYSCALE = 3, + HAL_COLOR_TRANSFORM_CORRECT_PROTANOPIA = 4, + HAL_COLOR_TRANSFORM_CORRECT_DEUTERANOPIA = 5, + HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA = 6, +}; + +struct decon_color_mode_info { + int index; + u32 color_mode; +}; + +/* decon supported color mode */ +enum decon_supported_color_mode { + DECON_COLOR_MODE_NATIVE = 0, + DECON_COLOR_MODE_SRGB, + DECON_COLOR_MODE_DCI_P3, + DECON_COLOR_MODE_NUM_MAX, +}; + +/* intents num and information in each color mode*/ +struct decon_render_intents_num_info { + u32 color_mode; + u32 render_intent_num; +}; + +struct decon_render_intent_info { + u32 color_mode; + u32 index; + u32 render_intent; +}; + +/* decon supported intent info */ +enum decon_supported_intent_info { + DECON_INTENT_COLORIMETRIC = 0, + DECON_INTENT_ENHANCE, + DECON_INTENT_NUM_MAX, +}; + +#define DECON_MATRIX_ELEMENT_NUM 16 +struct decon_color_transform_info { + u32 hint; + int matrix[DECON_MATRIX_ELEMENT_NUM]; +}; + +struct decon_color_mode_with_render_intent_info { + u32 color_mode; + u32 render_intent; +}; + struct decon_clocks { unsigned long decon[CLK_ID_DPLL + 1]; }; @@ -1367,4 +1443,16 @@ int decon_update_last_regs(struct decon_device *decon, /* DPU aclk */ #define EXYNOS_DPU_GET_ACLK _IOR('F', 500, u32) + +/* COLOR Mode */ +#define EXYNOS_GET_COLOR_MODE_NUM _IOW('F', 600, __u32) +#define EXYNOS_GET_COLOR_MODE _IOW('F', 601, struct decon_color_mode_info) +#define EXYNOS_SET_COLOR_MODE _IOW('F', 602, __u32) + +#define EXYNOS_GET_RENDER_INTENTS_NUM _IOW('F', 610, struct decon_render_intents_num_info) +#define EXYNOS_GET_RENDER_INTENT _IOW('F', 611, struct decon_render_intent_info) + +#define EXYNOS_SET_COLOR_TRANSFORM _IOW('F', 612, struct decon_color_transform_info) +#define EXYNOS_SET_COLOR_MODE_WITH_RENDER_INTENT _IOW('F', 613, struct decon_color_mode_with_render_intent_info) + #endif /* ___SAMSUNG_DECON_H__ */ diff --git a/drivers/video/fbdev/exynos/dpu20/decon_core.c b/drivers/video/fbdev/exynos/dpu20/decon_core.c index ac391511c00e..1a539d0fc7f4 100644 --- a/drivers/video/fbdev/exynos/dpu20/decon_core.c +++ b/drivers/video/fbdev/exynos/dpu20/decon_core.c @@ -2625,6 +2625,103 @@ static int decon_get_hdr_capa_info(struct decon_device *decon, } +static int decon_get_color_mode(struct decon_device *decon, + struct decon_color_mode_info *color_mode) +{ + int ret = 0; + + mutex_lock(&decon->lock); + + switch (color_mode->index) { + case 0: + color_mode->color_mode = HAL_COLOR_MODE_NATIVE; + break; + + case 1: + color_mode->color_mode = HAL_COLOR_MODE_SRGB; + break; + + case 2: + color_mode->color_mode = HAL_COLOR_MODE_DCI_P3; + break; + + case 3: + color_mode->color_mode = HAL_COLOR_MODE_DISPLAY_P3; + break; + + default: + decon_err("%s: queried color mode index is wrong!(%d)\n", + __func__, color_mode->index); + ret = -EINVAL; + break; + } + + decon_dbg("%s +- : %d, %d\n", __func__, + color_mode->index, color_mode->color_mode); + + mutex_unlock(&decon->lock); + + return ret; +} + +static int decon_set_color_mode(struct decon_device *decon, + struct decon_color_mode_info *color_mode) +{ + int ret = 0; + + decon_dbg("%s +-: %d\n", __func__, color_mode->index); + mutex_lock(&decon->lock); +#if 0 + switch (color_mode->index) { + case 0: + color_mode->color_mode = HAL_COLOR_MODE_NATIVE; + break; + + /* TODO: add supporting color mode if necessary */ + + default: + decon_err("%s: color mode index is out of range!(%d)\n", + __func__, color_mode->index); + ret = -EINVAL; + break; + } +#endif + mutex_unlock(&decon->lock); + + return ret; +} + +static int decon_get_render_intent_info(struct decon_device *decon, + struct decon_render_intent_info *intent_info) +{ + int ret = 0; + + mutex_lock(&decon->lock); + + switch (intent_info->index) { + case 0: + intent_info->render_intent = HAL_RENDER_INTENT_COLORIMETRIC; + break; + + case 1: + intent_info->render_intent = HAL_RENDER_INTENT_ENHANCE; + break; + + default: + decon_err("%s: queried intent info index is wrong!(%d)\n", + __func__, intent_info->index); + ret = -EINVAL; + break; + } + + decon_dbg("%s +- : %d, %d\n", __func__, + intent_info->index, intent_info->render_intent); + + mutex_unlock(&decon->lock); + + return ret; +} + static int decon_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { @@ -2648,6 +2745,12 @@ static int decon_ioctl(struct fb_info *info, unsigned int cmd, u32 crc_bit, crc_start; u32 crc_data[2]; u32 pwr; + struct decon_color_mode_info cm_info; + u32 cm_num; + struct decon_render_intents_num_info intents_num_info; + struct decon_render_intent_info intent_info; + struct decon_color_transform_info transform_info; + struct decon_color_mode_with_render_intent_info cm_intent_info; decon_hiber_block_exit(decon); switch (cmd) { @@ -2886,6 +2989,97 @@ static int decon_ioctl(struct fb_info *info, unsigned int cmd, } break; + case EXYNOS_GET_COLOR_MODE_NUM: + decon_dbg("DQE: EXYNOS_GET_COLOR_MODE_NUM\n"); + cm_num = DECON_COLOR_MODE_NUM_MAX; + if (copy_to_user((u32 __user *)arg, &cm_num, sizeof(u32))) + ret = -EFAULT; + break; + + case EXYNOS_GET_COLOR_MODE: + decon_dbg("DQE: EXYNOS_GET_COLOR_MODE\n"); + if (copy_from_user(&cm_info, (struct decon_color_mode_info __user *)arg, + sizeof(struct decon_color_mode_info))) { + ret = -EFAULT; + break; + } + + ret = decon_get_color_mode(decon, &cm_info); + if (ret) + break; + + if (copy_to_user((struct decon_color_mode_info __user *)arg, &cm_info, + sizeof(struct decon_color_mode_info))) { + ret = -EFAULT; + break; + } + break; + + case EXYNOS_SET_COLOR_MODE: + decon_dbg("DQE: EXYNOS_SET_COLOR_MODE\n"); + if (get_user(cm_info.index, (int __user *)arg)) { + ret = -EFAULT; + break; + } + + ret = decon_set_color_mode(decon, &cm_info); + if (ret) + break; + break; + + case EXYNOS_GET_RENDER_INTENTS_NUM: + decon_dbg("DQE: EXYNOS_GET_RENDER_INTENTS_NUM\n"); + intents_num_info.render_intent_num = DECON_INTENT_NUM_MAX; + if (copy_to_user((struct decon_render_intents_num_info __user *)arg, &intents_num_info, + sizeof(struct decon_render_intents_num_info))) { + ret = -EFAULT; + break; + } + break; + + case EXYNOS_GET_RENDER_INTENT: + decon_dbg("DQE: EXYNOS_GET_RENDER_INTENT\n"); + if (copy_from_user(&intent_info, (struct decon_render_intent_info __user *)arg, + sizeof(struct decon_render_intent_info))) { + ret = -EFAULT; + break; + } + + ret = decon_get_render_intent_info(decon, &intent_info); + if (ret) + break; + + if (copy_to_user((struct decon_render_intent_info __user *)arg, &intent_info, + sizeof(struct decon_render_intent_info))) { + ret = -EFAULT; + break; + } + break; + + case EXYNOS_SET_COLOR_MODE_WITH_RENDER_INTENT: + if (copy_from_user(&cm_intent_info, (struct decon_color_mode_with_render_intent_info __user *)arg, + sizeof(struct decon_color_mode_with_render_intent_info))) { + ret = -EFAULT; + break; + } + + decon_dbg("DQE: EXYNOS_SET_COLOR_MOE_WITH_RENDER_INTENT: %d %d\n", + cm_intent_info.color_mode, cm_intent_info.render_intent); + break; + + case EXYNOS_SET_COLOR_TRANSFORM: + if (copy_from_user(&transform_info, (struct decon_color_transform_info __user *)arg, + sizeof(struct decon_color_transform_info))) { + ret = -EFAULT; + break; + } + + decon_dbg("DQE: EXYNOS_SET_COLOR_TRANSFORM: %d\n", transform_info.hint); +#if defined(CONFIG_EXYNOS_DECON_DQE) + decon_dqe_set_color_transform(&transform_info); +#endif + break; + default: ret = -ENOTTY; } diff --git a/drivers/video/fbdev/exynos/dpu20/dqe.h b/drivers/video/fbdev/exynos/dpu20/dqe.h index 39cfa1d961b2..da5814532464 100644 --- a/drivers/video/fbdev/exynos/dpu20/dqe.h +++ b/drivers/video/fbdev/exynos/dpu20/dqe.h @@ -87,6 +87,8 @@ struct dqe_ctx { u32 hsc_on; u32 hsc_control; bool need_udpate; + u32 color_mode; + u32 night_light_on; }; struct dqe_device { @@ -152,4 +154,6 @@ void decon_dqe_enable(struct decon_device *decon); void decon_dqe_disable(struct decon_device *decon); int decon_dqe_create_interface(struct decon_device *decon); +int decon_dqe_set_color_transform(struct decon_color_transform_info *transform); + #endif diff --git a/drivers/video/fbdev/exynos/dpu20/dqe_drv.c b/drivers/video/fbdev/exynos/dpu20/dqe_drv.c index 622e4631766a..43b49d3ad760 100644 --- a/drivers/video/fbdev/exynos/dpu20/dqe_drv.c +++ b/drivers/video/fbdev/exynos/dpu20/dqe_drv.c @@ -32,6 +32,12 @@ u32 hsc_lut[35]; int dqe_log_level = 6; module_param(dqe_log_level, int, 0644); +const u32 nightlight_gamma_tune[3][65] = { + {0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124,128,132,136,140,144,148,152,156,160,164,168,172,176,180,184,188,192,196,200,204,208,212,216,220,224,228,232,236,240,244,248,252,256}, + {0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124,128,132,136,140,144,148,152,156,160,164,168,172,176,180,184,188,192,196,200,204,208,212,216,220,224,228,232,236,240,244,248,252,256}, + {0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124,128,132,136,140,144,148,152,156,160,164,168,172,176,180,184,188,192,196,200,204,208,212,216,220,224,228,232,236,240,244,248,252,256}, +}; + static void dqe_load_context(void) { int i, j; @@ -703,6 +709,110 @@ static struct attribute *dqe_attrs[] = { }; ATTRIBUTE_GROUPS(dqe); +static void dqe_color_transform_make_sfr(int coeff[16], int inR, int inG, int inB, int *outR, int *outG, int *outB) +{ + *outR = (coeff[ 0] * inR) >> 15; + *outR = (*outR & 0x1) ? (*outR >> 1) + 1 : *outR >> 1; + *outG = (coeff[ 5] * inG) >> 15; + *outG = (*outG & 0x1) ? (*outG >> 1) + 1 : *outG >> 1; + *outB = (coeff[10] * inB) >> 15; + *outB = (*outB & 0x1) ? (*outB >> 1) + 1 : *outB >> 1; +} + +static void dqe_color_transform_night_light(int in[16], int out[16]) +{ + int kk, jj; + long long int inverse_i[16] = {60847, 1269, 1269, 0, 4260, 63838, 4260, 0, 429, 429, 60007, 0, 0, 0, 0, 65536}; + + for (kk = 0; kk < 4; kk++) + for (jj = 0; jj < 4; jj++) + out[kk * 4 + jj] = (inverse_i[kk * 4] * in[jj] + inverse_i[kk * 4 + 1] * in[jj + 4] + inverse_i[kk * 4 + 2] * in[jj + 8] + inverse_i[kk * 4 + 3] * in[jj + 12]) >> 16; +} + +int decon_dqe_set_color_transform(struct decon_color_transform_info *transform) +{ + int ret = 0; + int i, j; + int temp[16]; + struct dqe_device *dqe = dqe_drvdata; + struct decon_device *decon = get_decon_drvdata(0); + + mutex_lock(&dqe->lock); + + if (decon) { + if ((decon->state == DECON_STATE_OFF) || + (decon->state == DECON_STATE_INIT)) { + dqe_err("decon is not enabled!(%d)\n", decon->state); + ret = -1; + goto err; + } + } else { + dqe_err("decon is NULL!\n"); + ret = -1; + goto err; + } + + + dqe_info("%s : color_mode=%d, hint=%d\n", __func__, + dqe->ctx.color_mode, transform->hint); + + if (transform->matrix[0] != 65536/*transform->hint*/) { + for (i = 0; i < 16; i++) + dqe_dbg("matrix[%d] = %d\n", i, transform->matrix[i]); + + for (i = 0; i < 16; i++) + temp[i] = transform->matrix[i]; + + dqe_color_transform_night_light(temp, transform->matrix); + + for (i = 0; i < 16; i++) + dqe_dbg("night[%d] = %d\n", i, transform->matrix[i]); + } + + if (transform->matrix[0] == 65536 && + transform->matrix[5] == 65536 && + transform->matrix[10] == 65536) + dqe->ctx.night_light_on = 0; + else + dqe->ctx.night_light_on = 1; + + for (i = 0; i < 3; i++) + for (j = 0; j < 65; j++) + gamma_lut[i][j] = nightlight_gamma_tune[i][j]; + + for (j = 0; j < 65; j++) { + int inR, inG, inB, outR, outG, outB; + + inR = gamma_lut[0][j]; + inG = gamma_lut[1][j]; + inB = gamma_lut[2][j]; + + dqe_color_transform_make_sfr(transform->matrix, inR, inG, inB, &outR, &outG, &outB); + + gamma_lut[0][j] = (u32)outR; + gamma_lut[1][j] = (u32)outG; + gamma_lut[2][j] = (u32)outB; + } + + for (i = 0; i < 3; i++) + for (j = 0; j < 65; j++) + dqe_dbg("%d ", gamma_lut[i][j]); + + dqe_gamma_lut_set(); + + dqe->ctx.gamma_on = DQE_GAMMA_ON_MASK; + dqe->ctx.need_udpate = true; + + dqe_restore_context(); + decon_reg_update_req_dqe(decon->id); +err: + mutex_unlock(&dqe->lock); + + dqe_info("%s : ret(%d)\n", __func__, ret); + + return ret; +} + void decon_dqe_enable(struct decon_device *decon) { u32 val; -- 2.20.1