blob: 0a2f8d89214d16fa6df68501af8971272abc55fd [file] [log] [blame]
/*
* linux/drivers/video/fbdev/exynos/panel/dimming.c
*
* Samsung AID Dimming Driver.
*
* Copyright (c) 2016 Samsung Electronics
* Gwanghui Lee <gwanghui.lee@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "dimming.h"
#include "dimming_gamma.h"
static int NR_LUMINANCE;
static int NR_TP;
static int TP_VT = 0;
static int TP_V0 = -1;
static int TP_V255 = -1;
static int TARGET_LUMINANCE;
static enum gamma_degree TARGET_G_CURVE_DEGREE;
static int *calc_gray_order;
static int *calc_vout_order;
const char *tag_name[] = {
"[DIM_GLOBAL_INFO_START]",
"[DIM_GLOBAL_INFO_END]",
"[TP_LUT_INFO_START]",
"[TP_LUT_INFO_END]",
"[TP_LUT_START]",
"[TP_LUT_END]",
"[DIM_LUT_INFO_START]",
"[DIM_LUT_INFO_END]",
"[DIM_LUT_START]",
"[DIM_LUT_END]",
"[MTP_OFFSET_START]",
"[MTP_OFFSET_END]",
"[TP_VOLT_START]",
"[TP_VOLT_END]",
"[GRAY_SCALE_VOLT_START]",
"[GRAY_SCALE_VOLT_END]",
"[GAMMA_CENTER_START]",
"[GAMMA_CENTER_END]",
"[MTP_OFFSET_TEST_RANGE_START]",
"[MTP_OFFSET_TEST_RANGE_END]",
};
const char *global_dim_info_name[] = {
"NR_TP",
"NR_LUMINANCE",
"VREGOUT",
"VREF",
"GAMMA",
"VT_VOLTAGE",
};
const char *tp_lut_field_name[] = {
"TP_NONE",
"TP_LEVEL",
"TP_NAME",
"TP_VOLT_SRC",
"TP_GAMMA_CENTER",
"TP_GAMMA_CALC",
};
const char *dim_lut_field_name[] = {
"DIM_NONE" ,
"DIM_LUMINANCE",
"DIM_AOR",
"DIM_BASE_LUMINANCE",
"DIM_GAMMA",
"DIM_GRAY_SCALE_OFFSET",
"DIM_COLOR_SHIFT_OFFSET",
};
const char *color_name[] = {
"RED", "GREEN", "BLUE",
};
const char *volt_src_name[] = {
"VREG_OUT", "V0_OUT", "VT_OUT",
};
struct gamma_curve gamma_curve_lut[] = {
#ifndef GENERATE_GAMMA_CURVE
{ 0, NULL },
{ 160, gamma_curve_1p60 },
{ 165, gamma_curve_1p65 },
{ 170, gamma_curve_1p70 },
{ 175, gamma_curve_1p75 },
{ 180, gamma_curve_1p80 },
{ 185, gamma_curve_1p85 },
{ 190, gamma_curve_1p90 },
{ 195, gamma_curve_1p95 },
{ 200, gamma_curve_2p00 },
{ 205, gamma_curve_2p05 },
{ 210, gamma_curve_2p10 },
{ 212, gamma_curve_2p12 },
{ 213, gamma_curve_2p13 },
{ 215, gamma_curve_2p15 },
{ 220, gamma_curve_2p20 },
{ 225, gamma_curve_2p25 },
#else
{ 0, NULL },
{ 160, NULL },
{ 165, NULL },
{ 170, NULL },
{ 175, NULL },
{ 180, NULL },
{ 185, NULL },
{ 190, NULL },
{ 195, NULL },
{ 200, NULL },
{ 205, NULL },
{ 210, NULL },
{ 212, NULL },
{ 213, NULL },
{ 215, NULL },
{ 220, NULL },
{ 225, NULL },
#endif /* GENERATE_GAMMA_CURVE */
};
s64 disp_pow(s64 num, u32 digits)
{
s64 res = num;
u32 i;
if (digits == 0)
return 1;
for (i = 1; i < digits; i++)
res *= num;
return res;
}
s64 disp_round(s64 num, u32 digits)
{
int sign = (num < 0 ? -1 : 1);
u64 tnum;
tnum = num * sign;
tnum *= disp_pow(10, digits + 1);
tnum += 5;
do_div(tnum, (u32)disp_pow(10, digits + 1));
return sign * (s64)tnum;
}
static s64 scale_down_round(s64 num, u32 digits)
{
int sign = (num < 0 ? -1 : 1);
u64 tnum, rem;
tnum = num * sign;
rem = do_div(tnum, (1U << BIT_SHIFT));
rem *= disp_pow(10, digits + 1);
rem >>= BIT_SHIFT;
rem += 5;
do_div(rem, (u32)disp_pow(10, digits + 1));
return sign * (s64)(tnum + rem);
}
static s64 scale_down_rem(s64 num, u32 digits)
{
int sign = (num < 0 ? -1 : 1);
u64 tnum, rem;
tnum = num * sign;
rem = do_div(tnum, (1U << BIT_SHIFT));
rem *= disp_pow(10, digits + 1);
rem >>= BIT_SHIFT;
rem += 5;
do_div(rem, 10);
/* round up and minus in remainder */
if (rem >= (u64)disp_pow(10, digits))
rem -= (u64)disp_pow(10, digits);
return sign * rem;
}
#if BIT_SHIFT > 26
static int msb64(s64 num)
{
int i;
for (i = 63; i >= 0; i--)
if (num & (1ULL << i))
return i;
return 0;
}
#endif
s64 disp_div64(s64 num, s64 den)
{
#if BIT_SHIFT > 26
if (num > ((1LL << (63 - BIT_SHIFT)) - 1)) {
int bit_shift = 62 - msb64(num);
pr_err("out of range num %lld\n", num);
return ((num << bit_shift) / den) >> bit_shift;
}
#endif
int sign_num = (num < 0 ? -1 : 1);
int sign_den = (den < 0 ? -1 : 1);
int sign = sign_num * sign_den;
u64 tnum, tden;
if (unlikely(den == 0)) {
pr_err("%s, den should not be zero (%llu %llu)\n", __func__, num, den);
return 0;
}
tnum = num * sign_num;
tden = den * sign_den;
if (tden >= (1ULL << 31))
pr_err("%s, out of range denominator %llu\n",
__func__, tden);
do_div(tnum, (u32)tden);
return sign * tnum;
}
#ifdef DIMMING_CALC_PRECISE
s64 disp_div64_round(s64 num, s64 den, u32 digits)
{
int sign_num = (num < 0 ? -1 : 1);
int sign_den = (den < 0 ? -1 : 1);
int sign = sign_num * sign_den;
u64 tnum, tden, rem;
if (unlikely(den == 0)) {
pr_err("%s, den should not be zero (%llu %llu %u)\n",
__func__, num, den, digits);
return 0;
}
tnum = num * sign_num;
tden = den * sign_den;
rem = do_div(tnum, (u32)tden);
rem *= disp_pow(10, digits + 2);
do_div(rem, (u32)tden);
/*
* floating point should not be used.
* Some variables are inaccurate in most case (e.g. vregout...)
* To compensate it, round_up function use (+ 6) instead of (+ 5).
*/
rem += 5;
do_div(rem, (u32)disp_pow(10, digits + 2));
return sign * (tnum + rem);
}
#endif /* DIMMING_CALC_PRECISE */
/*
* generate_order() is an helper function can make an order
* that mixed an initial order value and range based order values.
* @s : initial value
* @from - starting of the range
* @to - end of the range including 'to'.
* @return - an allocated array will be set as like { s, [from, to] }.
*/
static int *generate_order(int s, int from, int to) {
int i, len = abs(from - to) + 2;
int sign = ((to - from) < 0) ? -1 : 1;
int *t_order = (int *)kmalloc(len * sizeof(int), GFP_KERNEL);
if (!t_order) {
pr_err("%s, failed to allocate memory\n", __func__);
return NULL;
}
t_order[0] = s;
t_order[1] = from;
for (i = 2; i < len; i++)
t_order[i] = t_order[i - 1] + sign;
return t_order;
}
static int find_name_in_table(const char **table, int sz_table, char *s)
{
int i;
for (i = 0; i < sz_table; i++)
if (!strncmp(s, table[i], 128))
return i;
return -EINVAL;
}
int find_tag_name(char *s)
{
return find_name_in_table(tag_name, ARRAY_SIZE(tag_name), s);
}
int find_global_dim_info(char *s)
{
return find_name_in_table(global_dim_info_name,
ARRAY_SIZE(global_dim_info_name), s);
}
int find_voltage_source(char *s)
{
return find_name_in_table(volt_src_name, ARRAY_SIZE(volt_src_name), s);
}
int find_tp_lut_field(char *s)
{
return find_name_in_table(tp_lut_field_name, ARRAY_SIZE(tp_lut_field_name), s);
}
int find_dim_lut_field(char *s)
{
return find_name_in_table(dim_lut_field_name, ARRAY_SIZE(dim_lut_field_name), s);
}
#ifdef GENERATE_GAMMA_CURVE
static void generate_gamma_curve(int gamma, int *gamma_curve)
{
int i;
double gamma_f = (double)gamma / 100;
pr_info("%s, generate %d gamma\n", __func__, gamma);
for (i = 0; i < GRAY_SCALE_MAX; i++) {
gamma_curve[i] = (int)(pow(((double)i / 255), gamma_f) * (double)(1 << BIT_SHIFT) + 0.5f);
pr_info("%d ", gamma_curve[i]);
if (!((i + 1) % 8))
pr_info("\n");
}
}
void remove_gamma_curve(void)
{
size_t index;
for (index = 0; index < ARRAY_SIZE(gamma_curve_lut); index++)
if (gamma_curve_lut[index].gamma_curve_table) {
free(gamma_curve_lut[index].gamma_curve_table);
gamma_curve_lut[index].gamma_curve_table = NULL;
}
}
#endif /* GENERATE_GAMMA_CURVE */
int find_gamma_curve(int gamma)
{
size_t index;
#ifdef GENERATE_GAMMA_CURVE
int *gamma_curve_table;
#endif
for (index = 0; index < ARRAY_SIZE(gamma_curve_lut); index++)
if (gamma_curve_lut[index].gamma == gamma)
break;
if (index == ARRAY_SIZE(gamma_curve_lut)) {
pr_err("%s, gamma %d curve not found\n", __func__, gamma);
return -1;
}
#ifdef GENERATE_GAMMA_CURVE
if (!gamma_curve_lut[index].gamma_curve_table) {
pr_info("%s, generate gamma curve %d\n", __func__, gamma);
gamma_curve_table = kmalloc(GRAY_SCALE_MAX * sizeof(int), GFP_KERNEL);
generate_gamma_curve(gamma, gamma_curve_table);
gamma_curve_lut[index].gamma_curve_table = gamma_curve_table;
}
#endif
return (int)index;
}
enum gamma_degree gamma_value_to_degree(int gamma)
{
int g_curve_index;
g_curve_index = find_gamma_curve(gamma);
if (g_curve_index < 0) {
pr_err("%s, gamma %d not exist\n", __func__, gamma);
return GAMMA_NONE;
}
return (enum gamma_degree)g_curve_index;
}
/*
* @ g_curve : gamma curve
* @ idx : gray scale level 0 ~ 255.
* @ return ((idx/255)^g_curve)*(2^22)
*/
int gamma_curve_value(enum gamma_degree g_curve, int idx)
{
if (g_curve == GAMMA_NONE ||
idx >= GRAY_SCALE_MAX)
return -1;
#ifdef GENERATE_GAMMA_CURVE
if (!gamma_curve_lut[(int)g_curve].gamma_curve_table) {
int *gamma_curve_table = (int *)kmalloc(GRAY_SCALE_MAX * sizeof(int), GFP_KERNEL);
int gamma = gamma_curve_lut[(int)g_curve].gamma;
pr_info("%s, generate gamma curve %d\n", __func__, gamma);
generate_gamma_curve(gamma, gamma_curve_table);
gamma_curve_lut[(int)g_curve].gamma_curve_table = gamma_curve_table;
}
#endif
return gamma_curve_lut[(int)g_curve].gamma_curve_table[idx];
}
bool gamma_in_range(int gamma)
{
return !(gamma < gamma_curve_lut[GAMMA_MIN].gamma ||
gamma > gamma_curve_lut[GAMMA_MAX - 1].gamma);
}
void print_mtp_offset(struct dimming_info *dim_info)
{
int i;
for (i = 0; i < dim_info->nr_tp; i++)
pr_info("%-7s %-4d %-4d %-4d\n",
dim_info->tp[i].name,
dim_info->tp[i].offset[RED],
dim_info->tp[i].offset[GREEN],
dim_info->tp[i].offset[BLUE]);
}
void print_hbm_gamma_tbl(struct dimming_info *dim_info)
{
int i;
if (unlikely(!dim_info->hbm_gamma_tbl)) {
pr_warn("%s, hbm_gamma_tbl not exist\n", __func__);
return;
}
if (unlikely(!dim_info->tp)) {
pr_warn("%s, tp not exist\n", __func__);
return;
}
for (i = 0; i < dim_info->nr_tp; i++)
pr_info("%-7s %-4d %-4d %-4d\n",
dim_info->tp[i].name,
dim_info->hbm_gamma_tbl[i][RED],
dim_info->hbm_gamma_tbl[i][GREEN],
dim_info->hbm_gamma_tbl[i][BLUE]);
}
void print_tpout_center(struct dimming_info *dim_info)
{
int i, len, ilum, nr_luminance, nr_tp;
char buf[MAX_PRINT_BUF_SIZE];
struct dimming_lut *lut;
if (unlikely(!dim_info)) {
pr_err("%s, invalid dim_info\n", __func__);
return;
}
lut = dim_info->dim_lut;
nr_tp = dim_info->nr_tp;
nr_luminance = dim_info->nr_luminance;
if (unlikely(!lut)) {
pr_err("%s, invalid dim_lut\n", __func__);
return;
}
if ((nr_tp * 15UL) >= ARRAY_SIZE(buf)) {
pr_err("%s, exceed linebuf size (%lu)\n",
__func__, nr_tp * 15UL);
return;
}
for (ilum = 0; ilum < nr_luminance; ilum++) {
len = snprintf(buf, MAX_PRINT_BUF_SIZE,
"%-3d ", lut[ilum].luminance);
#ifdef DEBUG_DIMMING_RESULT_ASCENDING
for (i = 1; i < nr_tp; i++) {
len += snprintf(buf + len, max(MAX_PRINT_BUF_SIZE - len, 0),
"%-3d %-3d %-3d ",
lut[ilum].tpout[i].center[RED],
lut[ilum].tpout[i].center[GREEN],
lut[ilum].tpout[i].center[BLUE]);
}
len += snprintf(buf + len, max(MAX_PRINT_BUF_SIZE - len, 0),
"%-3d %-3d %-3d ",
lut[ilum].tpout[TP_VT].center[RED],
lut[ilum].tpout[TP_VT].center[GREEN],
lut[ilum].tpout[TP_VT].center[BLUE]);
#else
for (i = nr_tp - 1; i >= 0; i--) {
len += snprintf(buf + len, max(MAX_PRINT_BUF_SIZE - len, 0),
"%-3d %-3d %-3d ",
lut[ilum].tpout[i].center[RED],
lut[ilum].tpout[i].center[GREEN],
lut[ilum].tpout[i].center[BLUE]);
}
#endif
pr_info("%s\n", buf);
}
}
void print_tpout_center_debug(struct dimming_info *dim_info)
{
int i, ilum, len, nr_tp, nr_luminance;
char buf[MAX_PRINT_BUF_SIZE];
struct dimming_lut *lut;
if (unlikely(!dim_info)) {
pr_err("%s, invalid dim_info\n", __func__);
return;
}
lut = dim_info->dim_lut;
nr_tp = dim_info->nr_tp;
nr_luminance = dim_info->nr_luminance;
if (unlikely(!lut)) {
pr_err("%s, invalid dim_lut\n", __func__);
return;
}
if ((nr_tp * 15UL) >= ARRAY_SIZE(buf)) {
pr_err("%s, exceed linebuf size (%lu)\n",
__func__, nr_tp * 15UL);
return;
}
for (ilum = 0; ilum < nr_luminance; ilum++) {
len = snprintf(buf, MAX_PRINT_BUF_SIZE,
"%-3d ", lut[ilum].luminance);
for (i = nr_tp - 1; i >= 0; i--) {
len += snprintf(buf + len,
max(MAX_PRINT_BUF_SIZE - len, 0),
"%-3d %-3d %-3d ",
lut[ilum].tpout[i].center_debug[RED],
lut[ilum].tpout[i].center_debug[GREEN],
lut[ilum].tpout[i].center_debug[BLUE]);
}
pr_info("%s\n", buf);
}
}
void print_tp_vout(struct dimming_info *dim_info)
{
int i, nr_tp;
struct tp *tp;
if (unlikely(!dim_info)) {
pr_err("%s, invalid dim_info\n", __func__);
return;
}
tp = dim_info->tp;
nr_tp = dim_info->nr_tp;
if (unlikely(!tp)) {
pr_err("%s, invalid tp\n", __func__);
return;
}
for (i = 0; i < nr_tp; i++)
pr_info("%1lld.%04lld %1lld.%04lld %1lld.%04lld\n",
scale_down_round(tp[i].vout[RED], 4), scale_down_rem(tp[i].vout[RED], 4),
scale_down_round(tp[i].vout[GREEN], 4), scale_down_rem(tp[i].vout[GREEN], 4),
scale_down_round(tp[i].vout[BLUE], 4), scale_down_rem(tp[i].vout[BLUE], 4));
}
void print_gray_scale_vout(struct gray_scale *gray_scale_lut)
{
int i;
for (i = 0; i < GRAY_SCALE_MAX; i++) {
pr_info("%1lld.%04lld %1lld.%04lld %1lld.%04lld\n",
scale_down_round(gray_scale_lut[i].vout[RED], 4), scale_down_rem(gray_scale_lut[i].vout[RED], 4),
scale_down_round(gray_scale_lut[i].vout[GREEN], 4), scale_down_rem(gray_scale_lut[i].vout[GREEN], 4),
scale_down_round(gray_scale_lut[i].vout[BLUE], 4), scale_down_rem(gray_scale_lut[i].vout[BLUE], 4));
}
}
static u64 mtp_offset_to_vregout(struct dimming_info *dim_info,
int v, enum color c)
{
s64 num, den, vreg, vsrc, res = 0;
struct tp *tp = dim_info->tp;
u32 *vt_voltage = dim_info->vt_voltage;
u32 *v0_voltage = dim_info->v0_voltage;
s64 VREGOUT = dim_info->vregout;
s64 VREF = dim_info->vref;
if (!tp || v < 0 || v >= NR_TP) {
pr_err("%s, invalid tp %d\n", __func__, v);
return -EINVAL;
}
den = (s64)tp[v].denominator;
if (tp[v].volt_src == VREG_OUT || tp[v].volt_src == V0_OUT)
vsrc = VREGOUT;
else if (tp[v].volt_src == VT_OUT)
vsrc = tp[TP_VT].vout[c];
else {
vsrc = tp[TP_VT].vout[c];
pr_warn("%s unknown tp[%d].volt_src %d\n",
__func__, v, tp[v].volt_src);
}
if (v == TP_VT) {
s32 offset = tp[v].center[c] + tp[v].offset[c];
if ((offset > 0xF) || (offset < 0x0)) {
pr_warn("%s, warning : ivt (%d) out of range(0x0 ~ 0xF) replace to %s\n",
__func__, offset, offset > 0xF ? "0xF" : "0x0" );
offset = offset > 0xF ? 0xF : 0x0;
}
vreg = vsrc - VREF;
num = vreg * (s64)vt_voltage[offset];
res = vsrc - disp_div64(num, den);
} else if (v == TP_V0) {
s32 offset = tp[v].center[c] + tp[v].offset[c];
if ((offset > 0xF) || (offset < 0x0)) {
pr_warn("%s, warning : ivt (%d) out of range(0x0 ~ 0xF) replace to %s\n",
__func__, offset, offset > 0xF ? "0xF" : "0x0" );
offset = offset > 0xF ? 0xF : 0x0;
}
vreg = vsrc - VREF;
num = vreg * (s64)v0_voltage[offset];
res = vsrc - disp_div64(num, den);
} else if (v == TP_V255) {
vreg = vsrc - VREF;
num = vreg * ((s64)tp[v].numerator + tp[v].center[c] + tp[v].offset[c]);
res = vsrc - disp_div64(num, den);
} else {
vreg = vsrc - tp[v + 1].vout[c];
num = vreg * ((s64)tp[v].numerator + tp[v].center[c] + tp[v].offset[c]);
res = vsrc - disp_div64(num, den);
}
#ifdef DEBUG_DIMMING
pr_info("%s, %s %s vreg %lld, vsrc %lld, num %lld, den %lld, res %lld\n",
__func__, tp[(int)v].name, color_name[(int)c], vreg, vsrc, num, den, res);
#endif
if (res < 0)
pr_err("%s, res %lld should not be minus value\n", __func__, res);
return res;
}
static s32 mtp_vregout_to_offset(struct dimming_info *dim_info,
int v, enum color c, int luminance_index)
{
s64 num, den, res, vsrc;
u32 upper;
s32(*rgb_offset)[MAX_COLOR];
s64 VREGOUT = dim_info->vregout;
s64 VREF = dim_info->vref;
struct dimming_lut *dim_lut = dim_info->dim_lut;
struct tp *tp = dim_info->tp;
struct dimming_tp_output *tpout;
if (!tp || v < 0 || v >= NR_TP) {
pr_err("%s, invalid tp %d\n", __func__, v);
return -EINVAL;
}
if (luminance_index < 0 || luminance_index >= NR_LUMINANCE) {
pr_err("%s, out of range luminance index (%d)\n", __func__, luminance_index);
return 0;
}
if (tp[v].bits > 0)
upper = (1 << tp[v].bits) - 1;
else
upper = ((v == TP_V255) ? 511: 255);
rgb_offset = dim_lut[luminance_index].rgb_color_offset;
tpout = dim_lut[luminance_index].tpout;
if (tp[v].volt_src == VREG_OUT || tp[v].volt_src == V0_OUT)
vsrc = VREGOUT;
else if (tp[v].volt_src == VT_OUT)
vsrc = tpout[TP_VT].vout[c];
else {
vsrc = tpout[TP_VT].vout[c];
pr_warn("%s unknown tp[%d].volt_src %d\n",
__func__, v, tp[v].volt_src);
}
if (v == TP_V255) {
num = (vsrc - tpout[v].vout[c]) * (s64)tp[v].denominator;
den = vsrc - VREF;
} else {
num = (vsrc - tpout[v].vout[c]) * (s64)tp[v].denominator;
den = vsrc - tpout[v + 1].vout[c];
}
#ifdef DIMMING_CALC_PRECISE
/* To get precise value, use disp_div64_round() function */
num -= den * (tp[v].numerator);
num = disp_round(num, 7) - den * (s64)tp[v].offset[c];
res = disp_div64_round(num, den, 2);
pr_debug("lum %d, %s %s num %lld, den %lld, res %lld\n", dim_lut[luminance_index].luminance, tp[v].name, color_name[c], num, den, res);
#else
num -= den * ((s64)tp[v].numerator + (s64)tp[v].offset[c]);
res = disp_div64(num, den);
#endif
//if (in_range(v, dim_lut_info->rgb_color_offset_range))
res += rgb_offset[v][c];
#ifdef DEBUG_DIMMING
pr_debug("luminance %3d %5s %5s num %lld\t den %lld\t rgb_offset %d\t res %lld\n",
dim_lut[luminance_index].luminance, tp[v].name, color_name[c], num, den, rgb_offset[v][c], res);
#endif
if (res < 0)
res = 0;
if (res > upper)
res = upper;
return (s32)res;
}
/*
* ########### BASE LUMINANCE GRAY SCALE TABLE FUNCTIONS ###########
*/
/*
* sams as excel macro function min_diff_gray().
*/
static int find_gray_scale_gamma_value(struct gray_scale *gray_scale_lut, u64 gamma_value)
{
int l = 0, m, r = GRAY_SCALE_MAX - 1;
s64 delta_l, delta_r;
if (unlikely(!gray_scale_lut)) {
pr_err("%s, gray_scale_lut not exist\n", __func__);
return -EINVAL;
}
if ((gray_scale_lut[l].gamma_value > gamma_value) ||
(gray_scale_lut[r].gamma_value < gamma_value)) {
pr_err("%s, out of range([l:%d, r:%d], [%lld, %lld]) candela(%lld)\n",
__func__, l, r, gray_scale_lut[l].gamma_value,
gray_scale_lut[r].gamma_value, gamma_value);
return -EINVAL;
}
while (l <= r) {
m = l + (r - l) / 2;
if (gray_scale_lut[m].gamma_value == gamma_value)
return m;
if (gray_scale_lut[m].gamma_value < gamma_value)
l = m + 1;
else
r = m - 1;
}
delta_l = gamma_value - gray_scale_lut[r].gamma_value;
delta_r = gray_scale_lut[l].gamma_value - gamma_value;
return delta_l <= delta_r ? r : l;
}
s64 interpolation(s64 from, s64 to, int cur_step, int total_step)
{
s64 num, den;
if (cur_step == total_step || from == to)
return to;
num = (to - from) * (s64)cur_step;
den = (s64)total_step;
num = from + disp_div64(num, den);
return num;
}
int gamma_table_add_offset(s32 (*src)[MAX_COLOR], s32 (*ofs)[MAX_COLOR],
s32 (*out)[MAX_COLOR], struct tp *tp, int nr_tp)
{
int v, c, upper, res;
if (unlikely(!tp || nr_tp == 0 || !src|| !ofs || !out)) {
pr_err("%s, invalid parameter (tp %d, nr_tp %d, src %d, ofs %d, out %d)\n",
__func__, !!tp, nr_tp, !!src, !!ofs, !!out);
return -EINVAL;
}
for (v = 0; v < nr_tp; v++) {
upper = (1 << tp[v].bits) - 1;
for_each_color(c) {
res = src[v][c] + ofs[v][c];
if (res < 0)
res = 0;
if (res > upper)
res = upper;
out[v][c] = res;
#ifdef DEBUG_DIMMING
pr_info("%s, src %d, ofs %d, out %d\n",
__func__, src[v][c], ofs[v][c], out[v][c]);
#endif
}
}
return 0;
}
int gamma_table_interpolation(s32 (*from)[MAX_COLOR], s32 (*to)[MAX_COLOR],
s32 (*out)[MAX_COLOR], int nr_tp, int cur_step, int total_step)
{
int i, c;
if (unlikely(!from || !to || !out)) {
pr_err("%s, invalid parameter (from %p, to %p, out %p)\n",
__func__, from, to, out);
return -EINVAL;
}
if (unlikely(nr_tp < 0 || cur_step > total_step)) {
pr_err("%s, out of range (nr_tp %d, cur_step %d, total_step %d)\n",
__func__, nr_tp, cur_step, total_step);
return -EINVAL;
}
for (i = 0; i < nr_tp; i++) {
for_each_color(c) {
out[i][c] =
interpolation(from[i][c], to[i][c], cur_step, total_step);
#ifdef DEBUG_DIMMING
pr_info("%s, from %d, to %d, cur_step %d, total_step %d\n",
__func__, from[i][c], to[i][c], cur_step, total_step);
#endif
}
}
return 0;
}
static int generate_gray_scale(struct dimming_info *dim_info)
{
int i, c, iv = 0, iv_upper = 0, iv_lower = 0, ret = 0, cur_step, total_step = 0;
s64 vout, v_upper, v_lower;
struct gray_scale *gray_scale_lut = dim_info->gray_scale_lut;
struct tp *tp = dim_info->tp;
int nr_tp = dim_info->nr_tp;
for (i = 0, iv = (TP_V0 > 0 ? TP_V0 : TP_VT);
iv < nr_tp && i < GRAY_SCALE_MAX; i++) {
if (i == tp[iv].level) {
iv_upper = tp[((iv == nr_tp - 1) ? iv : iv + 1)].level;
iv_lower = tp[iv].level;
total_step = iv_upper - iv_lower;
iv++;
continue;
}
cur_step = i - iv_lower;
for_each_color(c) {
v_upper = (s64)gray_scale_lut[iv_upper].vout[c];
v_lower = (s64)gray_scale_lut[iv_lower].vout[c];
vout = interpolation(v_lower, v_upper, cur_step, total_step);
#ifdef DEBUG_DIMMING
pr_info("lower %3d, upper %3d, "
"cur_step %3d, total_step %3d, vout[%d]\t "
"from %2lld.%04lld, vout %2lld.%04lld, to %2lld.%04lld\n",
iv_lower, iv_upper, cur_step, total_step, c,
scale_down_round(v_lower, 4), scale_down_rem(v_lower, 4),
scale_down_round(vout, 4), scale_down_rem(vout, 4),
scale_down_round(v_upper, 4), scale_down_rem(v_upper, 4));
#endif
if (vout < 0) {
pr_warn("%s, from %2lld.%4lld, to %2lld.%4lld (idx %-3d %-3d), cur_step %-3d, total_step %-3d\n",
__func__, scale_down_round(v_lower, 4), scale_down_rem(v_lower, 4),
scale_down_round(v_upper, 4), scale_down_rem(v_upper, 4),
iv_lower, iv_upper, cur_step, total_step);
ret = -EINVAL;
}
gray_scale_lut[i].vout[c] = vout;
}
}
for (i = 0; i < GRAY_SCALE_MAX; i++) {
/* base luminance gamma curve value */
gray_scale_lut[i].gamma_value = TARGET_LUMINANCE * (u64)gamma_curve_value(TARGET_G_CURVE_DEGREE, i);
#ifdef DEBUG_DIMMING
pr_info("%-4d gamma %3lld.%02lld\n", i,
scale_down_round(gray_scale_lut[i].gamma_value, 2),
scale_down_rem(gray_scale_lut[i].gamma_value, 2));
#endif
}
return ret;
}
/*
* ########### GAMMA CORRECTION TABLE FUNCTIONS ###########
*/
int find_luminance(struct dimming_info *dim_info, u32 luminance)
{
int index, nr_luminance;
struct dimming_lut *dim_lut;
if (unlikely(!dim_info)) {
pr_err("%s, invalid dim_info\n", __func__);
return -1;
}
dim_lut = dim_info->dim_lut;
nr_luminance = dim_info->nr_luminance;
if (unlikely(!dim_lut)) {
pr_err("%s, invalid dim_lut\n", __func__);
return -1;
}
for (index = 0; index < nr_luminance; index++)
if (dim_lut[index].luminance == luminance)
return index;
return -1;
}
int get_luminance(struct dimming_info *dim_info, u32 index)
{
int nr_luminance;
struct dimming_lut *dim_lut;
if (unlikely(!dim_info)) {
pr_err("%s, invalid dim_info\n", __func__);
return 0;
}
dim_lut = dim_info->dim_lut;
nr_luminance = dim_info->nr_luminance;
if (unlikely(!dim_lut)) {
pr_err("%s, invalid dim_lut\n", __func__);
return 0;
}
if (index >= nr_luminance) {
pr_warn("%s, exceed max %d, index %d\n",
__func__, nr_luminance - 1, index);
return dim_lut[nr_luminance - 1].luminance;
}
return dim_lut[index].luminance;
}
static void copy_tpout_center(struct dimming_info *dim_info, u32 luminance, u8 *output,
void (*copy)(u8 *output, u32 value, u32 index, u32 color))
{
struct dimming_lut *dim_lut = dim_info->dim_lut;
struct tp *tp = dim_info->tp;
int ilum = find_luminance(dim_info, luminance);
int i, c, value;
if (unlikely(!tp || !dim_lut)) {
pr_err("%s, invalid tp or dim_lut\n", __func__);
return;
}
if (unlikely(ilum < 0)) {
pr_err("%s, luminance(%d) not found\n", __func__, luminance);
return;
}
for (i = TP_V255; i >= TP_VT; i--) {
for_each_color(c) {
value = dim_lut[ilum].tpout[i].center[c];
copy(output, value, i, c);
}
}
}
static void copy_tpout_hbm_center(struct dimming_info *dim_info, u32 luminance, u8 *output,
void (*copy)(u8 *output, u32 value, u32 index, u32 color))
{
struct dimming_lut *dim_lut = dim_info->dim_lut;
struct tp *tp = dim_info->tp;
int i, c, value, ilum;
int nr_tp = dim_info->nr_tp;
s32 (*target_gamma_tbl)[MAX_COLOR];
s32 (*output_gamma_tbl)[MAX_COLOR];
if (unlikely(!tp || !dim_lut)) {
pr_err("%s, invalid tp or dim_lut\n", __func__);
return;
}
target_gamma_tbl =
(s32 (*)[MAX_COLOR])kzalloc(sizeof(s32) * nr_tp * MAX_COLOR, GFP_KERNEL);
if (unlikely(!target_gamma_tbl)) {
pr_err("%s. failed to allocate target_gamma_tbl\n", __func__);
goto err;
}
output_gamma_tbl =
(s32 (*)[MAX_COLOR])kzalloc(sizeof(s32) * nr_tp * MAX_COLOR, GFP_KERNEL);
if (unlikely(!output_gamma_tbl)) {
pr_err("%s. failed to allocate output_gamma_tbl\n", __func__);
goto err_alloc;
}
/* make target gamma table */
ilum = find_luminance(dim_info, dim_info->target_luminance);
if (unlikely(ilum < 0)) {
pr_err("%s, target_luminance(%d) not found\n",
__func__, dim_info->target_luminance);
goto err_find;
}
for (i = 0; i < nr_tp; i++)
for_each_color(c)
target_gamma_tbl[i][c] = dim_lut[ilum].tpout[i].center[c];
gamma_table_interpolation(target_gamma_tbl, dim_info->hbm_gamma_tbl, output_gamma_tbl,
dim_info->nr_tp, luminance - dim_info->target_luminance,
dim_info->hbm_luminance - dim_info->target_luminance);
for (i = TP_V255; i >= TP_VT; i--) {
for_each_color(c) {
value = output_gamma_tbl[i][c];
copy(output, value, i, c);
}
}
err_find:
kfree(output_gamma_tbl);
err_alloc:
kfree(target_gamma_tbl);
err:
return;
}
void get_dimming_gamma(struct dimming_info *dim_info, u32 luminance, u8 *output,
void (*copy)(u8 *output, u32 value, u32 index, u32 color))
{
if (unlikely(!dim_info || !output)) {
pr_err("%s, invalid dim_info or output\n", __func__);
return;
}
if (luminance <= dim_info->target_luminance)
copy_tpout_center(dim_info, luminance, output, copy);
else if (dim_info->hbm_luminance && luminance <= dim_info->hbm_luminance)
copy_tpout_hbm_center(dim_info, luminance, output, copy);
else
pr_warn("%s, out of range (hbm_luminance %d, luminance %d)\n",
__func__, dim_info->hbm_luminance, luminance);
return;
}
void print_dimming_tp_output(struct dimming_lut *dim_lut, struct tp *tp, int size)
{
int i, v;
struct dimming_tp_output *tpout;
u32 luminance;
for (i = 0; i < size; i++) {
tpout = dim_lut[i].tpout;
luminance = dim_lut[i].luminance;
for (v = 0; v < NR_TP; v++) {
pr_info("luminance %3d %5s "
"L %3lld.%05lld\t M_GRAY %3u "
"R %3lld.%05lld\t G %3lld.%05lld\t B %3lld.%05lld\t "
"%3X %3X %3X\n",
luminance, tp[v].name,
scale_down_round(tpout[v].L, 5), scale_down_rem(tpout[v].L, 5), tpout[v].M_GRAY,
scale_down_round(tpout[v].vout[RED], 5), scale_down_rem(tpout[v].vout[RED], 5),
scale_down_round(tpout[v].vout[GREEN], 5), scale_down_rem(tpout[v].vout[GREEN], 5),
scale_down_round(tpout[v].vout[BLUE], 5), scale_down_rem(tpout[v].vout[BLUE], 5),
tpout[v].center[RED], tpout[v].center[GREEN], tpout[v].center[BLUE]);
}
}
pr_info("\n");
}
static int generate_gamma_table(struct dimming_info *dim_info)
{
int i, v, c, ilum;
enum gamma_degree g_curve_degree;
struct dimming_tp_output *tpout;
struct dimming_lut *dim_lut = dim_info->dim_lut;
struct gray_scale *gray_scale_lut = dim_info->gray_scale_lut;
struct tp *tp = dim_info->tp;
int NR_TP = dim_info->nr_tp;
int TARGET_LUMINANCE = dim_info->target_luminance;
enum gamma_degree TARGET_G_CURVE_DEGREE = dim_info->target_g_curve_degree;
u32 luminance;
if (unlikely(!calc_gray_order)) {
pr_err("%s, calc_gray_order not exist!!\n", __func__);
return -EINVAL;
}
if (unlikely(!calc_vout_order)) {
pr_err("%s, calc_vout_order not exist!!\n", __func__);
return -EINVAL;
}
for (ilum = 0; ilum < NR_LUMINANCE; ilum++) {
luminance = dim_lut[ilum].luminance;
tpout = dim_lut[ilum].tpout;
for (i = 0; i < NR_TP; i++) {
v = calc_gray_order[i];
g_curve_degree = dim_lut[ilum].g_curve_degree;
/* TODO : need to optimize */
if (v == TP_V255)
tpout[v].L = (dim_lut[ilum].base_luminance) ? ((u64)dim_lut[ilum].base_luminance << BIT_SHIFT) :
(u64)TARGET_LUMINANCE * (u64)gamma_curve_value(TARGET_G_CURVE_DEGREE, dim_lut[ilum].base_gray);
else if (v == TP_VT)
tpout[v].L = (luminance < 251) ? 0 :
((tpout[TP_V255].L * (u64)gamma_curve_value(g_curve_degree, tp[v].level)) >> BIT_SHIFT);
else
tpout[v].L = (tpout[TP_V255].L * (u64)gamma_curve_value(g_curve_degree, tp[v].level)) >> BIT_SHIFT;
/*
* In two special case, M_GRAY should be same with tp value
* 1. tp is VT (v == TP_VT)
* 2. tp's value is 1 (tp[v].level == 1)
*/
tpout[v].M_GRAY = (tp[v].level <= 1) ? tp[v].level :
find_gray_scale_gamma_value(gray_scale_lut, tpout[v].L) +
dim_lut[ilum].gray_scale_offset[v];
if (tpout[v].M_GRAY > 255)
tpout[v].M_GRAY = 255;
for_each_color(c) {
dim_lut[ilum].tpout[v].vout[c] =
(v == TP_VT) ? tp[v].vout[c] :
gray_scale_lut[tpout[v].M_GRAY].vout[c];
}
}
for (i = 0; i < NR_TP; i++) {
v = calc_vout_order[i];
for_each_color(c) {
dim_lut[ilum].tpout[v].center[c] =
(v == TP_VT || v == TP_V0) ? tp[v].center[c] :
mtp_vregout_to_offset(dim_info, v, (enum color)c, ilum);
}
}
#ifdef DEBUG_DIMMING
print_dimming_tp_output(&dim_lut[ilum], tp, 1);
#endif
}
return 0;
}
int alloc_dimming_info(struct dimming_info *dim_info, int nr_tp, int nr_luminance)
{
int i;
dim_info->nr_tp = nr_tp;
dim_info->nr_luminance = nr_luminance;
dim_info->tp = kzalloc(sizeof(struct tp) * nr_tp, GFP_KERNEL);
/* allocation of struct dimming_lut lut and members */
dim_info->dim_lut =
(struct dimming_lut *)kzalloc(sizeof(struct dimming_lut) * nr_luminance, GFP_KERNEL);
for (i = 0; i < nr_luminance; i++) {
dim_info->dim_lut[i].tpout =
kzalloc(sizeof(struct dimming_tp_output) * nr_tp, GFP_KERNEL);
dim_info->dim_lut[i].gray_scale_offset =
(s32 *)kzalloc(sizeof(s32) * nr_tp, GFP_KERNEL);
dim_info->dim_lut[i].rgb_color_offset =
(s32 (*)[MAX_COLOR])kzalloc(sizeof(s32) * nr_tp * MAX_COLOR, GFP_KERNEL);
}
return 0;
}
int free_dimming_info(struct dimming_info *dim_info)
{
int i;
for (i = 0; i < dim_info->nr_luminance; i++) {
kfree(dim_info->dim_lut[i].tpout);
kfree(dim_info->dim_lut[i].gray_scale_offset);
kfree(dim_info->dim_lut[i].rgb_color_offset);
}
kfree(dim_info->dim_lut);
kfree(dim_info->tp);
dim_info->nr_luminance = 0;
dim_info->nr_tp = 0;
return 0;
}
int find_tp_index(struct tp *tp, int nr_tp, char *name)
{
int i;
for (i = 0; i < nr_tp; i++)
if (!strcmp(tp[i].name, name))
return i;
return -1;
}
void prepare_dim_info(struct dimming_info *dim_info)
{
#ifdef DEBUG_DIMMING
int i, len;
char buf[MAX_PRINT_BUF_SIZE];
#endif
struct tp *tp = dim_info->tp;
NR_TP = dim_info->nr_tp;
NR_LUMINANCE = dim_info->nr_luminance;
TARGET_LUMINANCE = dim_info->target_luminance;
TARGET_G_CURVE_DEGREE = dim_info->target_g_curve_degree;
TP_VT = find_tp_index(tp, dim_info->nr_tp, "VT");
TP_V0 = find_tp_index(tp, dim_info->nr_tp, "V0");
TP_V255 = dim_info->nr_tp - 1;
#ifdef DEBUG_DIMMING
pr_info("NR_TP %d\n", NR_TP);
#endif
if (!calc_gray_order) {
calc_gray_order = generate_order(NR_TP - 1, 0, NR_TP - 2);
if (unlikely(!calc_gray_order)) {
pr_err("%s, calc_gray_order not exist!!\n", __func__);
return;
}
#ifdef DEBUG_DIMMING
len = snprintf(buf, MAX_PRINT_BUF_SIZE, "[calc_gray_order]\n");
for (i = 0; i < NR_TP; i++)
len += snprintf(buf + len,
max(MAX_PRINT_BUF_SIZE - len, 0), "[%d]%s ", i,
((calc_gray_order[i] >= 0 && calc_gray_order[i] < NR_TP) ?
tp[calc_gray_order[i]].name : "TP_ERR"));
pr_info("%s\n", buf);
#endif
}
if (!calc_vout_order) {
calc_vout_order = generate_order(0, NR_TP - 1, 1);
if (unlikely(!calc_vout_order)) {
pr_err("%s, calc_vout_order not exist!!\n", __func__);
return;
}
#ifdef DEBUG_DIMMING
len = snprintf(buf, MAX_PRINT_BUF_SIZE, "[calc_vout_order]\n");
for (i = 0; i < NR_TP; i++)
len += snprintf(buf + len,
max(MAX_PRINT_BUF_SIZE - len, 0), "[%d]%s ", i,
((calc_vout_order[i] >= 0 && calc_vout_order[i] < NR_TP) ?
tp[calc_vout_order[i]].name : "TP_ERR"));
pr_info("%s\n", buf);
#endif
}
}
void print_tp_lut(struct dimming_info *dim_info)
{
int i, len, col, ncol, *fields;
char buf[MAX_PRINT_BUF_SIZE];
ncol = dim_info->tp_lut_info.ncol;
fields = dim_info->tp_lut_info.fields;
for (i = 0; i < dim_info->nr_tp; i++) {
for (len = 0, col = 0; col < ncol; col++) {
switch (fields[col]) {
case TP_LUT_LEVEL:
len += snprintf(buf + len,
max(MAX_PRINT_BUF_SIZE - len, 0),
"%-3d ", dim_info->tp[i].level);
break;
case TP_LUT_VOLT_SRC:
len += snprintf(buf + len,
max(MAX_PRINT_BUF_SIZE - len, 0),
"%-10s ", volt_src_name[dim_info->tp[i].volt_src]);
break;
case TP_LUT_GAMMA_CENTER:
len += snprintf(buf + len,
max(MAX_PRINT_BUF_SIZE - len, 0),
"%-3X %-3X %-3X ",
dim_info->tp[i].center[RED],
dim_info->tp[i].center[GREEN],
dim_info->tp[i].center[BLUE]);
break;
case TP_LUT_GAMMA_CALC:
len += snprintf(buf + len,
max(MAX_PRINT_BUF_SIZE - len, 0),
"%-3d %-3d", dim_info->tp[i].numerator,
dim_info->tp[i].denominator);
break;
default:
break;
}
}
pr_info("%s\n", buf);
}
}
void print_dim_lut(struct dimming_info *dim_info)
{
int ilum, i, c, len, col, ncol, *fields;
char buf[MAX_PRINT_BUF_SIZE];
struct dimming_lut_info *dim_lut_info;
dim_lut_info = &dim_info->dim_lut_info;
ncol = dim_info->dim_lut_info.ncol;
fields = dim_info->dim_lut_info.fields;
for (ilum = 0; ilum < dim_info->nr_luminance; ilum++) {
struct dimming_lut *arr = &dim_info->dim_lut[ilum];
for (len = 0, col = 0; col < ncol; col++) {
switch (fields[col]) {
case DIM_LUT_LUMINANCE:
len += snprintf(buf + len,
max(MAX_PRINT_BUF_SIZE - len, 0),
"%-3d ", arr->luminance);
break;
case DIM_LUT_AOR:
len += snprintf(buf + len,
max(MAX_PRINT_BUF_SIZE - len, 0),
"%04X ", arr->aor);
break;
case DIM_LUT_BASE_LUMINANCE:
len += snprintf(buf + len,
max(MAX_PRINT_BUF_SIZE - len, 0),
"%3d ", arr->base_luminance);
break;
case DIM_LUT_GAMMA:
len += snprintf(buf + len,
max(MAX_PRINT_BUF_SIZE - len, 0),
"%3d ", gamma_curve_lut[arr->g_curve_degree].gamma);
break;
case DIM_LUT_GRAY_SCALE_OFFSET:
for_each_range(i, dim_lut_info->gray_scale_offset_range)
len += snprintf(buf + len,
max(MAX_PRINT_BUF_SIZE - len, 0),
"%3d ", arr->gray_scale_offset[i]);
len += snprintf(buf + len,
max(MAX_PRINT_BUF_SIZE - len, 0), "\t ");
break;
case DIM_LUT_COLOR_SHIFT_OFFSET:
for_each_range(i, dim_lut_info->rgb_color_offset_range)
for_each_color(c)
len += snprintf(buf + len,
max(MAX_PRINT_BUF_SIZE - len, 0),
"%3d ", arr->rgb_color_offset[i][c]);
break;
default:
break;
}
}
pr_info("%s\n", buf);
}
}
void print_dimming_info(struct dimming_info *dim_info, int tag)
{
static char buf[MAX_PRINT_BUF_SIZE];
size_t i;
int len, col, ncol, *fields;
struct dimming_lut_info *dim_lut_info = &dim_info->dim_lut_info;
pr_info("%s\n", tag_name[tag]);
switch (tag) {
case TAG_DIM_GLOBAL_INFO_START:
pr_info("%-15s %u\n", global_dim_info_name[GLOBAL_DIM_INFO_NR_TP],
dim_info->nr_tp);
pr_info("%-15s %u\n", global_dim_info_name[GLOBAL_DIM_INFO_NR_LUMINANCE],
dim_info->nr_luminance);
pr_info("%-15s %lld %d\n", global_dim_info_name[GLOBAL_DIM_INFO_VREGOUT],
dim_info->vregout, DIMMING_BITSHIFT);
pr_info("%-15s %lld %d\n", global_dim_info_name[GLOBAL_DIM_INFO_VREF],
dim_info->vref, DIMMING_BITSHIFT);
pr_info("%-15s %d\n", global_dim_info_name[GLOBAL_DIM_INFO_GAMMA],
gamma_curve_lut[(int)dim_info->target_g_curve_degree].gamma);
len = snprintf(buf, MAX_PRINT_BUF_SIZE,
"%-15s ", global_dim_info_name[GLOBAL_DIM_INFO_VT_VOLTAGE]);
for (i = 0; i < ARRAY_SIZE(dim_info->vt_voltage); i++)
len += snprintf(buf + len,
max(MAX_PRINT_BUF_SIZE - len, 0),
"%d ", dim_info->vt_voltage[i]);
pr_info("%s\n", buf);
break;
case TAG_TP_LUT_INFO_START:
ncol = dim_info->tp_lut_info.ncol;
fields = dim_info->tp_lut_info.fields;
for (len = 0, col = 0; col < ncol; col++)
len += snprintf(buf + len,
max(MAX_PRINT_BUF_SIZE - len, 0),
"%s ", tp_lut_field_name[fields[col]]);
pr_info("%s\n", buf);
break;
case TAG_TP_LUT_START:
print_tp_lut(dim_info);
break;
case TAG_DIM_LUT_INFO_START:
ncol = dim_lut_info->ncol;
fields = dim_lut_info->fields;
for (len = 0, col = 0; col < ncol; col++) {
len += snprintf(buf + len,
max(MAX_PRINT_BUF_SIZE - len, 0),
"%s ", dim_lut_field_name[fields[col]]);
if (fields[col] == DIM_LUT_GRAY_SCALE_OFFSET) {
len += snprintf(buf + len, max(MAX_PRINT_BUF_SIZE - len, 0), "%s %s ",
dim_info->tp[dim_lut_info->gray_scale_offset_range.from].name,
dim_info->tp[dim_lut_info->gray_scale_offset_range.to -
dim_lut_info->gray_scale_offset_range.step].name);
} else if (fields[col] == DIM_LUT_COLOR_SHIFT_OFFSET) {
len += snprintf(buf + len, max(MAX_PRINT_BUF_SIZE - len, 0), "%s %s ",
dim_info->tp[dim_lut_info->rgb_color_offset_range.from].name,
dim_info->tp[dim_lut_info->rgb_color_offset_range.to -
dim_lut_info->rgb_color_offset_range.step].name);
}
}
pr_info("%s\n", buf);
break;
case TAG_DIM_LUT_START:
print_dim_lut(dim_info);
break;
case TAG_MTP_OFFSET_START:
print_mtp_offset(dim_info);
break;
case TAG_GAMMA_CENTER_START:
print_tpout_center(dim_info);
break;
case TAG_TP_VOLT_START:
print_tp_vout(dim_info);
break;
case TAG_GRAY_SCALE_VOLT_START:
print_gray_scale_vout(dim_info->gray_scale_lut);
break;
}
pr_info("%s\n\n", tag_name[tag + 1]);
}
void print_input_data(struct dimming_info *dim_info)
{
print_dimming_info(dim_info, TAG_DIM_GLOBAL_INFO_START);
print_dimming_info(dim_info, TAG_TP_LUT_INFO_START);
print_dimming_info(dim_info, TAG_TP_LUT_START);
print_dimming_info(dim_info, TAG_DIM_LUT_INFO_START);
print_dimming_info(dim_info, TAG_DIM_LUT_START);
#if 0
print_dimming_info(dim_info, TAG_MTP_OFFSET_START);
#endif
}
void print_tbl(struct dimming_info *dim_info, s32 (*tbl)[MAX_COLOR])
{
int i, len;
char buf[MAX_PRINT_BUF_SIZE];
for (i = 0, len = 0; i < dim_info->nr_tp; i++)
len += snprintf(buf + len,
max(MAX_PRINT_BUF_SIZE - len, 0),
"0x%03X 0x%03X 0x%03X\n",
tbl[i][RED], tbl[i][GREEN], tbl[i][RED]);
pr_info("%s\n", buf);
}
int init_dimming_info(struct dimming_info *dim_info, struct dimming_init_info *src)
{
int ilum;
memcpy(dim_info->vt_voltage, src->vt_voltage, sizeof(src->vt_voltage));
memcpy(dim_info->v0_voltage, src->v0_voltage, sizeof(src->v0_voltage));
dim_info->vregout = src->vregout;
dim_info->vref = src->vref;
dim_info->nr_tp = src->nr_tp;
dim_info->tp = src->tp;
dim_info->nr_luminance = src->nr_luminance;
dim_info->dim_lut = src->dim_lut;
/* allocation of struct dimming_lut lut and members */
for (ilum = 0; ilum < dim_info->nr_luminance; ilum++) {
dim_info->dim_lut[ilum].tpout = (struct dimming_tp_output *)
kzalloc(sizeof(struct dimming_tp_output) * dim_info->nr_tp, GFP_KERNEL);
}
dim_info->target_luminance = src->target_luminance;
dim_info->target_g_curve_degree =
gamma_value_to_degree(src->target_gamma);
if (unlikely(dim_info->target_g_curve_degree == GAMMA_NONE)) {
pr_err("%s, invalid target gamma degree %d\n",
__func__, dim_info->target_g_curve_degree);
return -EINVAL;
}
return 0;
}
int init_dimming_mtp(struct dimming_info *dim_info, s32 (*mtp)[MAX_COLOR])
{
int i, c;
for (i = 0; i < dim_info->nr_tp; i++)
for_each_color(c)
dim_info->tp[i].offset[c] = mtp[i][c];
pr_info("%s, init mtp offset\n", __func__);
print_mtp_offset(dim_info);
return 0;
}
int init_dimming_hbm_info(struct dimming_info *dim_info, s32 (*hbm_gamma_tbl)[MAX_COLOR], u32 hbm_luminance)
{
int i, c, nr_tp;
if (unlikely(!dim_info || !hbm_gamma_tbl)) {
pr_err("%s, invalid parameter\n", __func__);
return -EINVAL;
}
if (unlikely(hbm_luminance < dim_info->target_luminance)) {
pr_err("%s, out of range (hbm_luminance %d < target_luminance %d)\n",
__func__, hbm_luminance,
dim_info->target_luminance);
return -EINVAL;
}
nr_tp = dim_info->nr_tp;
if (!dim_info->hbm_gamma_tbl)
dim_info->hbm_gamma_tbl =
(s32 (*)[MAX_COLOR])kzalloc(sizeof(s32) * nr_tp * MAX_COLOR, GFP_KERNEL);
for (i = 0; i < dim_info->nr_tp; i++)
for_each_color(c)
dim_info->hbm_gamma_tbl[i][c] = hbm_gamma_tbl[i][c];
dim_info->hbm_luminance = hbm_luminance;
#ifdef DEBUG_DIMMING
pr_info("%s, init hbm gamma table (hbm_luminance %d)\n",
__func__, hbm_luminance);
print_hbm_gamma_tbl(dim_info);
#endif
return 0;
}
int process_dimming(struct dimming_info *dim_info)
{
int i, c, v;
struct tp *tp;
s64 VREGOUT;
s64 VREF;
#ifdef DEBUG_EXCUTION_TIME
mytime_t ts, te;
gettime(ts);
#endif
if (unlikely(!dim_info)) {
pr_err("%s, invalid dim_info\n", __func__);
return -EINVAL;
}
VREGOUT = dim_info->vregout;
VREF = dim_info->vref;
prepare_dim_info(dim_info);
#ifdef DEBUG_DIMMING
print_dimming_info(dim_info, TAG_MTP_OFFSET_START);
#endif
tp = dim_info->tp;
if (unlikely(!tp)) {
pr_err("%s, tuning point not prepared\n", __func__);
return -EINVAL;
}
/*
* Generate RGB voltage output table of TP using MTP(TP_VT ~ TP_V255).
*/
for (i = 0; i < dim_info->nr_tp; i++) {
v = calc_vout_order[i];
for_each_color(c) {
tp[v].vout[c] = mtp_offset_to_vregout(dim_info, v, (enum color)c);
if (tp[v].vout[c] < 0) {
pr_warn("%s, invalid vout[%s][%s] %lld\n",
__func__, tp[v].name, color_name[c],
tp[v].vout[c]);
}
dim_info->gray_scale_lut[tp[v].level].vout[c] =
(v == TP_VT) ? (u64)VREGOUT : (u64)tp[v].vout[c];
}
}
#ifdef DEBUG_DIMMING
print_dimming_info(dim_info, TAG_TP_VOLT_START);
#endif
/*
* Fill in the all RGB voltate output of gray scale by interpolation.
*/
generate_gray_scale(dim_info);
#ifdef DEBUG_DIMMING
print_dimming_info(dim_info, TAG_GRAY_SCALE_VOLT_START);
#endif
/*
* Generate mtp offset of given luminances.
*/
generate_gamma_table(dim_info);
#if 0
print_dimming_tp_output(dim_lut, dim_info->tp, dim_info->nr_luminance);
print_dimming_info(dim_info, TAG_MTP_OFFSET_START);
#endif
#ifdef DEBUG_DIMMING_RESULT
print_dimming_info(dim_info, TAG_GAMMA_CENTER_START);
#endif
#ifdef DEBUG_EXCUTION_TIME
gettime(te);
pr_info("elapsed time %u us\n", difftime(ts, te));
#endif
/* TODO : free allocated heap */
return 0;
}