1485 lines
40 KiB
C
1485 lines
40 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2020 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/uaccess.h>
|
|
#include <linux/clk.h>
|
|
#include <mtk_vcorefs_manager.h>
|
|
#include <mt-plat/mtk_chip.h>
|
|
#include <mtk_dramc.h>
|
|
|
|
#if defined(SMI_WHI)
|
|
#include "mmdvfs_config_mt6799.h"
|
|
#include "mmdvfs_config_mt6799_v2.h"
|
|
#elif defined(SMI_ALA)
|
|
#include "mmdvfs_config_mt6759.h"
|
|
#elif defined(SMI_BIA)
|
|
#include "mmdvfs_config_mt6763.h"
|
|
#elif defined(SMI_VIN)
|
|
#include "mmdvfs_config_mt6758.h"
|
|
#elif defined(SMI_ZIO)
|
|
#include "mmdvfs_config_mt6739.h"
|
|
#elif defined(SMI_MER)
|
|
#include "mmdvfs_config_mt6761.h"
|
|
#elif defined(SMI_SYL)
|
|
#include "mmdvfs_config_mt6771.h"
|
|
#elif defined(SMI_CAN)
|
|
#include "mmdvfs_config_mt6775.h"
|
|
#endif
|
|
|
|
#include "mtk_smi.h"
|
|
#include "mmdvfs_mgr.h"
|
|
#include "mmdvfs_internal.h"
|
|
|
|
#ifdef CONFIG_MTK_FREQ_HOPPING
|
|
#include "mtk_freqhopping_drv.h"
|
|
#endif
|
|
|
|
#ifdef USE_DDR_TYPE
|
|
#include "mt_emi_api.h"
|
|
#endif
|
|
#include "mmdvfs_pmqos.h"
|
|
|
|
|
|
/* Class: mmdvfs_step_util */
|
|
static int mmdvfs_get_legacy_mmclk_step_from_mmclk_opp(
|
|
struct mmdvfs_step_util *self, int mmclk_step);
|
|
static int mmdvfs_get_opp_from_legacy_step(
|
|
struct mmdvfs_step_util *self, int legacy_step);
|
|
static void mmdvfs_step_util_init(struct mmdvfs_step_util *self);
|
|
static int mmdvfs_step_util_set_step(
|
|
struct mmdvfs_step_util *self, s32 step, u32 scenario);
|
|
static int mmdvfs_get_clients_clk_opp(
|
|
struct mmdvfs_step_util *self, struct mmdvfs_adaptor *adaptor,
|
|
int clients_mask, int clk_id);
|
|
|
|
#ifdef DYNAMIC_DISP_HRT
|
|
static void mmdvfs_change_disp_hrt(bool has_camera_scenario);
|
|
#endif
|
|
static bool in_camera_scenario;
|
|
u32 camera_bw_config;
|
|
u32 normal_bw_config;
|
|
static disp_hrt_change_cb g_disp_hrt_change_cb;
|
|
u32 cam_sensor_threshold;
|
|
u32 disp_hrt_decrease_level1;
|
|
u32 disp_hrt_decrease_default;
|
|
static bool g_is_md_on;
|
|
|
|
|
|
#if defined(SMI_WHI)
|
|
struct mmdvfs_step_util mmdvfs_step_util_obj = {
|
|
{0},
|
|
MMDVFS_SCEN_COUNT,
|
|
{0},
|
|
MMDVFS_OPP_MAX,
|
|
mmdvfs_legacy_step_to_opp,
|
|
MMDVFS_VOLTAGE_COUNT,
|
|
mmdvfs_mmclk_opp_to_legacy_mmclk_step,
|
|
MMDVFS_OPP_MAX,
|
|
MMDVFS_FINE_STEP_UNREQUEST,
|
|
mmdvfs_step_util_init,
|
|
mmdvfs_get_legacy_mmclk_step_from_mmclk_opp,
|
|
mmdvfs_get_opp_from_legacy_step,
|
|
mmdvfs_step_util_set_step,
|
|
mmdvfs_get_clients_clk_opp
|
|
};
|
|
|
|
struct mmdvfs_step_util mmdvfs_step_util_obj_mt6799_v2 = {
|
|
{0},
|
|
MMDVFS_SCEN_COUNT,
|
|
{0},
|
|
MT6799_V2_MMDVFS_OPP_MAX,
|
|
mmdvfs_legacy_step_to_opp_6799v2,
|
|
MMDVFS_VOLTAGE_COUNT,
|
|
mmdvfs_mmclk_opp_to_legacy_mmclk_step_6799v2,
|
|
MT6799_V2_MMDVFS_OPP_MAX,
|
|
MMDVFS_FINE_STEP_UNREQUEST,
|
|
mmdvfs_step_util_init,
|
|
mmdvfs_get_legacy_mmclk_step_from_mmclk_opp,
|
|
mmdvfs_get_opp_from_legacy_step,
|
|
mmdvfs_step_util_set_step,
|
|
mmdvfs_get_clients_clk_opp
|
|
};
|
|
|
|
#elif defined(SMI_ALA)
|
|
struct mmdvfs_step_util mmdvfs_step_util_obj_mt6759 = {
|
|
{0},
|
|
MMDVFS_SCEN_COUNT,
|
|
{0},
|
|
MT6759_MMDVFS_OPP_MAX,
|
|
mt6759_mmdvfs_legacy_step_to_opp,
|
|
MMDVFS_VOLTAGE_COUNT,
|
|
mt6759_mmdvfs_mmclk_opp_to_legacy_mmclk_step,
|
|
MT6759_MMDVFS_OPP_MAX,
|
|
MMDVFS_FINE_STEP_UNREQUEST,
|
|
mmdvfs_step_util_init,
|
|
mmdvfs_get_legacy_mmclk_step_from_mmclk_opp,
|
|
mmdvfs_get_opp_from_legacy_step,
|
|
mmdvfs_step_util_set_step,
|
|
mmdvfs_get_clients_clk_opp
|
|
};
|
|
|
|
struct mmdvfs_step_util mmdvfs_step_util_obj_mt6759_non_force = {
|
|
{0},
|
|
MMDVFS_SCEN_COUNT,
|
|
{0},
|
|
MT6759_MMDVFS_OPP_MAX,
|
|
mt6759_mmdvfs_legacy_step_to_opp,
|
|
MMDVFS_VOLTAGE_COUNT,
|
|
mt6759_mmdvfs_mmclk_opp_to_legacy_mmclk_step,
|
|
MT6759_MMDVFS_OPP_MAX,
|
|
MMDVFS_FINE_STEP_UNREQUEST,
|
|
mmdvfs_step_util_init,
|
|
mmdvfs_get_legacy_mmclk_step_from_mmclk_opp,
|
|
mmdvfs_get_opp_from_legacy_step,
|
|
mmdvfs_step_util_set_step,
|
|
mmdvfs_get_clients_clk_opp
|
|
};
|
|
|
|
#elif defined(SMI_BIA)
|
|
struct mmdvfs_step_util mmdvfs_step_util_obj_mt6763 = {
|
|
{0},
|
|
MMDVFS_SCEN_COUNT,
|
|
{0},
|
|
MT6763_MMDVFS_OPP_MAX,
|
|
mt6763_mmdvfs_legacy_step_to_opp,
|
|
MMDVFS_VOLTAGE_COUNT,
|
|
mt6763_mmdvfs_mmclk_opp_to_legacy_mmclk_step,
|
|
MT6763_MMDVFS_OPP_MAX,
|
|
MMDVFS_FINE_STEP_OPP0,
|
|
mmdvfs_step_util_init,
|
|
mmdvfs_get_legacy_mmclk_step_from_mmclk_opp,
|
|
mmdvfs_get_opp_from_legacy_step,
|
|
mmdvfs_step_util_set_step,
|
|
mmdvfs_get_clients_clk_opp
|
|
};
|
|
|
|
#elif defined(SMI_VIN)
|
|
struct mmdvfs_step_util mmdvfs_step_util_obj_mt6758 = {
|
|
{0},
|
|
MMDVFS_SCEN_COUNT,
|
|
{0},
|
|
MT6758_MMDVFS_OPP_MAX,
|
|
mt6758_mmdvfs_legacy_step_to_opp,
|
|
MMDVFS_VOLTAGE_COUNT,
|
|
mt6758_mmdvfs_mmclk_opp_to_legacy_mmclk_step,
|
|
MT6758_MMDVFS_OPP_MAX,
|
|
MMDVFS_FINE_STEP_OPP0,
|
|
mmdvfs_step_util_init,
|
|
mmdvfs_get_legacy_mmclk_step_from_mmclk_opp,
|
|
mmdvfs_get_opp_from_legacy_step,
|
|
mmdvfs_step_util_set_step,
|
|
mmdvfs_get_clients_clk_opp
|
|
};
|
|
|
|
#elif defined(SMI_ZIO)
|
|
struct mmdvfs_step_util mmdvfs_step_util_obj_mt6739 = {
|
|
{0},
|
|
MMDVFS_SCEN_COUNT,
|
|
{0},
|
|
MT6739_MMDVFS_OPP_MAX,
|
|
mt6739_mmdvfs_legacy_step_to_opp,
|
|
MMDVFS_VOLTAGE_COUNT,
|
|
mt6739_mmdvfs_mmclk_opp_to_legacy_mmclk_step,
|
|
MT6739_MMDVFS_OPP_MAX,
|
|
MMDVFS_FINE_STEP_OPP0,
|
|
mmdvfs_step_util_init,
|
|
mmdvfs_get_legacy_mmclk_step_from_mmclk_opp,
|
|
mmdvfs_get_opp_from_legacy_step,
|
|
mmdvfs_step_util_set_step,
|
|
mmdvfs_get_clients_clk_opp
|
|
};
|
|
|
|
#elif defined(SMI_MER)
|
|
struct mmdvfs_step_util mmdvfs_step_util_obj_mt6761 = {
|
|
{0},
|
|
MMDVFS_SCEN_COUNT,
|
|
{0},
|
|
MT6761_MMDVFS_OPP_MAX,
|
|
NULL,
|
|
MMDVFS_VOLTAGE_COUNT,
|
|
NULL,
|
|
MT6761_MMDVFS_OPP_MAX,
|
|
MMDVFS_FINE_STEP_OPP0,
|
|
mmdvfs_step_util_init,
|
|
mmdvfs_get_legacy_mmclk_step_from_mmclk_opp,
|
|
mmdvfs_get_opp_from_legacy_step,
|
|
mmdvfs_step_util_set_step,
|
|
mmdvfs_get_clients_clk_opp
|
|
};
|
|
|
|
#elif defined(SMI_SYL)
|
|
struct mmdvfs_step_util mmdvfs_step_util_obj_mt6771 = {
|
|
{0},
|
|
MMDVFS_SCEN_COUNT,
|
|
{0},
|
|
MT6771_MMDVFS_OPP_MAX,
|
|
mt6771_mmdvfs_legacy_step_to_opp,
|
|
MMDVFS_VOLTAGE_COUNT,
|
|
mt6771_mmdvfs_mmclk_opp_to_legacy_mmclk_step,
|
|
MT6771_MMDVFS_OPP_MAX,
|
|
MMDVFS_FINE_STEP_OPP0,
|
|
mmdvfs_step_util_init,
|
|
mmdvfs_get_legacy_mmclk_step_from_mmclk_opp,
|
|
mmdvfs_get_opp_from_legacy_step,
|
|
mmdvfs_step_util_set_step,
|
|
mmdvfs_get_clients_clk_opp
|
|
};
|
|
|
|
#elif defined(SMI_CAN)
|
|
struct mmdvfs_step_util mmdvfs_step_util_obj_mt6775 = {
|
|
{0},
|
|
MMDVFS_SCEN_COUNT,
|
|
{0},
|
|
MT6775_MMDVFS_OPP_MAX,
|
|
mt6775_mmdvfs_legacy_step_to_opp,
|
|
MMDVFS_VOLTAGE_COUNT,
|
|
mt6775_mmdvfs_mmclk_opp_to_legacy_mmclk_step,
|
|
MT6775_MMDVFS_OPP_MAX,
|
|
MMDVFS_FINE_STEP_OPP0,
|
|
mmdvfs_step_util_init,
|
|
mmdvfs_get_legacy_mmclk_step_from_mmclk_opp,
|
|
mmdvfs_get_opp_from_legacy_step,
|
|
mmdvfs_step_util_set_step,
|
|
mmdvfs_get_clients_clk_opp
|
|
};
|
|
#endif
|
|
|
|
/* Class: mmdvfs_adaptor */
|
|
static void mmdvfs_single_profile_dump(struct mmdvfs_profile *profile);
|
|
static void mmdvfs_profile_dump(struct mmdvfs_adaptor *self);
|
|
static void mmdvfs_single_hw_configuration_dump(struct mmdvfs_adaptor *self,
|
|
struct mmdvfs_hw_configurtion *hw_configuration);
|
|
static void mmdvfs_hw_configuration_dump(struct mmdvfs_adaptor *self);
|
|
static int mmdvfs_determine_step(struct mmdvfs_adaptor *self,
|
|
int smi_scenario,
|
|
struct mmdvfs_cam_property *cam_setting,
|
|
struct mmdvfs_video_property *codec_setting);
|
|
static int mmdvfs_apply_hw_configurtion_by_step(struct mmdvfs_adaptor *self,
|
|
int mmdvfs_step, int current_step);
|
|
static int mmdvfs_apply_vcore_hw_configurtion_by_step(
|
|
struct mmdvfs_adaptor *self, int mmdvfs_step);
|
|
static int mmdvfs_apply_clk_hw_configurtion_by_step(
|
|
struct mmdvfs_adaptor *self, int mmdvfs_step, bool to_high);
|
|
static int mmdvfs_get_cam_sys_clk(
|
|
struct mmdvfs_adaptor *self, int mmdvfs_step);
|
|
|
|
static int is_camera_profile_matched(struct mmdvfs_cam_property *cam_setting,
|
|
struct mmdvfs_cam_property *profile_property);
|
|
|
|
static int is_video_profile_matched(
|
|
struct mmdvfs_video_property *video_setting,
|
|
struct mmdvfs_video_property *profile_property);
|
|
|
|
#if defined(SMI_ZIO)
|
|
struct mmdvfs_adaptor mmdvfs_adaptor_obj_mt6739 = {
|
|
KIR_MM,
|
|
0, 0, 0,
|
|
mt6739_clk_sources, MT6739_CLK_SOURCE_NUM,
|
|
mt6739_mmdvfs_clk_hw_map, MMDVFS_CLK_MUX_NUM,
|
|
mt6739_step_profile, MT6739_MMDVFS_OPP_MAX,
|
|
MT6739_MMDVFS_SMI_USER_CONTROL_SCEN_MASK,
|
|
mmdvfs_profile_dump,
|
|
mmdvfs_single_hw_configuration_dump,
|
|
mmdvfs_hw_configuration_dump,
|
|
mmdvfs_determine_step,
|
|
mmdvfs_apply_hw_configurtion_by_step,
|
|
mmdvfs_apply_vcore_hw_configurtion_by_step,
|
|
mmdvfs_apply_clk_hw_configurtion_by_step,
|
|
mmdvfs_get_cam_sys_clk,
|
|
mmdvfs_single_profile_dump,
|
|
};
|
|
|
|
#elif defined(SMI_MER)
|
|
struct mmdvfs_adaptor mmdvfs_adaptor_obj_mt6761 = {
|
|
0, 0, 0, 0,
|
|
NULL, 0,
|
|
NULL, 0,
|
|
mt6761_step_profile, MT6761_MMDVFS_OPP_MAX,
|
|
0,
|
|
mmdvfs_profile_dump,
|
|
mmdvfs_single_hw_configuration_dump,
|
|
mmdvfs_hw_configuration_dump,
|
|
mmdvfs_determine_step,
|
|
mmdvfs_apply_hw_configurtion_by_step,
|
|
mmdvfs_apply_vcore_hw_configurtion_by_step,
|
|
mmdvfs_apply_clk_hw_configurtion_by_step,
|
|
mmdvfs_get_cam_sys_clk,
|
|
mmdvfs_single_profile_dump,
|
|
};
|
|
|
|
#elif defined(SMI_SYL)
|
|
struct mmdvfs_adaptor mmdvfs_adaptor_obj_mt6771 = {
|
|
KIR_MM,
|
|
0, 0, 0,
|
|
mt6771_clk_sources, MT6771_CLK_SOURCE_NUM,
|
|
mt6771_mmdvfs_clk_hw_map, MMDVFS_CLK_MUX_NUM,
|
|
mt6771_step_profile, MT6771_MMDVFS_OPP_MAX,
|
|
MT6771_MMDVFS_SMI_USER_CONTROL_SCEN_MASK,
|
|
mmdvfs_profile_dump,
|
|
mmdvfs_single_hw_configuration_dump,
|
|
mmdvfs_hw_configuration_dump,
|
|
mmdvfs_determine_step,
|
|
mmdvfs_apply_hw_configurtion_by_step,
|
|
mmdvfs_apply_vcore_hw_configurtion_by_step,
|
|
mmdvfs_apply_clk_hw_configurtion_by_step,
|
|
mmdvfs_get_cam_sys_clk,
|
|
mmdvfs_single_profile_dump,
|
|
};
|
|
|
|
struct mmdvfs_adaptor mmdvfs_adaptor_obj_mt6771_3600 = {
|
|
KIR_MM,
|
|
0, 0, 0,
|
|
mt6771_clk_sources, MT6771_CLK_SOURCE_NUM,
|
|
mt6771_mmdvfs_clk_hw_map, MMDVFS_CLK_MUX_NUM,
|
|
mt6771_step_profile_3600, MT6771_MMDVFS_OPP_MAX,
|
|
MT6771_MMDVFS_SMI_USER_CONTROL_SCEN_MASK,
|
|
mmdvfs_profile_dump,
|
|
mmdvfs_single_hw_configuration_dump,
|
|
mmdvfs_hw_configuration_dump,
|
|
mmdvfs_determine_step,
|
|
mmdvfs_apply_hw_configurtion_by_step,
|
|
mmdvfs_apply_vcore_hw_configurtion_by_step,
|
|
mmdvfs_apply_clk_hw_configurtion_by_step,
|
|
mmdvfs_get_cam_sys_clk,
|
|
mmdvfs_single_profile_dump,
|
|
};
|
|
|
|
struct mmdvfs_adaptor mmdvfs_adaptor_obj_mt6771_lp3 = {
|
|
KIR_MM,
|
|
0, 0, 0,
|
|
mt6771_clk_sources, MT6771_CLK_SOURCE_NUM,
|
|
mt6771_mmdvfs_clk_hw_map, MMDVFS_CLK_MUX_NUM,
|
|
mt6771_step_profile_lp3, MT6771_MMDVFS_OPP_MAX,
|
|
MT6771_MMDVFS_SMI_USER_CONTROL_SCEN_MASK,
|
|
mmdvfs_profile_dump,
|
|
mmdvfs_single_hw_configuration_dump,
|
|
mmdvfs_hw_configuration_dump,
|
|
mmdvfs_determine_step,
|
|
mmdvfs_apply_hw_configurtion_by_step,
|
|
mmdvfs_apply_vcore_hw_configurtion_by_step,
|
|
mmdvfs_apply_clk_hw_configurtion_by_step,
|
|
mmdvfs_get_cam_sys_clk,
|
|
mmdvfs_single_profile_dump,
|
|
};
|
|
#endif
|
|
|
|
/* class: ISP PMQoS Handler */
|
|
|
|
/* ISP DVFS Adaptor Impementation */
|
|
static s32 get_step_by_threshold(struct mmdvfs_thresholds_dvfs_handler *self,
|
|
u32 class_id, u32 value);
|
|
|
|
struct mmdvfs_thresholds_dvfs_handler mmdvfs_thresholds_dvfs_handler_obj = {
|
|
NULL,
|
|
0,
|
|
get_step_by_threshold
|
|
};
|
|
|
|
#if defined(SMI_ZIO)
|
|
struct mmdvfs_thresholds_dvfs_handler dvfs_handler_mt6739 = {
|
|
mt6739_mmdvfs_threshold,
|
|
MMDVFS_PMQOS_NUM,
|
|
get_step_by_threshold
|
|
};
|
|
#elif defined(SMI_SYL)
|
|
struct mmdvfs_thresholds_dvfs_handler dvfs_handler_mt6771 = {
|
|
mt6771_mmdvfs_threshold_settings,
|
|
MMDVFS_PMQOS_NUM,
|
|
get_step_by_threshold
|
|
};
|
|
struct mmdvfs_thresholds_dvfs_handler dvfs_handler_mt6771_3600 = {
|
|
mt6771_mmdvfs_threshold_settings_3600,
|
|
MMDVFS_PMQOS_NUM,
|
|
get_step_by_threshold
|
|
};
|
|
|
|
#elif defined(SMI_CAN)
|
|
struct mmdvfs_thresholds_dvfs_handler dvfs_handler_mt6775 = {
|
|
mt6775_mmdvfs_threshold_settings,
|
|
MMDVFS_PMQOS_NUM,
|
|
get_step_by_threshold
|
|
};
|
|
#endif
|
|
|
|
static const struct mmdvfs_vpu_steps_setting *get_vpu_setting_impl(
|
|
struct mmdvfs_vpu_dvfs_configurator *self, int vpu_opp);
|
|
|
|
struct mmdvfs_vpu_dvfs_configurator mmdvfs_vpu_dvfs_configurator_obj = {
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
get_vpu_setting_impl
|
|
};
|
|
|
|
/* Member function implementation */
|
|
static int mmdvfs_apply_hw_configurtion_by_step(struct mmdvfs_adaptor *self,
|
|
int mmdvfs_step, const int current_step)
|
|
{
|
|
MMDVFSDEBUG(3, "current = %d, target = %d\n",
|
|
current_step, mmdvfs_step);
|
|
|
|
if (mmdvfs_step == current_step) {
|
|
MMDVFSDEBUG(3, "Doesn't change step, already in step: %d\n",
|
|
mmdvfs_step);
|
|
} else {
|
|
if (current_step == -1 || ((mmdvfs_step != -1)
|
|
&& (mmdvfs_step < current_step))) {
|
|
if (self->apply_vcore_hw_configurtion_by_step) {
|
|
MMDVFSDEBUG(3, "Apply setting(%d --> %d):\n",
|
|
current_step, mmdvfs_step);
|
|
MMDVFSDEBUG(3, "current = %d, target = %d\n",
|
|
current_step, mmdvfs_step);
|
|
self->apply_vcore_hw_configurtion_by_step(
|
|
self, mmdvfs_step);
|
|
} else {
|
|
MMDVFSDEBUG(3, "apply_vcore NULL.\n");
|
|
}
|
|
|
|
if (self->apply_clk_hw_configurtion_by_step) {
|
|
MMDVFSDEBUG(3, "Apply CLK setting:\n");
|
|
self->apply_clk_hw_configurtion_by_step(
|
|
self, mmdvfs_step, true);
|
|
} else {
|
|
MMDVFSDEBUG(3, "apply_clk NULL.\n");
|
|
}
|
|
} else {
|
|
if (self->apply_clk_hw_configurtion_by_step) {
|
|
MMDVFSDEBUG(3, "Apply CLK setting:\n");
|
|
self->apply_clk_hw_configurtion_by_step(
|
|
self, mmdvfs_step, false);
|
|
} else {
|
|
MMDVFSDEBUG(3, "apply_clk NULL.\n");
|
|
}
|
|
|
|
if (self->apply_vcore_hw_configurtion_by_step) {
|
|
MMDVFSDEBUG(3, "Apply setting(%d --> %d)\n",
|
|
current_step, mmdvfs_step);
|
|
MMDVFSDEBUG(3, "current = %d, target = %d\n",
|
|
current_step, mmdvfs_step);
|
|
self->apply_vcore_hw_configurtion_by_step(
|
|
self, mmdvfs_step);
|
|
} else {
|
|
MMDVFSDEBUG(3, "apply_vcore NULL.\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mmdvfs_apply_vcore_hw_configurtion_by_step(
|
|
struct mmdvfs_adaptor *self, int mmdvfs_step)
|
|
{
|
|
#ifdef VCORE_READY
|
|
struct mmdvfs_hw_configurtion *hw_config_ptr = NULL;
|
|
int vcore_step = 0;
|
|
|
|
if (!self) {
|
|
MMDVFSMSG("apply_vcore: self can't be NULL\n");
|
|
return -1;
|
|
}
|
|
|
|
if (self->enable_vcore == 0) {
|
|
MMDVFSMSG("vcore module is disable in MMDVFS\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Check unrequest step */
|
|
if (mmdvfs_step == MMDVFS_FINE_STEP_UNREQUEST) {
|
|
vcore_step = OPP_UNREQ;
|
|
} else {
|
|
/* Check if the step is legall */
|
|
if (mmdvfs_step < 0 || mmdvfs_step >= self->step_num
|
|
|| (self->step_profile_mappings + mmdvfs_step) == NULL)
|
|
return -1;
|
|
|
|
/* Get hw configurtion fot the step */
|
|
hw_config_ptr =
|
|
&(self->step_profile_mappings + mmdvfs_step)->hw_config;
|
|
|
|
/* Check if mmdvfs_hw_configurtion is found */
|
|
if (hw_config_ptr == NULL)
|
|
return -1;
|
|
|
|
/* Get vcore step */
|
|
vcore_step = hw_config_ptr->vcore_step;
|
|
}
|
|
|
|
if (vcorefs_request_dvfs_opp(self->vcore_kicker, vcore_step) != 0)
|
|
MMDVFSMSG("Set vcore step failed: %d\n", vcore_step);
|
|
/* Set vcore step */
|
|
MMDVFSDEBUG(3, "Set vcore step: %d\n", vcore_step);
|
|
|
|
return 0;
|
|
#else
|
|
MMDVFSMSG("VCore is not ready\n");
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
static void mmdvfs_configure_clk_hw(struct mmdvfs_adaptor *self,
|
|
struct mmdvfs_hw_configurtion *hw_config_ptr,
|
|
int clk_idx, int mmdvfs_step)
|
|
{
|
|
/* Get the clk step setting of each mm clks */
|
|
int clk_step = hw_config_ptr->clk_steps[clk_idx];
|
|
/* Get the specific clk descriptor */
|
|
struct mmdvfs_clk_hw_map *clk_hw_map =
|
|
&(self->mmdvfs_clk_hw_maps[clk_idx]);
|
|
int clk_mux_mask = get_mmdvfs_clk_mux_mask();
|
|
|
|
if (clk_step < 0 || clk_step >= clk_hw_map->total_step) {
|
|
MMDVFSDEBUG(3, "invalid clk step (%d) for %s\n", clk_step,
|
|
clk_hw_map->clk_mux.ccf_name);
|
|
return;
|
|
}
|
|
if (!((1 << clk_idx) & clk_mux_mask)) {
|
|
MMDVFSMSG("CLK %d(%s) swich is not enabled, mask(0x%x)\n",
|
|
clk_idx, clk_hw_map->clk_mux.ccf_name, clk_mux_mask);
|
|
return;
|
|
}
|
|
/* Apply the clk setting */
|
|
if (clk_hw_map->config_method == MMDVFS_CLK_CONFIG_BY_MUX) {
|
|
/* Get clk source */
|
|
int clk_id =
|
|
clk_hw_map->step_clk_source_id_map[clk_step];
|
|
int ccf_ret = -1;
|
|
|
|
if (clk_id < 0
|
|
|| clk_id >= self->mmdvfs_clk_sources_num) {
|
|
MMDVFSDEBUG(3, "invalid source: %d step:%d mux:%s\n",
|
|
clk_id, mmdvfs_step,
|
|
clk_hw_map->clk_mux.ccf_name);
|
|
return;
|
|
}
|
|
MMDVFSDEBUG(3, "Change %s source to %s, expect clk = %d\n",
|
|
clk_hw_map->clk_mux.ccf_name,
|
|
self->mmdvfs_clk_sources[clk_id].ccf_name,
|
|
self->mmdvfs_clk_sources[clk_id].clk_rate_mhz);
|
|
|
|
if (self->mmdvfs_clk_sources[clk_id].ccf_handle == NULL
|
|
|| clk_hw_map->clk_mux.ccf_handle == NULL) {
|
|
MMDVFSMSG("CCF handle can't be NULL during MMDVFS\n");
|
|
return;
|
|
}
|
|
|
|
ccf_ret = clk_prepare_enable(
|
|
(struct clk *)clk_hw_map->clk_mux.ccf_handle);
|
|
MMDVFSDEBUG(3, "clk_prepare_enable: andle = %lx\n",
|
|
((unsigned long)clk_hw_map->clk_mux.ccf_handle));
|
|
|
|
if (ccf_ret)
|
|
MMDVFSMSG("prepare clk NG: %s\n",
|
|
clk_hw_map->clk_mux.ccf_name);
|
|
|
|
ccf_ret = clk_set_parent(
|
|
(struct clk *)clk_hw_map->clk_mux.ccf_handle,
|
|
(struct clk *)self->mmdvfs_clk_sources[clk_id].ccf_handle);
|
|
MMDVFSDEBUG(3, "clk_set_parent (%lx,%lx), src id = %d\n",
|
|
((unsigned long)clk_hw_map->clk_mux.ccf_handle),
|
|
((unsigned long)self->mmdvfs_clk_sources[clk_id].ccf_handle),
|
|
clk_id);
|
|
|
|
if (ccf_ret)
|
|
MMDVFSMSG("Failed to set parent:%s,%s\n",
|
|
clk_hw_map->clk_mux.ccf_name,
|
|
self->mmdvfs_clk_sources[clk_id].ccf_name);
|
|
|
|
clk_disable_unprepare(
|
|
(struct clk *)clk_hw_map->clk_mux.ccf_handle);
|
|
MMDVFSDEBUG(3, "clk_disable_unprepare: handle = %lx\n",
|
|
((unsigned long)clk_hw_map->clk_mux.ccf_handle));
|
|
} else if (clk_hw_map->config_method == MMDVFS_CLK_CONFIG_PLL_RATE) {
|
|
int clk_rate = clk_hw_map->step_pll_freq_map[clk_step];
|
|
int pll_id = clk_hw_map->pll_id;
|
|
|
|
if (pll_id == -1) {
|
|
int ccf_ret = -1;
|
|
|
|
if (clk_rate < 0) {
|
|
MMDVFSMSG("invalid rate %d step:%d pll:%s\n",
|
|
clk_rate, mmdvfs_step,
|
|
clk_hw_map->clk_mux.ccf_name);
|
|
return;
|
|
}
|
|
if (clk_hw_map->clk_mux.ccf_handle == NULL) {
|
|
MMDVFSMSG("CCF handle can't be NULL\n");
|
|
return;
|
|
}
|
|
|
|
ccf_ret = clk_set_rate(
|
|
(struct clk *)clk_hw_map->clk_mux.ccf_handle,
|
|
clk_rate);
|
|
MMDVFSMSG("clk_set_rate: handle = (%lx), rate = %d\n",
|
|
((unsigned long)clk_hw_map->clk_mux.ccf_handle),
|
|
clk_rate);
|
|
|
|
if (ccf_ret)
|
|
MMDVFSMSG("Failed to set rate:%s->%d\n",
|
|
clk_hw_map->clk_mux.ccf_name, clk_rate);
|
|
} else {
|
|
#ifdef CONFIG_MTK_FREQ_HOPPING
|
|
int hopping_ret = -1;
|
|
|
|
hopping_ret = mt_dfs_general_pll(pll_id, clk_rate);
|
|
MMDVFSMSG("pll_hopping id=%d dds=0x%08x\n",
|
|
pll_id, clk_rate);
|
|
|
|
if (hopping_ret)
|
|
MMDVFSMSG("Failed to hopping%d->0x%08x\n",
|
|
pll_id, clk_rate);
|
|
#else
|
|
MMDVFSMSG("hopping NOT SUPPORT: id = (%d)\n", pll_id);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
static int mmdvfs_apply_clk_hw_configurtion_by_step(
|
|
struct mmdvfs_adaptor *self, int mmdvfs_step_request, bool to_high)
|
|
{
|
|
struct mmdvfs_hw_configurtion *hw_config_ptr = NULL;
|
|
int i = 0;
|
|
int mmdvfs_step = -1;
|
|
int clk_mux_mask = get_mmdvfs_clk_mux_mask();
|
|
|
|
if (self->enable_clk_mux == 0) {
|
|
MMDVFSMSG("clk_mux module is disable in MMDVFS\n");
|
|
return -1;
|
|
}
|
|
|
|
if (clk_mux_mask == 0) {
|
|
MMDVFSMSG("clk_mux_mask is 0 in MMDVFS\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Check unrequest step and reset the mmdvfs step to the lowest one */
|
|
if (mmdvfs_step_request == -1)
|
|
mmdvfs_step = self->step_num - 1;
|
|
else
|
|
mmdvfs_step = mmdvfs_step_request;
|
|
|
|
/* Check if the step is legall */
|
|
if (mmdvfs_step < 0 || mmdvfs_step >= self->step_num
|
|
|| (self->step_profile_mappings + mmdvfs_step) == NULL)
|
|
return -1;
|
|
|
|
/* Get hw configurtion fot the step */
|
|
hw_config_ptr =
|
|
&(self->step_profile_mappings + mmdvfs_step)->hw_config;
|
|
|
|
/* Check if mmdvfs_hw_configurtion is found */
|
|
if (hw_config_ptr == NULL)
|
|
return -1;
|
|
|
|
MMDVFSDEBUG(3, "CLK SWITCH: total = %d, step: (%d)\n",
|
|
hw_config_ptr->total_clks, mmdvfs_step);
|
|
|
|
/* Get each clk and setp it accord to config method */
|
|
if (to_high)
|
|
for (i = 0; i < hw_config_ptr->total_clks; i++)
|
|
mmdvfs_configure_clk_hw(
|
|
self, hw_config_ptr, i, mmdvfs_step);
|
|
else
|
|
for (i = hw_config_ptr->total_clks - 1; i >= 0; i--)
|
|
mmdvfs_configure_clk_hw(
|
|
self, hw_config_ptr, i, mmdvfs_step);
|
|
return 0;
|
|
|
|
}
|
|
static int mmdvfs_get_clients_clk_opp(struct mmdvfs_step_util *self,
|
|
struct mmdvfs_adaptor *adaptor, int clients_mask, int clk_id)
|
|
{
|
|
/* Get the opp determined only by the specified clients */
|
|
int opp_idx = 0;
|
|
int final_opp = -1;
|
|
int final_clk_opp = -1;
|
|
|
|
for (opp_idx = 0; opp_idx < self->total_opps; opp_idx++) {
|
|
int masked_concurrency =
|
|
self->mmdvfs_concurrency_of_opps[opp_idx] & clients_mask;
|
|
|
|
if (masked_concurrency != 0) {
|
|
final_opp = opp_idx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* if no request, return the lowerest step */
|
|
if (final_opp == -1)
|
|
final_opp = adaptor->step_num - 1;
|
|
|
|
/* Retrieve the CLK opp setting associated the MMDVFS opp */
|
|
if (clk_id >= 0 && clk_id < adaptor->mmdvfs_clk_hw_maps_num) {
|
|
if (final_opp >= 0 && final_opp <= adaptor->step_num) {
|
|
struct mmdvfs_step_profile *mmdvfs_step_to_profile =
|
|
adaptor->step_profile_mappings + final_opp;
|
|
final_clk_opp =
|
|
mmdvfs_step_to_profile->hw_config.clk_steps[clk_id];
|
|
}
|
|
}
|
|
return final_clk_opp;
|
|
}
|
|
|
|
static int mmdvfs_get_cam_sys_clk(
|
|
struct mmdvfs_adaptor *self, int mmdvfs_step)
|
|
{
|
|
struct mmdvfs_step_profile *profile =
|
|
&self->step_profile_mappings[mmdvfs_step];
|
|
return profile->hw_config.clk_steps[MMDVFS_CLK_MUX_TOP_CAM_SEL];
|
|
}
|
|
|
|
/* single_profile_dump_func: */
|
|
static void mmdvfs_single_profile_dump(struct mmdvfs_profile *profile)
|
|
{
|
|
if (profile == NULL) {
|
|
MMDVFSDEBUG(3, "profile_dump: NULL profile found\n");
|
|
return;
|
|
}
|
|
MMDVFSDEBUG(3, "%s, %d, (%d,%d,0x%x,%d), (%d,%d,%d)\n",
|
|
profile->profile_name, profile->smi_scenario_id,
|
|
profile->cam_limit.sensor_size, profile->cam_limit.feature_flag,
|
|
profile->cam_limit.fps, profile->cam_limit.preview_size,
|
|
profile->video_limit.width, profile->video_limit.height,
|
|
profile->video_limit.codec);
|
|
}
|
|
|
|
/* Profile Matching Util */
|
|
static void mmdvfs_profile_dump(struct mmdvfs_adaptor *self)
|
|
{
|
|
int i = 0;
|
|
|
|
struct mmdvfs_step_profile *profile_mapping =
|
|
self->step_profile_mappings;
|
|
|
|
if (profile_mapping == NULL) {
|
|
MMDVFSMSG("step_profile_mappings can't be NULL\n");
|
|
return;
|
|
}
|
|
|
|
MMDVFSDEBUG(3, "MMDVFS DUMP (%d):\n", profile_mapping->mmdvfs_step);
|
|
|
|
if (profile_mapping == NULL)
|
|
MMDVFSDEBUG(3, "step_profile_mappings can't be NULL\n");
|
|
MMDVFSDEBUG(3, "MMDVFS DUMP (%d):\n", profile_mapping->mmdvfs_step);
|
|
for (i = 0; i < profile_mapping->total_profiles; i++) {
|
|
struct mmdvfs_profile *profile =
|
|
profile_mapping->profiles + i;
|
|
mmdvfs_single_profile_dump(profile);
|
|
}
|
|
}
|
|
|
|
static void mmdvfs_single_hw_configuration_dump(struct mmdvfs_adaptor *self,
|
|
struct mmdvfs_hw_configurtion *hw_configuration)
|
|
{
|
|
int i = 0;
|
|
const int clk_soure_total = self->mmdvfs_clk_sources_num;
|
|
struct mmdvfs_clk_source_desc *clk_sources = self->mmdvfs_clk_sources;
|
|
struct mmdvfs_clk_hw_map *clk_hw_map = self->mmdvfs_clk_hw_maps;
|
|
|
|
if (clk_hw_map == NULL) {
|
|
MMDVFSMSG(
|
|
"mmdvfs_clk_hw_maps can't be NULL\n");
|
|
return;
|
|
}
|
|
|
|
if (hw_configuration == NULL) {
|
|
MMDVFSMSG(
|
|
"hw_configuration can't be NULL\n");
|
|
return;
|
|
}
|
|
|
|
MMDVFSDEBUG(3, "Vcore tep: %d\n", hw_configuration->vcore_step);
|
|
|
|
for (i = 0; i < hw_configuration->total_clks; i++) {
|
|
char *ccf_clk_source_name = "NONE";
|
|
char *ccf_clk_mux_name = "NONE";
|
|
u32 clk_rate_mhz = 0;
|
|
u32 clk_step = 0;
|
|
u32 clk_id = 0;
|
|
|
|
struct mmdvfs_clk_hw_map *map_item = clk_hw_map + i;
|
|
|
|
if (map_item == NULL) {
|
|
MMDVFSMSG(
|
|
"mmdvfs_map_item can't be NULL, i:%d\n", i);
|
|
return;
|
|
}
|
|
clk_step = hw_configuration->clk_steps[i];
|
|
|
|
if (map_item != NULL && map_item->clk_mux.ccf_name != NULL) {
|
|
clk_id = map_item->step_clk_source_id_map[clk_step];
|
|
ccf_clk_mux_name = map_item->clk_mux.ccf_name;
|
|
}
|
|
|
|
if (map_item->config_method == MMDVFS_CLK_CONFIG_BY_MUX
|
|
&& clk_id < clk_soure_total) {
|
|
ccf_clk_source_name = clk_sources[clk_id].ccf_name;
|
|
clk_rate_mhz = clk_sources[clk_id].clk_rate_mhz;
|
|
}
|
|
|
|
MMDVFSDEBUG(3, "\t%s, %s, %dMhz\n",
|
|
map_item->clk_mux.ccf_name,
|
|
ccf_clk_source_name, clk_rate_mhz);
|
|
}
|
|
}
|
|
|
|
static int mmdvfs_determine_step(struct mmdvfs_adaptor *self,
|
|
int smi_scenario,
|
|
struct mmdvfs_cam_property *cam_setting,
|
|
struct mmdvfs_video_property *codec_setting)
|
|
{
|
|
/* Find the matching scenario from OPP 0 to Max OPP */
|
|
int opp_index = 0;
|
|
int profile_index = 0;
|
|
struct mmdvfs_step_profile *profile_mappings =
|
|
self->step_profile_mappings;
|
|
const int opp_max_num = self->step_num;
|
|
|
|
if (profile_mappings == NULL) {
|
|
MMDVFSMSG(
|
|
"step_profile_mappings can't be NULL\n");
|
|
return MMDVFS_FINE_STEP_UNREQUEST;
|
|
}
|
|
|
|
for (opp_index = 0; opp_index < opp_max_num; opp_index++) {
|
|
struct mmdvfs_step_profile *mapping_ptr =
|
|
profile_mappings + opp_index;
|
|
|
|
for (profile_index = 0;
|
|
profile_index < mapping_ptr->total_profiles;
|
|
profile_index++) {
|
|
/* Check if the scenario matches any profile */
|
|
struct mmdvfs_profile *profile_prt =
|
|
mapping_ptr->profiles + profile_index;
|
|
if (smi_scenario == profile_prt->smi_scenario_id) {
|
|
/* Check cam setting */
|
|
if (!is_camera_profile_matched(
|
|
cam_setting, &profile_prt->cam_limit))
|
|
continue;
|
|
if (!is_video_profile_matched(
|
|
codec_setting,
|
|
&profile_prt->video_limit))
|
|
continue;
|
|
/* Complete match, return the opp index */
|
|
mmdvfs_single_profile_dump(profile_prt);
|
|
return opp_index;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If there is no profile matched, return -1 (no dvfs request)*/
|
|
return MMDVFS_FINE_STEP_UNREQUEST;
|
|
}
|
|
|
|
/* Show each setting of opp */
|
|
static void mmdvfs_hw_configuration_dump(struct mmdvfs_adaptor *self)
|
|
{
|
|
int i = 0;
|
|
struct mmdvfs_step_profile *mapping =
|
|
self->step_profile_mappings;
|
|
|
|
if (mapping == NULL) {
|
|
MMDVFSMSG(
|
|
"mmdvfs_clk_hw_maps can't be NULL");
|
|
return;
|
|
}
|
|
|
|
MMDVFSDEBUG(3, "All OPP configurtion dump\n");
|
|
for (i = 0; i < self->step_num; i++) {
|
|
struct mmdvfs_step_profile *mapping_item = mapping + i;
|
|
|
|
MMDVFSDEBUG(3, "MMDVFS OPP %d:\n", i);
|
|
if (mapping_item != NULL)
|
|
self->single_hw_configuration_dump_func(
|
|
self, &mapping_item->hw_config);
|
|
}
|
|
}
|
|
|
|
static int is_camera_profile_matched(struct mmdvfs_cam_property *cam_setting,
|
|
struct mmdvfs_cam_property *property)
|
|
{
|
|
int is_match = 1;
|
|
|
|
/* Null pointer check: */
|
|
/* If the scenario doesn't has cam_setting, then there */
|
|
/* is no need to check the cam property */
|
|
if (!cam_setting || !property) {
|
|
is_match = 1;
|
|
} else {
|
|
int feature_flag_mask_default = cam_setting->feature_flag
|
|
& (~(MMDVFS_CAMERA_MODE_FLAG_DEFAULT));
|
|
|
|
/* Check the minimum sensor resolution */
|
|
if (cam_setting->sensor_size < property->sensor_size)
|
|
is_match = 0;
|
|
|
|
/* Check the minimum sensor resolution */
|
|
if (cam_setting->fps < property->fps)
|
|
is_match = 0;
|
|
|
|
/* Check the minimum sensor resolution */
|
|
if (cam_setting->preview_size < property->preview_size)
|
|
is_match = 0;
|
|
|
|
/* Check the if the feature match */
|
|
/* Not match if there is no featue matching the profile */
|
|
/* 1 ==> don't change */
|
|
/* 0 ==> set is_match to 0 */
|
|
if (property->feature_flag != 0
|
|
&& !(feature_flag_mask_default & property->feature_flag))
|
|
is_match = 0;
|
|
}
|
|
|
|
return is_match;
|
|
}
|
|
|
|
static int is_video_profile_matched(
|
|
struct mmdvfs_video_property *video_setting,
|
|
struct mmdvfs_video_property *profile_property)
|
|
{
|
|
int is_match = 1;
|
|
/* Null pointer check: */
|
|
/* If the scenario doesn't has video_setting, then there */
|
|
/* is no need to check the video property */
|
|
|
|
if (!video_setting || !profile_property)
|
|
is_match = 1;
|
|
else {
|
|
if (!(video_setting->height * video_setting->width
|
|
>= profile_property->height * profile_property->width))
|
|
/* Check the minimum sensor resolution */
|
|
is_match = 0;
|
|
}
|
|
|
|
return is_match;
|
|
}
|
|
|
|
static void mmdvfs_step_util_init(struct mmdvfs_step_util *self)
|
|
{
|
|
int idx = 0;
|
|
|
|
for (idx = 0; idx < self->total_scenario; idx++)
|
|
self->mmdvfs_scenario_mmdvfs_opp[idx] =
|
|
MMDVFS_FINE_STEP_UNREQUEST;
|
|
|
|
for (idx = 0; idx < self->total_opps; idx++)
|
|
self->mmdvfs_concurrency_of_opps[idx] = 0;
|
|
}
|
|
|
|
static inline void mmdvfs_adjust_scenario(s32 *mmdvfs_scen_opp_map,
|
|
u32 changed_scenario, u32 new_scenario)
|
|
{
|
|
if (mmdvfs_scen_opp_map[changed_scenario] >= 0) {
|
|
mmdvfs_scen_opp_map[changed_scenario] = -1;
|
|
MMDVFSDEBUG(5, "[adjust] new (%d) scenario (%d) to -1!\n",
|
|
new_scenario, changed_scenario);
|
|
}
|
|
}
|
|
|
|
#define MMDVFS_CAMERA_SCEN_MASK ((1<<SMI_BWC_SCEN_VR) | \
|
|
(1<<SMI_BWC_SCEN_VR_SLOW) | \
|
|
(1<<SMI_BWC_SCEN_ICFP) | \
|
|
(1<<SMI_BWC_SCEN_VSS) | \
|
|
(1<<SMI_BWC_SCEN_CAM_PV) | \
|
|
(1<<SMI_BWC_SCEN_CAM_CP))
|
|
|
|
/* updat the step members only (HW independent part) */
|
|
/* return the final step */
|
|
static int mmdvfs_step_util_set_step(
|
|
struct mmdvfs_step_util *self, s32 step, u32 scenario)
|
|
{
|
|
int i = 0;
|
|
int opp_idx = 0;
|
|
int final_opp = -1;
|
|
bool has_camera_scenario = false;
|
|
|
|
/* check step range here */
|
|
if (step < -1 || step >= self->total_opps)
|
|
return MMDVFS_FINE_STEP_UNREQUEST;
|
|
|
|
/* check invalid scenario */
|
|
if (scenario >= self->total_scenario)
|
|
return MMDVFS_FINE_STEP_UNREQUEST;
|
|
|
|
self->mmdvfs_scenario_mmdvfs_opp[scenario] = step;
|
|
|
|
if (self->wfd_vp_mix_step >= 0) {
|
|
/* special configuration for mixed step */
|
|
if (self->mmdvfs_scenario_mmdvfs_opp[SMI_BWC_SCEN_WFD] >= 0 &&
|
|
self->mmdvfs_scenario_mmdvfs_opp[SMI_BWC_SCEN_VP] >= 0) {
|
|
self->mmdvfs_scenario_mmdvfs_opp[MMDVFS_SCEN_VP_WFD] =
|
|
self->wfd_vp_mix_step;
|
|
} else {
|
|
self->mmdvfs_scenario_mmdvfs_opp[MMDVFS_SCEN_VP_WFD] =
|
|
MMDVFS_FINE_STEP_UNREQUEST;
|
|
}
|
|
}
|
|
|
|
/* Reset the concurrency fileds before the calculation */
|
|
for (opp_idx = 0; opp_idx < self->total_opps; opp_idx++)
|
|
self->mmdvfs_concurrency_of_opps[opp_idx] = 0;
|
|
|
|
for (i = 0; i < self->total_scenario; i++) {
|
|
for (opp_idx = 0; opp_idx < self->total_opps; opp_idx++) {
|
|
if (self->mmdvfs_scenario_mmdvfs_opp[i] == opp_idx)
|
|
self->mmdvfs_concurrency_of_opps[opp_idx] |=
|
|
1 << i;
|
|
}
|
|
}
|
|
|
|
for (opp_idx = 0; opp_idx < self->total_opps; opp_idx++) {
|
|
if (self->mmdvfs_concurrency_of_opps[opp_idx] != 0) {
|
|
final_opp = opp_idx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (self->mmdvfs_scenario_mmdvfs_opp[MMDVFS_MGR] !=
|
|
MMDVFS_FINE_STEP_UNREQUEST) {
|
|
final_opp = self->mmdvfs_scenario_mmdvfs_opp[MMDVFS_MGR];
|
|
MMDVFSMSG("[force] set step (%d)\n", final_opp);
|
|
}
|
|
|
|
for (opp_idx = 0; opp_idx < self->total_opps; opp_idx++) {
|
|
if ((self->mmdvfs_concurrency_of_opps[opp_idx]
|
|
& MMDVFS_CAMERA_SCEN_MASK) != 0) {
|
|
has_camera_scenario = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (camera_bw_config && in_camera_scenario != has_camera_scenario) {
|
|
#if defined(SPECIAL_BW_CONFIG_MM)
|
|
u32 bw_config = normal_bw_config;
|
|
u32 old_bw_config, new_bw_config;
|
|
|
|
if (has_camera_scenario)
|
|
bw_config = camera_bw_config;
|
|
MMDVFSDEBUG(3, "[DRAM setting] in camera? %d\n",
|
|
has_camera_scenario);
|
|
old_bw_config = BM_GetBW();
|
|
BM_SetBW(bw_config);
|
|
new_bw_config = BM_GetBW();
|
|
MMDVFSDEBUG(3, "[DRAM] old:%#x, want:%#x, new:%#x\n",
|
|
old_bw_config, bw_config, new_bw_config);
|
|
#else
|
|
MMDVFSDEBUG(3, "[DRAM setting] not support\n");
|
|
#endif
|
|
}
|
|
#ifdef DYNAMIC_DISP_HRT
|
|
mmdvfs_change_disp_hrt(has_camera_scenario);
|
|
#endif
|
|
in_camera_scenario = has_camera_scenario;
|
|
return final_opp;
|
|
}
|
|
|
|
void mmdvfs_set_disp_hrt_cb(disp_hrt_change_cb change_cb)
|
|
{
|
|
g_disp_hrt_change_cb = change_cb;
|
|
}
|
|
|
|
void mmdvfs_set_md_on(bool to_on)
|
|
{
|
|
g_is_md_on = to_on;
|
|
#ifdef DYNAMIC_DISP_HRT
|
|
mmdvfs_change_disp_hrt(in_camera_scenario);
|
|
#endif
|
|
}
|
|
|
|
#ifdef DYNAMIC_DISP_HRT
|
|
static void mmdvfs_change_disp_hrt(bool has_camera_scenario)
|
|
{
|
|
if (cam_sensor_threshold && g_disp_hrt_change_cb) {
|
|
struct mmdvfs_cam_property cam;
|
|
u32 cam_sensor_setting;
|
|
|
|
mmdvfs_internal_get_cam_setting(&cam);
|
|
cam_sensor_setting = cam.sensor_size * cam.fps;
|
|
if (has_camera_scenario &&
|
|
cam_sensor_setting >= cam_sensor_threshold &&
|
|
g_is_md_on) {
|
|
MMDVFSMSG("decrease HRT with %d\n",
|
|
disp_hrt_decrease_level1);
|
|
g_disp_hrt_change_cb(disp_hrt_decrease_level1);
|
|
} else {
|
|
MMDVFSMSG("decrease HRT with 0\n");
|
|
g_disp_hrt_change_cb(disp_hrt_decrease_default);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
static int mmdvfs_get_opp_from_legacy_step(
|
|
struct mmdvfs_step_util *self, int legacy_step)
|
|
{
|
|
if (self->legacy_step_to_oop == NULL || legacy_step < 0 ||
|
|
legacy_step >= self->legacy_step_to_oop_num)
|
|
return -1;
|
|
else
|
|
return self->legacy_step_to_oop_num + legacy_step;
|
|
}
|
|
|
|
static int mmdvfs_get_legacy_mmclk_step_from_mmclk_opp(
|
|
struct mmdvfs_step_util *self, int mmclk_step)
|
|
{
|
|
int step_ret = -1;
|
|
|
|
if (self->mmclk_oop_to_legacy_step == NULL || mmclk_step < 0
|
|
|| mmclk_step >= self->mmclk_oop_to_legacy_step_num) {
|
|
step_ret = -1;
|
|
} else {
|
|
int *step_ptr = self->mmclk_oop_to_legacy_step + mmclk_step;
|
|
|
|
step_ret = -1;
|
|
|
|
if (step_ptr != NULL)
|
|
step_ret = *step_ptr;
|
|
}
|
|
return step_ret;
|
|
}
|
|
|
|
static const struct mmdvfs_vpu_steps_setting *get_vpu_setting_impl(
|
|
struct mmdvfs_vpu_dvfs_configurator *self, int vpu_opp)
|
|
{
|
|
if (vpu_opp < 0 || vpu_opp > self->nr_vpu_steps)
|
|
return NULL;
|
|
else
|
|
return (const struct mmdvfs_vpu_steps_setting *)
|
|
&(self->mmdvfs_vpu_steps_settings[vpu_opp]);
|
|
}
|
|
|
|
/* ISP DVFS Adaptor Impementation */
|
|
static s32 get_step_by_threshold(
|
|
struct mmdvfs_thresholds_dvfs_handler *self, u32 class_id, u32 value)
|
|
{
|
|
struct mmdvfs_threshold_setting *setting = NULL;
|
|
int step_found = MMDVFS_FINE_STEP_UNREQUEST;
|
|
int i = 0;
|
|
|
|
if (class_id > self->mmdvfs_threshold_setting_num)
|
|
return MMDVFS_FINE_STEP_UNREQUEST;
|
|
|
|
if (value == 0)
|
|
return MMDVFS_FINE_STEP_UNREQUEST;
|
|
|
|
setting = &self->threshold_settings[class_id];
|
|
|
|
for (i = 0;
|
|
i < setting->thresholds_num;
|
|
i++) {
|
|
int *threshold = setting->thresholds + i;
|
|
int *opp = setting->opps + i;
|
|
|
|
if ((threshold) && (opp) && (value >= *threshold)) {
|
|
MMDVFSDEBUG(5, "value=%d,threshold=%d\n",
|
|
value, *threshold);
|
|
step_found = *opp;
|
|
break;
|
|
}
|
|
}
|
|
if (step_found == MMDVFS_FINE_STEP_UNREQUEST)
|
|
step_found = *(setting->opps +
|
|
setting->thresholds_num-1);
|
|
|
|
return step_found;
|
|
}
|
|
|
|
struct mmdvfs_vpu_dvfs_configurator *g_mmdvfs_vpu_adaptor;
|
|
struct mmdvfs_adaptor *g_mmdvfs_adaptor;
|
|
struct mmdvfs_adaptor *g_mmdvfs_non_force_adaptor;
|
|
struct mmdvfs_step_util *g_mmdvfs_step_util;
|
|
struct mmdvfs_step_util *g_non_force_step_util;
|
|
struct mmdvfs_thresholds_dvfs_handler *g_dvfs_handler;
|
|
|
|
#ifdef MMDVFS_QOS_SUPPORT
|
|
static int mask_concur[MMDVFS_OPP_NUM_LIMITATION];
|
|
static void update_qos_scenario(void);
|
|
#endif
|
|
|
|
void mmdvfs_config_util_init(void)
|
|
{
|
|
int mmdvfs_profile_id = mmdvfs_get_mmdvfs_profile();
|
|
|
|
switch (mmdvfs_profile_id) {
|
|
case MMDVFS_PROFILE_ZIO:
|
|
#if defined(SMI_ZIO)
|
|
g_mmdvfs_adaptor = &mmdvfs_adaptor_obj_mt6739;
|
|
g_mmdvfs_step_util = &mmdvfs_step_util_obj_mt6739;
|
|
g_dvfs_handler = &dvfs_handler_mt6739;
|
|
#endif
|
|
break;
|
|
case MMDVFS_PROFILE_MER:
|
|
#if defined(SMI_MER)
|
|
g_mmdvfs_adaptor = &mmdvfs_adaptor_obj_mt6761;
|
|
g_mmdvfs_step_util = &mmdvfs_step_util_obj_mt6761;
|
|
g_dvfs_handler = &mmdvfs_thresholds_dvfs_handler_obj;
|
|
#endif
|
|
break;
|
|
case MMDVFS_PROFILE_SYL:
|
|
#if defined(SMI_SYL)
|
|
g_dvfs_handler = &dvfs_handler_mt6771;
|
|
#if defined(USE_DDR_TYPE)
|
|
if (get_dram_type() == TYPE_LPDDR3) {
|
|
g_mmdvfs_adaptor = &mmdvfs_adaptor_obj_mt6771_lp3;
|
|
cam_sensor_threshold = 480000000;
|
|
disp_hrt_decrease_level1 = 150;
|
|
disp_hrt_decrease_default = 0;
|
|
MMDVFSMSG("g_mmdvfs_step_util init with lp3\n");
|
|
} else if (dram_steps_freq(0) == 3600) {
|
|
g_mmdvfs_adaptor = &mmdvfs_adaptor_obj_mt6771_3600;
|
|
g_dvfs_handler = &dvfs_handler_mt6771_3600;
|
|
legacy_to_qos_step[MMDVFS_FINE_STEP_OPP1].qos_step = 0;
|
|
cam_sensor_threshold = 1;
|
|
disp_hrt_decrease_level1 = 150;
|
|
disp_hrt_decrease_default = 0;
|
|
MMDVFSMSG(
|
|
"g_mmdvfs_step_util init with lp4 2-ch (3600)\n");
|
|
} else {
|
|
g_mmdvfs_adaptor = &mmdvfs_adaptor_obj_mt6771;
|
|
cam_sensor_threshold = 1;
|
|
disp_hrt_decrease_level1 = 150;
|
|
disp_hrt_decrease_default = 0;
|
|
MMDVFSMSG(
|
|
"g_mmdvfs_step_util init with lp4 2-ch\n");
|
|
}
|
|
#else
|
|
g_mmdvfs_adaptor = &mmdvfs_adaptor_obj_mt6771;
|
|
MMDVFSMSG("g_mmdvfs_step_util init with lp4 2-ch\n");
|
|
#endif
|
|
g_mmdvfs_step_util = &mmdvfs_step_util_obj_mt6771;
|
|
#endif
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
g_mmdvfs_adaptor->profile_dump_func(g_mmdvfs_adaptor);
|
|
MMDVFSMSG("g_mmdvfs_step_util init\n");
|
|
|
|
if (g_mmdvfs_step_util)
|
|
g_mmdvfs_step_util->init(g_mmdvfs_step_util);
|
|
|
|
if (g_non_force_step_util)
|
|
g_non_force_step_util->init(g_non_force_step_util);
|
|
|
|
#ifdef MMDVFS_QOS_SUPPORT
|
|
update_qos_scenario();
|
|
#endif
|
|
}
|
|
|
|
void mmdvfs_pm_qos_update_request(struct mmdvfs_pm_qos_request *req,
|
|
u32 mmdvfs_pm_qos_class, u32 new_value)
|
|
{
|
|
int step = MMDVFS_FINE_STEP_UNREQUEST;
|
|
int mmdvfs_vote_id = -1;
|
|
struct mmdvfs_threshold_setting *client_threshold_setting = NULL;
|
|
|
|
if (!req)
|
|
MMDVFSDEBUG(5, "single mode request\n");
|
|
|
|
if (!g_dvfs_handler) {
|
|
MMDVFSMSG("g_dvfs_handler is NULL\n");
|
|
return;
|
|
}
|
|
|
|
client_threshold_setting =
|
|
g_dvfs_handler->threshold_settings
|
|
+ mmdvfs_pm_qos_class;
|
|
|
|
if (!client_threshold_setting) {
|
|
MMDVFSMSG("client_threshold_setting is NULL\n");
|
|
return;
|
|
}
|
|
|
|
mmdvfs_vote_id = client_threshold_setting->mmdvfs_client_id;
|
|
step = g_dvfs_handler->get_step(
|
|
g_dvfs_handler,
|
|
mmdvfs_pm_qos_class, new_value);
|
|
|
|
mmdvfs_set_fine_step(mmdvfs_vote_id, step);
|
|
}
|
|
|
|
void mmdvfs_pm_qos_remove_request(struct mmdvfs_pm_qos_request *req)
|
|
{
|
|
if (!req) {
|
|
MMDVFSDEBUG(5, "can't be NULL for PMQoS\n");
|
|
return;
|
|
}
|
|
mmdvfs_pm_qos_update_request(req, req->pm_qos_class, 0);
|
|
}
|
|
|
|
void mmdvfs_pm_qos_add_request(
|
|
struct mmdvfs_pm_qos_request *req, u32 mmdvfs_pm_qos_class, u32 value)
|
|
{
|
|
if (!req) {
|
|
MMDVFSDEBUG(5, "mmdvfs_pm_qos_request can't be NULL(add)\n");
|
|
return;
|
|
}
|
|
|
|
mmdvfs_pm_qos_update_request(req, mmdvfs_pm_qos_class, value);
|
|
}
|
|
|
|
static inline struct mmdvfs_threshold_setting *get_threshold_setting(
|
|
u32 class_id)
|
|
{
|
|
if (class_id >
|
|
g_dvfs_handler->mmdvfs_threshold_setting_num)
|
|
return NULL;
|
|
|
|
return &g_dvfs_handler->threshold_settings[class_id];
|
|
}
|
|
|
|
u32 mmdvfs_qos_get_thres_count(
|
|
struct mmdvfs_pm_qos_request *req, u32 mmdvfs_pm_qos_class)
|
|
{
|
|
struct mmdvfs_threshold_setting *setting_ptr =
|
|
get_threshold_setting(mmdvfs_pm_qos_class);
|
|
|
|
if (setting_ptr)
|
|
return setting_ptr->thresholds_num;
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32 mmdvfs_qos_get_thres_value(struct mmdvfs_pm_qos_request *req,
|
|
u32 mmdvfs_pm_qos_class, u32 thres_idx)
|
|
{
|
|
struct mmdvfs_threshold_setting *setting_ptr =
|
|
get_threshold_setting(mmdvfs_pm_qos_class);
|
|
|
|
if (setting_ptr) {
|
|
if (thres_idx > setting_ptr->thresholds_num)
|
|
return 0;
|
|
return setting_ptr->thresholds[thres_idx];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u32 mmdvfs_qos_get_cur_thres(struct mmdvfs_pm_qos_request *req,
|
|
u32 mmdvfs_pm_qos_class)
|
|
{
|
|
struct mmdvfs_clk_hw_map *hw_map_ptr = NULL;
|
|
u32 clk_step = 0, config_method = 0, clk_rate_mhz = 0;
|
|
s32 current_step = mmdvfs_get_current_fine_step();
|
|
|
|
if (current_step < 0 ||
|
|
mmdvfs_pm_qos_class != MMDVFS_PM_QOS_SUB_SYS_CAMERA)
|
|
return 0;
|
|
|
|
clk_step = g_mmdvfs_adaptor->get_cam_sys_clk(
|
|
g_mmdvfs_adaptor, current_step);
|
|
hw_map_ptr =
|
|
&g_mmdvfs_adaptor->mmdvfs_clk_hw_maps[MMDVFS_CLK_MUX_TOP_CAM_SEL];
|
|
config_method = hw_map_ptr->config_method;
|
|
if (config_method == MMDVFS_CLK_CONFIG_PLL_RATE)
|
|
clk_rate_mhz =
|
|
hw_map_ptr->step_pll_freq_map[clk_step] / 1000000;
|
|
else if (config_method == MMDVFS_CLK_CONFIG_BY_MUX) {
|
|
u32 clk_id = hw_map_ptr->step_clk_source_id_map[clk_step];
|
|
|
|
clk_rate_mhz =
|
|
g_mmdvfs_adaptor->mmdvfs_clk_sources[clk_id].clk_rate_mhz;
|
|
}
|
|
return clk_rate_mhz;
|
|
}
|
|
|
|
#ifdef MMDVFS_QOS_SUPPORT
|
|
static int get_qos_step(s32 opp)
|
|
{
|
|
if (opp < 0 || opp >= ARRAY_SIZE(legacy_to_qos_step))
|
|
return MMDVFS_FINE_STEP_UNREQUEST;
|
|
|
|
return legacy_to_qos_step[opp].qos_step;
|
|
}
|
|
|
|
void mmdvfs_qos_update(struct mmdvfs_step_util *step_util, int new_step)
|
|
{
|
|
int i;
|
|
int *concur = step_util->mmdvfs_concurrency_of_opps;
|
|
|
|
i = ARRAY_SIZE(qos_apply_profiles) - 1;
|
|
if (qos_apply_profiles[i].smi_scenario_id == QOS_ALL_SCENARIO) {
|
|
MMDVFSDEBUG(5, "force update qos step: %d\n", new_step);
|
|
mmdvfs_qos_force_step(get_qos_step(new_step));
|
|
return;
|
|
}
|
|
for (i = 0; i < MMDVFS_OPP_NUM_LIMITATION; i++) {
|
|
if (mask_concur[i] & concur[i]) {
|
|
/* scenario matched, check */
|
|
MMDVFSDEBUG(5, "qos match,S(%d,%d,0x%0x,0x%0x)\n",
|
|
new_step, i, mask_concur[i],
|
|
step_util->mmdvfs_concurrency_of_opps[i]);
|
|
mmdvfs_qos_force_step(get_qos_step(i));
|
|
return;
|
|
}
|
|
}
|
|
/* No scenario is matched, cancel qos step anyway */
|
|
mmdvfs_qos_force_step(get_qos_step(MMDVFS_FINE_STEP_UNREQUEST));
|
|
}
|
|
|
|
static void update_qos_scenario(void)
|
|
{
|
|
int i;
|
|
|
|
memset(&mask_concur, 0, sizeof(mask_concur));
|
|
for (i = 0; i < ARRAY_SIZE(qos_apply_profiles); i++) {
|
|
int opp = qos_apply_profiles[i].mask_opp;
|
|
|
|
if (opp >= MMDVFS_OPP_NUM_LIMITATION || opp < 0)
|
|
continue;
|
|
mask_concur[opp] |=
|
|
(1 << qos_apply_profiles[i].smi_scenario_id);
|
|
}
|
|
}
|
|
|
|
int set_qos_scenario(const char *val, const struct kernel_param *kp)
|
|
{
|
|
int i, result, scenario, opp;
|
|
|
|
result = sscanf(val, "%d %d", &scenario, &opp);
|
|
if (result != 2) {
|
|
MMDVFSMSG("invalid input: %s, result(%d)\n", val, result);
|
|
return -EINVAL;
|
|
}
|
|
if (scenario != QOS_ALL_SCENARIO &&
|
|
(scenario < 0 || scenario > MMDVFS_SCEN_COUNT)) {
|
|
MMDVFSMSG("scenario %d not in %d~%d\n",
|
|
scenario, 0, MMDVFS_SCEN_COUNT);
|
|
return -EINVAL;
|
|
}
|
|
if (opp < MMDVFS_FINE_STEP_UNREQUEST || opp > MMDVFS_FINE_STEP_OPP5) {
|
|
MMDVFSMSG("opp %d not in %d~%d\n", opp,
|
|
MMDVFS_FINE_STEP_UNREQUEST, MMDVFS_FINE_STEP_OPP5);
|
|
return -EINVAL;
|
|
}
|
|
/* only update latest debug entry */
|
|
i = ARRAY_SIZE(qos_apply_profiles) - 1;
|
|
qos_apply_profiles[i].smi_scenario_id = scenario;
|
|
qos_apply_profiles[i].mask_opp = opp;
|
|
|
|
update_qos_scenario();
|
|
return 0;
|
|
}
|
|
|
|
int get_qos_scenario(char *buf, const struct kernel_param *kp)
|
|
{
|
|
int i, off = 0;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(qos_apply_profiles); i++) {
|
|
off += snprintf(buf + off, PAGE_SIZE - off,
|
|
"[%d]%s: %d / %d\n", i,
|
|
qos_apply_profiles[i].profile_name,
|
|
qos_apply_profiles[i].smi_scenario_id,
|
|
qos_apply_profiles[i].mask_opp);
|
|
}
|
|
buf[off] = '\0';
|
|
return off;
|
|
}
|
|
#endif
|