1278 lines
No EOL
34 KiB
C
Executable file
1278 lines
No EOL
34 KiB
C
Executable file
// 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;
|
|
} |