msm: camera: icp: Add LX7 driver

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

CRs-Fixed: 2722486
Change-Id: I626cc27e26e1ebac8ec6b4509ab5da2a013457b1
Signed-off-by: Karthik Anantha Ram <kartanan@codeaurora.org>
This commit is contained in:
Karthik Anantha Ram
2020-12-08 10:46:16 -08:00
parent c69ede622c
commit 4e25fa702e
15 changed files with 847 additions and 23 deletions

3
Kbuild
View File

@@ -149,6 +149,9 @@ camera-$(CONFIG_SPECTRA_ICP) += \
drivers/cam_icp/icp_hw/a5_hw/a5_dev.o \
drivers/cam_icp/icp_hw/a5_hw/a5_core.o \
drivers/cam_icp/icp_hw/a5_hw/a5_soc.o \
drivers/cam_icp/icp_hw/lx7_hw/lx7_dev.o \
drivers/cam_icp/icp_hw/lx7_hw/lx7_core.o \
drivers/cam_icp/icp_hw/lx7_hw/lx7_soc.o \
drivers/cam_icp/icp_hw/bps_hw/bps_dev.o \
drivers/cam_icp/icp_hw/bps_hw/bps_core.o \
drivers/cam_icp/icp_hw/bps_hw/bps_soc.o \

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
*/
#include <linux/slab.h>
@@ -336,15 +336,15 @@ int cam_a5_init_hw(void *device_priv,
cpas_vote.axi_vote.axi_path[0].transac_type =
CAM_ICP_DEFAULT_AXI_TRANSAC;
cpas_vote.axi_vote.axi_path[0].camnoc_bw =
CAM_ICP_A5_BW_BYTES_VOTE;
CAM_ICP_BW_BYTES_VOTE;
cpas_vote.axi_vote.axi_path[0].mnoc_ab_bw =
CAM_ICP_A5_BW_BYTES_VOTE;
CAM_ICP_BW_BYTES_VOTE;
cpas_vote.axi_vote.axi_path[0].mnoc_ib_bw =
CAM_ICP_A5_BW_BYTES_VOTE;
CAM_ICP_BW_BYTES_VOTE;
cpas_vote.axi_vote.axi_path[0].ddr_ab_bw =
CAM_ICP_A5_BW_BYTES_VOTE;
CAM_ICP_BW_BYTES_VOTE;
cpas_vote.axi_vote.axi_path[0].ddr_ib_bw =
CAM_ICP_A5_BW_BYTES_VOTE;
CAM_ICP_BW_BYTES_VOTE;
rc = cam_cpas_start(core_info->cpas_handle,
&cpas_vote.ahb_vote, &cpas_vote.axi_vote);
@@ -558,6 +558,7 @@ irqreturn_t cam_a5_irq(int irq_num, void *data)
struct cam_a5_device_core_info *core_info = NULL;
struct cam_a5_device_hw_info *hw_info = NULL;
uint32_t irq_status = 0;
bool recover = false;
if (!data) {
CAM_ERR(CAM_ICP, "Invalid cam_dev_info or query_cap args");
@@ -586,11 +587,13 @@ irqreturn_t cam_a5_irq(int irq_num, void *data)
if ((irq_status & A5_WDT_0) ||
(irq_status & A5_WDT_1)) {
CAM_ERR_RATE_LIMIT(CAM_ICP, "watch dog interrupt from A5");
recover = true;
}
spin_lock(&a5_dev->hw_lock);
if (core_info->irq_cb.cb)
core_info->irq_cb.cb(core_info->irq_cb.data, irq_status);
core_info->irq_cb.cb(core_info->irq_cb.data,
recover);
spin_unlock(&a5_dev->hw_lock);
return IRQ_HANDLED;

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
*/
#include <linux/uaccess.h>
@@ -38,6 +38,7 @@
#include "cam_req_mgr_workq.h"
#include "cam_mem_mgr.h"
#include "a5_core.h"
#include "lx7_core.h"
#include "hfi_sys_defs.h"
#include "cam_debug_util.h"
#include "cam_soc_util.h"
@@ -59,6 +60,12 @@ static const struct hfi_ops hfi_a5_ops = {
.iface_addr = cam_a5_iface_addr,
};
static const struct hfi_ops hfi_lx7_ops = {
.irq_raise = cam_lx7_irq_raise,
.irq_enable = cam_lx7_irq_enable,
.iface_addr = cam_lx7_iface_addr,
};
static struct cam_icp_hw_mgr icp_hw_mgr;
static void cam_icp_mgr_process_dbg_buf(unsigned int debug_lvl);
@@ -2653,9 +2660,8 @@ static int32_t cam_icp_mgr_process_msg(void *priv, void *data)
cam_icp_mgr_process_dbg_buf(icp_hw_mgr.icp_dbg_lvl);
if ((task_data->irq_status & A5_WDT_0) ||
(task_data->irq_status & A5_WDT_1)) {
CAM_ERR_RATE_LIMIT(CAM_ICP, "watch dog interrupt from A5");
if (task_data->recover) {
CAM_ERR_RATE_LIMIT(CAM_ICP, "issuing device recovery...");
rc = cam_icp_mgr_trigger_recovery(hw_mgr);
}
@@ -2663,7 +2669,7 @@ static int32_t cam_icp_mgr_process_msg(void *priv, void *data)
return rc;
}
static int cam_icp_hw_mgr_cb(void *data, uint32_t irq_status)
static int32_t cam_icp_hw_mgr_cb(void *data, bool recover)
{
int rc = 0;
unsigned long flags;
@@ -2686,7 +2692,7 @@ static int cam_icp_hw_mgr_cb(void *data, uint32_t irq_status)
task_data = (struct hfi_msg_work_data *)task->payload;
task_data->data = hw_mgr;
task_data->irq_status = irq_status;
task_data->recover = recover;
task_data->type = ICP_WORKQ_TASK_MSG_TYPE;
task->process_cb = cam_icp_mgr_process_msg;
rc = cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr,
@@ -3726,6 +3732,9 @@ static int cam_icp_mgr_hfi_init(struct cam_icp_hw_mgr *hw_mgr)
hfi_mem.io_mem2.len = 0x0;
}
if (icp_dev_intf->hw_type == CAM_ICP_DEV_LX7)
hfi_ops = &hfi_lx7_ops;
else
hfi_ops = &hfi_a5_ops;
return cam_hfi_init(&hfi_mem, hfi_ops, icp_dev, 0);

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
*/
#ifndef CAM_ICP_HW_MGR_H
@@ -115,12 +115,12 @@ struct hfi_cmd_work_data {
* struct hfi_msg_work_data
* @type: Task type
* @data: Pointer to message data
* @irq_status: IRQ status
* @recover: Device needs recovery
*/
struct hfi_msg_work_data {
uint32_t type;
void *data;
uint32_t irq_status;
bool recover;
};
/**

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
*/
#ifndef CAM_ICP_HW_INTF_H
@@ -17,6 +17,7 @@
enum cam_icp_hw_type {
CAM_ICP_DEV_A5,
CAM_ICP_DEV_LX7,
CAM_ICP_DEV_IPE,
CAM_ICP_DEV_BPS,
CAM_ICP_DEV_MAX,
@@ -41,8 +42,8 @@ enum cam_icp_cmd_type {
};
struct cam_icp_irq_cb {
int32_t (*cb)(void *data, bool recover);
void *data;
int (*cb)(void *data, uint32_t irq_status);
};
/**

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
*/
#ifndef CAM_ICP_HW_MGR_INTF_H
@@ -14,7 +14,7 @@
#define ICP_CLK_TURBO_HZ 600000000
#define ICP_CLK_SVS_HZ 400000000
#define CAM_ICP_A5_BW_BYTES_VOTE 40000000
#define CAM_ICP_BW_BYTES_VOTE 40000000
#define CAM_ICP_CTX_MAX 54

View File

@@ -0,0 +1,391 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#include <linux/firmware.h>
#include <linux/of_address.h>
#include <linux/qcom_scm.h>
#include "cam_cpas_api.h"
#include "cam_debug_util.h"
#include "cam_hw.h"
#include "cam_hw_intf.h"
#include "cam_icp_hw_mgr_intf.h"
#include "cam_icp_hw_intf.h"
#include "hfi_intf.h"
#include "lx7_core.h"
#include "lx7_reg.h"
#include "lx7_soc.h"
#define TZ_STATE_RESUME 0
#define TZ_STATE_SUSPEND 1
#define LX7_FW_NAME "CAMERA_ICP.elf"
#define LX7_GEN_PURPOSE_REG_OFFSET 0x20
static int cam_lx7_ubwc_configure(struct cam_hw_soc_info *soc_info)
{
int i = 0, rc, ddr_type;
struct lx7_soc_info *soc_priv;
uint32_t ipe_ubwc_cfg[UBWC_CONFIG_MAX];
uint32_t bps_ubwc_cfg[UBWC_CONFIG_MAX];
if (!soc_info || !soc_info->soc_private) {
CAM_ERR(CAM_ICP, "invalid LX7 soc info");
return -EINVAL;
}
soc_priv = soc_info->soc_private;
ddr_type = of_fdt_get_ddrtype();
if (ddr_type == DDR_TYPE_LPDDR5 || ddr_type == DDR_TYPE_LPDDR5X)
i = 1;
ipe_ubwc_cfg[0] = soc_priv->ubwc_cfg.ipe_fetch[i];
ipe_ubwc_cfg[1] = soc_priv->ubwc_cfg.ipe_write[i];
bps_ubwc_cfg[0] = soc_priv->ubwc_cfg.bps_fetch[i];
bps_ubwc_cfg[1] = soc_priv->ubwc_cfg.bps_write[i];
rc = hfi_cmd_ubwc_config_ext(ipe_ubwc_cfg, bps_ubwc_cfg);
if (rc) {
CAM_ERR(CAM_ICP, "failed to write UBWC config rc=%d", rc);
return rc;
}
return 0;
}
static int cam_lx7_cpas_vote(struct cam_lx7_core_info *core_info,
struct cam_icp_cpas_vote *vote)
{
int rc;
if (!core_info || !vote)
return -EINVAL;
if (vote->ahb_vote_valid) {
rc = cam_cpas_update_ahb_vote(core_info->cpas_handle,
&vote->ahb_vote);
if (rc) {
CAM_ERR(CAM_ICP, "AHB vote update failed rc=%d", rc);
return rc;
}
}
if (vote->axi_vote_valid) {
rc = cam_cpas_update_axi_vote(core_info->cpas_handle,
&vote->axi_vote);
if (rc) {
CAM_ERR(CAM_ICP, "AXI vote update failed rc=%d", rc);
return rc;
}
}
return 0;
}
static bool cam_lx7_cpas_cb(uint32_t handle, void *user_data,
struct cam_cpas_irq_data *irq_data)
{
bool ret = false;
(void)user_data;
if (!irq_data)
return false;
switch (irq_data->irq_type) {
case CAM_CAMNOC_IRQ_IPE_BPS_UBWC_DECODE_ERROR:
CAM_ERR_RATE_LIMIT(CAM_ICP,
"IPE/BPS UBWC decode error status=0x%08x",
irq_data->u.dec_err.decerr_status.value);
ret = true;
case CAM_CAMNOC_IRQ_IPE_BPS_UBWC_ENCODE_ERROR:
CAM_ERR_RATE_LIMIT(CAM_ICP,
"IPE/BPS UBWC encode error status=0x%08x",
irq_data->u.enc_err.encerr_status.value);
ret = true;
default:
CAM_ERR(CAM_ICP, "unhandled irq_type=%d", irq_data->irq_type);
break;
}
return ret;
}
int cam_lx7_cpas_register(struct cam_hw_intf *lx7_intf)
{
struct cam_cpas_register_params params;
struct cam_hw_info *lx7_info;
struct cam_lx7_core_info *core_info;
int rc;
if (!lx7_intf)
return -EINVAL;
lx7_info = lx7_intf->hw_priv;
params.dev = lx7_info->soc_info.dev;
params.cell_index = lx7_intf->hw_idx;
params.cam_cpas_client_cb = cam_lx7_cpas_cb;
params.userdata = NULL;
strlcpy(params.identifier, "icp", CAM_HW_IDENTIFIER_LENGTH);
rc = cam_cpas_register_client(&params);
if (rc)
return rc;
core_info = lx7_info->core_info;
core_info->cpas_handle = params.client_handle;
return rc;
}
int cam_lx7_cpas_unregister(struct cam_hw_intf *lx7_intf)
{
struct cam_hw_info *lx7_info;
struct cam_lx7_core_info *core_info;
if (!lx7_intf)
return -EINVAL;
lx7_info = lx7_intf->hw_priv;
core_info = lx7_info->core_info;
return cam_cpas_unregister_client(core_info->cpas_handle);
}
static int __lx7_cpas_start(struct cam_lx7_core_info *core_info,
struct cam_icp_cpas_vote *vote)
{
int rc;
if (!core_info || core_info->cpas_start)
return -EINVAL;
rc = cam_cpas_start(core_info->cpas_handle,
&vote->ahb_vote, &vote->axi_vote);
if (rc) {
CAM_ERR(CAM_ICP, "failed to start cpas rc=%d", rc);
return rc;
}
core_info->cpas_start = true;
return 0;
}
static int cam_lx7_cpas_start(struct cam_lx7_core_info *core_info)
{
struct cam_icp_cpas_vote vote;
vote.ahb_vote.type = CAM_VOTE_ABSOLUTE;
vote.ahb_vote.vote.level = CAM_LOWSVS_VOTE;
vote.axi_vote.num_paths = 1;
vote.axi_vote.axi_path[0].path_data_type = CAM_ICP_DEFAULT_AXI_PATH;
vote.axi_vote.axi_path[0].transac_type = CAM_ICP_DEFAULT_AXI_TRANSAC;
vote.axi_vote.axi_path[0].camnoc_bw = CAM_ICP_BW_BYTES_VOTE;
vote.axi_vote.axi_path[0].mnoc_ab_bw = CAM_ICP_BW_BYTES_VOTE;
vote.axi_vote.axi_path[0].mnoc_ib_bw = CAM_ICP_BW_BYTES_VOTE;
vote.axi_vote.axi_path[0].ddr_ab_bw = CAM_ICP_BW_BYTES_VOTE;
vote.axi_vote.axi_path[0].ddr_ib_bw = CAM_ICP_BW_BYTES_VOTE;
return __lx7_cpas_start(core_info, &vote);
}
static int cam_lx7_cpas_stop(struct cam_lx7_core_info *core_info)
{
int rc;
if (!core_info || !core_info->cpas_start)
return -EINVAL;
rc = cam_cpas_stop(core_info->cpas_handle);
if (rc) {
CAM_ERR(CAM_ICP, "failed to stop cpas rc=%d", rc);
return rc;
}
core_info->cpas_start = false;
return 0;
}
int cam_lx7_hw_init(void *priv, void *args, uint32_t arg_size)
{
struct cam_hw_info *lx7 = priv;
int rc;
if (!lx7) {
CAM_ERR(CAM_ICP, "LX7 device info cannot be NULL");
return -EINVAL;
}
rc = cam_lx7_cpas_start(lx7->core_info);
if (rc)
return rc;
rc = cam_lx7_soc_resources_enable(&lx7->soc_info);
if (rc) {
CAM_ERR(CAM_ICP, "failed to enable soc resources rc=%d", rc);
goto soc_fail;
}
return 0;
soc_fail:
cam_lx7_cpas_stop(lx7->core_info);
return rc;
}
int cam_lx7_hw_deinit(void *priv, void *args, uint32_t arg_size)
{
struct cam_hw_info *lx7_info = priv;
int rc;
if (!lx7_info) {
CAM_ERR(CAM_ICP, "LX7 device info cannot be NULL");
return -EINVAL;
}
rc = cam_lx7_soc_resources_disable(&lx7_info->soc_info);
if (rc) {
CAM_ERR(CAM_ICP, "failed to disable soc resources rc=%d", rc);
return rc;
}
return cam_lx7_cpas_stop(lx7_info->core_info);
}
static int __tz_set_icp_state(uint32_t state)
{
int rc;
rc = qcom_scm_set_remote_state(state, CAM_FW_PAS_ID);
if (rc)
CAM_ERR(CAM_ICP, "remote state set to %s failed rc=%d",
state == TZ_STATE_RESUME ? "resume" : "suspend", rc);
return rc;
}
int cam_lx7_process_cmd(void *priv, uint32_t cmd_type,
void *args, uint32_t arg_size)
{
struct cam_hw_info *lx7_info = priv;
int rc = -EINVAL;
if (!lx7_info) {
CAM_ERR(CAM_ICP, "LX7 device info cannot be NULL");
return -EINVAL;
}
switch (cmd_type) {
case CAM_ICP_CMD_POWER_COLLAPSE:
rc = __tz_set_icp_state(TZ_STATE_SUSPEND);
break;
case CAM_ICP_CMD_POWER_RESUME:
rc = __tz_set_icp_state(TZ_STATE_RESUME);
break;
case CAM_ICP_CMD_VOTE_CPAS:
rc = cam_lx7_cpas_vote(lx7_info->core_info, args);
break;
case CAM_ICP_CMD_CPAS_START:
rc = __lx7_cpas_start(lx7_info->core_info, args);
break;
case CAM_ICP_CMD_CPAS_STOP:
rc = cam_lx7_cpas_stop(lx7_info->core_info);
break;
case CAM_ICP_CMD_UBWC_CFG:
rc = cam_lx7_ubwc_configure(&lx7_info->soc_info);
break;
default:
CAM_ERR(CAM_ICP, "invalid command type=%u", cmd_type);
break;
}
return rc;
}
irqreturn_t cam_lx7_handle_irq(int irq_num, void *data)
{
struct cam_hw_info *lx7_info = data;
struct cam_lx7_core_info *core_info = NULL;
bool recover = false;
uint32_t status = 0;
void __iomem *cirq_base;
if (!lx7_info) {
CAM_ERR(CAM_ICP, "invalid LX7 device info");
return IRQ_NONE;
}
cirq_base = lx7_info->soc_info.reg_map[LX7_CIRQ_BASE].mem_base;
status = cam_io_r_mb(cirq_base + ICP_LX7_CIRQ_OB_STATUS);
cam_io_w_mb(status, cirq_base + ICP_LX7_CIRQ_OB_CLEAR);
cam_io_w_mb(LX7_IRQ_CLEAR_CMD, cirq_base + ICP_LX7_CIRQ_OB_IRQ_CMD);
if (status & (LX7_WDT_BITE_WS0 | LX7_WDT_BITE_WS1)) {
CAM_ERR_RATE_LIMIT(CAM_ICP, "got watchdog interrupt from LX7");
recover = true;
}
core_info = lx7_info->core_info;
spin_lock(&lx7_info->hw_lock);
if (core_info->irq_cb.cb)
core_info->irq_cb.cb(core_info->irq_cb.data,
recover);
spin_unlock(&lx7_info->hw_lock);
return IRQ_HANDLED;
}
void cam_lx7_irq_raise(void *priv)
{
struct cam_hw_info *lx7_info = priv;
if (!lx7_info) {
CAM_ERR(CAM_ICP, "invalid LX7 device info");
return;
}
cam_io_w_mb(LX7_HOST2ICPINT,
lx7_info->soc_info.reg_map[LX7_CIRQ_BASE].mem_base +
ICP_LX7_CIRQ_HOST2ICPINT);
}
void cam_lx7_irq_enable(void *priv)
{
struct cam_hw_info *lx7_info = priv;
if (!lx7_info) {
CAM_ERR(CAM_ICP, "invalid LX7 device info");
return;
}
cam_io_w_mb(LX7_WDT_BITE_WS0 | LX7_ICP2HOSTINT,
lx7_info->soc_info.reg_map[LX7_CIRQ_BASE].mem_base +
ICP_LX7_CIRQ_OB_MASK);
}
void __iomem *cam_lx7_iface_addr(void *priv)
{
struct cam_hw_info *lx7_info = priv;
void __iomem *base;
if (!lx7_info) {
CAM_ERR(CAM_ICP, "invalid LX7 device info");
return ERR_PTR(-EINVAL);
}
base = lx7_info->soc_info.reg_map[LX7_CSR_BASE].mem_base;
return base + LX7_GEN_PURPOSE_REG_OFFSET;
}

View File

@@ -0,0 +1,39 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef _CAM_LX7_CORE_H_
#define _CAM_LX7_CORE_H_
#include "cam_hw_intf.h"
#include "cam_icp_hw_intf.h"
#define LX7_CSR_BASE 0
#define LX7_CIRQ_BASE 1
/* TODO: Update once we're ready to use TZ */
#define UNSUPPORTED_PROC_PAS_ID 30
#define CAM_FW_PAS_ID UNSUPPORTED_PROC_PAS_ID
struct cam_lx7_core_info {
struct cam_icp_irq_cb irq_cb;
uint32_t cpas_handle;
bool cpas_start;
};
int cam_lx7_hw_init(void *priv, void *args, uint32_t arg_size);
int cam_lx7_hw_deinit(void *priv, void *args, uint32_t arg_size);
int cam_lx7_process_cmd(void *priv, uint32_t cmd_type,
void *args, uint32_t arg_size);
int cam_lx7_cpas_register(struct cam_hw_intf *lx7_intf);
int cam_lx7_cpas_unregister(struct cam_hw_intf *lx7_intf);
irqreturn_t cam_lx7_handle_irq(int irq_num, void *data);
void cam_lx7_irq_raise(void *priv);
void cam_lx7_irq_enable(void *priv);
void __iomem *cam_lx7_iface_addr(void *priv);
#endif /* _CAM_LX7_CORE_H_ */

View File

@@ -0,0 +1,164 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/of_device.h>
#include "camera_main.h"
#include "cam_debug_util.h"
#include "cam_hw.h"
#include "cam_hw_intf.h"
#include "cam_icp_hw_intf.h"
#include "lx7_core.h"
#include "lx7_soc.h"
static int cam_lx7_component_bind(struct device *dev,
struct device *mdev, void *data)
{
int rc = 0;
struct cam_hw_intf *lx7_intf = NULL;
struct cam_hw_info *lx7_info = NULL;
struct lx7_soc_info *lx7_soc_info = NULL;
struct cam_lx7_core_info *core_info = NULL;
struct platform_device *pdev = to_platform_device(dev);
lx7_intf = kzalloc(sizeof(*lx7_intf), GFP_KERNEL);
if (!lx7_intf)
return -ENOMEM;
lx7_info = kzalloc(sizeof(*lx7_info), GFP_KERNEL);
if (!lx7_info) {
rc = -ENOMEM;
goto free_hw_intf;
}
lx7_soc_info = kzalloc(sizeof(*lx7_soc_info), GFP_KERNEL);
if (!lx7_soc_info) {
rc = -ENOMEM;
goto free_hw_info;
}
core_info = kzalloc(sizeof(*core_info), GFP_KERNEL);
if (!core_info) {
rc = -ENOMEM;
goto free_soc_info;
}
lx7_info->core_info = core_info;
lx7_info->soc_info.pdev = pdev;
lx7_info->soc_info.dev = &pdev->dev;
lx7_info->soc_info.dev_name = pdev->name;
lx7_info->soc_info.soc_private = lx7_soc_info;
mutex_init(&lx7_info->hw_mutex);
spin_lock_init(&lx7_info->hw_lock);
init_completion(&lx7_info->hw_complete);
rc = cam_lx7_soc_resources_init(&lx7_info->soc_info,
cam_lx7_handle_irq, lx7_info);
if (rc) {
CAM_ERR(CAM_ICP, "soc resources init failed rc=%d", rc);
goto free_core_info;
}
lx7_intf->hw_priv = lx7_info;
lx7_intf->hw_type = CAM_ICP_DEV_LX7;
lx7_intf->hw_idx = lx7_info->soc_info.index;
lx7_intf->hw_ops.init = cam_lx7_hw_init;
lx7_intf->hw_ops.deinit = cam_lx7_hw_deinit;
lx7_intf->hw_ops.process_cmd = cam_lx7_process_cmd;
rc = cam_lx7_cpas_register(lx7_intf);
if (rc) {
CAM_ERR(CAM_ICP, "cpas registration failed rc=%d", rc);
goto res_deinit;
}
platform_set_drvdata(pdev, lx7_intf);
return 0;
res_deinit:
cam_lx7_soc_resources_deinit(&lx7_info->soc_info);
free_core_info:
kfree(core_info);
free_soc_info:
kfree(lx7_soc_info);
free_hw_info:
kfree(lx7_info);
free_hw_intf:
kfree(lx7_intf);
return rc;
}
static void cam_lx7_component_unbind(struct device *dev,
struct device *mdev, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
struct cam_hw_intf *lx7_intf = platform_get_drvdata(pdev);
struct cam_hw_info *lx7_info = lx7_intf->hw_priv;
cam_lx7_cpas_unregister(lx7_intf);
cam_lx7_soc_resources_deinit(&lx7_info->soc_info);
kfree(lx7_info->core_info);
kfree(lx7_info->soc_info.soc_private);
kfree(lx7_info);
kfree(lx7_intf);
}
static const struct component_ops cam_lx7_component_ops = {
.bind = cam_lx7_component_bind,
.unbind = cam_lx7_component_unbind,
};
static const struct of_device_id cam_lx7_match[] = {
{ .compatible = "qcom,cam-lx7"},
{},
};
MODULE_DEVICE_TABLE(of, cam_lx7_match);
static int cam_lx7_driver_probe(struct platform_device *pdev)
{
int rc;
rc = component_add(&pdev->dev, &cam_lx7_component_ops);
if (rc)
CAM_ERR(CAM_ICP, "cam-lx7 component add failed rc=%d", rc);
return rc;
}
static int cam_lx7_driver_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &cam_lx7_component_ops);
return 0;
}
struct platform_driver cam_lx7_driver = {
.probe = cam_lx7_driver_probe,
.remove = cam_lx7_driver_remove,
.driver = {
.name = "cam-lx7",
.of_match_table = cam_lx7_match,
.suppress_bind_attrs = true,
},
};
int cam_lx7_init_module(void)
{
return platform_driver_register(&cam_lx7_driver);
}
void cam_lx7_exit_module(void)
{
platform_driver_unregister(&cam_lx7_driver);
}
MODULE_DESCRIPTION("Camera LX7 driver");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef _CAM_LX7_DEV_H_
#define _CAM_LX7_DEV_H_
int cam_lx7_init_module(void);
void cam_lx7_exit_module(void);
#endif /* _CAM_LX7_DEV_H_ */

View File

@@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef _CAM_LX7_REG_H_
#define _CAM_LX7_REG_H_
#define ICP_LX7_CIRQ_OB_MASK 0x0
#define ICP_LX7_CIRQ_OB_CLEAR 0x4
#define ICP_LX7_CIRQ_OB_STATUS 0xc
/* These bitfields are shared by OB_MASK, OB_CLEAR, OB_STATUS */
#define LX7_WDT_BITE_WS1 (1 << 6)
#define LX7_WDT_BARK_WS1 (1 << 5)
#define LX7_WDT_BITE_WS0 (1 << 4)
#define LX7_WDT_BARK_WS0 (1 << 3)
#define LX7_ICP2HOSTINT (1 << 2)
#define ICP_LX7_CIRQ_OB_IRQ_CMD 0x10
#define LX7_IRQ_CLEAR_CMD (1 << 1)
#define ICP_LX7_CIRQ_HOST2ICPINT 0x124
#define LX7_HOST2ICPINT (1 << 0)
#endif /* _CAM_LX7_REG_H_ */

View File

@@ -0,0 +1,140 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#include <linux/interrupt.h>
#include <linux/of.h>
#include "cam_debug_util.h"
#include "cam_soc_util.h"
#include "lx7_soc.h"
static int __ubwc_config_get(struct device_node *np, char *name, uint32_t *cfg)
{
int nconfig;
int i;
nconfig = of_property_count_u32_elems(np, name);
if (nconfig < 0 || nconfig > UBWC_CONFIG_MAX) {
CAM_ERR(CAM_ICP, "invalid number of UBWC configs[=%d]",
nconfig);
return -EINVAL;
}
for (i = 0; i < nconfig; i++) {
int rc;
rc = of_property_read_u32_index(np, name, i, &cfg[i]);
if (rc) {
CAM_ERR(CAM_ICP,
"node %pOF has no valid %s prop at index=%d",
np, name, i);
return rc;
}
}
return 0;
}
static int cam_lx7_ubwc_config_get(struct lx7_soc_info *lx7_soc_info,
struct device_node *np)
{
int rc;
rc = __ubwc_config_get(np, "ubwc-ipe-fetch-cfg",
lx7_soc_info->ubwc_cfg.ipe_fetch);
if (rc)
return rc;
rc = __ubwc_config_get(np, "ubwc-ipe-write-cfg",
lx7_soc_info->ubwc_cfg.ipe_write);
if (rc)
return rc;
rc = __ubwc_config_get(np, "ubwc-bps-fetch-cfg",
lx7_soc_info->ubwc_cfg.bps_fetch);
if (rc)
return rc;
rc = __ubwc_config_get(np, "ubwc-bps-write-cfg",
lx7_soc_info->ubwc_cfg.bps_write);
if (rc)
return rc;
return 0;
}
static int cam_lx7_dt_properties_get(struct cam_hw_soc_info *soc_info)
{
int rc;
rc = cam_soc_util_get_dt_properties(soc_info);
if (rc) {
CAM_ERR(CAM_ICP, "failed to get DT properties rc=%d", rc);
return rc;
}
rc = cam_lx7_ubwc_config_get(soc_info->soc_private,
soc_info->pdev->dev.of_node);
if (rc) {
CAM_ERR(CAM_ICP, "failed to get UBWC config props rc=%d", rc);
return rc;
}
return 0;
}
int cam_lx7_soc_resources_init(struct cam_hw_soc_info *soc_info,
irq_handler_t handler, void *data)
{
int rc;
rc = cam_lx7_dt_properties_get(soc_info);
if (rc)
return rc;
rc = cam_soc_util_request_platform_resource(soc_info, handler, data);
if (rc) {
CAM_ERR(CAM_ICP,
"request for soc platform resource failed rc=%d", rc);
return rc;
}
return 0;
}
int cam_lx7_soc_resources_deinit(struct cam_hw_soc_info *soc_info)
{
int rc;
rc = cam_soc_util_release_platform_resource(soc_info);
if (rc)
CAM_ERR(CAM_ICP,
"release of soc platform resource failed rc=%d", rc);
return rc;
}
int cam_lx7_soc_resources_enable(struct cam_hw_soc_info *soc_info)
{
int rc = 0;
rc = cam_soc_util_enable_platform_resource(soc_info, true,
CAM_SVS_VOTE, true);
if (rc)
CAM_ERR(CAM_ICP, "failed to enable soc resources rc=%d", rc);
return rc;
}
int cam_lx7_soc_resources_disable(struct cam_hw_soc_info *soc_info)
{
int rc = 0;
rc = cam_soc_util_disable_platform_resource(soc_info, true, true);
if (rc)
CAM_ERR(CAM_ICP, "failed to disable soc resources rc=%d", rc);
return rc;
}

View File

@@ -0,0 +1,32 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef _CAM_LX7_SOC_H_
#define _CAM_LX7_SOC_H_
#include <linux/interrupt.h>
#include "cam_soc_util.h"
#define UBWC_CONFIG_MAX 2
struct lx7_soc_info {
struct {
uint32_t ipe_fetch[UBWC_CONFIG_MAX];
uint32_t ipe_write[UBWC_CONFIG_MAX];
uint32_t bps_fetch[UBWC_CONFIG_MAX];
uint32_t bps_write[UBWC_CONFIG_MAX];
} ubwc_cfg;
};
int cam_lx7_soc_resources_init(struct cam_hw_soc_info *soc_info,
irq_handler_t irq_handler,
void *irq_data);
int cam_lx7_soc_resources_deinit(struct cam_hw_soc_info *soc_info);
int cam_lx7_soc_resources_enable(struct cam_hw_soc_info *soc_info);
int cam_lx7_soc_resources_disable(struct cam_hw_soc_info *soc_info);
#endif /* _CAM_LX7_SOC_H_ */

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
#include <linux/build_bug.h>
@@ -30,6 +30,7 @@
#endif
#include "a5_core.h"
#include "lx7_dev.h"
#include "ipe_core.h"
#include "bps_core.h"
#include "cam_icp_subdev.h"
@@ -118,6 +119,7 @@ static const struct camera_submodule_component camera_sensor[] = {
static const struct camera_submodule_component camera_icp[] = {
#ifdef CONFIG_SPECTRA_ICP
{&cam_a5_init_module, &cam_a5_exit_module},
{&cam_lx7_init_module, &cam_lx7_exit_module},
{&cam_ipe_init_module, &cam_ipe_exit_module},
{&cam_bps_init_module, &cam_bps_exit_module},
{&cam_icp_init_module, &cam_icp_exit_module},

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
*/
#ifndef CAMERA_MAIN_H
@@ -44,6 +44,7 @@ extern struct platform_driver cam_flash_platform_driver;
#endif
#ifdef CONFIG_SPECTRA_ICP
extern struct platform_driver cam_a5_driver;
extern struct platform_driver cam_lx7_driver;
extern struct platform_driver cam_ipe_driver;
extern struct platform_driver cam_bps_driver;
extern struct platform_driver cam_icp_driver;
@@ -112,6 +113,7 @@ static struct platform_driver *const cam_component_drivers[] = {
#endif
#ifdef CONFIG_SPECTRA_ICP
&cam_a5_driver,
&cam_lx7_driver,
&cam_ipe_driver,
&cam_bps_driver,
&cam_icp_driver,