Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 094c2d94 authored by Camera Software Integration's avatar Camera Software Integration Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: camera: isp: Added PPI driver functionality" into camera-kernel.lnx.3.1

parents a50c3608 66dc2207
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@
obj-$(CONFIG_SPECTRA_CAMERA) += top_tpg/

ifdef CONFIG_SPECTRA_CAMERA_TFE
obj-$(CONFIG_SPECTRA_CAMERA) += tfe_csid_hw/ tfe_hw/
obj-$(CONFIG_SPECTRA_CAMERA) += tfe_csid_hw/ tfe_hw/ ppi_hw/
endif

ifdef CONFIG_SPECTRA_CAMERA_IFE
+14 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0-only

ccflags-y += -I$(srctree)/techpack/camera/drivers/cam_utils
ccflags-y += -I$(srctree)/techpack/camera/drivers/cam_core
ccflags-y += -I$(srctree)/techpack/camera/drivers/cam_cdm/
ccflags-y += -I$(srctree)/techpack/camera/drivers/cam_cpas/include
ccflags-y += -I$(srctree)/techpack/camera/drivers/cam_isp/isp_hw_mgr/include
ccflags-y += -I$(srctree)/techpack/camera/drivers/cam_isp/isp_hw_mgr/hw_utils/include
ccflags-y += -I$(srctree)/techpack/camera/drivers/cam_isp/isp_hw_mgr/hw_utils/irq_controller
ccflags-y += -I$(srctree)/techpack/camera/drivers/cam_isp/isp_hw_mgr/isp_hw/include
ccflags-y += -I$(srctree)/techpack/camera/drivers/cam_smmu/
ccflags-y += -I$(srctree)/techpack/camera/drivers/cam_req_mgr/

obj-$(CONFIG_SPECTRA_CAMERA) += cam_csid_ppi_dev.o cam_csid_ppi_core.o cam_csid_ppi100.o
+50 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2019, 2021, 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,
	},
};

static int __init cam_csid_ppi100_init_module(void)
{
	return platform_driver_register(&cam_csid_ppi100_driver);
}

static void __exit cam_csid_ppi100_exit_module(void)
{
	platform_driver_unregister(&cam_csid_ppi100_driver);
}

module_init(cam_csid_ppi100_init_module);
MODULE_DESCRIPTION("CAM CSID_PPI100 driver");
MODULE_LICENSE("GPL v2");
+25 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2019, 2021, 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,
};

#endif /*_CAM_CSID_PPI_100_H_ */
+392 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2019, 2021, 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);
}

Loading