Browse Source

msm: camera: isp: Bring PPI driver in camera techpack

This change bring PPI driver from msm-4.14 to camera-kernel.lnx.4.0.
msm: camera: isp: Added PPI driver functionality
msm: camera: ppi: Change PPI reset sequence
msm: camera: isp: Do not enable the ppi hw twice.

CRs-Fixed: 2682747
Change-Id: I7cde3aeed1dbcef95f25441c2482f5bc8c1534de
Signed-off-by: Trishansh Bhardwaj <[email protected]>
Trishansh Bhardwaj 5 năm trước cách đây
mục cha
commit
e625f33cff

+ 3 - 0
drivers/Makefile

@@ -197,6 +197,9 @@ camera-$(CONFIG_SPECTRA_OPE) += \
 	cam_ope/ope_hw_mgr/ope_hw/bus_wr/ope_bus_wr.o
 
 camera-$(CONFIG_SPECTRA_TFE) += \
+	cam_isp/isp_hw_mgr/isp_hw/ppi_hw/cam_csid_ppi_core.o \
+	cam_isp/isp_hw_mgr/isp_hw/ppi_hw/cam_csid_ppi_dev.o \
+	cam_isp/isp_hw_mgr/isp_hw/ppi_hw/cam_csid_ppi100.o \
 	cam_isp/isp_hw_mgr/isp_hw/tfe_hw/cam_tfe_soc.o \
 	cam_isp/isp_hw_mgr/isp_hw/tfe_hw/cam_tfe_dev.o \
 	cam_isp/isp_hw_mgr/isp_hw/tfe_hw/cam_tfe_core.o \

+ 49 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/ppi_hw/cam_csid_ppi100.c

@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include "cam_csid_ppi_core.h"
+#include "cam_csid_ppi100.h"
+#include "cam_csid_ppi_dev.h"
+
+#define CAM_PPI_DRV_NAME                    "ppi_100"
+#define CAM_PPI_VERSION_V100                 0x10000000
+
+static struct cam_csid_ppi_hw_info cam_csid_ppi100_hw_info = {
+	.ppi_reg = &cam_csid_ppi_100_reg_offset,
+};
+
+static const struct of_device_id cam_csid_ppi100_dt_match[] = {
+	{
+		.compatible = "qcom,ppi100",
+		.data = &cam_csid_ppi100_hw_info,
+	},
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, cam_csid_ppi100_dt_match);
+
+static struct platform_driver cam_csid_ppi100_driver = {
+	.probe  = cam_csid_ppi_probe,
+	.remove = cam_csid_ppi_remove,
+	.driver = {
+		.name = CAM_PPI_DRV_NAME,
+		.of_match_table = cam_csid_ppi100_dt_match,
+		.suppress_bind_attrs = true,
+	},
+};
+
+int cam_csid_ppi100_init_module(void)
+{
+	return platform_driver_register(&cam_csid_ppi100_driver);
+}
+
+void cam_csid_ppi100_exit_module(void)
+{
+	platform_driver_unregister(&cam_csid_ppi100_driver);
+}
+
+MODULE_DESCRIPTION("CAM CSID_PPI100 driver");
+MODULE_LICENSE("GPL v2");

+ 35 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/ppi_hw/cam_csid_ppi100.h

@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _CAM_CSID_PPI_100_H_
+#define _CAM_CSID_PPI_100_H_
+
+#include "cam_csid_ppi_core.h"
+
+static struct cam_csid_ppi_reg_offset cam_csid_ppi_100_reg_offset = {
+	.ppi_hw_version_addr    = 0,
+	.ppi_module_cfg_addr    = 0x60,
+	.ppi_irq_status_addr    = 0x68,
+	.ppi_irq_mask_addr      = 0x6c,
+	.ppi_irq_set_addr       = 0x70,
+	.ppi_irq_clear_addr     = 0x74,
+	.ppi_irq_cmd_addr       = 0x78,
+	.ppi_rst_cmd_addr       = 0x7c,
+	.ppi_test_bus_ctrl_addr = 0x1f4,
+	.ppi_debug_addr         = 0x1f8,
+	.ppi_spare_addr         = 0x1fc,
+};
+
+/**
+ * @brief : API to register PPI Dev to platform framework.
+ * @return struct platform_device pointer on on success, or ERR_PTR() on error.
+ */
+int cam_csid_ppi100_init_module(void);
+
+/**
+ * @brief : API to remove PPI Dev from platform framework.
+ */
+void cam_csid_ppi100_exit_module(void);
+#endif /*_CAM_CSID_PPI_100_H_ */

+ 391 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/ppi_hw/cam_csid_ppi_core.c

@@ -0,0 +1,391 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/iopoll.h>
+#include <linux/slab.h>
+#include <media/cam_defs.h>
+
+#include "cam_csid_ppi_core.h"
+#include "cam_csid_ppi_dev.h"
+#include "cam_soc_util.h"
+#include "cam_debug_util.h"
+#include "cam_io_util.h"
+
+static int cam_csid_ppi_reset(struct cam_csid_ppi_hw *ppi_hw)
+{
+	struct cam_hw_soc_info                *soc_info;
+	const struct cam_csid_ppi_reg_offset  *ppi_reg;
+	int rc = 0;
+	uint32_t status;
+
+	soc_info = &ppi_hw->hw_info->soc_info;
+	ppi_reg  = ppi_hw->ppi_info->ppi_reg;
+
+	CAM_DBG(CAM_ISP, "PPI:%d reset", ppi_hw->hw_intf->hw_idx);
+
+	cam_io_w_mb(0, soc_info->reg_map[0].mem_base +
+		ppi_reg->ppi_irq_mask_addr);
+	cam_io_w_mb(PPI_RST_CONTROL, soc_info->reg_map[0].mem_base +
+		ppi_reg->ppi_irq_set_addr);
+	cam_io_w_mb(PPI_RST_CONTROL, soc_info->reg_map[0].mem_base +
+		ppi_reg->ppi_rst_cmd_addr);
+	cam_io_w_mb(PPI_IRQ_CMD_SET, soc_info->reg_map[0].mem_base +
+		ppi_reg->ppi_irq_cmd_addr);
+
+	rc = readl_poll_timeout(soc_info->reg_map[0].mem_base +
+		ppi_reg->ppi_irq_status_addr, status,
+		(status & 0x1) == 0x1, 1000, 500000);
+	CAM_DBG(CAM_ISP, "PPI:%d reset status %d", ppi_hw->hw_intf->hw_idx,
+		status);
+	if (rc < 0) {
+		CAM_ERR(CAM_ISP, "PPI:%d ppi_reset fail rc = %d status = %d",
+			ppi_hw->hw_intf->hw_idx, rc, status);
+		return rc;
+	}
+	cam_io_w_mb(PPI_RST_CONTROL, soc_info->reg_map[0].mem_base +
+		ppi_reg->ppi_irq_clear_addr);
+	cam_io_w_mb(PPI_IRQ_CMD_CLEAR, soc_info->reg_map[0].mem_base +
+		ppi_reg->ppi_irq_cmd_addr);
+
+	return 0;
+}
+
+static int cam_csid_ppi_enable_hw(struct cam_csid_ppi_hw  *ppi_hw)
+{
+	int rc = 0;
+	int32_t i;
+	uint64_t val;
+	const struct cam_csid_ppi_reg_offset *ppi_reg;
+	struct cam_hw_soc_info               *soc_info;
+	uint32_t err_irq_mask;
+
+	ppi_reg  = ppi_hw->ppi_info->ppi_reg;
+	soc_info = &ppi_hw->hw_info->soc_info;
+
+	CAM_DBG(CAM_ISP, "PPI:%d init PPI HW", ppi_hw->hw_intf->hw_idx);
+
+	ppi_hw->hw_info->open_count++;
+	if (ppi_hw->hw_info->open_count > 1) {
+		CAM_DBG(CAM_ISP, "PPI:%d dual vfe already enabled",
+			ppi_hw->hw_intf->hw_idx);
+		return 0;
+	}
+
+	for (i = 0; i < soc_info->num_clk; i++) {
+		rc = cam_soc_util_clk_enable(soc_info->clk[i],
+			soc_info->clk_name[i], 0);
+		if (rc)
+			goto clk_disable;
+	}
+
+	rc = cam_csid_ppi_reset(ppi_hw);
+	if (rc)
+		goto clk_disable;
+
+	err_irq_mask = PPI_IRQ_FIFO0_OVERFLOW | PPI_IRQ_FIFO1_OVERFLOW |
+		PPI_IRQ_FIFO2_OVERFLOW;
+	cam_io_w_mb(err_irq_mask, soc_info->reg_map[0].mem_base +
+		ppi_reg->ppi_irq_mask_addr);
+	rc  = cam_soc_util_irq_enable(soc_info);
+	if (rc)
+		goto clk_disable;
+
+	cam_io_w_mb(PPI_RST_CONTROL, soc_info->reg_map[0].mem_base +
+		ppi_reg->ppi_irq_clear_addr);
+	cam_io_w_mb(PPI_IRQ_CMD_CLEAR, soc_info->reg_map[0].mem_base +
+		ppi_reg->ppi_irq_cmd_addr);
+	val = cam_io_r_mb(soc_info->reg_map[0].mem_base +
+		ppi_reg->ppi_hw_version_addr);
+	CAM_DBG(CAM_ISP, "PPI:%d PPI HW version: 0x%x",
+		ppi_hw->hw_intf->hw_idx, val);
+	ppi_hw->device_enabled = 1;
+
+	return 0;
+clk_disable:
+	for (--i; i >= 0; i--)
+		cam_soc_util_clk_disable(soc_info->clk[i],
+			soc_info->clk_name[i]);
+	ppi_hw->hw_info->open_count--;
+	return rc;
+}
+
+static int cam_csid_ppi_disable_hw(struct cam_csid_ppi_hw *ppi_hw)
+{
+	int rc = 0;
+	int i;
+	struct cam_hw_soc_info               *soc_info;
+	const struct cam_csid_ppi_reg_offset *ppi_reg;
+	uint64_t ppi_cfg_val = 0;
+
+	CAM_DBG(CAM_ISP, "PPI:%d De-init PPI HW",
+		ppi_hw->hw_intf->hw_idx);
+
+	if (!ppi_hw->hw_info->open_count) {
+		CAM_WARN(CAM_ISP, "ppi[%d] unbalanced disable hw",
+			ppi_hw->hw_intf->hw_idx);
+		return -EINVAL;
+	}
+	ppi_hw->hw_info->open_count--;
+
+	if (ppi_hw->hw_info->open_count)
+		return rc;
+
+	soc_info = &ppi_hw->hw_info->soc_info;
+	ppi_reg = ppi_hw->ppi_info->ppi_reg;
+
+	CAM_DBG(CAM_ISP, "Calling PPI Reset");
+	cam_csid_ppi_reset(ppi_hw);
+	CAM_DBG(CAM_ISP, "PPI Reset Done");
+
+	cam_io_w_mb(0, soc_info->reg_map[0].mem_base +
+		ppi_reg->ppi_irq_mask_addr);
+	cam_soc_util_irq_disable(soc_info);
+
+	for (i = 0; i < CAM_CSID_PPI_LANES_MAX; i++)
+		ppi_cfg_val &= ~PPI_CFG_CPHY_DLX_EN(i);
+
+	cam_io_w_mb(ppi_cfg_val, soc_info->reg_map[0].mem_base +
+			ppi_reg->ppi_module_cfg_addr);
+
+	ppi_hw->device_enabled = 0;
+
+	for (i = 0; i < soc_info->num_clk; i++)
+		cam_soc_util_clk_disable(soc_info->clk[i],
+			soc_info->clk_name[i]);
+
+	return rc;
+}
+
+static int cam_csid_ppi_init_hw(void *hw_priv, void *init_args,
+		uint32_t arg_size)
+{
+	int i, rc = 0;
+	uint32_t num_lanes;
+	uint32_t lanes[CAM_CSID_PPI_HW_MAX] = {0, 0, 0, 0};
+	uint32_t cphy;
+	bool dl0, dl1;
+	uint32_t ppi_cfg_val = 0;
+	struct cam_csid_ppi_hw                *ppi_hw;
+	struct cam_hw_info                    *ppi_hw_info;
+	const struct cam_csid_ppi_reg_offset  *ppi_reg;
+	struct cam_hw_soc_info                *soc_info;
+	struct cam_csid_ppi_cfg                ppi_cfg;
+
+	if (!hw_priv || !init_args ||
+		(arg_size != sizeof(struct cam_csid_ppi_cfg))) {
+		CAM_ERR(CAM_ISP, "PPI: Invalid args");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	dl0 = dl1 = false;
+	ppi_hw_info = (struct cam_hw_info *)hw_priv;
+	ppi_hw      = (struct cam_csid_ppi_hw *)ppi_hw_info->core_info;
+	ppi_reg     = ppi_hw->ppi_info->ppi_reg;
+	ppi_cfg     = *((struct cam_csid_ppi_cfg *)init_args);
+
+	rc = cam_csid_ppi_enable_hw(ppi_hw);
+	if (rc)
+		goto end;
+
+	num_lanes = ppi_cfg.lane_num;
+	cphy = ppi_cfg.lane_type;
+	CAM_DBG(CAM_ISP, "lane_cfg  0x%x | num_lanes  0x%x | lane_type 0x%x",
+		ppi_cfg.lane_cfg, num_lanes, cphy);
+
+	for (i = 0; i < num_lanes; i++) {
+		lanes[i] = ppi_cfg.lane_cfg & (0x3 << (4 * i));
+		(lanes[i] < 2) ? (dl0 = true) : (dl1 = true);
+		CAM_DBG(CAM_ISP, "lanes[%d] %d", i, lanes[i]);
+	}
+
+	if (num_lanes) {
+		if (cphy) {
+			for (i = 0; i < num_lanes; i++) {
+				ppi_cfg_val |= PPI_CFG_CPHY_DLX_SEL(lanes[i]);
+				ppi_cfg_val |= PPI_CFG_CPHY_DLX_EN(lanes[i]);
+			}
+		} else {
+			if (dl0)
+				ppi_cfg_val |= PPI_CFG_CPHY_DLX_EN(0);
+			if (dl1)
+				ppi_cfg_val |= PPI_CFG_CPHY_DLX_EN(1);
+		}
+	} else {
+		CAM_ERR(CAM_ISP,
+			"Number of lanes to enable is cannot be zero");
+		rc = -1;
+		goto end;
+	}
+
+	CAM_DBG(CAM_ISP, "ppi_cfg_val 0x%x", ppi_cfg_val);
+	soc_info = &ppi_hw->hw_info->soc_info;
+	cam_io_w_mb(ppi_cfg_val, soc_info->reg_map[0].mem_base +
+		ppi_reg->ppi_module_cfg_addr);
+
+	CAM_DBG(CAM_ISP, "ppi cfg 0x%x",
+		cam_io_r_mb(soc_info->reg_map[0].mem_base +
+		ppi_reg->ppi_module_cfg_addr));
+end:
+	return rc;
+}
+
+static int cam_csid_ppi_deinit_hw(void *hw_priv, void *deinit_args,
+		uint32_t arg_size)
+{
+	int rc = 0;
+	struct cam_csid_ppi_hw  *ppi_hw;
+	struct cam_hw_info      *ppi_hw_info;
+
+	CAM_DBG(CAM_ISP, "Enter");
+
+	if (!hw_priv) {
+		CAM_ERR(CAM_ISP, "PPI:Invalid arguments");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	ppi_hw_info = (struct cam_hw_info  *)hw_priv;
+	ppi_hw      = (struct cam_csid_ppi_hw *)ppi_hw_info->core_info;
+
+	CAM_DBG(CAM_ISP, "Disabling PPI Hw");
+	rc = cam_csid_ppi_disable_hw(ppi_hw);
+	if (rc < 0)
+		CAM_DBG(CAM_ISP, "Exit with %d", rc);
+end:
+	return rc;
+}
+
+int cam_csid_ppi_hw_probe_init(struct cam_hw_intf  *ppi_hw_intf,
+	uint32_t ppi_idx)
+{
+	int rc = -EINVAL;
+	struct cam_hw_info        *ppi_hw_info;
+	struct cam_csid_ppi_hw    *csid_ppi_hw = NULL;
+
+	if (ppi_idx >= CAM_CSID_PPI_HW_MAX) {
+		CAM_ERR(CAM_ISP, "Invalid ppi index:%d", ppi_idx);
+		goto err;
+	}
+
+	ppi_hw_info = (struct cam_hw_info  *) ppi_hw_intf->hw_priv;
+	csid_ppi_hw  = (struct cam_csid_ppi_hw  *) ppi_hw_info->core_info;
+
+	csid_ppi_hw->hw_intf = ppi_hw_intf;
+	csid_ppi_hw->hw_info = ppi_hw_info;
+
+	CAM_DBG(CAM_ISP, "type %d index %d",
+		csid_ppi_hw->hw_intf->hw_type, ppi_idx);
+
+	rc = cam_csid_ppi_init_soc_resources(&csid_ppi_hw->hw_info->soc_info,
+		cam_csid_ppi_irq, csid_ppi_hw);
+	if (rc < 0) {
+		CAM_ERR(CAM_ISP, "PPI:%d Failed to init_soc", ppi_idx);
+		goto err;
+	}
+
+	csid_ppi_hw->hw_intf->hw_ops.init   = cam_csid_ppi_init_hw;
+	csid_ppi_hw->hw_intf->hw_ops.deinit = cam_csid_ppi_deinit_hw;
+	return 0;
+err:
+	return rc;
+}
+
+int cam_csid_ppi_init_soc_resources(struct cam_hw_soc_info *soc_info,
+	irq_handler_t ppi_irq_handler, void *irq_data)
+{
+	int rc = 0;
+
+	rc = cam_soc_util_get_dt_properties(soc_info);
+	if (rc) {
+		CAM_ERR(CAM_ISP, "PPI: Failed to get dt properties");
+		goto end;
+	}
+
+	rc = cam_soc_util_request_platform_resource(soc_info, ppi_irq_handler,
+		irq_data);
+	if (rc) {
+		CAM_ERR(CAM_ISP,
+			"PPI: Error Request platform resources failed rc=%d",
+			rc);
+		goto err;
+	}
+end:
+	return rc;
+err:
+	cam_soc_util_release_platform_resource(soc_info);
+	return rc;
+}
+
+irqreturn_t cam_csid_ppi_irq(int irq_num, void *data)
+{
+	uint32_t      irq_status = 0;
+	uint32_t      i, ppi_cfg_val = 0;
+	bool          fatal_err_detected = false;
+
+	struct cam_csid_ppi_hw                *ppi_hw;
+	struct cam_hw_soc_info                *soc_info;
+	const struct cam_csid_ppi_reg_offset  *ppi_reg;
+
+	if (!data) {
+		CAM_ERR(CAM_ISP, "PPI: Invalid arguments");
+		return IRQ_HANDLED;
+	}
+
+	ppi_hw = (struct cam_csid_ppi_hw *)data;
+	ppi_reg = ppi_hw->ppi_info->ppi_reg;
+	soc_info = &ppi_hw->hw_info->soc_info;
+
+	if (ppi_hw->device_enabled != 1)
+		goto ret;
+
+	irq_status = cam_io_r_mb(soc_info->reg_map[0].mem_base +
+		ppi_reg->ppi_irq_status_addr);
+
+	cam_io_w_mb(irq_status, soc_info->reg_map[0].mem_base +
+		ppi_reg->ppi_irq_clear_addr);
+
+	cam_io_w_mb(PPI_IRQ_CMD_CLEAR, soc_info->reg_map[0].mem_base +
+		ppi_reg->ppi_irq_cmd_addr);
+
+	CAM_DBG(CAM_ISP, "PPI %d irq status 0x%x", ppi_hw->hw_intf->hw_idx,
+			irq_status);
+
+	if (irq_status & PPI_IRQ_RST_DONE) {
+		CAM_DBG(CAM_ISP, "PPI Reset Done");
+		goto ret;
+	}
+	if ((irq_status & PPI_IRQ_FIFO0_OVERFLOW) ||
+		(irq_status & PPI_IRQ_FIFO1_OVERFLOW) ||
+		(irq_status & PPI_IRQ_FIFO2_OVERFLOW)) {
+		fatal_err_detected = true;
+		goto handle_fatal_error;
+	}
+
+handle_fatal_error:
+	if (fatal_err_detected) {
+		CAM_ERR(CAM_ISP, "PPI: %d irq_status:0x%x",
+			ppi_hw->hw_intf->hw_idx, irq_status);
+		for (i = 0; i < CAM_CSID_PPI_LANES_MAX; i++)
+			ppi_cfg_val &= ~PPI_CFG_CPHY_DLX_EN(i);
+
+		cam_io_w_mb(ppi_cfg_val, soc_info->reg_map[0].mem_base +
+			ppi_reg->ppi_module_cfg_addr);
+	}
+ret:
+	CAM_DBG(CAM_ISP, "IRQ Handling exit");
+	return IRQ_HANDLED;
+}
+
+int cam_csid_ppi_hw_deinit(struct cam_csid_ppi_hw *csid_ppi_hw)
+{
+	if (!csid_ppi_hw) {
+		CAM_ERR(CAM_ISP, "Invalid param");
+		return -EINVAL;
+	}
+	return cam_soc_util_release_platform_resource(
+		&csid_ppi_hw->hw_info->soc_info);
+}
+

+ 95 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/ppi_hw/cam_csid_ppi_core.h

@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _CAM_CSID_PPI_HW_H_
+#define _CAM_CSID_PPI_HW_H_
+
+#include "cam_hw.h"
+#include "cam_hw_intf.h"
+
+#define CAM_CSID_PPI_HW_MAX      6
+#define CAM_CSID_PPI_LANES_MAX   3
+
+#define PPI_IRQ_RST_DONE                   BIT(0)
+#define PPI_IRQ_FIFO0_OVERFLOW             BIT(1)
+#define PPI_IRQ_FIFO1_OVERFLOW             BIT(2)
+#define PPI_IRQ_FIFO2_OVERFLOW             BIT(3)
+
+#define PPI_IRQ_CMD_SET                    BIT(1)
+
+#define PPI_IRQ_CMD_CLEAR                  BIT(0)
+
+#define PPI_RST_CONTROL                    BIT(0)
+/*
+ * Select the PHY (CPHY set '1' or DPHY set '0')
+ */
+#define PPI_CFG_CPHY_DLX_SEL(X)            ((X < 2) ? BIT(X) : 0)
+
+#define PPI_CFG_CPHY_DLX_EN(X)             BIT(4+X)
+
+struct cam_csid_ppi_reg_offset {
+	uint32_t ppi_hw_version_addr;
+	uint32_t ppi_module_cfg_addr;
+
+	uint32_t ppi_irq_status_addr;
+	uint32_t ppi_irq_mask_addr;
+	uint32_t ppi_irq_set_addr;
+	uint32_t ppi_irq_clear_addr;
+	uint32_t ppi_irq_cmd_addr;
+	uint32_t ppi_rst_cmd_addr;
+	uint32_t ppi_test_bus_ctrl_addr;
+	uint32_t ppi_debug_addr;
+	uint32_t ppi_spare_addr;
+};
+
+/**
+ * struct cam_csid_ppi_hw_info- ppi HW info
+ *
+ * @ppi_reg:         ppi register offsets
+ *
+ */
+struct cam_csid_ppi_hw_info {
+	const struct cam_csid_ppi_reg_offset *ppi_reg;
+};
+
+/**
+ * struct cam_csid_ppi_hw- ppi hw device resources data
+ *
+ * @hw_intf:                  contain the ppi hw interface information
+ * @hw_info:                  ppi hw device information
+ * @ppi_info:                 ppi hw specific information
+ * @device_enabled            Device enabled will set once ppi powered on and
+ *                            initial configuration are done.
+ *
+ */
+struct cam_csid_ppi_hw {
+	struct cam_hw_intf              *hw_intf;
+	struct cam_hw_info              *hw_info;
+	struct cam_csid_ppi_hw_info     *ppi_info;
+	uint32_t                         device_enabled;
+};
+
+/**
+ * struct cam_csid_ppi_cfg - ppi lane configuration data
+ * @lane_type:   lane type: c-phy or d-phy
+ * @lane_num :   active lane number
+ * @lane_cfg:    lane configurations: 4 bits per lane
+ *
+ */
+struct cam_csid_ppi_cfg {
+	uint32_t lane_type;
+	uint32_t lane_num;
+	uint32_t lane_cfg;
+};
+
+int cam_csid_ppi_hw_probe_init(struct cam_hw_intf  *ppi_hw_intf,
+	uint32_t ppi_idx);
+int cam_csid_ppi_hw_deinit(struct cam_csid_ppi_hw *csid_ppi_hw);
+int cam_csid_ppi_init_soc_resources(struct cam_hw_soc_info *soc_info,
+	irq_handler_t ppi_irq_handler, void *irq_data);
+int cam_csid_ppi_deinit_soc_resources(struct cam_hw_soc_info *soc_info);
+int cam_csid_ppi_hw_init(struct cam_hw_intf **csid_ppi_hw,
+	uint32_t hw_idx);
+#endif /* _CAM_CSID_PPI_HW_H_ */

+ 164 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/ppi_hw/cam_csid_ppi_dev.c

@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/slab.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of_device.h>
+#include <linux/component.h>
+
+#include "cam_isp_hw.h"
+#include "cam_hw_intf.h"
+#include "cam_csid_ppi_core.h"
+#include "cam_csid_ppi_dev.h"
+#include "cam_debug_util.h"
+
+static struct cam_hw_intf *cam_csid_ppi_hw_list[CAM_CSID_PPI_HW_MAX] = {
+	NULL, NULL, NULL, NULL};
+static char ppi_dev_name[8];
+
+static int cam_ppi_component_bind(struct device *dev,
+	struct device *master_dev, void *data)
+{
+	struct cam_hw_intf            *ppi_hw_intf;
+	struct cam_hw_info            *ppi_hw_info;
+	struct cam_csid_ppi_hw        *ppi_dev = NULL;
+	const struct of_device_id     *match_dev = NULL;
+	struct cam_csid_ppi_hw_info   *ppi_hw_data = NULL;
+	uint32_t                       ppi_dev_idx;
+	int                            rc = 0;
+	struct platform_device *pdev = to_platform_device(dev);
+
+	CAM_DBG(CAM_ISP, "PPI probe called");
+
+	ppi_hw_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL);
+	if (!ppi_hw_intf) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	ppi_hw_info = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL);
+	if (!ppi_hw_info) {
+		rc = -ENOMEM;
+		goto free_hw_intf;
+	}
+
+	ppi_dev = kzalloc(sizeof(struct cam_csid_ppi_hw), GFP_KERNEL);
+	if (!ppi_dev) {
+		rc = -ENOMEM;
+		goto free_hw_info;
+	}
+
+	of_property_read_u32(pdev->dev.of_node, "cell-index", &ppi_dev_idx);
+
+	match_dev = of_match_device(pdev->dev.driver->of_match_table,
+		&pdev->dev);
+	if (!match_dev) {
+		CAM_ERR(CAM_ISP, "No matching table for the CSID PPI HW!");
+		rc = -EINVAL;
+		goto free_dev;
+	}
+
+	memset(ppi_dev_name, 0, sizeof(ppi_dev_name));
+	snprintf(ppi_dev_name, sizeof(ppi_dev_name), "ppi%1u", ppi_dev_idx);
+
+	ppi_hw_intf->hw_idx  = ppi_dev_idx;
+	ppi_hw_intf->hw_priv = ppi_hw_info;
+
+	if (ppi_hw_intf->hw_idx < CAM_CSID_PPI_HW_MAX)
+		cam_csid_ppi_hw_list[ppi_hw_intf->hw_idx] = ppi_hw_intf;
+	else {
+		rc = -EINVAL;
+		goto free_dev;
+	}
+
+	ppi_hw_info->core_info         = ppi_dev;
+	ppi_hw_info->soc_info.pdev     = pdev;
+	ppi_hw_info->soc_info.dev      = &pdev->dev;
+	ppi_hw_info->soc_info.dev_name = ppi_dev_name;
+	ppi_hw_info->soc_info.index    = ppi_dev_idx;
+
+	ppi_hw_data = (struct cam_csid_ppi_hw_info  *)match_dev->data;
+	ppi_dev->ppi_info = ppi_hw_data;
+
+	rc = cam_csid_ppi_hw_probe_init(ppi_hw_intf, ppi_dev_idx);
+	if (rc) {
+		CAM_ERR(CAM_ISP, "PPI: Probe init failed!");
+		goto free_dev;
+	}
+
+	platform_set_drvdata(pdev, ppi_dev);
+	CAM_DBG(CAM_ISP, "PPI:%d probe successful",
+		ppi_hw_intf->hw_idx);
+
+	return 0;
+free_dev:
+	kfree(ppi_dev);
+free_hw_info:
+	kfree(ppi_hw_info);
+free_hw_intf:
+	kfree(ppi_hw_intf);
+err:
+	return rc;
+}
+
+static void cam_ppi_component_unbind(struct device *dev,
+	struct device *master_dev, void *data)
+{
+	struct cam_csid_ppi_hw         *ppi_dev = NULL;
+	struct cam_hw_intf             *ppi_hw_intf;
+	struct cam_hw_info             *ppi_hw_info;
+	struct platform_device *pdev = to_platform_device(dev);
+
+	ppi_dev = (struct cam_csid_ppi_hw *)platform_get_drvdata(pdev);
+	ppi_hw_intf = ppi_dev->hw_intf;
+	ppi_hw_info = ppi_dev->hw_info;
+
+	CAM_DBG(CAM_ISP, "PPI:%d remove", ppi_dev->hw_intf->hw_idx);
+
+	cam_csid_ppi_hw_deinit(ppi_dev);
+
+	kfree(ppi_dev);
+	kfree(ppi_hw_info);
+	kfree(ppi_hw_intf);
+}
+
+int cam_csid_ppi_hw_init(struct cam_hw_intf **csid_ppi_hw,
+	uint32_t hw_idx)
+{
+	int rc = 0;
+
+	if (cam_csid_ppi_hw_list[hw_idx]) {
+		*csid_ppi_hw = cam_csid_ppi_hw_list[hw_idx];
+	} else {
+		*csid_ppi_hw = NULL;
+		rc = -1;
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(cam_csid_ppi_hw_init);
+
+const static struct component_ops cam_ppi_component_ops = {
+	.bind = cam_ppi_component_bind,
+	.unbind = cam_ppi_component_unbind,
+};
+
+int cam_csid_ppi_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+
+	CAM_DBG(CAM_ISP, "Adding PPI component");
+	rc = component_add(&pdev->dev, &cam_ppi_component_ops);
+	if (rc)
+		CAM_ERR(CAM_ISP, "failed to add component rc: %d", rc);
+
+	return rc;
+}
+
+int cam_csid_ppi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &cam_ppi_component_ops);
+	return 0;
+}

+ 15 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/ppi_hw/cam_csid_ppi_dev.h

@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _CAM_CSID_PPI_DEV_H_
+#define _CAM_CSID_PPI_DEV_H_
+
+#include "cam_isp_hw.h"
+
+irqreturn_t cam_csid_ppi_irq(int irq_num, void *data);
+int cam_csid_ppi_probe(struct platform_device *pdev);
+int cam_csid_ppi_remove(struct platform_device *pdev);
+
+#endif /*_CAM_CSID_PPI_DEV_H_ */

+ 56 - 2
drivers/cam_isp/isp_hw_mgr/isp_hw/tfe_csid_hw/cam_tfe_csid_core.c

@@ -9,6 +9,7 @@
 #include <media/cam_defs.h>
 
 #include "cam_tfe_csid_core.h"
+#include "cam_csid_ppi_core.h"
 #include "cam_isp_hw.h"
 #include "cam_soc_util.h"
 #include "cam_io_util.h"
@@ -733,7 +734,9 @@ static int cam_tfe_csid_enable_csi2(
 {
 	const struct cam_tfe_csid_reg_offset       *csid_reg;
 	struct cam_hw_soc_info                     *soc_info;
+	struct cam_csid_ppi_cfg                     ppi_lane_cfg;
 	uint32_t val = 0;
+	uint32_t ppi_index = 0, rc;
 
 	csid_reg = csid_hw->csid_info->csid_reg;
 	soc_info = &csid_hw->hw_info->soc_info;
@@ -797,6 +800,25 @@ static int cam_tfe_csid_enable_csi2(
 	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 		csid_reg->csi2_reg->csid_csi2_rx_irq_mask_addr);
 
+	ppi_index = csid_hw->csi2_rx_cfg.phy_sel;
+	if (csid_hw->ppi_hw_intf[ppi_index] && csid_hw->ppi_enable) {
+		ppi_lane_cfg.lane_type = csid_hw->csi2_rx_cfg.lane_type;
+		ppi_lane_cfg.lane_num  = csid_hw->csi2_rx_cfg.lane_num;
+		ppi_lane_cfg.lane_cfg  = csid_hw->csi2_rx_cfg.lane_cfg;
+
+		CAM_DBG(CAM_ISP, "ppi_index to init %d", ppi_index);
+		rc = csid_hw->ppi_hw_intf[ppi_index]->hw_ops.init(
+				csid_hw->ppi_hw_intf[ppi_index]->hw_priv,
+				&ppi_lane_cfg,
+				sizeof(struct cam_csid_ppi_cfg));
+		if (rc < 0) {
+			CAM_ERR(CAM_ISP, "PPI:%d Init Failed",
+					ppi_index);
+			return rc;
+		}
+	}
+
+
 	return 0;
 }
 
@@ -805,6 +827,7 @@ static int cam_tfe_csid_disable_csi2(
 {
 	const struct cam_tfe_csid_reg_offset      *csid_reg;
 	struct cam_hw_soc_info                    *soc_info;
+	uint32_t ppi_index = 0, rc;
 
 	csid_reg = csid_hw->csid_info->csid_reg;
 	soc_info = &csid_hw->hw_info->soc_info;
@@ -821,6 +844,19 @@ static int cam_tfe_csid_disable_csi2(
 	cam_io_w_mb(0, soc_info->reg_map[0].mem_base +
 		csid_reg->csi2_reg->csid_csi2_rx_cfg1_addr);
 
+	ppi_index = csid_hw->csi2_rx_cfg.phy_sel;
+	if (csid_hw->ppi_hw_intf[ppi_index] && csid_hw->ppi_enable) {
+		/* De-Initialize the PPI bridge */
+		CAM_DBG(CAM_ISP, "ppi_index to de-init %d\n", ppi_index);
+		rc = csid_hw->ppi_hw_intf[ppi_index]->hw_ops.deinit(
+				csid_hw->ppi_hw_intf[ppi_index]->hw_priv,
+				NULL, 0);
+		if (rc < 0) {
+			CAM_ERR(CAM_ISP, "PPI:%d De-Init Failed", ppi_index);
+			return rc;
+		}
+	}
+
 	return 0;
 }
 
@@ -2456,13 +2492,14 @@ irqreturn_t cam_tfe_csid_irq(int irq_num, void *data)
 	unsigned long flags;
 	uint32_t i, val;
 
-	csid_hw = (struct cam_tfe_csid_hw *)data;
-
 	if (!data) {
 		CAM_ERR(CAM_ISP, "CSID: Invalid arguments");
 		return IRQ_HANDLED;
 	}
 
+	csid_hw = (struct cam_tfe_csid_hw *)data;
+	CAM_DBG(CAM_ISP, "CSID %d IRQ Handling", csid_hw->hw_intf->hw_idx);
+
 	csid_reg = csid_hw->csid_info->csid_reg;
 	soc_info = &csid_hw->hw_info->soc_info;
 	csi2_reg = csid_reg->csi2_reg;
@@ -2961,6 +2998,23 @@ int cam_tfe_csid_hw_probe_init(struct cam_hw_intf  *csid_hw_intf,
 		goto err;
 	}
 
+	/* Check if ppi bridge is present or not? */
+	tfe_csid_hw->ppi_enable = of_property_read_bool(
+		csid_hw_info->soc_info.pdev->dev.of_node,
+		"ppi-enable");
+
+	if (!tfe_csid_hw->ppi_enable)
+		return 0;
+
+	/* Initialize the PPI bridge */
+	for (i = 0; i < CAM_CSID_PPI_HW_MAX; i++) {
+		rc = cam_csid_ppi_hw_init(&tfe_csid_hw->ppi_hw_intf[i], i);
+		if (rc < 0) {
+			CAM_ERR(CAM_ISP, "PPI init failed for PPI %d", i);
+			break;
+		}
+	}
+
 	return 0;
 err:
 	if (rc) {

+ 6 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/tfe_csid_hw/cam_tfe_csid_core.h

@@ -9,6 +9,7 @@
 #include "cam_hw.h"
 #include "cam_tfe_csid_hw_intf.h"
 #include "cam_tfe_csid_soc.h"
+#include "cam_csid_ppi_core.h"
 
 #define CAM_TFE_CSID_CID_MAX                          4
 
@@ -383,6 +384,9 @@ struct cam_tfe_csid_path_cfg {
  * @lock_state                csid spin lock
  * @event_cb:                 Callback function to hw mgr in case of hw events
  * @event_cb_priv:            Context data
+ * @ppi_hw_intf               interface to ppi hardware
+ * @ppi_enabled               flag to specify if the hardware has ppi bridge
+ *                            or not
  *
  */
 struct cam_tfe_csid_hw {
@@ -409,6 +413,8 @@ struct cam_tfe_csid_hw {
 	spinlock_t                          spin_lock;
 	cam_hw_mgr_event_cb_func            event_cb;
 	void                               *event_cb_priv;
+	struct cam_hw_intf                 *ppi_hw_intf[CAM_CSID_PPI_HW_MAX];
+	bool                                ppi_enable;
 };
 
 int cam_tfe_csid_hw_probe_init(struct cam_hw_intf  *csid_hw_intf,

+ 15 - 13
drivers/camera_main.c

@@ -54,6 +54,7 @@
 #include "cam_top_tpg.h"
 #include "cam_tfe_dev.h"
 #include "cam_tfe_csid530.h"
+#include "cam_csid_ppi100.h"
 #include "camera_main.h"
 
 struct camera_submodule_component {
@@ -76,6 +77,15 @@ static const struct camera_submodule_component camera_base[] = {
 	{&cam_hw_cdm_init_module, &cam_hw_cdm_exit_module},
 };
 
+static const struct camera_submodule_component camera_tfe[] = {
+#ifdef CONFIG_SPECTRA_TFE
+	{&cam_top_tpg_init_module, &cam_top_tpg_exit_module},
+	{&cam_csid_ppi100_init_module, &cam_csid_ppi100_exit_module},
+	{&cam_tfe_init_module, &cam_tfe_exit_module},
+	{&cam_tfe_csid530_init_module, &cam_tfe_csid530_exit_module},
+#endif
+};
+
 static const struct camera_submodule_component camera_isp[] = {
 #ifdef CONFIG_SPECTRA_ISP
 	{&cam_top_tpg_init_module, &cam_top_tpg_exit_module},
@@ -86,14 +96,6 @@ static const struct camera_submodule_component camera_isp[] = {
 #endif
 };
 
-static const struct camera_submodule_component camera_tfe[] = {
-#ifdef CONFIG_SPECTRA_TFE
-	{&cam_top_tpg_init_module, &cam_top_tpg_exit_module},
-	{&cam_tfe_init_module, &cam_tfe_exit_module},
-	{&cam_tfe_csid530_init_module, &cam_tfe_csid530_exit_module},
-#endif
-};
-
 static const struct camera_submodule_component camera_sensor[] = {
 #ifdef CONFIG_SPECTRA_SENSOR
 	{&cam_res_mgr_init, &cam_res_mgr_exit},
@@ -162,16 +164,16 @@ static const struct camera_submodule submodule_table[] = {
 		.num_component = ARRAY_SIZE(camera_base),
 		.component = camera_base,
 	},
-	{
-		.name = "Camera ISP",
-		.num_component = ARRAY_SIZE(camera_isp),
-		.component = camera_isp,
-	},
 	{
 		.name = "Camera TFE",
 		.num_component = ARRAY_SIZE(camera_tfe),
 		.component = camera_tfe,
 	},
+	{
+		.name = "Camera ISP",
+		.num_component = ARRAY_SIZE(camera_isp),
+		.component = camera_isp,
+	},
 	{
 		.name = "Camera SENSOR",
 		.num_component = ARRAY_SIZE(camera_sensor),