msm: camera: tfe: Add support for TFE BW limiter

Add blob to allow userland to configure BW limter for TFE WMs.

CRs-Fixed: 3505174
Change-Id: I5c671cbcfa5557082c4ae5302471a2ece45294ab
Signed-off-by: Dharmender Sharma <quic_dharshar@quicinc.com>
Signed-off-by: Yash Upadhyay <quic_yupadhya@quicinc.com>
This commit is contained in:
Yash Upadhyay
2023-06-12 14:41:38 +05:30
committed by Camera Software Integration
parent 033efcbed0
commit c98bea4b04
8 changed files with 329 additions and 15 deletions

View File

@@ -3887,6 +3887,137 @@ static int cam_isp_tfe_blob_clock_update(
return rc;
}
static int cam_isp_tfe_blob_bw_limit_update(
uint32_t blob_type,
struct cam_isp_generic_blob_info *blob_info,
struct cam_isp_tfe_out_rsrc_bw_limiter_config *bw_limit_cfg,
struct cam_hw_prepare_update_args *prepare,
enum cam_isp_hw_type hw_type)
{
struct cam_isp_tfe_wm_bw_limiter_config *wm_bw_limit_cfg;
struct cam_kmd_buf_info *kmd_buf_info;
struct cam_tfe_hw_mgr_ctx *ctx = NULL;
struct cam_isp_hw_mgr_res *hw_mgr_res;
uint32_t res_id_out, i;
uint32_t total_used_bytes = 0;
uint32_t kmd_buf_remain_size;
uint32_t *cmd_buf_addr;
uint32_t bytes_used = 0;
int num_ent, rc = 0;
ctx = prepare->ctxt_to_hw_map;
if ((prepare->num_hw_update_entries + 1) >=
prepare->max_hw_update_entries) {
CAM_ERR(CAM_ISP, "Insufficient HW entries: %d max: %d",
prepare->num_hw_update_entries,
prepare->max_hw_update_entries);
return -EINVAL;
}
kmd_buf_info = blob_info->kmd_buf_info;
for (i = 0; i < bw_limit_cfg->num_ports; i++) {
wm_bw_limit_cfg = &bw_limit_cfg->bw_limiter_config[i];
res_id_out = wm_bw_limit_cfg->res_type & 0xFF;
CAM_DBG(CAM_ISP, "%s BW limit config idx: %d port: 0x%x enable: %d [0x%x:0x%x]",
"TFE", i, wm_bw_limit_cfg->res_type, wm_bw_limit_cfg->enable_limiter,
wm_bw_limit_cfg->counter_limit[0], wm_bw_limit_cfg->counter_limit[1]);
if ((kmd_buf_info->used_bytes
+ total_used_bytes) < kmd_buf_info->size) {
kmd_buf_remain_size = kmd_buf_info->size -
(kmd_buf_info->used_bytes +
total_used_bytes);
} else {
CAM_ERR(CAM_ISP,
"No free kmd memory for base idx: %d",
blob_info->base_info->idx);
rc = -ENOMEM;
return rc;
}
cmd_buf_addr = kmd_buf_info->cpu_addr +
(kmd_buf_info->used_bytes / 4) +
(total_used_bytes / 4);
hw_mgr_res = &ctx->res_list_tfe_out[res_id_out];
rc = cam_isp_add_cmd_buf_update(
hw_mgr_res, blob_type,
CAM_ISP_HW_CMD_WM_BW_LIMIT_CONFIG,
blob_info->base_info->idx,
(void *)cmd_buf_addr,
kmd_buf_remain_size,
(void *)wm_bw_limit_cfg,
&bytes_used);
if (rc < 0) {
CAM_ERR(CAM_ISP,
"Failed to update %s BW limiter config for res:0x%x enable:%d [0x%x:0x%x] base_idx:%d bytes_used:%u rc:%d",
"VFE", wm_bw_limit_cfg->res_type, wm_bw_limit_cfg->enable_limiter,
wm_bw_limit_cfg->counter_limit[0],
wm_bw_limit_cfg->counter_limit[1],
blob_info->base_info->idx, bytes_used, rc);
return rc;
}
total_used_bytes += bytes_used;
}
if (total_used_bytes) {
/* Update the HW entries */
num_ent = prepare->num_hw_update_entries;
prepare->hw_update_entries[num_ent].handle =
kmd_buf_info->handle;
prepare->hw_update_entries[num_ent].len = total_used_bytes;
prepare->hw_update_entries[num_ent].offset =
kmd_buf_info->offset;
num_ent++;
kmd_buf_info->used_bytes += total_used_bytes;
kmd_buf_info->offset += total_used_bytes;
prepare->num_hw_update_entries = num_ent;
}
return rc;
}
static inline int cam_isp_tfe_validate_bw_limiter_blob(
uint32_t blob_size,
struct cam_isp_tfe_out_rsrc_bw_limiter_config *bw_limit_config)
{
/* Check for blob version */
if (bw_limit_config->version != CAM_TFE_BW_LIMITER_CONFIG_V1) {
CAM_ERR(CAM_ISP, "Invalid Blob config version:%d", bw_limit_config->version);
return -EINVAL;
}
/* Check for number of out ports*/
if (bw_limit_config->num_ports > CAM_TFE_HW_OUT_RES_MAX) {
CAM_ERR(CAM_ISP, "Invalid num_ports:%u", bw_limit_config->num_ports);
return -EINVAL;
}
/* Check for integer overflow */
if (bw_limit_config->num_ports != 1) {
if (sizeof(struct cam_isp_tfe_wm_bw_limiter_config) > ((UINT_MAX -
sizeof(struct cam_isp_tfe_out_rsrc_bw_limiter_config)) /
(bw_limit_config->num_ports - 1))) {
CAM_ERR(CAM_ISP,
"Max size exceeded in bw limit config num_ports:%u size per port:%lu",
bw_limit_config->num_ports,
sizeof(struct cam_isp_tfe_wm_bw_limiter_config));
return -EINVAL;
}
}
if (blob_size < (sizeof(struct cam_isp_tfe_out_rsrc_bw_limiter_config) +
(bw_limit_config->num_ports - 1) *
sizeof(struct cam_isp_tfe_wm_bw_limiter_config))) {
CAM_ERR(CAM_ISP, "Invalid blob size %u expected %lu",
blob_size, sizeof(struct cam_isp_tfe_out_rsrc_bw_limiter_config)
+ (bw_limit_config->num_ports - 1) *
sizeof(struct cam_isp_tfe_wm_bw_limiter_config));
return -EINVAL;
}
return 0;
}
static int cam_isp_tfe_packet_generic_blob_handler(void *user_data,
uint32_t blob_type, uint32_t blob_size, uint8_t *blob_data)
{
@@ -4092,6 +4223,28 @@ static int cam_isp_tfe_packet_generic_blob_handler(void *user_data,
CAM_ERR(CAM_ISP, "Clock Update Failed");
}
break;
case CAM_ISP_TFE_GENERIC_BLOB_TYPE_BW_LIMITER_CFG: {
struct cam_isp_tfe_out_rsrc_bw_limiter_config *bw_limit_config;
if (blob_size <
sizeof(struct cam_isp_tfe_out_rsrc_bw_limiter_config)) {
CAM_ERR(CAM_ISP, "Invalid blob size %u expected %lu",
blob_size,
sizeof(struct cam_isp_tfe_out_rsrc_bw_limiter_config));
return -EINVAL;
}
bw_limit_config = (struct cam_isp_tfe_out_rsrc_bw_limiter_config *)blob_data;
rc = cam_isp_tfe_validate_bw_limiter_blob(blob_size, bw_limit_config);
if (rc)
return rc;
rc = cam_isp_tfe_blob_bw_limit_update(blob_type, blob_info,
bw_limit_config, prepare, CAM_ISP_HW_TYPE_TFE);
if (rc)
CAM_ERR(CAM_ISP, "BW limit update failed for TFE rc: %d", rc);
}
break;
default:
CAM_WARN(CAM_ISP, "Invalid blob type %d", blob_type);
break;

View File

@@ -16,19 +16,20 @@
#include "cam_cpas_api.h"
/* MAX IFE instance */
#define CAM_IFE_HW_NUM_MAX 8
#define CAM_SFE_HW_NUM_MAX 3
#define CAM_IFE_RDI_NUM_MAX 4
#define CAM_SFE_RDI_NUM_MAX 5
#define CAM_SFE_FE_RDI_NUM_MAX 3
#define CAM_ISP_BW_CONFIG_V1 1
#define CAM_ISP_BW_CONFIG_V2 2
#define CAM_ISP_BW_CONFIG_V3 2
#define CAM_TFE_HW_NUM_MAX 3
#define CAM_TFE_RDI_NUM_MAX 3
#define CAM_IFE_SCRATCH_NUM_MAX 2
#define CAM_IFE_BUS_COMP_NUM_MAX 18
#define CAM_SFE_BUS_COMP_NUM_MAX 12
#define CAM_IFE_HW_NUM_MAX 8
#define CAM_SFE_HW_NUM_MAX 3
#define CAM_IFE_RDI_NUM_MAX 4
#define CAM_SFE_RDI_NUM_MAX 5
#define CAM_SFE_FE_RDI_NUM_MAX 3
#define CAM_ISP_BW_CONFIG_V1 1
#define CAM_ISP_BW_CONFIG_V2 2
#define CAM_ISP_BW_CONFIG_V3 2
#define CAM_TFE_HW_NUM_MAX 3
#define CAM_TFE_RDI_NUM_MAX 3
#define CAM_IFE_SCRATCH_NUM_MAX 2
#define CAM_IFE_BUS_COMP_NUM_MAX 18
#define CAM_SFE_BUS_COMP_NUM_MAX 12
#define CAM_TFE_BW_LIMITER_CONFIG_V1 1
/* maximum context numbers for TFE */
#define CAM_TFE_CTX_MAX 4

View File

@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
@@ -855,6 +856,9 @@ static struct cam_tfe_bus_hw_info tfe530_bus_hw_info = {
.support_consumed_addr = true,
.pdaf_rdi2_mux_en = true,
.rdi_width = 64,
.max_bw_counter_limit = 0xFF,
.counter_limit_shift = 1,
.counter_limit_mask = 0xF,
};
struct cam_tfe_hw_info cam_tfe530 = {

View File

@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
@@ -1170,6 +1171,9 @@ static struct cam_tfe_bus_hw_info tfe640_bus_hw_info = {
.support_consumed_addr = true,
.pdaf_rdi2_mux_en = false,
.rdi_width = 128,
.max_bw_counter_limit = 0xFF,
.counter_limit_shift = 1,
.counter_limit_mask = 0xF,
};
struct cam_tfe_hw_info cam_tfe640 = {

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/ratelimit.h>
@@ -75,6 +75,9 @@ struct cam_tfe_bus_common_data {
cam_hw_mgr_event_cb_func event_cb;
bool rup_irq_enable[CAM_TFE_BUS_RUP_GRP_MAX];
bool pdaf_rdi2_mux_en;
uint32_t max_bw_counter_limit;
uint32_t counter_limit_shift;
uint32_t counter_limit_mask;
};
struct cam_tfe_bus_wm_resource_data {
@@ -104,6 +107,7 @@ struct cam_tfe_bus_wm_resource_data {
uint32_t acquired_width;
uint32_t acquired_height;
uint32_t acquired_stride;
bool limiter_blob_status;
};
struct cam_tfe_bus_comp_grp_data {
@@ -848,6 +852,7 @@ static int cam_tfe_bus_release_wm(void *bus_priv,
rsrc_data->framedrop_pattern = 0;
rsrc_data->en_cfg = 0;
rsrc_data->is_dual = 0;
rsrc_data->limiter_blob_status = false;
wm_res->tasklet_info = NULL;
wm_res->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE;
@@ -865,7 +870,10 @@ static int cam_tfe_bus_start_wm(struct cam_isp_resource_node *wm_res)
struct cam_tfe_bus_common_data *common_data =
rsrc_data->common_data;
cam_io_w(0xf, common_data->mem_base + rsrc_data->hw_regs->bw_limit);
/* Skip to overwrite if wm bandwidth limiter blob already sent */
if (!rsrc_data->limiter_blob_status)
cam_io_w(rsrc_data->common_data->counter_limit_mask,
common_data->mem_base + rsrc_data->hw_regs->bw_limit);
cam_io_w((rsrc_data->height << 16) | rsrc_data->width,
common_data->mem_base + rsrc_data->hw_regs->image_cfg_0);
@@ -2309,6 +2317,104 @@ end:
return 0;
}
static int cam_tfe_bus_update_bw_limiter(
void *priv, void *cmd_args, uint32_t arg_size)
{
struct cam_tfe_bus_priv *bus_priv;
struct cam_isp_hw_get_cmd_update *wm_config_update;
struct cam_tfe_bus_tfe_out_data *tfe_out_data = NULL;
struct cam_cdm_utils_ops *cdm_util_ops;
struct cam_tfe_bus_wm_resource_data *wm_data = NULL;
struct cam_isp_tfe_wm_bw_limiter_config *wm_bw_limit_cfg = NULL;
uint32_t counter_limit = 0, reg_val = 0;
uint32_t *reg_val_pair, num_regval_pairs = 0;
uint32_t i, j, size = 0;
bus_priv = (struct cam_tfe_bus_priv *) priv;
wm_config_update = (struct cam_isp_hw_get_cmd_update *) cmd_args;
wm_bw_limit_cfg = (struct cam_isp_tfe_wm_bw_limiter_config *) wm_config_update->data;
tfe_out_data = (struct cam_tfe_bus_tfe_out_data *) wm_config_update->res->res_priv;
if (!tfe_out_data || !tfe_out_data->cdm_util_ops) {
CAM_ERR(CAM_ISP, "Invalid data");
return -EINVAL;
}
cdm_util_ops = tfe_out_data->cdm_util_ops;
reg_val_pair = &tfe_out_data->common_data->io_buf_update[0];
for (i = 0, j = 0; i < tfe_out_data->num_wm; i++) {
if (j >= (MAX_REG_VAL_PAIR_SIZE - (MAX_BUF_UPDATE_REG_NUM * 2))) {
CAM_ERR(CAM_ISP,
"reg_val_pair %d exceeds the array limit %zu for WM idx %d",
j, MAX_REG_VAL_PAIR_SIZE, i);
return -ENOMEM;
}
/* Num WMs needs to match max planes */
if (i >= CAM_PACKET_MAX_PLANES) {
CAM_WARN(CAM_ISP, "Num of WMs: %d exceeded max planes", i);
goto add_reg_pair;
}
wm_data = (struct cam_tfe_bus_wm_resource_data *) tfe_out_data->wm_res[i]->res_priv;
if (!wm_data->hw_regs->bw_limit) {
CAM_ERR(CAM_ISP,
"WM: %d %s has no support for bw limiter",
wm_data->index, tfe_out_data->wm_res[i]->res_name);
return -EINVAL;
}
counter_limit = wm_bw_limit_cfg->counter_limit[i];
/* Validate max counter limit */
if (counter_limit > wm_data->common_data->max_bw_counter_limit) {
CAM_WARN(CAM_ISP, "Invalid counter limit: 0x%x capping to max: 0x%x",
wm_bw_limit_cfg->counter_limit[i],
wm_data->common_data->max_bw_counter_limit);
counter_limit = wm_data->common_data->max_bw_counter_limit;
}
if (wm_bw_limit_cfg->enable_limiter && counter_limit) {
reg_val = 1;
reg_val |= (counter_limit << wm_data->common_data->counter_limit_shift);
} else {
reg_val = 0;
}
wm_data->limiter_blob_status = true;
CAM_TFE_ADD_REG_VAL_PAIR(reg_val_pair, j, wm_data->hw_regs->bw_limit, reg_val);
CAM_DBG(CAM_ISP, "WM: %d for %s bw_limter: 0x%x",
wm_data->index, tfe_out_data->wm_res[i]->res_name, reg_val_pair[j-1]);
}
add_reg_pair:
num_regval_pairs = j / 2;
if (num_regval_pairs) {
size = cdm_util_ops->cdm_required_size_reg_random(num_regval_pairs);
/* cdm util returns dwords, need to convert to bytes */
if ((size * 4) > wm_config_update->cmd.size) {
CAM_ERR(CAM_ISP, "Failed! Buf size:%d insufficient, expected size:%d",
wm_config_update->cmd.size, size);
return -ENOMEM;
}
cdm_util_ops->cdm_write_regrandom(
wm_config_update->cmd.cmd_buf_addr, num_regval_pairs, reg_val_pair);
/* cdm util returns dwords, need to convert to bytes */
wm_config_update->cmd.used_bytes = size * 4;
} else {
CAM_DBG(CAM_ISP, "No reg val pairs. num_wms: %u", tfe_out_data->num_wm);
wm_config_update->cmd.used_bytes = 0;
}
return 0;
}
static int cam_tfe_bus_dump_bus_info(
struct cam_tfe_bus_priv *bus_priv,
void *cmd_args, uint32_t arg_size)
@@ -2484,6 +2590,9 @@ static int cam_tfe_bus_process_cmd(void *priv,
pdaf_rdi2_mux_en = (bool *)cmd_args;
*pdaf_rdi2_mux_en = bus_priv->common_data.pdaf_rdi2_mux_en;
break;
case CAM_ISP_HW_CMD_WM_BW_LIMIT_CONFIG:
rc = cam_tfe_bus_update_bw_limiter(priv, cmd_args, arg_size);
break;
default:
CAM_ERR_RATE_LIMIT(CAM_ISP, "Invalid camif process command:%d",
cmd_type);
@@ -2550,6 +2659,9 @@ int cam_tfe_bus_init(
hw_info->support_consumed_addr;
bus_priv->common_data.pdaf_rdi2_mux_en = hw_info->pdaf_rdi2_mux_en;
bus_priv->common_data.rdi_width = hw_info->rdi_width;
bus_priv->common_data.max_bw_counter_limit = hw_info->max_bw_counter_limit;
bus_priv->common_data.counter_limit_shift = hw_info->counter_limit_shift;
bus_priv->common_data.counter_limit_mask = hw_info->counter_limit_mask;
for (i = 0; i < CAM_TFE_BUS_IRQ_REGISTERS_MAX; i++)
bus_priv->bus_irq_error_mask[i] =

View File

@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
@@ -195,6 +196,9 @@ struct cam_tfe_bus_tfe_out_hw_info {
* @rdi_width: RDI WM width
* @support_consumed_addr: Indicate if bus support consumed address
* @pdaf_rdi2_mux_en: Indicate is PDAF is muxed with RDI2
* @max_bw_counter_limit: Max BW counter limit
* @counter_limit_shift: Mask shift for BW counter limit
* @counter_limit_mask: Default Mask of BW limit counter
*/
struct cam_tfe_bus_hw_info {
struct cam_tfe_bus_reg_offset_common common_reg;
@@ -214,6 +218,9 @@ struct cam_tfe_bus_hw_info {
uint32_t rdi_width;
bool support_consumed_addr;
bool pdaf_rdi2_mux_en;
uint32_t max_bw_counter_limit;
uint32_t counter_limit_shift;
uint32_t counter_limit_mask;
};
/*

View File

@@ -2919,6 +2919,7 @@ int cam_tfe_process_cmd(void *hw_priv, uint32_t cmd_type,
case CAM_ISP_HW_CMD_GET_RES_FOR_MID:
case CAM_ISP_HW_CMD_DUMP_BUS_INFO:
case CAM_ISP_HW_CMD_IS_PDAF_RDI2_MUX_EN:
case CAM_ISP_HW_CMD_WM_BW_LIMIT_CONFIG:
rc = core_info->tfe_bus->hw_ops.process_cmd(
core_info->tfe_bus->bus_priv, cmd_type, cmd_args,
arg_size);

View File

@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
/*
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __UAPI_CAM_TFE_H__
@@ -71,6 +72,7 @@
#define CAM_ISP_TFE_GENERIC_BLOB_TYPE_CLOCK_CONFIG 1
#define CAM_ISP_TFE_GENERIC_BLOB_TYPE_BW_CONFIG_V2 2
#define CAM_ISP_TFE_GENERIC_BLOB_TYPE_CSID_CLOCK_CONFIG 3
#define CAM_ISP_TFE_GENERIC_BLOB_TYPE_BW_LIMITER_CFG 16
/* DSP mode */
#define CAM_ISP_TFE_DSP_MODE_NONE 0
@@ -473,6 +475,36 @@ struct cam_isp_tfe_acquire_hw_info {
__u64 data;
};
/**
* struct cam_isp_tfe_wm_bw_limiter_config - ISP TFE write master
* BW limter config
*
*
* @res_type : output resource type defined in file cam_isp_tfe.h
* @enable_limiter : 0 for disable else enabled
* @counter_limit : Max counter value
*/
struct cam_isp_tfe_wm_bw_limiter_config {
__u32 res_type;
__u32 enable_limiter;
__u32 counter_limit[CAM_PACKET_MAX_PLANES];
};
/**
* struct cam_isp_tfe_out_rsrc_bw_limiter_config - ISP TFE out rsrc BW limiter config
*
* Configure BW limiter for ISP TFE WMs
*
* @version : Version field
* @num_ports : Number of ports
* @bw_limit_config : WM BW limiter config
*/
struct cam_isp_tfe_out_rsrc_bw_limiter_config {
__u32 version;
__u32 num_ports;
struct cam_isp_tfe_wm_bw_limiter_config bw_limiter_config[1];
};
#define CAM_TFE_ACQUIRE_COMMON_VER0 0x1000
#define CAM_TFE_ACQUIRE_COMMON_SIZE_VER0 0x0