diff --git a/Kbuild b/Kbuild index 13b429dda0..5e7ae9b25f 100644 --- a/Kbuild +++ b/Kbuild @@ -181,6 +181,9 @@ camera-$(CONFIG_SPECTRA_ICP) += \ drivers/cam_icp/icp_hw/bps_hw/bps_dev.o \ drivers/cam_icp/icp_hw/bps_hw/bps_core.o \ drivers/cam_icp/icp_hw/bps_hw/bps_soc.o \ + drivers/cam_icp/icp_hw/ofe_hw/ofe_dev.o \ + drivers/cam_icp/icp_hw/ofe_hw/ofe_core.o \ + drivers/cam_icp/icp_hw/ofe_hw/ofe_soc.o \ drivers/cam_icp/cam_icp_subdev.o \ drivers/cam_icp/cam_icp_context.o \ drivers/cam_icp/hfi.o diff --git a/drivers/cam_cpas/include/cam_cpas_api.h b/drivers/cam_cpas/include/cam_cpas_api.h index b7c2cb43cd..1dcc2ca1a2 100644 --- a/drivers/cam_cpas/include/cam_cpas_api.h +++ b/drivers/cam_cpas/include/cam_cpas_api.h @@ -218,6 +218,12 @@ enum cam_camnoc_slave_error_codes { * @CAM_CAMNOC_IRQ_IPE_BPS_UBWC_ENCODE_ERROR: Triggered if any error detected * in the IPE/BPS UBWC encoder * instance + * @CAM_CAMNOC_IRQ_OFE_WR_UBWC_DECODE_ERROR : Triggered if any error detected + * in the OFE write UBWC decoder + * instance + * @CAM_CAMNOC_IRQ_OFE_RD_UBWC_DECODE_ERROR : Triggered if any error detected + * in the OFE read UBWC decoder + * instance * @CAM_CAMNOC_IRQ_AHB_TIMEOUT : Triggered when the QHS_ICP slave * times out after 4000 AHB cycles */ @@ -237,6 +243,8 @@ enum cam_camnoc_irq_type { CAM_CAMNOC_IRQ_IPE1_UBWC_DECODE_ERROR, CAM_CAMNOC_IRQ_IPE_BPS_UBWC_DECODE_ERROR, CAM_CAMNOC_IRQ_IPE_BPS_UBWC_ENCODE_ERROR, + CAM_CAMNOC_IRQ_OFE_WR_UBWC_DECODE_ERROR, + CAM_CAMNOC_IRQ_OFE_RD_UBWC_DECODE_ERROR, CAM_CAMNOC_IRQ_AHB_TIMEOUT, }; diff --git a/drivers/cam_icp/icp_hw/icp_hw_mgr/include/cam_icp_hw_intf.h b/drivers/cam_icp/icp_hw/icp_hw_mgr/include/cam_icp_hw_intf.h index 4d04c99427..d2578ff323 100644 --- a/drivers/cam_icp/icp_hw/icp_hw_mgr/include/cam_icp_hw_intf.h +++ b/drivers/cam_icp/icp_hw/icp_hw_mgr/include/cam_icp_hw_intf.h @@ -30,6 +30,7 @@ enum cam_icp_hw_type { CAM_ICP_DEV_ICP_V2, CAM_ICP_DEV_IPE, CAM_ICP_DEV_BPS, + CAM_ICP_DEV_OFE, CAM_ICP_DEV_MAX, }; diff --git a/drivers/cam_icp/icp_hw/icp_hw_mgr/include/cam_ofe_hw_intf.h b/drivers/cam_icp/icp_hw/icp_hw_mgr/include/cam_ofe_hw_intf.h new file mode 100644 index 0000000000..34ed25342c --- /dev/null +++ b/drivers/cam_icp/icp_hw/icp_hw_mgr/include/cam_ofe_hw_intf.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef CAM_OFE_HW_INTF_H +#define CAM_OFE_HW_INTF_H + +#include +#include +#include "cam_hw_mgr_intf.h" +#include "cam_icp_hw_intf.h" + +enum cam_icp_ofe_cmd_type { + CAM_ICP_OFE_CMD_FW_DOWNLOAD, + CAM_ICP_OFE_CMD_POWER_COLLAPSE, + CAM_ICP_OFE_CMD_POWER_RESUME, + CAM_ICP_OFE_CMD_SET_FW_BUF, + CAM_ICP_OFE_CMD_VOTE_CPAS, + CAM_ICP_OFE_CMD_CPAS_START, + CAM_ICP_OFE_CMD_CPAS_STOP, + CAM_ICP_OFE_CMD_UPDATE_CLK, + CAM_ICP_OFE_CMD_DISABLE_CLK, + CAM_ICP_OFE_CMD_RESET, + CAM_ICP_OFE_CMD_MAX, +}; + +#endif /* CAM_OFE_HW_INTF_H */ diff --git a/drivers/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.h b/drivers/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.h index e1f2c38ca5..5402785893 100644 --- a/drivers/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.h +++ b/drivers/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.h @@ -25,6 +25,8 @@ #define CAM_IPE_DEFAULT_AXI_TRANSAC CAM_AXI_TRANSACTION_WRITE #define CAM_BPS_DEFAULT_AXI_PATH CAM_AXI_PATH_DATA_ALL #define CAM_BPS_DEFAULT_AXI_TRANSAC CAM_AXI_TRANSACTION_WRITE +#define CAM_OFE_DEFAULT_AXI_PATH CAM_AXI_PATH_DATA_ALL +#define CAM_OFE_DEFAULT_AXI_TRANSAC CAM_AXI_TRANSACTION_WRITE #define CAM_ICP_DEFAULT_AXI_PATH CAM_AXI_PATH_DATA_ALL #define CAM_ICP_DEFAULT_AXI_TRANSAC CAM_AXI_TRANSACTION_READ diff --git a/drivers/cam_icp/icp_hw/ofe_hw/ofe_core.c b/drivers/cam_icp/icp_hw/ofe_hw/ofe_core.c new file mode 100644 index 0000000000..b44c0c9b23 --- /dev/null +++ b/drivers/cam_icp/icp_hw/ofe_hw/ofe_core.c @@ -0,0 +1,482 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "cam_io_util.h" +#include "cam_hw.h" +#include "cam_hw_intf.h" +#include "ofe_core.h" +#include "ofe_soc.h" +#include "cam_soc_util.h" +#include "cam_io_util.h" +#include "cam_ofe_hw_intf.h" +#include "cam_icp_hw_intf.h" +#include "cam_icp_hw_mgr_intf.h" +#include "cam_cpas_api.h" +#include "cam_debug_util.h" +#include "hfi_reg.h" +#include "cam_common_util.h" + +static int cam_ofe_cpas_vote(struct cam_ofe_device_core_info *core_info, + struct cam_icp_cpas_vote *cpas_vote) +{ + int rc = 0; + + if (cpas_vote->ahb_vote_valid) + rc = cam_cpas_update_ahb_vote(core_info->cpas_handle, + &cpas_vote->ahb_vote); + if (rc) + CAM_ERR(CAM_PERF, "CPAS AHB vote failed rc:%d", rc); + + if (cpas_vote->axi_vote_valid) + rc = cam_cpas_update_axi_vote(core_info->cpas_handle, + &cpas_vote->axi_vote); + if (rc) + CAM_ERR(CAM_PERF, "CPAS AXI vote failed rc:%d", rc); + + return rc; +} + +int cam_ofe_init_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size) +{ + struct cam_hw_info *ofe_dev = device_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_ofe_device_core_info *core_info = NULL; + struct cam_icp_cpas_vote cpas_vote; + int rc = 0; + + if (!device_priv) { + CAM_ERR(CAM_ICP, "Invalid cam_dev_info"); + return -EINVAL; + } + + soc_info = &ofe_dev->soc_info; + core_info = (struct cam_ofe_device_core_info *)ofe_dev->core_info; + + if ((!soc_info) || (!core_info)) { + CAM_ERR(CAM_ICP, "soc_info = %pK core_info = %pK", + soc_info, core_info); + return -EINVAL; + } + + cpas_vote.ahb_vote.type = CAM_VOTE_ABSOLUTE; + cpas_vote.ahb_vote.vote.level = CAM_LOWSVS_VOTE; + cpas_vote.axi_vote.num_paths = 1; + cpas_vote.axi_vote.axi_path[0].path_data_type = + CAM_OFE_DEFAULT_AXI_PATH; + cpas_vote.axi_vote.axi_path[0].transac_type = + CAM_OFE_DEFAULT_AXI_TRANSAC; + cpas_vote.axi_vote.axi_path[0].camnoc_bw = + CAM_CPAS_DEFAULT_AXI_BW; + cpas_vote.axi_vote.axi_path[0].mnoc_ab_bw = + CAM_CPAS_DEFAULT_AXI_BW; + cpas_vote.axi_vote.axi_path[0].mnoc_ib_bw = + CAM_CPAS_DEFAULT_AXI_BW; + + rc = cam_cpas_start(core_info->cpas_handle, + &cpas_vote.ahb_vote, &cpas_vote.axi_vote); + if (rc) { + CAM_ERR(CAM_ICP, "cpas start failed: %d", rc); + return rc; + } + core_info->cpas_start = true; + + rc = cam_ofe_enable_soc_resources(soc_info); + if (rc) { + CAM_ERR(CAM_ICP, "soc enable failed rc:%d", rc); + if (cam_cpas_stop(core_info->cpas_handle)) + CAM_ERR(CAM_ICP, "cpas stop failed"); + else + core_info->cpas_start = false; + } else { + core_info->clk_enable = true; + } + + return rc; +} + +int cam_ofe_deinit_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size) +{ + struct cam_hw_info *ofe_dev = device_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_ofe_device_core_info *core_info = NULL; + int rc = 0; + + if (!device_priv) { + CAM_ERR(CAM_ICP, "Invalid cam_dev_info"); + return -EINVAL; + } + + soc_info = &ofe_dev->soc_info; + core_info = (struct cam_ofe_device_core_info *)ofe_dev->core_info; + if ((!soc_info) || (!core_info)) { + CAM_ERR(CAM_ICP, "soc_info = %pK core_info = %pK", + soc_info, core_info); + return -EINVAL; + } + + rc = cam_ofe_disable_soc_resources(soc_info, core_info->clk_enable); + if (rc) + CAM_ERR(CAM_ICP, "soc disable failed: %d", rc); + else + core_info->clk_enable = false; + + if (core_info->cpas_start) { + if (cam_cpas_stop(core_info->cpas_handle)) + CAM_ERR(CAM_ICP, "cpas stop failed"); + else + core_info->cpas_start = false; + } + + return rc; +} + +static int cam_ofe_handle_pc(struct cam_hw_info *ofe_dev) +{ + struct cam_hw_soc_info *soc_info = NULL; + struct cam_ofe_device_core_info *core_info = NULL; + struct cam_ofe_device_hw_info *hw_info = NULL; + int rc = 0, pwr_ctrl, pwr_status; + + soc_info = &ofe_dev->soc_info; + core_info = (struct cam_ofe_device_core_info *)ofe_dev->core_info; + hw_info = core_info->ofe_hw_info; + + if (!core_info->cpas_start) { + CAM_DBG(CAM_ICP, "CPAS OFE client not started"); + return 0; + } + + rc = cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_ctrl, + true, &pwr_ctrl); + if (rc) { + CAM_ERR(CAM_ICP, "power ctrl read failed rc=%d", rc); + return rc; + } + + if (!(pwr_ctrl & OFE_COLLAPSE_MASK)) { + rc = cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_status, + true, &pwr_status); + if (rc) { + CAM_ERR(CAM_ICP, "power status read failed rc=%d", rc); + return rc; + } + + cam_cpas_reg_write(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, + hw_info->pwr_ctrl, true, 0x1); + + if ((pwr_status >> OFE_PWR_ON_MASK)) + CAM_WARN(CAM_PERF, "OFE: pwr_status(%x):pwr_ctrl(%x)", + pwr_status, pwr_ctrl); + } + + rc = cam_ofe_get_gdsc_control(soc_info); + if (rc) { + CAM_ERR(CAM_ICP, "failed to get gdsc control rc=%d", rc); + return rc; + } + + rc = cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_ctrl, + true, &pwr_ctrl); + if (rc) { + CAM_ERR(CAM_ICP, "power ctrl read failed rc=%d", rc); + return rc; + } + + rc = cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_status, + true, &pwr_status); + if (rc) { + CAM_ERR(CAM_ICP, "power status read failed rc=%d", rc); + return rc; + } + + CAM_DBG(CAM_PERF, "pwr_ctrl=%x pwr_status=%x", pwr_ctrl, pwr_status); + + return 0; +} + +static int cam_ofe_handle_resume(struct cam_hw_info *ofe_dev) +{ + struct cam_hw_soc_info *soc_info = NULL; + struct cam_ofe_device_core_info *core_info = NULL; + struct cam_ofe_device_hw_info *hw_info = NULL; + int pwr_ctrl, pwr_status, rc = 0; + + soc_info = &ofe_dev->soc_info; + core_info = (struct cam_ofe_device_core_info *)ofe_dev->core_info; + hw_info = core_info->ofe_hw_info; + + if (!core_info->cpas_start) { + CAM_DBG(CAM_ICP, "CPAS OFE client not started"); + return 0; + } + + rc = cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_ctrl, + true, &pwr_ctrl); + if (rc) { + CAM_ERR(CAM_ICP, "power ctrl read failed rc=%d", rc); + return rc; + } + + if (pwr_ctrl & OFE_COLLAPSE_MASK) { + CAM_DBG(CAM_PERF, "OFE: pwr_ctrl set(%x)", pwr_ctrl); + cam_cpas_reg_write(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, + hw_info->pwr_ctrl, true, 0); + } + + rc = cam_ofe_transfer_gdsc_control(soc_info); + if (rc) { + CAM_ERR(CAM_ICP, "failed to transfer gdsc control rc=%d", rc); + return rc; + } + + rc = cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_ctrl, + true, &pwr_ctrl); + if (rc) { + CAM_ERR(CAM_ICP, "power ctrl read failed rc=%d", rc); + return rc; + } + + rc = cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_status, + true, &pwr_status); + if (rc) { + CAM_ERR(CAM_ICP, "power status read failed rc=%d", rc); + return rc; + } + + CAM_DBG(CAM_PERF, "pwr_ctrl=%x pwr_status=%x", pwr_ctrl, pwr_status); + + return rc; +} + +static int cam_ofe_cmd_reset(struct cam_hw_soc_info *soc_info, + struct cam_ofe_device_core_info *core_info) +{ + uint32_t retry_cnt = 0, status = 0; + int pwr_ctrl, pwr_status, rc = 0; + bool reset_ofe_cdm_fail = false, reset_ofe_top_fail = false; + struct cam_ofe_device_hw_info *hw_info = NULL; + + CAM_DBG(CAM_ICP, "CAM_ICP_OFE_CMD_RESET"); + + if (!core_info->clk_enable || !core_info->cpas_start) { + CAM_DBG(CAM_ICP, "OFE not powered on clk_en %d cpas_start %d", + core_info->clk_enable, core_info->cpas_start); + return 0; + } + + hw_info = core_info->ofe_hw_info; + + /* Reset OFE CDM core*/ + cam_io_w_mb(hw_info->cdm_rst_val, + soc_info->reg_map[0].mem_base + hw_info->cdm_rst_cmd); + while (retry_cnt < HFI_MAX_POLL_TRY) { + cam_common_read_poll_timeout((soc_info->reg_map[0].mem_base + + hw_info->cdm_irq_status), + PC_POLL_DELAY_US, PC_POLL_TIMEOUT_US, + OFE_RST_DONE_IRQ_STATUS_BIT, OFE_RST_DONE_IRQ_STATUS_BIT, + &status); + + CAM_DBG(CAM_ICP, "ofe_cdm_irq_status = %u", status); + + if ((status & OFE_RST_DONE_IRQ_STATUS_BIT) == 0x1) + break; + retry_cnt++; + } + + if (retry_cnt == HFI_MAX_POLL_TRY) { + CAM_ERR(CAM_ICP, "OFE CDM rst failed status 0x%x", status); + reset_ofe_cdm_fail = true; + } + + /* Reset OFE core*/ + status = 0; + retry_cnt = 0; + cam_io_w_mb(hw_info->top_rst_val, + soc_info->reg_map[0].mem_base + hw_info->top_rst_cmd); + while (retry_cnt < HFI_MAX_POLL_TRY) { + cam_common_read_poll_timeout((soc_info->reg_map[0].mem_base + + hw_info->top_irq_status), + PC_POLL_DELAY_US, PC_POLL_TIMEOUT_US, + OFE_RST_DONE_IRQ_STATUS_BIT, OFE_RST_DONE_IRQ_STATUS_BIT, + &status); + + CAM_DBG(CAM_ICP, "ofe_top_irq_status = %u", status); + + if ((status & OFE_RST_DONE_IRQ_STATUS_BIT) == 0x1) + break; + retry_cnt++; + } + + if (retry_cnt == HFI_MAX_POLL_TRY) { + CAM_ERR(CAM_ICP, "OFE top rst failed status 0x%x", status); + reset_ofe_top_fail = true; + } + + cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, core_info->ofe_hw_info->pwr_ctrl, + true, &pwr_ctrl); + cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, core_info->ofe_hw_info->pwr_status, + true, &pwr_status); + CAM_DBG(CAM_ICP, "(After) pwr_ctrl = %x pwr_status = %x", + pwr_ctrl, pwr_status); + + if (reset_ofe_cdm_fail || reset_ofe_top_fail) + rc = -EAGAIN; + else + CAM_DBG(CAM_ICP, "OFE cdm and OFE top reset success"); + + return rc; +} + +int cam_ofe_process_cmd(void *device_priv, uint32_t cmd_type, + void *cmd_args, uint32_t arg_size) +{ + struct cam_hw_info *ofe_dev = device_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_ofe_device_core_info *core_info = NULL; + struct cam_ofe_device_hw_info *hw_info = NULL; + int rc = 0; + + if (!device_priv) { + CAM_ERR(CAM_ICP, "Invalid arguments"); + return -EINVAL; + } + + if (cmd_type >= CAM_ICP_OFE_CMD_MAX) { + CAM_ERR(CAM_ICP, "Invalid command : %x", cmd_type); + return -EINVAL; + } + + soc_info = &ofe_dev->soc_info; + core_info = (struct cam_ofe_device_core_info *)ofe_dev->core_info; + hw_info = core_info->ofe_hw_info; + + switch (cmd_type) { + case CAM_ICP_OFE_CMD_VOTE_CPAS: { + struct cam_icp_cpas_vote *cpas_vote = cmd_args; + + if (!cmd_args) { + CAM_ERR(CAM_ICP, "cmd args NULL"); + return -EINVAL; + } + + cam_ofe_cpas_vote(core_info, cpas_vote); + break; + } + + case CAM_ICP_OFE_CMD_CPAS_START: { + struct cam_icp_cpas_vote *cpas_vote = cmd_args; + + if (!cmd_args) { + CAM_ERR(CAM_ICP, "cmd args NULL"); + return -EINVAL; + } + + if (!core_info->cpas_start) { + rc = cam_cpas_start(core_info->cpas_handle, + &cpas_vote->ahb_vote, + &cpas_vote->axi_vote); + core_info->cpas_start = true; + } + break; + } + + case CAM_ICP_OFE_CMD_CPAS_STOP: + if (core_info->cpas_start) { + cam_cpas_stop(core_info->cpas_handle); + core_info->cpas_start = false; + } + break; + case CAM_ICP_OFE_CMD_POWER_COLLAPSE: + rc = cam_ofe_handle_pc(ofe_dev); + break; + case CAM_ICP_OFE_CMD_POWER_RESUME: + rc = cam_ofe_handle_resume(ofe_dev); + break; + case CAM_ICP_OFE_CMD_UPDATE_CLK: { + struct cam_icp_clk_update_cmd *clk_upd_cmd = cmd_args; + struct cam_ahb_vote ahb_vote; + uint32_t clk_rate = clk_upd_cmd->curr_clk_rate; + int32_t clk_level = 0, err = 0; + + CAM_DBG(CAM_PERF, "ofe_src_clk rate = %d", (int)clk_rate); + + if (!core_info->clk_enable) { + if (clk_upd_cmd->dev_pc_enable) { + cam_ofe_handle_pc(ofe_dev); + cam_cpas_reg_write(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, + hw_info->pwr_ctrl, true, 0x0); + } + rc = cam_ofe_toggle_clk(soc_info, true); + if (rc) + CAM_ERR(CAM_ICP, "Enable failed"); + else + core_info->clk_enable = true; + if (clk_upd_cmd->dev_pc_enable) { + rc = cam_ofe_handle_resume(ofe_dev); + if (rc) + CAM_ERR(CAM_ICP, "OFE resume failed"); + } + } + CAM_DBG(CAM_PERF, "clock rate %d", clk_rate); + rc = cam_ofe_update_clk_rate(soc_info, clk_rate); + if (rc) + CAM_ERR(CAM_PERF, "Failed to update clk %d", clk_rate); + + err = cam_soc_util_get_clk_level(soc_info, + clk_rate, soc_info->src_clk_idx, + &clk_level); + + if (!err) { + ahb_vote.type = CAM_VOTE_ABSOLUTE; + ahb_vote.vote.level = clk_level; + cam_cpas_update_ahb_vote( + core_info->cpas_handle, + &ahb_vote); + } + break; + } + case CAM_ICP_OFE_CMD_DISABLE_CLK: + if (core_info->clk_enable) + cam_ofe_toggle_clk(soc_info, false); + core_info->clk_enable = false; + break; + case CAM_ICP_OFE_CMD_RESET: + rc = cam_ofe_cmd_reset(soc_info, core_info); + break; + default: + CAM_ERR(CAM_ICP, "Invalid Cmd Type:%u", cmd_type); + rc = -EINVAL; + break; + } + return rc; +} + +irqreturn_t cam_ofe_irq(int irq_num, void *data) +{ + return IRQ_HANDLED; +} diff --git a/drivers/cam_icp/icp_hw/ofe_hw/ofe_core.h b/drivers/cam_icp/icp_hw/ofe_hw/ofe_core.h new file mode 100644 index 0000000000..41b76372ac --- /dev/null +++ b/drivers/cam_icp/icp_hw/ofe_hw/ofe_core.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef CAM_OFE_CORE_H +#define CAM_OFE_CORE_H + +#include +#include +#include +#include + +/* OFE CDM/TOP status register */ +#define OFE_RST_DONE_IRQ_STATUS_BIT 0x1 + +#define OFE_COLLAPSE_MASK 0x1 +#define OFE_PWR_ON_MASK 0x2 + +struct cam_ofe_device_hw_info { + uint32_t hw_idx; + uint32_t pwr_ctrl; + uint32_t pwr_status; + + uint32_t top_rst_cmd; + uint32_t top_irq_status; + uint32_t top_rst_val; + uint32_t cdm_rst_cmd; + uint32_t cdm_irq_status; + + uint32_t cdm_rst_val; +}; + +struct cam_ofe_device_core_info { + struct cam_ofe_device_hw_info *ofe_hw_info; + uint32_t cpas_handle; + bool cpas_start; + bool clk_enable; +}; + +int cam_ofe_init_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size); +int cam_ofe_deinit_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size); +int cam_ofe_process_cmd(void *device_priv, uint32_t cmd_type, + void *cmd_args, uint32_t arg_size); + +irqreturn_t cam_ofe_irq(int irq_num, void *data); + +/** + * @brief : API to register OFE hw to platform framework. + * @return struct platform_device pointer on success, or ERR_PTR() on error. + */ +int cam_ofe_init_module(void); + +/** + * @brief : API to remove OFE Hw from platform framework. + */ +void cam_ofe_exit_module(void); +#endif /* CAM_OFE_CORE_H */ diff --git a/drivers/cam_icp/icp_hw/ofe_hw/ofe_dev.c b/drivers/cam_icp/icp_hw/ofe_hw/ofe_dev.c new file mode 100644 index 0000000000..30a77fd899 --- /dev/null +++ b/drivers/cam_icp/icp_hw/ofe_hw/ofe_dev.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include "ofe_core.h" +#include "ofe_soc.h" +#include "cam_hw.h" +#include "cam_hw_intf.h" +#include "cam_io_util.h" +#include "cam_icp_hw_intf.h" +#include "cam_icp_hw_mgr_intf.h" +#include "cam_cpas_api.h" +#include "cam_debug_util.h" +#include "camera_main.h" + +static struct cam_ofe_device_hw_info cam_ofe_hw_info = { + .hw_idx = 0x0, + .pwr_ctrl = 0x100, + .pwr_status = 0xFC, + .top_rst_cmd = 0x5B4, + .top_irq_status = 0x5C4, + .top_rst_val = 0x3, + .cdm_rst_cmd = 0x10, + .cdm_irq_status = 0x44, + .cdm_rst_val = 0x7F, +}; + +static bool cam_ofe_cpas_cb(uint32_t client_handle, void *userdata, + struct cam_cpas_irq_data *irq_data) +{ + bool error_handled = false; + + if (!irq_data) + return error_handled; + + switch (irq_data->irq_type) { + case CAM_CAMNOC_IRQ_OFE_WR_UBWC_DECODE_ERROR: + CAM_ERR_RATE_LIMIT(CAM_ICP, + "OFE Write UBWC Decode error type=%d status=%x thr_err=%d, fcl_err=%d, len_md_err=%d, format_err=%d", + irq_data->irq_type, + irq_data->u.dec_err.decerr_status.value, + irq_data->u.dec_err.decerr_status.thr_err, + irq_data->u.dec_err.decerr_status.fcl_err, + irq_data->u.dec_err.decerr_status.len_md_err, + irq_data->u.dec_err.decerr_status.format_err); + error_handled = true; + break; + case CAM_CAMNOC_IRQ_OFE_RD_UBWC_DECODE_ERROR: + CAM_ERR_RATE_LIMIT(CAM_ICP, + "OFE Read UBWC Decode error type=%d status=%x thr_err=%d, fcl_err=%d, len_md_err=%d, format_err=%d", + irq_data->irq_type, + irq_data->u.dec_err.decerr_status.value, + irq_data->u.dec_err.decerr_status.thr_err, + irq_data->u.dec_err.decerr_status.fcl_err, + irq_data->u.dec_err.decerr_status.len_md_err, + irq_data->u.dec_err.decerr_status.format_err); + error_handled = true; + break; + default: + break; + } + + return error_handled; +} + +int cam_ofe_register_cpas(struct cam_hw_soc_info *soc_info, + struct cam_ofe_device_core_info *core_info, uint32_t hw_idx) +{ + struct cam_cpas_register_params cpas_register_params; + int rc; + + cpas_register_params.dev = &soc_info->pdev->dev; + memcpy(cpas_register_params.identifier, "ofe", sizeof("ofe")); + cpas_register_params.cam_cpas_client_cb = cam_ofe_cpas_cb; + cpas_register_params.cell_index = hw_idx; + cpas_register_params.userdata = NULL; + + rc = cam_cpas_register_client(&cpas_register_params); + if (rc < 0) { + CAM_ERR(CAM_ICP, "failed: %d", rc); + return rc; + } + core_info->cpas_handle = cpas_register_params.client_handle; + + return rc; +} + +static int cam_ofe_component_bind(struct device *dev, + struct device *master_dev, void *data) +{ + struct cam_hw_info *ofe_dev = NULL; + struct cam_hw_intf *ofe_dev_intf = NULL; + const struct of_device_id *match_dev = NULL; + struct cam_ofe_device_core_info *core_info = NULL; + struct cam_ofe_device_hw_info *hw_info = NULL; + int rc = 0; + struct platform_device *pdev = to_platform_device(dev); + + ofe_dev_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL); + if (!ofe_dev_intf) + return -ENOMEM; + + of_property_read_u32(pdev->dev.of_node, + "cell-index", &ofe_dev_intf->hw_idx); + + ofe_dev = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL); + if (!ofe_dev) { + rc = -ENOMEM; + goto free_dev_intf; + } + + ofe_dev->soc_info.pdev = pdev; + ofe_dev->soc_info.dev = &pdev->dev; + ofe_dev->soc_info.dev_name = pdev->name; + ofe_dev_intf->hw_priv = ofe_dev; + ofe_dev_intf->hw_ops.init = cam_ofe_init_hw; + ofe_dev_intf->hw_ops.deinit = cam_ofe_deinit_hw; + ofe_dev_intf->hw_ops.process_cmd = cam_ofe_process_cmd; + ofe_dev_intf->hw_type = CAM_ICP_DEV_OFE; + platform_set_drvdata(pdev, ofe_dev_intf); + ofe_dev->core_info = kzalloc(sizeof(struct cam_ofe_device_core_info), GFP_KERNEL); + if (!ofe_dev->core_info) { + rc = -ENOMEM; + goto free_dev; + } + core_info = (struct cam_ofe_device_core_info *)ofe_dev->core_info; + + match_dev = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); + if (!match_dev) { + CAM_ERR(CAM_ICP, "No ofe hardware info"); + goto free_core_info; + } + + hw_info = (struct cam_ofe_device_hw_info *)match_dev->data; + core_info->ofe_hw_info = hw_info; + + rc = cam_ofe_init_soc_resources(&ofe_dev->soc_info, cam_ofe_irq, ofe_dev); + if (rc) { + CAM_ERR(CAM_ICP, "failed to init_soc"); + goto free_core_info; + } + CAM_DBG(CAM_ICP, "soc info : %pK", (void *)&ofe_dev->soc_info); + + rc = cam_ofe_register_cpas(&ofe_dev->soc_info, + core_info, ofe_dev_intf->hw_idx); + if (rc) + goto free_soc_resources; + + ofe_dev->hw_state = CAM_HW_STATE_POWER_DOWN; + mutex_init(&ofe_dev->hw_mutex); + spin_lock_init(&ofe_dev->hw_lock); + init_completion(&ofe_dev->hw_complete); + CAM_DBG(CAM_ICP, "OFE:%d component bound successfully", + ofe_dev_intf->hw_idx); + + return rc; + +free_soc_resources: + cam_ofe_deinit_soc_resources(&ofe_dev->soc_info); +free_core_info: + kfree(ofe_dev->core_info); +free_dev: + kfree(ofe_dev); +free_dev_intf: + kfree(ofe_dev_intf); + + return rc; +} + +static void cam_ofe_component_unbind(struct device *dev, + struct device *master_dev, void *data) +{ + struct cam_hw_info *ofe_dev = NULL; + struct cam_hw_intf *ofe_dev_intf = NULL; + struct cam_ofe_device_core_info *core_info = NULL; + struct platform_device *pdev = to_platform_device(dev); + + CAM_DBG(CAM_ICP, "Unbinding component: %s", pdev->name); + ofe_dev_intf = platform_get_drvdata(pdev); + ofe_dev = ofe_dev_intf->hw_priv; + core_info = (struct cam_ofe_device_core_info *)ofe_dev->core_info; + cam_cpas_unregister_client(core_info->cpas_handle); + cam_ofe_deinit_soc_resources(&ofe_dev->soc_info); + + kfree(ofe_dev->core_info); + kfree(ofe_dev); + kfree(ofe_dev_intf); +} + +static const struct component_ops cam_ofe_component_ops = { + .bind = cam_ofe_component_bind, + .unbind = cam_ofe_component_unbind, +}; + +int cam_ofe_probe(struct platform_device *pdev) +{ + int rc = 0; + + CAM_DBG(CAM_ICP, "Adding OFE component"); + rc = component_add(&pdev->dev, &cam_ofe_component_ops); + if (rc) + CAM_ERR(CAM_ICP, "failed to add component rc: %d", rc); + + return rc; +} + +static int cam_ofe_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &cam_ofe_component_ops); + return 0; +} + +static const struct of_device_id cam_ofe_dt_match[] = { + { + .compatible = "qcom,cam-ofe", + .data = &cam_ofe_hw_info, + }, + {} +}; +MODULE_DEVICE_TABLE(of, cam_ofe_dt_match); + +struct platform_driver cam_ofe_driver = { + .probe = cam_ofe_probe, + .remove = cam_ofe_remove, + .driver = { + .name = "cam-ofe", + .owner = THIS_MODULE, + .of_match_table = cam_ofe_dt_match, + .suppress_bind_attrs = true, + }, +}; + +int cam_ofe_init_module(void) +{ + return platform_driver_register(&cam_ofe_driver); +} + +void cam_ofe_exit_module(void) +{ + platform_driver_unregister(&cam_ofe_driver); +} + +MODULE_DESCRIPTION("CAM OFE driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/cam_icp/icp_hw/ofe_hw/ofe_soc.c b/drivers/cam_icp/icp_hw/ofe_hw/ofe_soc.c new file mode 100644 index 0000000000..3f1603c793 --- /dev/null +++ b/drivers/cam_icp/icp_hw/ofe_hw/ofe_soc.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include "ofe_soc.h" +#include "cam_soc_util.h" +#include "cam_debug_util.h" + +static int cam_ofe_get_dt_properties(struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc < 0) + CAM_ERR(CAM_ICP, "get ofe dt prop is failed"); + + return rc; +} + +static int cam_ofe_request_platform_resource( + struct cam_hw_soc_info *soc_info, + irq_handler_t ofe_irq_handler, void *irq_data) +{ + int rc = 0; + + rc = cam_soc_util_request_platform_resource(soc_info, + ofe_irq_handler, irq_data); + + return rc; +} + +int cam_ofe_init_soc_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t ofe_irq_handler, void *irq_data) +{ + int rc = 0; + + rc = cam_ofe_get_dt_properties(soc_info); + if (rc < 0) + return rc; + + rc = cam_ofe_request_platform_resource(soc_info, + ofe_irq_handler, irq_data); + if (rc < 0) + return rc; + + return rc; +} + +void cam_ofe_deinit_soc_resources(struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + + rc = cam_soc_util_release_platform_resource(soc_info); + if (rc) + CAM_WARN(CAM_ICP, "release platform resources fail"); +} + +int cam_ofe_enable_soc_resources(struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + + rc = cam_soc_util_enable_platform_resource(soc_info, + true, CAM_SVS_VOTE, false); + if (rc) + CAM_ERR(CAM_ICP, "enable platform failed"); + + return rc; +} + +int cam_ofe_disable_soc_resources(struct cam_hw_soc_info *soc_info, + bool disable_clk) +{ + int rc = 0; + + rc = cam_soc_util_disable_platform_resource(soc_info, + disable_clk, false); + if (rc) + CAM_ERR(CAM_ICP, "disable platform failed"); + + return rc; +} + +static inline int cam_ofe_set_regulators_mode(struct cam_hw_soc_info *soc_info, uint32_t mode) +{ + int i, rc; + + for (i = 0; i < soc_info->num_rgltr; i++) { + rc = regulator_set_mode(soc_info->rgltr[i], mode); + if (rc) { + CAM_ERR(CAM_ICP, "Regulator set mode %s failed", + soc_info->rgltr_name[i]); + goto rgltr_set_mode_failed; + } + } + return 0; + +rgltr_set_mode_failed: + for (i = i - 1; i >= 0; i--) { + if (soc_info->rgltr[i]) + regulator_set_mode(soc_info->rgltr[i], + (mode == REGULATOR_MODE_NORMAL ? + REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL)); + } + + return rc; +} + +int cam_ofe_transfer_gdsc_control(struct cam_hw_soc_info *soc_info) +{ + return cam_ofe_set_regulators_mode(soc_info, REGULATOR_MODE_FAST); +} + +int cam_ofe_get_gdsc_control(struct cam_hw_soc_info *soc_info) +{ + return cam_ofe_set_regulators_mode(soc_info, REGULATOR_MODE_NORMAL); +} + +int cam_ofe_update_clk_rate(struct cam_hw_soc_info *soc_info, + uint32_t clk_rate) +{ + int32_t src_clk_idx; + + if (!soc_info) + return -EINVAL; + + src_clk_idx = soc_info->src_clk_idx; + + if ((soc_info->clk_level_valid[CAM_TURBO_VOTE] == true) && + (soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx] != 0) && + (clk_rate > soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx])) { + CAM_DBG(CAM_PERF, "clk_rate %d greater than max, reset to %d", + clk_rate, + soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx]); + clk_rate = soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx]; + } + + return cam_soc_util_set_src_clk_rate(soc_info, clk_rate); +} + +int cam_ofe_toggle_clk(struct cam_hw_soc_info *soc_info, bool clk_enable) +{ + int rc = 0; + + if (clk_enable) + rc = cam_soc_util_clk_enable_default(soc_info, CAM_SVS_VOTE); + else + cam_soc_util_clk_disable_default(soc_info); + + return rc; +} diff --git a/drivers/cam_icp/icp_hw/ofe_hw/ofe_soc.h b/drivers/cam_icp/icp_hw/ofe_hw/ofe_soc.h new file mode 100644 index 0000000000..6bae41d2f0 --- /dev/null +++ b/drivers/cam_icp/icp_hw/ofe_hw/ofe_soc.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _CAM_OFE_SOC_H_ +#define _CAM_OFE_SOC_H_ + +#include "cam_soc_util.h" + +int cam_ofe_init_soc_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t ofe_irq_handler, void *irq_data); + +int cam_ofe_enable_soc_resources(struct cam_hw_soc_info *soc_info); + +int cam_ofe_disable_soc_resources(struct cam_hw_soc_info *soc_info, + bool disable_clk); + +int cam_ofe_get_gdsc_control(struct cam_hw_soc_info *soc_info); + +int cam_ofe_transfer_gdsc_control(struct cam_hw_soc_info *soc_info); + +int cam_ofe_update_clk_rate(struct cam_hw_soc_info *soc_info, + uint32_t clk_rate); + +int cam_ofe_toggle_clk(struct cam_hw_soc_info *soc_info, bool clk_enable); + +void cam_ofe_deinit_soc_resources(struct cam_hw_soc_info *soc_info); +#endif /* _CAM_OFE_SOC_H_*/ diff --git a/drivers/camera_main.c b/drivers/camera_main.c index 58c8bc8233..eba5c814d9 100644 --- a/drivers/camera_main.c +++ b/drivers/camera_main.c @@ -31,6 +31,7 @@ #include "cam_icp_v2_dev.h" #include "ipe_core.h" #include "bps_core.h" +#include "ofe_core.h" #include "cam_icp_subdev.h" #include "jpeg_dma_core.h" @@ -128,6 +129,7 @@ static const struct camera_submodule_component camera_icp[] = { {&cam_icp_v2_init_module, &cam_icp_v2_exit_module}, {&cam_ipe_init_module, &cam_ipe_exit_module}, {&cam_bps_init_module, &cam_bps_exit_module}, + {&cam_ofe_init_module, &cam_ofe_exit_module}, {&cam_icp_init_module, &cam_icp_exit_module}, #endif }; diff --git a/drivers/camera_main.h b/drivers/camera_main.h index c8db2a2e5b..536bb5be8a 100644 --- a/drivers/camera_main.h +++ b/drivers/camera_main.h @@ -53,6 +53,7 @@ extern struct platform_driver cam_icp_v2_driver; extern struct platform_driver cam_ipe_driver; extern struct platform_driver cam_bps_driver; extern struct platform_driver cam_icp_driver; +extern struct platform_driver cam_ofe_driver; #endif #ifdef CONFIG_SPECTRA_OPE extern struct platform_driver cam_ope_driver; @@ -124,6 +125,7 @@ static struct platform_driver *const cam_component_platform_drivers[] = { &cam_ipe_driver, &cam_bps_driver, &cam_icp_driver, + &cam_ofe_driver, #endif #ifdef CONFIG_SPECTRA_OPE &cam_ope_driver,