msm: camera: isp: Add BW voting path for SFE HW
Add register, start, stop and bw update/control functions. CRs-Fixed: 2783797 Change-Id: I587e6eca66ee9d7316f2602d60bfc77b9f8704cf Signed-off-by: Mukund Madhusudan Atre <matre@codeaurora.org> Signed-off-by: Karthik Anantha Ram <kartanan@codeaurora.org>
Dieser Commit ist enthalten in:

committet von
Gerrit - the friendly Code Review server

Ursprung
e4b180abaf
Commit
9f092f1d49
@@ -96,6 +96,22 @@ const char *cam_cpas_axi_util_path_type_to_string(
|
||||
case CAM_AXI_PATH_DATA_OPE_WR_REF:
|
||||
return "OPE_WR_REF";
|
||||
|
||||
/* SFE Paths */
|
||||
case CAM_AXI_PATH_DATA_SFE_NRDI:
|
||||
return "SFE_NRDI";
|
||||
case CAM_AXI_PATH_DATA_SFE_RDI0:
|
||||
return "IFE_RDI0";
|
||||
case CAM_AXI_PATH_DATA_SFE_RDI1:
|
||||
return "IFE_RDI1";
|
||||
case CAM_AXI_PATH_DATA_SFE_RDI2:
|
||||
return "IFE_RDI2";
|
||||
case CAM_AXI_PATH_DATA_SFE_RDI3:
|
||||
return "IFE_RDI3";
|
||||
case CAM_AXI_PATH_DATA_SFE_RDI4:
|
||||
return "IFE_RDI4";
|
||||
case CAM_AXI_PATH_DATA_SFE_STATS:
|
||||
return "SFE_STATS";
|
||||
|
||||
/* Common Paths */
|
||||
case CAM_AXI_PATH_DATA_ALL:
|
||||
return "DATA_ALL";
|
||||
|
@@ -3472,8 +3472,8 @@ static int cam_isp_classify_vote_info(
|
||||
struct cam_isp_bw_config_v2 *bw_config,
|
||||
struct cam_axi_vote *isp_vote,
|
||||
uint32_t split_idx,
|
||||
bool *camif_l_bw_updated,
|
||||
bool *camif_r_bw_updated)
|
||||
bool *nrdi_l_bw_updated,
|
||||
bool *nrdi_r_bw_updated)
|
||||
{
|
||||
int rc = 0, i, j = 0;
|
||||
|
||||
@@ -3482,7 +3482,7 @@ static int cam_isp_classify_vote_info(
|
||||
(hw_mgr_res->res_id == CAM_ISP_HW_VFE_IN_PDLIB) ||
|
||||
(hw_mgr_res->res_id == CAM_ISP_HW_VFE_IN_LCR)) {
|
||||
if (split_idx == CAM_ISP_HW_SPLIT_LEFT) {
|
||||
if (*camif_l_bw_updated)
|
||||
if (*nrdi_l_bw_updated)
|
||||
return rc;
|
||||
|
||||
for (i = 0; i < bw_config->num_paths; i++) {
|
||||
@@ -3497,9 +3497,9 @@ static int cam_isp_classify_vote_info(
|
||||
}
|
||||
isp_vote->num_paths = j;
|
||||
|
||||
*camif_l_bw_updated = true;
|
||||
*nrdi_l_bw_updated = true;
|
||||
} else {
|
||||
if (*camif_r_bw_updated)
|
||||
if (*nrdi_r_bw_updated)
|
||||
return rc;
|
||||
|
||||
for (i = 0; i < bw_config->num_paths; i++) {
|
||||
@@ -3514,7 +3514,7 @@ static int cam_isp_classify_vote_info(
|
||||
}
|
||||
isp_vote->num_paths = j;
|
||||
|
||||
*camif_r_bw_updated = true;
|
||||
*nrdi_r_bw_updated = true;
|
||||
}
|
||||
} else if ((hw_mgr_res->res_id >= CAM_ISP_HW_VFE_IN_RDI0)
|
||||
&& (hw_mgr_res->res_id <=
|
||||
@@ -3570,8 +3570,8 @@ static int cam_isp_blob_bw_update_v2(
|
||||
struct cam_vfe_bw_update_args_v2 bw_upd_args;
|
||||
int rc = -EINVAL;
|
||||
uint32_t i, split_idx;
|
||||
bool camif_l_bw_updated = false;
|
||||
bool camif_r_bw_updated = false;
|
||||
bool nrdi_l_bw_updated = false;
|
||||
bool nrdi_r_bw_updated = false;
|
||||
|
||||
for (i = 0; i < bw_config->num_paths; i++) {
|
||||
CAM_DBG(CAM_PERF,
|
||||
@@ -3598,7 +3598,7 @@ static int cam_isp_blob_bw_update_v2(
|
||||
sizeof(struct cam_axi_vote));
|
||||
rc = cam_isp_classify_vote_info(hw_mgr_res, bw_config,
|
||||
&bw_upd_args.isp_vote, split_idx,
|
||||
&camif_l_bw_updated, &camif_r_bw_updated);
|
||||
&nrdi_l_bw_updated, &nrdi_r_bw_updated);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
@@ -21,7 +21,7 @@
|
||||
#define CAM_TFE_RDI_NUM_MAX 3
|
||||
|
||||
/* Appliacble vote paths for dual ife, based on no. of UAPI definitions */
|
||||
#define CAM_ISP_MAX_PER_PATH_VOTES 30
|
||||
#define CAM_ISP_MAX_PER_PATH_VOTES 40
|
||||
|
||||
/**
|
||||
* enum cam_isp_hw_event_type - Collection of the ISP hardware events
|
||||
|
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "cam_isp_hw.h"
|
||||
#include "cam_isp_hw_mgr_intf.h"
|
||||
#include "cam_cpas_api.h"
|
||||
|
||||
#define SFE_CORE_BASE_IDX 0
|
||||
#define SFE_RT_CDM_BASE_IDX 1
|
||||
@@ -54,6 +55,28 @@ enum cam_sfe_bus_rd_irq_regs {
|
||||
CAM_SFE_BUS_RD_IRQ_REGISTERS_MAX,
|
||||
};
|
||||
|
||||
/*
|
||||
* struct cam_sfe_bw_control_args:
|
||||
*
|
||||
* @node_res: Resource node info
|
||||
* @action: Bandwidth control action
|
||||
*/
|
||||
struct cam_sfe_bw_control_args {
|
||||
struct cam_isp_resource_node *node_res;
|
||||
enum cam_sfe_bw_control_action action;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct cam_sfe_bw_update_args:
|
||||
*
|
||||
* @node_res: Resource to get the BW
|
||||
* @sfe_vote: Vote info according to usage data (left/right/rdi)
|
||||
*/
|
||||
struct cam_sfe_bw_update_args {
|
||||
struct cam_isp_resource_node *node_res;
|
||||
struct cam_axi_vote sfe_vote;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct cam_sfe_fe_update_args:
|
||||
*
|
||||
|
@@ -157,7 +157,7 @@ int cam_sfe_enable_soc_resources(struct cam_hw_soc_info *soc_info)
|
||||
axi_vote.axi_path[0].mnoc_ib_bw = 10640000000L;
|
||||
|
||||
rc = cam_cpas_start(soc_private->cpas_handle,
|
||||
&ahb_vote, &axi_vote);
|
||||
&ahb_vote, &axi_vote);
|
||||
if (rc) {
|
||||
CAM_ERR(CAM_SFE, "CPAS start failed rc=%d", rc);
|
||||
rc = -EFAULT;
|
||||
|
@@ -13,6 +13,8 @@
|
||||
#include "cam_sfe_soc.h"
|
||||
#include "cam_sfe_core.h"
|
||||
|
||||
#define CAM_SFE_DELAY_BW_REDUCTION_NUM_FRAMES 18
|
||||
|
||||
struct cam_sfe_core_cfg {
|
||||
uint32_t mode_sel;
|
||||
uint32_t ops_mode_cfg;
|
||||
@@ -41,6 +43,7 @@ struct cam_sfe_top_priv {
|
||||
CAM_SFE_DELAY_BW_REDUCTION_NUM_FRAMES];
|
||||
enum cam_sfe_bw_control_action axi_vote_control[
|
||||
CAM_SFE_TOP_IN_PORT_MAX];
|
||||
struct cam_axi_vote applied_axi_vote;
|
||||
struct cam_sfe_core_cfg core_cfg;
|
||||
uint32_t sfe_debug_cfg;
|
||||
spinlock_t spin_lock;
|
||||
@@ -82,6 +85,279 @@ static const char *cam_sfe_top_res_id_to_string(
|
||||
}
|
||||
}
|
||||
|
||||
static struct cam_axi_vote *cam_sfe_top_delay_bw_reduction(
|
||||
struct cam_sfe_top_priv *top_priv,
|
||||
uint64_t *to_be_applied_bw)
|
||||
{
|
||||
uint32_t i, j;
|
||||
int vote_idx = -1;
|
||||
uint64_t max_bw = 0;
|
||||
uint64_t total_bw;
|
||||
struct cam_axi_vote *curr_l_vote;
|
||||
|
||||
for (i = 0; i < CAM_SFE_DELAY_BW_REDUCTION_NUM_FRAMES; i++) {
|
||||
total_bw = 0;
|
||||
curr_l_vote = &top_priv->last_vote[i];
|
||||
for (j = 0; j < curr_l_vote->num_paths; j++) {
|
||||
if (total_bw >
|
||||
(U64_MAX -
|
||||
curr_l_vote->axi_path[j].camnoc_bw)) {
|
||||
CAM_ERR(CAM_PERF,
|
||||
"sfe[%d] : Integer overflow at hist idx: %d, path: %d, total_bw = %llu, camnoc_bw = %llu",
|
||||
top_priv->common_data.hw_intf->hw_idx,
|
||||
i, j, total_bw,
|
||||
curr_l_vote->axi_path[j].camnoc_bw);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
total_bw += curr_l_vote->axi_path[j].camnoc_bw;
|
||||
}
|
||||
|
||||
if (total_bw > max_bw) {
|
||||
vote_idx = i;
|
||||
max_bw = total_bw;
|
||||
}
|
||||
}
|
||||
|
||||
if (vote_idx < 0)
|
||||
return NULL;
|
||||
|
||||
*to_be_applied_bw = max_bw;
|
||||
|
||||
return &top_priv->last_vote[vote_idx];
|
||||
}
|
||||
|
||||
int cam_sfe_top_set_axi_bw_vote(struct cam_sfe_soc_private *soc_private,
|
||||
struct cam_sfe_top_priv *top_priv, bool start_stop)
|
||||
{
|
||||
struct cam_axi_vote agg_vote = {0};
|
||||
struct cam_axi_vote *to_be_applied_axi_vote = NULL;
|
||||
int rc = 0;
|
||||
uint32_t i;
|
||||
uint32_t num_paths = 0;
|
||||
uint64_t total_bw_new_vote = 0;
|
||||
bool bw_unchanged = true;
|
||||
bool apply_bw_update = false;
|
||||
|
||||
for (i = 0; i < top_priv->num_in_ports; i++) {
|
||||
if (top_priv->axi_vote_control[i] ==
|
||||
CAM_SFE_BW_CONTROL_INCLUDE) {
|
||||
if (num_paths +
|
||||
top_priv->req_axi_vote[i].num_paths >
|
||||
CAM_CPAS_MAX_PATHS_PER_CLIENT) {
|
||||
CAM_ERR(CAM_PERF,
|
||||
"Required paths(%d) more than max(%d)",
|
||||
num_paths +
|
||||
top_priv->req_axi_vote[i].num_paths,
|
||||
CAM_CPAS_MAX_PATHS_PER_CLIENT);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(&agg_vote.axi_path[num_paths],
|
||||
&top_priv->req_axi_vote[i].axi_path[0],
|
||||
top_priv->req_axi_vote[i].num_paths *
|
||||
sizeof(
|
||||
struct cam_axi_per_path_bw_vote));
|
||||
num_paths += top_priv->req_axi_vote[i].num_paths;
|
||||
}
|
||||
}
|
||||
|
||||
agg_vote.num_paths = num_paths;
|
||||
|
||||
for (i = 0; i < agg_vote.num_paths; i++) {
|
||||
CAM_DBG(CAM_PERF,
|
||||
"sfe[%d] : New BW Vote : counter[%d] [%s][%s] [%llu %llu %llu]",
|
||||
top_priv->common_data.hw_intf->hw_idx,
|
||||
top_priv->last_counter,
|
||||
cam_cpas_axi_util_path_type_to_string(
|
||||
agg_vote.axi_path[i].path_data_type),
|
||||
cam_cpas_axi_util_trans_type_to_string(
|
||||
agg_vote.axi_path[i].transac_type),
|
||||
agg_vote.axi_path[i].camnoc_bw,
|
||||
agg_vote.axi_path[i].mnoc_ab_bw,
|
||||
agg_vote.axi_path[i].mnoc_ib_bw);
|
||||
|
||||
total_bw_new_vote += agg_vote.axi_path[i].camnoc_bw;
|
||||
}
|
||||
|
||||
memcpy(&top_priv->last_vote[top_priv->last_counter], &agg_vote,
|
||||
sizeof(struct cam_axi_vote));
|
||||
top_priv->last_counter = (top_priv->last_counter + 1) %
|
||||
CAM_SFE_DELAY_BW_REDUCTION_NUM_FRAMES;
|
||||
|
||||
if ((agg_vote.num_paths != top_priv->applied_axi_vote.num_paths) ||
|
||||
(total_bw_new_vote != top_priv->total_bw_applied))
|
||||
bw_unchanged = false;
|
||||
|
||||
CAM_DBG(CAM_PERF,
|
||||
"applied_total=%lld, new_total=%lld unchanged=%d, start_stop=%d",
|
||||
top_priv->total_bw_applied,
|
||||
total_bw_new_vote, bw_unchanged, start_stop);
|
||||
|
||||
if (bw_unchanged) {
|
||||
CAM_DBG(CAM_PERF, "BW config unchanged");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (start_stop) {
|
||||
/* need to vote current request immediately */
|
||||
to_be_applied_axi_vote = &agg_vote;
|
||||
/* Reset everything, we can start afresh */
|
||||
memset(top_priv->last_vote, 0x0, sizeof(struct cam_axi_vote) *
|
||||
CAM_SFE_DELAY_BW_REDUCTION_NUM_FRAMES);
|
||||
top_priv->last_counter = 0;
|
||||
top_priv->last_vote[top_priv->last_counter] = agg_vote;
|
||||
top_priv->last_counter = (top_priv->last_counter + 1) %
|
||||
CAM_SFE_DELAY_BW_REDUCTION_NUM_FRAMES;
|
||||
} else {
|
||||
/*
|
||||
* Find max bw request in last few frames. This will the bw
|
||||
* that we want to vote to CPAS now.
|
||||
*/
|
||||
to_be_applied_axi_vote =
|
||||
cam_sfe_top_delay_bw_reduction(top_priv,
|
||||
&total_bw_new_vote);
|
||||
if (!to_be_applied_axi_vote) {
|
||||
CAM_ERR(CAM_PERF, "to_be_applied_axi_vote is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < to_be_applied_axi_vote->num_paths; i++) {
|
||||
CAM_DBG(CAM_PERF,
|
||||
"sfe[%d] : Apply BW Vote : [%s][%s] [%llu %llu %llu]",
|
||||
top_priv->common_data.hw_intf->hw_idx,
|
||||
cam_cpas_axi_util_path_type_to_string(
|
||||
to_be_applied_axi_vote->axi_path[i].path_data_type),
|
||||
cam_cpas_axi_util_trans_type_to_string(
|
||||
to_be_applied_axi_vote->axi_path[i].transac_type),
|
||||
to_be_applied_axi_vote->axi_path[i].camnoc_bw,
|
||||
to_be_applied_axi_vote->axi_path[i].mnoc_ab_bw,
|
||||
to_be_applied_axi_vote->axi_path[i].mnoc_ib_bw);
|
||||
}
|
||||
|
||||
if ((to_be_applied_axi_vote->num_paths !=
|
||||
top_priv->applied_axi_vote.num_paths) ||
|
||||
(total_bw_new_vote != top_priv->total_bw_applied))
|
||||
apply_bw_update = true;
|
||||
|
||||
CAM_DBG(CAM_PERF,
|
||||
"sfe[%d] : Delayed update: applied_total=%lld, new_total=%lld apply_bw_update=%d, start_stop=%d",
|
||||
top_priv->common_data.hw_intf->hw_idx,
|
||||
top_priv->total_bw_applied, total_bw_new_vote, apply_bw_update,
|
||||
start_stop);
|
||||
|
||||
if (apply_bw_update) {
|
||||
rc = cam_cpas_update_axi_vote(soc_private->cpas_handle,
|
||||
to_be_applied_axi_vote);
|
||||
if (!rc) {
|
||||
memcpy(&top_priv->applied_axi_vote,
|
||||
to_be_applied_axi_vote,
|
||||
sizeof(struct cam_axi_vote));
|
||||
top_priv->total_bw_applied = total_bw_new_vote;
|
||||
} else {
|
||||
CAM_ERR(CAM_PERF, "BW request failed, rc=%d", rc);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int cam_sfe_top_bw_update(struct cam_sfe_soc_private *soc_private,
|
||||
struct cam_sfe_top_priv *top_priv, void *cmd_args,
|
||||
uint32_t arg_size)
|
||||
{
|
||||
struct cam_sfe_bw_update_args *bw_update = NULL;
|
||||
struct cam_isp_resource_node *res = NULL;
|
||||
struct cam_hw_info *hw_info = NULL;
|
||||
int rc = 0;
|
||||
int i;
|
||||
|
||||
bw_update = (struct cam_sfe_bw_update_args *)cmd_args;
|
||||
res = bw_update->node_res;
|
||||
|
||||
if (!res || !res->hw_intf || !res->hw_intf->hw_priv)
|
||||
return -EINVAL;
|
||||
|
||||
hw_info = res->hw_intf->hw_priv;
|
||||
|
||||
if (res->res_type != CAM_ISP_RESOURCE_SFE_IN ||
|
||||
res->res_id >= CAM_ISP_HW_SFE_IN_MAX) {
|
||||
CAM_ERR(CAM_SFE, "SFE:%d Invalid res_type:%d res id%d",
|
||||
res->hw_intf->hw_idx, res->res_type,
|
||||
res->res_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < top_priv->num_in_ports; i++) {
|
||||
if (top_priv->in_rsrc[i].res_id == res->res_id) {
|
||||
memcpy(&top_priv->req_axi_vote[i],
|
||||
&bw_update->sfe_vote,
|
||||
sizeof(struct cam_axi_vote));
|
||||
top_priv->axi_vote_control[i] =
|
||||
CAM_SFE_BW_CONTROL_INCLUDE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hw_info->hw_state != CAM_HW_STATE_POWER_UP) {
|
||||
CAM_ERR_RATE_LIMIT(CAM_SFE,
|
||||
"SFE:%d Not ready to set BW yet :%d",
|
||||
res->hw_intf->hw_idx,
|
||||
hw_info->hw_state);
|
||||
} else {
|
||||
rc = cam_sfe_top_set_axi_bw_vote(soc_private, top_priv,
|
||||
false);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int cam_sfe_top_bw_control(struct cam_sfe_soc_private *soc_private,
|
||||
struct cam_sfe_top_priv *top_priv, void *cmd_args,
|
||||
uint32_t arg_size)
|
||||
{
|
||||
struct cam_sfe_bw_control_args *bw_ctrl = NULL;
|
||||
struct cam_isp_resource_node *res = NULL;
|
||||
struct cam_hw_info *hw_info = NULL;
|
||||
int rc = 0;
|
||||
int i;
|
||||
|
||||
bw_ctrl = (struct cam_sfe_bw_control_args *)cmd_args;
|
||||
res = bw_ctrl->node_res;
|
||||
|
||||
if (!res || !res->hw_intf->hw_priv)
|
||||
return -EINVAL;
|
||||
|
||||
hw_info = res->hw_intf->hw_priv;
|
||||
|
||||
if (res->res_type != CAM_ISP_RESOURCE_SFE_IN ||
|
||||
res->res_id >= CAM_ISP_HW_SFE_IN_MAX) {
|
||||
CAM_ERR(CAM_SFE, "SFE:%d Invalid res_type:%d res id%d",
|
||||
res->hw_intf->hw_idx, res->res_type,
|
||||
res->res_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < top_priv->num_in_ports; i++) {
|
||||
if (top_priv->in_rsrc[i].res_id == res->res_id) {
|
||||
top_priv->axi_vote_control[i] = bw_ctrl->action;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hw_info->hw_state != CAM_HW_STATE_POWER_UP) {
|
||||
CAM_ERR_RATE_LIMIT(CAM_SFE,
|
||||
"SFE:%d Not ready to set BW yet :%d",
|
||||
res->hw_intf->hw_idx,
|
||||
hw_info->hw_state);
|
||||
} else {
|
||||
rc = cam_sfe_top_set_axi_bw_vote(soc_private, top_priv, true);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cam_sfe_top_core_cfg(
|
||||
struct cam_sfe_top_priv *top_priv,
|
||||
void *cmd_args, uint32_t arg_size)
|
||||
@@ -292,6 +568,8 @@ int cam_sfe_top_process_cmd(void *priv, uint32_t cmd_type,
|
||||
{
|
||||
int rc = 0;
|
||||
struct cam_sfe_top_priv *top_priv;
|
||||
struct cam_hw_soc_info *soc_info = NULL;
|
||||
struct cam_sfe_soc_private *soc_private = NULL;
|
||||
|
||||
if (!priv) {
|
||||
CAM_ERR(CAM_SFE, "Invalid top_priv");
|
||||
@@ -299,6 +577,13 @@ int cam_sfe_top_process_cmd(void *priv, uint32_t cmd_type,
|
||||
}
|
||||
|
||||
top_priv = (struct cam_sfe_top_priv *) priv;
|
||||
soc_info = top_priv->common_data.soc_info;
|
||||
soc_private = soc_info->soc_private;
|
||||
|
||||
if (!soc_private) {
|
||||
CAM_ERR(CAM_SFE, "soc private is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (cmd_type) {
|
||||
case CAM_ISP_HW_CMD_GET_CHANGE_BASE:
|
||||
@@ -310,6 +595,8 @@ int cam_sfe_top_process_cmd(void *priv, uint32_t cmd_type,
|
||||
arg_size);
|
||||
break;
|
||||
case CAM_ISP_HW_CMD_BW_UPDATE_V2:
|
||||
rc = cam_sfe_top_bw_update(soc_private, top_priv,
|
||||
cmd_args, arg_size);
|
||||
break;
|
||||
case CAM_ISP_HW_CMD_BW_CONTROL:
|
||||
break;
|
||||
|
In neuem Issue referenzieren
Einen Benutzer sperren