diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/Makefile b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/Makefile index 8220f1abf7de9..5a8203b8e53b0 100644 --- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/Makefile +++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/Makefile @@ -8,5 +8,6 @@ ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/inclu ccflags-y += -Idrivers/media/platform/msm/camera/cam_smmu/ ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_csid_ppi_dev.o cam_csid_ppi_core.o cam_csid_ppi170.o obj-$(CONFIG_SPECTRA_CAMERA) += cam_ife_csid_dev.o cam_ife_csid_soc.o cam_ife_csid_core.o obj-$(CONFIG_SPECTRA_CAMERA) += cam_ife_csid17x.o cam_ife_csid_lite17x.o diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_csid_ppi170.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_csid_ppi170.c new file mode 100644 index 0000000000000..2051292be5be6 --- /dev/null +++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_csid_ppi170.c @@ -0,0 +1,58 @@ +/* Copyright (c) 2019, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include "cam_csid_ppi_core.h" +#include "cam_csid_ppi170.h" +#include "cam_csid_ppi_dev.h" + +#define CAM_PPI_DRV_NAME "ppi_170" +#define CAM_PPI_VERSION_V170 0x10070000 + +static struct cam_csid_ppi_hw_info cam_csid_ppi170_hw_info = { + .ppi_reg = &cam_csid_ppi_170_reg_offset, +}; + +static const struct of_device_id cam_csid_ppi170_dt_match[] = { + { + .compatible = "qcom,ppi170", + .data = &cam_csid_ppi170_hw_info, + }, + {} +}; + +MODULE_DEVICE_TABLE(of, cam_csid_ppi170_dt_match); + +static struct platform_driver cam_csid_ppi170_driver = { + .probe = cam_csid_ppi_probe, + .remove = cam_csid_ppi_remove, + .driver = { + .name = CAM_PPI_DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = cam_csid_ppi170_dt_match, + .suppress_bind_attrs = true, + }, +}; + +static int __init cam_csid_ppi170_init_module(void) +{ + return platform_driver_register(&cam_csid_ppi170_driver); +} + +static void __exit cam_csid_ppi170_exit_module(void) +{ + platform_driver_unregister(&cam_csid_ppi170_driver); +} + +module_init(cam_csid_ppi170_init_module); +MODULE_DESCRIPTION("CAM CSID_PPI170 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_csid_ppi170.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_csid_ppi170.h new file mode 100644 index 0000000000000..0ec767204914c --- /dev/null +++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_csid_ppi170.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2019, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CAM_CSID_PPI_170_H_ +#define _CAM_CSID_PPI_170_H_ + +#include "cam_csid_ppi_core.h" + +static struct cam_csid_ppi_reg_offset cam_csid_ppi_170_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_170_H_ */ diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_csid_ppi_core.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_csid_ppi_core.c new file mode 100644 index 0000000000000..058a4e955b387 --- /dev/null +++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_csid_ppi_core.c @@ -0,0 +1,404 @@ +/* Copyright (c) 2019, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include + +#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 val = 0; + uint32_t clear_mask; + 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); + + /* Mask all interrupts */ + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + ppi_reg->ppi_irq_mask_addr); + + /* clear all interrupts */ + clear_mask = PPI_IRQ_FIFO0_OVERFLOW | PPI_IRQ_FIFO1_OVERFLOW | + PPI_IRQ_FIFO2_OVERFLOW; + cam_io_w_mb(clear_mask, 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); + + /* perform the top PPI HW registers reset */ + cam_io_w_mb(PPI_RST_CONTROL, soc_info->reg_map[0].mem_base + + ppi_reg->ppi_rst_cmd_addr); + + rc = readl_poll_timeout(soc_info->reg_map[0].mem_base + + ppi_reg->ppi_irq_status_addr, status, + (status & 0x1) == 0x1, 1000, 100000); + if (rc < 0) { + CAM_ERR(CAM_ISP, "PPI:%d ppi_reset fail rc = %d", + ppi_hw->hw_intf->hw_idx, rc); + rc = -ETIMEDOUT; + } + CAM_DBG(CAM_ISP, "PPI: reset status %d", status); + + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + ppi_reg->ppi_irq_mask_addr); + if (val != 0) + CAM_ERR(CAM_ISP, "PPI:%d IRQ value after reset rc = %d", + ppi_hw->hw_intf->hw_idx, val); + + return rc; +} + +static int cam_csid_ppi_enable_hw(struct cam_csid_ppi_hw *ppi_hw) +{ + int rc = 0; + uint32_t i; + uint64_t val; + + const struct cam_csid_ppi_reg_offset *ppi_reg; + struct cam_hw_soc_info *soc_info; + + 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); + + for (i = 0; i < soc_info->num_clk; i++) { + /* Passing zero in clk_rate results in setting no clk_rate */ + rc = cam_soc_util_clk_enable(soc_info->clk[i], + soc_info->clk_name[i], 0); + if (rc) + goto clk_disable; + } + + rc = cam_soc_util_irq_enable(soc_info); + if (rc) + goto clk_disable; + + /* Reset PPI */ + rc = cam_csid_ppi_reset(ppi_hw); + if (rc) + goto irq_disable; + + /* Clear the RST done IRQ */ + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + ppi_reg->ppi_irq_clear_addr); + cam_io_w_mb(1, 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; +irq_disable: + cam_soc_util_irq_disable(soc_info); +clk_disable: + for (i--; i >= 0; i--) { + cam_soc_util_clk_disable(soc_info->clk[i], + soc_info->clk_name[i]); + } + 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); + + soc_info = &ppi_hw->hw_info->soc_info; + ppi_reg = ppi_hw->ppi_info->ppi_reg; + + CAM_DBG(CAM_ISP, "%s:Calling PPI Reset\n", __func__); + cam_csid_ppi_reset(ppi_hw); + CAM_DBG(CAM_ISP, "%s:PPI Reset Done\n", __func__); + + /* disable the clocks */ + for (i = 0; i < soc_info->num_clk; i++) { + cam_soc_util_clk_disable(soc_info->clk[i], + soc_info->clk_name[i]); + } + + /* disable the interrupt */ + 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); + + /* disable lanes */ + 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; + + 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); + + /* Initialize the ppi hardware */ + rc = cam_csid_ppi_enable_hw(ppi_hw); + if (rc) + goto end; + + /* Do lane configuration here*/ + num_lanes = ppi_cfg.lane_num; + /* lane_type = 1 refers to cphy */ + 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++) { + /* Select Cphy */ + ppi_cfg_val |= PPI_CFG_CPHY_DLX_SEL(lanes[i]); + /* Enable lane Cphy */ + ppi_cfg_val |= PPI_CFG_CPHY_DLX_EN(lanes[i]); + } + } else { + if (dl0) + /* Enable lane 0 */ + ppi_cfg_val |= PPI_CFG_CPHY_DLX_EN(0); + if (dl1) + /* Enable lane 1 */ + 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); +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\n"); + rc = cam_csid_ppi_disable_hw(ppi_hw); + if (rc < 0) + CAM_DBG(CAM_ISP, "%s: Exit with %d\n", __func__, 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; + CAM_DBG(CAM_ISP, "PPI %d IRQ Handling", ppi_hw->hw_intf->hw_idx); + ppi_reg = ppi_hw->ppi_info->ppi_reg; + soc_info = &ppi_hw->hw_info->soc_info; + + /* read */ + irq_status = cam_io_r_mb(soc_info->reg_map[0].mem_base + + ppi_reg->ppi_irq_status_addr); + + /* clear */ + cam_io_w_mb(irq_status, soc_info->reg_map[0].mem_base + + ppi_reg->ppi_irq_clear_addr); + + cam_io_w_mb(1, 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 (ppi_hw->device_enabled == 1) { + 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); + /* disable lanes */ + 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; + } + /* release the privdate data memory from resources */ + return cam_soc_util_release_platform_resource( + &csid_ppi_hw->hw_info->soc_info); +} + diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_csid_ppi_core.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_csid_ppi_core.h new file mode 100644 index 0000000000000..058c2421b6da5 --- /dev/null +++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_csid_ppi_core.h @@ -0,0 +1,103 @@ +/* Copyright (c) 2019, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#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 4 +#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(0) + +#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. + * @lock_state ppi spin lock + * + */ +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_ */ diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_csid_ppi_dev.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_csid_ppi_dev.c new file mode 100644 index 0000000000000..1a4e14525bc30 --- /dev/null +++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_csid_ppi_dev.c @@ -0,0 +1,147 @@ +/* Copyright (c) 2019, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#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] = { + 0, 0, 0, 0}; +static char ppi_dev_name[8]; + +int cam_csid_ppi_probe(struct platform_device *pdev) +{ + 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; + + 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; + } + + /* get csid ppi hw index */ + of_property_read_u32(pdev->dev.of_node, "cell-index", &ppi_dev_idx); + + /* get csid ppi hw information */ + 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; + + 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; + /* need to setup the pdev before call the csid ppi hw probe init */ + 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); + + 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 + goto free_dev; + + return 0; +free_dev: + kfree(ppi_dev); +free_hw_info: + kfree(ppi_hw_info); +free_hw_intf: + kfree(ppi_hw_intf); +err: + return rc; +} + +int cam_csid_ppi_remove(struct platform_device *pdev) +{ + struct cam_csid_ppi_hw *ppi_dev = NULL; + struct cam_hw_intf *ppi_hw_intf; + struct cam_hw_info *ppi_hw_info; + + 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); + + /* release the ppi device memory */ + kfree(ppi_dev); + kfree(ppi_hw_info); + kfree(ppi_hw_intf); + return 0; +} + +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); + diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_csid_ppi_dev.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_csid_ppi_dev.h new file mode 100644 index 0000000000000..b2ebeaf929478 --- /dev/null +++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_csid_ppi_dev.h @@ -0,0 +1,22 @@ +/* Copyright (c) 2019, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#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_ */ diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c index 7b04f1c085e92..5df20d7aefcb6 100644 --- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c +++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c @@ -16,6 +16,7 @@ #include #include "cam_ife_csid_core.h" +#include "cam_csid_ppi_core.h" #include "cam_isp_hw.h" #include "cam_soc_util.h" #include "cam_io_util.h" @@ -1427,10 +1428,12 @@ static int cam_ife_csid_enable_csi2( struct cam_isp_resource_node *res) { int rc = 0; - const struct cam_ife_csid_reg_offset *csid_reg; - struct cam_hw_soc_info *soc_info; - struct cam_ife_csid_cid_data *cid_data; + const struct cam_ife_csid_reg_offset *csid_reg; + struct cam_hw_soc_info *soc_info; + struct cam_ife_csid_cid_data *cid_data; + struct cam_csid_ppi_cfg ppi_lane_cfg; uint32_t val = 0; + uint32_t ppi_index = 0; csid_reg = csid_hw->csid_info->csid_reg; soc_info = &csid_hw->hw_info->soc_info; @@ -1521,6 +1524,24 @@ static int cam_ife_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; } @@ -1528,8 +1549,10 @@ static int cam_ife_csid_disable_csi2( struct cam_ife_csid_hw *csid_hw, struct cam_isp_resource_node *res) { - const struct cam_ife_csid_reg_offset *csid_reg; - struct cam_hw_soc_info *soc_info; + int rc = 0; + const struct cam_ife_csid_reg_offset *csid_reg; + struct cam_hw_soc_info *soc_info; + uint32_t ppi_index = 0; if (res->res_id >= CAM_IFE_CSID_CID_MAX) { CAM_ERR(CAM_ISP, "CSID:%d Invalid res id :%d", @@ -1560,6 +1583,19 @@ static int cam_ife_csid_disable_csi2( res->res_state = CAM_ISP_RESOURCE_STATE_RESERVED; + 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; } @@ -3292,15 +3328,14 @@ irqreturn_t cam_ife_csid_irq(int irq_num, void *data) uint32_t sof_irq_debug_en = 0; unsigned long flags; - csid_hw = (struct cam_ife_csid_hw *)data; - - CAM_DBG(CAM_ISP, "CSID %d IRQ Handling", csid_hw->hw_intf->hw_idx); - if (!data) { CAM_ERR(CAM_ISP, "CSID: Invalid arguments"); return IRQ_HANDLED; } + csid_hw = (struct cam_ife_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; @@ -3826,7 +3861,6 @@ int cam_ife_csid_hw_probe_init(struct cam_hw_intf *csid_hw_intf, CAM_DBG(CAM_ISP, "type %d index %d", ife_csid_hw->hw_intf->hw_type, csid_idx); - ife_csid_hw->device_enabled = 0; ife_csid_hw->hw_info->hw_state = CAM_HW_STATE_POWER_DOWN; mutex_init(&ife_csid_hw->hw_info->hw_mutex); @@ -3841,7 +3875,6 @@ int cam_ife_csid_hw_probe_init(struct cam_hw_intf *csid_hw_intf, for (i = 0; i < CAM_IFE_CSID_RDI_MAX; i++) init_completion(&ife_csid_hw->csid_rdin_complete[i]); - rc = cam_ife_csid_init_soc_resources(&ife_csid_hw->hw_info->soc_info, cam_ife_csid_irq, ife_csid_hw); if (rc < 0) { @@ -3940,7 +3973,22 @@ int cam_ife_csid_hw_probe_init(struct cam_hw_intf *csid_hw_intf, for (i = 0; i <= CAM_IFE_PIX_PATH_RES_RDI_3; i++) ife_csid_hw->rdi_path_config[i].measure_enabled = 0; - return 0; + /* Check if ppi bridge is present or not? */ + ife_csid_hw->ppi_enable = of_property_read_bool( + csid_hw_info->soc_info.pdev->dev.of_node, + "ppi-enable"); + + if (!ife_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(&ife_csid_hw->ppi_hw_intf[i], i); + if (rc < 0) { + CAM_ERR(CAM_ISP, "PPI init failed for PPI %d", i); + break; + } + } err: if (rc) { kfree(ife_csid_hw->ipp_res.res_priv); diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h index 1a4533a7c6b38..f2173f13f0c4b 100644 --- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h +++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h @@ -16,6 +16,7 @@ #include "cam_hw.h" #include "cam_ife_csid_hw_intf.h" #include "cam_ife_csid_soc.h" +#include "cam_csid_ppi_core.h" #define CAM_IFE_CSID_HW_RES_MAX 4 #define CAM_IFE_CSID_CID_RES_MAX 4 @@ -490,7 +491,9 @@ struct cam_ife_csid_path_cfg { * @res_sof_cnt path resource sof count value. it used for initial * frame drop * @first_sof_ts flag to mark the first sof has been registered - * + * @ppi_hw_intf interface to ppi hardware + * @ppi_enabled flag to specify if the hardware has ppi bridge + * or not */ struct cam_ife_csid_hw { struct cam_hw_intf *hw_intf; @@ -527,6 +530,8 @@ struct cam_ife_csid_hw { uint32_t init_frame_drop; uint32_t res_sof_cnt[CAM_IFE_PIX_PATH_RES_MAX]; uint32_t first_sof_ts; + struct cam_hw_intf *ppi_hw_intf[CAM_CSID_PPI_HW_MAX]; + bool ppi_enable; }; int cam_ife_csid_hw_probe_init(struct cam_hw_intf *csid_hw_intf,