Loading drivers/cam_cdm/cam_cdm_util.c +169 −1 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #include <linux/types.h> Loading Loading @@ -715,3 +715,171 @@ void cam_cdm_util_dump_cmd_buf( } } while (buf_now <= cmd_buf_end); } static uint32_t cam_cdm_util_dump_reg_cont_cmd_v2( uint32_t *cmd_buf_addr, struct cam_cdm_cmd_buf_dump_info *dump_info) { int i; long ret; uint8_t *dst; size_t remain_len; uint32_t *temp_ptr = cmd_buf_addr; uint32_t *addr, *start; uint32_t min_len; struct cdm_regcontinuous_cmd *p_regcont_cmd; struct cam_cdm_cmd_dump_header *hdr; p_regcont_cmd = (struct cdm_regcontinuous_cmd *)temp_ptr; temp_ptr += cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT); ret = cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT); min_len = (sizeof(uint32_t) * p_regcont_cmd->count) + sizeof(struct cam_cdm_cmd_dump_header) + (2 * sizeof(uint32_t)); remain_len = dump_info->dst_max_size - dump_info->dst_offset; if (remain_len < min_len) { CAM_WARN_RATE_LIMIT(CAM_CDM, "Dump buffer exhaust remain %zu min %u", remain_len, min_len); return ret; } dst = (char *)dump_info->dst_start + dump_info->dst_offset; hdr = (struct cam_cdm_cmd_dump_header *)dst; scnprintf(hdr->tag, CAM_CDM_CMD_TAG_MAX_LEN, "CDM_REG_CONT:"); hdr->word_size = sizeof(uint32_t); addr = (uint32_t *)(dst + sizeof(struct cam_cdm_cmd_dump_header)); start = addr; *addr++ = p_regcont_cmd->offset; *addr++ = p_regcont_cmd->count; for (i = 0; i < p_regcont_cmd->count; i++) { *addr = *temp_ptr; temp_ptr++; addr++; ret++; } hdr->size = hdr->word_size * (addr - start); dump_info->dst_offset += hdr->size + sizeof(struct cam_cdm_cmd_dump_header); return ret; } static uint32_t cam_cdm_util_dump_reg_random_cmd_v2( uint32_t *cmd_buf_addr, struct cam_cdm_cmd_buf_dump_info *dump_info) { int i; long ret; uint8_t *dst; uint32_t *temp_ptr = cmd_buf_addr; uint32_t *addr, *start; size_t remain_len; uint32_t min_len; struct cdm_regrandom_cmd *p_regrand_cmd; struct cam_cdm_cmd_dump_header *hdr; p_regrand_cmd = (struct cdm_regrandom_cmd *)temp_ptr; temp_ptr += cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM); ret = cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM); min_len = (2 * sizeof(uint32_t) * p_regrand_cmd->count) + sizeof(struct cam_cdm_cmd_dump_header) + sizeof(uint32_t); remain_len = dump_info->dst_max_size - dump_info->dst_offset; if (remain_len < min_len) { CAM_WARN_RATE_LIMIT(CAM_CDM, "Dump buffer exhaust remain %zu min %u", remain_len, min_len); return ret; } dst = (char *)dump_info->dst_start + dump_info->dst_offset; hdr = (struct cam_cdm_cmd_dump_header *)dst; scnprintf(hdr->tag, CAM_CDM_CMD_TAG_MAX_LEN, "CDM_REG_RANDOM:"); hdr->word_size = sizeof(uint32_t); addr = (uint32_t *)(dst + sizeof(struct cam_cdm_cmd_dump_header)); start = addr; *addr++ = p_regrand_cmd->count; for (i = 0; i < p_regrand_cmd->count; i++) { addr[0] = temp_ptr[0] & CAM_CDM_REG_OFFSET_MASK; addr[1] = temp_ptr[1]; temp_ptr += 2; addr += 2; ret += 2; } hdr->size = hdr->word_size * (addr - start); dump_info->dst_offset += hdr->size + sizeof(struct cam_cdm_cmd_dump_header); return ret; } int cam_cdm_util_dump_cmd_bufs_v2( struct cam_cdm_cmd_buf_dump_info *dump_info) { uint32_t cmd; uint32_t *buf_now; int rc = 0; if (!dump_info || !dump_info->src_start || !dump_info->src_end || !dump_info->dst_start) { CAM_INFO(CAM_CDM, "Invalid args"); return -EINVAL; } buf_now = dump_info->src_start; do { if (dump_info->dst_offset >= dump_info->dst_max_size) { CAM_WARN(CAM_CDM, "Dump overshoot offset %zu size %zu", dump_info->dst_offset, dump_info->dst_max_size); return -ENOSPC; } cmd = *buf_now; cmd = cmd >> CAM_CDM_COMMAND_OFFSET; switch (cmd) { case CAM_CDM_CMD_DMI: case CAM_CDM_CMD_DMI_32: case CAM_CDM_CMD_DMI_64: buf_now += cdm_get_cmd_header_size(CAM_CDM_CMD_DMI); break; case CAM_CDM_CMD_REG_CONT: buf_now += cam_cdm_util_dump_reg_cont_cmd_v2(buf_now, dump_info); break; case CAM_CDM_CMD_REG_RANDOM: buf_now += cam_cdm_util_dump_reg_random_cmd_v2(buf_now, dump_info); break; case CAM_CDM_CMD_BUFF_INDIRECT: buf_now += cdm_get_cmd_header_size( CAM_CDM_CMD_BUFF_INDIRECT); break; case CAM_CDM_CMD_GEN_IRQ: buf_now += cdm_get_cmd_header_size( CAM_CDM_CMD_GEN_IRQ); break; case CAM_CDM_CMD_WAIT_EVENT: buf_now += cdm_get_cmd_header_size( CAM_CDM_CMD_WAIT_EVENT); break; case CAM_CDM_CMD_CHANGE_BASE: buf_now += cdm_get_cmd_header_size( CAM_CDM_CMD_CHANGE_BASE); break; case CAM_CDM_CMD_PERF_CTRL: buf_now += cdm_get_cmd_header_size( CAM_CDM_CMD_PERF_CTRL); break; default: CAM_ERR(CAM_CDM, "Invalid CMD: 0x%x", cmd); buf_now++; break; } } while (buf_now <= dump_info->src_end); return rc; } drivers/cam_cdm/cam_cdm_util.h +44 −1 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #ifndef _CAM_CDM_UTIL_H_ #define _CAM_CDM_UTIL_H_ /* Max len for tag name for header while dumping cmd buffer*/ #define CAM_CDM_CMD_TAG_MAX_LEN 32 enum cam_cdm_command { CAM_CDM_CMD_UNUSED = 0x0, CAM_CDM_CMD_DMI = 0x1, Loading Loading @@ -144,6 +147,34 @@ void (*cdm_write_genirq)( uint32_t userdata); }; /** * struct cam_cdm_cmd_buf_dump_info; - Camera CDM dump info * @dst_offset: dst offset * @dst_max_size max size of destination buffer * @src_start: source start address * @src_end: source end address * @dst_start: dst start address */ struct cam_cdm_cmd_buf_dump_info { size_t dst_offset; size_t dst_max_size; uint32_t *src_start; uint32_t *src_end; uintptr_t dst_start; }; /** * struct cam_cdm_cmd_dump_header- Camera CDM dump header * @tag: tag name for header * @size: size of data * @word_size: size of each word */ struct cam_cdm_cmd_dump_header { uint8_t tag[CAM_CDM_CMD_TAG_MAX_LEN]; uint64_t size; uint32_t word_size; }; /** * cam_cdm_util_log_cmd_bufs() * Loading @@ -156,6 +187,18 @@ void (*cdm_write_genirq)( void cam_cdm_util_dump_cmd_buf( uint32_t *cmd_buffer_start, uint32_t *cmd_buffer_end); /** * cam_cdm_util_dump_cmd_bufs_v2() * * @brief: Util function to cdm command buffers * to a buffer * * @dump_info: Information about source and destination buffers * * return SUCCESS/FAILURE */ int cam_cdm_util_dump_cmd_bufs_v2( struct cam_cdm_cmd_buf_dump_info *dump_info); #endif /* _CAM_CDM_UTIL_H_ */ drivers/cam_cpas/cam_cpas_hw.c +142 −24 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #include <linux/device.h> Loading Loading @@ -242,6 +242,12 @@ static int cam_cpas_util_axi_cleanup(struct cam_cpas *cpas_core, return -EINVAL; } if (cpas_core->num_camnoc_axi_ports > CAM_CPAS_MAX_AXI_PORTS) { CAM_ERR(CAM_CPAS, "Invalid num_camnoc_axi_ports: %d", cpas_core->num_camnoc_axi_ports); return -EINVAL; } for (i = 0; i < cpas_core->num_axi_ports; i++) { cam_cpas_util_unregister_bus_client( &cpas_core->axi_port[i].bus_client); Loading @@ -249,6 +255,13 @@ static int cam_cpas_util_axi_cleanup(struct cam_cpas *cpas_core, cpas_core->axi_port[i].axi_port_node = NULL; } for (i = 0; i < cpas_core->num_camnoc_axi_ports; i++) { cam_cpas_util_unregister_bus_client( &cpas_core->camnoc_axi_port[i].bus_client); of_node_put(cpas_core->camnoc_axi_port[i].axi_port_node); cpas_core->camnoc_axi_port[i].axi_port_node = NULL; } return 0; } Loading @@ -257,6 +270,7 @@ static int cam_cpas_util_axi_setup(struct cam_cpas *cpas_core, { int i = 0, rc = 0; struct device_node *axi_port_mnoc_node = NULL; struct device_node *axi_port_camnoc_node = NULL; if (cpas_core->num_axi_ports > CAM_CPAS_MAX_AXI_PORTS) { CAM_ERR(CAM_CPAS, "Invalid num_axi_ports: %d", Loading @@ -271,6 +285,15 @@ static int cam_cpas_util_axi_setup(struct cam_cpas *cpas_core, if (rc) goto bus_register_fail; } for (i = 0; i < cpas_core->num_camnoc_axi_ports; i++) { axi_port_camnoc_node = cpas_core->camnoc_axi_port[i].axi_port_node; rc = cam_cpas_util_register_bus_client(soc_info, axi_port_camnoc_node, &cpas_core->camnoc_axi_port[i].bus_client); if (rc) goto bus_register_fail; } return 0; bus_register_fail: Loading Loading @@ -608,6 +631,99 @@ static int cam_cpas_axi_consolidate_path_votes( return rc; } static int cam_cpas_update_axi_vote_bw( struct cam_hw_info *cpas_hw, struct cam_cpas_tree_node *cpas_tree_node, bool *mnoc_axi_port_updated, bool *camnoc_axi_port_updated) { struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; struct cam_cpas_private_soc *soc_private = (struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private; if (cpas_tree_node->axi_port_idx >= CAM_CPAS_MAX_AXI_PORTS) { CAM_ERR(CAM_CPAS, "Invalid axi_port_idx: %d", cpas_tree_node->axi_port_idx); return -EINVAL; } cpas_core->axi_port[cpas_tree_node->axi_port_idx].ab_bw = cpas_tree_node->mnoc_ab_bw; cpas_core->axi_port[cpas_tree_node->axi_port_idx].ib_bw = cpas_tree_node->mnoc_ib_bw; mnoc_axi_port_updated[cpas_tree_node->axi_port_idx] = true; if (soc_private->control_camnoc_axi_clk) return 0; cpas_core->camnoc_axi_port[cpas_tree_node->axi_port_idx].camnoc_bw = cpas_tree_node->camnoc_bw; camnoc_axi_port_updated[cpas_tree_node->camnoc_axi_port_idx] = true; return 0; } static int cam_cpas_camnoc_set_vote_axi_clk_rate( struct cam_hw_info *cpas_hw, bool *camnoc_axi_port_updated) { struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; struct cam_cpas_private_soc *soc_private = (struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private; int i; int rc = 0; struct cam_cpas_axi_port *camnoc_axi_port = NULL; uint64_t camnoc_bw; if (soc_private->control_camnoc_axi_clk) { rc = cam_cpas_util_set_camnoc_axi_clk_rate(cpas_hw); if (rc) CAM_ERR(CAM_CPAS, "Failed in setting axi clk rate rc=%d", rc); return rc; } /* Below code is executed if we just vote and do not set the clk rate * for camnoc */ if (cpas_core->num_camnoc_axi_ports > CAM_CPAS_MAX_AXI_PORTS) { CAM_ERR(CAM_CPAS, "Invalid num_camnoc_axi_ports: %d", cpas_core->num_camnoc_axi_ports); return -EINVAL; } for (i = 0; i < cpas_core->num_camnoc_axi_ports; i++) { if (camnoc_axi_port_updated[i]) camnoc_axi_port = &cpas_core->camnoc_axi_port[i]; else continue; CAM_DBG(CAM_PERF, "Port[%s] : camnoc_bw=%lld", camnoc_axi_port->axi_port_name, camnoc_axi_port->camnoc_bw); if (camnoc_axi_port->camnoc_bw) camnoc_bw = camnoc_axi_port->camnoc_bw; else camnoc_bw = camnoc_axi_port->additional_bw; rc = cam_cpas_util_vote_bus_client_bw( &camnoc_axi_port->bus_client, 0, camnoc_bw, true); CAM_DBG(CAM_CPAS, "camnoc vote camnoc_bw[%llu] rc=%d %s", camnoc_bw, rc, camnoc_axi_port->axi_port_name); if (rc) { CAM_ERR(CAM_CPAS, "Failed in camnoc vote camnoc_bw[%llu] rc=%d", camnoc_bw, rc); break; } } return rc; } static int cam_cpas_util_apply_client_axi_vote( struct cam_hw_info *cpas_hw, struct cam_cpas_client *cpas_client, Loading @@ -615,12 +731,13 @@ static int cam_cpas_util_apply_client_axi_vote( { struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; struct cam_axi_vote *con_axi_vote = NULL; struct cam_cpas_axi_port *axi_port = NULL; struct cam_cpas_axi_port *mnoc_axi_port = NULL; struct cam_cpas_tree_node *curr_tree_node = NULL; struct cam_cpas_tree_node *par_tree_node = NULL; uint32_t transac_type; uint32_t path_data_type; bool axi_port_updated[CAM_CPAS_MAX_AXI_PORTS] = {false}; bool mnoc_axi_port_updated[CAM_CPAS_MAX_AXI_PORTS] = {false}; bool camnoc_axi_port_updated[CAM_CPAS_MAX_AXI_PORTS] = {false}; uint64_t mnoc_ab_bw = 0, mnoc_ib_bw = 0, curr_camnoc_old = 0, curr_mnoc_ab_old = 0, curr_mnoc_ib_old = 0, par_camnoc_old = 0, par_mnoc_ab_old = 0, par_mnoc_ib_old = 0; Loading @@ -643,7 +760,7 @@ static int cam_cpas_util_apply_client_axi_vote( cpas_core->axi_port[i].additional_bw -= CAM_CPAS_DEFAULT_AXI_BW; } axi_port_updated[i] = true; mnoc_axi_port_updated[i] = true; } goto vote_start_clients; } Loading Loading @@ -733,15 +850,15 @@ static int cam_cpas_util_apply_client_axi_vote( rc = -EINVAL; goto unlock_tree; } cpas_core->axi_port [par_tree_node->axi_port_idx].ab_bw = par_tree_node->mnoc_ab_bw; cpas_core->axi_port [par_tree_node->axi_port_idx].ib_bw = par_tree_node->mnoc_ib_bw; axi_port_updated[par_tree_node->axi_port_idx] = true; rc = cam_cpas_update_axi_vote_bw(cpas_hw, par_tree_node, mnoc_axi_port_updated, camnoc_axi_port_updated); if (rc) { CAM_ERR(CAM_CPAS, "Update Vote failed"); goto unlock_tree; } } curr_tree_node = par_tree_node; Loading @@ -759,26 +876,27 @@ static int cam_cpas_util_apply_client_axi_vote( vote_start_clients: for (i = 0; i < cpas_core->num_axi_ports; i++) { if (axi_port_updated[i]) axi_port = &cpas_core->axi_port[i]; if (mnoc_axi_port_updated[i]) mnoc_axi_port = &cpas_core->axi_port[i]; else continue; CAM_DBG(CAM_PERF, "Port[%s] : ab=%lld ib=%lld additional=%lld", axi_port->axi_port_name, axi_port->ab_bw, axi_port->ib_bw, axi_port->additional_bw); mnoc_axi_port->axi_port_name, mnoc_axi_port->ab_bw, mnoc_axi_port->ib_bw, mnoc_axi_port->additional_bw); if (axi_port->ab_bw) mnoc_ab_bw = axi_port->ab_bw; if (mnoc_axi_port->ab_bw) mnoc_ab_bw = mnoc_axi_port->ab_bw; else mnoc_ab_bw = axi_port->additional_bw; mnoc_ab_bw = mnoc_axi_port->additional_bw; if (cpas_core->axi_port[i].ib_bw_voting_needed) mnoc_ib_bw = axi_port->ib_bw; mnoc_ib_bw = mnoc_axi_port->ib_bw; else mnoc_ib_bw = 0; rc = cam_cpas_util_vote_bus_client_bw(&axi_port->bus_client, rc = cam_cpas_util_vote_bus_client_bw( &mnoc_axi_port->bus_client, mnoc_ab_bw, mnoc_ib_bw, false); if (rc) { CAM_ERR(CAM_CPAS, Loading @@ -787,8 +905,8 @@ static int cam_cpas_util_apply_client_axi_vote( goto unlock_tree; } } rc = cam_cpas_util_set_camnoc_axi_clk_rate(cpas_hw); rc = cam_cpas_camnoc_set_vote_axi_clk_rate( cpas_hw, camnoc_axi_port_updated); if (rc) CAM_ERR(CAM_CPAS, "Failed in setting axi clk rate rc=%d", rc); Loading drivers/cam_cpas/cam_cpas_hw.h +7 −1 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #ifndef _CAM_CPAS_HW_H_ Loading Loading @@ -153,6 +153,7 @@ struct cam_cpas_bus_client { * @axi_port_node: Node representing AXI Port info in device tree * @ab_bw: AB bw value for this port * @ib_bw: IB bw value for this port * @camnoc_bw: CAMNOC bw value for this port * @additional_bw: Additional bandwidth to cover non-hw cpas clients */ struct cam_cpas_axi_port { Loading @@ -162,6 +163,7 @@ struct cam_cpas_axi_port { struct device_node *axi_port_node; uint64_t ab_bw; uint64_t ib_bw; uint64_t camnoc_bw; uint64_t additional_bw; }; Loading @@ -174,11 +176,13 @@ struct cam_cpas_axi_port { * @tree_lock: Mutex lock for accessing CPAS node tree * @num_clients: Total number of clients that CPAS supports * @num_axi_ports: Total number of axi ports found in device tree * @num_camnoc_axi_ports: Total number of camnoc axi ports found in device tree * @registered_clients: Number of Clients registered currently * @streamon_clients: Number of Clients that are in start state currently * @regbase_index: Register base indices for CPAS register base IDs * @ahb_bus_client: AHB Bus client info * @axi_port: AXI port info for a specific axi index * @camnoc_axi_port: CAMNOC AXI port info for a specific camnoc axi index * @internal_ops: CPAS HW internal ops * @work_queue: Work queue handle * @irq_count: atomic irq count Loading @@ -193,11 +197,13 @@ struct cam_cpas { struct mutex tree_lock; uint32_t num_clients; uint32_t num_axi_ports; uint32_t num_camnoc_axi_ports; uint32_t registered_clients; uint32_t streamon_clients; int32_t regbase_index[CAM_CPAS_REG_MAX]; struct cam_cpas_bus_client ahb_bus_client; struct cam_cpas_axi_port axi_port[CAM_CPAS_MAX_AXI_PORTS]; struct cam_cpas_axi_port camnoc_axi_port[CAM_CPAS_MAX_AXI_PORTS]; struct cam_cpas_internal_ops internal_ops; struct workqueue_struct *work_queue; atomic_t irq_count; Loading drivers/cam_cpas/cam_cpas_soc.c +54 −2 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #include <linux/device.h> Loading Loading @@ -134,6 +134,47 @@ static int cam_cpas_util_path_type_to_idx(uint32_t *path_data_type) return 0; } static int cam_cpas_update_camnoc_node(struct cam_cpas *cpas_core, struct device_node *curr_node, struct cam_cpas_tree_node *cpas_node_ptr, int *camnoc_idx) { struct device_node *camnoc_node; int rc; camnoc_node = of_find_node_by_name(curr_node, "qcom,axi-port-camnoc"); if (camnoc_node) { if (*camnoc_idx >= CAM_CPAS_MAX_AXI_PORTS) { CAM_ERR(CAM_CPAS, "CAMNOC axi index overshoot %d", *camnoc_idx); return -EINVAL; } cpas_core->camnoc_axi_port[*camnoc_idx] .axi_port_node = camnoc_node; rc = of_property_read_string( curr_node, "qcom,axi-port-name", &cpas_core->camnoc_axi_port[*camnoc_idx] .axi_port_name); if (rc) { CAM_ERR(CAM_CPAS, "fail to read camnoc-port-name rc=%d", rc); return rc; } cpas_node_ptr->camnoc_axi_port_idx = *camnoc_idx; cpas_core->num_camnoc_axi_ports++; (*camnoc_idx)++; } return 0; } static int cam_cpas_parse_node_tree(struct cam_cpas *cpas_core, struct device_node *of_node, struct cam_cpas_private_soc *soc_private) { Loading @@ -142,7 +183,7 @@ static int cam_cpas_parse_node_tree(struct cam_cpas *cpas_core, struct device_node *curr_node; struct device_node *parent_node; struct device_node *mnoc_node; int mnoc_idx = 0; int mnoc_idx = 0, camnoc_idx = 0; uint32_t path_idx; bool camnoc_max_needed = false; struct cam_cpas_tree_node *curr_node_ptr = NULL; Loading Loading @@ -248,6 +289,17 @@ static int cam_cpas_parse_node_tree(struct cam_cpas *cpas_core, cpas_core->num_axi_ports++; } if (!soc_private->control_camnoc_axi_clk) { rc = cam_cpas_update_camnoc_node( cpas_core, curr_node, curr_node_ptr, &camnoc_idx); if (rc) { CAM_ERR(CAM_CPAS, "Parse Camnoc port fail"); return rc; } } rc = of_property_read_string(curr_node, "client-name", &client_name); if (!rc) { Loading Loading
drivers/cam_cdm/cam_cdm_util.c +169 −1 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #include <linux/types.h> Loading Loading @@ -715,3 +715,171 @@ void cam_cdm_util_dump_cmd_buf( } } while (buf_now <= cmd_buf_end); } static uint32_t cam_cdm_util_dump_reg_cont_cmd_v2( uint32_t *cmd_buf_addr, struct cam_cdm_cmd_buf_dump_info *dump_info) { int i; long ret; uint8_t *dst; size_t remain_len; uint32_t *temp_ptr = cmd_buf_addr; uint32_t *addr, *start; uint32_t min_len; struct cdm_regcontinuous_cmd *p_regcont_cmd; struct cam_cdm_cmd_dump_header *hdr; p_regcont_cmd = (struct cdm_regcontinuous_cmd *)temp_ptr; temp_ptr += cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT); ret = cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT); min_len = (sizeof(uint32_t) * p_regcont_cmd->count) + sizeof(struct cam_cdm_cmd_dump_header) + (2 * sizeof(uint32_t)); remain_len = dump_info->dst_max_size - dump_info->dst_offset; if (remain_len < min_len) { CAM_WARN_RATE_LIMIT(CAM_CDM, "Dump buffer exhaust remain %zu min %u", remain_len, min_len); return ret; } dst = (char *)dump_info->dst_start + dump_info->dst_offset; hdr = (struct cam_cdm_cmd_dump_header *)dst; scnprintf(hdr->tag, CAM_CDM_CMD_TAG_MAX_LEN, "CDM_REG_CONT:"); hdr->word_size = sizeof(uint32_t); addr = (uint32_t *)(dst + sizeof(struct cam_cdm_cmd_dump_header)); start = addr; *addr++ = p_regcont_cmd->offset; *addr++ = p_regcont_cmd->count; for (i = 0; i < p_regcont_cmd->count; i++) { *addr = *temp_ptr; temp_ptr++; addr++; ret++; } hdr->size = hdr->word_size * (addr - start); dump_info->dst_offset += hdr->size + sizeof(struct cam_cdm_cmd_dump_header); return ret; } static uint32_t cam_cdm_util_dump_reg_random_cmd_v2( uint32_t *cmd_buf_addr, struct cam_cdm_cmd_buf_dump_info *dump_info) { int i; long ret; uint8_t *dst; uint32_t *temp_ptr = cmd_buf_addr; uint32_t *addr, *start; size_t remain_len; uint32_t min_len; struct cdm_regrandom_cmd *p_regrand_cmd; struct cam_cdm_cmd_dump_header *hdr; p_regrand_cmd = (struct cdm_regrandom_cmd *)temp_ptr; temp_ptr += cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM); ret = cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM); min_len = (2 * sizeof(uint32_t) * p_regrand_cmd->count) + sizeof(struct cam_cdm_cmd_dump_header) + sizeof(uint32_t); remain_len = dump_info->dst_max_size - dump_info->dst_offset; if (remain_len < min_len) { CAM_WARN_RATE_LIMIT(CAM_CDM, "Dump buffer exhaust remain %zu min %u", remain_len, min_len); return ret; } dst = (char *)dump_info->dst_start + dump_info->dst_offset; hdr = (struct cam_cdm_cmd_dump_header *)dst; scnprintf(hdr->tag, CAM_CDM_CMD_TAG_MAX_LEN, "CDM_REG_RANDOM:"); hdr->word_size = sizeof(uint32_t); addr = (uint32_t *)(dst + sizeof(struct cam_cdm_cmd_dump_header)); start = addr; *addr++ = p_regrand_cmd->count; for (i = 0; i < p_regrand_cmd->count; i++) { addr[0] = temp_ptr[0] & CAM_CDM_REG_OFFSET_MASK; addr[1] = temp_ptr[1]; temp_ptr += 2; addr += 2; ret += 2; } hdr->size = hdr->word_size * (addr - start); dump_info->dst_offset += hdr->size + sizeof(struct cam_cdm_cmd_dump_header); return ret; } int cam_cdm_util_dump_cmd_bufs_v2( struct cam_cdm_cmd_buf_dump_info *dump_info) { uint32_t cmd; uint32_t *buf_now; int rc = 0; if (!dump_info || !dump_info->src_start || !dump_info->src_end || !dump_info->dst_start) { CAM_INFO(CAM_CDM, "Invalid args"); return -EINVAL; } buf_now = dump_info->src_start; do { if (dump_info->dst_offset >= dump_info->dst_max_size) { CAM_WARN(CAM_CDM, "Dump overshoot offset %zu size %zu", dump_info->dst_offset, dump_info->dst_max_size); return -ENOSPC; } cmd = *buf_now; cmd = cmd >> CAM_CDM_COMMAND_OFFSET; switch (cmd) { case CAM_CDM_CMD_DMI: case CAM_CDM_CMD_DMI_32: case CAM_CDM_CMD_DMI_64: buf_now += cdm_get_cmd_header_size(CAM_CDM_CMD_DMI); break; case CAM_CDM_CMD_REG_CONT: buf_now += cam_cdm_util_dump_reg_cont_cmd_v2(buf_now, dump_info); break; case CAM_CDM_CMD_REG_RANDOM: buf_now += cam_cdm_util_dump_reg_random_cmd_v2(buf_now, dump_info); break; case CAM_CDM_CMD_BUFF_INDIRECT: buf_now += cdm_get_cmd_header_size( CAM_CDM_CMD_BUFF_INDIRECT); break; case CAM_CDM_CMD_GEN_IRQ: buf_now += cdm_get_cmd_header_size( CAM_CDM_CMD_GEN_IRQ); break; case CAM_CDM_CMD_WAIT_EVENT: buf_now += cdm_get_cmd_header_size( CAM_CDM_CMD_WAIT_EVENT); break; case CAM_CDM_CMD_CHANGE_BASE: buf_now += cdm_get_cmd_header_size( CAM_CDM_CMD_CHANGE_BASE); break; case CAM_CDM_CMD_PERF_CTRL: buf_now += cdm_get_cmd_header_size( CAM_CDM_CMD_PERF_CTRL); break; default: CAM_ERR(CAM_CDM, "Invalid CMD: 0x%x", cmd); buf_now++; break; } } while (buf_now <= dump_info->src_end); return rc; }
drivers/cam_cdm/cam_cdm_util.h +44 −1 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #ifndef _CAM_CDM_UTIL_H_ #define _CAM_CDM_UTIL_H_ /* Max len for tag name for header while dumping cmd buffer*/ #define CAM_CDM_CMD_TAG_MAX_LEN 32 enum cam_cdm_command { CAM_CDM_CMD_UNUSED = 0x0, CAM_CDM_CMD_DMI = 0x1, Loading Loading @@ -144,6 +147,34 @@ void (*cdm_write_genirq)( uint32_t userdata); }; /** * struct cam_cdm_cmd_buf_dump_info; - Camera CDM dump info * @dst_offset: dst offset * @dst_max_size max size of destination buffer * @src_start: source start address * @src_end: source end address * @dst_start: dst start address */ struct cam_cdm_cmd_buf_dump_info { size_t dst_offset; size_t dst_max_size; uint32_t *src_start; uint32_t *src_end; uintptr_t dst_start; }; /** * struct cam_cdm_cmd_dump_header- Camera CDM dump header * @tag: tag name for header * @size: size of data * @word_size: size of each word */ struct cam_cdm_cmd_dump_header { uint8_t tag[CAM_CDM_CMD_TAG_MAX_LEN]; uint64_t size; uint32_t word_size; }; /** * cam_cdm_util_log_cmd_bufs() * Loading @@ -156,6 +187,18 @@ void (*cdm_write_genirq)( void cam_cdm_util_dump_cmd_buf( uint32_t *cmd_buffer_start, uint32_t *cmd_buffer_end); /** * cam_cdm_util_dump_cmd_bufs_v2() * * @brief: Util function to cdm command buffers * to a buffer * * @dump_info: Information about source and destination buffers * * return SUCCESS/FAILURE */ int cam_cdm_util_dump_cmd_bufs_v2( struct cam_cdm_cmd_buf_dump_info *dump_info); #endif /* _CAM_CDM_UTIL_H_ */
drivers/cam_cpas/cam_cpas_hw.c +142 −24 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #include <linux/device.h> Loading Loading @@ -242,6 +242,12 @@ static int cam_cpas_util_axi_cleanup(struct cam_cpas *cpas_core, return -EINVAL; } if (cpas_core->num_camnoc_axi_ports > CAM_CPAS_MAX_AXI_PORTS) { CAM_ERR(CAM_CPAS, "Invalid num_camnoc_axi_ports: %d", cpas_core->num_camnoc_axi_ports); return -EINVAL; } for (i = 0; i < cpas_core->num_axi_ports; i++) { cam_cpas_util_unregister_bus_client( &cpas_core->axi_port[i].bus_client); Loading @@ -249,6 +255,13 @@ static int cam_cpas_util_axi_cleanup(struct cam_cpas *cpas_core, cpas_core->axi_port[i].axi_port_node = NULL; } for (i = 0; i < cpas_core->num_camnoc_axi_ports; i++) { cam_cpas_util_unregister_bus_client( &cpas_core->camnoc_axi_port[i].bus_client); of_node_put(cpas_core->camnoc_axi_port[i].axi_port_node); cpas_core->camnoc_axi_port[i].axi_port_node = NULL; } return 0; } Loading @@ -257,6 +270,7 @@ static int cam_cpas_util_axi_setup(struct cam_cpas *cpas_core, { int i = 0, rc = 0; struct device_node *axi_port_mnoc_node = NULL; struct device_node *axi_port_camnoc_node = NULL; if (cpas_core->num_axi_ports > CAM_CPAS_MAX_AXI_PORTS) { CAM_ERR(CAM_CPAS, "Invalid num_axi_ports: %d", Loading @@ -271,6 +285,15 @@ static int cam_cpas_util_axi_setup(struct cam_cpas *cpas_core, if (rc) goto bus_register_fail; } for (i = 0; i < cpas_core->num_camnoc_axi_ports; i++) { axi_port_camnoc_node = cpas_core->camnoc_axi_port[i].axi_port_node; rc = cam_cpas_util_register_bus_client(soc_info, axi_port_camnoc_node, &cpas_core->camnoc_axi_port[i].bus_client); if (rc) goto bus_register_fail; } return 0; bus_register_fail: Loading Loading @@ -608,6 +631,99 @@ static int cam_cpas_axi_consolidate_path_votes( return rc; } static int cam_cpas_update_axi_vote_bw( struct cam_hw_info *cpas_hw, struct cam_cpas_tree_node *cpas_tree_node, bool *mnoc_axi_port_updated, bool *camnoc_axi_port_updated) { struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; struct cam_cpas_private_soc *soc_private = (struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private; if (cpas_tree_node->axi_port_idx >= CAM_CPAS_MAX_AXI_PORTS) { CAM_ERR(CAM_CPAS, "Invalid axi_port_idx: %d", cpas_tree_node->axi_port_idx); return -EINVAL; } cpas_core->axi_port[cpas_tree_node->axi_port_idx].ab_bw = cpas_tree_node->mnoc_ab_bw; cpas_core->axi_port[cpas_tree_node->axi_port_idx].ib_bw = cpas_tree_node->mnoc_ib_bw; mnoc_axi_port_updated[cpas_tree_node->axi_port_idx] = true; if (soc_private->control_camnoc_axi_clk) return 0; cpas_core->camnoc_axi_port[cpas_tree_node->axi_port_idx].camnoc_bw = cpas_tree_node->camnoc_bw; camnoc_axi_port_updated[cpas_tree_node->camnoc_axi_port_idx] = true; return 0; } static int cam_cpas_camnoc_set_vote_axi_clk_rate( struct cam_hw_info *cpas_hw, bool *camnoc_axi_port_updated) { struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; struct cam_cpas_private_soc *soc_private = (struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private; int i; int rc = 0; struct cam_cpas_axi_port *camnoc_axi_port = NULL; uint64_t camnoc_bw; if (soc_private->control_camnoc_axi_clk) { rc = cam_cpas_util_set_camnoc_axi_clk_rate(cpas_hw); if (rc) CAM_ERR(CAM_CPAS, "Failed in setting axi clk rate rc=%d", rc); return rc; } /* Below code is executed if we just vote and do not set the clk rate * for camnoc */ if (cpas_core->num_camnoc_axi_ports > CAM_CPAS_MAX_AXI_PORTS) { CAM_ERR(CAM_CPAS, "Invalid num_camnoc_axi_ports: %d", cpas_core->num_camnoc_axi_ports); return -EINVAL; } for (i = 0; i < cpas_core->num_camnoc_axi_ports; i++) { if (camnoc_axi_port_updated[i]) camnoc_axi_port = &cpas_core->camnoc_axi_port[i]; else continue; CAM_DBG(CAM_PERF, "Port[%s] : camnoc_bw=%lld", camnoc_axi_port->axi_port_name, camnoc_axi_port->camnoc_bw); if (camnoc_axi_port->camnoc_bw) camnoc_bw = camnoc_axi_port->camnoc_bw; else camnoc_bw = camnoc_axi_port->additional_bw; rc = cam_cpas_util_vote_bus_client_bw( &camnoc_axi_port->bus_client, 0, camnoc_bw, true); CAM_DBG(CAM_CPAS, "camnoc vote camnoc_bw[%llu] rc=%d %s", camnoc_bw, rc, camnoc_axi_port->axi_port_name); if (rc) { CAM_ERR(CAM_CPAS, "Failed in camnoc vote camnoc_bw[%llu] rc=%d", camnoc_bw, rc); break; } } return rc; } static int cam_cpas_util_apply_client_axi_vote( struct cam_hw_info *cpas_hw, struct cam_cpas_client *cpas_client, Loading @@ -615,12 +731,13 @@ static int cam_cpas_util_apply_client_axi_vote( { struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; struct cam_axi_vote *con_axi_vote = NULL; struct cam_cpas_axi_port *axi_port = NULL; struct cam_cpas_axi_port *mnoc_axi_port = NULL; struct cam_cpas_tree_node *curr_tree_node = NULL; struct cam_cpas_tree_node *par_tree_node = NULL; uint32_t transac_type; uint32_t path_data_type; bool axi_port_updated[CAM_CPAS_MAX_AXI_PORTS] = {false}; bool mnoc_axi_port_updated[CAM_CPAS_MAX_AXI_PORTS] = {false}; bool camnoc_axi_port_updated[CAM_CPAS_MAX_AXI_PORTS] = {false}; uint64_t mnoc_ab_bw = 0, mnoc_ib_bw = 0, curr_camnoc_old = 0, curr_mnoc_ab_old = 0, curr_mnoc_ib_old = 0, par_camnoc_old = 0, par_mnoc_ab_old = 0, par_mnoc_ib_old = 0; Loading @@ -643,7 +760,7 @@ static int cam_cpas_util_apply_client_axi_vote( cpas_core->axi_port[i].additional_bw -= CAM_CPAS_DEFAULT_AXI_BW; } axi_port_updated[i] = true; mnoc_axi_port_updated[i] = true; } goto vote_start_clients; } Loading Loading @@ -733,15 +850,15 @@ static int cam_cpas_util_apply_client_axi_vote( rc = -EINVAL; goto unlock_tree; } cpas_core->axi_port [par_tree_node->axi_port_idx].ab_bw = par_tree_node->mnoc_ab_bw; cpas_core->axi_port [par_tree_node->axi_port_idx].ib_bw = par_tree_node->mnoc_ib_bw; axi_port_updated[par_tree_node->axi_port_idx] = true; rc = cam_cpas_update_axi_vote_bw(cpas_hw, par_tree_node, mnoc_axi_port_updated, camnoc_axi_port_updated); if (rc) { CAM_ERR(CAM_CPAS, "Update Vote failed"); goto unlock_tree; } } curr_tree_node = par_tree_node; Loading @@ -759,26 +876,27 @@ static int cam_cpas_util_apply_client_axi_vote( vote_start_clients: for (i = 0; i < cpas_core->num_axi_ports; i++) { if (axi_port_updated[i]) axi_port = &cpas_core->axi_port[i]; if (mnoc_axi_port_updated[i]) mnoc_axi_port = &cpas_core->axi_port[i]; else continue; CAM_DBG(CAM_PERF, "Port[%s] : ab=%lld ib=%lld additional=%lld", axi_port->axi_port_name, axi_port->ab_bw, axi_port->ib_bw, axi_port->additional_bw); mnoc_axi_port->axi_port_name, mnoc_axi_port->ab_bw, mnoc_axi_port->ib_bw, mnoc_axi_port->additional_bw); if (axi_port->ab_bw) mnoc_ab_bw = axi_port->ab_bw; if (mnoc_axi_port->ab_bw) mnoc_ab_bw = mnoc_axi_port->ab_bw; else mnoc_ab_bw = axi_port->additional_bw; mnoc_ab_bw = mnoc_axi_port->additional_bw; if (cpas_core->axi_port[i].ib_bw_voting_needed) mnoc_ib_bw = axi_port->ib_bw; mnoc_ib_bw = mnoc_axi_port->ib_bw; else mnoc_ib_bw = 0; rc = cam_cpas_util_vote_bus_client_bw(&axi_port->bus_client, rc = cam_cpas_util_vote_bus_client_bw( &mnoc_axi_port->bus_client, mnoc_ab_bw, mnoc_ib_bw, false); if (rc) { CAM_ERR(CAM_CPAS, Loading @@ -787,8 +905,8 @@ static int cam_cpas_util_apply_client_axi_vote( goto unlock_tree; } } rc = cam_cpas_util_set_camnoc_axi_clk_rate(cpas_hw); rc = cam_cpas_camnoc_set_vote_axi_clk_rate( cpas_hw, camnoc_axi_port_updated); if (rc) CAM_ERR(CAM_CPAS, "Failed in setting axi clk rate rc=%d", rc); Loading
drivers/cam_cpas/cam_cpas_hw.h +7 −1 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #ifndef _CAM_CPAS_HW_H_ Loading Loading @@ -153,6 +153,7 @@ struct cam_cpas_bus_client { * @axi_port_node: Node representing AXI Port info in device tree * @ab_bw: AB bw value for this port * @ib_bw: IB bw value for this port * @camnoc_bw: CAMNOC bw value for this port * @additional_bw: Additional bandwidth to cover non-hw cpas clients */ struct cam_cpas_axi_port { Loading @@ -162,6 +163,7 @@ struct cam_cpas_axi_port { struct device_node *axi_port_node; uint64_t ab_bw; uint64_t ib_bw; uint64_t camnoc_bw; uint64_t additional_bw; }; Loading @@ -174,11 +176,13 @@ struct cam_cpas_axi_port { * @tree_lock: Mutex lock for accessing CPAS node tree * @num_clients: Total number of clients that CPAS supports * @num_axi_ports: Total number of axi ports found in device tree * @num_camnoc_axi_ports: Total number of camnoc axi ports found in device tree * @registered_clients: Number of Clients registered currently * @streamon_clients: Number of Clients that are in start state currently * @regbase_index: Register base indices for CPAS register base IDs * @ahb_bus_client: AHB Bus client info * @axi_port: AXI port info for a specific axi index * @camnoc_axi_port: CAMNOC AXI port info for a specific camnoc axi index * @internal_ops: CPAS HW internal ops * @work_queue: Work queue handle * @irq_count: atomic irq count Loading @@ -193,11 +197,13 @@ struct cam_cpas { struct mutex tree_lock; uint32_t num_clients; uint32_t num_axi_ports; uint32_t num_camnoc_axi_ports; uint32_t registered_clients; uint32_t streamon_clients; int32_t regbase_index[CAM_CPAS_REG_MAX]; struct cam_cpas_bus_client ahb_bus_client; struct cam_cpas_axi_port axi_port[CAM_CPAS_MAX_AXI_PORTS]; struct cam_cpas_axi_port camnoc_axi_port[CAM_CPAS_MAX_AXI_PORTS]; struct cam_cpas_internal_ops internal_ops; struct workqueue_struct *work_queue; atomic_t irq_count; Loading
drivers/cam_cpas/cam_cpas_soc.c +54 −2 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #include <linux/device.h> Loading Loading @@ -134,6 +134,47 @@ static int cam_cpas_util_path_type_to_idx(uint32_t *path_data_type) return 0; } static int cam_cpas_update_camnoc_node(struct cam_cpas *cpas_core, struct device_node *curr_node, struct cam_cpas_tree_node *cpas_node_ptr, int *camnoc_idx) { struct device_node *camnoc_node; int rc; camnoc_node = of_find_node_by_name(curr_node, "qcom,axi-port-camnoc"); if (camnoc_node) { if (*camnoc_idx >= CAM_CPAS_MAX_AXI_PORTS) { CAM_ERR(CAM_CPAS, "CAMNOC axi index overshoot %d", *camnoc_idx); return -EINVAL; } cpas_core->camnoc_axi_port[*camnoc_idx] .axi_port_node = camnoc_node; rc = of_property_read_string( curr_node, "qcom,axi-port-name", &cpas_core->camnoc_axi_port[*camnoc_idx] .axi_port_name); if (rc) { CAM_ERR(CAM_CPAS, "fail to read camnoc-port-name rc=%d", rc); return rc; } cpas_node_ptr->camnoc_axi_port_idx = *camnoc_idx; cpas_core->num_camnoc_axi_ports++; (*camnoc_idx)++; } return 0; } static int cam_cpas_parse_node_tree(struct cam_cpas *cpas_core, struct device_node *of_node, struct cam_cpas_private_soc *soc_private) { Loading @@ -142,7 +183,7 @@ static int cam_cpas_parse_node_tree(struct cam_cpas *cpas_core, struct device_node *curr_node; struct device_node *parent_node; struct device_node *mnoc_node; int mnoc_idx = 0; int mnoc_idx = 0, camnoc_idx = 0; uint32_t path_idx; bool camnoc_max_needed = false; struct cam_cpas_tree_node *curr_node_ptr = NULL; Loading Loading @@ -248,6 +289,17 @@ static int cam_cpas_parse_node_tree(struct cam_cpas *cpas_core, cpas_core->num_axi_ports++; } if (!soc_private->control_camnoc_axi_clk) { rc = cam_cpas_update_camnoc_node( cpas_core, curr_node, curr_node_ptr, &camnoc_idx); if (rc) { CAM_ERR(CAM_CPAS, "Parse Camnoc port fail"); return rc; } } rc = of_property_read_string(curr_node, "client-name", &client_name); if (!rc) { Loading