/*
|
* drivers/video/sunxi/disp2/disp/de/lowlevel_v2x/de_fce.c
|
*
|
* Copyright (c) 2007-2019 Allwinnertech Co., Ltd.
|
* Author: zhengxiaobin <zhengxiaobin@allwinnertech.com>
|
*
|
* This software is licensed under the terms of the GNU General Public
|
* License version 2, as published by the Free Software Foundation, and
|
* may be copied, distributed, and modified under those terms.
|
*
|
* 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 "de_feat.h"
|
|
#ifdef CONFIG_DISP2_SUNXI_SUPPORT_ENAHNCE
|
|
#include "de_fce_type.h"
|
#include "de_rtmx.h"
|
#include "de_enhance.h"
|
#include "de_vep_table.h"
|
|
static volatile struct __fce_reg_t *fce_dev[DE_NUM][CHN_NUM];
|
|
static struct de_reg_blocks fce_para_block[DE_NUM][CHN_NUM];
|
static struct de_reg_blocks fce_celut_block[DE_NUM][CHN_NUM];
|
static struct de_reg_blocks fce_hist_block[DE_NUM][CHN_NUM];
|
|
static uintptr_t fce_hw_base[DE_NUM][CHN_NUM] = { {0} };
|
|
struct __hist_status_t *g_hist_status[DE_NUM][CHN_NUM];
|
|
/* for ce */
|
struct __ce_status_t *g_ce_status[DE_NUM][CHN_NUM];
|
static unsigned char *g_celut[DE_NUM][CHN_NUM];
|
static struct hist_data *hist_res[DE_NUM][CHN_NUM];
|
|
/*******************************************************************************
|
* function : de_fce_set_reg_base(unsigned int sel, unsigned int chno,
|
* unsigned int base)
|
* description : set fce reg base
|
* parameters :
|
* sel <rtmx select>
|
* chno <overlay select>
|
* base <reg base>
|
* return :
|
* success
|
******************************************************************************/
|
int de_fce_set_reg_base(unsigned int sel, unsigned int chno, void *base)
|
{
|
fce_dev[sel][chno] = (struct __fce_reg_t *) base;
|
|
return 0;
|
}
|
|
int de_fce_update_regs(unsigned int sel, unsigned int chno)
|
{
|
uintptr_t base;
|
|
if (fce_para_block[sel][chno].dirty == 0x1) {
|
memcpy((void *)fce_para_block[sel][chno].off,
|
fce_para_block[sel][chno].val,
|
fce_para_block[sel][chno].size);
|
fce_para_block[sel][chno].dirty = 0x0;
|
}
|
|
if (fce_celut_block[sel][chno].dirty == 0x1) {
|
base = fce_hw_base[sel][chno];
|
writel(0x1, (void __iomem *)(base + 0x28));
|
/* AHB access CE LUT */
|
memcpy((void *)fce_celut_block[sel][chno].off,
|
fce_celut_block[sel][chno].val,
|
fce_celut_block[sel][chno].size);
|
writel(0x0, (void __iomem *)(base + 0x28));
|
/* Module access CE LUT */
|
fce_celut_block[sel][chno].dirty = 0x0;
|
}
|
|
return 0;
|
}
|
|
int de_fce_init(unsigned int sel, unsigned int chno, uintptr_t reg_base)
|
{
|
uintptr_t base;
|
void *memory;
|
|
base = reg_base + (sel + 1) * 0x00100000 + FCE_OFST;
|
#if defined(CONFIG_INDEPENDENT_DE)
|
if (sel)
|
base = base - 0x00100000;
|
#endif
|
/* FIXME display path offset should be defined */
|
fce_hw_base[sel][chno] = base;
|
|
memory = kmalloc(sizeof(struct __fce_reg_t), GFP_KERNEL | __GFP_ZERO);
|
if (NULL == memory) {
|
__wrn("malloc fce[%d][%d] memory fail! size=0x%x\n", sel, chno,
|
(unsigned int)sizeof(struct __fce_reg_t));
|
return -1;
|
}
|
|
fce_para_block[sel][chno].off = base;
|
fce_para_block[sel][chno].val = memory;
|
fce_para_block[sel][chno].size = 0x34;
|
fce_para_block[sel][chno].dirty = 0;
|
|
fce_celut_block[sel][chno].off = base + 0x100;
|
fce_celut_block[sel][chno].val = memory + 0x100;
|
fce_celut_block[sel][chno].size = 0x100;
|
fce_celut_block[sel][chno].dirty = 0;
|
|
fce_hist_block[sel][chno].off = base + 0x200;
|
fce_hist_block[sel][chno].val = memory + 0x200;
|
fce_hist_block[sel][chno].size = 0x400;
|
fce_hist_block[sel][chno].dirty = 0;
|
|
de_fce_set_reg_base(sel, chno, memory);
|
|
/* hist */
|
g_hist_status[sel][chno] =
|
kmalloc(sizeof(struct __hist_status_t),
|
GFP_KERNEL | __GFP_ZERO);
|
if (NULL == g_hist_status[sel][chno]) {
|
__wrn("malloc g_hist_status[%d][%d] memory fail! size=0x%x\n",
|
sel, chno, (unsigned int)sizeof(struct __hist_status_t));
|
return -1;
|
}
|
|
g_hist_status[sel][chno]->runtime = 0;
|
g_hist_status[sel][chno]->isenable = 0;
|
g_hist_status[sel][chno]->twohistready = 0;
|
|
g_hist[sel][chno] = kmalloc(1024, GFP_KERNEL | __GFP_ZERO);
|
if (NULL == g_hist[sel][chno]) {
|
__wrn("malloc hist[%d][%d] memory fail! size=0x%x\n",
|
sel, chno, 1024);
|
return -1;
|
}
|
g_hist_p[sel][chno] = kmalloc(1024, GFP_KERNEL | __GFP_ZERO);
|
if (NULL == g_hist_p[sel][chno]) {
|
__wrn("malloc g_hist_p[%d][%d] memory fail! size=0x%x\n", sel,
|
chno, 1024);
|
return -1;
|
}
|
/* ce */
|
g_ce_status[sel][chno] =
|
kmalloc(sizeof(struct __ce_status_t), GFP_KERNEL | __GFP_ZERO);
|
if (NULL == g_ce_status[sel][chno]) {
|
__wrn("malloc g_ce_status[%d][%d] memory fail! size=0x%x\n",
|
sel, chno, (unsigned int)sizeof(struct __ce_status_t));
|
return -1;
|
}
|
|
g_ce_status[sel][chno]->isenable = 0;
|
|
g_celut[sel][chno] = kmalloc(256, GFP_KERNEL | __GFP_ZERO);
|
if (NULL == g_celut[sel][chno]) {
|
__wrn("malloc celut[%d][%d] memory fail! size=0x%x\n",
|
sel, chno, 256);
|
return -1;
|
}
|
|
hist_res[sel][chno] =
|
kmalloc(sizeof(struct hist_data), GFP_KERNEL | __GFP_ZERO);
|
if (NULL == hist_res[sel][chno]) {
|
__wrn("malloc hist_res[%d][%d] memory fail! size=0x%x\n", sel,
|
chno, (unsigned int)sizeof(struct hist_data));
|
return -1;
|
}
|
|
memset(hist_res[sel][chno], 0, sizeof(struct hist_data));
|
|
return 0;
|
}
|
|
/*******************************************************************************
|
* function : de_fce_enable(unsigned int sel, unsigned int chno,
|
* unsigned int en)
|
* description : enable/disable fce
|
* parameters :
|
* sel <rtmx select>
|
* chno <overlay select>
|
* en <enable: 0-diable; 1-enable>
|
* return :
|
* success
|
******************************************************************************/
|
int de_fce_enable(unsigned int sel, unsigned int chno, unsigned int en)
|
{
|
fce_dev[sel][chno]->ctrl.bits.en = en;
|
/* must enable when BWS_EN == 1 or ASE_EN == 1 */
|
fce_para_block[sel][chno].dirty = 1;
|
|
return 0;
|
}
|
|
/*******************************************************************************
|
* function : de_fce_set_size(unsigned int sel, unsigned int chno,
|
* unsigned int width, unsigned int height)
|
* description : set fce size
|
* parameters :
|
* sel <rtmx select>
|
* chno <overlay select>
|
* width <input width>
|
* height <input height>
|
* return :
|
* success
|
******************************************************************************/
|
int de_fce_set_size(unsigned int sel, unsigned int chno, unsigned int width,
|
unsigned int height)
|
{
|
fce_dev[sel][chno]->size.bits.width = width - 1;
|
fce_dev[sel][chno]->size.bits.height = height - 1;
|
fce_para_block[sel][chno].dirty = 1;
|
|
return 0;
|
}
|
|
/*******************************************************************************
|
* function : de_fce_set_window(unsigned int sel, unsigned int chno,
|
* unsigned int win_enable, struct de_rect window)
|
* description : set fce window
|
* parameters :
|
* sel <rtmx select>
|
* chno <overlay select>
|
* win_enable <enable: 0-window mode diable;
|
* 1-window mode enable>
|
* window <window rectangle>
|
* return :
|
* success
|
******************************************************************************/
|
int de_fce_set_window(unsigned int sel, unsigned int chno,
|
unsigned int win_enable, struct de_rect window)
|
{
|
fce_dev[sel][chno]->ctrl.bits.win_en = win_enable;
|
|
if (win_enable) {
|
fce_dev[sel][chno]->win0.bits.win_left = window.x;
|
fce_dev[sel][chno]->win0.bits.win_top = window.y;
|
fce_dev[sel][chno]->win1.bits.win_right =
|
window.x + window.w - 1;
|
fce_dev[sel][chno]->win1.bits.win_bot = window.y + window.h - 1;
|
}
|
fce_para_block[sel][chno].dirty = 1;
|
|
return 0;
|
}
|
|
/*******************************************************************************
|
* function : de_fce_csc_en(unsigned int sel, unsigned int chno,
|
* unsigned int csc_enable)
|
* description : set fce window
|
* parameters :
|
* sel <rtmx select>
|
* chno <overlay select>
|
* csc_enable <enable: 0-csc diable; 1-csc enable>
|
* return :
|
* success
|
******************************************************************************/
|
int de_fce_csc_en(unsigned int sel, unsigned int chno, unsigned int csc_enable)
|
{
|
fce_dev[sel][chno]->cscbypass.bits.csc_bypass = csc_enable;
|
fce_para_block[sel][chno].dirty = 1;
|
|
return 0;
|
}
|
|
/*******************************************************************************
|
* function : de_fce_set_para(unsigned int sel, unsigned int chno,
|
* unsigned int lce_en, unsigned int ftc_en,
|
* unsigned int ce_en, unsigned int hist_en)
|
* description : set fce para
|
* parameters :
|
* sel <rtmx select>
|
* chno <overlay select>
|
* fce_para <fce parameters>
|
* return :
|
* success
|
******************************************************************************/
|
int de_fce_set_para(unsigned int sel, unsigned int chno,
|
struct __fce_config_data fce_para)
|
{
|
fce_dev[sel][chno]->ctrl.bits.lce_en = fce_para.lce_en;
|
fce_dev[sel][chno]->ctrl.bits.ftc_en = fce_para.ftc_en;
|
fce_dev[sel][chno]->ctrl.bits.ce_en = fce_para.ce_en;
|
fce_dev[sel][chno]->ctrl.bits.hist_en = fce_para.hist_en;
|
|
fce_dev[sel][chno]->lcegain.bits.lce_gain = fce_para.lce_gain;
|
fce_dev[sel][chno]->lcegain.bits.lce_blend = fce_para.lce_blend;
|
fce_dev[sel][chno]->ftcgain.bits.ftc_gain1 = fce_para.ftc_gain1;
|
fce_dev[sel][chno]->ftcgain.bits.ftc_gain2 = fce_para.ftc_gain2;
|
fce_para_block[sel][chno].dirty = 1;
|
|
return 0;
|
}
|
|
/*******************************************************************************
|
* function : de_fce_get_hist(unsigned int sel, unsigned int chno,
|
* unsigned int hist[256], unsigned int *sum)
|
* description : get histogram result
|
* parameters :
|
* sel <rtmx select>
|
* chno <overlay select>
|
* hist[256] <frame histogram>
|
* sum <frame pixel value sum>
|
* return :
|
* success
|
******************************************************************************/
|
int de_fce_get_hist(unsigned int sel, unsigned int chno, unsigned int hist[256],
|
unsigned int *sum)
|
{
|
uintptr_t base;
|
base = fce_hw_base[sel][chno];
|
|
/* Read histogram to hist[256] */
|
memcpy((unsigned char *)hist,
|
(unsigned char *)fce_hist_block[sel][chno].off,
|
sizeof(unsigned int) * 256);
|
|
/* Read */
|
*sum = readl((void __iomem *)(base + 0x20));
|
|
return 0;
|
}
|
|
/*******************************************************************************
|
* function : de_fce_set_ce(unsigned int sel, unsigned int chno,
|
* unsigned int ce_lut[256])
|
* description : set ce lut, directly write to registers
|
* parameters :
|
* sel <rtmx select>
|
* chno <overlay select>
|
* ce_lut[256] <ce lut>
|
* return :
|
* success
|
******************************************************************************/
|
int de_fce_set_ce(unsigned int sel, unsigned int chno,
|
unsigned char ce_lut[256])
|
{
|
memcpy(fce_celut_block[sel][chno].val, (unsigned char *)ce_lut,
|
sizeof(unsigned char) * 256);
|
fce_celut_block[sel][chno].dirty = 1;
|
|
return 0;
|
}
|
|
/*******************************************************************************
|
* function : de_fce_info2para(unsigned int sharp,
|
* unsigned int auto_contrast, unsigned int auto_color,
|
* struct de_rect window, struct __fce_config_data *para)
|
* description : info->para conversion
|
* parameters :
|
* sharp/auto_contrast/auto_color <gain info from user>
|
* window <window info>
|
* para <bsp para>
|
* return :
|
* success
|
******************************************************************************/
|
int de_fce_info2para(unsigned int sharp, unsigned int auto_contrast,
|
unsigned int auto_color, struct de_rect window,
|
struct __fce_config_data *para)
|
{
|
int lce_mode, autoce_mode, ftc_mode;
|
int lce_para[LCE_PARA_NUM][LCE_MODE_NUM] = {
|
{0, 15}, /* gain */
|
{0, 84}, /* blending */
|
};
|
/* int ce_para[CE_PARA_NUM][CE_MODE_NUM]; //FIXME */
|
int auto_ce_para[AUTOCE_PARA_NUM][AUTOCE_MODE_NUM] = {
|
{0, 15, 10}, /* stretch_precent_thr */
|
{0, 15, 10}, /* suppress_precent_thr */
|
{0, 16, 8}, /* update_diff_thr */
|
{0, 384, 300}, /* slope_black_lmt */
|
{0, 384, 300}, /* slope_white_lmt */
|
};
|
int ftc_para[FTC_PARA_NUM][FTC_MODE_NUM] = {
|
{0, 10}, /* ftc_gain_1 */
|
{0, 5}, /* ftc_gain_2 */
|
};
|
|
/* auto_contrast */
|
lce_mode = (auto_contrast >> 16) & 0xf;
|
autoce_mode = (auto_contrast >> 4) & 0xf;
|
para->lce_en = lce_mode ? 1 : 0;
|
para->ce_en = (auto_contrast & 0xff) ? 1 : 0;
|
para->b_automode = autoce_mode ? 1 : 0;
|
para->hist_en = autoce_mode ? 1 : 0;
|
|
para->lce_gain = lce_para[0][lce_mode];
|
para->lce_blend = lce_para[1][lce_mode];
|
|
para->up_precent_thr = auto_ce_para[0][autoce_mode];
|
para->down_precent_thr = auto_ce_para[1][autoce_mode];
|
para->update_diff_thr = auto_ce_para[2][autoce_mode];
|
para->slope_black_lmt = auto_ce_para[3][autoce_mode];
|
para->slope_white_lmt = auto_ce_para[4][autoce_mode];
|
|
/* auto_color */
|
ftc_mode = auto_color & 0xf;
|
para->ftc_en = ftc_mode ? 1 : 0;
|
|
para->ftc_gain1 = ftc_para[0][ftc_mode];
|
para->ftc_gain2 = ftc_para[1][ftc_mode];
|
|
/* fce */
|
para->fce_en =
|
(para->lce_en | para->ce_en | para->hist_en | para->ftc_en) ? 1 : 0;
|
|
return 0;
|
}
|
|
static int toupdate(int hist_mean, int hist_diff, int diff_coeff,
|
int change_thr)
|
{
|
int toupdate = 0;
|
int diff_div = 1;
|
|
if (hist_diff < 0)
|
diff_div = 2;
|
else
|
diff_div = 1;
|
|
if (hist_mean > 96)
|
change_thr = 3 * change_thr / diff_div;
|
if (hist_mean > 64)
|
change_thr = 2 * change_thr / diff_div;
|
else
|
change_thr = change_thr / diff_div;
|
|
hist_diff = abs(hist_diff);
|
|
if (hist_diff > change_thr ||
|
(diff_coeff > 100 && hist_diff > change_thr / 8) ||
|
(diff_coeff > 64 && hist_diff > change_thr / 4) ||
|
(diff_coeff > 32 && hist_diff > change_thr / 2)) {
|
toupdate = 1;
|
} else
|
toupdate = 0;
|
|
return toupdate;
|
}
|
|
/*******************************************************************************
|
* function :auto_ce_model(unsigned int width, unsigned height,
|
* unsigned int sumcnt, unsigned int hist[256],
|
* unsigned int up_precent_thr, unsigned int down_precent_thr,
|
* unsigned char celut[256])
|
* description : Auto-ce Alg
|
* parameters :
|
* width <layer width>
|
* height <layer height>
|
* hist[256] <the latest frame histogram>
|
* sumcnt <the latest frame pixel value sum>
|
* up_precent_thr/down_precent_thr <ce para>
|
* celut <auto-ce result>
|
* return :
|
******************************************************************************/
|
static void
|
auto_ce_model(unsigned int width, unsigned height,
|
unsigned int sumcnt, unsigned int hist[256],
|
unsigned int up_precent_thr,
|
unsigned int down_precent_thr,
|
unsigned int change_thr, unsigned int lowest_black,
|
unsigned int highest_white, unsigned int ce_thr,
|
unsigned char celut[256], struct hist_data *p_hist_data)
|
{
|
static u32 hist_r[256], p[256];
|
u32 i;
|
u32 mean;
|
u32 total_pixel, total_pixel_r, total_size;
|
s32 uthr, lthr;
|
u32 rate, half;
|
int tmp;
|
unsigned int black_str_lv;
|
unsigned int white_str_lv;
|
|
if (height % 2 == 0) {
|
total_size = total_pixel = (width * height) >> 1;
|
} else {
|
total_size = total_pixel = (width * (height - 1)
|
+ (width + 1)) >> 1;
|
}
|
|
for (i = 0; i < lowest_black; i++)
|
total_pixel -= hist[i];
|
|
for (i = highest_white; i < 256; i++)
|
total_pixel -= hist[i];
|
|
/* less than 66% pixel in valid range */
|
if (total_size > 3 * total_pixel)
|
total_size = 0;
|
|
total_pixel_r = 0;
|
|
/* MEAN */
|
mean = total_pixel / (highest_white - lowest_black);
|
|
/* picture too small, can't ce */
|
if ((mean == 0) || (p_hist_data->hist_mean < ce_thr)
|
|| total_size == 0) {
|
for (i = 0; i < 256; i++)
|
celut[i] = i;
|
} else {
|
int black_thr;
|
|
uthr = mean + mean * up_precent_thr / 100;
|
lthr = mean - mean * down_precent_thr / 100;
|
|
black_thr = lthr;
|
white_str_lv = lthr * 1 / 3;
|
|
if (p_hist_data->hist_mean > 96)
|
black_str_lv = black_thr;
|
else if (p_hist_data->hist_mean > 64)
|
black_str_lv = black_thr * 2 / 3;
|
else if (p_hist_data->hist_mean < 21)
|
black_str_lv = black_thr * 1 / 3;
|
else
|
black_str_lv = black_thr * 1 / 3
|
+ (p_hist_data->hist_mean * black_thr / (64 * 3));
|
|
/* generate p */
|
if (hist[lowest_black] > mean)
|
hist_r[lowest_black] = mean;
|
else if (hist[lowest_black] > black_str_lv)
|
hist_r[lowest_black] = hist[lowest_black];
|
else
|
hist_r[lowest_black] = black_str_lv;
|
|
total_pixel_r = hist_r[lowest_black];
|
p[lowest_black] = hist_r[lowest_black];
|
|
/* black zone */
|
for (i = lowest_black + 1; i < p_hist_data->black_thr0; i++) {
|
if (hist[i] > mean)
|
hist_r[i] = mean;
|
else if (hist[i] > black_str_lv)
|
hist_r[i] = hist[i];
|
else
|
hist_r[i] = black_str_lv;
|
|
total_pixel_r = total_pixel_r + hist_r[i];
|
p[i] = p[i - 1] + hist_r[i];
|
}
|
|
for (i = p_hist_data->black_thr0; i < p_hist_data->black_thr1;
|
i++) {
|
if (hist[i] > mean)
|
hist_r[i] = mean;
|
else if (hist[i] > lthr)
|
hist_r[i] = hist[i];
|
else
|
hist_r[i] = lthr;
|
|
total_pixel_r = total_pixel_r + hist_r[i];
|
p[i] = p[i - 1] + hist_r[i];
|
}
|
|
if (p_hist_data->white_thr0 >= 256) {
|
__wrn("p_hist_data->white_thr0(%d) >= 256",
|
p_hist_data->white_thr0);
|
p_hist_data->white_thr0 = 255;
|
}
|
for (i = p_hist_data->black_thr1; i < p_hist_data->white_thr0;
|
i++) {
|
if (hist[i] > uthr)
|
hist_r[i] = uthr;
|
else if (hist[i] > lthr)
|
hist_r[i] = hist[i];
|
else
|
hist_r[i] = lthr;
|
|
total_pixel_r = total_pixel_r + hist_r[i];
|
p[i] = p[i - 1] + hist_r[i];
|
}
|
|
/* white zone */
|
for (i = p_hist_data->white_thr0; i < p_hist_data->white_thr1;
|
i++) {
|
if (hist[i] > uthr)
|
hist_r[i] = uthr;
|
else if (hist[i] > white_str_lv)
|
hist_r[i] = hist[i];
|
else
|
hist_r[i] = lthr;
|
|
total_pixel_r = total_pixel_r + hist_r[i];
|
p[i] = p[i - 1] + hist_r[i];
|
}
|
for (i = p_hist_data->white_thr1; i < highest_white; i++) {
|
if (hist[i] > uthr)
|
hist_r[i] = uthr;
|
else if (hist[i] > white_str_lv)
|
hist_r[i] = hist[i];
|
else
|
hist_r[i] = white_str_lv;
|
|
total_pixel_r = total_pixel_r + hist_r[i];
|
p[i] = p[i - 1] + hist_r[i];
|
}
|
if (total_pixel_r != 0) {
|
rate =
|
(total_pixel * (highest_white - lowest_black))
|
/ total_pixel_r;
|
half = rate >> 1;
|
} else {
|
__wrn("auto_ce_model : total_pixel_r == 0.\n");
|
rate = highest_white - lowest_black;
|
half = rate >> 1;
|
}
|
tmp =
|
toupdate(p_hist_data->hist_mean,
|
p_hist_data->hist_mean_diff,
|
p_hist_data->diff_coeff, change_thr);
|
|
if (tmp) {
|
for (i = lowest_black; i < highest_white; i++) {
|
tmp = (p[i] * rate + half) / total_pixel
|
+ lowest_black;
|
if (tmp >= highest_white)
|
tmp = highest_white - 1;
|
|
celut[i] = (unsigned char)tmp;
|
|
}
|
|
for (i = 0; i < lowest_black; i++)
|
celut[i] = i;
|
for (i = highest_white; i < 256; i++)
|
celut[i] = i;
|
|
}
|
}
|
return;
|
}
|
|
/*******************************************************************************
|
* function : auto_bws_model(unsigned int width, unsigned int height,
|
* unsigned int hist[256], unsigned int sum,
|
* unsigned int pre_slope_black, unsigned int pre_slope_white,
|
* unsigned int frame_bld_en, unsigned int bld_high_thr,
|
* unsigned int bld_low_thr, unsigned int bld_weight_lmt,
|
* unsigned int present_black, unsigned int present_white,
|
* unsigned int slope_black_lmt, unsigned int slope_white_lmt,
|
* unsigned int black_prec, unsigned int white_prec,
|
* unsigned int lowest_black, unsigned int highest_white,
|
* unsigned int *ymin, unsigned int *black,
|
* unsigned int *white, unsigned int *ymax,
|
* unsigned int *slope0, unsigned int *slope1,
|
* unsigned int *slope2, unsigned int *slope3)
|
* description : Auto-BWS Alg
|
* parameters :
|
* width <layer width>
|
* height <layer height>
|
* hist[256] <the latest frame histogram>
|
* hist_pre[256] <the frame before the latest frame histogram>
|
* sum <the latest frame pixel value sum>
|
* pre_slope_black/pre_slope_white
|
* <the frame before the latest frame auto-bws result>
|
* ymin/black/white/ymax/shope0/1/2/3 <auto-bws result>
|
* return :
|
*
|
******************************************************************************/
|
/*
|
* R_ROPC_EN--frame_bld_en--1
|
* R_ROPC_TH_UPPER--bld_high_thr--90
|
* R_ROPC_TH_LOWER--bld_low_thr--74
|
* R_ROPC_WEIGHT_MIN--bld_weight_lmt--8
|
* R_PRESET_TILT_BLACK--present_black--53
|
* R_PRESET_TILT_WHITE--present_white--235
|
* R_SLOPE_LIMIT_BLACK--slope_black_lmt--512
|
* R_SLOPE_LIMIT_WHITE--slope_white_lmt--384
|
* R_BLACK_PERCENT--black_prec--5
|
* R_WHITE_PERCENT--white_prec--2
|
* R_LOWEST_BLACK--lowest_black--3
|
* R_HIGHEST_WHITE--highest_white--252
|
*/
|
static void auto_bws_model(unsigned int width, unsigned int height,
|
unsigned int hist[256], unsigned int hist_pre[256],
|
unsigned int sum,
|
unsigned int pre_slope_black, unsigned int pre_slope_white,
|
unsigned int frame_bld_en, unsigned int bld_high_thr,
|
unsigned int bld_low_thr, unsigned int bld_weight_lmt,
|
unsigned int present_black, unsigned int present_white,
|
unsigned int slope_black_lmt, unsigned int slope_white_lmt,
|
unsigned int black_prec, unsigned int white_prec,
|
unsigned int lowest_black, unsigned int highest_white,
|
struct hist_data *p_hist_data)
|
{
|
int total, k;
|
int validcnt, validsum;
|
int ratio_b, ratio_w, cdf_b, cdf_w;
|
int mean;
|
int pd_ymin = lowest_black, pd_ymax = highest_white;
|
int pd_black, pd_white;
|
int pd_ymin_fix, pd_ymax_fix;
|
int pd_s0, pd_s1, pd_s2, pd_s3;
|
int tmp;
|
int i = 0;
|
|
int coeff, diff_hist = 0;
|
total = 0;
|
for (k = 0; k < 256; k++) {
|
diff_hist += abs(hist[k] - hist_pre[k]);
|
total += hist[k];
|
}
|
if (total == 0) {
|
if (diff_hist == 0)
|
coeff = 0;
|
else
|
coeff = 200;
|
} else
|
coeff = (100 * diff_hist) / total;
|
p_hist_data->diff_coeff = coeff;
|
|
/* 2.kick out the lowest black and the highest white in hist and sum */
|
validsum = sum;
|
for (k = 0; k < lowest_black; k++) {
|
if (validsum < hist[k] * k)
|
break;
|
else
|
validsum -= hist[k] * k;
|
}
|
for (k = 255; k > highest_white - 1; k--) {
|
if (validsum < hist[k] * k)
|
break;
|
else
|
validsum -= hist[k] * k;
|
}
|
|
validcnt = total;
|
for (k = 0; k < lowest_black; k++)
|
validcnt -= hist[k];
|
|
if (validcnt == 0)
|
mean = lowest_black;
|
else {
|
for (k = 255; k > highest_white - 1; k--)
|
validcnt -= hist[k];
|
|
if (validcnt == 0)
|
mean = highest_white;
|
else
|
mean = validsum / validcnt;
|
}
|
|
if (validcnt != 0) {
|
/* 3.find Ymin and Ymax */
|
ratio_b = validcnt * black_prec / 100;
|
cdf_b = 0;
|
for (k = lowest_black; k < 255; k++) {
|
cdf_b += hist[k];
|
if (cdf_b > ratio_b) {
|
pd_ymin = k;
|
break;
|
}
|
}
|
|
ratio_w = validcnt * white_prec / 100;
|
cdf_w = 0;
|
for (k = highest_white; k >= 0; k--) {
|
cdf_w += hist[k];
|
if (cdf_w > ratio_w) {
|
pd_ymax = k;
|
break;
|
}
|
}
|
|
/*
|
printk("p_hist_data->hist_mean=%d blk0/1=%d/%d\n",
|
p_hist_data->hist_mean, p_hist_data->black_thr0,
|
p_hist_data->black_thr1);
|
*/
|
|
/* bright */
|
if (p_hist_data->hist_mean > 16) {
|
int step = slope_black_lmt - 256;
|
if (step < 0)
|
step = 0;
|
|
slope_black_lmt = slope_black_lmt
|
- (p_hist_data->hist_mean) * step / (5 * 16);
|
|
if (slope_black_lmt < 256)
|
slope_black_lmt = 256;
|
}
|
|
/* 4.limit black and white don't cross mean */
|
pd_black = (present_black < mean * 3 / 2) ?
|
present_black : mean * 3 / 2;
|
pd_white = (present_white > mean) ? present_white : mean;
|
|
/* 5.calculate slope1/2
|
and limit to slope_black_lmt or slope_white_lmt */
|
pd_s1 = (pd_ymin < pd_black) ?
|
((pd_black << 8) / (pd_black - pd_ymin)) : 256;
|
|
pd_s1 = (pd_s1 > slope_black_lmt) ? slope_black_lmt : pd_s1;
|
|
pd_s2 = (pd_ymax > pd_white) ?
|
(((255 - pd_white) << 8) / (pd_ymax - pd_white)) : 256;
|
pd_s2 = (pd_s2 > slope_white_lmt) ? slope_white_lmt : pd_s2;
|
|
tmp = pd_black + ((pd_s1 * (pd_ymin - pd_black) + 128) >> 8);
|
/*7.calculate slope0/3 and re-calculate ymin and ymax */
|
if ((tmp > 0) && (pd_ymin < pd_black) && (pd_ymin > 0)) {
|
pd_s0 = ((tmp << 8) + 128) / pd_ymin;
|
pd_ymin_fix = pd_ymin;
|
} else if (pd_ymin >= pd_black) {
|
pd_s0 = 256;
|
pd_ymin_fix = 16;
|
/* do noting use s0 */
|
} else {
|
pd_s0 = 0;
|
pd_ymin_fix =
|
-((pd_black << 8) - 128) / pd_s1 + pd_black;
|
}
|
|
if (pd_s0 == 0)
|
pd_s1 = 256 * pd_black / (pd_black - pd_ymin_fix);
|
|
if (pd_s1 == 256)
|
pd_s0 = 256;
|
|
tmp = pd_white + ((pd_s2 * (pd_ymax - pd_white)) >> 8);
|
if ((tmp < 255) && (pd_ymax > pd_white) && (pd_ymax < 255)) {
|
pd_s3 = (((255 - tmp) << 8) + 128) / (255 - pd_ymax);
|
pd_ymax_fix = pd_ymax;
|
} else if (pd_ymax <= pd_white) {
|
pd_s3 = 256;
|
pd_ymax_fix = 255;
|
/* do noting use s3 */
|
} else {
|
pd_s3 = 0;
|
pd_ymax_fix =
|
(((255 - pd_white) << 8) - 128) / pd_s2 + pd_white;
|
}
|
|
if (pd_s3 == 256)
|
pd_s2 = 256;
|
} else {
|
/* no enough pixel for auto bws */
|
pd_ymin_fix = 16;
|
pd_black = 32;
|
pd_white = 224;
|
pd_ymax_fix = 240;
|
pd_s0 = 0x100;
|
pd_s1 = 0x100;
|
pd_s2 = 0x100;
|
pd_s3 = 0x100;
|
}
|
|
if (mean < 0 || pd_ymin_fix < 0 || pd_black < 0 || pd_white > 255
|
|| pd_white < 0 || pd_ymax_fix < 0 || pd_ymax_fix > 255) {
|
mean = 0;
|
pd_ymin_fix = 16;
|
pd_black = 32;
|
pd_white = 224;
|
pd_ymax_fix = 240;
|
pd_s0 = 0x100;
|
pd_s1 = 0x100;
|
pd_s2 = 0x100;
|
pd_s3 = 0x100;
|
}
|
|
p_hist_data->old_hist_mean = p_hist_data->hist_mean;
|
p_hist_data->hist_mean = mean; /* add by zly 2014-8-19 16:12:14 */
|
|
p_hist_data->avg_mean_saved[p_hist_data->avg_mean_idx] = mean;
|
p_hist_data->avg_mean_idx++;
|
if (p_hist_data->avg_mean_idx == AVG_NUM)
|
p_hist_data->avg_mean_idx = 0;
|
|
p_hist_data->avg_mean = 0;
|
for (i = 0; i < AVG_NUM; i++)
|
p_hist_data->avg_mean += p_hist_data->avg_mean_saved[i];
|
|
p_hist_data->avg_mean = p_hist_data->avg_mean / AVG_NUM;
|
|
p_hist_data->hist_mean_diff =
|
(int)p_hist_data->avg_mean - (int)p_hist_data->hist_mean;
|
|
/* cal th */
|
p_hist_data->black_thr0 = pd_ymin_fix;
|
p_hist_data->black_thr1 = pd_black;
|
p_hist_data->white_thr0 = pd_white;
|
p_hist_data->white_thr1 = pd_ymax_fix;
|
p_hist_data->black_slp0 = pd_s0;
|
p_hist_data->black_slp1 = pd_s1;
|
p_hist_data->white_slp0 = pd_s2;
|
p_hist_data->white_slp1 = pd_s3;
|
|
return;
|
}
|
|
int de_hist_apply(unsigned int screen_id, unsigned int chno,
|
unsigned int hist_en, unsigned int auto_contrast_dirty)
|
{
|
if (hist_en == 1 && auto_contrast_dirty) {
|
/*
|
* enable this time
|
* reset hist buffer
|
*/
|
memset((unsigned char *)g_hist[screen_id][chno], 0, 1024);
|
|
g_hist_status[screen_id][chno]->runtime = 0;
|
g_hist_status[screen_id][chno]->twohistready = 0;
|
g_hist_status[screen_id][chno]->isenable = 1;
|
} else if (hist_en == 0 && auto_contrast_dirty) {
|
/* disable this time */
|
g_hist_status[screen_id][chno]->runtime = 0;
|
g_hist_status[screen_id][chno]->twohistready = 0;
|
g_hist_status[screen_id][chno]->isenable = 0;
|
}
|
|
return 0;
|
}
|
|
int de_hist_tasklet(unsigned int screen_id, unsigned int chno,
|
unsigned int frame_cnt)
|
{
|
if ((g_hist_status[screen_id][chno]->isenable)
|
&& ((HIST_FRAME_MASK == (frame_cnt % 2))
|
|| (HIST_FRAME_MASK == 0x2))) {
|
memcpy((unsigned char *)g_hist_p[screen_id][chno],
|
(unsigned char *)g_hist[screen_id][chno], 1024);
|
de_fce_get_hist(screen_id, chno, g_hist[screen_id][chno],
|
&g_sum[screen_id][chno]);
|
|
if (g_hist_status[screen_id][chno]->runtime < 2)
|
g_hist_status[screen_id][chno]->runtime++;
|
else
|
g_hist_status[screen_id][chno]->twohistready = 1;
|
}
|
return 0;
|
|
}
|
|
int de_ce_apply(unsigned int screen_id, unsigned int chno, unsigned int ce_en,
|
unsigned int b_automode, unsigned int up_precent_thr,
|
unsigned int down_precent_thr, unsigned int update_diff_thr,
|
unsigned int black_slope_lmt, unsigned int white_slope_lmt,
|
unsigned int auto_contrast_dirty)
|
{
|
if (ce_en == 1 && auto_contrast_dirty) {
|
/* enable this time */
|
g_ce_status[screen_id][chno]->isenable = 1;
|
g_ce_status[screen_id][chno]->b_automode = b_automode;
|
|
if (b_automode) {
|
g_ce_status[screen_id][chno]->up_precent_thr =
|
up_precent_thr;
|
g_ce_status[screen_id][chno]->down_precent_thr =
|
down_precent_thr;
|
g_ce_status[screen_id][chno]->update_diff_thr =
|
update_diff_thr;
|
g_ce_status[screen_id][chno]->slope_black_lmt =
|
black_slope_lmt;
|
g_ce_status[screen_id][chno]->slope_white_lmt =
|
white_slope_lmt;
|
|
memset(hist_res[screen_id][chno], 0,
|
sizeof(struct hist_data));
|
/* clear hist data when disable */
|
|
/* set bypass lut first */
|
memcpy((void *)g_celut[screen_id][chno],
|
(void *)ce_bypass_lut, 256);
|
} else {
|
memcpy((void *)g_celut[screen_id][chno],
|
(void *)ce_constant_lut, 256);
|
}
|
|
de_fce_set_ce(screen_id, chno, g_celut[screen_id][chno]);
|
|
} else if (ce_en == 0 && auto_contrast_dirty) {
|
/* disable this time */
|
g_ce_status[screen_id][chno]->isenable = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
int de_ce_tasklet(unsigned int screen_id, unsigned int chno,
|
unsigned int frame_cnt)
|
{
|
unsigned int percent_black, percent_white;
|
unsigned int lowest_black, highest_white, autoce_thr;
|
|
percent_black = 5;
|
percent_white = 4;
|
lowest_black = 4;
|
highest_white = 252;
|
autoce_thr = 21;
|
|
if (g_ce_status[screen_id][chno]->isenable
|
&& g_ce_status[screen_id][chno]->b_automode
|
&& ((CE_FRAME_MASK == (frame_cnt % 2)) || (CE_FRAME_MASK == 0x2))) {
|
if (g_hist_status[screen_id][chno]->twohistready) {
|
auto_bws_model(g_ce_status[screen_id][chno]->width,
|
g_ce_status[screen_id][chno]->height,
|
g_hist[screen_id][chno],
|
g_hist_p[screen_id][chno],
|
g_sum[screen_id][chno], 256, 256, 1, 90,
|
74, 8, 53, 235,
|
g_ce_status[screen_id][chno]->
|
slope_black_lmt,
|
g_ce_status[screen_id][chno]->
|
slope_white_lmt, percent_black,
|
percent_white, lowest_black,
|
highest_white,
|
hist_res[screen_id][chno]);
|
|
auto_ce_model(g_ce_status[screen_id][chno]->width,
|
g_ce_status[screen_id][chno]->height,
|
g_sum[screen_id][chno],
|
g_hist[screen_id][chno],
|
g_ce_status[screen_id][chno]->
|
up_precent_thr,
|
g_ce_status[screen_id][chno]->
|
down_precent_thr,
|
g_ce_status[screen_id][chno]->
|
update_diff_thr, lowest_black,
|
highest_white, autoce_thr,
|
g_celut[screen_id][chno],
|
hist_res[screen_id][chno]);
|
} else {
|
memcpy((void *)g_celut[screen_id][chno],
|
(void *)ce_bypass_lut, 256);
|
}
|
de_fce_set_ce(screen_id, chno, g_celut[screen_id][chno]);
|
}
|
|
return 0;
|
}
|
#endif
|