1697 lines
43 KiB
C
Executable file
1697 lines
43 KiB
C
Executable file
/*
|
|
* 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 "panel_drv.h"
|
|
#include "panel_debug.h"
|
|
#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;
|
|
}
|
|
EXPORT_SYMBOL(disp_pow);
|
|
|
|
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);
|
|
|
|
panel_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)) {
|
|
panel_err("den should not be zero (%llu %llu)\n", num, den);
|
|
return 0;
|
|
}
|
|
|
|
tnum = num * sign_num;
|
|
tden = den * sign_den;
|
|
|
|
if (tden >= (1ULL << 31))
|
|
panel_err("out of range denominator %llu\n", tden);
|
|
|
|
do_div(tnum, (u32)tden);
|
|
|
|
return sign * tnum;
|
|
}
|
|
EXPORT_SYMBOL(disp_div64);
|
|
|
|
#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)) {
|
|
panel_err("den should not be zero (%llu %llu %u)\n", 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) {
|
|
panel_err("failed to allocate memory\n");
|
|
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;
|
|
|
|
panel_info("generate %d gamma\n", 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);
|
|
panel_info("%d ", gamma_curve[i]);
|
|
if (!((i + 1) % 8))
|
|
panel_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)) {
|
|
panel_err("gamma %d curve not found\n", gamma);
|
|
return -1;
|
|
}
|
|
|
|
#ifdef GENERATE_GAMMA_CURVE
|
|
if (!gamma_curve_lut[index].gamma_curve_table) {
|
|
panel_info("generate gamma curve %d\n", 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) {
|
|
panel_err("gamma %d not exist\n", 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;
|
|
|
|
panel_info("generate gamma curve %d\n", 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++)
|
|
panel_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_center_gamma(struct dimming_info *dim_info)
|
|
{
|
|
int i;
|
|
panel_info("[CENTER_GAMMA]\n");
|
|
|
|
for (i = 0; i < dim_info->nr_tp; i++)
|
|
panel_info("%-7s %-4X %-4X %-4X\n",
|
|
dim_info->tp[i].name,
|
|
dim_info->tp[i].center[RED],
|
|
dim_info->tp[i].center[GREEN],
|
|
dim_info->tp[i].center[BLUE]);
|
|
}
|
|
|
|
void print_hbm_gamma_tbl(struct dimming_info *dim_info)
|
|
{
|
|
int i;
|
|
|
|
if (unlikely(!dim_info->hbm_gamma_tbl)) {
|
|
panel_warn("hbm_gamma_tbl not exist\n");
|
|
return;
|
|
}
|
|
|
|
if (unlikely(!dim_info->tp)) {
|
|
panel_warn("tp not exist\n");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < dim_info->nr_tp; i++)
|
|
panel_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)) {
|
|
panel_err("invalid dim_info\n");
|
|
return;
|
|
}
|
|
|
|
lut = dim_info->dim_lut;
|
|
nr_tp = dim_info->nr_tp;
|
|
nr_luminance = dim_info->nr_luminance;
|
|
|
|
if (unlikely(!lut)) {
|
|
panel_err("invalid dim_lut\n");
|
|
return;
|
|
}
|
|
|
|
if ((nr_tp * 15UL) >= ARRAY_SIZE(buf)) {
|
|
panel_err("exceed linebuf size (%lu)\n", 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
|
|
panel_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)) {
|
|
panel_err("invalid dim_info\n");
|
|
return;
|
|
}
|
|
|
|
lut = dim_info->dim_lut;
|
|
nr_tp = dim_info->nr_tp;
|
|
nr_luminance = dim_info->nr_luminance;
|
|
|
|
if (unlikely(!lut)) {
|
|
panel_err("invalid dim_lut\n");
|
|
return;
|
|
}
|
|
|
|
if ((nr_tp * 15UL) >= ARRAY_SIZE(buf)) {
|
|
panel_err("exceed linebuf size (%lu)\n", 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]);
|
|
}
|
|
panel_info("%s\n", buf);
|
|
}
|
|
}
|
|
|
|
void print_tp_vout(struct dimming_info *dim_info)
|
|
{
|
|
int i, nr_tp;
|
|
struct tp *tp;
|
|
|
|
if (unlikely(!dim_info)) {
|
|
panel_err("invalid dim_info\n");
|
|
return;
|
|
}
|
|
|
|
tp = dim_info->tp;
|
|
nr_tp = dim_info->nr_tp;
|
|
if (unlikely(!tp)) {
|
|
panel_err("invalid tp\n");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < nr_tp; i++)
|
|
panel_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++) {
|
|
panel_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) {
|
|
panel_err("invalid tp %d\n", 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];
|
|
panel_warn("unknown tp[%d].volt_src %d\n", v, tp[v].volt_src);
|
|
}
|
|
|
|
if (v == TP_VT) {
|
|
s32 offset = tp[v].center[c] + tp[v].offset[c];
|
|
if ((offset > 0xF) || (offset < 0x0)) {
|
|
panel_warn("ivt (%d) out of range(0x0 ~ 0xF) replace to %s\n",
|
|
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)) {
|
|
panel_warn("ivt (%d) out of range(0x0 ~ 0xF) replace to %s\n",
|
|
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
|
|
panel_info("%s %s vreg %lld, vsrc %lld, num %lld, den %lld, res %lld\n",
|
|
tp[(int)v].name, color_name[(int)c], vreg, vsrc, num, den, res);
|
|
#endif
|
|
if (res < 0)
|
|
panel_err("res %lld should not be minus value\n", 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) {
|
|
panel_err("invalid tp %d\n", v);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (luminance_index < 0 || luminance_index >= NR_LUMINANCE) {
|
|
panel_err("out of range luminance index (%d)\n", 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];
|
|
panel_warn("unknown tp[%d].volt_src %d\n", 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);
|
|
panel_dbg("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
|
|
panel_info("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)) {
|
|
panel_err("gray_scale_lut not exist\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((gray_scale_lut[l].gamma_value > gamma_value) ||
|
|
(gray_scale_lut[r].gamma_value < gamma_value)) {
|
|
panel_err("out of range([l:%d, r:%d], [%lld, %lld]) candela(%lld)\n",
|
|
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_round(s64 from, s64 to, int cur_step, int total_step)
|
|
{
|
|
s64 num, den, sign = 1LL;
|
|
|
|
if (cur_step == total_step || from == to)
|
|
return to;
|
|
|
|
if (from > to)
|
|
sign = -1LL;
|
|
num = abs(to - from) * (s64)cur_step + (total_step / 2);
|
|
den = (s64)total_step;
|
|
num = from + sign * disp_div64(num, den);
|
|
|
|
return num;
|
|
}
|
|
|
|
s64 disp_interpolation64(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;
|
|
}
|
|
EXPORT_SYMBOL(disp_interpolation64);
|
|
|
|
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)) {
|
|
panel_err("invalid parameter (tp %d, nr_tp %d, src %d, ofs %d, out %d)\n",
|
|
!!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
|
|
panel_info("src %d, ofs %d, out %d\n",
|
|
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)) {
|
|
panel_err("invalid parameter (from %p, to %p, out %p)\n", from, to, out);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (unlikely(nr_tp < 0 || cur_step > total_step)) {
|
|
panel_err("out of range (nr_tp %d, cur_step %d, total_step %d)\n",
|
|
nr_tp, cur_step, total_step);
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < nr_tp; i++) {
|
|
for_each_color(c) {
|
|
out[i][c] =
|
|
disp_interpolation64(from[i][c], to[i][c], cur_step, total_step);
|
|
#ifdef DEBUG_DIMMING
|
|
panel_info("from %d, to %d, out %d, cur_step %d, total_step %d\n",
|
|
from[i][c], to[i][c], out[i][c], cur_step, total_step);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int gamma_table_interpolation_round(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)) {
|
|
panel_err("invalid parameter (from %p, to %p, out %p)\n", from, to, out);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (unlikely(nr_tp < 0 || cur_step > total_step)) {
|
|
panel_err("out of range (nr_tp %d, cur_step %d, total_step %d)\n",
|
|
nr_tp, cur_step, total_step);
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < nr_tp; i++) {
|
|
for_each_color(c) {
|
|
out[i][c] =
|
|
interpolation_round(from[i][c], to[i][c], cur_step, total_step);
|
|
#ifdef DEBUG_DIMMING
|
|
panel_info("from %d, to %d, out %d, cur_step %d, total_step %d\n",
|
|
from[i][c], to[i][c], out[i][c], cur_step, total_step);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(gamma_table_interpolation_round);
|
|
|
|
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 = disp_interpolation64(v_lower, v_upper, cur_step, total_step);
|
|
#ifdef DEBUG_DIMMING
|
|
panel_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) {
|
|
panel_warn("from %2lld.%4lld, to %2lld.%4lld (idx %-3d %-3d), cur_step %-3d, total_step %-3d\n",
|
|
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
|
|
panel_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)) {
|
|
panel_err("invalid dim_info\n");
|
|
return -1;
|
|
}
|
|
|
|
dim_lut = dim_info->dim_lut;
|
|
nr_luminance = dim_info->nr_luminance;
|
|
|
|
if (unlikely(!dim_lut)) {
|
|
panel_err("invalid dim_lut\n");
|
|
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)) {
|
|
panel_err("invalid dim_info\n");
|
|
return 0;
|
|
}
|
|
|
|
dim_lut = dim_info->dim_lut;
|
|
nr_luminance = dim_info->nr_luminance;
|
|
|
|
if (unlikely(!dim_lut)) {
|
|
panel_err("invalid dim_lut\n");
|
|
return 0;
|
|
}
|
|
|
|
if (index >= nr_luminance) {
|
|
panel_warn("exceed max %d, index %d\n", 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)) {
|
|
panel_err("invalid tp or dim_lut\n");
|
|
return;
|
|
}
|
|
|
|
if (unlikely(ilum < 0)) {
|
|
panel_err("luminance(%d) not found\n", 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)) {
|
|
panel_err("invalid tp or dim_lut\n");
|
|
return;
|
|
}
|
|
|
|
target_gamma_tbl =
|
|
(s32 (*)[MAX_COLOR])kzalloc(sizeof(s32) * nr_tp * MAX_COLOR, GFP_KERNEL);
|
|
if (unlikely(!target_gamma_tbl)) {
|
|
panel_err("failed to allocate target_gamma_tbl\n");
|
|
goto err;
|
|
}
|
|
|
|
output_gamma_tbl =
|
|
(s32 (*)[MAX_COLOR])kzalloc(sizeof(s32) * nr_tp * MAX_COLOR, GFP_KERNEL);
|
|
if (unlikely(!output_gamma_tbl)) {
|
|
panel_err("failed to allocate output_gamma_tbl\n");
|
|
goto err_alloc;
|
|
}
|
|
|
|
/* make target gamma table */
|
|
ilum = find_luminance(dim_info, dim_info->target_luminance);
|
|
if (unlikely(ilum < 0)) {
|
|
panel_err("target_luminance(%d) not found\n", 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)) {
|
|
panel_err("invalid dim_info or output\n");
|
|
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
|
|
panel_warn("out of range (hbm_luminance %d, luminance %d)\n",
|
|
dim_info->hbm_luminance, luminance);
|
|
|
|
return;
|
|
}
|
|
EXPORT_SYMBOL(get_dimming_gamma);
|
|
|
|
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++) {
|
|
panel_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]);
|
|
}
|
|
}
|
|
panel_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)) {
|
|
panel_err("calc_gray_order not exist!!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (unlikely(!calc_vout_order)) {
|
|
panel_err("calc_vout_order not exist!!\n");
|
|
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
|
|
panel_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)) {
|
|
panel_err("calc_gray_order not exist!!\n");
|
|
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"));
|
|
panel_info("%s\n", buf);
|
|
#endif
|
|
}
|
|
|
|
if (!calc_vout_order) {
|
|
calc_vout_order = generate_order(0, NR_TP - 1, 1);
|
|
if (unlikely(!calc_vout_order)) {
|
|
panel_err("calc_vout_order not exist!!\n");
|
|
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"));
|
|
panel_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;
|
|
}
|
|
}
|
|
panel_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;
|
|
}
|
|
}
|
|
panel_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;
|
|
|
|
panel_info("%s\n", tag_name[tag]);
|
|
switch (tag) {
|
|
case TAG_DIM_GLOBAL_INFO_START:
|
|
panel_info("%-15s %u\n", global_dim_info_name[GLOBAL_DIM_INFO_NR_TP],
|
|
dim_info->nr_tp);
|
|
panel_info("%-15s %u\n", global_dim_info_name[GLOBAL_DIM_INFO_NR_LUMINANCE],
|
|
dim_info->nr_luminance);
|
|
panel_info("%-15s %lld %d\n", global_dim_info_name[GLOBAL_DIM_INFO_VREGOUT],
|
|
dim_info->vregout, DIMMING_BITSHIFT);
|
|
panel_info("%-15s %lld %d\n", global_dim_info_name[GLOBAL_DIM_INFO_VREF],
|
|
dim_info->vref, DIMMING_BITSHIFT);
|
|
panel_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]);
|
|
panel_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]]);
|
|
panel_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);
|
|
}
|
|
}
|
|
panel_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;
|
|
}
|
|
panel_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]);
|
|
panel_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)) {
|
|
panel_err("invalid target gamma degree %d\n",
|
|
dim_info->target_g_curve_degree);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(init_dimming_info);
|
|
|
|
int init_dimming_mtp(struct dimming_info *dim_info, s32 (*mtp)[MAX_COLOR])
|
|
{
|
|
int i, c;
|
|
print_center_gamma(dim_info);
|
|
|
|
for (i = 0; i < dim_info->nr_tp; i++)
|
|
for_each_color(c)
|
|
dim_info->tp[i].offset[c] = mtp[i][c];
|
|
|
|
panel_info("init mtp offset\n");
|
|
print_mtp_offset(dim_info);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(init_dimming_mtp);
|
|
|
|
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)) {
|
|
panel_err("invalid parameter\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (unlikely(hbm_luminance < dim_info->target_luminance)) {
|
|
panel_err("out of range (hbm_luminance %d < target_luminance %d)\n",
|
|
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
|
|
panel_info("init hbm gamma table (hbm_luminance %d)\n", 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)) {
|
|
panel_err("invalid dim_info\n");
|
|
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)) {
|
|
panel_err("tuning point not prepared\n");
|
|
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) {
|
|
panel_warn("invalid vout[%s][%s] %lld\n", 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);
|
|
panel_info("elapsed time %u us\n", difftime(ts, te));
|
|
#endif
|
|
/* TODO : free allocated heap */
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(process_dimming);
|