kernel_samsung_a53x/drivers/gpu/drm/samsung/panel/panel_bl.c

1278 lines
34 KiB
C
Raw Normal View History

2024-06-15 16:02:09 -03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) Samsung Electronics Co., Ltd.
* 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 <linux/backlight.h>
#include "panel_kunit.h"
#include "panel.h"
#include "panel_bl.h"
#ifdef CONFIG_EXYNOS_DECON_LCD_COPR
#include "copr.h"
#endif
#include "timenval.h"
#include "panel_debug.h"
#ifdef CONFIG_PANEL_AID_DIMMING
#include "dimming.h"
#include "panel_dimming.h"
#endif
#include "panel_drv.h"
#include "panel_irc.h"
static char *dim_type_str[MAX_DIM_TYPE_STR] = {
[DIM_TYPE_STR_TABLE] = "table",
[DIM_TYPE_STR_FLASH] = "flash",
[DIM_TYPE_STR_GM2] = "gm2",
};
#ifdef DEBUG_PAC
static void print_tbl(int *tbl, int sz)
{
int i;
for (i = 0; i < sz; i++) {
panel_info("%d", tbl[i]);
if (!((i + 1) % 10))
panel_info("\n");
}
}
#else
static void print_tbl(int *tbl, int sz) {}
#endif
int max_brt_tbl(struct brightness_table *brt_tbl)
{
if (unlikely(!brt_tbl || !brt_tbl->brt || !brt_tbl->sz_brt)) {
panel_err("invalid parameter\n");
return -EINVAL;
}
return brt_tbl->brt[brt_tbl->sz_brt - 1];
}
static int get_subdev_max_brightness(struct panel_bl_device *panel_bl, int id)
{
struct panel_bl_sub_dev *subdev;
if (unlikely(!panel_bl)) {
panel_err("invalid parameter\n");
return -EINVAL;
}
if (id < 0 || id >= MAX_PANEL_BL_SUBDEV) {
panel_err("bl-%d exceeded max subdev\n", id);
return -EINVAL;
}
subdev = &panel_bl->subdev[id];
return max_brt_tbl(&subdev->brt_tbl);
}
int get_max_brightness(struct panel_bl_device *panel_bl)
{
if (unlikely(!panel_bl)) {
panel_err("invalid parameter\n");
return false;
}
return get_subdev_max_brightness(panel_bl, panel_bl->props.id);
}
static bool is_valid_brightness(struct panel_bl_device *panel_bl, int brightness)
{
int max_brightness;
if (unlikely(!panel_bl)) {
panel_err("invalid parameter\n");
return false;
}
max_brightness = get_max_brightness(panel_bl);
if (brightness < 0 || brightness > max_brightness) {
panel_err("out of range %d, (min:0, max:%d)\n",
brightness, max_brightness);
return false;
}
return true;
}
bool __mockable is_hbm_brightness(struct panel_bl_device *panel_bl, int brightness)
{
struct panel_bl_sub_dev *subdev;
int luminance;
int sz_ui_lum;
if (unlikely(!panel_bl)) {
panel_err("invalid parameter\n");
return false;
}
subdev = &panel_bl->subdev[panel_bl->props.id];
if (subdev->brt_tbl.control_type == BRIGHTNESS_CONTROL_TYPE_GAMMA_MODE2)
return (brightness > subdev->brt_tbl.brt[subdev->brt_tbl.sz_ui_brt - 1]);
luminance = get_actual_brightness(panel_bl, brightness);
sz_ui_lum = subdev->brt_tbl.sz_ui_lum;
if (sz_ui_lum <= 0 || sz_ui_lum > subdev->brt_tbl.sz_lum) {
panel_err("bl-%d out of range (sz_ui_lum %d)\n",
panel_bl->props.id, sz_ui_lum);
return false;
}
return (luminance > subdev->brt_tbl.lum[sz_ui_lum - 1]);
}
EXPORT_SYMBOL(is_hbm_brightness);
bool is_ext_hbm_brightness(struct panel_bl_device *panel_bl, int brightness)
{
struct panel_bl_sub_dev *subdev;
int luminance;
int sz_ui_lum;
int sz_hbm_lum;
if (unlikely(!panel_bl)) {
panel_err("invalid parameter\n");
return false;
}
subdev = &panel_bl->subdev[panel_bl->props.id];
if (subdev->brt_tbl.control_type == BRIGHTNESS_CONTROL_TYPE_GAMMA_MODE2)
return false;
luminance = get_actual_brightness(panel_bl, brightness);
sz_ui_lum = subdev->brt_tbl.sz_ui_lum;
sz_hbm_lum = subdev->brt_tbl.sz_hbm_lum;
if (sz_hbm_lum <= 0 || sz_ui_lum + sz_hbm_lum > subdev->brt_tbl.sz_lum) {
panel_err("bl-%d out of range (sz_hbm_lum %d)\n",
panel_bl->props.id, sz_hbm_lum);
return false;
}
return (luminance > subdev->brt_tbl.lum[sz_ui_lum + sz_hbm_lum - 1]);
}
/*
* search_tbl - binary search an array of integer elements
* @tbl : pointer to first element to search
* @sz : number of elements
* @type : type of searching type (i.e LOWER, UPPER, EXACT value)
* @value : a value being searched for
*
* This function does a binary search on the given array.
* The contents of the array should already be in ascending sorted order
*
* Note that the value need to be inside of the range of array ['start', 'end']
* if the value is out of array range, return -1.
* if not, this function returns array index.
*/
int search_tbl(int *tbl, int sz, enum SEARCH_TYPE type, int value)
{
int l = 0, m, r = sz - 1;
if (unlikely(!tbl || sz == 0)) {
panel_err("invalid parameter(tbl %p, sz %d)\n", tbl, sz);
return -EINVAL;
}
if (value <= tbl[l])
return l;
if (value >= tbl[r])
return r;
while (l <= r) {
m = l + (r - l) / 2;
if (tbl[m] == value)
return m;
if (tbl[m] < value)
l = m + 1;
else
r = m - 1;
}
if (type == SEARCH_TYPE_LOWER)
return ((r < 0) ? -1 : r);
else if (type == SEARCH_TYPE_UPPER)
return ((l > sz - 1) ? -1 : l);
else if (type == SEARCH_TYPE_EXACT)
return -1;
return -1;
}
#ifdef CONFIG_PANEL_AID_DIMMING
static int search_brt_tbl(struct brightness_table *brt_tbl, int brightness)
{
if (unlikely(!brt_tbl || !brt_tbl->brt)) {
panel_err("invalid parameter\n");
return -EINVAL;
}
return search_tbl(brt_tbl->brt, brt_tbl->sz_brt,
SEARCH_TYPE_UPPER, brightness);
}
static int search_brt_to_step_tbl(struct brightness_table *brt_tbl, int brightness)
{
if (unlikely(!brt_tbl || !brt_tbl->brt_to_step)) {
panel_err("invalid parameter\n");
return -EINVAL;
}
return search_tbl(brt_tbl->brt_to_step, brt_tbl->sz_brt_to_step,
SEARCH_TYPE_UPPER, brightness);
}
__mockable int get_subdev_actual_brightness_index(struct panel_bl_device *panel_bl,
int id, int brightness)
{
int index;
struct panel_bl_sub_dev *subdev;
if (unlikely(!panel_bl)) {
panel_err("invalid parameter\n");
return -EINVAL;
}
if (id < 0 || id >= MAX_PANEL_BL_SUBDEV) {
panel_err("bl-%d exceeded max subdev\n", id);
return -EINVAL;
}
if (!is_valid_brightness(panel_bl, brightness)) {
panel_err("bl-%d invalid brightness\n", id);
return -EINVAL;
}
subdev = &panel_bl->subdev[id];
#if defined(CONFIG_PANEL_BL_USE_BRT_CACHE)
if (brightness >= subdev->sz_brt_cache_tbl) {
panel_err("bl-%d exceeded brt_cache_tbl size %d\n", id, brightness);
return -EINVAL;
}
if (subdev->brt_cache_tbl[brightness] == -1) {
index = search_brt_tbl(&subdev->brt_tbl, brightness);
if (index < 0) {
panel_err("bl-%d failed to search %d, ret %d\n",
id, brightness, index);
return index;
}
subdev->brt_cache_tbl[brightness] = index;
#ifdef DEBUG_PAC
panel_info("bl-%d brightness %d, brt_cache_tbl[%d] = %d\n",
id, brightness, brightness,
subdev->brt_cache_tbl[brightness]);
#endif
} else {
index = subdev->brt_cache_tbl[brightness];
#ifdef DEBUG_PAC
panel_info("bl-%d brightness %d, brt_cache_tbl[%d] = %d\n",
id, brightness, brightness,
subdev->brt_cache_tbl[brightness]);
#endif
}
#else
index = search_brt_tbl(&subdev->brt_tbl, brightness);
if (index < 0) {
panel_err("bl-%d failed to search %d, ret %d\n",
id, brightness, index);
return index;
}
#endif
if (index > subdev->brt_tbl.sz_brt - 1)
index = subdev->brt_tbl.sz_brt - 1;
return index;
}
EXPORT_SYMBOL(get_subdev_actual_brightness_index);
__mockable int get_actual_brightness_index(struct panel_bl_device *panel_bl, int brightness)
{
if (unlikely(!panel_bl)) {
panel_err("invalid parameter\n");
return -EINVAL;
}
if (!is_valid_brightness(panel_bl, brightness)) {
panel_err("bl-%d invalid brightness\n", panel_bl->props.id);
return -EINVAL;
}
return get_subdev_actual_brightness_index(panel_bl,
panel_bl->props.id, brightness);
}
EXPORT_SYMBOL(get_actual_brightness_index);
int get_brightness_pac_step_by_subdev_id(struct panel_bl_device *panel_bl, int id, int brightness)
{
int index;
struct panel_bl_sub_dev *subdev;
if (unlikely(!panel_bl)) {
panel_err("invalid parameter\n");
return -EINVAL;
}
if (unlikely(id >= MAX_PANEL_BL_SUBDEV)) {
panel_err("invalid id %d\n", id);
return -EINVAL;
}
subdev = &panel_bl->subdev[id];
if (!is_valid_brightness(panel_bl, brightness)) {
panel_err("bl-%d invalid brightness\n", id);
return -EINVAL;
}
index = search_brt_to_step_tbl(&subdev->brt_tbl, brightness);
if (index < 0) {
panel_err("failed to search %d, ret %d\n", brightness, index);
print_tbl(subdev->brt_tbl.brt_to_step,
subdev->brt_tbl.sz_brt_to_step);
return index;
}
#ifdef DEBUG_PAC
panel_info("bl-%d brightness %d, pac step %d, brt %d\n",
id, brightness, index,
subdev->brt_tbl.brt_to_step[index]);
#endif
if (index > subdev->brt_tbl.sz_brt_to_step - 1)
index = subdev->brt_tbl.sz_brt_to_step - 1;
return index;
}
EXPORT_SYMBOL(get_brightness_pac_step_by_subdev_id);
int get_brightness_pac_step(struct panel_bl_device *panel_bl, int brightness)
{
int id;
if (unlikely(!panel_bl)) {
panel_err("invalid parameter\n");
return -EINVAL;
}
id = panel_bl->props.id;
return get_brightness_pac_step_by_subdev_id(panel_bl, id, brightness);
}
EXPORT_SYMBOL(get_brightness_pac_step);
int get_brightness_of_brt_to_step(struct panel_bl_device *panel_bl, int id, int brightness)
{
struct panel_bl_sub_dev *subdev;
struct brightness_table *brt_tbl;
int step;
subdev = &panel_bl->subdev[id];
brt_tbl = &subdev->brt_tbl;
step = get_brightness_pac_step(panel_bl, brightness);
if (step < 0) {
panel_err("bl-%d invalid pac stap %d\n", id, step);
return -EINVAL;
}
return brt_tbl->brt_to_step[step];
}
int get_subdev_actual_brightness(struct panel_bl_device *panel_bl, int id, int brightness)
{
int index;
if (unlikely(!panel_bl)) {
panel_err("invalid parameter\n");
return -EINVAL;
}
if (id < 0 || id >= MAX_PANEL_BL_SUBDEV) {
panel_err("bl-%d exceeded max subdev\n", id);
return -EINVAL;
}
/* return 0 if luminance table is invalid */
if (panel_bl->subdev[id].brt_tbl.lum == NULL || panel_bl->subdev[id].brt_tbl.sz_lum == 0)
return 0;
index = get_subdev_actual_brightness_index(panel_bl, id, brightness);
if (index < 0) {
panel_err("bl-%d failed to get actual_brightness_index %d\n", id, index);
return index;
}
return panel_bl->subdev[id].brt_tbl.lum[index];
}
int get_subdev_actual_brightness_interpolation(struct panel_bl_device *panel_bl, int id, int brightness)
{
int upper_idx, lower_idx;
int upper_lum, lower_lum;
int upper_brt, lower_brt;
int step, upper_step, lower_step;
if (unlikely(!panel_bl)) {
panel_err("invalid parameter\n");
return -EINVAL;
}
if (id < 0 || id >= MAX_PANEL_BL_SUBDEV) {
panel_err("bl-%d exceeded max subdev\n", id);
return -EINVAL;
}
upper_idx = get_subdev_actual_brightness_index(panel_bl, id, brightness);
if (upper_idx < 0) {
panel_err("bl-%d failed to get actual_brightness_index %d\n",
id, upper_idx);
return upper_idx;
}
lower_idx = max(0, (upper_idx - 1));
lower_lum = panel_bl->subdev[id].brt_tbl.lum[lower_idx];
upper_lum = panel_bl->subdev[id].brt_tbl.lum[upper_idx];
lower_brt = panel_bl->subdev[id].brt_tbl.brt[lower_idx];
upper_brt = panel_bl->subdev[id].brt_tbl.brt[upper_idx];
lower_step = get_brightness_pac_step(panel_bl, lower_brt);
step = get_brightness_pac_step(panel_bl, brightness);
upper_step = get_brightness_pac_step(panel_bl, upper_brt);
return (lower_lum * CALC_SCALE) + (s32)((upper_step == lower_step) ? 0 :
((s64)(upper_lum - lower_lum) * (step - lower_step) * CALC_SCALE) /
(s64)(upper_step - lower_step));
}
int get_actual_brightness(struct panel_bl_device *panel_bl, int brightness)
{
if (unlikely(!panel_bl)) {
panel_err("invalid parameter\n");
return -EINVAL;
}
return get_subdev_actual_brightness(panel_bl,
panel_bl->props.id, brightness);
}
EXPORT_SYMBOL(get_actual_brightness);
int get_actual_brightness_interpolation(struct panel_bl_device *panel_bl, int brightness)
{
if (unlikely(!panel_bl)) {
panel_err("invalid parameter\n");
return -EINVAL;
}
return get_subdev_actual_brightness_interpolation(panel_bl,
panel_bl->props.id, brightness);
}
#endif /* CONFIG_PANEL_AID_DIMMING */
static void panel_bl_update_acl_state(struct panel_bl_device *panel_bl)
{
struct panel_device *panel;
struct panel_info *panel_data;
panel = to_panel_device(panel_bl);
panel_data = &panel->panel_data;
#ifdef CONFIG_SUPPORT_HMD
if (panel_bl->props.id == PANEL_BL_SUBDEV_TYPE_HMD) {
panel_bl->props.acl_opr = 0;
panel_bl->props.acl_pwrsave = ACL_PWRSAVE_OFF;
return;
}
#endif
#ifdef CONFIG_SUPPORT_AOD_BL
if (panel_bl->props.id == PANEL_BL_SUBDEV_TYPE_AOD) {
panel_bl->props.acl_opr = 0;
panel_bl->props.acl_pwrsave = ACL_PWRSAVE_OFF;
return;
}
#endif
#ifdef CONFIG_SUPPORT_MASK_LAYER
if (panel_bl->props.mask_layer_br_hook == MASK_LAYER_HOOK_ON) {
panel_bl->props.acl_opr = 0;
panel_bl->props.acl_pwrsave = ACL_PWRSAVE_OFF;
return;
}
#endif
if (panel_data->props.adaptive_control > 100) {
panel_warn("invalid range %d\n", panel_data->props.adaptive_control);
return;
}
panel_bl->props.acl_opr = panel_data->props.adaptive_control;
panel_bl->props.acl_pwrsave =
(panel_data->props.adaptive_control == 0) ?
ACL_PWRSAVE_OFF : ACL_PWRSAVE_ON;
}
int panel_bl_get_acl_pwrsave(struct panel_bl_device *panel_bl)
{
return panel_bl->props.acl_pwrsave;
}
EXPORT_SYMBOL(panel_bl_get_acl_pwrsave);
int panel_bl_get_acl_opr(struct panel_bl_device *panel_bl)
{
return panel_bl->props.acl_opr;
}
EXPORT_SYMBOL(panel_bl_get_acl_opr);
void panel_bl_clear_brightness_set_count(struct panel_bl_device *panel_bl)
{
atomic_set(&panel_bl->props.brightness_set_count, 0);
}
int panel_bl_get_brightness_set_count(struct panel_bl_device *panel_bl)
{
return atomic_read(&panel_bl->props.brightness_set_count);
}
EXPORT_SYMBOL(panel_bl_get_brightness_set_count);
inline void panel_bl_inc_brightness_set_count(struct panel_bl_device *panel_bl)
{
atomic_inc(&panel_bl->props.brightness_set_count);
}
int panel_bl_set_subdev(struct panel_bl_device *panel_bl, int id)
{
panel_bl->props.id = id;
panel_bl_clear_brightness_set_count(panel_bl);
return 0;
}
int panel_bl_update_average(struct panel_bl_device *panel_bl, size_t index)
{
struct timespec64 cur_ts;
int cur_brt;
if (index >= ARRAY_SIZE(panel_bl->tnv))
return -EINVAL;
ktime_get_ts64(&cur_ts);
cur_brt = panel_bl->props.actual_brightness_intrp / CALC_SCALE;
timenval_update_snapshot(&panel_bl->tnv[index], cur_brt, cur_ts);
return 0;
}
int panel_bl_clear_average(struct panel_bl_device *panel_bl, size_t index)
{
if (index >= ARRAY_SIZE(panel_bl->tnv))
return -EINVAL;
timenval_clear_average(&panel_bl->tnv[index]);
return 0;
}
int panel_bl_get_average_and_clear(struct panel_bl_device *panel_bl, size_t index)
{
int avg;
if (index >= ARRAY_SIZE(panel_bl->tnv))
return -EINVAL;
mutex_lock(&panel_bl->lock);
panel_bl_update_average(panel_bl, index);
avg = panel_bl->tnv[index].avg;
panel_bl_clear_average(panel_bl, index);
mutex_unlock(&panel_bl->lock);
return avg;
}
/*
* aor interpolation function type 1
* A-Dimming : calculate interpolation aor.
* S-Dimming : calculate interpolation aor using vbase luminance.
*/
int aor_interpolation(unsigned int *brt_tbl, unsigned int *lum_tbl,
u8(*aor_tbl)[2], int size, int size_ui_lum, u32 vtotal, int brightness)
{
int upper_idx, lower_idx;
u64 upper_lum, lower_lum;
u64 upper_brt, lower_brt;
u64 upper_aor, lower_aor, aor = 0;
u64 upper_aor_ratio, lower_aor_ratio, aor_ratio = 0;
u64 intrp_brt = 0, vbase_lum = 0;
enum DIMTYPE dimtype;
upper_idx = search_tbl(brt_tbl, size, SEARCH_TYPE_UPPER, brightness);
lower_idx = max(0, (upper_idx - 1));
upper_lum = lum_tbl[upper_idx] * CALC_SCALE;
lower_lum = lum_tbl[lower_idx] * CALC_SCALE;
upper_brt = brt_tbl[upper_idx];
lower_brt = brt_tbl[lower_idx];
upper_aor = aor_tbl[upper_idx][0] << 8 | aor_tbl[upper_idx][1];
lower_aor = aor_tbl[lower_idx][0] << 8 | aor_tbl[lower_idx][1];
upper_aor_ratio = AOR_TO_RATIO(upper_aor, vtotal);
lower_aor_ratio = AOR_TO_RATIO(lower_aor, vtotal);
if (upper_brt == brightness)
return (int)upper_aor;
dimtype = ((upper_aor == lower_aor) ||
((upper_idx < size_ui_lum - 1) &&
(aor_tbl[upper_idx + 1][0] << 8 |
aor_tbl[upper_idx + 1][1]) == upper_aor) ||
((lower_idx > 0) &&
(aor_tbl[lower_idx - 1][0] << 8 |
aor_tbl[lower_idx - 1][1]) == lower_aor)) ?
S_DIMMING : A_DIMMING;
if (dimtype == A_DIMMING) {
aor_ratio = (disp_interpolation64(lower_aor_ratio * disp_pow(10, 3), upper_aor_ratio * disp_pow(10, 3),
(s32)((u64)brightness - lower_brt) * disp_pow(10, 2),
(s32)(upper_brt - lower_brt) * disp_pow(10, 2)) + 5 * disp_pow(10, 2)) / disp_pow(10, 3);
aor = disp_div64(vtotal * aor_ratio + 5 * disp_pow(10, 3), disp_pow(10, 4));
} else if (dimtype == S_DIMMING) {
vbase_lum = VIRTUAL_BASE_LUMINANCE(upper_lum, upper_aor_ratio);
vbase_lum = disp_pow_round(vbase_lum, 2);
intrp_brt = disp_interpolation64(lower_lum * disp_pow(10, 4), upper_lum * disp_pow(10, 4),
(s32)((u64)brightness - lower_brt), (s32)(upper_brt - lower_brt));
intrp_brt = disp_pow_round(intrp_brt, 4);
aor_ratio = disp_pow(10, 8) - disp_div64(intrp_brt * disp_pow(10, 6), vbase_lum);
aor_ratio = disp_pow_round(aor_ratio, 4) / disp_pow(10, 4);
aor = disp_pow_round(vtotal * aor_ratio, 4) / disp_pow(10, 4);
}
panel_dbg("aor: brightness %3d.%02d lum %3lld aor %2lld.%02lld, vbase_lum %3lld.%04lld, intrp_brt %3lld.%03lld, aor(%2lld.%02lld %3lld %04X)\n",
brightness / CALC_SCALE, brightness % CALC_SCALE, upper_lum / CALC_SCALE, upper_aor_ratio / CALC_SCALE, upper_aor_ratio % CALC_SCALE,
vbase_lum / disp_pow(10, 4), vbase_lum % disp_pow(10, 4),
intrp_brt / disp_pow(10, 6), intrp_brt % disp_pow(10, 6) / disp_pow(10, 3),
aor_ratio / disp_pow(10, 2), aor_ratio % disp_pow(10, 2), aor, (int)aor);
return (int)aor;
}
/*
* aor interpolation function type 2
* calculate interpolation aor without
* distinction of A/S dimming type.
*/
int aor_interpolation_2(unsigned int *brt_tbl,
u8(*aor_tbl)[2], int size, int size_ui_lum, u32 vtotal, int brightness)
{
int upper_idx, lower_idx;
u64 upper_brt, lower_brt;
u64 upper_aor, lower_aor, aor;
u64 upper_aor_ratio, lower_aor_ratio, aor_ratio = 0;
upper_idx = search_tbl(brt_tbl, size, SEARCH_TYPE_UPPER, brightness);
lower_idx = max(0, (upper_idx - 1));
upper_brt = brt_tbl[upper_idx];
lower_brt = brt_tbl[lower_idx];
upper_aor = aor_tbl[upper_idx][0] << 8 | aor_tbl[upper_idx][1];
lower_aor = aor_tbl[lower_idx][0] << 8 | aor_tbl[lower_idx][1];
upper_aor_ratio = AOR_TO_RATIO(upper_aor, vtotal);
lower_aor_ratio = AOR_TO_RATIO(lower_aor, vtotal);
if (upper_brt == brightness) {
aor = upper_aor;
} else {
aor_ratio = (disp_interpolation64(lower_aor_ratio * disp_pow(10, 3), upper_aor_ratio * disp_pow(10, 3),
(s32)((u64)brightness - lower_brt) * disp_pow(10, 2),
(s32)(upper_brt - lower_brt) * disp_pow(10, 2)) + 5 * disp_pow(10, 2)) / disp_pow(10, 3);
aor = disp_div64(vtotal * aor_ratio + 5 * disp_pow(10, 3), disp_pow(10, 4));
}
panel_dbg("aor: brightness %3d.%02d aor([%d]%2lld.%02lld(0x%04X) [%d]%2lld.%02lld(0x%04X)) aor(%2lld.%02lld %3lld %04X) vtotal %d\n",
brightness / CALC_SCALE, brightness % CALC_SCALE, upper_idx, upper_aor_ratio / CALC_SCALE, upper_aor_ratio % CALC_SCALE, (unsigned int)upper_aor,
lower_idx, lower_aor_ratio / CALC_SCALE, lower_aor_ratio % CALC_SCALE, (unsigned int)lower_aor,
aor_ratio / disp_pow(10, 2), aor_ratio % disp_pow(10, 2), aor, (unsigned int)aor, vtotal);
return (int)aor;
}
int panel_bl_aor_interpolation(struct panel_bl_device *panel_bl,
int id, u8(*aor_tbl)[2])
{
struct panel_bl_sub_dev *subdev;
struct brightness_table *brt_tbl;
int brightness;
subdev = &panel_bl->subdev[id];
brt_tbl = &subdev->brt_tbl;
brightness = subdev->brightness;
brightness = get_brightness_of_brt_to_step(panel_bl, id, brightness);
return aor_interpolation(brt_tbl->brt, brt_tbl->lum,
aor_tbl, brt_tbl->sz_lum,
brt_tbl->sz_ui_lum, brt_tbl->vtotal, brightness);
}
int panel_bl_aor_interpolation_2(struct panel_bl_device *panel_bl,
int id, u8(*aor_tbl)[2])
{
struct panel_bl_sub_dev *subdev;
struct brightness_table *brt_tbl;
int brightness;
subdev = &panel_bl->subdev[id];
brt_tbl = &subdev->brt_tbl;
brightness = subdev->brightness;
brightness = get_brightness_of_brt_to_step(panel_bl, id, brightness);
return aor_interpolation_2(brt_tbl->brt, aor_tbl, brt_tbl->sz_lum,
brt_tbl->sz_ui_lum, brt_tbl->vtotal, brightness);
}
EXPORT_SYMBOL(panel_bl_aor_interpolation_2);
int panel_bl_irc_interpolation(struct panel_bl_device *panel_bl, int id, struct panel_irc_info *irc_info)
{
struct panel_bl_sub_dev *subdev;
struct brightness_table *brt_tbl;
int brightness;
subdev = &panel_bl->subdev[id];
brt_tbl = &subdev->brt_tbl;
brightness = subdev->brightness;
brightness = get_brightness_of_brt_to_step(panel_bl, id, brightness);
if (is_ext_hbm_brightness(panel_bl, brightness))
brightness = brt_tbl->brt[brt_tbl->sz_ui_lum + brt_tbl->sz_hbm_lum - 1];
return generate_irc(brt_tbl, irc_info, brightness);
}
EXPORT_SYMBOL(panel_bl_irc_interpolation);
//void g_tracing_mark_write(char id, char *str1, int value);
int panel_bl_set_brightness(struct panel_bl_device *panel_bl, int id, u32 send_cmd)
{
int ret = 0, ilum = 0, luminance = 0, brightness, index = PANEL_SET_BL_SEQ, step;
struct panel_bl_sub_dev *subdev;
struct panel_device *panel;
int luminance_interp = 0;
u32 dim_type;
bool need_update_display_mode = false;
if (panel_bl == NULL) {
panel_err("panel is null\n");
return -EINVAL;
}
if (id < 0 || id >= MAX_PANEL_BL_SUBDEV) {
panel_err("bl-%d exceeded max subdev\n", id);
return -EINVAL;
}
panel = to_panel_device(panel_bl);
panel_bl->props.id = id;
subdev = &panel_bl->subdev[id];
brightness = subdev->brightness;
if (!subdev->brt_tbl.brt || subdev->brt_tbl.sz_brt == 0) {
panel_err("bl-%d brightness table not exist\n", id);
return -EINVAL;
}
if (!is_valid_brightness(panel_bl, brightness)) {
panel_err("bl-%d invalid brightness\n", id);
return -EINVAL;
}
luminance = get_actual_brightness(panel_bl, brightness);
ilum = get_actual_brightness_index(panel_bl, brightness);
step = get_brightness_pac_step(panel_bl, brightness);
if (step < 0) {
panel_err("bl-%d invalid pac stap %d\n", id, step);
return -EINVAL;
}
luminance_interp = get_actual_brightness_interpolation(panel_bl, brightness);
panel_bl->props.prev_brightness = panel_bl->props.brightness;
panel_bl->props.brightness = brightness;
panel_bl->props.actual_brightness = luminance;
panel_bl->props.actual_brightness_index = ilum;
panel_bl->props.actual_brightness_intrp = luminance_interp;
panel_bl->props.step = step;
panel_bl->props.brightness_of_step = subdev->brt_tbl.brt_to_step[step];
panel_bl_update_acl_state(panel_bl);
dim_type = DIM_TYPE_STR_TABLE;
#ifdef CONFIG_SUPPORT_DIM_FLASH
if (panel->panel_data.props.cur_dim_type)
dim_type = DIM_TYPE_STR_FLASH;
#endif
if (subdev->brt_tbl.control_type == BRIGHTNESS_CONTROL_TYPE_GAMMA_MODE2)
dim_type = DIM_TYPE_STR_GM2;
panel_info("bl-%d dim:%s plat_br:%d br[%d]:%d nit:%d(%u.%02u) acl:%s(%d) cnt:%d %s\n", id,
((dim_type < MAX_DIM_TYPE_STR) ? dim_type_str[dim_type] : "invalid"),
brightness, step, subdev->brt_tbl.brt_to_step[step],
luminance, luminance_interp / CALC_SCALE, luminance_interp % CALC_SCALE,
panel_bl->props.acl_pwrsave ? "on" : "off",
panel_bl->props.acl_opr,
panel_bl_get_brightness_set_count(panel_bl),
(send_cmd == SKIP_CMD ? "skip" : ""));
if (unlikely(send_cmd == SKIP_CMD)) {
panel_bl_set_saved_flag(panel_bl, true);
goto set_br_exit;
}
#ifdef CONFIG_PANEL_VRR_BRIDGE
if (panel_vrr_bridge_is_supported(panel) &&
!panel_vrr_bridge_is_reached_target_nolock(panel)) {
panel->panel_data.props.panel_mode =
panel->panel_data.props.target_panel_mode;
need_update_display_mode = true;
}
#endif
#if defined(CONFIG_PANEL_DISPLAY_MODE)
if (panel_vrr_is_supported(panel) && panel->panel_data.props.vrr_updated == true) {
panel->panel_data.props.vrr_updated = false;
need_update_display_mode = true;
}
#endif
//g_tracing_mark_write('C', "lcd_br", luminance);
#ifdef CONFIG_SUPPORT_HMD
if (id == PANEL_BL_SUBDEV_TYPE_HMD)
index = PANEL_HMD_BL_SEQ;
#endif
#ifdef CONFIG_SUPPORT_AOD_BL
if (id == PANEL_BL_SUBDEV_TYPE_AOD)
index = PANEL_ALPM_SET_BL_SEQ;
#endif
#ifdef CONFIG_EVASION_DISP_DET
if (panel->state.cur_state == PANEL_STATE_NORMAL)
panel_disable_disp_det_irq(panel);
#endif
if (index == PANEL_SET_BL_SEQ && need_update_display_mode) {
#if defined(CONFIG_PANEL_DISPLAY_MODE)
ret = panel_set_display_mode_nolock(panel, panel->panel_data.props.panel_mode);
if (unlikely(ret < 0)) {
panel_err("failed to panel_set_display_mode\n");
goto set_br_exit;
}
panel_display_mode_cb(panel);
#endif
} else {
ret = panel_do_seqtbl_by_index_nolock(panel, index);
if (unlikely(ret < 0)) {
panel_err("failed to write set_bl seqtbl %d\n", index);
goto set_br_exit;
}
}
panel_bl_update_average(panel_bl, 0);
panel_bl_update_average(panel_bl, 1);
wake_up_interruptible_all(&panel_bl->wq.wait);
panel_bl_inc_brightness_set_count(panel_bl);
#ifdef CONFIG_EVASION_DISP_DET
if (panel->state.cur_state == PANEL_STATE_NORMAL)
panel_enable_disp_det_irq(panel);
#endif
panel_bl_set_saved_flag(panel_bl, false);
set_br_exit:
return ret;
}
static int panel_bl_subdev_get_valid_brightness(struct panel_bl_device *panel_bl,
const int subdev_id, const int brt)
{
int max;
max = get_subdev_max_brightness(panel_bl, subdev_id);
if (max < 0) {
panel_err("failed to update subdev[%d] brightness\n", subdev_id);
return 0;
} else if (max < brt) {
panel_dbg("subdev[%d] brightness set to %d -> %d\n", subdev_id, brt, max);
return max;
}
return brt;
}
static int panel_get_brightness(struct backlight_device *bd)
{
struct panel_bl_device *panel_bl = bl_get_data(bd);
return get_actual_brightness(panel_bl, bd->props.brightness);
}
int _panel_update_brightness(struct panel_device *panel, u32 send_cmd)
{
int ret = 0;
int id, brightness;
struct panel_bl_device *panel_bl = &panel->panel_bl;
struct backlight_device *bd = panel_bl->bd;
mutex_lock(&panel_bl->lock);
if (bd == NULL) {
panel_dbg("panel_bl not prepared\n");
mutex_unlock(&panel_bl->lock);
return -ENODEV;
}
mutex_lock(&panel->op_lock);
if (panel->state.cur_state == PANEL_POWER_OFF ||
panel->state.cur_state == PANEL_POWER_ON)
send_cmd = SKIP_CMD;
brightness = bd->props.brightness;
if (brightness < 0) {
panel_err("brightness %d is out of range\n", brightness);
brightness = 0;
}
#ifdef CONFIG_SUPPORT_MASK_LAYER
if (panel_bl->props.mask_layer_br_hook == MASK_LAYER_HOOK_ON) {
brightness = panel_bl->props.mask_layer_br_target;
panel_info("mask_layer_br_hook (%d)->(%d)\n",
bd->props.brightness, panel_bl->props.mask_layer_br_target);
}
#endif
panel_bl->subdev[PANEL_BL_SUBDEV_TYPE_DISP].brightness =
panel_bl_subdev_get_valid_brightness(panel_bl, PANEL_BL_SUBDEV_TYPE_DISP, brightness);
#ifdef CONFIG_SUPPORT_AOD_BL
panel_bl->subdev[PANEL_BL_SUBDEV_TYPE_AOD].brightness =
panel_bl_subdev_get_valid_brightness(panel_bl, PANEL_BL_SUBDEV_TYPE_AOD, brightness);
#endif
id = panel_bl->props.id;
#ifdef CONFIG_SUPPORT_HMD
if (id == PANEL_BL_SUBDEV_TYPE_HMD) {
panel_info("keep plat_br:%d\n", brightness);
ret = -EINVAL;
goto exit_set;
}
#endif
ret = panel_bl_set_brightness(panel_bl, id, send_cmd);
if (ret) {
panel_err("failed to set_brightness (ret %d)\n", ret);
goto exit_set;
}
exit_set:
mutex_unlock(&panel->op_lock);
mutex_unlock(&panel_bl->lock);
return ret;
}
inline int panel_update_brightness(struct panel_device *panel)
{
return _panel_update_brightness(panel, SEND_CMD);
}
inline int panel_update_brightness_cmd_skip(struct panel_device *panel)
{
return _panel_update_brightness(panel, SKIP_CMD);
}
static int panel_bl_update_status(struct backlight_device *bd)
{
int ret;
struct panel_bl_device *panel_bl = bl_get_data(bd);
struct panel_device *panel = to_panel_device(panel_bl);
panel_wake_lock(panel, WAKE_TIMEOUT_MSEC);
ret = panel_update_brightness(panel);
panel_wake_unlock(panel);
return ret;
}
static const struct backlight_ops panel_backlight_ops = {
.get_brightness = panel_get_brightness,
.update_status = panel_bl_update_status,
};
static int panel_bl_thread(void *data)
{
struct panel_bl_device *panel_bl = data;
#ifdef CONFIG_PANEL_NOTIFY
struct panel_bl_event_data bl_evt_data;
#endif
int ret, brightness, acl_state;
bool should_stop = false;
while (!kthread_should_stop()) {
brightness = panel_bl->props.brightness;
acl_state = panel_bl->props.acl_pwrsave;
ret = wait_event_interruptible(panel_bl->wq.wait,
(should_stop = panel_bl->wq.should_stop || kthread_should_stop()) ||
(brightness != panel_bl->props.brightness) ||
(acl_state != panel_bl->props.acl_pwrsave));
if (should_stop)
break;
#ifdef CONFIG_PANEL_NOTIFY
bl_evt_data.display_idx = to_panel_device(panel_bl)->id;
bl_evt_data.brightness = panel_bl->props.brightness;
bl_evt_data.aor_ratio =
(panel_bl->props.id == PANEL_BL_SUBDEV_TYPE_DISP) ?
panel_bl->props.aor_ratio : 0;
bl_evt_data.acl_status =
(panel_bl->props.acl_pwrsave == ACL_PWRSAVE_OFF) ? 0 : 1;
panel_notifier_call_chain(PANEL_EVENT_BL_CHANGED, &bl_evt_data);
#endif
}
return 0;
}
static int panel_bl_create_thread(struct panel_bl_device *panel_bl)
{
if (unlikely(!panel_bl)) {
panel_warn("panel_bl unsupported\n");
return 0;
}
init_waitqueue_head(&panel_bl->wq.wait);
panel_bl->wq.should_stop = false;
panel_bl->wq.thread = kthread_run(panel_bl_thread,
panel_bl, "panel-bl-thread");
if (IS_ERR_OR_NULL(panel_bl->wq.thread)) {
panel_err("failed to run panel_bl thread\n");
panel_bl->wq.thread = NULL;
return PTR_ERR(panel_bl->wq.thread);
}
return 0;
}
static int panel_bl_destroy_thread(struct panel_bl_device *panel_bl)
{
if (IS_ERR_OR_NULL(panel_bl->wq.thread))
return 0;
panel_bl->wq.should_stop = true;
/* wake up waitqueue to stop */
wake_up_interruptible_all(&panel_bl->wq.wait);
/* kthread_should_stop() == true */
kthread_stop(panel_bl->wq.thread);
return 0;
}
__visible_for_testing int panel_bl_set_name(struct panel_bl_device *panel_bl, unsigned int id)
{
if (!panel_bl)
return -EINVAL;
if (id == 0)
snprintf(panel_bl->name, MAX_PANEL_BL_NAME_SIZE,
"%s", PANEL_DEV_NAME);
else
snprintf(panel_bl->name, MAX_PANEL_BL_NAME_SIZE,
"%s-%d", PANEL_DEV_NAME, id);
return 0;
}
__visible_for_testing const char *panel_bl_get_name(struct panel_bl_device *panel_bl)
{
return panel_bl ? panel_bl->name : NULL;
}
__visible_for_testing int panel_bl_init_property(struct panel_bl_device *panel_bl)
{
if (!panel_bl)
return -EINVAL;
panel_bl->props.id = PANEL_BL_SUBDEV_TYPE_DISP;
panel_bl->props.brightness = UI_DEF_BRIGHTNESS;
panel_bl->props.acl_pwrsave = ACL_PWRSAVE_OFF;
panel_bl->props.acl_opr = 1;
panel_bl->props.smooth_transition = SMOOTH_TRANS_ON;
return 0;
}
int panel_bl_init(struct panel_bl_device *panel_bl)
{
struct backlight_device *bd;
int ret;
if (!panel_bl)
return -EINVAL;
mutex_init(&panel_bl->lock);
ret = panel_bl_set_name(panel_bl,
to_panel_device(panel_bl)->id);
if (ret < 0)
return -EINVAL;
ret = panel_bl_init_property(panel_bl);
if (ret < 0) {
panel_err("failed to init property\n");
return ret;
}
bd = backlight_device_register(panel_bl_get_name(panel_bl),
to_panel_device(panel_bl)->dev, panel_bl, &panel_backlight_ops, NULL);
if (IS_ERR(bd)) {
panel_err("failed to register backlight\n");
return PTR_ERR(panel_bl->bd);
}
panel_bl->bd = bd;
panel_bl->bd->props.brightness = UI_DEF_BRIGHTNESS;
ret = panel_bl_create_thread(panel_bl);
if (ret < 0) {
panel_err("failed to create panel_bl thread\n");
return ret;
}
panel_info("panel_bl init success\n");
return 0;
}
int panel_bl_exit(struct panel_bl_device *panel_bl)
{
if (!panel_bl)
return -EINVAL;
if (IS_ERR_OR_NULL(panel_bl->bd)) {
panel_err("backlight device is null\n");
return -EINVAL;
}
mutex_lock(&panel_bl->lock);
panel_bl_destroy_thread(panel_bl);
backlight_device_unregister(panel_bl->bd);
panel_bl->bd = NULL;
memset(panel_bl->name, 0, sizeof(panel_bl->name));
mutex_unlock(&panel_bl->lock);
return 0;
}
__visible_for_testing int panel_bl_init_backlight_device_property(struct panel_bl_device *panel_bl)
{
if (!panel_bl)
return -EINVAL;
if (!panel_bl->bd) {
panel_err("backlight_device is null\n");
return -EINVAL;
}
panel_bl->bd->props.max_brightness =
get_subdev_max_brightness(panel_bl, panel_bl->props.id);
return 0;
}
#if defined(CONFIG_PANEL_BL_USE_BRT_CACHE)
__visible_for_testing int panel_bl_alloc_brt_cache(struct panel_bl_device *panel_bl)
{
int id, size;
if (!panel_bl)
return -EINVAL;
for (id = 0; id < MAX_PANEL_BL_SUBDEV; id++) {
size = get_subdev_max_brightness(panel_bl, id) + 1;
if (size <= 0) {
panel_warn("invalid brightness table (size %d)\n", size);
panel_bl->subdev[id].sz_brt_cache_tbl = -1;
continue;
}
if (panel_bl->subdev[id].brt_cache_tbl) {
panel_info("bl-%d free brt_cache\n", id);
kfree(panel_bl->subdev[id].brt_cache_tbl);
panel_bl->subdev[id].sz_brt_cache_tbl = 0;
}
panel_bl->subdev[id].brt_cache_tbl =
kcalloc(size, sizeof(int), GFP_KERNEL);
memset(panel_bl->subdev[id].brt_cache_tbl, 0xFF, size * sizeof(int));
panel_bl->subdev[id].sz_brt_cache_tbl = size;
}
return 0;
}
__visible_for_testing int panel_bl_free_brt_cache(struct panel_bl_device *panel_bl)
{
int id;
if (!panel_bl)
return -EINVAL;
for (id = 0; id < MAX_PANEL_BL_SUBDEV; id++) {
if (panel_bl->subdev[id].sz_brt_cache_tbl <= 0)
continue;
kfree(panel_bl->subdev[id].brt_cache_tbl);
}
return 0;
}
#else
static inline int panel_bl_alloc_brt_cache(struct panel_bl_device *panel_bl) { return 0; }
static inline int panel_bl_free_brt_cache(struct panel_bl_device *panel_bl) { return 0; }
#endif
int panel_bl_probe(struct panel_bl_device *panel_bl)
{
int ret;
if (!panel_bl)
return -EINVAL;
mutex_lock(&panel_bl->lock);
ret = panel_bl_init_backlight_device_property(panel_bl);
if (ret < 0) {
panel_err("failed to init backlight device property\n");
goto err;
}
ret = panel_bl_alloc_brt_cache(panel_bl);
if (ret < 0) {
panel_err("failed to alloc brt cache\n");
goto err;
}
mutex_unlock(&panel_bl->lock);
panel_info("panel_bl probe success\n");
return 0;
err:
mutex_unlock(&panel_bl->lock);
return ret;
}
int panel_bl_remove(struct panel_bl_device *panel_bl)
{
if (!panel_bl)
return -EINVAL;
mutex_lock(&panel_bl->lock);
panel_bl_free_brt_cache(panel_bl);
mutex_unlock(&panel_bl->lock);
panel_info("done\n");
return 0;
}