diff --git a/Kbuild b/Kbuild index 9816302b82..fe2085bd18 100644 --- a/Kbuild +++ b/Kbuild @@ -149,6 +149,9 @@ camera-$(CONFIG_SPECTRA_ICP) += \ drivers/cam_icp/icp_hw/a5_hw/a5_dev.o \ drivers/cam_icp/icp_hw/a5_hw/a5_core.o \ drivers/cam_icp/icp_hw/a5_hw/a5_soc.o \ + drivers/cam_icp/icp_hw/lx7_hw/lx7_dev.o \ + drivers/cam_icp/icp_hw/lx7_hw/lx7_core.o \ + drivers/cam_icp/icp_hw/lx7_hw/lx7_soc.o \ 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 \ diff --git a/drivers/cam_icp/icp_hw/a5_hw/a5_core.c b/drivers/cam_icp/icp_hw/a5_hw/a5_core.c index dbe5bae3dc..4dc6c1cedb 100644 --- a/drivers/cam_icp/icp_hw/a5_hw/a5_core.c +++ b/drivers/cam_icp/icp_hw/a5_hw/a5_core.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. */ #include @@ -336,15 +336,15 @@ int cam_a5_init_hw(void *device_priv, cpas_vote.axi_vote.axi_path[0].transac_type = CAM_ICP_DEFAULT_AXI_TRANSAC; cpas_vote.axi_vote.axi_path[0].camnoc_bw = - CAM_ICP_A5_BW_BYTES_VOTE; + CAM_ICP_BW_BYTES_VOTE; cpas_vote.axi_vote.axi_path[0].mnoc_ab_bw = - CAM_ICP_A5_BW_BYTES_VOTE; + CAM_ICP_BW_BYTES_VOTE; cpas_vote.axi_vote.axi_path[0].mnoc_ib_bw = - CAM_ICP_A5_BW_BYTES_VOTE; + CAM_ICP_BW_BYTES_VOTE; cpas_vote.axi_vote.axi_path[0].ddr_ab_bw = - CAM_ICP_A5_BW_BYTES_VOTE; + CAM_ICP_BW_BYTES_VOTE; cpas_vote.axi_vote.axi_path[0].ddr_ib_bw = - CAM_ICP_A5_BW_BYTES_VOTE; + CAM_ICP_BW_BYTES_VOTE; rc = cam_cpas_start(core_info->cpas_handle, &cpas_vote.ahb_vote, &cpas_vote.axi_vote); @@ -558,6 +558,7 @@ irqreturn_t cam_a5_irq(int irq_num, void *data) struct cam_a5_device_core_info *core_info = NULL; struct cam_a5_device_hw_info *hw_info = NULL; uint32_t irq_status = 0; + bool recover = false; if (!data) { CAM_ERR(CAM_ICP, "Invalid cam_dev_info or query_cap args"); @@ -586,11 +587,13 @@ irqreturn_t cam_a5_irq(int irq_num, void *data) if ((irq_status & A5_WDT_0) || (irq_status & A5_WDT_1)) { CAM_ERR_RATE_LIMIT(CAM_ICP, "watch dog interrupt from A5"); + recover = true; } spin_lock(&a5_dev->hw_lock); if (core_info->irq_cb.cb) - core_info->irq_cb.cb(core_info->irq_cb.data, irq_status); + core_info->irq_cb.cb(core_info->irq_cb.data, + recover); spin_unlock(&a5_dev->hw_lock); return IRQ_HANDLED; diff --git a/drivers/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c b/drivers/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c index 3f85b4b846..c887d4840d 100644 --- a/drivers/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c +++ b/drivers/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. */ #include @@ -38,6 +38,7 @@ #include "cam_req_mgr_workq.h" #include "cam_mem_mgr.h" #include "a5_core.h" +#include "lx7_core.h" #include "hfi_sys_defs.h" #include "cam_debug_util.h" #include "cam_soc_util.h" @@ -59,6 +60,12 @@ static const struct hfi_ops hfi_a5_ops = { .iface_addr = cam_a5_iface_addr, }; +static const struct hfi_ops hfi_lx7_ops = { + .irq_raise = cam_lx7_irq_raise, + .irq_enable = cam_lx7_irq_enable, + .iface_addr = cam_lx7_iface_addr, +}; + static struct cam_icp_hw_mgr icp_hw_mgr; static void cam_icp_mgr_process_dbg_buf(unsigned int debug_lvl); @@ -2653,9 +2660,8 @@ static int32_t cam_icp_mgr_process_msg(void *priv, void *data) cam_icp_mgr_process_dbg_buf(icp_hw_mgr.icp_dbg_lvl); - if ((task_data->irq_status & A5_WDT_0) || - (task_data->irq_status & A5_WDT_1)) { - CAM_ERR_RATE_LIMIT(CAM_ICP, "watch dog interrupt from A5"); + if (task_data->recover) { + CAM_ERR_RATE_LIMIT(CAM_ICP, "issuing device recovery..."); rc = cam_icp_mgr_trigger_recovery(hw_mgr); } @@ -2663,7 +2669,7 @@ static int32_t cam_icp_mgr_process_msg(void *priv, void *data) return rc; } -static int cam_icp_hw_mgr_cb(void *data, uint32_t irq_status) +static int32_t cam_icp_hw_mgr_cb(void *data, bool recover) { int rc = 0; unsigned long flags; @@ -2686,7 +2692,7 @@ static int cam_icp_hw_mgr_cb(void *data, uint32_t irq_status) task_data = (struct hfi_msg_work_data *)task->payload; task_data->data = hw_mgr; - task_data->irq_status = irq_status; + task_data->recover = recover; task_data->type = ICP_WORKQ_TASK_MSG_TYPE; task->process_cb = cam_icp_mgr_process_msg; rc = cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr, @@ -3726,7 +3732,10 @@ static int cam_icp_mgr_hfi_init(struct cam_icp_hw_mgr *hw_mgr) hfi_mem.io_mem2.len = 0x0; } - hfi_ops = &hfi_a5_ops; + if (icp_dev_intf->hw_type == CAM_ICP_DEV_LX7) + hfi_ops = &hfi_lx7_ops; + else + hfi_ops = &hfi_a5_ops; return cam_hfi_init(&hfi_mem, hfi_ops, icp_dev, 0); } diff --git a/drivers/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h b/drivers/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h index e179e2cf5d..ac1e4a12cb 100644 --- a/drivers/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h +++ b/drivers/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. */ #ifndef CAM_ICP_HW_MGR_H @@ -115,12 +115,12 @@ struct hfi_cmd_work_data { * struct hfi_msg_work_data * @type: Task type * @data: Pointer to message data - * @irq_status: IRQ status + * @recover: Device needs recovery */ struct hfi_msg_work_data { uint32_t type; void *data; - uint32_t irq_status; + bool recover; }; /** 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 5be025a194..99f722fb4f 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 @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. */ #ifndef CAM_ICP_HW_INTF_H @@ -17,6 +17,7 @@ enum cam_icp_hw_type { CAM_ICP_DEV_A5, + CAM_ICP_DEV_LX7, CAM_ICP_DEV_IPE, CAM_ICP_DEV_BPS, CAM_ICP_DEV_MAX, @@ -41,8 +42,8 @@ enum cam_icp_cmd_type { }; struct cam_icp_irq_cb { + int32_t (*cb)(void *data, bool recover); void *data; - int (*cb)(void *data, uint32_t irq_status); }; /** 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 c18e4053bc..c072069eec 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 @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. */ #ifndef CAM_ICP_HW_MGR_INTF_H @@ -14,7 +14,7 @@ #define ICP_CLK_TURBO_HZ 600000000 #define ICP_CLK_SVS_HZ 400000000 -#define CAM_ICP_A5_BW_BYTES_VOTE 40000000 +#define CAM_ICP_BW_BYTES_VOTE 40000000 #define CAM_ICP_CTX_MAX 54 diff --git a/drivers/cam_icp/icp_hw/lx7_hw/lx7_core.c b/drivers/cam_icp/icp_hw/lx7_hw/lx7_core.c new file mode 100644 index 0000000000..2c0cb5e75f --- /dev/null +++ b/drivers/cam_icp/icp_hw/lx7_hw/lx7_core.c @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include + +#include "cam_cpas_api.h" +#include "cam_debug_util.h" +#include "cam_hw.h" +#include "cam_hw_intf.h" +#include "cam_icp_hw_mgr_intf.h" +#include "cam_icp_hw_intf.h" +#include "hfi_intf.h" +#include "lx7_core.h" +#include "lx7_reg.h" +#include "lx7_soc.h" + +#define TZ_STATE_RESUME 0 +#define TZ_STATE_SUSPEND 1 + +#define LX7_FW_NAME "CAMERA_ICP.elf" + +#define LX7_GEN_PURPOSE_REG_OFFSET 0x20 + +static int cam_lx7_ubwc_configure(struct cam_hw_soc_info *soc_info) +{ + int i = 0, rc, ddr_type; + struct lx7_soc_info *soc_priv; + uint32_t ipe_ubwc_cfg[UBWC_CONFIG_MAX]; + uint32_t bps_ubwc_cfg[UBWC_CONFIG_MAX]; + + if (!soc_info || !soc_info->soc_private) { + CAM_ERR(CAM_ICP, "invalid LX7 soc info"); + return -EINVAL; + } + + soc_priv = soc_info->soc_private; + + ddr_type = of_fdt_get_ddrtype(); + if (ddr_type == DDR_TYPE_LPDDR5 || ddr_type == DDR_TYPE_LPDDR5X) + i = 1; + + ipe_ubwc_cfg[0] = soc_priv->ubwc_cfg.ipe_fetch[i]; + ipe_ubwc_cfg[1] = soc_priv->ubwc_cfg.ipe_write[i]; + + bps_ubwc_cfg[0] = soc_priv->ubwc_cfg.bps_fetch[i]; + bps_ubwc_cfg[1] = soc_priv->ubwc_cfg.bps_write[i]; + + rc = hfi_cmd_ubwc_config_ext(ipe_ubwc_cfg, bps_ubwc_cfg); + if (rc) { + CAM_ERR(CAM_ICP, "failed to write UBWC config rc=%d", rc); + return rc; + } + + return 0; +} + +static int cam_lx7_cpas_vote(struct cam_lx7_core_info *core_info, + struct cam_icp_cpas_vote *vote) +{ + int rc; + + if (!core_info || !vote) + return -EINVAL; + + if (vote->ahb_vote_valid) { + rc = cam_cpas_update_ahb_vote(core_info->cpas_handle, + &vote->ahb_vote); + if (rc) { + CAM_ERR(CAM_ICP, "AHB vote update failed rc=%d", rc); + return rc; + } + } + + if (vote->axi_vote_valid) { + rc = cam_cpas_update_axi_vote(core_info->cpas_handle, + &vote->axi_vote); + if (rc) { + CAM_ERR(CAM_ICP, "AXI vote update failed rc=%d", rc); + return rc; + } + } + + return 0; +} + +static bool cam_lx7_cpas_cb(uint32_t handle, void *user_data, + struct cam_cpas_irq_data *irq_data) +{ + bool ret = false; + (void)user_data; + + if (!irq_data) + return false; + + switch (irq_data->irq_type) { + case CAM_CAMNOC_IRQ_IPE_BPS_UBWC_DECODE_ERROR: + CAM_ERR_RATE_LIMIT(CAM_ICP, + "IPE/BPS UBWC decode error status=0x%08x", + irq_data->u.dec_err.decerr_status.value); + ret = true; + case CAM_CAMNOC_IRQ_IPE_BPS_UBWC_ENCODE_ERROR: + CAM_ERR_RATE_LIMIT(CAM_ICP, + "IPE/BPS UBWC encode error status=0x%08x", + irq_data->u.enc_err.encerr_status.value); + ret = true; + default: + CAM_ERR(CAM_ICP, "unhandled irq_type=%d", irq_data->irq_type); + break; + } + + return ret; +} + +int cam_lx7_cpas_register(struct cam_hw_intf *lx7_intf) +{ + struct cam_cpas_register_params params; + struct cam_hw_info *lx7_info; + struct cam_lx7_core_info *core_info; + int rc; + + if (!lx7_intf) + return -EINVAL; + + lx7_info = lx7_intf->hw_priv; + + params.dev = lx7_info->soc_info.dev; + params.cell_index = lx7_intf->hw_idx; + params.cam_cpas_client_cb = cam_lx7_cpas_cb; + params.userdata = NULL; + + strlcpy(params.identifier, "icp", CAM_HW_IDENTIFIER_LENGTH); + + rc = cam_cpas_register_client(¶ms); + if (rc) + return rc; + + core_info = lx7_info->core_info; + core_info->cpas_handle = params.client_handle; + + return rc; +} + +int cam_lx7_cpas_unregister(struct cam_hw_intf *lx7_intf) +{ + struct cam_hw_info *lx7_info; + struct cam_lx7_core_info *core_info; + + if (!lx7_intf) + return -EINVAL; + + lx7_info = lx7_intf->hw_priv; + core_info = lx7_info->core_info; + + return cam_cpas_unregister_client(core_info->cpas_handle); +} + +static int __lx7_cpas_start(struct cam_lx7_core_info *core_info, + struct cam_icp_cpas_vote *vote) +{ + int rc; + + if (!core_info || core_info->cpas_start) + return -EINVAL; + + rc = cam_cpas_start(core_info->cpas_handle, + &vote->ahb_vote, &vote->axi_vote); + if (rc) { + CAM_ERR(CAM_ICP, "failed to start cpas rc=%d", rc); + return rc; + } + + core_info->cpas_start = true; + + return 0; +} + +static int cam_lx7_cpas_start(struct cam_lx7_core_info *core_info) +{ + struct cam_icp_cpas_vote vote; + + vote.ahb_vote.type = CAM_VOTE_ABSOLUTE; + vote.ahb_vote.vote.level = CAM_LOWSVS_VOTE; + vote.axi_vote.num_paths = 1; + + vote.axi_vote.axi_path[0].path_data_type = CAM_ICP_DEFAULT_AXI_PATH; + vote.axi_vote.axi_path[0].transac_type = CAM_ICP_DEFAULT_AXI_TRANSAC; + vote.axi_vote.axi_path[0].camnoc_bw = CAM_ICP_BW_BYTES_VOTE; + vote.axi_vote.axi_path[0].mnoc_ab_bw = CAM_ICP_BW_BYTES_VOTE; + vote.axi_vote.axi_path[0].mnoc_ib_bw = CAM_ICP_BW_BYTES_VOTE; + vote.axi_vote.axi_path[0].ddr_ab_bw = CAM_ICP_BW_BYTES_VOTE; + vote.axi_vote.axi_path[0].ddr_ib_bw = CAM_ICP_BW_BYTES_VOTE; + + return __lx7_cpas_start(core_info, &vote); +} + +static int cam_lx7_cpas_stop(struct cam_lx7_core_info *core_info) +{ + int rc; + + if (!core_info || !core_info->cpas_start) + return -EINVAL; + + rc = cam_cpas_stop(core_info->cpas_handle); + if (rc) { + CAM_ERR(CAM_ICP, "failed to stop cpas rc=%d", rc); + return rc; + } + + core_info->cpas_start = false; + + return 0; +} + +int cam_lx7_hw_init(void *priv, void *args, uint32_t arg_size) +{ + struct cam_hw_info *lx7 = priv; + int rc; + + if (!lx7) { + CAM_ERR(CAM_ICP, "LX7 device info cannot be NULL"); + return -EINVAL; + } + + rc = cam_lx7_cpas_start(lx7->core_info); + if (rc) + return rc; + + rc = cam_lx7_soc_resources_enable(&lx7->soc_info); + if (rc) { + CAM_ERR(CAM_ICP, "failed to enable soc resources rc=%d", rc); + goto soc_fail; + } + + return 0; + +soc_fail: + cam_lx7_cpas_stop(lx7->core_info); + return rc; +} + +int cam_lx7_hw_deinit(void *priv, void *args, uint32_t arg_size) +{ + struct cam_hw_info *lx7_info = priv; + int rc; + + if (!lx7_info) { + CAM_ERR(CAM_ICP, "LX7 device info cannot be NULL"); + return -EINVAL; + } + + rc = cam_lx7_soc_resources_disable(&lx7_info->soc_info); + if (rc) { + CAM_ERR(CAM_ICP, "failed to disable soc resources rc=%d", rc); + return rc; + } + + return cam_lx7_cpas_stop(lx7_info->core_info); +} + +static int __tz_set_icp_state(uint32_t state) +{ + int rc; + + rc = qcom_scm_set_remote_state(state, CAM_FW_PAS_ID); + if (rc) + CAM_ERR(CAM_ICP, "remote state set to %s failed rc=%d", + state == TZ_STATE_RESUME ? "resume" : "suspend", rc); + + return rc; +} + +int cam_lx7_process_cmd(void *priv, uint32_t cmd_type, + void *args, uint32_t arg_size) +{ + struct cam_hw_info *lx7_info = priv; + int rc = -EINVAL; + + if (!lx7_info) { + CAM_ERR(CAM_ICP, "LX7 device info cannot be NULL"); + return -EINVAL; + } + + switch (cmd_type) { + case CAM_ICP_CMD_POWER_COLLAPSE: + rc = __tz_set_icp_state(TZ_STATE_SUSPEND); + break; + case CAM_ICP_CMD_POWER_RESUME: + rc = __tz_set_icp_state(TZ_STATE_RESUME); + break; + case CAM_ICP_CMD_VOTE_CPAS: + rc = cam_lx7_cpas_vote(lx7_info->core_info, args); + break; + case CAM_ICP_CMD_CPAS_START: + rc = __lx7_cpas_start(lx7_info->core_info, args); + break; + case CAM_ICP_CMD_CPAS_STOP: + rc = cam_lx7_cpas_stop(lx7_info->core_info); + break; + case CAM_ICP_CMD_UBWC_CFG: + rc = cam_lx7_ubwc_configure(&lx7_info->soc_info); + break; + default: + CAM_ERR(CAM_ICP, "invalid command type=%u", cmd_type); + break; + } + + return rc; +} + +irqreturn_t cam_lx7_handle_irq(int irq_num, void *data) +{ + struct cam_hw_info *lx7_info = data; + struct cam_lx7_core_info *core_info = NULL; + bool recover = false; + uint32_t status = 0; + void __iomem *cirq_base; + + if (!lx7_info) { + CAM_ERR(CAM_ICP, "invalid LX7 device info"); + return IRQ_NONE; + } + + cirq_base = lx7_info->soc_info.reg_map[LX7_CIRQ_BASE].mem_base; + + status = cam_io_r_mb(cirq_base + ICP_LX7_CIRQ_OB_STATUS); + + cam_io_w_mb(status, cirq_base + ICP_LX7_CIRQ_OB_CLEAR); + cam_io_w_mb(LX7_IRQ_CLEAR_CMD, cirq_base + ICP_LX7_CIRQ_OB_IRQ_CMD); + + if (status & (LX7_WDT_BITE_WS0 | LX7_WDT_BITE_WS1)) { + CAM_ERR_RATE_LIMIT(CAM_ICP, "got watchdog interrupt from LX7"); + recover = true; + } + + core_info = lx7_info->core_info; + + spin_lock(&lx7_info->hw_lock); + if (core_info->irq_cb.cb) + core_info->irq_cb.cb(core_info->irq_cb.data, + recover); + spin_unlock(&lx7_info->hw_lock); + + return IRQ_HANDLED; +} + +void cam_lx7_irq_raise(void *priv) +{ + struct cam_hw_info *lx7_info = priv; + + if (!lx7_info) { + CAM_ERR(CAM_ICP, "invalid LX7 device info"); + return; + } + + cam_io_w_mb(LX7_HOST2ICPINT, + lx7_info->soc_info.reg_map[LX7_CIRQ_BASE].mem_base + + ICP_LX7_CIRQ_HOST2ICPINT); +} + +void cam_lx7_irq_enable(void *priv) +{ + struct cam_hw_info *lx7_info = priv; + + if (!lx7_info) { + CAM_ERR(CAM_ICP, "invalid LX7 device info"); + return; + } + + cam_io_w_mb(LX7_WDT_BITE_WS0 | LX7_ICP2HOSTINT, + lx7_info->soc_info.reg_map[LX7_CIRQ_BASE].mem_base + + ICP_LX7_CIRQ_OB_MASK); +} + +void __iomem *cam_lx7_iface_addr(void *priv) +{ + struct cam_hw_info *lx7_info = priv; + void __iomem *base; + + if (!lx7_info) { + CAM_ERR(CAM_ICP, "invalid LX7 device info"); + return ERR_PTR(-EINVAL); + } + + base = lx7_info->soc_info.reg_map[LX7_CSR_BASE].mem_base; + + return base + LX7_GEN_PURPOSE_REG_OFFSET; +} diff --git a/drivers/cam_icp/icp_hw/lx7_hw/lx7_core.h b/drivers/cam_icp/icp_hw/lx7_hw/lx7_core.h new file mode 100644 index 0000000000..ce81618a76 --- /dev/null +++ b/drivers/cam_icp/icp_hw/lx7_hw/lx7_core.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#ifndef _CAM_LX7_CORE_H_ +#define _CAM_LX7_CORE_H_ + +#include "cam_hw_intf.h" +#include "cam_icp_hw_intf.h" + +#define LX7_CSR_BASE 0 +#define LX7_CIRQ_BASE 1 + +/* TODO: Update once we're ready to use TZ */ +#define UNSUPPORTED_PROC_PAS_ID 30 +#define CAM_FW_PAS_ID UNSUPPORTED_PROC_PAS_ID + +struct cam_lx7_core_info { + struct cam_icp_irq_cb irq_cb; + uint32_t cpas_handle; + bool cpas_start; +}; + +int cam_lx7_hw_init(void *priv, void *args, uint32_t arg_size); +int cam_lx7_hw_deinit(void *priv, void *args, uint32_t arg_size); +int cam_lx7_process_cmd(void *priv, uint32_t cmd_type, + void *args, uint32_t arg_size); + +int cam_lx7_cpas_register(struct cam_hw_intf *lx7_intf); +int cam_lx7_cpas_unregister(struct cam_hw_intf *lx7_intf); + +irqreturn_t cam_lx7_handle_irq(int irq_num, void *data); + +void cam_lx7_irq_raise(void *priv); +void cam_lx7_irq_enable(void *priv); +void __iomem *cam_lx7_iface_addr(void *priv); + +#endif /* _CAM_LX7_CORE_H_ */ diff --git a/drivers/cam_icp/icp_hw/lx7_hw/lx7_dev.c b/drivers/cam_icp/icp_hw/lx7_hw/lx7_dev.c new file mode 100644 index 0000000000..53707bfcfc --- /dev/null +++ b/drivers/cam_icp/icp_hw/lx7_hw/lx7_dev.c @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include + +#include "camera_main.h" +#include "cam_debug_util.h" +#include "cam_hw.h" +#include "cam_hw_intf.h" +#include "cam_icp_hw_intf.h" +#include "lx7_core.h" +#include "lx7_soc.h" + +static int cam_lx7_component_bind(struct device *dev, + struct device *mdev, void *data) +{ + int rc = 0; + struct cam_hw_intf *lx7_intf = NULL; + struct cam_hw_info *lx7_info = NULL; + struct lx7_soc_info *lx7_soc_info = NULL; + struct cam_lx7_core_info *core_info = NULL; + struct platform_device *pdev = to_platform_device(dev); + + lx7_intf = kzalloc(sizeof(*lx7_intf), GFP_KERNEL); + if (!lx7_intf) + return -ENOMEM; + + lx7_info = kzalloc(sizeof(*lx7_info), GFP_KERNEL); + if (!lx7_info) { + rc = -ENOMEM; + goto free_hw_intf; + } + + lx7_soc_info = kzalloc(sizeof(*lx7_soc_info), GFP_KERNEL); + if (!lx7_soc_info) { + rc = -ENOMEM; + goto free_hw_info; + } + + core_info = kzalloc(sizeof(*core_info), GFP_KERNEL); + if (!core_info) { + rc = -ENOMEM; + goto free_soc_info; + } + + lx7_info->core_info = core_info; + lx7_info->soc_info.pdev = pdev; + lx7_info->soc_info.dev = &pdev->dev; + lx7_info->soc_info.dev_name = pdev->name; + lx7_info->soc_info.soc_private = lx7_soc_info; + + mutex_init(&lx7_info->hw_mutex); + spin_lock_init(&lx7_info->hw_lock); + init_completion(&lx7_info->hw_complete); + + rc = cam_lx7_soc_resources_init(&lx7_info->soc_info, + cam_lx7_handle_irq, lx7_info); + if (rc) { + CAM_ERR(CAM_ICP, "soc resources init failed rc=%d", rc); + goto free_core_info; + } + + lx7_intf->hw_priv = lx7_info; + lx7_intf->hw_type = CAM_ICP_DEV_LX7; + lx7_intf->hw_idx = lx7_info->soc_info.index; + lx7_intf->hw_ops.init = cam_lx7_hw_init; + lx7_intf->hw_ops.deinit = cam_lx7_hw_deinit; + lx7_intf->hw_ops.process_cmd = cam_lx7_process_cmd; + + rc = cam_lx7_cpas_register(lx7_intf); + if (rc) { + CAM_ERR(CAM_ICP, "cpas registration failed rc=%d", rc); + goto res_deinit; + } + + platform_set_drvdata(pdev, lx7_intf); + + return 0; + +res_deinit: + cam_lx7_soc_resources_deinit(&lx7_info->soc_info); +free_core_info: + kfree(core_info); +free_soc_info: + kfree(lx7_soc_info); +free_hw_info: + kfree(lx7_info); +free_hw_intf: + kfree(lx7_intf); + + return rc; +} + +static void cam_lx7_component_unbind(struct device *dev, + struct device *mdev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct cam_hw_intf *lx7_intf = platform_get_drvdata(pdev); + struct cam_hw_info *lx7_info = lx7_intf->hw_priv; + + cam_lx7_cpas_unregister(lx7_intf); + cam_lx7_soc_resources_deinit(&lx7_info->soc_info); + + kfree(lx7_info->core_info); + kfree(lx7_info->soc_info.soc_private); + kfree(lx7_info); + kfree(lx7_intf); +} + +static const struct component_ops cam_lx7_component_ops = { + .bind = cam_lx7_component_bind, + .unbind = cam_lx7_component_unbind, +}; + +static const struct of_device_id cam_lx7_match[] = { + { .compatible = "qcom,cam-lx7"}, + {}, +}; +MODULE_DEVICE_TABLE(of, cam_lx7_match); + +static int cam_lx7_driver_probe(struct platform_device *pdev) +{ + int rc; + + rc = component_add(&pdev->dev, &cam_lx7_component_ops); + if (rc) + CAM_ERR(CAM_ICP, "cam-lx7 component add failed rc=%d", rc); + + return rc; +} + +static int cam_lx7_driver_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &cam_lx7_component_ops); + + return 0; +} + +struct platform_driver cam_lx7_driver = { + .probe = cam_lx7_driver_probe, + .remove = cam_lx7_driver_remove, + .driver = { + .name = "cam-lx7", + .of_match_table = cam_lx7_match, + .suppress_bind_attrs = true, + }, +}; + +int cam_lx7_init_module(void) +{ + return platform_driver_register(&cam_lx7_driver); +} + +void cam_lx7_exit_module(void) +{ + platform_driver_unregister(&cam_lx7_driver); +} + +MODULE_DESCRIPTION("Camera LX7 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/cam_icp/icp_hw/lx7_hw/lx7_dev.h b/drivers/cam_icp/icp_hw/lx7_hw/lx7_dev.h new file mode 100644 index 0000000000..d96d638f3b --- /dev/null +++ b/drivers/cam_icp/icp_hw/lx7_hw/lx7_dev.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#ifndef _CAM_LX7_DEV_H_ +#define _CAM_LX7_DEV_H_ + +int cam_lx7_init_module(void); +void cam_lx7_exit_module(void); + +#endif /* _CAM_LX7_DEV_H_ */ diff --git a/drivers/cam_icp/icp_hw/lx7_hw/lx7_reg.h b/drivers/cam_icp/icp_hw/lx7_hw/lx7_reg.h new file mode 100644 index 0000000000..984fb734ee --- /dev/null +++ b/drivers/cam_icp/icp_hw/lx7_hw/lx7_reg.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#ifndef _CAM_LX7_REG_H_ +#define _CAM_LX7_REG_H_ + +#define ICP_LX7_CIRQ_OB_MASK 0x0 +#define ICP_LX7_CIRQ_OB_CLEAR 0x4 +#define ICP_LX7_CIRQ_OB_STATUS 0xc + +/* These bitfields are shared by OB_MASK, OB_CLEAR, OB_STATUS */ +#define LX7_WDT_BITE_WS1 (1 << 6) +#define LX7_WDT_BARK_WS1 (1 << 5) +#define LX7_WDT_BITE_WS0 (1 << 4) +#define LX7_WDT_BARK_WS0 (1 << 3) +#define LX7_ICP2HOSTINT (1 << 2) + +#define ICP_LX7_CIRQ_OB_IRQ_CMD 0x10 +#define LX7_IRQ_CLEAR_CMD (1 << 1) + +#define ICP_LX7_CIRQ_HOST2ICPINT 0x124 +#define LX7_HOST2ICPINT (1 << 0) + +#endif /* _CAM_LX7_REG_H_ */ diff --git a/drivers/cam_icp/icp_hw/lx7_hw/lx7_soc.c b/drivers/cam_icp/icp_hw/lx7_hw/lx7_soc.c new file mode 100644 index 0000000000..23d828fa0c --- /dev/null +++ b/drivers/cam_icp/icp_hw/lx7_hw/lx7_soc.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#include +#include + +#include "cam_debug_util.h" +#include "cam_soc_util.h" +#include "lx7_soc.h" + +static int __ubwc_config_get(struct device_node *np, char *name, uint32_t *cfg) +{ + int nconfig; + int i; + + nconfig = of_property_count_u32_elems(np, name); + if (nconfig < 0 || nconfig > UBWC_CONFIG_MAX) { + CAM_ERR(CAM_ICP, "invalid number of UBWC configs[=%d]", + nconfig); + return -EINVAL; + } + + for (i = 0; i < nconfig; i++) { + int rc; + + rc = of_property_read_u32_index(np, name, i, &cfg[i]); + if (rc) { + CAM_ERR(CAM_ICP, + "node %pOF has no valid %s prop at index=%d", + np, name, i); + return rc; + } + } + + return 0; +} + +static int cam_lx7_ubwc_config_get(struct lx7_soc_info *lx7_soc_info, + struct device_node *np) +{ + int rc; + + rc = __ubwc_config_get(np, "ubwc-ipe-fetch-cfg", + lx7_soc_info->ubwc_cfg.ipe_fetch); + if (rc) + return rc; + + rc = __ubwc_config_get(np, "ubwc-ipe-write-cfg", + lx7_soc_info->ubwc_cfg.ipe_write); + if (rc) + return rc; + + rc = __ubwc_config_get(np, "ubwc-bps-fetch-cfg", + lx7_soc_info->ubwc_cfg.bps_fetch); + if (rc) + return rc; + + rc = __ubwc_config_get(np, "ubwc-bps-write-cfg", + lx7_soc_info->ubwc_cfg.bps_write); + if (rc) + return rc; + + return 0; +} + +static int cam_lx7_dt_properties_get(struct cam_hw_soc_info *soc_info) +{ + int rc; + + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc) { + CAM_ERR(CAM_ICP, "failed to get DT properties rc=%d", rc); + return rc; + } + + rc = cam_lx7_ubwc_config_get(soc_info->soc_private, + soc_info->pdev->dev.of_node); + if (rc) { + CAM_ERR(CAM_ICP, "failed to get UBWC config props rc=%d", rc); + return rc; + } + + return 0; +} + +int cam_lx7_soc_resources_init(struct cam_hw_soc_info *soc_info, + irq_handler_t handler, void *data) +{ + int rc; + + rc = cam_lx7_dt_properties_get(soc_info); + if (rc) + return rc; + + rc = cam_soc_util_request_platform_resource(soc_info, handler, data); + if (rc) { + CAM_ERR(CAM_ICP, + "request for soc platform resource failed rc=%d", rc); + return rc; + } + + return 0; +} + +int cam_lx7_soc_resources_deinit(struct cam_hw_soc_info *soc_info) +{ + int rc; + + rc = cam_soc_util_release_platform_resource(soc_info); + if (rc) + CAM_ERR(CAM_ICP, + "release of soc platform resource failed rc=%d", rc); + + return rc; +} + +int cam_lx7_soc_resources_enable(struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + + rc = cam_soc_util_enable_platform_resource(soc_info, true, + CAM_SVS_VOTE, true); + if (rc) + CAM_ERR(CAM_ICP, "failed to enable soc resources rc=%d", rc); + + return rc; +} + +int cam_lx7_soc_resources_disable(struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + + rc = cam_soc_util_disable_platform_resource(soc_info, true, true); + if (rc) + CAM_ERR(CAM_ICP, "failed to disable soc resources rc=%d", rc); + + return rc; +} diff --git a/drivers/cam_icp/icp_hw/lx7_hw/lx7_soc.h b/drivers/cam_icp/icp_hw/lx7_hw/lx7_soc.h new file mode 100644 index 0000000000..f8a98f86a6 --- /dev/null +++ b/drivers/cam_icp/icp_hw/lx7_hw/lx7_soc.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#ifndef _CAM_LX7_SOC_H_ +#define _CAM_LX7_SOC_H_ + +#include + +#include "cam_soc_util.h" + +#define UBWC_CONFIG_MAX 2 + +struct lx7_soc_info { + struct { + uint32_t ipe_fetch[UBWC_CONFIG_MAX]; + uint32_t ipe_write[UBWC_CONFIG_MAX]; + uint32_t bps_fetch[UBWC_CONFIG_MAX]; + uint32_t bps_write[UBWC_CONFIG_MAX]; + } ubwc_cfg; +}; + +int cam_lx7_soc_resources_init(struct cam_hw_soc_info *soc_info, + irq_handler_t irq_handler, + void *irq_data); +int cam_lx7_soc_resources_deinit(struct cam_hw_soc_info *soc_info); + +int cam_lx7_soc_resources_enable(struct cam_hw_soc_info *soc_info); +int cam_lx7_soc_resources_disable(struct cam_hw_soc_info *soc_info); + +#endif /* _CAM_LX7_SOC_H_ */ diff --git a/drivers/camera_main.c b/drivers/camera_main.c index 5c345fd7da..aea0f33e5d 100644 --- a/drivers/camera_main.c +++ b/drivers/camera_main.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */ #include #include @@ -30,6 +30,7 @@ #endif #include "a5_core.h" +#include "lx7_dev.h" #include "ipe_core.h" #include "bps_core.h" #include "cam_icp_subdev.h" @@ -118,6 +119,7 @@ static const struct camera_submodule_component camera_sensor[] = { static const struct camera_submodule_component camera_icp[] = { #ifdef CONFIG_SPECTRA_ICP {&cam_a5_init_module, &cam_a5_exit_module}, + {&cam_lx7_init_module, &cam_lx7_exit_module}, {&cam_ipe_init_module, &cam_ipe_exit_module}, {&cam_bps_init_module, &cam_bps_exit_module}, {&cam_icp_init_module, &cam_icp_exit_module}, diff --git a/drivers/camera_main.h b/drivers/camera_main.h index 7ee4a71d70..5350f4f7ff 100644 --- a/drivers/camera_main.h +++ b/drivers/camera_main.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */ #ifndef CAMERA_MAIN_H @@ -44,6 +44,7 @@ extern struct platform_driver cam_flash_platform_driver; #endif #ifdef CONFIG_SPECTRA_ICP extern struct platform_driver cam_a5_driver; +extern struct platform_driver cam_lx7_driver; extern struct platform_driver cam_ipe_driver; extern struct platform_driver cam_bps_driver; extern struct platform_driver cam_icp_driver; @@ -112,6 +113,7 @@ static struct platform_driver *const cam_component_drivers[] = { #endif #ifdef CONFIG_SPECTRA_ICP &cam_a5_driver, + &cam_lx7_driver, &cam_ipe_driver, &cam_bps_driver, &cam_icp_driver,