blob: afff3a1efa8b5f7ee90442e550a4fff7789ec7e5 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* drivers/amlogic/media/enhancement/amvecm/hdr/gamut_convet.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/types.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/amlogic/media/amvecm/amvecm.h>
#include <linux/amlogic/media/amvecm/hdr10_tmo_alg.h>
#include "am_hdr10_tm.h"
#include "../set_hdr2_v0.h"
#include "am_hdr10_tmo_fw.h"
unsigned int hdr10_tm_dbg;
module_param(hdr10_tm_dbg, uint, 0664);
MODULE_PARM_DESC(hdr10_tm_dbg, "HDR10 tone mapping dbg\n");
#define pr_hdr_tm(fmt, args...)\
do {\
if (hdr10_tm_dbg)\
pr_info(fmt, ## args);\
} while (0)
unsigned int panell = 400;
module_param(panell, uint, 0664);
MODULE_PARM_DESC(panell, "display panel luminance\n");
static unsigned int hdr10_tm_sel = 2; /*1 old algorithm, 2 hdr_tmo algorithm default 2*/
module_param(hdr10_tm_sel, uint, 0664);
MODULE_PARM_DESC(hdr10_tm_sel, "hdr10_tm_sel\n");
#define KNEE_POINT 2
static unsigned int kp = KNEE_POINT;
static unsigned int knee_point[KNEE_POINT] = {0, 0};
module_param_array(knee_point, uint, &kp, 0664);
MODULE_PARM_DESC(knee_point, "\n knee point xy\n");
/*u32 ebz_p[MAX_BEIZER_ORDER] = {
* 0, 1170, 1755, 2121, 2377, 2596, 2779, 2925, 3064, 3185,
* 3320, 3514, 3799, 3957, 4095
*};
*/
/*time domain filter, avoid flicker*/
/*scene change th: 2/3 scene diff*/
u32 sc_th = 682;
module_param(sc_th, uint, 0664);
MODULE_PARM_DESC(sc_th, "scene change th\n");
u32 hdr_tm_iir = 1;
module_param(hdr_tm_iir, uint, 0664);
MODULE_PARM_DESC(hdr_tm_iir, "HDR_TM_IIR\n");
int is_hdr_tmo_support(void)
{
if (get_cpu_type() >= MESON_CPU_MAJOR_ID_SM1)
return 1;
return 0;
}
int time_iir(u32 *maxl)
{
int i;
static u32 hist_diff[3];
static u32 max_lum[3];
u32 diff;
u32 sum = 0;
int sumshft;
int norm14;
u32 bld_coef;
u32 dtl_lum = 0;
/*ret=1: scn change; ret=2: blend*/
int ret;
for (i = 0; i < 2; i++) {
hist_diff[i] = hist_diff[i + 1];
max_lum[i] = max_lum[i + 1];
}
hist_diff[2] = 0;
max_lum[2] = *maxl;
for (i = 0; i < 128; i++) {
sum += hdr_hist[15][i];
diff = (hdr_hist[15][i] > hdr_hist[14][i]) ?
(hdr_hist[15][i] - hdr_hist[14][i]) :
(hdr_hist[14][i] - hdr_hist[15][i]);
hist_diff[2] += diff;
}
if (sum == 0)
return 0;
sumshft =
(sum >= (1 << 24)) ? 8 :
(sum >= (1 << 22)) ? 6 :
(sum >= (1 << 20)) ? 4 :
(sum >= (1 << 18)) ? 2 :
(sum >= (1 << 16)) ? 0 :
(sum >= (1 << 14)) ? -2 :
(sum >= (1 << 12)) ? -4 :
(sum >= (1 << 10)) ? -6 :
(sum >= (1 << 8)) ? -8 :
(sum >= (1 << 6)) ? -10 :
(sum >= (1 << 4)) ? -12 : -16;
if (sumshft >= 0)
norm14 = (1 << 30) / (sum >> sumshft);
else if (sumshft > -16)
norm14 = (1 << (30 + sumshft)) / sum;
else
norm14 = 1 << 14;
if (sumshft >= 0) {
hist_diff[2] = ((hist_diff[2] >> sumshft) * norm14 +
(1 << 13)) >> 14;
} else {
hist_diff[2] = (((hist_diff[2] << (-sumshft)) * norm14) +
(1 << 13)) >> 14;
}
/*normalize to 10bit*/
hist_diff[2] >>= 6;
if (hist_diff[2] > sc_th || (*maxl <= panell)) {
ret = 1;
} else {
bld_coef = hist_diff[2];
dtl_lum =
(max_lum[2] > max_lum[1]) ?
(max_lum[2] - max_lum[1]) :
(max_lum[1] - max_lum[2]);
if (dtl_lum < (1 << 3))
max_lum[2] =
(max_lum[2] > max_lum[1]) ?
(max_lum[1] + (dtl_lum >> 1)) :
(max_lum[1] - (dtl_lum >> 1));
else if (dtl_lum < (1 << 6))
max_lum[2] =
(max_lum[2] > max_lum[1]) ?
(max_lum[1] + (dtl_lum >> 3)) :
(max_lum[1] - (dtl_lum >> 3));
else
max_lum[2] =
(max_lum[2] > max_lum[1]) ?
(max_lum[1] + (dtl_lum >> 6)) :
(max_lum[1] - (dtl_lum >> 6));
*maxl = max_lum[2];
ret = 2;
}
pr_hdr_tm("ret = %d, dtl_lum = %d, max_lum[1] = %d, max_lum[2] = %d\n",
ret, dtl_lum, max_lum[1], max_lum[2]);
return ret;
}
/*hdr10 tone mapping alg */
int gen_oe_x_val(u64 *oe_x_val)
{
unsigned int i = 0, j = 0;
int bin_num = 0;
int temp1 = 0, temp2 = 0;
oe_x_val[0] = 0;
for (i = 1; i < 8; i++) {
bin_num++;
temp1 = 1 << ((i - 1) + 4);
oe_x_val[i] = (u64)temp1;
}
for (j = 11; j < 20; j++) {
for (i = 0; i < 4; i++) {
bin_num++;
temp1 = 1 << (j - 2);
temp2 = 1 << j;
oe_x_val[bin_num] = (u64)temp1 * i + (u64)temp2;
}
}
for (j = 20; j < 31; j++) {
for (i = 0; i < 8; i++) {
bin_num++;
temp1 = 1 << (j - 3);
temp2 = 1 << j;
oe_x_val[bin_num] = (u64)temp1 * i + (u64)temp2;
}
}
for (i = 0; i < 16; i++) {
bin_num++;
oe_x_val[bin_num] = (1 << (31 - 4)) * i + (1 << 31);
}
oe_x_val[OE_X - 1] = MAX_32;
return 0;
}
void decasteliau_alg(u64 *ebzcur, u64 step, u64 range, u64 *p, u32 order)
{
u64 point[MAX_BEIZER_ORDER + 1];
int i, j;
for (i = 0; i < order; i++)
point[i] = p[i];
for (i = 1; i < order; i++) {
for (j = 0; j < order - i; j++) {
point[j] = (range - step) * point[j] +
step * point[j + 1] + (range >> 1);
point[j] = div64_u64(point[j], range);
}
}
ebzcur[1] = point[0];
}
static u64 curvex[OE_X], curvey[OE_X];
static u32 oo_gain[OE_X];
int gen_knee_anchor(u32 maxl, u32 panell, u64 *sx, u64 *sy, u64 *anchor)
{
int i;
int knee_bypass = 1229;
u64 anchor_linear[MAX_BEIZER_ORDER];
u64 norm, blendcoef;
//int tgtl = 10000;
struct basisootf_params basisootf_param;
u64 sx1, sy1;
u64 anchor0;
sx1 = *sx;
sy1 = *sy;
basisootf_params_init(&basisootf_param);
for (i = 0; i < MAX_BEIZER_ORDER - 1; i++)
anchor_linear[i] = (i + 1) *
(1 << MAX12_BIT) / MAX_BEIZER_ORDER;
pr_hdr_tm("knee anchor p1: %lld, %lld, %lld, %lld, %lld, %lld, %lld, %lld, %lld, %lld %lld\n",
sx1, sy1,
anchor_linear[0], anchor_linear[1],
anchor_linear[2], anchor_linear[3],
anchor_linear[4], anchor_linear[5],
anchor_linear[6], anchor_linear[7],
anchor_linear[8]);
if (maxl <= panell) {
sx1 = knee_bypass;
sy1 = knee_bypass;
for (i = 0; i < MAX_BEIZER_ORDER - 1; i++)
anchor[i] = anchor_linear[i];
} else {
norm = maxl;
blendcoef = panell;
sy1 = (blendcoef * knee_bypass +
(norm - blendcoef) * sy1 + (norm >> 1));
sy1 = div64_u64(sy1, norm);
sx1 = sy1 * panell;
sx1 = div64_u64(sx1, maxl);
for (i = 0; i < MAX_BEIZER_ORDER - 1; i++) {
anchor[i] =
(blendcoef * anchor_linear[i] +
(norm - blendcoef) * anchor[i] +
(norm >> 1));
anchor[i] = div64_u64(anchor[i], norm);
}
anchor0 = calcp1((u32)sx1, (u32)sy1,
panell, maxl, &basisootf_param, NULL);
for (i = 1; i < MAX_BEIZER_ORDER - 1; i++)
anchor[i] = min(anchor[i], (i + 1) * anchor0);
if (anchor0 < anchor[1])
anchor[0] = anchor0;
}
*sx = sx1;
*sy = sy1;
pr_hdr_tm("knee anchor p2: %lld, %lld, %lld, %lld, %lld, %lld, %lld, %lld, %lld, %lld %lld\n",
sx1, sy1,
anchor[0], anchor[1],
anchor[2], anchor[3],
anchor[4], anchor[5],
anchor[6], anchor[7],
anchor[8]);
return 0;
}
int dynamic_hdr_sdr_ootf(u32 maxl, u32 panell, u64 sx, u64 sy, u64 *anchor)
{
int i;
u32 tgtl = 10000;
static u64 oe_x[OE_X] = {0};
int order = MAX_BEIZER_ORDER + 1;
u64 step;
u64 kx, ky;
u64 ebzcur[2];
u64 range_ebz_x, range_ebz_y;
u64 finalanchor[MAX_BEIZER_ORDER + 1];
u64 temp;
u64 sx_primy;
u64 kx_primy;
gen_oe_x_val(oe_x);
gen_knee_anchor(maxl, panell, &sx, &sy, anchor);
pr_hdr_tm("d1: %d, %d, %lld, %lld, %lld, %lld, %lld, %lld, %lld, %lld, %lld, %lld %lld\n",
maxl, panell,
sx, sy,
anchor[0], anchor[1],
anchor[2], anchor[3],
anchor[4], anchor[5],
anchor[6], anchor[7],
anchor[8]);
for (i = 0; i < order - 2; i++)
finalanchor[i + 1] = anchor[i] << (MAX32_BIT - MAX12_BIT);
finalanchor[0] = 0;
finalanchor[10] = MAX_32;
if (tgtl <= maxl)
maxl = tgtl - 1;
kx = sx << (MAX32_BIT - MAX12_BIT);
ky = sy << (MAX32_BIT - MAX12_BIT);
kx = kx * maxl;
kx = div64_u64(kx, tgtl);
if (maxl <= panell) {
sx_primy = (u64)(1 << MAX12_BIT) * panell + (tgtl >> 1);
sx_primy = div64_u64(sx_primy, tgtl);
} else {
sx_primy = (u64)(1 << MAX12_BIT) * maxl + (tgtl >> 1);
sx_primy = div64_u64(sx_primy, tgtl);
}
kx_primy = sx_primy << (MAX32_BIT - MAX12_BIT);
range_ebz_x = kx_primy - kx;
range_ebz_y = MAX_32 - ky;
if (maxl <= panell) {
for (i = 0; i < OE_X; i++) {
if (oe_x[i] <= kx_primy) {
temp = tgtl << TM_GAIN_BIT;
oo_gain[i] = div64_u64(temp, panell);
} else {
curvey[i] = oe_x[148] << TM_GAIN_BIT;
curvex[i] = oe_x[i];
oo_gain[i] = div64_u64(curvey[i], curvex[i]);
}
if (oo_gain[i] < (1 << TM_GAIN_BIT))
oo_gain[i] = 1 << TM_GAIN_BIT;
if (oo_gain[i] > (1 << MAX12_BIT) - 1)
oo_gain[i] = (1 << MAX12_BIT) - 1;
}
} else {
for (i = 0; i < OE_X; i++) {
if (oe_x[i] <= kx) {
temp = tgtl << TM_GAIN_BIT;
oo_gain[i] = div64_u64(temp, panell);
} else if (oe_x[i] <= kx_primy) {
step = oe_x[i] - kx;
decasteliau_alg(&ebzcur[0], step,
range_ebz_x,
finalanchor, order);
curvey[i] = ky +
((range_ebz_y * ebzcur[1] + MAX_32 / 2)
>> MAX32_BIT);
temp = curvey[i] << TM_GAIN_BIT;
curvex[i] = kx + step;
oo_gain[i] = div64_u64(temp, curvex[i]);
} else {
curvey[i] = oe_x[148] << TM_GAIN_BIT;
curvex[i] = oe_x[i];
oo_gain[i] = div64_u64(curvey[i], curvex[i]);
}
if (oo_gain[i] < (1 << TM_GAIN_BIT))
oo_gain[i] = 1 << TM_GAIN_BIT;
if (oo_gain[i] > (1 << MAX12_BIT) - 1)
oo_gain[i] = (1 << MAX12_BIT) - 1;
}
}
for (i = 0; i < OE_X; i++) {
if ((i + 1) % 10 == 0)
pr_hdr_tm("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n",
oo_gain[i - 9], oo_gain[i - 8],
oo_gain[i - 7], oo_gain[i - 6],
oo_gain[i - 5], oo_gain[i - 4],
oo_gain[i - 3], oo_gain[i - 2],
oo_gain[i - 1], oo_gain[i]);
if (i == 148)
pr_hdr_tm("%d, %d, %d, %d, %d, %d, %d, %d, %d\n",
oo_gain[i - 8],
oo_gain[i - 7], oo_gain[i - 6],
oo_gain[i - 5], oo_gain[i - 4],
oo_gain[i - 3], oo_gain[i - 2],
oo_gain[i - 1], oo_gain[i]);
}
pr_hdr_tm("\n");
return 0;
}
int hdr10_tm_dynamic_proc(struct vframe_master_display_colour_s *p)
{
int i;
u32 maxl;
u32 primry_maxl;
u32 panel_luma;
u64 sx, sy;
u64 anchor[MAX_BEIZER_ORDER - 1];
u32 P_init[MAX_BEIZER_ORDER - 1] = {
181, 406, 607, 796, 834, 863, 890, 917, 938
};
int scn_chang_flag = 1;
struct aml_tmo_reg_sw *pre_tmo_reg;
if (p->luminance[0] > 10000)
p->luminance[0] /= 10000;
/*no luminance*/
if (p->luminance[0] == 0)
p->luminance[0] = 1200;
primry_maxl = p->luminance[0];
/*use 95% maxl because of high percert flicker*/
maxl = (percentile[8] > primry_maxl) ? primry_maxl : percentile[8];
pr_hdr_tm("maxl = %d, percentile[8] = %d, primry_maxl =%d\n",
maxl, percentile[8], primry_maxl);
if (hdr_tm_iir)
scn_chang_flag = time_iir(&maxl);
if (scn_chang_flag == 0)
pr_hdr_tm("invalid hist\n");
else if (scn_chang_flag == 1)
pr_hdr_tm("scn change\n");
else if (scn_chang_flag == 2)
pr_hdr_tm("maxl blend: maxl = %d\n", maxl);
panel_luma = panell;
sx = knee_point[0];
sy = knee_point[1];
/*invalid case*/
if (maxl == 0) {
maxl = 1200;
pr_hdr_tm("invalid maxl\n");
}
for (i = 0; i < MAX_BEIZER_ORDER - 1; i++)
anchor[i] = P_init[i] << 2;
pre_tmo_reg = tmo_fw_param_get();
if (!is_hdr_tmo_support() || !pre_tmo_reg->pre_hdr10_tmo_alg) {
/*used old hdr alg*/
pr_hdr_tm("used old hdr alg - ");
if (!pre_tmo_reg->pre_hdr10_tmo_alg)
pr_hdr_tm("hdr alg ko insmod failed.\n");
else
pr_hdr_tm("the chip is not support.\n");
hdr10_tm_sel = 1;
}
if (hdr10_tm_sel == 1) {
dynamic_hdr_sdr_ootf(maxl, panel_luma, sx, sy, anchor);
memcpy(oo_y_lut_hdr_sdr, oo_gain, sizeof(u32) * OE_X);
} else if (hdr10_tm_sel == 2) {
hdr10_tmo_gen(oo_gain);
memcpy(oo_y_lut_hdr_sdr, oo_gain, sizeof(u32) * OE_X);
} else {
memcpy(oo_y_lut_hdr_sdr, oo_y_lut_hdr_sdr_def,
sizeof(u32) * OE_X);
}
if (hdr10_tm_dbg > 0)
hdr10_tm_dbg--;
return 0;
}