Browse Source

msm: camera: icp: Add LX7 driver

Add initial support for the LX7 processor.
Support for firmware loading and processor boot are
still pending. Note this also cleans up some descriptions
in struct cam_icp_hw_mgr that were left over when the
debugfs entries were renamed.

CRs-Fixed: 2722486
Change-Id: I626cc27e26e1ebac8ec6b4509ab5da2a013457b1
Signed-off-by: Karthik Anantha Ram <[email protected]>
Karthik Anantha Ram 4 years ago
parent
commit
4e25fa702e

+ 3 - 0
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 \

+ 10 - 7
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 <linux/slab.h>
@@ -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;

+ 16 - 7
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 <linux/uaccess.h>
@@ -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);
 }

+ 3 - 3
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;
 };
 
 /**

+ 3 - 2
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);
 };
 
 /**

+ 2 - 2
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
 

+ 391 - 0
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 <linux/firmware.h>
+#include <linux/of_address.h>
+#include <linux/qcom_scm.h>
+
+#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(&params);
+	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;
+}

+ 39 - 0
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_ */

+ 164 - 0
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 <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of_device.h>
+
+#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");

+ 12 - 0
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_ */

+ 26 - 0
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_ */

+ 140 - 0
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 <linux/interrupt.h>
+#include <linux/of.h>
+
+#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;
+}

+ 32 - 0
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 <linux/interrupt.h>
+
+#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_ */

+ 3 - 1
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 <linux/module.h>
 #include <linux/build_bug.h>
@@ -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},

+ 3 - 1
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,