瀏覽代碼

msm: camera: icp: Add OFE Kernel Driver

Create OFE HW layer which contains device, soc, and core files.
Device file provides the prob functionality and OFE dev interface
to ICP HW MGR. Core file provides HW related functionalities such
as PC, reset, init/deinit hw, etc. Soc file provides intermediate
functions to utilize camera soc APIs such as DT parsing, Clock
update, etc.

Modify Kbuild to include compilation of the source files. Update
camera main file to invoke ofe init module function during camera
module init. Update camera main header file to allow CRM to match
OFE driver for component bind.

Update OFE HW register addresses such as power status/power ctrl,
reset, irq, etc.

CRs-Fixed: 3266673
Change-Id: I97efe095801b1b42e9de07aeba6f26112cb8f92b
Signed-off-by: Sokchetra Eung <[email protected]>
Sokchetra Eung 3 年之前
父節點
當前提交
df4373f69f

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

+ 8 - 0
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,
 };
 

+ 1 - 0
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,
 };
 

+ 29 - 0
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 <media/cam_defs.h>
+#include <media/cam_icp.h>
+#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 */

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

+ 482 - 0
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 <linux/of.h>
+#include <linux/debugfs.h>
+#include <linux/videodev2.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/iopoll.h>
+#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;
+}

+ 61 - 0
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 <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+/* 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 */

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

+ 157 - 0
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 <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <media/cam_defs.h>
+#include <media/cam_icp.h>
+#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;
+}

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

+ 2 - 0
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
 };

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