1269 lines
34 KiB
C
1269 lines
34 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/*
|
||
|
* Copyright (c) 2020 MediaTek Inc.
|
||
|
*/
|
||
|
|
||
|
#include <linux/uaccess.h>
|
||
|
#include <linux/timer.h>
|
||
|
#include <linux/jiffies.h>
|
||
|
#include <linux/workqueue.h>
|
||
|
#include <linux/clk.h>
|
||
|
#include <linux/pm_qos.h>
|
||
|
|
||
|
#include <mt-plat/mtk_chip.h>
|
||
|
|
||
|
#include <aee.h>
|
||
|
|
||
|
#include "mtk_gpu_utility.h"
|
||
|
#include "mtk_smi.h"
|
||
|
/*#include "mtk_vcorefs_manager.h"*/
|
||
|
#ifdef PLL_HOPPING_READY
|
||
|
#include "mtk_freqhopping_drv.h"
|
||
|
#endif
|
||
|
#include "mmdvfs_mgr.h"
|
||
|
#include "mmdvfs_config_util.h"
|
||
|
#include "mmdvfs_internal.h"
|
||
|
#include "mmdvfs_pmqos.h"
|
||
|
|
||
|
#undef pr_fmt
|
||
|
#define pr_fmt(fmt) "[" MMDVFS_LOG_TAG "]" fmt
|
||
|
|
||
|
#define MMDVFS_ENABLE 1
|
||
|
|
||
|
#if defined(MMDVFS_ENABLE) && defined(MMDVFS_USE_FLIPER)
|
||
|
#include <mach/fliper.h>
|
||
|
#endif
|
||
|
|
||
|
/* MMDVFS legacy APIs */
|
||
|
|
||
|
#define MMDVFS_CLK_SWITCH_CB_MAX 16
|
||
|
#define MMDVFS_CLK_SWITCH_CLIENT_MSG_MAX 20
|
||
|
|
||
|
/* Legacy MMCLK change notifier. Keep the API now but */
|
||
|
/* it will be phased out (ISP may using the functionality)*/
|
||
|
static void notify_camsys_clk_change(
|
||
|
int ori_mmsys_clk_mode, int update_mmsys_clk_mode);
|
||
|
static int mmsys_clk_change_notify_checked(
|
||
|
clk_switch_cb func, int ori_mmsys_clk_mode,
|
||
|
int update_mmsys_clk_mode, char *msg);
|
||
|
|
||
|
/* Keep to adapt the new MMDVFS profile management method */
|
||
|
/* to the legacy IOCTL command quickly */
|
||
|
static int mmdvfs_query(enum MTK_SMI_BWC_SCEN scenario,
|
||
|
struct MTK_MMDVFS_CMD *cmd);
|
||
|
|
||
|
/* For legacy mmclk change notification and will be phase out */
|
||
|
static clk_switch_cb quick_mmclk_cbs[MMDVFS_CLK_SWITCH_CB_MAX];
|
||
|
|
||
|
/* Record the current CLK (debug only) */
|
||
|
static int current_mmsys_clk = MMSYS_CLK_MEDIUM;
|
||
|
|
||
|
/* Record the current step */
|
||
|
static s32 g_mmdvfs_current_step;
|
||
|
|
||
|
static int g_mmdvfs_current_vpu_step;
|
||
|
|
||
|
/* Record the enabled client */
|
||
|
static unsigned int g_mmdvfs_concurrency;
|
||
|
static struct MTK_SMI_BWC_MM_INFO *g_mmdvfs_info;
|
||
|
static struct MTK_MMDVFS_CMD g_mmdvfs_cmd;
|
||
|
|
||
|
#define SF_HWC_PIXEL_MAX_NORMAL (1920 * 1080 * 7)
|
||
|
struct MTK_SMI_BWC_MM_INFO g_bwc_mm_info = {
|
||
|
0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}, 0, 0, 0,
|
||
|
SF_HWC_PIXEL_MAX_NORMAL
|
||
|
};
|
||
|
|
||
|
struct mmdvfs_context_struct {
|
||
|
spinlock_t scen_lock;
|
||
|
int is_mmdvfs_start;
|
||
|
int is_vp_high_fps_enable;
|
||
|
int is_mhl_enable;
|
||
|
int is_wfd_enable;
|
||
|
int is_mjc_enable;
|
||
|
int is_boost_disable;
|
||
|
};
|
||
|
|
||
|
/* mmdvfs_query() return value, remember to sync with user space */
|
||
|
enum mmdvfs_step_enum {
|
||
|
MMDVFS_STEP_LOW = 0, MMDVFS_STEP_HIGH,
|
||
|
MMDVFS_STEP_LOW2LOW, /* LOW */
|
||
|
MMDVFS_STEP_HIGH2LOW, /* LOW */
|
||
|
MMDVFS_STEP_LOW2HIGH, /* HIGH */
|
||
|
MMDVFS_STEP_HIGH2HIGH,
|
||
|
/* HIGH */
|
||
|
};
|
||
|
|
||
|
static unsigned int disable_mmdvfs;
|
||
|
static unsigned int disable_freq_hopping = 1;
|
||
|
static unsigned int disable_freq_mux;
|
||
|
static unsigned int force_max_mmsys_clk;
|
||
|
static unsigned int force_always_on_mm_clks_mask;
|
||
|
static unsigned int clk_mux_mask = 0xFFFF;
|
||
|
|
||
|
static struct mmdvfs_context_struct g_mmdvfs_mgr_cntx;
|
||
|
static struct mmdvfs_context_struct * const g_mmdvfs_mgr = &g_mmdvfs_mgr_cntx;
|
||
|
|
||
|
int get_mmdvfs_clk_mux_mask(void)
|
||
|
{
|
||
|
return clk_mux_mask;
|
||
|
}
|
||
|
|
||
|
static int mmdvfs_get_default_step(void)
|
||
|
{
|
||
|
return MMDVFS_FINE_STEP_UNREQUEST;
|
||
|
}
|
||
|
|
||
|
s32 mmdvfs_get_current_fine_step(void)
|
||
|
{
|
||
|
return g_mmdvfs_current_step;
|
||
|
}
|
||
|
|
||
|
static int mmdvfs_determine_fine_step(struct mmdvfs_adaptor *adaptor,
|
||
|
int scenario, int sensor_size, int feature_flag, int sensor_fps,
|
||
|
int codec_width, int codec_height, int preview_size)
|
||
|
{
|
||
|
|
||
|
int mmdvfs_fine_step = MMDVFS_FINE_STEP_UNREQUEST;
|
||
|
|
||
|
struct mmdvfs_cam_property cam_setting = {0, 0, 0, 0};
|
||
|
struct mmdvfs_video_property codec_setting = {0, 0, 0};
|
||
|
|
||
|
if (!adaptor) {
|
||
|
MMDVFSMSG("%s: adaptor is NULL\n", __func__);
|
||
|
return MMDVFS_FINE_STEP_UNREQUEST;
|
||
|
}
|
||
|
|
||
|
cam_setting.sensor_size = sensor_size;
|
||
|
cam_setting.feature_flag = feature_flag;
|
||
|
cam_setting.fps = sensor_fps;
|
||
|
cam_setting.preview_size = preview_size;
|
||
|
|
||
|
codec_setting.width = codec_width;
|
||
|
codec_setting.height = codec_height;
|
||
|
|
||
|
mmdvfs_fine_step = adaptor->determine_mmdvfs_step(adaptor,
|
||
|
scenario, &cam_setting, &codec_setting);
|
||
|
|
||
|
return mmdvfs_fine_step;
|
||
|
|
||
|
}
|
||
|
|
||
|
static int mmdvfs_determine_fine_step_default(int scenario, int sensor_size,
|
||
|
int feature_flag, int sensor_fps, int codec_width,
|
||
|
int codec_height, int preview_size)
|
||
|
{
|
||
|
|
||
|
return mmdvfs_determine_fine_step(g_mmdvfs_adaptor,
|
||
|
scenario, sensor_size, feature_flag, sensor_fps,
|
||
|
codec_width, codec_height, preview_size);
|
||
|
}
|
||
|
|
||
|
static int mmdvfs_query(enum MTK_SMI_BWC_SCEN scenario,
|
||
|
struct MTK_MMDVFS_CMD *cmd)
|
||
|
{
|
||
|
int sensor_size = 0;
|
||
|
int camera_mode = 0;
|
||
|
int sensor_fps = 0;
|
||
|
int preview_size = 0;
|
||
|
|
||
|
/* use default info */
|
||
|
if (cmd == NULL) {
|
||
|
MMDVFSMSG("%s: cmd is NULL, scen=%d\n", __func__, scenario);
|
||
|
} else {
|
||
|
sensor_size = cmd->sensor_size;
|
||
|
camera_mode = cmd->camera_mode;
|
||
|
sensor_fps = cmd->sensor_fps;
|
||
|
preview_size = cmd->preview_size;
|
||
|
}
|
||
|
|
||
|
return mmdvfs_determine_fine_step_default(scenario, sensor_size,
|
||
|
camera_mode, sensor_fps,
|
||
|
g_mmdvfs_info->video_record_size[0],
|
||
|
g_mmdvfs_info->video_record_size[1], preview_size);
|
||
|
}
|
||
|
|
||
|
|
||
|
int mmdvfs_get_stable_isp_clk(void)
|
||
|
{
|
||
|
int legacy_mm_step = MMSYS_CLK_LOW;
|
||
|
int cam_clk_opp = g_mmdvfs_step_util->get_clients_clk_opp(
|
||
|
g_mmdvfs_step_util, g_mmdvfs_adaptor,
|
||
|
LEGACY_CAM_SCENS, MMDVFS_CLK_MUX_TOP_CAM_SEL);
|
||
|
|
||
|
if (cam_clk_opp != -1)
|
||
|
legacy_mm_step =
|
||
|
g_mmdvfs_step_util->get_legacy_mmclk_step_from_mmclk_opp(
|
||
|
g_mmdvfs_step_util, cam_clk_opp);
|
||
|
|
||
|
if (legacy_mm_step < 0 || legacy_mm_step >= MMDVFS_MMSYS_CLK_COUNT) {
|
||
|
MMDVFSDEBUG(3, "invalid legacy mmclk return:%d\n",
|
||
|
legacy_mm_step);
|
||
|
legacy_mm_step = MMSYS_CLK_LOW;
|
||
|
}
|
||
|
|
||
|
return legacy_mm_step;
|
||
|
}
|
||
|
|
||
|
static void mmdvfs_update_cmd(struct MTK_MMDVFS_CMD *cmd)
|
||
|
{
|
||
|
if (cmd == NULL)
|
||
|
return;
|
||
|
|
||
|
if (cmd->sensor_size)
|
||
|
g_mmdvfs_cmd.sensor_size = cmd->sensor_size;
|
||
|
|
||
|
if (cmd->sensor_fps)
|
||
|
g_mmdvfs_cmd.sensor_fps = cmd->sensor_fps;
|
||
|
|
||
|
/* MMDVFSMSG("update cm %d\n", cmd->camera_mode); */
|
||
|
g_mmdvfs_cmd.camera_mode = cmd->camera_mode;
|
||
|
|
||
|
/* MMDVFSMSG("update preview_size %d\n", cmd->preview_size); */
|
||
|
g_mmdvfs_cmd.preview_size = cmd->preview_size;
|
||
|
}
|
||
|
|
||
|
/* delay 4 seconds to go LPM to workaround camera ZSD + PIP issue */
|
||
|
static void mmdvfs_cam_work_handler(struct work_struct *work)
|
||
|
{
|
||
|
/* MMDVFSMSG("CAM handler %d\n", jiffies_to_msecs(jiffies)); */
|
||
|
mmdvfs_set_fine_step(MMDVFS_CAM_MON_SCEN, MMDVFS_FINE_STEP_UNREQUEST);
|
||
|
|
||
|
}
|
||
|
static DECLARE_DELAYED_WORK(g_mmdvfs_cam_work, mmdvfs_cam_work_handler);
|
||
|
|
||
|
static void mmdvfs_stop_cam_monitor(void)
|
||
|
{
|
||
|
cancel_delayed_work_sync(&g_mmdvfs_cam_work);
|
||
|
}
|
||
|
|
||
|
static void mmdvfs_start_cam_monitor(int scen, int delay_hz)
|
||
|
{
|
||
|
if (g_mmdvfs_mgr->is_boost_disable) {
|
||
|
MMDVFSMSG("MMDVFS boost (cam monitor) is disabled!!\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
mmdvfs_stop_cam_monitor();
|
||
|
|
||
|
/* Set to MAX clk opp here */
|
||
|
mmdvfs_set_fine_step(MMDVFS_CAM_MON_SCEN, MMDVFS_FINE_STEP_OPP0);
|
||
|
schedule_delayed_work(&g_mmdvfs_cam_work, delay_hz * HZ);
|
||
|
}
|
||
|
|
||
|
|
||
|
int mmdvfs_set_corse_step(int scenario, enum mmdvfs_voltage_enum step)
|
||
|
{
|
||
|
int fine_step_opp = MMDVFS_FINE_STEP_UNREQUEST;
|
||
|
|
||
|
if (step == MMDVFS_VOLTAGE_HIGH)
|
||
|
fine_step_opp = MMDVFS_FINE_STEP_OPP0;
|
||
|
else
|
||
|
fine_step_opp = MMDVFS_FINE_STEP_UNREQUEST;
|
||
|
|
||
|
mmdvfs_set_fine_step(scenario, step);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* The legacy set step function can only set corse step (HPM/ NON-HPM)*/
|
||
|
int mmdvfs_set_step(enum MTK_SMI_BWC_SCEN scenario,
|
||
|
enum mmdvfs_voltage_enum step)
|
||
|
{
|
||
|
return mmdvfs_set_corse_step(scenario, step);
|
||
|
}
|
||
|
|
||
|
int mmdvfs_internal_set_fine_step(const char *adaptor_name,
|
||
|
struct mmdvfs_adaptor *adaptor, struct mmdvfs_step_util *step_util,
|
||
|
enum MTK_SMI_BWC_SCEN smi_scenario,
|
||
|
int mmdvfs_step, int notify_clk_change)
|
||
|
{
|
||
|
int original_step = 0;
|
||
|
int final_step = MMDVFS_FINE_STEP_UNREQUEST;
|
||
|
int legacy_clk = -1;
|
||
|
|
||
|
if (!adaptor) {
|
||
|
MMDVFSMSG("%s: adaptor is NULL\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if ((smi_scenario >= (enum MTK_SMI_BWC_SCEN)MMDVFS_SCEN_COUNT) ||
|
||
|
(smi_scenario < SMI_BWC_SCEN_NORMAL)) {
|
||
|
MMDVFSERR("invalid scenario\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
spin_lock(&g_mmdvfs_mgr->scen_lock);
|
||
|
original_step = g_mmdvfs_current_step;
|
||
|
final_step = step_util->set_step(
|
||
|
step_util, mmdvfs_step, smi_scenario);
|
||
|
g_mmdvfs_current_step = final_step;
|
||
|
spin_unlock(&g_mmdvfs_mgr->scen_lock);
|
||
|
|
||
|
/* Change HW configuration */
|
||
|
#ifdef MMDVFS_QOS_SUPPORT
|
||
|
mmdvfs_qos_update(step_util, final_step);
|
||
|
#else
|
||
|
adaptor->apply_hw_configurtion_by_step(
|
||
|
adaptor, final_step, original_step);
|
||
|
#endif
|
||
|
|
||
|
if (notify_clk_change)
|
||
|
notify_camsys_clk_change(original_step, final_step);
|
||
|
|
||
|
legacy_clk = mmdvfs_get_stable_isp_clk();
|
||
|
|
||
|
if (((*g_mmdvfs_scen_log_mask) == (1 << MMDVFS_SCEN_COUNT)
|
||
|
&& original_step == final_step) ||
|
||
|
((1 << smi_scenario) & (*g_mmdvfs_scen_log_mask))) {
|
||
|
MMDVFSDEBUG(3,
|
||
|
"%s,set scen:(%d,0x%x)step:(%d,%d,0x%x,0x%x,0x%x,0x%x)\n",
|
||
|
adaptor_name, smi_scenario, g_mmdvfs_concurrency,
|
||
|
mmdvfs_step, final_step,
|
||
|
step_util->mmdvfs_concurrency_of_opps[0],
|
||
|
step_util->mmdvfs_concurrency_of_opps[1],
|
||
|
step_util->mmdvfs_concurrency_of_opps[2],
|
||
|
step_util->mmdvfs_concurrency_of_opps[3]);
|
||
|
MMDVFSDEBUG(3,
|
||
|
"%s,C(%d,%d,0x%x,%d),I(%d,%d),CLK:%d\n",
|
||
|
adaptor_name,
|
||
|
g_mmdvfs_cmd.sensor_size, g_mmdvfs_cmd.sensor_fps,
|
||
|
g_mmdvfs_cmd.camera_mode, g_mmdvfs_cmd.preview_size,
|
||
|
g_mmdvfs_info->video_record_size[0],
|
||
|
g_mmdvfs_info->video_record_size[1], legacy_clk);
|
||
|
} else {
|
||
|
MMDVFSMSG(
|
||
|
"%s,set scen:(%d,0x%x)step:(%d,%d,0x%x,0x%x,0x%x,0x%x)\n",
|
||
|
adaptor_name, smi_scenario, g_mmdvfs_concurrency,
|
||
|
mmdvfs_step, final_step,
|
||
|
step_util->mmdvfs_concurrency_of_opps[0],
|
||
|
step_util->mmdvfs_concurrency_of_opps[1],
|
||
|
step_util->mmdvfs_concurrency_of_opps[2],
|
||
|
step_util->mmdvfs_concurrency_of_opps[3]);
|
||
|
MMDVFSMSG("%s,C(%d,%d,0x%x,%d),I(%d,%d),CLK:%d\n",
|
||
|
adaptor_name,
|
||
|
g_mmdvfs_cmd.sensor_size, g_mmdvfs_cmd.sensor_fps,
|
||
|
g_mmdvfs_cmd.camera_mode, g_mmdvfs_cmd.preview_size,
|
||
|
g_mmdvfs_info->video_record_size[0],
|
||
|
g_mmdvfs_info->video_record_size[1], legacy_clk);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
int mmdvfs_internal_set_fine_step_default(
|
||
|
enum MTK_SMI_BWC_SCEN smi_scenario, int mmdvfs_step)
|
||
|
{
|
||
|
return mmdvfs_internal_set_fine_step("Fixed",
|
||
|
g_mmdvfs_adaptor, g_mmdvfs_step_util,
|
||
|
smi_scenario, mmdvfs_step, 1);
|
||
|
}
|
||
|
|
||
|
|
||
|
void mmdvfs_internal_notify_vcore_calibration(
|
||
|
struct mmdvfs_prepare_action_event *event)
|
||
|
{
|
||
|
if (mmdvfs_get_mmdvfs_profile() == MMDVFS_PROFILE_VIN) {
|
||
|
MMDVFSMSG("calibration event is not hanlded\n");
|
||
|
g_mmdvfs_mgr->is_mmdvfs_start = 1;
|
||
|
return;
|
||
|
}
|
||
|
if (event->event_type == MMDVFS_EVENT_PREPARE_CALIBRATION_START) {
|
||
|
g_mmdvfs_mgr->is_mmdvfs_start = 0;
|
||
|
#ifdef MMDVFS_QOS_SUPPORT
|
||
|
mmdvfs_qos_enable(false);
|
||
|
#endif
|
||
|
MMDVFSMSG("mmdvfs service is disabled for calibration\n");
|
||
|
} else if (event->event_type ==
|
||
|
MMDVFS_EVENT_PREPARE_CALIBRATION_END) {
|
||
|
g_mmdvfs_mgr->is_mmdvfs_start = 1;
|
||
|
#ifdef MMDVFS_QOS_SUPPORT
|
||
|
mmdvfs_qos_enable(true);
|
||
|
#endif
|
||
|
MMDVFSMSG("mmdvfs service has been enabled\n");
|
||
|
} else {
|
||
|
MMDVFSMSG("calibration: unknown status code:%d\n",
|
||
|
event->event_type);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int mmdvfs_set_fine_step_force(enum MTK_SMI_BWC_SCEN smi_scenario,
|
||
|
int mmdvfs_step)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
if (!g_mmdvfs_adaptor) {
|
||
|
MMDVFSMSG("step_force: g_mmdvfs_adaptor is NULL\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* Update HW runtime option */
|
||
|
g_mmdvfs_adaptor->enable_vcore = 1;
|
||
|
g_mmdvfs_adaptor->enable_clk_mux = 1;
|
||
|
ret = mmdvfs_internal_set_fine_step_default(
|
||
|
smi_scenario, mmdvfs_step);
|
||
|
|
||
|
/* recover the original setting */
|
||
|
g_mmdvfs_adaptor->enable_vcore = !disable_mmdvfs;
|
||
|
g_mmdvfs_adaptor->enable_clk_mux = !disable_freq_mux;
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int mmdvfs_set_fine_step(enum MTK_SMI_BWC_SCEN smi_scenario, int mmdvfs_step)
|
||
|
{
|
||
|
if (disable_mmdvfs || g_mmdvfs_mgr->is_mmdvfs_start == 0) {
|
||
|
MMDVFSMSG("MMDVFS request denalied; scen:%d, step:%d\n",
|
||
|
smi_scenario, mmdvfs_step);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (!g_mmdvfs_adaptor) {
|
||
|
MMDVFSMSG("fine_step: g_mmdvfs_adaptor is NULL\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* Update HW runtime option */
|
||
|
g_mmdvfs_adaptor->enable_vcore = !disable_mmdvfs;
|
||
|
g_mmdvfs_adaptor->enable_pll_hopping = !disable_freq_hopping;
|
||
|
g_mmdvfs_adaptor->enable_clk_mux = !disable_freq_mux;
|
||
|
|
||
|
return mmdvfs_internal_set_fine_step_default(
|
||
|
smi_scenario, mmdvfs_step);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
int mmdvfs_set_fine_step_non_force(
|
||
|
enum MTK_SMI_BWC_SCEN smi_scenario, int mmdvfs_step)
|
||
|
{
|
||
|
if (disable_mmdvfs || g_mmdvfs_mgr->is_mmdvfs_start == 0) {
|
||
|
MMDVFSMSG("MMDVFS request denalied; scen:%d, step:%d\n",
|
||
|
smi_scenario, mmdvfs_step);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (!g_mmdvfs_non_force_adaptor) {
|
||
|
MMDVFSMSG("step_non_force is disable\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* Update HW runtime option */
|
||
|
g_mmdvfs_non_force_adaptor->enable_vcore = !disable_mmdvfs;
|
||
|
g_mmdvfs_non_force_adaptor->enable_pll_hopping = 0;
|
||
|
g_mmdvfs_non_force_adaptor->enable_clk_mux = 0;
|
||
|
|
||
|
return mmdvfs_internal_set_fine_step("Auto-Adjust",
|
||
|
g_mmdvfs_non_force_adaptor,
|
||
|
g_non_force_step_util, smi_scenario, mmdvfs_step, 0);
|
||
|
}
|
||
|
|
||
|
static int handle_step_mmmclk_set(struct MTK_MMDVFS_CMD *cmd)
|
||
|
{
|
||
|
int mmdvfs_step_request = 0;
|
||
|
int mmclk_request = 0;
|
||
|
|
||
|
if (cmd == NULL) {
|
||
|
MMDVFSMSG("step ioctl cmd can't be NULL\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (!g_mmdvfs_adaptor) {
|
||
|
MMDVFSMSG("mmmclk_set: g_mmdvfs_adaptor is NULL\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* Get step from the command (bit 0-7) */
|
||
|
mmdvfs_step_request = cmd->step & MMDVFS_IOCTL_CMD_STEP_FIELD_MASK;
|
||
|
if (mmdvfs_step_request == MMDVFS_IOCTL_CMD_STEP_FIELD_MASK)
|
||
|
mmdvfs_step_request = -1;
|
||
|
/* Get clk from the command (bit 8-15) */
|
||
|
mmclk_request = (cmd->step & MMDVFS_IOCTL_CMD_MMCLK_FIELD_MASK)
|
||
|
>> MMDVFS_IOCTL_CMD_STEP_FIELD_LEN;
|
||
|
|
||
|
if (mmdvfs_step_request < MMDVFS_FINE_STEP_UNREQUEST ||
|
||
|
mmdvfs_step_request >= g_mmdvfs_adaptor->step_num) {
|
||
|
MMDVFSMSG("invalid step (%d)\n", mmdvfs_step_request);
|
||
|
} else {
|
||
|
MMDVFSMSG("Request step=%d mmclk %d is ignaored\n",
|
||
|
mmdvfs_step_request, mmclk_request);
|
||
|
mmdvfs_set_fine_step(cmd->scen, mmdvfs_step_request);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void mmdvfs_handle_vpu_dvfs_set_cmd(struct MTK_MMDVFS_CMD *cmd)
|
||
|
{
|
||
|
/* Check if the scenrio is normal for VPU here */
|
||
|
if (cmd->scen != SMI_BWC_SCEN_NORMAL) {
|
||
|
int current_vpu_step_config = 0;
|
||
|
int update_vpu_step = 0;
|
||
|
int result = -1;
|
||
|
|
||
|
spin_lock(&g_mmdvfs_mgr->scen_lock);
|
||
|
current_vpu_step_config = g_mmdvfs_current_vpu_step;
|
||
|
spin_unlock(&g_mmdvfs_mgr->scen_lock);
|
||
|
|
||
|
if (cmd->camera_mode == MMDVFS_IOCTL_CMD_VPU_STEP_UNREQUEST)
|
||
|
update_vpu_step = -1;
|
||
|
else
|
||
|
update_vpu_step = cmd->camera_mode;
|
||
|
|
||
|
result = mmdvfs_internal_set_vpu_step(
|
||
|
current_vpu_step_config, update_vpu_step);
|
||
|
|
||
|
if (result) {
|
||
|
MMDVFSMSG("vpu_step failed: %d, req:%d, current:%d\n",
|
||
|
result, update_vpu_step, current_vpu_step_config);
|
||
|
cmd->ret = -1;
|
||
|
} else {
|
||
|
spin_lock(&g_mmdvfs_mgr->scen_lock);
|
||
|
g_mmdvfs_current_vpu_step = update_vpu_step;
|
||
|
spin_unlock(&g_mmdvfs_mgr->scen_lock);
|
||
|
cmd->ret = 0;
|
||
|
}
|
||
|
} else {
|
||
|
MMDVFSMSG("vpu_dvfs_set_cmd must with normal scenario\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void mmdvfs_handle_vpu_dvfs_get_cmd(struct MTK_MMDVFS_CMD *cmd)
|
||
|
{
|
||
|
cmd->ret = g_mmdvfs_current_vpu_step;
|
||
|
}
|
||
|
|
||
|
void mmdvfs_handle_cmd(struct MTK_MMDVFS_CMD *cmd)
|
||
|
{
|
||
|
if (disable_mmdvfs) {
|
||
|
MMDVFSMSG("MMDVFS is disabled\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* MMDVFSMSG("MMDVFS handle cmd %u s %d\n", cmd->type, cmd->scen); */
|
||
|
|
||
|
switch (cmd->type) {
|
||
|
case MTK_MMDVFS_CMD_TYPE_SET:
|
||
|
/* save cmd */
|
||
|
mmdvfs_update_cmd(cmd);
|
||
|
if (!(g_mmdvfs_concurrency & (1 << cmd->scen))) {
|
||
|
/*MMDVFSMSG("invalid set scen %d\n", cmd->scen); */
|
||
|
cmd->ret = -1;
|
||
|
} else {
|
||
|
/* determine the step and apply the HW setting */
|
||
|
cmd->ret = mmdvfs_set_fine_step(
|
||
|
cmd->scen, mmdvfs_query(cmd->scen, cmd));
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case MTK_MMDVFS_CMD_TYPE_QUERY: /* query with some parameters */
|
||
|
{
|
||
|
int step = mmdvfs_query(cmd->scen, cmd);
|
||
|
s32 cur_step =
|
||
|
mmdvfs_get_current_fine_step();
|
||
|
|
||
|
/* Compare the step and return the result */
|
||
|
if (step == cur_step) {
|
||
|
if (step == MMDVFS_FINE_STEP_UNREQUEST)
|
||
|
cmd->ret = (u32)MMDVFS_STEP_LOW2LOW;
|
||
|
else if (step == MMDVFS_FINE_STEP_OPP0)
|
||
|
cmd->ret = (u32)MMDVFS_STEP_HIGH2HIGH;
|
||
|
} else if (step > cur_step) {
|
||
|
if (cur_step == MMDVFS_FINE_STEP_UNREQUEST)
|
||
|
cmd->ret = (u32)MMDVFS_STEP_LOW2HIGH;
|
||
|
else
|
||
|
cmd->ret = (u32)MMDVFS_STEP_HIGH2LOW;
|
||
|
} else {
|
||
|
if (step == MMDVFS_FINE_STEP_UNREQUEST)
|
||
|
cmd->ret = (u32)MMDVFS_STEP_HIGH2LOW;
|
||
|
else
|
||
|
cmd->ret = (u32)MMDVFS_STEP_LOW2HIGH;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case MTK_MMDVFS_CMD_TYPE_GET:
|
||
|
{
|
||
|
cmd->ret = 0;
|
||
|
/* Put step in the command (bit 0-7) */
|
||
|
cmd->ret = g_mmdvfs_current_step
|
||
|
& MMDVFS_IOCTL_CMD_STEP_FIELD_MASK;
|
||
|
|
||
|
MMDVFSMSG("Current step query result: %d, 0x%x\n",
|
||
|
g_mmdvfs_current_step, cmd->ret);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case MTK_MMDVFS_CMD_TYPE_CONFIG:
|
||
|
g_mmdvfs_mgr->is_boost_disable = cmd->boost_disable;
|
||
|
MMDVFSMSG("Config: is_boost_disable=%d\n",
|
||
|
g_mmdvfs_mgr->is_boost_disable);
|
||
|
camera_bw_config = cmd->ddr_type;
|
||
|
MMDVFSMSG("Config: bw_config=0x%08x\n", camera_bw_config);
|
||
|
break;
|
||
|
|
||
|
case MTK_MMDVFS_CMD_TYPE_STEP_SET:
|
||
|
/* Get the target step from step field */
|
||
|
cmd->ret = handle_step_mmmclk_set(cmd);
|
||
|
break;
|
||
|
|
||
|
case MTK_MMDVFS_CMD_TYPE_VPU_STEP_SET:
|
||
|
mmdvfs_handle_vpu_dvfs_set_cmd(cmd);
|
||
|
break;
|
||
|
|
||
|
case MTK_MMDVFS_CMD_TYPE_VPU_STEP_GET:
|
||
|
mmdvfs_handle_vpu_dvfs_get_cmd(cmd);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
MMDVFSMSG("invalid mmdvfs cmd\n");
|
||
|
dump_stack();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#define MMDVFS_CAMERA_BOOST_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_VENC) | \
|
||
|
(1<<SMI_BWC_SCEN_CAM_PV) | \
|
||
|
(1<<SMI_BWC_SCEN_CAM_CP))
|
||
|
|
||
|
void mmdvfs_notify_scenario_exit(enum MTK_SMI_BWC_SCEN scen)
|
||
|
{
|
||
|
if (disable_mmdvfs) {
|
||
|
MMDVFSMSG("MMDVFS is disabled\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (force_max_mmsys_clk) {
|
||
|
MMDVFSMSG("MMDVFS is always high\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* MMDVFSMSG("leave %d\n", scen); */
|
||
|
if (scen == SMI_BWC_SCEN_WFD)
|
||
|
g_mmdvfs_mgr->is_wfd_enable = 0;
|
||
|
|
||
|
if (scen == SMI_BWC_SCEN_VP_HIGH_FPS)
|
||
|
g_mmdvfs_mgr->is_vp_high_fps_enable = 0;
|
||
|
|
||
|
/* Boost for ISP related scenario */
|
||
|
if ((1 << scen & MMDVFS_CAMERA_BOOST_MASK))
|
||
|
mmdvfs_start_cam_monitor(scen, 8);
|
||
|
|
||
|
/* If the scenario is defined in disable_auto_control_mask */
|
||
|
/* mmdvfs_mgr doesn't change the step automatically. */
|
||
|
/* The kernel driver of the scenarios will change the step */
|
||
|
/* by mmdvfs_set_fine_step directly */
|
||
|
if (g_mmdvfs_adaptor &&
|
||
|
(!((1 << scen)
|
||
|
& g_mmdvfs_adaptor->disable_auto_control_mask)))
|
||
|
mmdvfs_set_fine_step(scen, MMDVFS_FINE_STEP_UNREQUEST);
|
||
|
|
||
|
if (g_mmdvfs_non_force_adaptor &&
|
||
|
(!((1 << scen)
|
||
|
& g_mmdvfs_non_force_adaptor->disable_auto_control_mask)))
|
||
|
mmdvfs_set_fine_step_non_force(
|
||
|
scen, MMDVFS_FINE_STEP_UNREQUEST);
|
||
|
/* reset scenario voltage to default when it exits */
|
||
|
/* Also force the system to leave low low mode */
|
||
|
|
||
|
}
|
||
|
|
||
|
void mmdvfs_internal_get_cam_setting(struct mmdvfs_cam_property *cam_setting)
|
||
|
{
|
||
|
cam_setting->sensor_size = g_mmdvfs_cmd.sensor_size;
|
||
|
cam_setting->fps = g_mmdvfs_cmd.sensor_fps;
|
||
|
cam_setting->feature_flag = g_mmdvfs_cmd.camera_mode;
|
||
|
cam_setting->preview_size = g_mmdvfs_cmd.preview_size;
|
||
|
}
|
||
|
|
||
|
void mmdvfs_notify_scenario_enter(enum MTK_SMI_BWC_SCEN scen)
|
||
|
{
|
||
|
int mmdvfs_fine_step = MMDVFS_FINE_STEP_UNREQUEST;
|
||
|
int mmdvfs_fine_step_non_force = MMDVFS_FINE_STEP_UNREQUEST;
|
||
|
|
||
|
if (disable_mmdvfs) {
|
||
|
MMDVFSMSG("MMDVFS is disabled\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (force_max_mmsys_clk) {
|
||
|
MMDVFSMSG("MMDVFS is always high\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* If the scenario is defined in disable_auto_control_mask */
|
||
|
/* mmdvfs_mgr doesn't change the step automatically. */
|
||
|
/* The kernel driver of the scenarios will change the step */
|
||
|
/* by mmdvfs_set_fine_step directly */
|
||
|
|
||
|
if (g_mmdvfs_adaptor &&
|
||
|
(!((1 << scen)
|
||
|
& g_mmdvfs_adaptor->disable_auto_control_mask))) {
|
||
|
mmdvfs_fine_step = mmdvfs_determine_fine_step_default(
|
||
|
scen, g_mmdvfs_cmd.sensor_size,
|
||
|
g_mmdvfs_cmd.camera_mode, g_mmdvfs_cmd.sensor_fps,
|
||
|
g_mmdvfs_info->video_record_size[0],
|
||
|
g_mmdvfs_info->video_record_size[1],
|
||
|
g_mmdvfs_cmd.preview_size);
|
||
|
|
||
|
mmdvfs_set_fine_step(scen, mmdvfs_fine_step);
|
||
|
}
|
||
|
|
||
|
if (g_mmdvfs_non_force_adaptor &&
|
||
|
(!((1 << scen)
|
||
|
& g_mmdvfs_non_force_adaptor->disable_auto_control_mask))) {
|
||
|
mmdvfs_fine_step_non_force = mmdvfs_determine_fine_step(
|
||
|
g_mmdvfs_non_force_adaptor,
|
||
|
scen, g_mmdvfs_cmd.sensor_size, g_mmdvfs_cmd.camera_mode,
|
||
|
g_mmdvfs_cmd.sensor_fps,
|
||
|
g_mmdvfs_info->video_record_size[0],
|
||
|
g_mmdvfs_info->video_record_size[1],
|
||
|
g_mmdvfs_cmd.preview_size);
|
||
|
mmdvfs_set_fine_step_non_force(
|
||
|
scen, mmdvfs_fine_step_non_force);
|
||
|
}
|
||
|
|
||
|
/* Boost for ISP related scenario */
|
||
|
if ((1 << scen & MMDVFS_CAMERA_BOOST_MASK))
|
||
|
mmdvfs_start_cam_monitor(scen, 8);
|
||
|
|
||
|
/* Record the engine status for debugging */
|
||
|
if (scen == SMI_BWC_SCEN_WFD)
|
||
|
g_mmdvfs_mgr->is_wfd_enable = 1;
|
||
|
|
||
|
if (scen == SMI_BWC_SCEN_VP_HIGH_FPS)
|
||
|
g_mmdvfs_mgr->is_vp_high_fps_enable = 1;
|
||
|
}
|
||
|
|
||
|
void bwc_mm_info_set(int property_id, long val1, long val2)
|
||
|
{
|
||
|
|
||
|
switch (property_id) {
|
||
|
case SMI_BWC_INFO_CON_PROFILE:
|
||
|
g_mmdvfs_info->concurrent_profile = (int)val1;
|
||
|
break;
|
||
|
case SMI_BWC_INFO_SENSOR_SIZE:
|
||
|
g_mmdvfs_info->sensor_size[0] = val1;
|
||
|
g_mmdvfs_info->sensor_size[1] = val2;
|
||
|
break;
|
||
|
case SMI_BWC_INFO_VIDEO_RECORD_SIZE:
|
||
|
g_mmdvfs_info->video_record_size[0] = val1;
|
||
|
g_mmdvfs_info->video_record_size[1] = val2;
|
||
|
break;
|
||
|
case SMI_BWC_INFO_DISP_SIZE:
|
||
|
g_mmdvfs_info->display_size[0] = val1;
|
||
|
g_mmdvfs_info->display_size[1] = val2;
|
||
|
break;
|
||
|
case SMI_BWC_INFO_TV_OUT_SIZE:
|
||
|
g_mmdvfs_info->tv_out_size[0] = val1;
|
||
|
g_mmdvfs_info->tv_out_size[1] = val2;
|
||
|
break;
|
||
|
case SMI_BWC_INFO_FPS:
|
||
|
g_mmdvfs_info->fps = (int)val1;
|
||
|
break;
|
||
|
case SMI_BWC_INFO_VIDEO_ENCODE_CODEC:
|
||
|
g_mmdvfs_info->video_encode_codec = (int)val1;
|
||
|
break;
|
||
|
case SMI_BWC_INFO_VIDEO_DECODE_CODEC:
|
||
|
g_mmdvfs_info->video_decode_codec = (int)val1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int set_mm_info_ioctl_wrapper(struct file *pFile,
|
||
|
unsigned int cmd, unsigned long param)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
struct MTK_SMI_BWC_INFO_SET cfg;
|
||
|
|
||
|
ret = copy_from_user(&cfg, (void *)param,
|
||
|
sizeof(struct MTK_SMI_BWC_INFO_SET));
|
||
|
if (ret) {
|
||
|
MMDVFSMSG("SET copy_to_user failed: %d\n", ret);
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
/* Set the address to the value assigned by user space program */
|
||
|
bwc_mm_info_set(cfg.property, cfg.value1, cfg.value2);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
int get_mm_info_ioctl_wrapper(struct file *pFile,
|
||
|
unsigned int cmd, unsigned long param)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
ret = copy_to_user((void *)param, (void *)g_mmdvfs_info,
|
||
|
sizeof(struct MTK_SMI_BWC_MM_INFO));
|
||
|
|
||
|
if (ret) {
|
||
|
MMDVFSMSG("GET copy_to_user failed: %d\n", ret);
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/* MMDVFS related clk initialization */
|
||
|
static struct clk *smi_clk_get_by_name(struct device_node *of_node,
|
||
|
const char *clk_name)
|
||
|
{
|
||
|
struct clk *clk_ptr = NULL;
|
||
|
|
||
|
clk_ptr = of_clk_get_by_name(of_node, clk_name);
|
||
|
|
||
|
if (IS_ERR(clk_ptr)) {
|
||
|
MMDVFSMSG("Can't get clk_name %s\n", clk_name);
|
||
|
clk_ptr = NULL;
|
||
|
}
|
||
|
return clk_ptr;
|
||
|
}
|
||
|
|
||
|
void mmdvfs_clks_init(struct device_node *of_node)
|
||
|
{
|
||
|
int i = 0;
|
||
|
|
||
|
MMDVFSMSG("start %s\n", __func__);
|
||
|
/* const int mmdvfs_disable_setting = disable_mmdvfs; */
|
||
|
/* init clk mux of each MM clks*/
|
||
|
for (i = 0; i < g_mmdvfs_adaptor->mmdvfs_clk_hw_maps_num; i++) {
|
||
|
/* Get the clk mux desc */
|
||
|
struct mmdvfs_clk_hw_map *hw_map_ptr =
|
||
|
g_mmdvfs_adaptor->mmdvfs_clk_hw_maps + i;
|
||
|
|
||
|
if (hw_map_ptr->config_method != MMDVFS_CLK_CONFIG_NONE) {
|
||
|
MMDVFSMSG("Init CLK %s\n",
|
||
|
hw_map_ptr->clk_mux.ccf_name);
|
||
|
hw_map_ptr->clk_mux.ccf_handle = smi_clk_get_by_name(
|
||
|
of_node, hw_map_ptr->clk_mux.ccf_name);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < g_mmdvfs_adaptor->mmdvfs_clk_sources_num; i++) {
|
||
|
MMDVFSMSG("Init CLK %s\n",
|
||
|
g_mmdvfs_adaptor->mmdvfs_clk_sources[i].ccf_name);
|
||
|
g_mmdvfs_adaptor->mmdvfs_clk_sources[i].ccf_handle =
|
||
|
smi_clk_get_by_name(of_node,
|
||
|
g_mmdvfs_adaptor->mmdvfs_clk_sources[i].ccf_name);
|
||
|
}
|
||
|
|
||
|
/* Enanle the MASK for CLK change */
|
||
|
clk_mux_mask = 0xFFFF;
|
||
|
|
||
|
MMDVFSMSG("Finish %s\n", __func__);
|
||
|
|
||
|
/* Set default high berfore MMDVFS feature is enabled, */
|
||
|
/* Onlye work when force_max_mmsys_clk is enabled */
|
||
|
mmdvfs_default_start_delayed_setting();
|
||
|
}
|
||
|
|
||
|
void mmdvfs_init(void)
|
||
|
{
|
||
|
spin_lock_init(&g_mmdvfs_mgr->scen_lock);
|
||
|
|
||
|
/* set current step as the default step */
|
||
|
g_mmdvfs_current_step = mmdvfs_get_default_step();
|
||
|
|
||
|
g_mmdvfs_info = &g_bwc_mm_info;
|
||
|
|
||
|
mmdvfs_config_util_init();
|
||
|
|
||
|
if (mmdvfs_get_mmdvfs_profile() == MMDVFS_PROFILE_ALA)
|
||
|
g_mmdvfs_mgr->is_mmdvfs_start = 1;
|
||
|
if (mmdvfs_get_mmdvfs_profile() == MMDVFS_PROFILE_BIA)
|
||
|
g_mmdvfs_mgr->is_mmdvfs_start = 1;
|
||
|
if (mmdvfs_get_mmdvfs_profile() == MMDVFS_PROFILE_VIN)
|
||
|
g_mmdvfs_mgr->is_mmdvfs_start = 1;
|
||
|
if (mmdvfs_get_mmdvfs_profile() == MMDVFS_PROFILE_ZIO)
|
||
|
g_mmdvfs_mgr->is_mmdvfs_start = 1;
|
||
|
if (mmdvfs_get_mmdvfs_profile() == MMDVFS_PROFILE_MER)
|
||
|
g_mmdvfs_mgr->is_mmdvfs_start = 1;
|
||
|
if (mmdvfs_get_mmdvfs_profile() == MMDVFS_PROFILE_SYL)
|
||
|
g_mmdvfs_mgr->is_mmdvfs_start = 1;
|
||
|
if (mmdvfs_get_mmdvfs_profile() == MMDVFS_PROFILE_CAN)
|
||
|
g_mmdvfs_mgr->is_mmdvfs_start = 1;
|
||
|
}
|
||
|
|
||
|
/* To be implemented */
|
||
|
void mmdvfs_mhl_enable(int enable)
|
||
|
{
|
||
|
int mmdvfs_fine_step = mmdvfs_determine_fine_step_default(
|
||
|
MMDVFS_SCEN_MHL, 0, 0, 0, 0, 0, 0);
|
||
|
|
||
|
g_mmdvfs_mgr->is_mhl_enable = enable;
|
||
|
|
||
|
if (enable)
|
||
|
mmdvfs_set_fine_step(MMDVFS_SCEN_MHL, mmdvfs_fine_step);
|
||
|
else
|
||
|
mmdvfs_set_fine_step(MMDVFS_SCEN_MHL,
|
||
|
MMDVFS_FINE_STEP_UNREQUEST);
|
||
|
}
|
||
|
|
||
|
void mmdvfs_mjc_enable(int enable)
|
||
|
{
|
||
|
g_mmdvfs_mgr->is_mjc_enable = enable;
|
||
|
}
|
||
|
|
||
|
void mmdvfs_notify_scenario_concurrency(unsigned int u4Concurrency)
|
||
|
{
|
||
|
/*
|
||
|
* THIS FUNCTION IS IN SMI SPIN LOCK.
|
||
|
*/
|
||
|
g_mmdvfs_concurrency = u4Concurrency;
|
||
|
}
|
||
|
|
||
|
int mmdvfs_is_default_step_need_perf(void)
|
||
|
{
|
||
|
MMDVFSMSG("need_perf is not supported in this platform\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* switch MM CLK callback from VCORE DVFS driver */
|
||
|
void mmdvfs_mm_clock_switch_notify(int is_before, int is_to_high)
|
||
|
{
|
||
|
MMDVFSMSG("notify is not supported in this platform\n");
|
||
|
}
|
||
|
|
||
|
int register_mmclk_switch_cb(
|
||
|
clk_switch_cb notify_cb, clk_switch_cb notify_cb_nolock)
|
||
|
{
|
||
|
MMDVFSMSG("%s is deplicated\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* This desing is only for CLK Mux switch relate flows */
|
||
|
int mmdvfs_notify_mmclk_switch_request(int event)
|
||
|
{
|
||
|
MMDVFSMSG("switch_request is deplicated: %d\n", event);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
int mmdvfs_register_mmclk_switch_cb(
|
||
|
clk_switch_cb notify_cb, int mmdvfs_client_id)
|
||
|
{
|
||
|
if (mmdvfs_client_id >= 0
|
||
|
&& mmdvfs_client_id < MMDVFS_CLK_SWITCH_CB_MAX) {
|
||
|
quick_mmclk_cbs[mmdvfs_client_id] = notify_cb;
|
||
|
} else {
|
||
|
MMDVFSMSG("switch register failed: id=%d\n",
|
||
|
mmdvfs_client_id);
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int mmsys_clk_change_notify_checked(clk_switch_cb func,
|
||
|
int ori_mmsys_clk_mode,
|
||
|
int update_mmsys_clk_mode, char *msg)
|
||
|
{
|
||
|
if (func == NULL) {
|
||
|
MMDVFSMSG("notify_cb_func is NULL %s, (%d,%d)\n",
|
||
|
msg, ori_mmsys_clk_mode,
|
||
|
update_mmsys_clk_mode);
|
||
|
} else {
|
||
|
func(ori_mmsys_clk_mode, update_mmsys_clk_mode);
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void notify_camsys_clk_change(
|
||
|
int ori_mmdvfs_step, int update_mmdvfs_step)
|
||
|
{
|
||
|
int i = 0;
|
||
|
int ori_cam_clk_mode = 0;
|
||
|
int update_cam_clk_mode = 0;
|
||
|
char msg[MMDVFS_CLK_SWITCH_CLIENT_MSG_MAX] = "";
|
||
|
int result;
|
||
|
|
||
|
if (!g_mmdvfs_adaptor) {
|
||
|
MMDVFSMSG("clk_change: g_mmdvfs_adaptor is NULL\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (ori_mmdvfs_step == -1)
|
||
|
ori_mmdvfs_step = g_mmdvfs_adaptor->step_num - 1;
|
||
|
if (update_mmdvfs_step == -1)
|
||
|
update_mmdvfs_step = g_mmdvfs_adaptor->step_num - 1;
|
||
|
|
||
|
if (ori_mmdvfs_step < 0
|
||
|
|| ori_mmdvfs_step >= g_mmdvfs_adaptor->step_num
|
||
|
|| update_mmdvfs_step < 0
|
||
|
|| update_mmdvfs_step >= g_mmdvfs_adaptor->step_num) {
|
||
|
MMDVFSMSG("invalid step change %d --> %d",
|
||
|
ori_mmdvfs_step, update_mmdvfs_step);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ori_cam_clk_mode = g_mmdvfs_adaptor->get_cam_sys_clk(
|
||
|
g_mmdvfs_adaptor, ori_mmdvfs_step);
|
||
|
update_cam_clk_mode = g_mmdvfs_adaptor->get_cam_sys_clk(
|
||
|
g_mmdvfs_adaptor, update_mmdvfs_step);
|
||
|
|
||
|
for (i = 0; i < MMDVFS_CLK_SWITCH_CB_MAX; i++) {
|
||
|
result = snprintf(msg,
|
||
|
MMDVFS_CLK_SWITCH_CLIENT_MSG_MAX, "id=%d", i);
|
||
|
if (result < 0) {
|
||
|
MMDVFSMSG("snprint fail for id:%d retult=%d\n",
|
||
|
i, result);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (quick_mmclk_cbs[i] != NULL)
|
||
|
mmsys_clk_change_notify_checked(
|
||
|
quick_mmclk_cbs[i], ori_cam_clk_mode,
|
||
|
update_cam_clk_mode, msg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Default step configurtion (after boot up) */
|
||
|
static unsigned int delayed_default_step_finished;
|
||
|
|
||
|
void mmdvfs_default_step_set(int default_step)
|
||
|
{
|
||
|
if (force_max_mmsys_clk) {
|
||
|
MMDVFSMSG("Forcing max mm clks is enabled\n");
|
||
|
mmdvfs_set_fine_step_force(MMDVFS_MGR, default_step);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
static void mmdvfs_default_step_delayed(struct work_struct *work)
|
||
|
{
|
||
|
if (force_always_on_mm_clks_mask)
|
||
|
mmdvfs_debug_set_mmdvfs_clks_enabled(1);
|
||
|
|
||
|
/* Set default high berfore MMDVFS feature is enabled */
|
||
|
if (force_max_mmsys_clk) {
|
||
|
mmdvfs_default_step_set(MMDVFS_FINE_STEP_OPP0);
|
||
|
delayed_default_step_finished = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static DECLARE_DELAYED_WORK(g_mmdvfs_set_default_step_delayed,
|
||
|
mmdvfs_default_step_delayed);
|
||
|
|
||
|
void mmdvfs_default_start_delayed_setting(void)
|
||
|
{
|
||
|
schedule_delayed_work(&g_mmdvfs_set_default_step_delayed, 60 * HZ);
|
||
|
}
|
||
|
|
||
|
void mmdvfs_default_stop_delayed_setting(void)
|
||
|
{
|
||
|
/* Set default high berfore MMDVFS feature is enabled */
|
||
|
if (force_max_mmsys_clk && (delayed_default_step_finished == 0))
|
||
|
cancel_delayed_work_sync(&g_mmdvfs_set_default_step_delayed);
|
||
|
}
|
||
|
|
||
|
/* Used for MMDVFS before CLK controlling is ready */
|
||
|
static unsigned int mm_clks_enabled;
|
||
|
|
||
|
void mmdvfs_debug_set_mmdvfs_clks_enabled(int clk_enable_request)
|
||
|
{
|
||
|
int clk_idx = 0;
|
||
|
int ccf_ret = 0;
|
||
|
int always_on_mask = force_always_on_mm_clks_mask;
|
||
|
struct clk *mux;
|
||
|
|
||
|
if (!g_mmdvfs_adaptor) {
|
||
|
MMDVFSMSG("clks_enabled: g_mmdvfs_adaptor is NULL\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (mm_clks_enabled == 0 && clk_enable_request == 0) {
|
||
|
MMDVFSMSG("clks_enabled: clk is already disabled\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (mm_clks_enabled == 1 && clk_enable_request == 1) {
|
||
|
MMDVFSMSG("clks_enabled: clk is already enabled\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (clk_idx = 0;
|
||
|
clk_idx < g_mmdvfs_adaptor->mmdvfs_clk_hw_maps_num;
|
||
|
clk_idx++) {
|
||
|
/* Get the specific clk descriptor */
|
||
|
struct mmdvfs_clk_hw_map *map =
|
||
|
&(g_mmdvfs_adaptor->mmdvfs_clk_hw_maps[clk_idx]);
|
||
|
|
||
|
if (map->config_method ==
|
||
|
MMDVFS_CLK_CONFIG_BY_MUX) {
|
||
|
if (map->clk_mux.ccf_handle == NULL) {
|
||
|
MMDVFSMSG("handle can't be NULL\n");
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* Check if the clk be always on the configurtion */
|
||
|
if (!((1 << clk_idx) & always_on_mask))
|
||
|
continue;
|
||
|
|
||
|
mux = (struct clk *)map->clk_mux.ccf_handle;
|
||
|
if (clk_enable_request == 1) {
|
||
|
MMDVFSMSG("clk_prepare_enable\n");
|
||
|
ccf_ret =
|
||
|
clk_prepare_enable(mux);
|
||
|
if (ccf_ret) {
|
||
|
MMDVFSMSG("Failed enable clk: %s\n",
|
||
|
map->clk_mux.ccf_name);
|
||
|
continue;
|
||
|
}
|
||
|
} else {
|
||
|
MMDVFSMSG("clk_disable_unprepare\n");
|
||
|
clk_disable_unprepare(mux);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int mmdvfs_internal_set_vpu_step(int current_step, int update_step)
|
||
|
{
|
||
|
/* Fill event object */
|
||
|
struct mmdvfs_state_change_event evt = {0};
|
||
|
const struct mmdvfs_vpu_steps_setting *setting;
|
||
|
|
||
|
if (!g_mmdvfs_vpu_adaptor) {
|
||
|
MMDVFSMSG("set_vpu_step: adaptor can't be NULL\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
setting = g_mmdvfs_vpu_adaptor->get_vpu_setting(
|
||
|
g_mmdvfs_vpu_adaptor, update_step);
|
||
|
|
||
|
if (!setting) {
|
||
|
MMDVFSMSG("get_vpu_setting return NULL for %d\n",
|
||
|
update_step);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (update_step == -1) {
|
||
|
evt.vcore_vol_step = -1;
|
||
|
evt.vpu_clk_step = -1;
|
||
|
evt.vpu_if_clk_step = -1;
|
||
|
evt.vimvo_vol_step = -1;
|
||
|
} else {
|
||
|
evt.vcore_vol_step = setting->mmdvfs_step;
|
||
|
evt.vpu_clk_step = setting->vpu_clk_step;
|
||
|
evt.vpu_if_clk_step = setting->vpu_if_clk_step;
|
||
|
evt.vimvo_vol_step = setting->vimvo_vol_step;
|
||
|
}
|
||
|
|
||
|
/* Rising */
|
||
|
if (current_step == -1 || ((update_step != -1) && (update_step
|
||
|
< current_step))) {
|
||
|
MMDVFSDEBUG(3, "Apply MMDVFS setting");
|
||
|
mmdvfs_set_fine_step(MMDVFS_SCEN_VPU, evt.vcore_vol_step);
|
||
|
MMDVFSDEBUG(3, "Apply VPU setting:\n");
|
||
|
mmdvfs_internal_handle_state_change(&evt);
|
||
|
} else {
|
||
|
/* Falling */
|
||
|
MMDVFSDEBUG(3, "Apply VPU setting:\n");
|
||
|
mmdvfs_internal_handle_state_change(NULL);
|
||
|
MMDVFSDEBUG(3, "Apply MMDVFS setting");
|
||
|
mmdvfs_set_fine_step(MMDVFS_SCEN_VPU, evt.vcore_vol_step);
|
||
|
mmdvfs_internal_handle_state_change(&evt);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void dump_mmdvfs_info(void)
|
||
|
{
|
||
|
MMDVFSMSG("MMDVFS dump: CMD(%d,%d,0x%x,%d),INFO VR(%d,%d),CLK: %d\n",
|
||
|
g_mmdvfs_cmd.sensor_size, g_mmdvfs_cmd.sensor_fps,
|
||
|
g_mmdvfs_cmd.camera_mode, g_mmdvfs_cmd.preview_size,
|
||
|
g_mmdvfs_info->video_record_size[0],
|
||
|
g_mmdvfs_info->video_record_size[1],
|
||
|
current_mmsys_clk);
|
||
|
}
|
||
|
|
||
|
void mmdvfs_unit_test_func(void)
|
||
|
{
|
||
|
MMDVFSMSG("MMDVFS unit test\n");
|
||
|
mmdvfs_start_cam_monitor(MMDVFS_SCEN_MHL, 8);
|
||
|
|
||
|
}
|
||
|
|
||
|
int mmdvfs_get_mmdvfs_profile(void)
|
||
|
{
|
||
|
|
||
|
int mmdvfs_profile_id = MMDVFS_PROFILE_UNKNOWN;
|
||
|
unsigned int segment_code = 0;
|
||
|
|
||
|
#ifdef MMDVFS_USE_APMCU_CLK_MUX_SWITCH
|
||
|
segment_code = _GET_BITS_VAL_(31 : 25, get_devinfo_with_index(47));
|
||
|
#endif
|
||
|
|
||
|
#if defined(SMI_D1)
|
||
|
mmdvfs_profile_id = MMDVFS_PROFILE_D1;
|
||
|
if (segment_code == 0x41 || segment_code == 0x42 ||
|
||
|
segment_code == 0x43 || segment_code == 0x49 ||
|
||
|
segment_code == 0x51)
|
||
|
mmdvfs_profile_id = MMDVFS_PROFILE_D1_PLUS;
|
||
|
else
|
||
|
mmdvfs_profile_id = MMDVFS_PROFILE_D1;
|
||
|
#elif defined(SMI_D2)
|
||
|
mmdvfs_profile_id = MMDVFS_PROFILE_D2;
|
||
|
|
||
|
if (segment_code == 0x4B)
|
||
|
mmdvfs_profile_id = MMDVFS_PROFILE_D2_M_PLUS;
|
||
|
else if (segment_code == 0x53)
|
||
|
mmdvfs_profile_id = MMDVFS_PROFILE_D2_P_PLUS;
|
||
|
else
|
||
|
mmdvfs_profile_id = MMDVFS_PROFILE_D2;
|
||
|
#elif defined(SMI_D3)
|
||
|
mmdvfs_profile_id = MMDVFS_PROFILE_D3;
|
||
|
#elif defined(SMI_J)
|
||
|
mmdvfs_profile_id = MMDVFS_PROFILE_J1;
|
||
|
#elif defined(SMI_EV)
|
||
|
mmdvfs_profile_id = MMDVFS_PROFILE_E1;
|
||
|
#elif defined(SMI_WHI)
|
||
|
segment_code = mt_get_chip_sw_ver();
|
||
|
if (segment_code >= CHIP_SW_VER_02)
|
||
|
mmdvfs_profile_id = MMDVFS_PROFILE_WHY2;
|
||
|
else
|
||
|
mmdvfs_profile_id = MMDVFS_PROFILE_WHY;
|
||
|
#elif defined(SMI_ALA)
|
||
|
mmdvfs_profile_id = MMDVFS_PROFILE_ALA;
|
||
|
#elif defined(SMI_BIA)
|
||
|
mmdvfs_profile_id = MMDVFS_PROFILE_BIA;
|
||
|
#elif defined(SMI_VIN)
|
||
|
mmdvfs_profile_id = MMDVFS_PROFILE_VIN;
|
||
|
#elif defined(SMI_ZIO)
|
||
|
mmdvfs_profile_id = MMDVFS_PROFILE_ZIO;
|
||
|
#elif defined(SMI_MER)
|
||
|
mmdvfs_profile_id = MMDVFS_PROFILE_MER;
|
||
|
#elif defined(SMI_SYL)
|
||
|
mmdvfs_profile_id = MMDVFS_PROFILE_SYL;
|
||
|
#elif defined(SMI_CAN)
|
||
|
mmdvfs_profile_id = MMDVFS_PROFILE_CAN;
|
||
|
#endif
|
||
|
|
||
|
MMDVFSDEBUG(4, "Segment_code=%d,mmdvfs_profile_id=%d\n", segment_code,
|
||
|
mmdvfs_profile_id);
|
||
|
return mmdvfs_profile_id;
|
||
|
|
||
|
}
|
||
|
static unsigned int mmdvfs_debug_level;
|
||
|
static unsigned int mmdvfs_scen_log_mask = 1 << MMDVFS_SCEN_COUNT;
|
||
|
|
||
|
/* Record MMDVFS debug level */
|
||
|
unsigned int *g_mmdvfs_debug_level = &mmdvfs_debug_level;
|
||
|
unsigned int *g_mmdvfs_scen_log_mask = &mmdvfs_scen_log_mask;
|
||
|
module_param_named(mmdvfs_debug_level, mmdvfs_debug_level,
|
||
|
uint, 0644);
|
||
|
module_param_named(mmdvfs_scen_log_mask, mmdvfs_scen_log_mask,
|
||
|
uint, 0644);
|
||
|
module_param_named(disable_mmdvfs, disable_mmdvfs,
|
||
|
uint, 0644);
|
||
|
module_param_named(disable_freq_hopping, disable_freq_hopping,
|
||
|
uint, 0644);
|
||
|
module_param_named(disable_freq_mux, disable_freq_mux,
|
||
|
uint, 0644);
|
||
|
module_param_named(force_max_mmsys_clk, force_max_mmsys_clk,
|
||
|
uint, 0644);
|
||
|
module_param_named(force_always_on_mm_clks_mask, force_always_on_mm_clks_mask,
|
||
|
uint, 0644);
|