#include "dm_services.h"
#include "dc.h"
#include "mod_power.h"
#include "core_types.h"
#include "dmcu.h"
#include "abm.h"
#include "power_helpers.h"
#include "dce/dmub_psr.h"
#include "dal_asic_id.h"
#include "link_service.h"
#include <linux/math.h>
#define DC_TRACE_LEVEL_MESSAGE(...)
#define DC_TRACE_LEVEL_MESSAGEP(...)
#define MOD_POWER_MAX_CONCURRENT_STREAMS 32
#define SMOOTH_BRIGHTNESS_ADJUSTMENT_TIME_IN_MS 500
#define LOW_REFRESH_RATE_DURATION_US_UPPER_BOUND 25000
static const unsigned int pwr_default_min_brightness_millinits = 1000;
static const unsigned int pwr_default_sdr_brightness_millinits = 270000;
static const unsigned int default_ac_backlight_percent = 100;
static const unsigned int default_dc_backlight_percent = 70;
#define MOD_POWER_TO_CORE(mod_power)\
container_of(mod_power, struct core_power, mod_public)
unsigned int map_index_from_stream(struct core_power *core_power,
const struct dc_stream_state *stream)
{
unsigned int index = 0;
for (index = 0; index < core_power->num_entities; index++) {
if (core_power->map[index].stream == stream)
return index;
}
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"map index from stream: ERROR: core_power=%p stream=%p",
core_power,
stream);
ASSERT(false);
return 0;
}
bool mod_power_hw_init(struct mod_power *mod_power)
{
return mod_power_hw_init_backlight(mod_power);
}
struct mod_power *mod_power_create(struct dc *dc,
struct mod_power_init_params *init_params,
unsigned int edp_num)
{
struct core_power *core_power = NULL;
int i = 0;
unsigned int abm_max_config = 0;
unsigned int inst = 0;
bool is_brightness_range_valid = false;
if (dc == NULL)
goto fail_dc_null;
core_power = kzalloc(sizeof(struct core_power), GFP_KERNEL);
if (core_power == NULL)
goto fail_alloc_context;
core_power->edp_num = edp_num;
core_power->map = kzalloc(sizeof(struct power_entity) * MOD_POWER_MAX_CONCURRENT_STREAMS,
GFP_KERNEL);
if (core_power->map == NULL)
goto fail_alloc_map;
for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++) {
core_power->map[i].stream = NULL;
}
for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++) {
core_power->map[i].psr_context =
kzalloc(sizeof(struct mod_power_psr_context),
GFP_KERNEL);
if (core_power->map[i].psr_context == NULL)
goto fail_construct;
}
core_power->psr_smu_optimizations_support = init_params->allow_psr_smu_optimizations;
core_power->multi_disp_optimizations_support = init_params->allow_psr_multi_disp_optimizations;
for (inst = 0; inst < edp_num; inst++) {
core_power->bl_prop[inst].min_abm_backlight =
init_params[inst].min_abm_backlight;
core_power->bl_prop[inst].disable_fractional_pwm =
init_params[inst].disable_fractional_pwm;
core_power->bl_prop[inst].use_linear_backlight_curve =
init_params[inst].use_linear_backlight_curve;
core_power->bl_prop[inst].use_nits_based_brightness =
init_params[inst].use_nits_based_brightness;
core_power->bl_prop[inst].backlight_ramping_override =
init_params[inst].backlight_ramping_override;
core_power->bl_prop[inst].backlight_ramping_reduction =
init_params[inst].backlight_ramping_reduction;
core_power->bl_prop[inst].backlight_ramping_start =
init_params[inst].backlight_ramping_start;
core_power->bl_prop[inst].use_custom_backlight_caps =
init_params[inst].use_custom_backlight_caps;
core_power->bl_prop[inst].custom_backlight_caps_config_no =
init_params[inst].custom_backlight_caps_config_no;
if (init_params[inst].num_backlight_levels < 101)
core_power->bl_prop[inst].num_backlight_levels = 101;
else
core_power->bl_prop[inst].num_backlight_levels =
init_params[inst].num_backlight_levels;
core_power->bl_prop[inst].backlight_lut = (unsigned int *)
(kzalloc(sizeof(unsigned int) *
core_power->bl_prop[inst].num_backlight_levels, GFP_KERNEL));
if (core_power->bl_prop[inst].backlight_lut == NULL)
goto fail_alloc_backlight_array;
}
core_power->varibright_prop.varibright_active = false;
core_power->varibright_prop.varibright_user_enable =
init_params->def_varibright_enable;
if (init_params->varibright_level <= abm_defines_max_level) {
core_power->varibright_prop.varibright_level =
init_params->varibright_level;
} else {
core_power->varibright_prop.varibright_level = 3;
}
if (init_params->def_varibright_level <= abm_defines_max_level) {
core_power->varibright_prop.def_varibright_level =
init_params->def_varibright_level;
} else {
core_power->varibright_prop.def_varibright_level = 3;
}
if ((dc->res_pool->dmcu != NULL) && (dc->res_pool->dmcu->dmcu_version.abm_version < 0x23))
abm_max_config = 4;
else
abm_max_config = 3;
if (init_params->abm_config_setting < abm_max_config)
core_power->varibright_prop.varibright_config_setting =
init_params->abm_config_setting;
else
core_power->varibright_prop.varibright_config_setting = 0;
for (inst = 0; inst < edp_num; inst++) {
core_power->bl_prop[inst].backlight_lut[0] = init_params[inst].min_backlight_pwm;
core_power->bl_prop[inst].backlight_lut[
core_power->bl_prop[inst].num_backlight_levels-1] =
init_params[inst].max_backlight_pwm;
core_power->bl_prop[inst].min_backlight_pwm = init_params[inst].min_backlight_pwm;
core_power->bl_prop[inst].max_backlight_pwm = init_params[inst].max_backlight_pwm;
core_power->bl_prop[inst].ac_backlight_percent =
default_ac_backlight_percent;
core_power->bl_prop[inst].dc_backlight_percent =
default_dc_backlight_percent;
core_power->bl_prop[inst].backlight_caps_valid = false;
if (core_power->bl_prop[inst].use_nits_based_brightness) {
core_power->bl_prop[inst].min_brightness_millinits =
init_params[inst].panel_min_millinits;
core_power->bl_prop[inst].max_brightness_millinits =
init_params[inst].panel_max_millinits;
} else {
core_power->bl_prop[inst].min_brightness_millinits =
pwr_default_min_brightness_millinits;
core_power->bl_prop[inst].max_brightness_millinits =
pwr_default_sdr_brightness_millinits;
}
core_power->bl_prop[inst].backlight_range =
core_power->bl_prop[inst].max_backlight_pwm-
core_power->bl_prop[inst].min_backlight_pwm;
core_power->bl_prop[inst].nits_range =
core_power->bl_prop[inst].max_brightness_millinits -
core_power->bl_prop[inst].min_brightness_millinits;
core_power->bl_state[inst].smooth_brightness_enabled = true;
}
for (inst = 0; inst < edp_num; inst++) {
if (core_power->bl_prop[inst].nits_range != 0 && core_power->bl_prop[inst].backlight_range != 0) {
is_brightness_range_valid = true;
break;
}
}
if (!is_brightness_range_valid)
goto fail_bad_brightness_range;
core_power->num_entities = 0;
core_power->dc = dc;
for (inst = 0; inst < edp_num; inst++) {
initialize_backlight_caps(core_power, inst);
core_power->bl_state[inst].backlight_millipercent =
core_power->bl_prop[inst].dc_backlight_percent * 1000;
core_power->bl_state[inst].backlight_pwm = backlight_millipercent_to_pwm(core_power,
core_power->bl_state[inst].backlight_millipercent, inst);
core_power->bl_state[inst].backlight_millinit = backlight_millipercent_to_millinit(core_power,
core_power->bl_state[inst].backlight_millipercent, inst);
}
return &core_power->mod_public;
fail_bad_brightness_range:
fail_alloc_backlight_array:
for (inst = 0; inst < edp_num; inst++)
if (core_power->bl_prop[inst].backlight_lut)
kfree(core_power->bl_prop[inst].backlight_lut);
fail_construct:
for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++) {
if (core_power->map[i].psr_context)
kfree(core_power->map[i].psr_context);
}
kfree(core_power->map);
fail_alloc_map:
kfree(core_power);
fail_alloc_context:
fail_dc_null:
return NULL;
}
void mod_power_destroy(struct mod_power *mod_power)
{
if (mod_power != NULL) {
unsigned int i;
struct core_power *core_power =
MOD_POWER_TO_CORE(mod_power);
for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++)
if (core_power->map[i].psr_context)
kfree(core_power->map[i].psr_context);
for (i = 0; i < core_power->num_entities; i++)
if (core_power->map[i].stream)
dc_stream_release(core_power->map[i].stream);
kfree(core_power->map);
for (i = 0; i < MAX_NUM_EDP; i++)
if (core_power->bl_prop[i].backlight_lut)
kfree(core_power->bl_prop[i].backlight_lut);
kfree(core_power);
}
}
bool mod_power_add_stream(struct mod_power *mod_power,
struct dc_stream_state *stream, struct psr_caps *caps)
{
struct core_power *core_power = NULL;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities < MOD_POWER_MAX_CONCURRENT_STREAMS) {
dc_stream_retain(stream);
core_power->map[core_power->num_entities].stream = stream;
core_power->map[core_power->num_entities].caps = caps;
core_power->map[core_power->num_entities].psr_enabled = 0;
core_power->map[core_power->num_entities].psr_events = psr_event_vsync;
core_power->map[core_power->num_entities].psr_power_opt = 0;
core_power->num_entities++;
return true;
}
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"mod_power: add_stream: ERROR: stream=%p num_entities=%u >= MOD_POWER_MAX_CONCURRENT_STREAMS",
stream,
core_power->num_entities);
return false;
}
bool mod_power_remove_stream(struct mod_power *mod_power,
const struct dc_stream_state *stream)
{
unsigned int i = 0;
struct core_power *core_power = NULL;
unsigned int index = 0;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0) {
BREAK_TO_DEBUGGER();
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"mod_power: remove_stream: ERROR: num_entities=0 stream=%p",
stream);
return false;
}
index = map_index_from_stream(core_power, stream);
if (index >= core_power->num_entities) {
BREAK_TO_DEBUGGER();
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"mod_power: remove_stream: ERROR: index=%u >= num_entities=%u stream=%p",
index,
core_power->num_entities,
stream);
return false;
}
dc_stream_release(core_power->map[index].stream);
core_power->map[index].stream = NULL;
for (i = index; i < core_power->num_entities - 1; i++) {
core_power->map[i].stream = core_power->map[i + 1].stream;
core_power->map[i].caps = core_power->map[i + 1].caps;
core_power->map[i].psr_enabled = core_power->map[i + 1].psr_enabled;
core_power->map[i].psr_events = core_power->map[i + 1].psr_events;
core_power->map[i].psr_power_opt = core_power->map[i + 1].psr_power_opt;
memcpy(core_power->map[i].psr_context, core_power->map[i + 1].psr_context, sizeof(struct mod_power_psr_context));
memset(core_power->map[i + 1].psr_context, 0, sizeof(struct mod_power_psr_context));
}
core_power->num_entities--;
return true;
}
bool mod_power_replace_stream(struct mod_power *mod_power,
const struct dc_stream_state *current_stream,
struct dc_stream_state *new_stream,
struct psr_caps *new_caps)
{
struct core_power *core_power = NULL;
unsigned int index = 0;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0) {
BREAK_TO_DEBUGGER();
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"mod_power: replace_stream: ERROR: num_entities=0 stream=%p",
current_stream);
return false;
}
index = map_index_from_stream(core_power, current_stream);
if (index >= core_power->num_entities) {
BREAK_TO_DEBUGGER();
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"mod_power: replace_stream: ERROR: index=%u >= num_entities=%u stream=%p",
index,
core_power->num_entities,
current_stream);
return false;
}
dc_stream_release(core_power->map[index].stream);
dc_stream_retain(new_stream);
core_power->map[index].stream = new_stream;
core_power->map[index].caps = new_caps;
memset(core_power->map[index].psr_context, 0, sizeof(struct mod_power_psr_context));
return true;
}
bool mod_power_notify_mode_change(struct mod_power *mod_power,
const struct dc_stream_state *stream,
bool is_hdr)
{
unsigned int stream_index = 0;
struct core_power *core_power = NULL;
struct dc_link *link = NULL;
struct dc *dc = NULL;
unsigned int panel_inst = 0;
uint8_t aux_inst = 0;
if ((mod_power == NULL) || (stream == NULL))
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
stream_index = map_index_from_stream(core_power, stream);
if (stream_index >= core_power->num_entities)
return false;
dc = core_power->dc;
link = dc_stream_get_link(stream);
if (link != NULL && dc_get_edp_link_panel_inst(dc, link, &panel_inst)) {
if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) {
aux_inst = (uint8_t)link->aux_hw_inst;
} else {
ASSERT(link->ddc->ddc_pin->hw_info.ddc_channel <= 0xFF);
aux_inst = (uint8_t)link->ddc->ddc_pin->hw_info.ddc_channel;
}
mod_power_update_backlight_on_mode_change(core_power, link, panel_inst, aux_inst, is_hdr);
mod_power_psr_notify_mode_change(mod_power, stream, link, stream_index);
mod_power_replay_notify_mode_change(mod_power, dc, link, stream, stream_index);
}
return true;
}