msm: camera: cre: Add CRE driver changes

Add new camera driver Camera Reformat Engine (CRE).

CRs-Fixed: 2893978
Change-Id: Ia39b222493393bf7bb72525c86f0060ca0dc77c2
Signed-off-by: Vikram Sharma <vikramsa@codeaurora.org>
This commit is contained in:
Vikram Sharma
2021-01-19 16:30:24 +05:30
committed by Gerrit - the friendly Code Review server
parent 4aa9286fd7
commit 23762a67ed
27 changed files with 7513 additions and 70 deletions

11
Kbuild
View File

@@ -241,6 +241,17 @@ camera-$(CONFIG_SPECTRA_OPE) += \
drivers/cam_ope/ope_hw_mgr/ope_hw/bus_rd/ope_bus_rd.o\
drivers/cam_ope/ope_hw_mgr/ope_hw/bus_wr/ope_bus_wr.o
camera-$(CONFIG_SPECTRA_CRE) += \
drivers/cam_cre/cam_cre_hw_mgr/cre_hw/cre_core.o \
drivers/cam_cre/cam_cre_hw_mgr/cre_hw/cre_soc.o \
drivers/cam_cre/cam_cre_hw_mgr/cre_hw/cre_dev.o \
drivers/cam_cre/cam_cre_hw_mgr/cre_hw/top/cre_top.o \
drivers/cam_cre/cam_cre_hw_mgr/cre_hw/bus_rd/cre_bus_rd.o \
drivers/cam_cre/cam_cre_hw_mgr/cre_hw/bus_wr/cre_bus_wr.o \
drivers/cam_cre/cam_cre_hw_mgr/cam_cre_hw_mgr.o \
drivers/cam_cre/cam_cre_dev.o \
drivers/cam_cre/cam_cre_context.o
camera-$(CONFIG_SPECTRA_TFE) += \
drivers/cam_isp/isp_hw_mgr/isp_hw/ppi_hw/cam_csid_ppi_core.o \
drivers/cam_isp/isp_hw_mgr/isp_hw/ppi_hw/cam_csid_ppi_dev.o \

View File

@@ -0,0 +1,287 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#include <linux/debugfs.h>
#include <linux/videodev2.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "cam_trace.h"
#include "cam_mem_mgr.h"
#include "cam_cre_context.h"
#include "cam_context_utils.h"
#include "cam_debug_util.h"
#include "cam_packet_util.h"
#include "cam_context.h"
static const char cre_dev_name[] = "cam-cre";
static int __cam_cre_start_dev_in_acquired(struct cam_context *ctx,
struct cam_start_stop_dev_cmd *cmd)
{
int rc;
rc = cam_context_start_dev_to_hw(ctx, cmd);
if (!rc) {
ctx->state = CAM_CTX_READY;
trace_cam_context_state("CRE", ctx);
}
return rc;
}
static int __cam_cre_ctx_flush_dev_in_ready(struct cam_context *ctx,
struct cam_flush_dev_cmd *cmd)
{
int rc;
rc = cam_context_flush_dev_to_hw(ctx, cmd);
if (rc)
CAM_ERR(CAM_CRE, "Failed to flush device");
return rc;
}
static int __cam_cre_ctx_dump_dev_in_ready(struct cam_context *ctx,
struct cam_dump_req_cmd *cmd)
{
int rc;
rc = cam_context_dump_dev_to_hw(ctx, cmd);
if (rc)
CAM_ERR(CAM_CRE, "Failed to dump device");
return rc;
}
static int __cam_cre_ctx_config_dev_in_ready(struct cam_context *ctx,
struct cam_config_dev_cmd *cmd)
{
int rc;
size_t len;
uintptr_t packet_addr;
rc = cam_mem_get_cpu_buf((int32_t) cmd->packet_handle,
&packet_addr, &len);
if (rc) {
CAM_ERR(CAM_CRE, "[%s][%d] Can not get packet address",
ctx->dev_name, ctx->ctx_id);
rc = -EINVAL;
return rc;
}
rc = cam_context_prepare_dev_to_hw(ctx, cmd);
if (rc)
CAM_ERR(CAM_CRE, "Failed to prepare device");
return rc;
}
static int __cam_cre_ctx_stop_dev_in_ready(struct cam_context *ctx,
struct cam_start_stop_dev_cmd *cmd)
{
int rc;
rc = cam_context_stop_dev_to_hw(ctx);
if (rc)
CAM_ERR(CAM_CRE, "Failed to stop device");
ctx->state = CAM_CTX_ACQUIRED;
trace_cam_context_state("CRE", ctx);
return rc;
}
static int __cam_cre_ctx_release_dev_in_acquired(struct cam_context *ctx,
struct cam_release_dev_cmd *cmd)
{
int rc;
rc = cam_context_release_dev_to_hw(ctx, cmd);
if (rc)
CAM_ERR(CAM_CRE, "Unable to release device %d", rc);
ctx->state = CAM_CTX_AVAILABLE;
return rc;
}
static int __cam_cre_ctx_release_dev_in_ready(struct cam_context *ctx,
struct cam_release_dev_cmd *cmd)
{
int rc;
rc = __cam_cre_ctx_stop_dev_in_ready(ctx, NULL);
if (rc)
CAM_ERR(CAM_CRE, "Failed to stop device");
rc = __cam_cre_ctx_release_dev_in_acquired(ctx, cmd);
if (rc)
CAM_ERR(CAM_CRE, "Failed to release device");
return rc;
}
static int __cam_cre_ctx_handle_buf_done_in_ready(void *ctx,
uint32_t evt_id, void *done)
{
return cam_context_buf_done_from_hw(ctx, done, evt_id);
}
static int cam_cre_context_dump_active_request(void *data,
struct cam_smmu_pf_info *pf_info)
{
struct cam_context *ctx = (struct cam_context *)data;
struct cam_ctx_request *req = NULL;
struct cam_ctx_request *req_temp = NULL;
struct cam_hw_mgr_dump_pf_data *pf_dbg_entry = NULL;
uint32_t resource_type = 0;
int rc = 0;
int closest_port;
bool b_mem_found = false, b_ctx_found = false;
if (!ctx) {
CAM_ERR(CAM_CRE, "Invalid ctx");
return -EINVAL;
}
CAM_INFO(CAM_CRE, "iommu fault for cre ctx %d state %d",
ctx->ctx_id, ctx->state);
list_for_each_entry_safe(req, req_temp,
&ctx->active_req_list, list) {
pf_dbg_entry = &(req->pf_data);
closest_port = -1;
CAM_INFO(CAM_CRE, "req_id : %lld ", req->request_id);
rc = cam_context_dump_pf_info_to_hw(ctx, pf_dbg_entry->packet,
&b_mem_found, &b_ctx_found, &resource_type, pf_info);
if (rc)
CAM_ERR(CAM_CRE, "Failed to dump pf info");
if (b_mem_found)
CAM_ERR(CAM_CRE, "Found page fault in req %lld %d",
req->request_id, rc);
}
return rc;
}
static int __cam_cre_ctx_acquire_dev_in_available(struct cam_context *ctx,
struct cam_acquire_dev_cmd *cmd)
{
int rc;
rc = cam_context_acquire_dev_to_hw(ctx, cmd);
if (rc)
CAM_ERR(CAM_CRE, "Unable to Acquire device %d", rc);
else
ctx->state = CAM_CTX_ACQUIRED;
return rc;
}
/* top state machine */
static struct cam_ctx_ops
cam_cre_ctx_state_machine[CAM_CTX_STATE_MAX] = {
/* Uninit */
{
.ioctl_ops = { },
.crm_ops = { },
.irq_ops = NULL,
},
/* Available */
{
.ioctl_ops = {
.acquire_dev = __cam_cre_ctx_acquire_dev_in_available,
},
.crm_ops = { },
.irq_ops = NULL,
},
/* Acquired */
{
.ioctl_ops = {
.release_dev = __cam_cre_ctx_release_dev_in_acquired,
.start_dev = __cam_cre_start_dev_in_acquired,
.config_dev = __cam_cre_ctx_config_dev_in_ready,
.flush_dev = __cam_cre_ctx_flush_dev_in_ready,
.dump_dev = __cam_cre_ctx_dump_dev_in_ready,
},
.crm_ops = { },
.irq_ops = __cam_cre_ctx_handle_buf_done_in_ready,
.pagefault_ops = cam_cre_context_dump_active_request,
},
/* Ready */
{
.ioctl_ops = {
.stop_dev = __cam_cre_ctx_stop_dev_in_ready,
.release_dev = __cam_cre_ctx_release_dev_in_ready,
.config_dev = __cam_cre_ctx_config_dev_in_ready,
.flush_dev = __cam_cre_ctx_flush_dev_in_ready,
.dump_dev = __cam_cre_ctx_dump_dev_in_ready,
},
.crm_ops = {},
.irq_ops = __cam_cre_ctx_handle_buf_done_in_ready,
.pagefault_ops = cam_cre_context_dump_active_request,
},
/* Activated */
{
.ioctl_ops = {},
.crm_ops = {},
.irq_ops = NULL,
.pagefault_ops = cam_cre_context_dump_active_request,
},
};
int cam_cre_context_init(struct cam_cre_context *ctx,
struct cam_context *ctx_base,
struct cam_hw_mgr_intf *hw_intf,
uint32_t ctx_id)
{
int rc;
int i;
if (!ctx || !ctx_base) {
CAM_ERR(CAM_CRE, "Invalid Context");
rc = -EFAULT;
goto err;
}
memset(ctx, 0, sizeof(*ctx));
ctx->base = ctx_base;
for (i = 0; i < CAM_CTX_REQ_MAX; i++)
ctx->req_base[i].req_priv = ctx;
rc = cam_context_init(ctx_base, cre_dev_name, CAM_CRE, ctx_id,
NULL, hw_intf, ctx->req_base, CAM_CTX_REQ_MAX);
if (rc) {
CAM_ERR(CAM_CRE, "Camera Context Base init failed");
goto err;
}
ctx_base->state_machine = cam_cre_ctx_state_machine;
ctx_base->ctx_priv = ctx;
err:
return rc;
}
int cam_cre_context_deinit(struct cam_cre_context *ctx)
{
if (!ctx || !ctx->base) {
CAM_ERR(CAM_CRE, "Invalid params: %pK", ctx);
return -EINVAL;
}
cam_context_deinit(ctx->base);
memset(ctx, 0, sizeof(*ctx));
return 0;
}

View File

@@ -0,0 +1,67 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef _CAM_CRE_CONTEXT_H_
#define _CAM_CRE_CONTEXT_H_
#include <media/cam_cre.h>
#include "cam_context.h"
#include "cam_cre_hw_mgr_intf.h"
#define CAM_CRE_HW_EVENT_MAX 20
/**
* struct cam_cre_context - CRE context
* @base: Base cre cam context object
* @req_base: Common request structure
*/
struct cam_cre_context {
struct cam_context *base;
struct cam_ctx_request req_base[CAM_CTX_REQ_MAX];
};
/* cam cre context irq handling function type */
typedef int (*cam_cre_hw_event_cb_func)(
struct cam_cre_context *ctx_cre,
void *evt_data);
/**
* struct cam_cre_ctx_irq_ops - Function table for handling IRQ callbacks
*
* @irq_ops: Array of handle function pointers.
*
*/
struct cam_cre_ctx_irq_ops {
cam_cre_hw_event_cb_func irq_ops[CAM_CRE_HW_EVENT_MAX];
};
/**
* cam_cre_context_init()
*
* @brief: Initialization function for the CRE context
*
* @ctx: CRE context obj to be initialized
* @ctx_base: Context base from cam_context
* @hw_intf: CRE hw manager interface
* @ctx_id: ID for this context
*
*/
int cam_cre_context_init(struct cam_cre_context *ctx,
struct cam_context *ctx_base,
struct cam_hw_mgr_intf *hw_intf,
uint32_t ctx_id);
/**
* cam_cre_context_deinit()
*
* @brief: Deinitialize function for the CRE context
*
* @ctx: CRE context obj to be deinitialized
*
*/
int cam_cre_context_deinit(struct cam_cre_context *ctx);
#endif /* __CAM_CRE_CONTEXT_H__ */

View File

@@ -0,0 +1,232 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include "cam_node.h"
#include "cam_hw_mgr_intf.h"
#include "cam_cre_hw_mgr.h"
#include "cam_cre_dev.h"
#include "cam_debug_util.h"
#include "cam_smmu_api.h"
#include "camera_main.h"
#define CAM_CRE_DEV_NAME "cam-cre"
struct cam_cre_subdev {
struct cam_subdev sd;
struct cam_node *node;
struct cam_context ctx[CRE_CTX_MAX];
struct cam_cre_context ctx_cre[CRE_CTX_MAX];
struct mutex cre_lock;
int32_t open_cnt;
int32_t reserved;
};
static struct cam_cre_subdev g_cre_dev;
static void cam_cre_dev_iommu_fault_handler(
struct cam_smmu_pf_info *pf_info)
{
int i = 0;
struct cam_node *node = NULL;
if (!pf_info || !pf_info->token) {
CAM_ERR(CAM_ISP, "invalid token in page handler cb");
return;
}
node = (struct cam_node *)pf_info->token;
for (i = 0; i < node->ctx_size; i++)
cam_context_dump_pf_info(&(node->ctx_list[i]), pf_info);
}
static int cam_cre_subdev_open(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh)
{
mutex_lock(&g_cre_dev.cre_lock);
g_cre_dev.open_cnt++;
mutex_unlock(&g_cre_dev.cre_lock);
return 0;
}
static int cam_cre_subdev_close(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh)
{
int rc = 0;
struct cam_node *node = v4l2_get_subdevdata(sd);
mutex_lock(&g_cre_dev.cre_lock);
if (g_cre_dev.open_cnt <= 0) {
CAM_DBG(CAM_CRE, "CRE subdev is already closed");
rc = -EINVAL;
goto end;
}
g_cre_dev.open_cnt--;
if (!node) {
CAM_ERR(CAM_CRE, "Node ptr is NULL");
rc = -EINVAL;
goto end;
}
if (g_cre_dev.open_cnt == 0)
cam_node_shutdown(node);
end:
mutex_unlock(&g_cre_dev.cre_lock);
return rc;
}
static const struct v4l2_subdev_internal_ops cam_cre_subdev_internal_ops = {
.close = cam_cre_subdev_close,
.open = cam_cre_subdev_open,
};
static int cam_cre_subdev_component_bind(struct device *dev,
struct device *master_dev, void *data)
{
int rc;
int i;
struct cam_hw_mgr_intf hw_mgr_intf;
struct cam_node *node;
int iommu_hdl = -1;
struct platform_device *pdev = to_platform_device(dev);
g_cre_dev.sd.internal_ops = &cam_cre_subdev_internal_ops;
rc = cam_subdev_probe(&g_cre_dev.sd, pdev, CAM_CRE_DEV_NAME,
CAM_CRE_DEVICE_TYPE);
if (rc) {
CAM_ERR(CAM_CRE, "CRE cam_subdev_probe failed %d", rc);
goto err;
}
node = (struct cam_node *)g_cre_dev.sd.token;
rc = cam_cre_hw_mgr_init(pdev->dev.of_node,
(uint64_t *)&hw_mgr_intf, &iommu_hdl);
if (rc) {
CAM_ERR(CAM_CRE, "Can not initialize CRE HWmanager %d", rc);
goto unregister;
}
for (i = 0; i < CAM_CRE_CTX_MAX; i++) {
rc = cam_cre_context_init(&g_cre_dev.ctx_cre[i],
&g_cre_dev.ctx[i],
&node->hw_mgr_intf,
i);
if (rc) {
CAM_ERR(CAM_CRE, "CRE context init failed %d %d",
i, rc);
goto ctx_init_fail;
}
}
rc = cam_node_init(node, &hw_mgr_intf, g_cre_dev.ctx, CAM_CRE_CTX_MAX,
CAM_CRE_DEV_NAME);
if (rc) {
CAM_ERR(CAM_CRE, "CRE node init failed %d", rc);
goto ctx_init_fail;
}
cam_smmu_set_client_page_fault_handler(iommu_hdl,
cam_cre_dev_iommu_fault_handler, node);
mutex_init(&g_cre_dev.cre_lock);
CAM_DBG(CAM_CRE, "Component bound successfully");
return rc;
ctx_init_fail:
for (--i; i >= 0; i--)
if (cam_cre_context_deinit(&g_cre_dev.ctx_cre[i]))
CAM_ERR(CAM_CRE, "deinit fail %d %d", i, rc);
unregister:
if (cam_subdev_remove(&g_cre_dev.sd))
CAM_ERR(CAM_CRE, "remove fail %d", rc);
err:
return rc;
}
static void cam_cre_subdev_component_unbind(struct device *dev,
struct device *master_dev, void *data)
{
int rc;
int i;
for (i = 0; i < CAM_CTX_MAX; i++) {
rc = cam_cre_context_deinit(&g_cre_dev.ctx_cre[i]);
if (rc)
CAM_ERR(CAM_CRE, "CRE context %d deinit failed %d",
i, rc);
}
rc = cam_subdev_remove(&g_cre_dev.sd);
if (rc)
CAM_ERR(CAM_CRE, "Unregister failed %d", rc);
}
const static struct component_ops cam_cre_subdev_component_ops = {
.bind = cam_cre_subdev_component_bind,
.unbind = cam_cre_subdev_component_unbind,
};
static int cam_cre_subdev_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &cam_cre_subdev_component_ops);
return 0;
}
static int cam_cre_subdev_probe(struct platform_device *pdev)
{
int rc = 0;
CAM_DBG(CAM_CRE, "Adding CRE component");
rc = component_add(&pdev->dev, &cam_cre_subdev_component_ops);
if (rc)
CAM_ERR(CAM_CRE, "failed to add component rc: %d", rc);
return rc;
}
static const struct of_device_id cam_cre_subdev_dt_match[] = {
{
.compatible = "qcom,cam-cre",
},
{}
};
MODULE_DEVICE_TABLE(of, cam_cre_subdev_dt_match);
struct platform_driver cam_cre_subdev_driver = {
.probe = cam_cre_subdev_probe,
.remove = cam_cre_subdev_remove,
.driver = {
.name = "cam_cre",
.of_match_table = cam_cre_subdev_dt_match,
.suppress_bind_attrs = true,
},
};
int cam_cre_subdev_init_module(void)
{
return platform_driver_register(&cam_cre_driver);
}
void cam_cre_subdev_exit_module(void)
{
platform_driver_unregister(&cam_cre_driver);
}
MODULE_DESCRIPTION("MSM CRE driver");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef _CAM_CRE_DEV_H_
#define _CAM_CRE_DEV_H_
#include "cam_subdev.h"
#include "cam_hw_mgr_intf.h"
#include "cam_context.h"
#include "cam_cre_context.h"
/**
* struct cam_cre_dev - Camera CRE V4l2 device node
*
* @sd: Commone camera subdevice node
* @node: Pointer to cre subdevice
* @ctx: CRE base context storage
* @ctx_cre: CRE private context storage
* @cre_mutex: CRE dev mutex
* @open_cnt: Open device count
*/
struct cam_cre_dev {
struct cam_subdev sd;
struct cam_node *node;
struct cam_context ctx[CAM_CRE_CTX_MAX];
struct cam_cre_context ctx_cre[CAM_CRE_CTX_MAX];
struct mutex cre_mutex;
int32_t open_cnt;
};
/**
* @brief : API to register CRE dev to platform framework.
* @return struct platform_device pointer on success, or ERR_PTR() on error.
*/
int cam_cre_dev_init_module(void);
/**
* @brief : API to remove CRE dev from platform framework.
*/
void cam_cre_dev_exit_module(void);
#endif /* __CAM_CRE_DEV_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,447 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef CAM_CRE_HW_MGR_H
#define CAM_CRE_HW_MGR_H
#include <linux/types.h>
#include <linux/completion.h>
#include <media/cam_cre.h>
#include "cam_cre_hw_intf.h"
#include "cam_hw_mgr_intf.h"
#include "cam_hw_intf.h"
#include "cam_req_mgr_workq.h"
#include "cam_mem_mgr.h"
#include "cam_context.h"
#include "cre_top.h"
#define CRE_CTX_MAX 32
#define CAM_FRAME_CMD_MAX 20
#define CRE_WORKQ_NUM_TASK 100
#define CRE_WORKQ_TASK_CMD_TYPE 1
#define CRE_WORKQ_TASK_MSG_TYPE 2
#define CRE_PACKET_SIZE 0
#define CRE_PACKET_TYPE 1
#define CRE_PACKET_OPCODE 2
#define CRE_PACKET_MAX_CMD_BUFS 4
#define CRE_FRAME_PROCESS_SUCCESS 0
#define CRE_FRAME_PROCESS_FAILURE 1
#define CRE_CTX_STATE_FREE 0
#define CRE_CTX_STATE_IN_USE 1
#define CRE_CTX_STATE_ACQUIRED 2
#define CRE_CTX_STATE_RELEASE 3
#define CRE_MAX_IN_RES 2
#define CRE_MAX_OUT_RES 2
#define CRE_MAX_IO_BUFS 3
#define CAM_CRE_BW_CONFIG_UNKNOWN 0
#define CAM_CRE_BW_CONFIG_V2 2
#define CRE_DEV_MAX 1
#define CLK_HW_CRE 0x0
#define CLK_HW_MAX 0x1
#define CRE_DEVICE_IDLE_TIMEOUT 400
#define CRE_REQUEST_TIMEOUT 200
#define CAM_CRE_HW_CFG_Q_MAX 50
#define CAM_CRE_MAX_PER_PATH_VOTES 6
#define CAM_CRE_MAX_REG_SET 32
/*
* Response time threshold in ms beyond which a request is not expected
* to be with CRE hw
*/
#define CAM_CRE_RESPONSE_TIME_THRESHOLD 100000
/*
* struct cam_cre_irq_data
*
* @error: IRQ error
* @top_irq_status: CRE top irq status
* @wr_buf_done: write engine buf done
*/
struct cam_cre_irq_data {
uint32_t error;
uint32_t top_irq_status;
uint32_t wr_buf_done;
};
/**
* struct cam_cre_hw_intf_data - CRE hw intf data
*
* @Brief: cre hw intf pointer and pid list data
*
* @devices: cre hw intf pointer
* @num_devices: Number of CRE devices
* @num_hw_pid: Number of pids for this hw
* @hw_pid: cre hw pid values
*
*/
struct cam_cre_hw_intf_data {
struct cam_hw_intf *hw_intf;
uint32_t num_hw_pid;
uint32_t hw_pid[CRE_DEV_MAX];
};
/**
* struct cam_ctx_clk_info
* @curr_fc: Context latest request frame cycles
* @rt_flag: Flag to indicate real time request
* @base_clk: Base clock to process the request
* @reserved: Reserved field
* @clk_rate: Supported clock rates for the context
* @num_paths: Number of valid AXI paths
* @axi_path: ctx based per path bw vote
*/
struct cam_ctx_clk_info {
uint32_t curr_fc;
uint32_t rt_flag;
uint32_t base_clk;
uint32_t reserved;
int32_t clk_rate[CAM_MAX_VOTE];
uint32_t num_paths;
struct cam_axi_per_path_bw_vote axi_path[CAM_CRE_MAX_PER_PATH_VOTES];
};
/**
* struct cre_cmd_generic_blob
* @ctx: Current context info
* @req_info_idx: Index used for request
* @io_buf_addr: pointer to io buffer address
*/
struct cre_cmd_generic_blob {
struct cam_cre_ctx *ctx;
uint32_t req_idx;
uint64_t *io_buf_addr;
};
/**
* struct cam_cre_clk_info
* @base_clk: Base clock to process request
* @curr_clk: Current clock of hadrware
* @threshold: Threshold for overclk count
* @over_clked: Over clock count
* @num_paths: Number of AXI vote paths
* @axi_path: Current per path bw vote info
* @hw_type: IPE/BPS device type
* @watch_dog: watchdog timer handle
* @watch_dog_reset_counter: Counter for watch dog reset
*/
struct cam_cre_clk_info {
uint32_t base_clk;
uint32_t curr_clk;
uint32_t threshold;
uint32_t over_clked;
uint32_t num_paths;
struct cam_axi_per_path_bw_vote axi_path[CAM_CRE_MAX_PER_PATH_VOTES];
uint32_t hw_type;
struct cam_req_mgr_timer *watch_dog;
uint32_t watch_dog_reset_counter;
};
/**
* struct cre_cmd_work_data
*
* @type: Type of work data
* @data: Private data
* @req_id: Request Idx
*/
struct cre_cmd_work_data {
uint32_t type;
void *data;
int64_t req_idx;
};
/**
* struct cre_msg_work_data
*
* @type: Type of work data
* @data: Private data
* @irq_status: IRQ status
*/
struct cre_msg_work_data {
uint32_t type;
void *data;
struct cam_cre_irq_data irq_data;
};
/**
* struct cre_clk_work_data
*
* @type: Type of work data
* @data: Private data
*/
struct cre_clk_work_data {
uint32_t type;
void *data;
};
/**
* struct cre_debug_buffer
*
* @cpu_addr: CPU address
* @iova_addr: IOVA address
* @len: Buffer length
* @size: Buffer Size
* @offset: buffer offset
*/
struct cre_debug_buffer {
uintptr_t cpu_addr;
dma_addr_t iova_addr;
size_t len;
uint32_t size;
uint32_t offset;
};
struct plane_info {
uintptr_t cpu_addr;
dma_addr_t iova_addr;
uint32_t width;
uint32_t height;
uint32_t stride;
uint32_t format;
uint32_t alignment;
uint32_t offset;
uint32_t x_init;
size_t len;
};
/**
* struct cre_io_buf
*
* @direction: Direction of a buffer
* @resource_type: Resource type of IO Buffer
* @format: Format
* @fence: Fence
* @num_planes: Number of planes
*/
struct cre_io_buf {
uint32_t direction;
uint32_t resource_type;
uint32_t format;
uint32_t fence;
uint32_t num_planes;
struct plane_info p_info[CAM_CRE_MAX_PLANES];
};
struct cre_reg_set {
uint32_t offset;
uint32_t value;
};
struct cre_reg_buffer {
uint32_t num_rd_reg_set;
uint32_t num_wr_reg_set;
struct cre_reg_set rd_reg_set[CAM_CRE_MAX_REG_SET];
struct cre_reg_set wr_reg_set[CAM_CRE_MAX_REG_SET];
};
/**
* struct cam_cre_clk_bw_request
* @budget_ns: Time required to process frame
* @frame_cycles: Frame cycles needed to process the frame
* @rt_flag: Flag to indicate real time stream
* @uncompressed_bw: Bandwidth required to process frame
* @compressed_bw: Compressed bandwidth to process frame
*/
struct cam_cre_clk_bw_request {
uint64_t budget_ns;
uint32_t frame_cycles;
uint32_t rt_flag;
uint64_t uncompressed_bw;
uint64_t compressed_bw;
};
/**
* struct cam_cre_clk_bw_req_internal_v2
* @budget_ns: Time required to process frame
* @frame_cycles: Frame cycles needed to process the frame
* @rt_flag: Flag to indicate real time stream
* @reserved: Reserved for future use
* @num_paths: Number of paths for per path bw vote
* @axi_path: Per path vote info for CRE
*/
struct cam_cre_clk_bw_req_internal_v2 {
uint64_t budget_ns;
uint32_t frame_cycles;
uint32_t rt_flag;
uint32_t reserved;
uint32_t num_paths;
struct cam_axi_per_path_bw_vote axi_path[CAM_CRE_MAX_PER_PATH_VOTES];
};
/**
* struct cam_cre_request
*
* @request_id: Request Id
* @req_idx: Index in request list
* @state: Request state
* @num_batch: Number of batches
* @num_frame_bufs: Number of frame buffers
* @num_pass_bufs: Number of pass Buffers
* @num_io_bufs: Number of IO Buffers
* @in_resource: Input resource
* @cre_debug_buf: Debug buffer
* @io_buf: IO config info of a request
* @clk_info: Clock Info V1
* @clk_info_v2: Clock Info V2
* @hang_data: Debug data for HW error
* @submit_timestamp: Submit timestamp to hw
*/
struct cam_cre_request {
uint64_t request_id;
uint32_t req_idx;
uint32_t state;
uint32_t num_batch;
uint32_t num_frame_bufs;
uint32_t num_pass_bufs;
uint32_t num_io_bufs[CRE_MAX_BATCH_SIZE];
uint32_t in_resource;
struct cre_reg_buffer cre_reg_buf;
struct cre_debug_buffer cre_debug_buf;
struct cre_io_buf *io_buf[CRE_MAX_BATCH_SIZE][CRE_MAX_IO_BUFS];
struct cam_cre_clk_bw_request clk_info;
struct cam_cre_clk_bw_req_internal_v2 clk_info_v2;
struct cam_hw_mgr_dump_pf_data hang_data;
ktime_t submit_timestamp;
};
/**
* struct cam_cre_ctx
*
* @context_priv: Private data of context
* @bitmap: Context bit map
* @bitmap_size: Context bit map size
* @bits: Context bit map bits
* @ctx_id: Context ID
* @ctx_state: State of a context
* @req_cnt: Requests count
* @ctx_mutex: Mutex for context
* @acquire_dev_cmd: Cam acquire command
* @cre_acquire: CRE acquire command
* @ctxt_event_cb: Callback of a context
* @req_list: Request List
* @last_req_time: Timestamp of last request
* @req_watch_dog: Watchdog for requests
* @req_watch_dog_reset_counter: Request reset counter
* @clk_info: CRE Ctx clock info
* @clk_watch_dog: Clock watchdog
* @clk_watch_dog_reset_counter: Reset counter
* @last_flush_req: last flush req for this ctx
*/
struct cam_cre_ctx {
void *context_priv;
size_t bitmap_size;
void *bitmap;
size_t bits;
uint32_t ctx_id;
uint32_t ctx_state;
uint32_t req_cnt;
struct mutex ctx_mutex;
struct cam_acquire_dev_cmd acquire_dev_cmd;
struct cam_cre_acquire_dev_info cre_acquire;
cam_hw_event_cb_func ctxt_event_cb;
struct cam_cre_request *req_list[CAM_CTX_REQ_MAX];
struct cam_cre_request *active_req;
uint64_t last_req_time;
struct cam_req_mgr_timer *req_watch_dog;
uint32_t req_watch_dog_reset_counter;
struct cam_ctx_clk_info clk_info;
struct cam_req_mgr_timer *clk_watch_dog;
struct cre_top *cre_top;
uint32_t clk_watch_dog_reset_counter;
uint64_t last_flush_req;
};
/**
* struct cam_cre_hw_mgr
*
* @cren_cnt: CRE device cren count
* @cre_ctx_cnt: Open context count
* @hw_mgr_mutex: Mutex for HW manager
* @hw_mgr_lock: Spinlock for HW manager
* @iommu_hdl: CRE Handle
* @iommu_sec_hdl: CRE Handle for secure
* @num_cre: Number of CRE
* @secure_mode: Mode of CRE creration
* @ctx_bitmap: Context bit map
* @ctx_bitmap_size: Context bit map size
* @ctx_bits: Context bit map bits
* @ctx: CRE context
* @devices: CRE devices
* @cre_caps: CRE capabilities
* @cmd_work: Command work
* @msg_work: Message work
* @timer_work: Timer work
* @cmd_work_data: Command work data
* @msg_work_data: Message work data
* @timer_work_data: Timer work data
* @cre_dev_intf: CRE device interface
* @clk_info: CRE clock Info for HW manager
* @dentry: Pointer to CRE debugfs directory
* @frame_dump_enable: CRE frame setting dump enablement
* @dump_req_data_enable: CRE hang dump enablement
*/
struct cam_cre_hw_mgr {
int32_t cren_cnt;
uint32_t cre_ctx_cnt;
struct mutex hw_mgr_mutex;
spinlock_t hw_mgr_lock;
int32_t iommu_hdl;
int32_t iommu_sec_hdl;
uint32_t num_cre;
bool secure_mode;
void *ctx_bitmap;
size_t ctx_bitmap_size;
size_t ctx_bits;
struct cam_cre_ctx ctx[CRE_CTX_MAX];
struct cam_hw_intf **devices[CRE_DEV_MAX];
struct cam_cre_query_cap_cmd cre_caps;
struct cam_req_mgr_core_workq *cmd_work;
struct cam_req_mgr_core_workq *msg_work;
struct cam_req_mgr_core_workq *timer_work;
struct cre_cmd_work_data *cmd_work_data;
struct cre_msg_work_data *msg_work_data;
struct cre_clk_work_data *timer_work_data;
struct cam_hw_intf *cre_dev_intf[CRE_DEV_MAX];
struct cam_soc_reg_map *reg_map[CRE_DEV_MAX][CRE_BASE_MAX];
struct cam_cre_clk_info clk_info;
struct dentry *dentry;
bool frame_dump_enable;
bool dump_req_data_enable;
};
/**
* struct cam_cre_hw_ctx_data
*
* @context_priv: Context private data, cam_context from
* acquire.
* @ctx_mutex: Mutex for context
* @cre_dev_acquire_info: Acquire device info
* @ctxt_event_cb: Context callback function
* @in_use: Flag for context usage
* @wait_complete: Completion info
* @last_flush_req: req id which was flushed last.
*/
struct cam_cre_hw_ctx_data {
void *context_priv;
struct mutex ctx_mutex;
struct cam_cre_acquire_dev_info cre_dev_acquire_info;
cam_hw_event_cb_func ctxt_event_cb;
bool in_use;
struct completion wait_complete;
uint64_t last_flush_req;
};
#endif /* CAM_CRE_HW_MGR_H */

View File

@@ -0,0 +1,619 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#include <linux/of.h>
#include <linux/debugfs.h>
#include <linux/videodev2.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/iopoll.h>
#include <media/cam_cre.h>
#include "cam_io_util.h"
#include "cam_hw.h"
#include "cam_hw_intf.h"
#include "cre_core.h"
#include "cre_soc.h"
#include "cam_soc_util.h"
#include "cam_io_util.h"
#include "cam_cpas_api.h"
#include "cam_debug_util.h"
#include "cre_hw.h"
#include "cre_dev_intf.h"
#include "cre_bus_rd.h"
static struct cre_bus_rd *bus_rd;
#define update_cre_reg_set(cre_reg_buf, off, val) \
do { \
cre_reg_buf->rd_reg_set[cre_reg_buf->num_rd_reg_set].offset = (off); \
cre_reg_buf->rd_reg_set[cre_reg_buf->num_rd_reg_set].value = (val); \
cre_reg_buf->num_rd_reg_set++; \
} while (0)
static int cam_cre_bus_rd_release(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, void *data)
{
if (ctx_id < 0 || ctx_id >= CRE_CTX_MAX) {
CAM_ERR(CAM_CRE, "Invalid data: %d", ctx_id);
return -EINVAL;
}
vfree(bus_rd->bus_rd_ctx[ctx_id]);
bus_rd->bus_rd_ctx[ctx_id] = NULL;
return 0;
}
static int cam_cre_bus_is_rm_enabled(
struct cam_cre_request *cre_request,
uint32_t batch_idx,
uint32_t rm_id)
{
int i, k;
struct cre_io_buf *io_buf;
struct cre_bus_in_port_to_rm *in_port_to_rm;
if (batch_idx >= CRE_MAX_BATCH_SIZE) {
CAM_ERR(CAM_CRE, "Invalid batch idx: %d", batch_idx);
return -EINVAL;
}
for (i = 0; i < cre_request->num_io_bufs[batch_idx]; i++) {
io_buf = cre_request->io_buf[batch_idx][i];
if (io_buf->direction != CAM_BUF_INPUT)
continue;
in_port_to_rm =
&bus_rd->in_port_to_rm[io_buf->resource_type - 1];
for (k = 0; k < io_buf->num_planes; k++) {
if (rm_id ==
in_port_to_rm->rm_port_id[k])
return true;
}
}
return false;
}
static uint32_t *cam_cre_bus_rd_update(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, struct cre_reg_buffer *cre_reg_buf, int batch_idx,
int io_idx, struct cam_cre_dev_prepare_req *prepare)
{
int k;
uint32_t req_idx, temp;
uint32_t rm_id;
uint32_t rsc_type;
struct cam_hw_prepare_update_args *prepare_args;
struct cam_cre_ctx *ctx_data;
struct cam_cre_request *cre_request;
struct cre_io_buf *io_buf;
struct cre_bus_rd_ctx *bus_rd_ctx;
struct cam_cre_bus_rd_reg *rd_reg;
struct cam_cre_bus_rd_client_reg *rd_reg_client;
struct cam_cre_bus_rd_reg_val *rd_reg_val;
struct cam_cre_bus_rd_client_reg_val *rd_res_val_client;
struct cre_bus_in_port_to_rm *in_port_to_rm;
struct cre_bus_rd_io_port_info *io_port_info;
if (ctx_id < 0 || !prepare) {
CAM_ERR(CAM_CRE, "Invalid data: %d %x", ctx_id, prepare);
return NULL;
}
if (batch_idx >= CRE_MAX_BATCH_SIZE) {
CAM_ERR(CAM_CRE, "Invalid batch idx: %d", batch_idx);
return NULL;
}
if (io_idx >= CRE_MAX_IO_BUFS) {
CAM_ERR(CAM_CRE, "Invalid IO idx: %d", io_idx);
return NULL;
}
prepare_args = prepare->prepare_args;
ctx_data = prepare->ctx_data;
req_idx = prepare->req_idx;
cre_request = ctx_data->req_list[req_idx];
CAM_DBG(CAM_CRE, "req_idx = %d req_id = %lld",
req_idx, cre_request->request_id);
bus_rd_ctx = bus_rd->bus_rd_ctx[ctx_id];
io_port_info = &bus_rd_ctx->io_port_info;
rd_reg = cam_cre_hw_info->bus_rd_reg_offset;
rd_reg_val = cam_cre_hw_info->bus_rd_reg_val;
io_buf = cre_request->io_buf[batch_idx][io_idx];
CAM_DBG(CAM_CRE,
"req_idx = %d req_id = %lld rsc %d",
req_idx, cre_request->request_id,
io_buf->resource_type);
CAM_DBG(CAM_CRE, "batch:%d iobuf:%d direction:%d",
batch_idx, io_idx, io_buf->direction);
in_port_to_rm =
&bus_rd->in_port_to_rm[io_buf->resource_type - 1];
for (k = 0; k < io_buf->num_planes; k++) {
rsc_type = io_buf->resource_type - 1;
/* frame level info */
rm_id = in_port_to_rm->rm_port_id[k];
rd_reg_client = &rd_reg->rd_clients[rm_id];
rd_res_val_client = &rd_reg_val->rd_clients[rm_id];
/* security cfg */
update_cre_reg_set(cre_reg_buf,
rd_reg->offset + rd_reg->security_cfg,
ctx_data->cre_acquire.secure_mode & 0x1);
/* enable client */
update_cre_reg_set(cre_reg_buf,
rd_reg->offset + rd_reg_client->core_cfg,
1);
/* ccif meta data */
temp = 0;
update_cre_reg_set(cre_reg_buf,
(rd_reg->offset + rd_reg_client->ccif_meta_data),
temp);
/* Address of the Image */
update_cre_reg_set(cre_reg_buf,
rd_reg->offset + rd_reg_client->img_addr,
io_buf->p_info[k].iova_addr);
/* Buffer size */
update_cre_reg_set(cre_reg_buf,
rd_reg->offset + rd_reg_client->rd_width,
io_buf->p_info[k].width);
update_cre_reg_set(cre_reg_buf,
rd_reg->offset + rd_reg_client->rd_height,
io_buf->p_info[k].height);
/* stride */
update_cre_reg_set(cre_reg_buf,
rd_reg->offset + rd_reg_client->rd_stride,
io_buf->p_info[k].stride);
/* unpacker cfg : Mode and alignment */
temp = 0;
temp |= (io_buf->p_info[k].format &
rd_res_val_client->mode_mask) <<
rd_res_val_client->mode_shift;
temp |= (io_buf->p_info[k].alignment &
rd_res_val_client->alignment_mask) <<
rd_res_val_client->alignment_shift;
update_cre_reg_set(cre_reg_buf,
rd_reg->offset + rd_reg_client->unpacker_cfg,
temp);
/* latency buffer allocation */
update_cre_reg_set(cre_reg_buf,
rd_reg->offset + rd_reg_client->latency_buf_allocation,
io_port_info->latency_buf_size);
/* Enable Debug cfg */
temp = 0xFFFF;
update_cre_reg_set(cre_reg_buf,
rd_reg->offset + rd_reg_client->debug_status_cfg,
temp);
}
return (uint32_t *)cre_reg_buf;
}
static uint32_t *cam_cre_bus_rm_disable(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, struct cam_cre_dev_prepare_req *prepare,
int batch_idx, int rm_idx,
struct cre_reg_buffer *cre_reg_buf)
{
uint32_t req_idx;
struct cam_cre_ctx *ctx_data;
struct cre_bus_rd_ctx *bus_rd_ctx;
struct cam_cre_bus_rd_reg *rd_reg;
struct cam_cre_bus_rd_client_reg *rd_reg_client;
if (ctx_id < 0 || !prepare) {
CAM_ERR(CAM_CRE, "Invalid data: %d %x", ctx_id, prepare);
return NULL;
}
if (batch_idx >= CRE_MAX_BATCH_SIZE) {
CAM_ERR(CAM_CRE, "Invalid batch idx: %d", batch_idx);
return NULL;
}
if (rm_idx >= CAM_CRE_INPUT_IMAGES_MAX) {
CAM_ERR(CAM_CRE, "Invalid read client: %d", rm_idx);
return NULL;
}
ctx_data = prepare->ctx_data;
req_idx = prepare->req_idx;
bus_rd_ctx = bus_rd->bus_rd_ctx[ctx_id];
rd_reg = cam_cre_hw_info->bus_rd_reg_offset;
rd_reg_client = &rd_reg->rd_clients[rm_idx];
/* Core cfg: enable, Mode */
update_cre_reg_set(cre_reg_buf,
rd_reg->offset + rd_reg_client->core_cfg,
0);
return (uint32_t *)cre_reg_buf;
}
static int cam_cre_bus_rd_prepare(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, void *data)
{
int rc = 0;
int i, j;
uint32_t req_idx;
int is_rm_enabled;
struct cam_cre_dev_prepare_req *prepare;
struct cam_cre_ctx *ctx_data;
struct cam_cre_request *cre_request;
struct cre_io_buf *io_buf;
struct cre_bus_rd_ctx *bus_rd_ctx;
struct cam_cre_bus_rd_reg *rd_reg;
struct cam_cre_bus_rd_reg_val *rd_reg_val;
struct cre_reg_buffer *cre_reg_buf;
uint32_t *ret;
int temp;
if (ctx_id < 0 || !data) {
CAM_ERR(CAM_CRE, "Invalid data: %d %x", ctx_id, data);
return -EINVAL;
}
prepare = data;
ctx_data = prepare->ctx_data;
req_idx = prepare->req_idx;
cre_request = ctx_data->req_list[req_idx];
cre_reg_buf = &cre_request->cre_reg_buf;
CAM_DBG(CAM_CRE, "req_idx = %d req_id = %lld",
req_idx, cre_request->request_id);
bus_rd_ctx = bus_rd->bus_rd_ctx[ctx_id];
rd_reg = cam_cre_hw_info->bus_rd_reg_offset;
rd_reg_val = cam_cre_hw_info->bus_rd_reg_val;
for (i = 0; i < cre_request->num_batch; i++) {
for (j = 0; j < cre_request->num_io_bufs[i]; j++) {
io_buf = cre_request->io_buf[i][j];
if (io_buf->direction != CAM_BUF_INPUT)
continue;
CAM_DBG(CAM_CRE, "batch:%d iobuf:%d direction:%d",
i, j, io_buf->direction);
ret = cam_cre_bus_rd_update(cam_cre_hw_info,
ctx_id, cre_reg_buf, i, j, prepare);
if (!ret) {
rc = -EINVAL;
goto end;
}
}
}
/* Disable RMs which are not enabled */
for (i = 0; i < cre_request->num_batch; i++) {
for (j = 0; j < rd_reg_val->num_clients; j++) {
is_rm_enabled = cam_cre_bus_is_rm_enabled(
cre_request, i, j);
if (is_rm_enabled < 0) {
rc = -EINVAL;
goto end;
}
if (is_rm_enabled)
continue;
ret = cam_cre_bus_rm_disable(cam_cre_hw_info,
ctx_id, prepare, i, j,
cre_reg_buf);
if (!ret) {
rc = -EINVAL;
goto end;
}
}
}
/* Go command */
temp = 0;
temp |= rd_reg_val->go_cmd;
temp |= rd_reg_val->static_prg & rd_reg_val->static_prg_mask;
update_cre_reg_set(cre_reg_buf,
rd_reg->offset + rd_reg->input_if_cmd,
temp);
end:
return 0;
}
static int cam_cre_bus_rd_in_port_idx(uint32_t input_port_id)
{
int i;
for (i = 0; i < CRE_MAX_IN_RES; i++)
if (bus_rd->in_port_to_rm[i].input_port_id ==
input_port_id)
return i;
return -EINVAL;
}
static int cam_cre_bus_rd_acquire(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, void *data)
{
int rc = 0, i;
struct cam_cre_acquire_dev_info *in_acquire;
struct cre_bus_rd_ctx *bus_rd_ctx;
struct cre_bus_in_port_to_rm *in_port_to_rm;
struct cam_cre_bus_rd_reg_val *bus_rd_reg_val;
int in_port_idx;
if (ctx_id < 0 || !data || !cam_cre_hw_info || ctx_id >= CRE_CTX_MAX) {
CAM_ERR(CAM_CRE, "Invalid data: %d %x %x",
ctx_id, data, cam_cre_hw_info);
return -EINVAL;
}
bus_rd->bus_rd_ctx[ctx_id] = vzalloc(sizeof(struct cre_bus_rd_ctx));
if (!bus_rd->bus_rd_ctx[ctx_id]) {
CAM_ERR(CAM_CRE, "Out of memory");
return -ENOMEM;
}
bus_rd->bus_rd_ctx[ctx_id]->cre_acquire = data;
in_acquire = data;
bus_rd_ctx = bus_rd->bus_rd_ctx[ctx_id];
bus_rd_ctx->num_in_ports = in_acquire->num_in_res;
bus_rd_ctx->security_flag = in_acquire->secure_mode;
bus_rd_reg_val = cam_cre_hw_info->bus_rd_reg_val;
for (i = 0; i < in_acquire->num_in_res; i++) {
if (!in_acquire->in_res[i].width)
continue;
CAM_DBG(CAM_CRE, "i = %d format = %u width = %x height = %x",
i, in_acquire->in_res[i].format,
in_acquire->in_res[i].width,
in_acquire->in_res[i].height);
in_port_idx = cam_cre_bus_rd_in_port_idx(i + 1);
if (in_port_idx < 0) {
CAM_ERR(CAM_CRE, "Invalid in_port_idx: %d", i + 1);
rc = -EINVAL;
goto end;
}
in_port_to_rm = &bus_rd->in_port_to_rm[in_port_idx];
if (!in_port_to_rm->num_rm) {
CAM_ERR(CAM_CRE, "Invalid format for Input port");
rc = -EINVAL;
goto end;
}
bus_rd_ctx->io_port_info.input_port_id[i] =
in_acquire->in_res[i].res_id;
bus_rd_ctx->io_port_info.input_format_type[i] =
in_acquire->in_res[i].format;
CAM_DBG(CAM_CRE, "i:%d port_id = %u format %u",
i, bus_rd_ctx->io_port_info.input_port_id[i],
bus_rd_ctx->io_port_info.input_format_type[i]);
}
end:
return rc;
}
static int cam_cre_bus_rd_reg_set_update(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, void *data)
{
int i;
uint32_t num_reg_set;
struct cre_reg_set *rd_reg_set;
struct cam_cre_dev_reg_set_update *reg_set_upd_cmd =
(struct cam_cre_dev_reg_set_update *)data;
num_reg_set = reg_set_upd_cmd->cre_reg_buf.num_rd_reg_set;
rd_reg_set = reg_set_upd_cmd->cre_reg_buf.rd_reg_set;
for (i = 0; i < num_reg_set; i++) {
cam_io_w_mb(rd_reg_set[i].value,
cam_cre_hw_info->bus_rd_reg_offset->base + rd_reg_set[i].offset);
}
return 0;
}
static int cam_cre_bus_rd_init(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, void *data)
{
struct cam_cre_bus_rd_reg_val *bus_rd_reg_val;
struct cam_cre_bus_rd_reg *bus_rd_reg;
struct cam_cre_dev_init *dev_init = data;
if (!cam_cre_hw_info) {
CAM_ERR(CAM_CRE, "Invalid cam_cre_hw_info");
return -EINVAL;
}
bus_rd_reg_val = cam_cre_hw_info->bus_rd_reg_val;
bus_rd_reg = cam_cre_hw_info->bus_rd_reg_offset;
bus_rd_reg->base =
dev_init->core_info->cre_hw_info->cre_hw->bus_rd_reg_offset->base;
/* enable interrupt mask */
cam_io_w_mb(bus_rd_reg_val->irq_mask,
cam_cre_hw_info->bus_rd_reg_offset->base + bus_rd_reg->irq_mask);
return 0;
}
static int cam_cre_bus_rd_probe(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, void *data)
{
int i, k, rm_idx;
struct cam_cre_bus_rd_reg_val *bus_rd_reg_val;
struct cam_cre_bus_rd_reg *bus_rd_reg;
struct cre_bus_in_port_to_rm *in_port_to_rm;
uint32_t input_port_idx;
if (!cam_cre_hw_info) {
CAM_ERR(CAM_CRE, "Invalid cam_cre_hw_info");
return -EINVAL;
}
bus_rd = kzalloc(sizeof(struct cre_bus_rd), GFP_KERNEL);
if (!bus_rd) {
CAM_ERR(CAM_CRE, "Out of memory");
return -ENOMEM;
}
bus_rd->cre_hw_info = cam_cre_hw_info;
bus_rd_reg_val = cam_cre_hw_info->bus_rd_reg_val;
bus_rd_reg = cam_cre_hw_info->bus_rd_reg_offset;
for (i = 0; i < bus_rd_reg_val->num_clients; i++) {
input_port_idx =
bus_rd_reg_val->rd_clients[i].input_port_id - 1;
in_port_to_rm = &bus_rd->in_port_to_rm[input_port_idx];
rm_idx = in_port_to_rm->num_rm;
in_port_to_rm->input_port_id =
bus_rd_reg_val->rd_clients[i].input_port_id;
in_port_to_rm->rm_port_id[rm_idx] =
bus_rd_reg_val->rd_clients[i].rm_port_id;
in_port_to_rm->num_rm++;
}
for (i = 0; i < CRE_MAX_IN_RES; i++) {
in_port_to_rm = &bus_rd->in_port_to_rm[i];
CAM_DBG(CAM_CRE, "input port id = %d",
in_port_to_rm->input_port_id);
CAM_DBG(CAM_CRE, "num_rms = %d",
in_port_to_rm->num_rm);
for (k = 0; k < in_port_to_rm->num_rm; k++) {
CAM_DBG(CAM_CRE, "rm port id = %d",
in_port_to_rm->rm_port_id[k]);
}
}
return 0;
}
static int cam_cre_bus_rd_isr(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, void *data)
{
uint32_t irq_status;
uint32_t violation_status;
uint32_t debug_status_0;
uint32_t debug_status_1;
struct cam_cre_bus_rd_reg *bus_rd_reg;
struct cam_cre_bus_rd_reg_val *bus_rd_reg_val;
struct cam_cre_irq_data *irq_data = data;
if (!cam_cre_hw_info) {
CAM_ERR(CAM_CRE, "Invalid cam_cre_hw_info");
return -EINVAL;
}
CAM_DBG(CAM_CRE, "error 0x%x", irq_data->error);
bus_rd_reg = cam_cre_hw_info->bus_rd_reg_offset;
bus_rd_reg_val = cam_cre_hw_info->bus_rd_reg_val;
/* Read and Clear Top Interrupt status */
irq_status = cam_io_r_mb(bus_rd_reg->base + bus_rd_reg->irq_status);
cam_io_w_mb(irq_status,
bus_rd_reg->base + bus_rd_reg->irq_clear);
cam_io_w_mb(bus_rd_reg_val->irq_cmd_clear,
bus_rd_reg->base + bus_rd_reg->irq_cmd);
if (irq_status & bus_rd_reg_val->rup_done)
CAM_DBG(CAM_CRE, "CRE Read Bus RUP done");
if (irq_status & bus_rd_reg_val->rd_buf_done)
CAM_DBG(CAM_CRE, "CRE Read Bus Buff done");
if (irq_status & bus_rd_reg_val->cons_violation) {
irq_data->error = 1;
violation_status = cam_io_r_mb(bus_rd_reg->base +
bus_rd_reg->rd_clients[0].cons_violation_status);
debug_status_0 = cam_io_r_mb(bus_rd_reg->base +
bus_rd_reg->rd_clients[0].debug_status_0);
debug_status_1 = cam_io_r_mb(bus_rd_reg->base +
bus_rd_reg->rd_clients[0].debug_status_1);
CAM_DBG(CAM_CRE, "CRE Read Bus Violation");
CAM_DBG(CAM_CRE,
"violation status 0x%x debug status 0/1 0x%x/0x%x",
violation_status, debug_status_0, debug_status_1);
}
return 0;
}
int cam_cre_bus_rd_process(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, uint32_t cmd_id, void *data)
{
int rc = -EINVAL;
switch (cmd_id) {
case CRE_HW_PROBE:
CAM_DBG(CAM_CRE, "CRE_HW_PROBE: E");
rc = cam_cre_bus_rd_probe(cam_cre_hw_info, ctx_id, data);
CAM_DBG(CAM_CRE, "CRE_HW_PROBE: X");
break;
case CRE_HW_INIT:
CAM_DBG(CAM_CRE, "CRE_HW_INIT: E");
rc = cam_cre_bus_rd_init(cam_cre_hw_info, ctx_id, data);
CAM_DBG(CAM_CRE, "CRE_HW_INIT: X");
break;
case CRE_HW_ACQUIRE:
CAM_DBG(CAM_CRE, "CRE_HW_ACQUIRE: E");
rc = cam_cre_bus_rd_acquire(cam_cre_hw_info, ctx_id, data);
CAM_DBG(CAM_CRE, "CRE_HW_ACQUIRE: X");
break;
case CRE_HW_RELEASE:
CAM_DBG(CAM_CRE, "CRE_HW_RELEASE: E");
rc = cam_cre_bus_rd_release(cam_cre_hw_info, ctx_id, data);
CAM_DBG(CAM_CRE, "CRE_HW_RELEASE: X");
break;
case CRE_HW_PREPARE:
CAM_DBG(CAM_CRE, "CRE_HW_PREPARE: E");
rc = cam_cre_bus_rd_prepare(cam_cre_hw_info, ctx_id, data);
CAM_DBG(CAM_CRE, "CRE_HW_PREPARE: X");
break;
case CRE_HW_ISR:
rc = cam_cre_bus_rd_isr(cam_cre_hw_info, 0, data);
break;
case CRE_HW_REG_SET_UPDATE:
rc = cam_cre_bus_rd_reg_set_update(cam_cre_hw_info, 0, data);
break;
case CRE_HW_DEINIT:
case CRE_HW_START:
case CRE_HW_STOP:
case CRE_HW_FLUSH:
case CRE_HW_CLK_UPDATE:
case CRE_HW_BW_UPDATE:
case CRE_HW_RESET:
case CRE_HW_SET_IRQ_CB:
rc = 0;
CAM_DBG(CAM_CRE, "Unhandled cmds: %d", cmd_id);
break;
default:
CAM_ERR(CAM_CRE, "Unsupported cmd: %d", cmd_id);
break;
}
return rc;
}

View File

@@ -0,0 +1,98 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef CRE_BUS_RD_H
#define CRE_BUS_RD_H
#include <linux/types.h>
#include <linux/completion.h>
#include <media/cam_cre.h>
#include "cre_hw.h"
#include "cam_hw_mgr_intf.h"
#include "cam_hw_intf.h"
#include "cam_soc_util.h"
#include "cam_cre_hw_mgr.h"
/**
* struct cre_bus_rd_io_port_info
*
* @pixel_pattern: Pixel pattern
* @input_port_id: Port Id
* @input_format_type: Format type
* @latency_buf_size: Latency buffer size
*/
struct cre_bus_rd_io_port_info {
uint32_t pixel_pattern[CRE_MAX_IN_RES];
uint32_t input_port_id[CRE_MAX_IN_RES];
uint32_t input_format_type[CRE_MAX_IN_RES];
uint32_t latency_buf_size;
};
/**
* struct cre_bus_rd_io_port_batch
*
* num_batch: Number of batches
* io_port: CDM IO Port Info
*/
struct cre_bus_rd_io_port_batch {
uint32_t num_batch;
struct cre_bus_rd_io_port_info io_port[CRE_MAX_BATCH_SIZE];
};
/**
* struct cre_bus_rd_rm
*
* @rm_port_id: RM port ID
* @format_type: Format type
*/
struct cre_bus_rd_rm {
uint32_t rm_port_id;
uint32_t format_type;
};
/**
* struct cre_bus_rd_ctx
*
* @cre_acquire: CRE acquire structure
* @security_flag: security flag
* @num_in_ports: Number of in ports
* @io_port_info: IO port info
* @io_port_batch: IO port info
*/
struct cre_bus_rd_ctx {
struct cam_cre_acquire_dev_info *cre_acquire;
bool security_flag;
uint32_t num_in_ports;
struct cre_bus_rd_io_port_info io_port_info;
struct cre_bus_rd_io_port_batch io_port_batch;
};
/**
* struct cre_bus_in_port_to_rm
*
* @input_port_id: Intput port ID
* @num_rm: Number of RMs
* @rm_port_id: RM port Id
*/
struct cre_bus_in_port_to_rm {
uint32_t input_port_id;
uint32_t num_rm;
uint32_t rm_port_id[CRE_MAX_IN_RES];
};
/**
* struct cre_bus_rd
*
* @cre_hw_info: CRE hardware info
* @in_port_to_rm: IO port to RM mapping
* @bus_rd_ctx: RM context
*/
struct cre_bus_rd {
struct cam_cre_hw *cre_hw_info;
struct cre_bus_in_port_to_rm in_port_to_rm[CRE_MAX_IN_RES];
struct cre_bus_rd_ctx *bus_rd_ctx[CRE_CTX_MAX];
struct completion reset_complete;
};
#endif /* CRE_BUS_RD_H */

View File

@@ -0,0 +1,613 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#include <linux/of.h>
#include <linux/debugfs.h>
#include <linux/videodev2.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/iopoll.h>
#include <media/cam_cre.h>
#include "cam_io_util.h"
#include "cam_hw.h"
#include "cam_hw_intf.h"
#include "cre_core.h"
#include "cre_soc.h"
#include "cam_soc_util.h"
#include "cam_io_util.h"
#include "cam_cpas_api.h"
#include "cam_debug_util.h"
#include "cre_hw.h"
#include "cre_dev_intf.h"
#include "cre_bus_wr.h"
static struct cre_bus_wr *wr_info;
#define update_cre_reg_set(cre_reg_buf, off, val) \
do { \
cre_reg_buf->wr_reg_set[cre_reg_buf->num_wr_reg_set].offset = (off); \
cre_reg_buf->wr_reg_set[cre_reg_buf->num_wr_reg_set].value = (val); \
cre_reg_buf->num_wr_reg_set++; \
} while (0)
static int cam_cre_bus_en_port_idx(
struct cam_cre_request *cre_request,
uint32_t batch_idx,
uint32_t output_port_id)
{
int i;
struct cre_io_buf *io_buf;
if (batch_idx >= CRE_MAX_BATCH_SIZE) {
CAM_ERR(CAM_CRE, "Invalid batch idx: %d", batch_idx);
return -EINVAL;
}
for (i = 0; i < cre_request->num_io_bufs[batch_idx]; i++) {
io_buf = cre_request->io_buf[batch_idx][i];
if (io_buf->direction != CAM_BUF_OUTPUT)
continue;
if (io_buf->resource_type == output_port_id)
return i;
}
return -EINVAL;
}
static int cam_cre_bus_wr_out_port_idx(uint32_t output_port_id)
{
int i;
for (i = 0; i < CRE_MAX_OUT_RES; i++)
if (wr_info->out_port_to_wm[i].output_port_id == output_port_id)
return i;
return -EINVAL;
}
static int cam_cre_bus_wr_reg_set_update(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, void *data)
{
int i;
uint32_t num_reg_set;
struct cre_reg_set *wr_reg_set;
struct cam_cre_dev_reg_set_update *reg_set_upd_cmd =
(struct cam_cre_dev_reg_set_update *)data;
num_reg_set = reg_set_upd_cmd->cre_reg_buf.num_wr_reg_set;
wr_reg_set = reg_set_upd_cmd->cre_reg_buf.wr_reg_set;
for (i = 0; i < num_reg_set; i++) {
cam_io_w_mb(wr_reg_set[i].value,
cam_cre_hw_info->bus_wr_reg_offset->base + wr_reg_set[i].offset);
}
return 0;
}
static int cam_cre_bus_wr_release(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, void *data)
{
if (ctx_id < 0 || ctx_id >= CRE_CTX_MAX) {
CAM_ERR(CAM_CRE, "Invalid data: %d", ctx_id);
return -EINVAL;
}
vfree(wr_info->bus_wr_ctx[ctx_id]);
wr_info->bus_wr_ctx[ctx_id] = NULL;
return 0;
}
static uint32_t *cam_cre_bus_wr_update(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, struct cam_cre_dev_prepare_req *prepare,
int batch_idx, int io_idx,
struct cre_reg_buffer *cre_reg_buf)
{
int k, out_port_idx;
uint32_t num_wm_ports;
uint32_t comb_idx = 0;
uint32_t req_idx;
uint32_t temp = 0;
uint32_t wm_port_id;
struct cam_hw_prepare_update_args *prepare_args;
struct cam_cre_ctx *ctx_data;
struct cam_cre_request *cre_request;
struct cre_io_buf *io_buf;
struct cre_bus_wr_ctx *bus_wr_ctx;
struct cam_cre_bus_wr_reg *wr_reg;
struct cam_cre_bus_wr_client_reg *wr_reg_client;
struct cam_cre_bus_wr_reg_val *wr_reg_val;
struct cam_cre_bus_wr_client_reg_val *wr_res_val_client;
struct cre_bus_out_port_to_wm *out_port_to_wm;
if (ctx_id < 0 || !prepare) {
CAM_ERR(CAM_CRE, "Invalid data: %d %x", ctx_id, prepare);
return NULL;
}
if (batch_idx >= CRE_MAX_BATCH_SIZE) {
CAM_ERR(CAM_CRE, "Invalid batch idx: %d", batch_idx);
return NULL;
}
if (io_idx >= CRE_MAX_IO_BUFS) {
CAM_ERR(CAM_CRE, "Invalid IO idx: %d", io_idx);
return NULL;
}
prepare_args = prepare->prepare_args;
ctx_data = prepare->ctx_data;
req_idx = prepare->req_idx;
cre_request = ctx_data->req_list[req_idx];
bus_wr_ctx = wr_info->bus_wr_ctx[ctx_id];
wr_reg = cam_cre_hw_info->bus_wr_reg_offset;
wr_reg_val = cam_cre_hw_info->bus_wr_reg_val;
CAM_DBG(CAM_CRE, "req_idx = %d req_id = %lld offset = %d",
req_idx, cre_request->request_id);
io_buf = cre_request->io_buf[batch_idx][io_idx];
CAM_DBG(CAM_CRE, "batch = %d io buf num = %d dir = %d rsc %d",
batch_idx, io_idx, io_buf->direction, io_buf->resource_type);
out_port_idx =
cam_cre_bus_wr_out_port_idx(io_buf->resource_type);
if (out_port_idx < 0) {
CAM_ERR(CAM_CRE, "Invalid idx for rsc type: %d",
io_buf->resource_type);
return NULL;
}
out_port_to_wm = &wr_info->out_port_to_wm[out_port_idx];
num_wm_ports = out_port_to_wm->num_wm;
for (k = 0; k < io_buf->num_planes; k++) {
CAM_DBG(CAM_CRE, "comb_idx = %d p_idx = %d",
comb_idx, k);
/* frame level info */
wm_port_id = out_port_to_wm->wm_port_id[k];
wr_reg_client = &wr_reg->wr_clients[wm_port_id];
wr_res_val_client = &wr_reg_val->wr_clients[wm_port_id];
/* Core cfg: enable, Mode */
temp = 0;
temp |= ((wr_res_val_client->mode &
wr_res_val_client->mode_mask) <<
wr_res_val_client->mode_shift);
update_cre_reg_set(cre_reg_buf,
wr_reg->offset + wr_reg_client->client_cfg,
temp);
/* Address of the Image */
update_cre_reg_set(cre_reg_buf,
wr_reg->offset + wr_reg_client->img_addr,
io_buf->p_info[k].iova_addr);
/* Buffer size */
temp = 0;
temp = io_buf->p_info[k].width;
temp |= (io_buf->p_info[k].height &
wr_res_val_client->height_mask) <<
wr_res_val_client->height_shift;
update_cre_reg_set(cre_reg_buf,
wr_reg->offset + wr_reg_client->img_cfg_0,
temp);
update_cre_reg_set(cre_reg_buf,
wr_reg->offset + wr_reg_client->img_cfg_1,
io_buf->p_info[k].x_init);
/* stride */
update_cre_reg_set(cre_reg_buf,
wr_reg->offset + wr_reg_client->img_cfg_2,
io_buf->p_info[k].stride);
/* pack cfg : Format and alignment */
temp = 0;
temp |= ((io_buf->p_info[k].format &
wr_res_val_client->format_mask) <<
wr_res_val_client->format_shift);
temp |= ((io_buf->p_info[k].alignment &
wr_res_val_client->alignment_mask) <<
wr_res_val_client->alignment_shift);
update_cre_reg_set(cre_reg_buf,
wr_reg->offset + wr_reg_client->packer_cfg,
temp);
/* Upadte debug status CFG*/
temp = 0xFFFF;
update_cre_reg_set(cre_reg_buf,
wr_reg->offset + wr_reg_client->debug_status_cfg,
temp);
}
return (uint32_t *)cre_reg_buf;
}
static uint32_t *cam_cre_bus_wm_disable(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, struct cam_cre_dev_prepare_req *prepare,
int batch_idx, int io_idx,
struct cre_reg_buffer *cre_reg_buf)
{
int k;
uint32_t num_wm_ports;
uint32_t req_idx;
uint32_t wm_port_id;
struct cam_cre_ctx *ctx_data;
struct cre_bus_wr_ctx *bus_wr_ctx;
struct cam_cre_bus_wr_reg *wr_reg;
struct cre_bus_out_port_to_wm *out_port_to_wm;
struct cam_cre_bus_wr_client_reg *wr_reg_client;
if (ctx_id < 0 || !prepare) {
CAM_ERR(CAM_CRE, "Invalid data: %d %x", ctx_id, prepare);
return NULL;
}
if (batch_idx >= CRE_MAX_BATCH_SIZE) {
CAM_ERR(CAM_CRE, "Invalid batch idx: %d", batch_idx);
return NULL;
}
ctx_data = prepare->ctx_data;
req_idx = prepare->req_idx;
bus_wr_ctx = wr_info->bus_wr_ctx[ctx_id];
wr_reg = cam_cre_hw_info->bus_wr_reg_offset;
CAM_DBG(CAM_CRE,
"req_idx = %d out_idx %d b %d",
req_idx, io_idx, batch_idx);
out_port_to_wm = &wr_info->out_port_to_wm[io_idx];
num_wm_ports = out_port_to_wm->num_wm;
for (k = 0; k < num_wm_ports; k++) {
/* frame level info */
wm_port_id = out_port_to_wm->wm_port_id[k];
wr_reg_client = &wr_reg->wr_clients[wm_port_id];
/* Core cfg: enable, Mode */
update_cre_reg_set(cre_reg_buf,
wr_reg->offset + wr_reg_client->client_cfg,
0);
}
return (uint32_t *)cre_reg_buf;
}
static int cam_cre_bus_wr_prepare(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, void *data)
{
int rc = 0;
int i, j = 0;
uint32_t req_idx;
struct cam_cre_dev_prepare_req *prepare;
struct cam_cre_ctx *ctx_data;
struct cam_cre_request *cre_request;
struct cre_io_buf *io_buf;
int io_buf_idx;
struct cre_bus_wr_ctx *bus_wr_ctx;
struct cre_reg_buffer *cre_reg_buf;
uint32_t *ret;
if (ctx_id < 0 || !data) {
CAM_ERR(CAM_CRE, "Invalid data: %d %x", ctx_id, data);
return -EINVAL;
}
prepare = data;
ctx_data = prepare->ctx_data;
req_idx = prepare->req_idx;
bus_wr_ctx = wr_info->bus_wr_ctx[ctx_id];
cre_request = ctx_data->req_list[req_idx];
cre_reg_buf = &cre_request->cre_reg_buf;
CAM_DBG(CAM_CRE, "req_idx = %d req_id = %lld offset = %d",
req_idx, cre_request->request_id);
for (i = 0; i < cre_request->num_batch; i++) {
for (j = 0; j < cre_request->num_io_bufs[i]; j++) {
io_buf = cre_request->io_buf[i][j];
CAM_DBG(CAM_CRE, "batch = %d io buf num = %d dir = %d",
i, j, io_buf->direction);
if (io_buf->direction != CAM_BUF_OUTPUT)
continue;
ret = cam_cre_bus_wr_update(cam_cre_hw_info,
ctx_id, prepare, i, j,
cre_reg_buf);
if (!ret) {
rc = -EINVAL;
goto end;
}
}
}
/* Disable WMs which are not enabled */
for (i = 0; i < cre_request->num_batch; i++) {
for (j = CRE_MAX_IN_RES; j <= CRE_MAX_OUT_RES; j++) {
io_buf_idx = cam_cre_bus_en_port_idx(cre_request, i, j);
if (io_buf_idx >= 0)
continue;
io_buf_idx = cam_cre_bus_wr_out_port_idx(j);
if (io_buf_idx < 0) {
CAM_ERR(CAM_CRE, "Invalid idx for rsc type:%d",
j);
return io_buf_idx;
}
ret = cam_cre_bus_wm_disable(cam_cre_hw_info,
ctx_id, prepare, i, io_buf_idx,
cre_reg_buf);
if (!ret) {
rc = -EINVAL;
goto end;
}
}
}
end:
return rc;
}
static int cam_cre_bus_wr_acquire(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, void *data)
{
int rc = 0, i;
struct cam_cre_acquire_dev_info *in_acquire;
struct cre_bus_wr_ctx *bus_wr_ctx;
struct cre_bus_out_port_to_wm *out_port_to_wr;
int out_port_idx;
if (ctx_id < 0 || !data || ctx_id >= CRE_CTX_MAX) {
CAM_ERR(CAM_CRE, "Invalid data: %d %x", ctx_id, data);
return -EINVAL;
}
wr_info->bus_wr_ctx[ctx_id] = vzalloc(sizeof(struct cre_bus_wr_ctx));
if (!wr_info->bus_wr_ctx[ctx_id]) {
CAM_ERR(CAM_CRE, "Out of memory");
return -ENOMEM;
}
wr_info->bus_wr_ctx[ctx_id]->cre_acquire = data;
in_acquire = data;
bus_wr_ctx = wr_info->bus_wr_ctx[ctx_id];
bus_wr_ctx->num_out_ports = in_acquire->num_out_res;
bus_wr_ctx->security_flag = in_acquire->secure_mode;
for (i = 0; i < in_acquire->num_out_res; i++) {
if (!in_acquire->out_res[i].width)
continue;
CAM_DBG(CAM_CRE, "i = %d format = %u width = %x height = %x",
i, in_acquire->out_res[i].format,
in_acquire->out_res[i].width,
in_acquire->out_res[i].height);
out_port_idx =
cam_cre_bus_wr_out_port_idx(in_acquire->out_res[i].res_id);
if (out_port_idx < 0) {
CAM_DBG(CAM_CRE, "Invalid out_port_idx: %d",
in_acquire->out_res[i].res_id);
rc = -EINVAL;
goto end;
}
out_port_to_wr = &wr_info->out_port_to_wm[out_port_idx];
if (!out_port_to_wr->num_wm) {
CAM_DBG(CAM_CRE, "Invalid format for Input port");
rc = -EINVAL;
goto end;
}
bus_wr_ctx->io_port_info.output_port_id[i] =
in_acquire->out_res[i].res_id;
bus_wr_ctx->io_port_info.output_format_type[i] =
in_acquire->out_res[i].format;
CAM_DBG(CAM_CRE, "i:%d port_id = %u",
i, bus_wr_ctx->io_port_info.output_port_id[i]);
}
end:
return rc;
}
static int cam_cre_bus_wr_init(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, void *data)
{
struct cam_cre_bus_wr_reg_val *bus_wr_reg_val;
struct cam_cre_bus_wr_reg *bus_wr_reg;
struct cam_cre_dev_init *dev_init = data;
if (!cam_cre_hw_info) {
CAM_ERR(CAM_CRE, "Invalid cam_cre_hw_info");
return -EINVAL;
}
wr_info->cre_hw_info = cam_cre_hw_info;
bus_wr_reg_val = cam_cre_hw_info->bus_wr_reg_val;
bus_wr_reg = cam_cre_hw_info->bus_wr_reg_offset;
bus_wr_reg->base = dev_init->core_info->cre_hw_info->cre_bus_wr_base;
cam_io_w_mb(bus_wr_reg_val->irq_mask_0,
cam_cre_hw_info->bus_wr_reg_offset->base +
bus_wr_reg->irq_mask_0);
cam_io_w_mb(bus_wr_reg_val->irq_mask_1,
cam_cre_hw_info->bus_wr_reg_offset->base +
bus_wr_reg->irq_mask_1);
return 0;
}
static int cam_cre_bus_wr_probe(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, void *data)
{
int i, k;
struct cam_cre_bus_wr_reg_val *bus_wr_reg_val;
struct cre_bus_out_port_to_wm *out_port_to_wm;
uint32_t output_port_idx;
uint32_t wm_idx;
if (!cam_cre_hw_info) {
CAM_ERR(CAM_CRE, "Invalid cam_cre_hw_info");
return -EINVAL;
}
wr_info = kzalloc(sizeof(struct cre_bus_wr), GFP_KERNEL);
if (!wr_info) {
CAM_ERR(CAM_CRE, "Out of memory");
return -ENOMEM;
}
wr_info->cre_hw_info = cam_cre_hw_info;
bus_wr_reg_val = cam_cre_hw_info->bus_wr_reg_val;
for (i = 0; i < bus_wr_reg_val->num_clients; i++) {
output_port_idx =
bus_wr_reg_val->wr_clients[i].output_port_id - 1;
out_port_to_wm = &wr_info->out_port_to_wm[output_port_idx];
wm_idx = out_port_to_wm->num_wm;
out_port_to_wm->output_port_id =
bus_wr_reg_val->wr_clients[i].output_port_id;
out_port_to_wm->wm_port_id[wm_idx] =
bus_wr_reg_val->wr_clients[i].wm_port_id;
out_port_to_wm->num_wm++;
}
for (i = 0; i < CRE_MAX_OUT_RES; i++) {
out_port_to_wm = &wr_info->out_port_to_wm[i];
CAM_DBG(CAM_CRE, "output port id = %d",
out_port_to_wm->output_port_id);
CAM_DBG(CAM_CRE, "num_wms = %d",
out_port_to_wm->num_wm);
for (k = 0; k < out_port_to_wm->num_wm; k++) {
CAM_DBG(CAM_CRE, "wm port id = %d",
out_port_to_wm->wm_port_id[k]);
}
}
return 0;
}
static int cam_cre_bus_wr_isr(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, void *data)
{
uint32_t irq_status_0, irq_status_1;
struct cam_cre_bus_wr_reg *bus_wr_reg;
struct cam_cre_bus_wr_reg_val *bus_wr_reg_val;
struct cam_cre_irq_data *irq_data = data;
uint32_t debug_status_0;
uint32_t debug_status_1;
uint32_t img_violation_status;
uint32_t violation_status;
if (!cam_cre_hw_info || !irq_data) {
CAM_ERR(CAM_CRE, "Invalid cam_cre_hw_info");
return -EINVAL;
}
bus_wr_reg = cam_cre_hw_info->bus_wr_reg_offset;
bus_wr_reg_val = cam_cre_hw_info->bus_wr_reg_val;
/* Read and Clear Top Interrupt status */
irq_status_0 = cam_io_r_mb(bus_wr_reg->base + bus_wr_reg->irq_status_0);
irq_status_1 = cam_io_r_mb(bus_wr_reg->base + bus_wr_reg->irq_status_1);
cam_io_w_mb(irq_status_0,
bus_wr_reg->base + bus_wr_reg->irq_clear_0);
cam_io_w_mb(irq_status_1,
bus_wr_reg->base + bus_wr_reg->irq_clear_1);
cam_io_w_mb(bus_wr_reg_val->irq_cmd_clear,
bus_wr_reg->base + bus_wr_reg->irq_cmd);
if (irq_status_0 & bus_wr_reg_val->cons_violation) {
irq_data->error = 1;
CAM_ERR(CAM_CRE, "cre bus wr cons_violation");
}
if ((irq_status_0 & bus_wr_reg_val->violation) ||
(irq_status_0 & bus_wr_reg_val->img_size_violation)) {
irq_data->error = 1;
img_violation_status = cam_io_r_mb(bus_wr_reg->base +
bus_wr_reg->image_size_violation_status);
violation_status = cam_io_r_mb(bus_wr_reg->base +
bus_wr_reg->violation_status);
debug_status_0 = cam_io_r_mb(bus_wr_reg->base +
bus_wr_reg->wr_clients[0].debug_status_0);
debug_status_1 = cam_io_r_mb(bus_wr_reg->base +
bus_wr_reg->wr_clients[0].debug_status_1);
CAM_ERR(CAM_CRE,
"violation status 0x%x 0x%x debug status 0/1 0x%x/0x%x",
violation_status, img_violation_status,
debug_status_0, debug_status_1);
}
if (irq_status_1 & bus_wr_reg_val->client_buf_done)
CAM_INFO(CAM_CRE, "Cleint 0 Buff done");
return 0;
}
int cam_cre_bus_wr_process(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, uint32_t cmd_id, void *data)
{
int rc = 0;
switch (cmd_id) {
case CRE_HW_PROBE:
CAM_DBG(CAM_CRE, "CRE_HW_PROBE: E");
rc = cam_cre_bus_wr_probe(cam_cre_hw_info, ctx_id, data);
CAM_DBG(CAM_CRE, "CRE_HW_PROBE: X");
break;
case CRE_HW_INIT:
CAM_DBG(CAM_CRE, "CRE_HW_INIT: E");
rc = cam_cre_bus_wr_init(cam_cre_hw_info, ctx_id, data);
CAM_DBG(CAM_CRE, "CRE_HW_INIT: X");
break;
case CRE_HW_ACQUIRE:
CAM_DBG(CAM_CRE, "CRE_HW_ACQUIRE: E");
rc = cam_cre_bus_wr_acquire(cam_cre_hw_info, ctx_id, data);
CAM_DBG(CAM_CRE, "CRE_HW_ACQUIRE: X");
break;
case CRE_HW_RELEASE:
CAM_DBG(CAM_CRE, "CRE_HW_RELEASE: E");
rc = cam_cre_bus_wr_release(cam_cre_hw_info, ctx_id, data);
CAM_DBG(CAM_CRE, "CRE_HW_RELEASE: X");
break;
case CRE_HW_PREPARE:
CAM_DBG(CAM_CRE, "CRE_HW_PREPARE: E");
rc = cam_cre_bus_wr_prepare(cam_cre_hw_info, ctx_id, data);
CAM_DBG(CAM_CRE, "CRE_HW_PREPARE: X");
break;
case CRE_HW_REG_SET_UPDATE:
rc = cam_cre_bus_wr_reg_set_update(cam_cre_hw_info, 0, data);
break;
case CRE_HW_DEINIT:
case CRE_HW_START:
case CRE_HW_STOP:
case CRE_HW_FLUSH:
case CRE_HW_CLK_UPDATE:
case CRE_HW_BW_UPDATE:
case CRE_HW_RESET:
case CRE_HW_SET_IRQ_CB:
rc = 0;
CAM_DBG(CAM_CRE, "Unhandled cmds: %d", cmd_id);
break;
case CRE_HW_ISR:
rc = cam_cre_bus_wr_isr(cam_cre_hw_info, 0, data);
break;
default:
CAM_ERR(CAM_CRE, "Unsupported cmd: %d", cmd_id);
break;
}
return rc;
}

View File

@@ -0,0 +1,98 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef CRE_BUS_WR_H
#define CRE_BUS_WR_H
#include <linux/types.h>
#include <linux/completion.h>
#include <media/cam_cre.h>
#include "cre_hw.h"
#include "cam_hw_mgr_intf.h"
#include "cam_hw_intf.h"
#include "cam_soc_util.h"
#include "cam_cre_hw_mgr.h"
/**
* struct cre_bus_wr_io_port_info
*
* @num_frames_cmds: Number of frame commands
* @go_cmd_addr: GO command address
* @go_cmd_len: GO command length
*/
struct cre_bus_wr_io_port_info {
uint32_t num_frames_cmds;
uint32_t *go_cmd_addr;
uint32_t go_cmd_len;
uint32_t output_port_id[CRE_MAX_OUT_RES];
uint32_t output_format_type[CRE_MAX_OUT_RES];
};
/**
* struct cre_bus_wr_io_port_batch
*
* num_batch: Number of batches
* io_port: CDM IO Port Info
*/
struct cre_bus_wr_io_port_batch {
uint32_t num_batch;
struct cre_bus_wr_io_port_info io_port[CRE_MAX_BATCH_SIZE];
};
/**
* struct cre_bus_wr_wm
*
* @wm_port_id: WM port ID
* @format_type: Format type
*/
struct cre_bus_wr_wm {
uint32_t wm_port_id;
uint32_t format_type;
};
/**
* struct cre_bus_out_port_to_wm
*
* @output_port_id: Output port ID
* @num_combos: Number of combos
* @num_wm: Number of WMs
* @wm_port_id: WM port Id
*/
struct cre_bus_out_port_to_wm {
uint32_t output_port_id;
uint32_t num_wm;
uint32_t wm_port_id[CRE_MAX_OUT_RES];
};
/**
* struct cre_bus_wr_ctx
*
* @cre_acquire: CRE acquire structure
* @security_flag: security flag
* @num_out_ports: Number of out ports
* @io_port_info: IO port info
*/
struct cre_bus_wr_ctx {
struct cam_cre_acquire_dev_info *cre_acquire;
bool security_flag;
uint32_t num_out_ports;
struct cre_bus_wr_io_port_info io_port_info;
struct cre_bus_wr_io_port_batch io_port_batch;
};
/**
* struct cre_bus_wr
*
* @cre_hw_info: CRE hardware info
* @out_port_to_wm: IO port to WM mapping
* @bus_wr_ctx: WM context
*/
struct cre_bus_wr {
struct cam_cre_hw *cre_hw_info;
struct cre_bus_out_port_to_wm out_port_to_wm[CRE_MAX_OUT_RES];
struct cre_bus_wr_ctx *bus_wr_ctx[CRE_CTX_MAX];
};
#endif /* CRE_BUS_WR_H */

View File

@@ -0,0 +1,580 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#include <linux/of.h>
#include <linux/debugfs.h>
#include <linux/videodev2.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include "cam_io_util.h"
#include "cam_hw.h"
#include "cam_hw_intf.h"
#include "cre_core.h"
#include "cre_soc.h"
#include "cam_soc_util.h"
#include "cam_io_util.h"
#include "cam_cre_hw_intf.h"
#include "cam_cre_hw_mgr_intf.h"
#include "cam_cpas_api.h"
#include "cam_debug_util.h"
#include "cre_dev_intf.h"
#include "cam_compat.h"
#include "cre_bus_wr.h"
#include "cre_bus_rd.h"
#define CAM_CRE_RESET_TIMEOUT msecs_to_jiffies(500)
#define CAM_CRE_FE_IRQ 0x4
#define CAM_CRE_WE_IRQ 0x2
struct cam_cre_irq_data irq_data;
static int cam_cre_caps_vote(struct cam_cre_device_core_info *core_info,
struct cam_cre_dev_bw_update *cpas_vote)
{
int rc = 0;
if (cpas_vote->ahb_vote_valid)
rc = cam_cpas_update_ahb_vote(core_info->cpas_handle,
&cpas_vote->ahb_vote);
if (cpas_vote->axi_vote_valid)
rc = cam_cpas_update_axi_vote(core_info->cpas_handle,
&cpas_vote->axi_vote);
if (rc)
CAM_ERR(CAM_CRE, "cpas vote is failed: %d", rc);
return rc;
}
int cam_cre_get_hw_caps(void *hw_priv, void *get_hw_cap_args,
uint32_t arg_size)
{
struct cam_hw_info *cre_dev = hw_priv;
struct cam_hw_soc_info *soc_info = NULL;
struct cam_cre_device_core_info *core_info = NULL;
struct cam_cre_hw_ver *cre_hw_ver;
struct cam_cre_top_reg_val *top_reg_val;
if (!hw_priv) {
CAM_ERR(CAM_CRE, "Invalid cam_dev_info");
return -EINVAL;
}
soc_info = &cre_dev->soc_info;
core_info = (struct cam_cre_device_core_info *)cre_dev->core_info;
if ((!soc_info) || (!core_info)) {
CAM_ERR(CAM_CRE, "soc_info = %x core_info = %x",
soc_info, core_info);
return -EINVAL;
}
if (!get_hw_cap_args) {
CAM_ERR(CAM_CRE, "Invalid caps");
return -EINVAL;
}
top_reg_val = core_info->cre_hw_info->cre_hw->top_reg_val;
cre_hw_ver = get_hw_cap_args;
cre_hw_ver->hw_ver.major =
(core_info->hw_version & top_reg_val->major_mask) >>
top_reg_val->major_shift;
cre_hw_ver->hw_ver.minor =
(core_info->hw_version & top_reg_val->minor_mask) >>
top_reg_val->minor_shift;
return 0;
}
static int cam_cre_dev_process_init(struct cam_cre_hw *cre_hw,
void *cmd_args)
{
int rc = 0;
rc = cam_cre_top_process(cre_hw, 0, CRE_HW_INIT, cmd_args);
if (rc)
goto top_init_fail;
rc = cam_cre_bus_rd_process(cre_hw, 0, CRE_HW_INIT, cmd_args);
if (rc)
goto bus_rd_init_fail;
rc = cam_cre_bus_wr_process(cre_hw, 0, CRE_HW_INIT, cmd_args);
if (rc)
goto bus_wr_init_fail;
return rc;
bus_wr_init_fail:
rc = cam_cre_bus_rd_process(cre_hw, 0,
CRE_HW_DEINIT, NULL);
bus_rd_init_fail:
rc = cam_cre_top_process(cre_hw, 0,
CRE_HW_DEINIT, NULL);
top_init_fail:
return rc;
}
static int cam_cre_process_init(struct cam_cre_hw *cre_hw,
void *cmd_args)
{
return cam_cre_dev_process_init(cre_hw, cmd_args);
}
int cam_cre_init_hw(void *device_priv,
void *init_hw_args, uint32_t arg_size)
{
struct cam_hw_info *cre_dev = device_priv;
struct cam_hw_soc_info *soc_info = NULL;
struct cam_cre_device_core_info *core_info = NULL;
struct cam_cre_cpas_vote *cpas_vote;
int rc = 0;
struct cam_cre_dev_init *init;
struct cam_cre_hw *cre_hw;
if (!device_priv) {
CAM_ERR(CAM_CRE, "Invalid cam_dev_info");
rc = -EINVAL;
goto end;
}
soc_info = &cre_dev->soc_info;
core_info = (struct cam_cre_device_core_info *)cre_dev->core_info;
if ((!soc_info) || (!core_info)) {
CAM_ERR(CAM_CRE, "soc_info = %pK core_info = %pK",
soc_info, core_info);
rc = -EINVAL;
goto end;
}
cre_hw = core_info->cre_hw_info->cre_hw;
cpas_vote = kzalloc(sizeof(struct cam_cre_cpas_vote), GFP_KERNEL);
if (!cpas_vote) {
CAM_ERR(CAM_ISP, "Out of memory");
rc = -ENOMEM;
goto end;
}
cpas_vote->ahb_vote.type = CAM_VOTE_ABSOLUTE;
cpas_vote->ahb_vote.vote.level = CAM_SVS_VOTE;
cpas_vote->axi_vote.num_paths = 1;
cpas_vote->axi_vote.axi_path[0].path_data_type =
CAM_AXI_PATH_DATA_ALL;
cpas_vote->axi_vote.axi_path[0].transac_type =
CAM_AXI_TRANSACTION_WRITE;
cpas_vote->axi_vote.axi_path[0].camnoc_bw =
CAM_CPAS_DEFAULT_AXI_BW;
cpas_vote->axi_vote.axi_path[0].mnoc_ab_bw =
CAM_CPAS_DEFAULT_AXI_BW;
cpas_vote->axi_vote.axi_path[0].mnoc_ib_bw =
CAM_CPAS_DEFAULT_AXI_BW;
cpas_vote->axi_vote.axi_path[0].ddr_ab_bw =
CAM_CPAS_DEFAULT_AXI_BW;
cpas_vote->axi_vote.axi_path[0].ddr_ib_bw =
CAM_CPAS_DEFAULT_AXI_BW;
rc = cam_cpas_start(core_info->cpas_handle,
&cpas_vote->ahb_vote, &cpas_vote->axi_vote);
if (rc) {
CAM_ERR(CAM_CRE, "cpass start failed: %d", rc);
goto free_cpas_vote;
}
core_info->cpas_start = true;
rc = cam_cre_enable_soc_resources(soc_info);
if (rc)
goto enable_soc_resource_failed;
else
core_info->clk_enable = true;
init = init_hw_args;
init->core_info = core_info;
rc = cam_cre_process_init(cre_hw, init_hw_args);
if (rc)
goto process_init_failed;
else
goto free_cpas_vote;
process_init_failed:
if (cam_cre_disable_soc_resources(soc_info))
CAM_ERR(CAM_CRE, "disable soc resource failed");
enable_soc_resource_failed:
if (cam_cpas_stop(core_info->cpas_handle))
CAM_ERR(CAM_CRE, "cpas stop is failed");
else
core_info->cpas_start = false;
free_cpas_vote:
cam_free_clear((void *)cpas_vote);
cpas_vote = NULL;
end:
return rc;
}
int cam_cre_deinit_hw(void *device_priv,
void *init_hw_args, uint32_t arg_size)
{
struct cam_hw_info *cre_dev = device_priv;
struct cam_hw_soc_info *soc_info = NULL;
struct cam_cre_device_core_info *core_info = NULL;
int rc = 0;
if (!device_priv) {
CAM_ERR(CAM_CRE, "Invalid cam_dev_info");
return -EINVAL;
}
soc_info = &cre_dev->soc_info;
core_info = (struct cam_cre_device_core_info *)cre_dev->core_info;
if ((!soc_info) || (!core_info)) {
CAM_ERR(CAM_CRE, "soc_info = %pK core_info = %pK",
soc_info, core_info);
return -EINVAL;
}
rc = cam_cre_disable_soc_resources(soc_info);
if (rc)
CAM_ERR(CAM_CRE, "soc disable is failed : %d", rc);
core_info->clk_enable = false;
return rc;
}
static int cam_cre_dev_process_dump_debug_reg(struct cam_cre_hw *cre_hw)
{
int rc = 0;
rc = cam_cre_top_process(cre_hw, -1,
CRE_HW_DUMP_DEBUG, NULL);
return rc;
}
static int cam_cre_dev_process_reset(struct cam_cre_hw *cre_hw, void *cmd_args)
{
int rc = 0;
rc = cam_cre_top_process(cre_hw, -1,
CRE_HW_RESET, NULL);
return rc;
}
static int cam_cre_dev_process_release(struct cam_cre_hw *cre_hw, void *cmd_args)
{
int rc = 0;
struct cam_cre_dev_release *cre_dev_release;
cre_dev_release = cmd_args;
rc = cam_cre_top_process(cre_hw, cre_dev_release->ctx_id,
CRE_HW_RELEASE, NULL);
rc |= cam_cre_bus_rd_process(cre_hw, cre_dev_release->ctx_id,
CRE_HW_RELEASE, NULL);
rc |= cam_cre_bus_wr_process(cre_hw, cre_dev_release->ctx_id,
CRE_HW_RELEASE, NULL);
return rc;
}
static int cam_cre_dev_process_acquire(struct cam_cre_hw *cre_hw, void *cmd_args)
{
int rc = 0;
struct cam_cre_dev_acquire *cre_dev_acquire;
if (!cmd_args || !cre_hw) {
CAM_ERR(CAM_CRE, "Invalid arguments: %pK %pK",
cmd_args, cre_hw);
return -EINVAL;
}
cre_dev_acquire = cmd_args;
rc = cam_cre_top_process(cre_hw, cre_dev_acquire->ctx_id,
CRE_HW_ACQUIRE, cre_dev_acquire);
if (rc)
goto top_acquire_fail;
rc = cam_cre_bus_rd_process(cre_hw, cre_dev_acquire->ctx_id,
CRE_HW_ACQUIRE, cre_dev_acquire->cre_acquire);
if (rc)
goto bus_rd_acquire_fail;
rc = cam_cre_bus_wr_process(cre_hw, cre_dev_acquire->ctx_id,
CRE_HW_ACQUIRE, cre_dev_acquire->cre_acquire);
if (rc)
goto bus_wr_acquire_fail;
return 0;
bus_wr_acquire_fail:
cam_cre_bus_rd_process(cre_hw, cre_dev_acquire->ctx_id,
CRE_HW_RELEASE, cre_dev_acquire->cre_acquire);
bus_rd_acquire_fail:
cam_cre_top_process(cre_hw, cre_dev_acquire->ctx_id,
CRE_HW_RELEASE, cre_dev_acquire->cre_acquire);
top_acquire_fail:
return rc;
}
static int cam_cre_dev_process_reg_set_update(struct cam_cre_hw *cre_hw, void *cmd_args)
{
int rc = 0;
struct cam_cre_dev_reg_set_update *reg_set_update;
reg_set_update = cmd_args;
rc = cam_cre_top_process(cre_hw, 0,
CRE_HW_REG_SET_UPDATE, reg_set_update);
if (rc)
goto end;
rc = cam_cre_bus_rd_process(cre_hw, 0,
CRE_HW_REG_SET_UPDATE, reg_set_update);
if (rc)
goto end;
rc = cam_cre_bus_wr_process(cre_hw, 0,
CRE_HW_REG_SET_UPDATE, reg_set_update);
if (rc)
goto end;
end:
return rc;
}
static int cam_cre_dev_process_prepare(struct cam_cre_hw *cre_hw, void *cmd_args)
{
int rc = 0;
struct cam_cre_dev_prepare_req *cre_dev_prepare_req;
cre_dev_prepare_req = cmd_args;
rc = cam_cre_top_process(cre_hw, cre_dev_prepare_req->ctx_data->ctx_id,
CRE_HW_PREPARE, cre_dev_prepare_req);
if (rc)
goto end;
rc = cam_cre_bus_rd_process(cre_hw,
cre_dev_prepare_req->ctx_data->ctx_id,
CRE_HW_PREPARE, cre_dev_prepare_req);
if (rc)
goto end;
rc = cam_cre_bus_wr_process(cre_hw,
cre_dev_prepare_req->ctx_data->ctx_id,
CRE_HW_PREPARE, cre_dev_prepare_req);
if (rc)
goto end;
end:
return rc;
}
static int cam_cre_dev_process_probe(struct cam_cre_hw *cre_hw,
void *cmd_args)
{
cam_cre_top_process(cre_hw, -1, CRE_HW_PROBE, NULL);
cam_cre_bus_rd_process(cre_hw, -1, CRE_HW_PROBE, NULL);
cam_cre_bus_wr_process(cre_hw, -1, CRE_HW_PROBE, NULL);
return 0;
}
static int cam_cre_process_probe(struct cam_cre_hw *cre_hw,
void *cmd_args)
{
return cam_cre_dev_process_probe(cre_hw, cmd_args);
}
static int cam_cre_process_dump_debug_reg(struct cam_cre_hw *cre_hw)
{
return cam_cre_dev_process_dump_debug_reg(cre_hw);
}
static int cam_cre_process_reset(struct cam_cre_hw *cre_hw,
void *cmd_args)
{
return cam_cre_dev_process_reset(cre_hw, cmd_args);
}
static int cam_cre_process_release(struct cam_cre_hw *cre_hw,
void *cmd_args)
{
return cam_cre_dev_process_release(cre_hw, cmd_args);
}
static int cam_cre_process_acquire(struct cam_cre_hw *cre_hw,
void *cmd_args)
{
return cam_cre_dev_process_acquire(cre_hw, cmd_args);
}
static int cam_cre_process_prepare(struct cam_cre_hw *cre_hw,
void *cmd_args)
{
return cam_cre_dev_process_prepare(cre_hw, cmd_args);
}
static int cam_cre_process_reg_set_update(struct cam_cre_hw *cre_hw,
void *cmd_args)
{
return cam_cre_dev_process_reg_set_update(cre_hw, cmd_args);
}
int cam_cre_process_cmd(void *device_priv, uint32_t cmd_type,
void *cmd_args, uint32_t arg_size)
{
int rc = 0;
struct cam_hw_info *cre_dev = device_priv;
struct cam_hw_soc_info *soc_info = NULL;
struct cam_cre_device_core_info *core_info = NULL;
struct cam_cre_hw *cre_hw;
unsigned long flags;
if (!device_priv) {
CAM_ERR(CAM_CRE, "Invalid args %x for cmd %u",
device_priv, cmd_type);
return -EINVAL;
}
soc_info = &cre_dev->soc_info;
core_info = (struct cam_cre_device_core_info *)cre_dev->core_info;
if ((!soc_info) || (!core_info)) {
CAM_ERR(CAM_CRE, "soc_info = %x core_info = %x",
soc_info, core_info);
return -EINVAL;
}
cre_hw = core_info->cre_hw_info->cre_hw;
if (!cre_hw) {
CAM_ERR(CAM_CRE, "Invalid cre hw info");
return -EINVAL;
}
switch (cmd_type) {
case CRE_HW_PROBE:
rc = cam_cre_process_probe(cre_hw, cmd_args);
break;
case CRE_HW_ACQUIRE:
rc = cam_cre_process_acquire(cre_hw, cmd_args);
break;
case CRE_HW_RELEASE:
rc = cam_cre_process_release(cre_hw, cmd_args);
break;
case CRE_HW_PREPARE:
rc = cam_cre_process_prepare(cre_hw, cmd_args);
break;
case CRE_HW_START:
break;
case CRE_HW_STOP:
break;
case CRE_HW_FLUSH:
break;
case CRE_HW_RESET:
rc = cam_cre_process_reset(cre_hw, cmd_args);
break;
case CRE_HW_CLK_UPDATE: {
struct cam_cre_dev_clk_update *clk_upd_cmd =
(struct cam_cre_dev_clk_update *)cmd_args;
if (!core_info->clk_enable) {
rc = cam_soc_util_clk_enable_default(soc_info,
CAM_SVS_VOTE);
if (rc) {
CAM_ERR(CAM_CRE, "Clock enable is failed");
return rc;
}
core_info->clk_enable = true;
}
rc = cam_cre_update_clk_rate(soc_info, clk_upd_cmd->clk_rate);
if (rc)
CAM_ERR(CAM_CRE, "Failed to update clk: %d", rc);
}
break;
case CRE_HW_CLK_DISABLE: {
if (core_info->clk_enable)
cam_soc_util_clk_disable_default(soc_info);
core_info->clk_enable = false;
}
break;
case CRE_HW_BW_UPDATE: {
struct cam_cre_dev_bw_update *cpas_vote = cmd_args;
if (!cmd_args)
return -EINVAL;
rc = cam_cre_caps_vote(core_info, cpas_vote);
if (rc)
CAM_ERR(CAM_CRE, "failed to update bw: %d", rc);
}
break;
case CRE_HW_SET_IRQ_CB: {
struct cam_cre_set_irq_cb *irq_cb = cmd_args;
if (!cmd_args) {
CAM_ERR(CAM_CRE, "cmd args NULL");
return -EINVAL;
}
spin_lock_irqsave(&cre_dev->hw_lock, flags);
core_info->irq_cb.cre_hw_mgr_cb = irq_cb->cre_hw_mgr_cb;
core_info->irq_cb.data = irq_cb->data;
spin_unlock_irqrestore(&cre_dev->hw_lock, flags);
}
break;
case CRE_HW_REG_SET_UPDATE:
rc = cam_cre_process_reg_set_update(cre_hw, cmd_args);
break;
case CRE_HW_DUMP_DEBUG:
rc = cam_cre_process_dump_debug_reg(cre_hw);
break;
default:
break;
}
return rc;
}
irqreturn_t cam_cre_irq(int irq_num, void *data)
{
struct cam_hw_info *cre_dev = data;
struct cam_cre_device_core_info *core_info = NULL;
struct cam_cre_hw *cre_hw;
if (!data) {
CAM_ERR(CAM_CRE, "Invalid cam_dev_info or query_cap args");
return IRQ_HANDLED;
}
core_info = (struct cam_cre_device_core_info *)cre_dev->core_info;
cre_hw = core_info->cre_hw_info->cre_hw;
irq_data.error = 0;
cam_cre_top_process(cre_hw, 0, CRE_HW_ISR, &irq_data);
if (irq_data.top_irq_status & CAM_CRE_FE_IRQ)
cam_cre_bus_rd_process(cre_hw, 0, CRE_HW_ISR, &irq_data);
if (irq_data.top_irq_status & CAM_CRE_WE_IRQ)
cam_cre_bus_wr_process(cre_hw, 0, CRE_HW_ISR, &irq_data);
spin_lock(&cre_dev->hw_lock);
if (core_info->irq_cb.cre_hw_mgr_cb && core_info->irq_cb.data)
if (irq_data.error ||
((irq_data.top_irq_status & CAM_CRE_WE_IRQ) &&
irq_data.wr_buf_done))
core_info->irq_cb.cre_hw_mgr_cb(&irq_data,
sizeof(struct cam_hw_info),
core_info->irq_cb.data);
spin_unlock(&cre_dev->hw_lock);
return IRQ_HANDLED;
}

View File

@@ -0,0 +1,84 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef CAM_CRE_CORE_H
#define CAM_CRE_CORE_H
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/dma-buf.h>
#include <media/cam_cre.h>
#include "cam_cpas_api.h"
#include "cre_hw.h"
#include "cam_cre_hw_intf.h"
/**
* struct cam_cre_cpas_vote
* @ahb_vote: AHB vote info
* @axi_vote: AXI vote info
* @ahb_vote_valid: Flag for ahb vote data
* @axi_vote_valid: flag for axi vote data
*/
struct cam_cre_cpas_vote {
struct cam_ahb_vote ahb_vote;
struct cam_axi_vote axi_vote;
uint32_t ahb_vote_valid;
uint32_t axi_vote_valid;
};
struct cam_cre_device_hw_info {
struct cam_cre_hw *cre_hw;
uint32_t hw_idx;
void *cre_top_base;
void *cre_qos_base;
void *cre_pp_base;
void *cre_bus_rd_base;
void *cre_bus_wr_base;
uint32_t reserved;
};
struct cam_cre_device_core_info {
struct cam_cre_device_hw_info *cre_hw_info;
uint32_t hw_version;
uint32_t hw_idx;
uint32_t hw_type;
uint32_t cpas_handle;
bool cpas_start;
bool clk_enable;
struct cam_cre_set_irq_cb irq_cb;
};
int cam_cre_init_hw(void *device_priv,
void *init_hw_args, uint32_t arg_size);
int cam_cre_deinit_hw(void *device_priv,
void *init_hw_args, uint32_t arg_size);
int cam_cre_start_hw(void *device_priv,
void *start_hw_args, uint32_t arg_size);
int cam_cre_stop_hw(void *device_priv,
void *stop_hw_args, uint32_t arg_size);
int cam_cre_reset_hw(void *device_priv,
void *reset_hw_args, uint32_t arg_size);
int cam_cre_flush_hw(void *device_priv,
void *flush_args, uint32_t arg_size);
int cam_cre_get_hw_caps(void *device_priv,
void *get_hw_cap_args, uint32_t arg_size);
int cam_cre_process_cmd(void *device_priv, uint32_t cmd_type,
void *cmd_args, uint32_t arg_size);
irqreturn_t cam_cre_irq(int irq_num, void *data);
/**
* @brief : API to register CRE hw to platform framework.
* @return struct platform_device pointer on success, or ERR_PTR() on error.
*/
int cam_cre_init_module(void);
/**
* @brief : API to remove CRE Hw from platform framework.
*/
void cam_cre_exit_module(void);
#endif /* CAM_CRE_CORE_H */

View File

@@ -0,0 +1,321 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/mod_devicetable.h>
#include <linux/of_device.h>
#include <linux/timer.h>
#include "cam_node.h"
#include "cre_core.h"
#include "cre_soc.h"
#include "cam_hw.h"
#include "cre_hw.h"
#include "cam_hw_intf.h"
#include "cam_io_util.h"
#include "cam_cre_hw_mgr_intf.h"
#include "cam_cpas_api.h"
#include "cam_debug_util.h"
#include "cre_hw_100.h"
#include "cre_dev_intf.h"
#include "cam_smmu_api.h"
#include "camera_main.h"
static struct cam_cre_hw_intf_data cam_cre_dev_list[CRE_DEV_MAX];
static struct cam_cre_device_hw_info cre_hw_info;
static struct cam_cre_soc_private cre_soc_info;
static char cre_dev_name[8];
static struct cre_hw_version_reg cre_hw_version_reg = {
.hw_ver = 0x0,
};
static int cam_cre_init_hw_version(struct cam_hw_soc_info *soc_info,
struct cam_cre_device_core_info *core_info)
{
int rc = 0;
CAM_DBG(CAM_CRE, "soc_info = %x core_info = %x",
soc_info, core_info);
CAM_DBG(CAM_CRE, "TOP: %x RD: %x WR: %x",
soc_info->reg_map[CRE_TOP_BASE].mem_base,
soc_info->reg_map[CRE_BUS_RD].mem_base,
soc_info->reg_map[CRE_BUS_WR].mem_base);
core_info->cre_hw_info->cre_top_base =
soc_info->reg_map[CRE_TOP_BASE].mem_base;
core_info->cre_hw_info->cre_bus_rd_base =
soc_info->reg_map[CRE_BUS_RD].mem_base;
core_info->cre_hw_info->cre_bus_wr_base =
soc_info->reg_map[CRE_BUS_WR].mem_base;
core_info->hw_version = cam_io_r_mb(
core_info->cre_hw_info->cre_top_base +
cre_hw_version_reg.hw_ver);
switch (core_info->hw_version) {
case CRE_HW_VER_1_0_0:
core_info->cre_hw_info->cre_hw = &cre_hw_100;
break;
default:
CAM_ERR(CAM_CRE, "Unsupported version : %u",
core_info->hw_version);
rc = -EINVAL;
break;
}
cre_hw_100.top_reg_offset->base = core_info->cre_hw_info->cre_top_base;
cre_hw_100.bus_rd_reg_offset->base = core_info->cre_hw_info->cre_bus_rd_base;
cre_hw_100.bus_wr_reg_offset->base = core_info->cre_hw_info->cre_bus_wr_base;
return rc;
}
int cam_cre_register_cpas(struct cam_hw_soc_info *soc_info,
struct cam_cre_device_core_info *core_info,
uint32_t hw_idx)
{
struct cam_cpas_register_params cpas_register_params;
int rc;
cpas_register_params.dev = &soc_info->pdev->dev;
memcpy(cpas_register_params.identifier, "cre", sizeof("cre"));
cpas_register_params.cam_cpas_client_cb = NULL;
cpas_register_params.cell_index = hw_idx;
cpas_register_params.userdata = NULL;
rc = cam_cpas_register_client(&cpas_register_params);
if (rc < 0) {
CAM_ERR(CAM_CRE, "failed: %d", rc);
return rc;
}
core_info->cpas_handle = cpas_register_params.client_handle;
return rc;
}
static int cam_cre_component_bind(struct device *dev,
struct device *master_dev, void *data)
{
struct cam_hw_intf *cre_dev_intf = NULL;
struct cam_hw_info *cre_dev = NULL;
const struct of_device_id *match_dev = NULL;
struct cam_cre_device_core_info *core_info = NULL;
struct cam_cre_dev_probe cre_probe;
struct cam_cre_cpas_vote cpas_vote;
struct cam_cre_soc_private *soc_private;
int i;
uint32_t hw_idx;
int rc = 0;
struct platform_device *pdev = to_platform_device(dev);
of_property_read_u32(pdev->dev.of_node,
"cell-index", &hw_idx);
cre_dev_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL);
if (!cre_dev_intf)
return -ENOMEM;
cre_dev_intf->hw_idx = hw_idx;
cre_dev_intf->hw_type = CRE_DEV_CRE;
cre_dev = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL);
if (!cre_dev) {
rc = -ENOMEM;
goto cre_dev_alloc_failed;
}
memset(cre_dev_name, 0, sizeof(cre_dev_name));
snprintf(cre_dev_name, sizeof(cre_dev_name),
"cre%1u", cre_dev_intf->hw_idx);
cre_dev->soc_info.pdev = pdev;
cre_dev->soc_info.dev = &pdev->dev;
cre_dev->soc_info.dev_name = cre_dev_name;
cre_dev_intf->hw_priv = cre_dev;
cre_dev_intf->hw_ops.init = cam_cre_init_hw;
cre_dev_intf->hw_ops.deinit = cam_cre_deinit_hw;
cre_dev_intf->hw_ops.get_hw_caps = cam_cre_get_hw_caps;
cre_dev_intf->hw_ops.process_cmd = cam_cre_process_cmd;
CAM_DBG(CAM_CRE, "type %d index %d",
cre_dev_intf->hw_type,
cre_dev_intf->hw_idx);
if (cre_dev_intf->hw_idx < CRE_DEV_MAX)
cam_cre_dev_list[cre_dev_intf->hw_idx].hw_intf =
cre_dev_intf;
platform_set_drvdata(pdev, cre_dev_intf);
cre_dev->core_info = kzalloc(sizeof(struct cam_cre_device_core_info),
GFP_KERNEL);
if (!cre_dev->core_info) {
rc = -ENOMEM;
goto cre_core_alloc_failed;
}
core_info = (struct cam_cre_device_core_info *)cre_dev->core_info;
core_info->cre_hw_info = &cre_hw_info;
cre_dev->soc_info.soc_private = &cre_soc_info;
match_dev = of_match_device(pdev->dev.driver->of_match_table,
&pdev->dev);
if (!match_dev) {
rc = -EINVAL;
CAM_DBG(CAM_CRE, "No cre hardware info");
goto cre_match_dev_failed;
}
rc = cam_cre_init_soc_resources(&cre_dev->soc_info, cam_cre_irq,
cre_dev);
if (rc < 0) {
CAM_ERR(CAM_CRE, "failed to init_soc");
goto init_soc_failed;
}
core_info->hw_type = CRE_DEV_CRE;
core_info->hw_idx = hw_idx;
rc = cam_cre_register_cpas(&cre_dev->soc_info,
core_info, cre_dev_intf->hw_idx);
if (rc < 0)
goto register_cpas_failed;
rc = cam_cre_enable_soc_resources(&cre_dev->soc_info);
if (rc < 0) {
CAM_ERR(CAM_CRE, "enable soc resorce failed: %d", rc);
goto enable_soc_failed;
}
cpas_vote.ahb_vote.type = CAM_VOTE_ABSOLUTE;
cpas_vote.ahb_vote.vote.level = CAM_SVS_VOTE;
cpas_vote.axi_vote.num_paths = 1;
cpas_vote.axi_vote.axi_path[0].path_data_type =
CAM_AXI_PATH_DATA_CRE_WR_OUT;
cpas_vote.axi_vote.axi_path[0].transac_type =
CAM_AXI_TRANSACTION_WRITE;
cpas_vote.axi_vote.axi_path[0].camnoc_bw =
CAM_CPAS_DEFAULT_AXI_BW;
cpas_vote.axi_vote.axi_path[0].mnoc_ab_bw =
CAM_CPAS_DEFAULT_AXI_BW;
cpas_vote.axi_vote.axi_path[0].mnoc_ib_bw =
CAM_CPAS_DEFAULT_AXI_BW;
cpas_vote.axi_vote.axi_path[0].ddr_ab_bw =
CAM_CPAS_DEFAULT_AXI_BW;
cpas_vote.axi_vote.axi_path[0].ddr_ib_bw =
CAM_CPAS_DEFAULT_AXI_BW;
rc = cam_cpas_start(core_info->cpas_handle,
&cpas_vote.ahb_vote, &cpas_vote.axi_vote);
rc = cam_cre_init_hw_version(&cre_dev->soc_info, cre_dev->core_info);
if (rc)
goto init_hw_failure;
cam_cre_disable_soc_resources(&cre_dev->soc_info);
cam_cpas_stop(core_info->cpas_handle);
cre_dev->hw_state = CAM_HW_STATE_POWER_DOWN;
cam_cre_process_cmd(cre_dev, CRE_HW_PROBE,
&cre_probe, sizeof(cre_probe));
mutex_init(&cre_dev->hw_mutex);
spin_lock_init(&cre_dev->hw_lock);
init_completion(&cre_dev->hw_complete);
CAM_DBG(CAM_CRE, "CRE:%d component bound successfully",
cre_dev_intf->hw_idx);
soc_private = cre_dev->soc_info.soc_private;
cam_cre_dev_list[cre_dev_intf->hw_idx].num_hw_pid =
soc_private->num_pid;
for (i = 0; i < soc_private->num_pid; i++)
cam_cre_dev_list[cre_dev_intf->hw_idx].hw_pid[i] =
soc_private->pid[i];
return rc;
init_hw_failure:
enable_soc_failed:
register_cpas_failed:
init_soc_failed:
cre_match_dev_failed:
kfree(cre_dev->core_info);
cre_core_alloc_failed:
kfree(cre_dev);
cre_dev_alloc_failed:
kfree(cre_dev_intf);
return rc;
}
static void cam_cre_component_unbind(struct device *dev,
struct device *master_dev, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
CAM_DBG(CAM_CRE, "Unbinding component: %s", pdev->name);
}
int cam_cre_hw_init(struct cam_cre_hw_intf_data **cre_hw_intf_data,
uint32_t hw_idx)
{
int rc = 0;
if (cam_cre_dev_list[hw_idx].hw_intf) {
*cre_hw_intf_data = &cam_cre_dev_list[hw_idx];
rc = 0;
} else {
CAM_ERR(CAM_CRE, "inval param");
*cre_hw_intf_data = NULL;
rc = -ENODEV;
}
return rc;
}
const static struct component_ops cam_cre_component_ops = {
.bind = cam_cre_component_bind,
.unbind = cam_cre_component_unbind,
};
int cam_cre_probe(struct platform_device *pdev)
{
int rc = 0;
CAM_DBG(CAM_CRE, "Adding CRE component");
rc = component_add(&pdev->dev, &cam_cre_component_ops);
if (rc)
CAM_ERR(CAM_CRE, "failed to add component rc: %d", rc);
return rc;
}
static const struct of_device_id cam_cre_dt_match[] = {
{
.compatible = "qcom,cre",
.data = &cre_hw_version_reg,
},
{}
};
MODULE_DEVICE_TABLE(of, cam_cre_dt_match);
struct platform_driver cam_cre_driver = {
.probe = cam_cre_probe,
.driver = {
.name = "cre",
.of_match_table = cam_cre_dt_match,
.suppress_bind_attrs = true,
},
};
int cam_cre_init_module(void)
{
return platform_driver_register(&cam_cre_driver);
}
void cam_cre_exit_module(void)
{
platform_driver_unregister(&cam_cre_driver);
}
MODULE_DESCRIPTION("CAM CRE driver");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,135 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef CAM_CRE_DEV_INTF_H
#define CAM_CRE_DEV_INTF_H
#include <media/cam_cre.h>
#include "cam_cre_hw_mgr.h"
#include "cam_cpas_api.h"
#include "cre_top.h"
#define CRE_HW_INIT 0x1
#define CRE_HW_DEINIT 0x2
#define CRE_HW_ACQUIRE 0x3
#define CRE_HW_RELEASE 0x4
#define CRE_HW_START 0x5
#define CRE_HW_STOP 0x6
#define CRE_HW_FLUSH 0x7
#define CRE_HW_PREPARE 0x8
#define CRE_HW_ISR 0x9
#define CRE_HW_PROBE 0xA
#define CRE_HW_CLK_UPDATE 0xB
#define CRE_HW_BW_UPDATE 0xC
#define CRE_HW_RESET 0xD
#define CRE_HW_SET_IRQ_CB 0xE
#define CRE_HW_CLK_DISABLE 0xF
#define CRE_HW_CLK_ENABLE 0x10
#define CRE_HW_DUMP_DEBUG 0x11
#define CRE_HW_REG_SET_UPDATE 0x12
/**
* struct cam_cre_dev_probe
*
* @reserved: Reserved field for future use
*/
struct cam_cre_dev_probe {
uint32_t reserved;
};
/**
* struct cam_cre_dev_init
*
* @core_info: CRE core info
*/
struct cam_cre_dev_init {
struct cam_cre_device_core_info *core_info;
};
/**
* struct cam_cre_dev_clk_update
*
* @clk_rate: Clock rate
*/
struct cam_cre_dev_clk_update {
uint32_t clk_rate;
};
struct cam_cre_dev_reg_set_update {
struct cre_reg_buffer cre_reg_buf;
};
/**
* struct cam_cre_dev_bw_update
*
* @ahb_vote: AHB vote info
* @axi_vote: AXI vote info
* @ahb_vote_valid: Flag for ahb vote
* @axi_vote_valid: Flag for axi vote
*/
struct cam_cre_dev_bw_update {
struct cam_ahb_vote ahb_vote;
struct cam_axi_vote axi_vote;
uint32_t ahb_vote_valid;
uint32_t axi_vote_valid;
};
/**
* struct cam_cre_dev_acquire
*
* @ctx_id: Context id
* @cre_acquire: CRE acquire info
* @bus_wr_ctx: Bus Write context
* @bus_rd_ctx: Bus Read context
*/
struct cam_cre_dev_acquire {
uint32_t ctx_id;
struct cre_top *cre_top;
struct cam_cre_acquire_dev_info *cre_acquire;
struct cre_bus_wr_ctx *bus_wr_ctx;
struct cre_bus_rd_ctx *bus_rd_ctx;
};
/**
* struct cam_cre_dev_release
*
* @ctx_id: Context id
* @bus_wr_ctx: Bus Write context
* @bus_rd_ctx: Bus Read context
*/
struct cam_cre_dev_release {
uint32_t ctx_id;
struct cre_bus_wr_ctx *bus_wr_ctx;
struct cre_bus_rd_ctx *bus_rd_ctx;
};
/**
* struct cam_cre_dev_prepare_req
*
* @hw_mgr: CRE hardware manager
* @packet: Packet
* @prepare_args: Prepare request args
* @ctx_data: Context data
* @frame_process: Frame process command
* @req_idx: Request Index
*/
struct cam_cre_dev_prepare_req {
struct cam_cre_hw_mgr *hw_mgr;
struct cam_packet *packet;
struct cam_hw_prepare_update_args *prepare_args;
struct cam_cre_ctx *ctx_data;
uint32_t req_idx;
};
int cam_cre_top_process(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, uint32_t cmd_id, void *data);
int cam_cre_bus_rd_process(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, uint32_t cmd_id, void *data);
int cam_cre_bus_wr_process(struct cam_cre_hw *cam_cre_hw_info,
int32_t ctx_id, uint32_t cmd_id, void *data);
#endif /* CAM_CRE_DEV_INTF_H */

View File

@@ -0,0 +1,340 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef CAM_CRE_HW_H
#define CAM_CRE_HW_H
#define CRE_HW_VER_1_0_0 0x10000000
#define CRE_DEV_CRE 0
#define CRE_DEV_MAX 1
#define MAX_CRE_RD_CLIENTS 1
#define MAX_CRE_WR_CLIENTS 1
#define CRE_TOP_BASE 0x1
#define CRE_QOS_BASE 0x2
#define CRE_BUS_RD 0x3
#define CRE_BUS_WR 0x4
#define CRE_BASE_MAX 0x5
#define CRE_WAIT_BUS_WR_RUP 0x1
#define CRE_WAIT_BUS_WR_DONE 0x2
#define CRE_WAIT_BUS_RD_DONE 0x3
#define CRE_WAIT_IDLE_IRQ 0x4
struct cam_cre_top_reg {
void *base;
uint32_t offset;
uint32_t hw_version;
uint32_t hw_cap;
uint32_t debug_0;
uint32_t debug_1;
uint32_t debug_cfg;
uint32_t testbus_ctrl;
uint32_t scratch_0;
uint32_t irq_status;
uint32_t irq_mask;
uint32_t irq_clear;
uint32_t irq_set;
uint32_t irq_cmd;
uint32_t reset_cmd;
uint32_t core_clk_cfg_ctrl_0;
uint32_t core_clk_cfg_ctrl_1;
uint32_t top_spare;
};
struct cam_cre_top_reg_val {
uint32_t hw_version;
uint32_t hw_cap;
uint32_t major_mask;
uint32_t major_shift;
uint32_t minor_mask;
uint32_t minor_shift;
uint32_t irq_mask;
uint32_t irq_set;
uint32_t irq_clear;
uint32_t irq_cmd_set;
uint32_t irq_cmd_clear;
uint32_t idle;
uint32_t fe_done;
uint32_t we_done;
uint32_t rst_done;
uint32_t sw_reset_cmd;
uint32_t hw_reset_cmd;
uint32_t core_clk_cfg_ctrl_0;
uint32_t core_clk_cfg_ctrl_1;
uint32_t top_override;
uint32_t we_override;
uint32_t fe_override;
uint32_t ahb_override;
};
struct cam_cre_bus_rd_client_reg {
uint32_t bus_ctrl;
uint32_t spare;
uint32_t cons_violation;
uint32_t cons_violation_status;
uint32_t core_cfg;
uint32_t ccif_meta_data;
uint32_t img_addr;
uint32_t rd_width;
uint32_t rd_height;
uint32_t rd_stride;
uint32_t unpacker_cfg;
uint32_t latency_buf_allocation;
uint32_t misr_cfg_0;
uint32_t misr_cfg_1;
uint32_t misr_rd_val;
uint32_t debug_status_cfg;
uint32_t debug_status_0;
uint32_t debug_status_1;
uint32_t read_buff_cfg;
uint32_t addr_cfg;
};
struct cam_cre_bus_rd_reg {
void *base;
uint32_t offset;
uint32_t hw_version;
uint32_t irq_mask;
uint32_t irq_clear;
uint32_t irq_cmd;
uint32_t irq_status;
uint32_t input_if_cmd;
uint32_t irq_set;
uint32_t misr_reset;
uint32_t security_cfg;
uint32_t iso_cfg;
uint32_t iso_seed;
uint32_t test_bus_ctrl;
uint32_t num_clients;
struct cam_cre_bus_rd_client_reg rd_clients[MAX_CRE_RD_CLIENTS];
};
struct cam_cre_bus_rd_client_reg_val {
uint32_t client_en;
uint32_t ai_en;
uint32_t ai_en_mask;
uint32_t ai_en_shift;
uint32_t pix_pattern;
uint32_t pix_pattern_mask;
uint32_t pix_pattern_shift;
uint32_t stripe_location;
uint32_t stripe_location_mask;
uint32_t stripe_location_shift;
uint32_t img_addr;
uint32_t img_width;
uint32_t img_height;
uint32_t stride;
uint32_t alignment;
uint32_t alignment_mask;
uint32_t alignment_shift;
uint32_t mode;
uint32_t mode_mask;
uint32_t mode_shift;
uint32_t latency_buf_size;
uint32_t latency_buf_size_mask;
uint32_t misr_cfg_en;
uint32_t misr_cfg_en_mask;
uint32_t misr_cfg_samp_mode;
uint32_t misr_cfg_samp_mode_mask;
uint32_t misr_cfg_1;
uint32_t misr_rd_val;
uint32_t x_int;
uint32_t x_int_mask;
uint32_t byte_offset;
uint32_t byte_offset_mask;
uint32_t input_port_id;
uint32_t rm_port_id;
};
struct cam_cre_bus_rd_reg_val {
uint32_t hw_version;
uint32_t cgc_override;
uint32_t irq_mask;
uint32_t irq_status;
uint32_t irq_cmd_set;
uint32_t irq_cmd_clear;
uint32_t rup_done;
uint32_t rd_buf_done;
uint32_t cons_violation;
uint32_t static_prg;
uint32_t static_prg_mask;
uint32_t ica_en;
uint32_t ica_en_mask;
uint32_t go_cmd;
uint32_t go_cmd_mask;
uint32_t irq_set;
uint32_t irq_clear;
uint32_t misr_reset;
uint32_t security_cfg;
uint32_t iso_bpp_select;
uint32_t iso_bpp_select_mask;
uint32_t iso_pattern_select;
uint32_t iso_pattern_select_mask;
uint32_t iso_en;
uint32_t iso_en_mask;
uint32_t iso_seed;
uint32_t bus_ctrl;
uint32_t spare;
uint32_t num_clients;
struct cam_cre_bus_rd_client_reg_val rd_clients[MAX_CRE_RD_CLIENTS];
};
struct cam_cre_bus_wr_client_reg {
uint32_t client_cfg;
uint32_t img_addr;
uint32_t img_cfg_0;
uint32_t img_cfg_1;
uint32_t img_cfg_2;
uint32_t bw_limit;
uint32_t packer_cfg;
uint32_t addr_cfg;
uint32_t debug_status_cfg;
uint32_t debug_status_0;
uint32_t debug_status_1;
};
struct cam_cre_bus_wr_reg {
void *base;
uint32_t offset;
uint32_t hw_version;
uint32_t cgc_override;
uint32_t irq_mask_0;
uint32_t irq_mask_1;
uint32_t irq_clear_0;
uint32_t irq_clear_1;
uint32_t irq_status_0;
uint32_t irq_status_1;
uint32_t irq_cmd;
uint32_t frame_header_cfg_0;
uint32_t local_frame_header_cfg_0;
uint32_t irq_set_0;
uint32_t irq_set_1;
uint32_t iso_cfg;
uint32_t violation_status;
uint32_t image_size_violation_status;
uint32_t perf_count_cfg_0;
uint32_t perf_count_cfg_1;
uint32_t perf_count_cfg_2;
uint32_t perf_count_cfg_3;
uint32_t perf_count_val_0;
uint32_t perf_count_val_1;
uint32_t perf_count_val_2;
uint32_t perf_count_val_3;
uint32_t perf_count_status;
uint32_t misr_cfg_0;
uint32_t misr_cfg_1;
uint32_t misr_rd_sel;
uint32_t misr_reset;
uint32_t misr_val;
uint32_t num_clients;
struct cam_cre_bus_wr_client_reg wr_clients[MAX_CRE_WR_CLIENTS];
};
struct cam_cre_bus_wr_client_reg_val {
uint32_t client_en;
uint32_t client_en_mask;
uint32_t client_en_shift;
uint32_t auto_recovery_en;
uint32_t auto_recovery_en_mask;
uint32_t auto_recovery_en_shift;
uint32_t mode;
uint32_t mode_mask;
uint32_t mode_shift;
uint32_t img_addr;
uint32_t width;
uint32_t width_mask;
uint32_t width_shift;
uint32_t height;
uint32_t height_mask;
uint32_t height_shift;
uint32_t x_init;
uint32_t x_init_mask;
uint32_t stride;
uint32_t stride_mask;
uint32_t format;
uint32_t format_mask;
uint32_t format_shift;
uint32_t alignment;
uint32_t alignment_mask;
uint32_t alignment_shift;
uint32_t bw_limit_en;
uint32_t bw_limit_en_mask;
uint32_t bw_limit_counter;
uint32_t bw_limit_counter_mask;
uint32_t frame_header_addr;
uint32_t output_port_id;
uint32_t wm_port_id;
};
struct cam_cre_bus_wr_reg_val {
uint32_t hw_version;
uint32_t cgc_override;
uint32_t irq_mask_0;
uint32_t irq_set_0;
uint32_t irq_clear_0;
uint32_t irq_status_0;
uint32_t img_size_violation;
uint32_t violation;
uint32_t cons_violation;
uint32_t comp_buf_done;
uint32_t comp_rup_done;
uint32_t irq_mask_1;
uint32_t irq_set_1;
uint32_t irq_clear_1;
uint32_t irq_status_1;
uint32_t irq_cmd_set;
uint32_t irq_cmd_clear;
uint32_t client_buf_done;
uint32_t frame_header_cfg_0;
uint32_t local_frame_header_cfg_0;
uint32_t iso_en;
uint32_t iso_en_mask;
uint32_t violation_status;
uint32_t img_size_violation_status;
uint32_t misr_0_en;
uint32_t misr_0_en_mask;
uint32_t misr_0_samp_mode;
uint32_t misr_0_samp_mode_mask;
uint32_t misr_0_id;
uint32_t misr_0_id_mask;
uint32_t misr_rd_misr_sel;
uint32_t misr_rd_misr_sel_mask;
uint32_t misr_rd_word_sel;
uint32_t misr_rd_word_sel_mask;
uint32_t misr_reset;
uint32_t misr_val;
uint32_t num_clients;
struct cam_cre_bus_wr_client_reg_val wr_clients[MAX_CRE_WR_CLIENTS];
};
struct cam_cre_debug_register {
uint32_t offset;
};
struct cam_cre_hw {
struct cam_cre_top_reg *top_reg_offset;
struct cam_cre_top_reg_val *top_reg_val;
struct cam_cre_bus_rd_reg *bus_rd_reg_offset;
struct cam_cre_bus_rd_reg_val *bus_rd_reg_val;
struct cam_cre_bus_wr_reg *bus_wr_reg_offset;
struct cam_cre_bus_wr_reg_val *bus_wr_reg_val;
struct cam_cre_common *common;
};
struct cre_hw_version_reg {
uint32_t hw_ver;
uint32_t reserved;
};
#endif /* CAM_CRE_HW_H */

View File

@@ -0,0 +1,239 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef CAM_CRE_HW_100_H
#define CAM_CRE_HW_100_H
#include "cre_hw.h"
#define CRE_BUS_RD_TYPE 0x1
#define CRE_BUS_WR_TYPE 0x2
static struct cam_cre_top_reg top_reg = {
.hw_version = 0xFA000,
.hw_cap = 0xFA004,
.debug_0 = 0xFA080,
.debug_1 = 0xFA084,
.debug_cfg = 0xFA0DC,
.testbus_ctrl = 0xFA1F4,
.scratch_0 = 0xFA1F8,
.irq_status = 0xFA00C,
.irq_mask = 0xFA010,
.irq_clear = 0xFA014,
.irq_set = 0xFA018,
.irq_cmd = 0xFA01C,
.reset_cmd = 0xFA008,
.core_clk_cfg_ctrl_0 = 0xFA020,
.core_clk_cfg_ctrl_1 = 0xFA024,
.top_spare = 0xFA1FC,
};
struct cam_cre_top_reg_val top_reg_value = {
.hw_version = 0x10000000,
.hw_cap = 0x4000,
.irq_mask = 0xf,
.irq_clear = 0xf,
.irq_set = 0xf,
.irq_cmd_set = 0xf,
.irq_cmd_clear = 0xf,
.idle = 0x8,
.fe_done = 0x4,
.we_done = 0x2,
.rst_done = 0x1,
.sw_reset_cmd = 0x2,
.hw_reset_cmd = 0x1,
};
struct cam_cre_bus_rd_reg bus_rd_reg = {
.hw_version = 0xFA400,
.irq_mask = 0xFA404,
.irq_clear = 0xFA408,
.irq_cmd = 0xFA40C,
.irq_status = 0xFA410,
.input_if_cmd = 0xFA414,
.irq_set = 0xFA418,
.misr_reset = 0xFA41C,
.security_cfg = 0xFA420,
.iso_cfg = 0xFA424,
.iso_seed = 0xFA428,
.test_bus_ctrl = 0xFA42C,
.num_clients = 1,
.rd_clients[0] = {
.core_cfg = 0xFA450,
.ccif_meta_data = 0xFA454,
.img_addr = 0xFA458,
.rd_width = 0xFA45C,
.rd_height = 0xFA460,
.rd_stride = 0xFA464,
.unpacker_cfg = 0xFA468,
.latency_buf_allocation = 0xFA47C,
.misr_cfg_0 = 0xFA484,
.misr_cfg_1 = 0xFA488,
.misr_rd_val = 0xFA48C,
.debug_status_cfg = 0xFA490,
.debug_status_0 = 0xFA494,
.debug_status_1 = 0xFA498,
.read_buff_cfg = 0xFA4A0,
.addr_cfg = 0xFA4A4,
.spare = 0xFA430,
.cons_violation = 0xFA434,
},
};
struct cam_cre_bus_wr_reg_val bus_wr_reg_value = {
.hw_version = 0x30000000,
.cgc_override = 0x1,
.irq_mask_0 = 0xd0000101,
.irq_set_0 = 0xd0000101,
.irq_clear_0 = 0xd0000101,
.img_size_violation = 0x80000000,
.violation = 0x40000000,
.cons_violation = 0x10000000,
.comp_buf_done = 0x100,
.comp_rup_done = 0x1,
.irq_mask_1 = 0x1,
.irq_set_1 = 0x1,
.irq_clear_1 = 0x1,
.irq_status_1 = 0x1,
.irq_cmd_set = 0x10,
.irq_cmd_clear = 0x1,
.client_buf_done = 0x1,
.iso_en = 0x1,
.iso_en_mask = 0x1,
.misr_0_en = 0x1,
.misr_0_en_mask = 0x1,
.misr_0_samp_mode = 0x1,
.misr_0_samp_mode_mask = 0x10000,
.misr_0_id_mask = 0xff,
.misr_rd_misr_sel_mask = 0xf,
.misr_rd_word_sel_mask = 0x70,
.num_clients = 1,
.wr_clients[0] = {
.client_en = 0x1,
.client_en_mask = 0x1,
.client_en_shift = 0x0,
.auto_recovery_en = 0x1,
.auto_recovery_en_mask = 0x1,
.auto_recovery_en_shift = 0x4,
.mode_mask = 0x3,
.mode_shift = 0x10,
.width_mask = 0xffff,
.width_shift = 0x0,
.height_mask = 0xffff,
.height_shift = 0x10,
.x_init_mask = 0xff,
.stride_mask = 0xffffff,
.format_mask = 0xf,
.format_shift = 0x0,
.alignment_mask = 0x1,
.alignment_shift = 0x5,
.bw_limit_en = 0x1,
.bw_limit_en_mask = 0x1,
.bw_limit_counter_mask = 0x1fe,
.output_port_id = 1,
.wm_port_id = 1,
},
};
struct cam_cre_bus_rd_reg_val bus_rd_reg_value = {
.hw_version = 0x30000000,
.irq_mask = 0x7,
.rd_buf_done = 0x4,
.rup_done = 0x2,
.cons_violation = 0x1,
.irq_cmd_set = 0x10,
.irq_cmd_clear = 0x1,
.static_prg = 0x8,
.static_prg_mask = 0x8,
.ica_en = 0x1,
.ica_en_mask = 0x2,
.go_cmd = 0x1,
.go_cmd_mask = 0x1,
.irq_set = 0x7,
.irq_clear = 0x7,
.misr_reset = 0x1,
.security_cfg = 0x1,
.iso_bpp_select_mask = 0x60,
.iso_pattern_select_mask = 0x6,
.iso_en = 0x1,
.iso_en_mask = 0x1,
.num_clients = 1,
.rd_clients[0] = {
.client_en = 0x1,
.ai_en = 0x1,
.ai_en_mask = 0x1000,
.ai_en_shift = 0xc,
.pix_pattern_mask = 0xfc,
.pix_pattern_shift = 0x2,
.stripe_location_mask = 0x3,
.stripe_location_shift = 0x0,
.alignment_mask = 0x1,
.alignment_shift = 0x5,
.mode_mask = 0x1f,
.mode_shift = 0x0,
.latency_buf_size_mask = 0xffff,
.misr_cfg_en_mask = 0x4,
.misr_cfg_samp_mode_mask = 0x3,
.x_int_mask = 0xffff,
.byte_offset_mask = 0xff,
.rm_port_id = 0x0,
},
};
struct cam_cre_bus_wr_reg bus_wr_reg = {
.hw_version = 0xFA700,
.cgc_override = 0xFA708,
.irq_mask_0 = 0xFA718,
.irq_mask_1 = 0xFA71C,
.irq_clear_0 = 0xFA720,
.irq_clear_1 = 0xFA724,
.irq_status_0 = 0xFA728,
.irq_status_1 = 0xFA72C,
.irq_cmd = 0xFA730,
.frame_header_cfg_0 = 0x0,
.local_frame_header_cfg_0 = 0xFA74C,
.irq_set_0 = 0xFA750,
.irq_set_1 = 0xFA754,
.iso_cfg = 0xFA75C,
.violation_status = 0xFA764,
.image_size_violation_status = 0xFA770,
.perf_count_cfg_0 = 0xFA774,
.perf_count_cfg_1 = 0xFA778,
.perf_count_cfg_2 = 0xFA77C,
.perf_count_cfg_3 = 0xFA780,
.perf_count_val_0 = 0xFA794,
.perf_count_val_1 = 0xFA798,
.perf_count_val_2 = 0xFA79C,
.perf_count_val_3 = 0xFA7A0,
.perf_count_status = 0xFA7B4,
.misr_cfg_0 = 0xFA7B8,
.misr_cfg_1 = 0xFA7BC,
.misr_rd_sel = 0xFA7C8,
.misr_reset = 0xFA7CC,
.misr_val = 0xFA7D0,
.wr_clients[0] = {
.client_cfg = 0xFA900,
.img_addr = 0xFA904,
.img_cfg_0 = 0xFA90C,
.img_cfg_1 = 0xFA910,
.img_cfg_2 = 0xFA914,
.bw_limit = 0xFA918,
.packer_cfg = 0xFA91C,
.addr_cfg = 0xFA970,
.debug_status_cfg = 0xFA984,
.debug_status_0 = 0xFA988,
.debug_status_1 = 0xFA98C,
},
};
static struct cam_cre_hw cre_hw_100 = {
.top_reg_offset = &top_reg,
.top_reg_val = &top_reg_value,
.bus_wr_reg_offset = &bus_wr_reg,
.bus_wr_reg_val = &bus_wr_reg_value,
.bus_rd_reg_offset = &bus_rd_reg,
.bus_rd_reg_val = &bus_rd_reg_value,
};
#endif // CAM_CRE_HW_100_H

View File

@@ -0,0 +1,83 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#include <linux/io.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <media/cam_defs.h>
#include <media/cam_cre.h>
#include "cre_soc.h"
#include "cam_soc_util.h"
#include "cam_debug_util.h"
int cam_cre_init_soc_resources(struct cam_hw_soc_info *soc_info,
irq_handler_t cre_irq_handler, void *irq_data)
{
int rc;
rc = cam_soc_util_get_dt_properties(soc_info);
if (rc)
return rc;
rc = cam_soc_util_request_platform_resource(soc_info,
cre_irq_handler,
irq_data);
if (rc)
CAM_ERR(CAM_CRE, "init soc failed %d", rc);
return rc;
}
int cam_cre_enable_soc_resources(struct cam_hw_soc_info *soc_info)
{
int rc;
rc = cam_soc_util_enable_platform_resource(soc_info, true,
CAM_SVS_VOTE, true);
if (rc)
CAM_ERR(CAM_CRE, "enable platform failed %d", rc);
return rc;
}
int cam_cre_disable_soc_resources(struct cam_hw_soc_info *soc_info)
{
int rc;
rc = cam_soc_util_disable_platform_resource(soc_info, true, true);
if (rc)
CAM_ERR(CAM_CRE, "disable platform failed %d", rc);
return rc;
}
int cam_cre_update_clk_rate(struct cam_hw_soc_info *soc_info,
uint32_t clk_rate)
{
int32_t src_clk_idx;
if (!soc_info) {
CAM_ERR(CAM_CRE, "Invalid soc info");
return -EINVAL;
}
src_clk_idx = soc_info->src_clk_idx;
CAM_DBG(CAM_CRE, "clk_rate = %u src_clk_index = %d",
clk_rate, src_clk_idx);
if ((soc_info->clk_level_valid[CAM_TURBO_VOTE] == true) &&
(soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx] != 0) &&
(clk_rate > soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx])) {
CAM_DBG(CAM_CRE, "clk_rate %d greater than max, reset to %d",
clk_rate,
soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx]);
clk_rate = soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx];
}
CAM_DBG(CAM_CRE, "clk_rate = %u src_clk_index = %d",
clk_rate, src_clk_idx);
return cam_soc_util_set_src_clk_rate(soc_info, clk_rate);
}

View File

@@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef _CAM_CRE_SOC_H_
#define _CAM_CRE_SOC_H_
#include "cam_soc_util.h"
#define CAM_CRE_HW_MAX_NUM_PID 2
/**
* struct cam_cre_soc_private
*
* @num_pid: CRE number of pids
* @pid: CRE pid value list
*/
struct cam_cre_soc_private {
uint32_t num_pid;
uint32_t pid[CAM_CRE_HW_MAX_NUM_PID];
};
int cam_cre_init_soc_resources(struct cam_hw_soc_info *soc_info,
irq_handler_t cre_irq_handler, void *irq_data);
int cam_cre_enable_soc_resources(struct cam_hw_soc_info *soc_info);
int cam_cre_disable_soc_resources(struct cam_hw_soc_info *soc_info);
int cam_cre_update_clk_rate(struct cam_hw_soc_info *soc_info,
uint32_t clk_rate);
#endif /* _CAM_CRE_SOC_H_*/

View File

@@ -0,0 +1,49 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef CAM_CRE_HW_INTF_H
#define CAM_CRE_HW_INTF_H
#include "cam_cpas_api.h"
#define CAM_CRE_DEV_PER_TYPE_MAX 1
#define CAM_CRE_CMD_BUF_MAX_SIZE 128
#define CAM_CRE_MSG_BUF_MAX_SIZE CAM_CRE_CMD_BUF_MAX_SIZE
#define CRE_VOTE 640000000
#define CAM_CRE_HW_DUMP_TAG_MAX_LEN 32
#define CAM_CRE_HW_DUMP_NUM_WORDS 5
struct cam_cre_set_irq_cb {
int32_t (*cre_hw_mgr_cb)(void *irq_data,
int32_t result_size, void *data);
void *data;
uint32_t b_set_cb;
};
struct cam_cre_hw_dump_args {
uint64_t request_id;
uintptr_t cpu_addr;
size_t offset;
size_t buf_len;
};
struct cam_cre_hw_dump_header {
uint8_t tag[CAM_CRE_HW_DUMP_TAG_MAX_LEN];
uint64_t size;
uint32_t word_size;
};
enum cam_cre_cmd_type {
CAM_CRE_CMD_CFG,
CAM_CRE_CMD_SET_IRQ_CB,
CAM_CRE_CMD_HW_DUMP,
CAM_CRE_CMD_RESET_HW,
CAM_CRE_CMD_MAX,
};
#endif

View File

@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef CAM_CRE_HW_MGR_INTF_H
#define CAM_CRE_HW_MGR_INTF_H
#include <linux/of.h>
#include <media/cam_cre.h>
#include <media/cam_defs.h>
#define CAM_CRE_CTX_MAX 16
int cam_cre_hw_mgr_init(struct device_node *of_node,
uint64_t *hw_mgr_hdl, int *iommu_hdl);
#endif /* CAM_CRE_HW_MGR_INTF_H */

View File

@@ -0,0 +1,315 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#include <linux/of.h>
#include <linux/debugfs.h>
#include <linux/videodev2.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/iopoll.h>
#include <linux/completion.h>
#include <media/cam_cre.h>
#include "cam_io_util.h"
#include "cam_hw.h"
#include "cam_hw_intf.h"
#include "cre_core.h"
#include "cre_soc.h"
#include "cam_soc_util.h"
#include "cam_io_util.h"
#include "cam_cpas_api.h"
#include "cam_debug_util.h"
#include "cre_hw.h"
#include "cre_dev_intf.h"
#include "cre_top.h"
static struct cre_top cre_top_info;
static int cam_cre_top_reset(struct cam_cre_hw *cre_hw_info,
int32_t ctx_id, void *data)
{
int rc = 0;
struct cam_cre_top_reg *top_reg;
struct cam_cre_top_reg_val *top_reg_val;
uint32_t irq_mask, irq_status;
unsigned long flags;
if (!cre_hw_info) {
CAM_ERR(CAM_CRE, "Invalid cre_hw_info");
return -EINVAL;
}
top_reg = cre_hw_info->top_reg_offset;
top_reg_val = cre_hw_info->top_reg_val;
mutex_lock(&cre_top_info.cre_hw_mutex);
reinit_completion(&cre_top_info.reset_complete);
reinit_completion(&cre_top_info.idle_done);
/* enable interrupt mask */
cam_io_w_mb(top_reg_val->irq_mask,
cre_hw_info->top_reg_offset->base + top_reg->irq_mask);
/* CRE SW RESET */
cam_io_w_mb(top_reg_val->sw_reset_cmd,
cre_hw_info->top_reg_offset->base + top_reg->reset_cmd);
rc = wait_for_completion_timeout(
&cre_top_info.reset_complete,
msecs_to_jiffies(60));
if (!rc || rc < 0) {
spin_lock_irqsave(&cre_top_info.hw_lock, flags);
if (!completion_done(&cre_top_info.reset_complete)) {
CAM_DBG(CAM_CRE,
"IRQ delayed, checking the status registers");
irq_mask = cam_io_r_mb(cre_hw_info->top_reg_offset->base +
top_reg->irq_mask);
irq_status = cam_io_r_mb(cre_hw_info->top_reg_offset->base +
top_reg->irq_status);
if (irq_status & top_reg_val->rst_done) {
CAM_DBG(CAM_CRE, "cre reset done");
cam_io_w_mb(irq_status,
top_reg->base + top_reg->irq_clear);
cam_io_w_mb(top_reg_val->irq_cmd_clear,
top_reg->base + top_reg->irq_cmd);
} else {
CAM_ERR(CAM_CRE,
"irq mask 0x%x irq status 0x%x",
irq_mask, irq_status);
rc = -ETIMEDOUT;
}
} else {
rc = 0;
}
spin_unlock_irqrestore(&cre_top_info.hw_lock, flags);
} else {
rc = 0;
}
/* enable interrupt mask */
cam_io_w_mb(top_reg_val->irq_mask,
cre_hw_info->top_reg_offset->base + top_reg->irq_mask);
mutex_unlock(&cre_top_info.cre_hw_mutex);
return rc;
}
static int cam_cre_top_release(struct cam_cre_hw *cre_hw_info,
int32_t ctx_id, void *data)
{
int rc = 0;
if (ctx_id < 0) {
CAM_ERR(CAM_CRE, "Invalid data: %d", ctx_id);
return -EINVAL;
}
cre_top_info.top_ctx[ctx_id].cre_acquire = NULL;
return rc;
}
static int cam_cre_top_acquire(struct cam_cre_hw *cre_hw_info,
int32_t ctx_id, void *data)
{
int rc = 0;
struct cam_cre_dev_acquire *cre_dev_acquire = data;
if (ctx_id < 0 || !data) {
CAM_ERR(CAM_CRE, "Invalid data: %d %x", ctx_id, data);
return -EINVAL;
}
cre_top_info.top_ctx[ctx_id].cre_acquire = cre_dev_acquire->cre_acquire;
cre_dev_acquire->cre_top = &cre_top_info;
return rc;
}
static int cam_cre_top_init(struct cam_cre_hw *cre_hw_info,
int32_t ctx_id, void *data)
{
int rc = 0;
struct cam_cre_top_reg *top_reg;
struct cam_cre_top_reg_val *top_reg_val;
struct cam_cre_dev_init *dev_init = data;
uint32_t irq_mask, irq_status;
unsigned long flags;
if (!cre_hw_info) {
CAM_ERR(CAM_CRE, "Invalid cre_hw_info");
return -EINVAL;
}
top_reg = cre_hw_info->top_reg_offset;
top_reg_val = cre_hw_info->top_reg_val;
top_reg->base = dev_init->core_info->cre_hw_info->cre_top_base;
mutex_init(&cre_top_info.cre_hw_mutex);
/* CRE SW RESET */
init_completion(&cre_top_info.reset_complete);
init_completion(&cre_top_info.idle_done);
/* enable interrupt mask */
cam_io_w_mb(top_reg_val->irq_mask,
cre_hw_info->top_reg_offset->base + top_reg->irq_mask);
cam_io_w_mb(top_reg_val->sw_reset_cmd,
cre_hw_info->top_reg_offset->base + top_reg->reset_cmd);
rc = wait_for_completion_timeout(
&cre_top_info.reset_complete,
msecs_to_jiffies(60));
if (!rc || rc < 0) {
spin_lock_irqsave(&cre_top_info.hw_lock, flags);
if (!completion_done(&cre_top_info.reset_complete)) {
CAM_DBG(CAM_CRE,
"IRQ delayed, checking the status registers");
irq_mask = cam_io_r_mb(cre_hw_info->top_reg_offset->base +
top_reg->irq_mask);
irq_status = cam_io_r_mb(cre_hw_info->top_reg_offset->base +
top_reg->irq_status);
if (irq_status & top_reg_val->rst_done) {
CAM_DBG(CAM_CRE, "cre reset done");
cam_io_w_mb(irq_status,
top_reg->base + top_reg->irq_clear);
cam_io_w_mb(top_reg_val->irq_cmd_clear,
top_reg->base + top_reg->irq_cmd);
} else {
CAM_ERR(CAM_CRE,
"irq mask 0x%x irq status 0x%x",
irq_mask, irq_status);
rc = -ETIMEDOUT;
}
} else {
CAM_DBG(CAM_CRE, "reset done");
rc = 0;
}
spin_unlock_irqrestore(&cre_top_info.hw_lock, flags);
} else {
rc = 0;
}
/* enable interrupt mask */
cam_io_w_mb(top_reg_val->irq_mask,
cre_hw_info->top_reg_offset->base + top_reg->irq_mask);
return rc;
}
static int cam_cre_top_probe(struct cam_cre_hw *cre_hw_info,
int32_t ctx_id, void *data)
{
int rc = 0;
if (!cre_hw_info) {
CAM_ERR(CAM_CRE, "Invalid cre_hw_info");
return -EINVAL;
}
cre_top_info.cre_hw_info = cre_hw_info;
spin_lock_init(&cre_top_info.hw_lock);
return rc;
}
static int cam_cre_top_isr(struct cam_cre_hw *cre_hw_info,
int32_t ctx_id, void *data)
{
int rc = 0;
uint32_t irq_status;
struct cam_cre_top_reg *top_reg;
struct cam_cre_top_reg_val *top_reg_val;
struct cam_cre_irq_data *irq_data = data;
if (!cre_hw_info) {
CAM_ERR(CAM_CRE, "Invalid cre_hw_info");
return -EINVAL;
}
top_reg = cre_hw_info->top_reg_offset;
top_reg_val = cre_hw_info->top_reg_val;
spin_lock(&cre_top_info.hw_lock);
/* Read and Clear Top Interrupt status */
irq_status = cam_io_r_mb(top_reg->base + top_reg->irq_status);
cam_io_w_mb(irq_status,
top_reg->base + top_reg->irq_clear);
cam_io_w_mb(top_reg_val->irq_cmd_clear,
top_reg->base + top_reg->irq_cmd);
if (irq_status & top_reg_val->rst_done) {
CAM_DBG(CAM_CRE, "cre reset done");
complete(&cre_top_info.reset_complete);
}
if (irq_status & top_reg_val->idle) {
CAM_DBG(CAM_CRE, "cre idle IRQ, can configure new settings");
complete(&cre_top_info.idle_done);
}
if (irq_status & top_reg_val->we_done)
CAM_DBG(CAM_CRE, "Received Write Engine IRQ");
if (irq_status & top_reg_val->fe_done)
CAM_DBG(CAM_CRE, "Received Fetch Engine IRQ");
irq_data->top_irq_status = irq_status;
spin_unlock(&cre_top_info.hw_lock);
return rc;
}
int cam_cre_top_process(struct cam_cre_hw *cre_hw_info,
int32_t ctx_id, uint32_t cmd_id, void *data)
{
int rc = 0;
switch (cmd_id) {
case CRE_HW_PROBE:
CAM_DBG(CAM_CRE, "CRE_HW_PROBE: E");
rc = cam_cre_top_probe(cre_hw_info, ctx_id, data);
CAM_DBG(CAM_CRE, "CRE_HW_PROBE: X");
break;
case CRE_HW_INIT:
CAM_DBG(CAM_CRE, "CRE_HW_INIT: E");
rc = cam_cre_top_init(cre_hw_info, ctx_id, data);
CAM_DBG(CAM_CRE, "CRE_HW_INIT: X");
break;
case CRE_HW_DEINIT:
break;
case CRE_HW_ACQUIRE:
CAM_DBG(CAM_CRE, "CRE_HW_ACQUIRE: E");
rc = cam_cre_top_acquire(cre_hw_info, ctx_id, data);
CAM_DBG(CAM_CRE, "CRE_HW_ACQUIRE: X");
break;
case CRE_HW_PREPARE:
break;
case CRE_HW_RELEASE:
rc = cam_cre_top_release(cre_hw_info, ctx_id, data);
break;
case CRE_HW_REG_SET_UPDATE:
break;
case CRE_HW_START:
break;
case CRE_HW_STOP:
break;
case CRE_HW_FLUSH:
break;
case CRE_HW_ISR:
rc = cam_cre_top_isr(cre_hw_info, ctx_id, data);
break;
case CRE_HW_RESET:
rc = cam_cre_top_reset(cre_hw_info, ctx_id, 0);
break;
default:
break;
}
return rc;
}

View File

@@ -0,0 +1,46 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef CRE_TOP_H
#define CRE_TOP_H
#include <linux/types.h>
#include <linux/completion.h>
#include <media/cam_cre.h>
#include "cre_hw.h"
#include "cam_hw_mgr_intf.h"
#include "cam_hw_intf.h"
#include "cam_soc_util.h"
#include "cam_context.h"
#include "cam_cre_context.h"
#include "cam_cre_hw_mgr.h"
/**
* struct cre_top_ctx
*
* @cre_acquire: CRE acquire info
*/
struct cre_top_ctx {
struct cam_cre_acquire_dev_info *cre_acquire;
};
/**
* struct cre_top
*
* @cre_hw_info: CRE hardware info
* @top_ctx: CRE top context
* @reset_complete: Reset complete flag
* @cre_mutex: CRE hardware mutex
* @hw_lock: CRE hardware spinlock
*/
struct cre_top {
struct cam_cre_hw *cre_hw_info;
struct cre_top_ctx top_ctx[CAM_CRE_CTX_MAX];
struct completion reset_complete;
struct completion idle_done;
struct mutex cre_hw_mutex;
spinlock_t hw_lock;
};
#endif /* CRE_TOP_H */

View File

@@ -40,6 +40,7 @@
#define CAM_OPE (1 << 28)
#define CAM_IO_ACCESS (1 << 29)
#define CAM_SFE (1 << 30)
#define CAM_CRE (1 << 31)
/* Log level types */
#define CAM_TYPE_TRACE (1 << 0)

View File

@@ -39,6 +39,9 @@
#include "jpeg_enc_core.h"
#include "cam_jpeg_dev.h"
#include "cre_core.h"
#include "cam_cre_dev.h"
#include "cam_fd_hw_intf.h"
#include "cam_fd_dev.h"
@@ -131,6 +134,11 @@ static const struct camera_submodule_component camera_ope[] = {
#endif
};
static const struct camera_submodule_component camera_cre[] = {
#ifdef CONFIG_SPECTRA_CRE
{&cam_cre_init_module, &cam_cre_exit_module},
#endif
};
static const struct camera_submodule_component camera_jpeg[] = {
#ifdef CONFIG_SPECTRA_JPEG
{&cam_jpeg_enc_init_module, &cam_jpeg_enc_exit_module},
@@ -207,6 +215,11 @@ static const struct camera_submodule submodule_table[] = {
.num_component = ARRAY_SIZE(camera_lrme),
.component = camera_lrme,
},
{
.name = "Camera CRE",
.num_component = ARRAY_SIZE(camera_cre),
.component = camera_cre,
},
{
.name = "Camera CUSTOM",
.num_component = ARRAY_SIZE(camera_custom),

View File

@@ -51,6 +51,9 @@ extern struct platform_driver cam_icp_driver;
extern struct platform_driver cam_ope_driver;
extern struct platform_driver cam_ope_subdev_driver;
#endif
#ifdef CONFIG_SPECTRA_CRE
extern struct platform_driver cam_cre_driver;
#endif
#ifdef CONFIG_SPECTRA_JPEG
extern struct platform_driver cam_jpeg_enc_driver;
extern struct platform_driver cam_jpeg_dma_driver;
@@ -123,6 +126,9 @@ static struct platform_driver *const cam_component_drivers[] = {
&cam_jpeg_dma_driver,
&jpeg_driver,
#endif
#ifdef CONFIG_SPECTRA_CRE
&cam_cre_driver,
#endif
#ifdef CONFIG_SPECTRA_FD
&cam_fd_hw_driver,
&cam_fd_driver,

View File

@@ -13,7 +13,7 @@
/* CRE HW TYPE */
#define CAM_CRE_HW_TYPE_CRE 0x1
#define CAM_CRE_HW_TYPE_MAX 0x2
#define CAM_CRE_HW_TYPE_MAX 0x1
/* packet opcode types */
#define CAM_CRE_OPCODE_CONFIG 0x1
@@ -26,50 +26,17 @@
#define CAM_CRE_OUTPUT_IMAGE 0x1
#define CAM_CRE_OUTPUT_IMAGES_MAX (CAM_CRE_OUTPUT_IMAGE + 1)
#define CAM_CRE_MAX_PLANES 0x2
#define CRE_MAX_BATCH_SIZE 0x10
/* definitions needed for cre aquire device */
#define CAM_CRE_DEV_TYPE_NRT 0x1
#define CAM_CRE_DEV_TYPE_RT 0x2
#define CAM_CRE_DEV_TYPE_MAX 0x3
#define CAM_CRE_CMD_META_GENERIC_BLOB 0x1
/* Clock blob */
#define CAM_CRE_CMD_GENERIC_BLOB_CLK 0x1
#define CAM_CRE_MAX_PLANES 0x2
#define CRE_MAX_BATCH_SIZE 0x10
#define CAM_CRE_MAX_IO_BUFS 0x3
#define CAM_CRE_ACQUIRE_FLAG_SECURE 0x1
/**
* struct cam_cre_io_buf_info - CRE IO buffers meta
*
* @direction: Direction of a buffer of a port(Input/Output)
* @res_id: Resource ID
* @num_planes: Number of planes
* @width: Height of a plane buffer
* @height: Height of a plane buffer
* @stride: Plane stride
* @packer_format: Format
* @alignment: Alignment
* @reserved: Reserved field 0
* @reserved_1: Reserved field 1
* @reserved_2: Reserved field 2
*
*/
struct cam_cre_io_buf_info {
__u32 direction;
__u32 res_id;
__u32 num_planes;
__u32 width;
__u32 height;
__u32 stride;
__u32 fence;
__u32 packer_format;
__u32 alignment;
__u32 reserved;
__u32 reserved_1;
__u32 reserved_2;
};
#define CAM_CRE_CMD_GENERIC_BLOB_CLK_V2 0x1
/**
* struct cam_cre_hw_ver - Device information for particular hw type
@@ -77,13 +44,9 @@ struct cam_cre_io_buf_info {
* This is used to get device version info of CRE
* from hardware and use this info in CAM_QUERY_CAP IOCTL
*
* @hw_type: Hardware type
* @reserved: Reserved field
* @hw_ver: Major, minor and incr values of a device version
*/
struct cam_cre_hw_ver {
__u32 hw_type;
__u32 reserved;
struct cam_hw_version hw_ver;
};
@@ -103,29 +66,29 @@ struct cam_cre_query_cap_cmd {
};
/**
* struct cam_cre_clk_bw_request
* struct cam_cre_io_buf_info - CRE IO buffers meta
*
* @budget_ns: Time required to process frame
* @frame_cycles: Frame cycles needed to process the frame
* @rt_flag: Flag to indicate real time stream
* @uncompressed_bw: Bandwidth required to process frame
* @compressed_bw: Compressed bandwidth to process frame
* @direction: Direction of a buffer of a port(Input/Output)
* @res_id: Resource ID
* @num_planes: Number of planes
* @width: Height of a plane buffer
* @height: Height of a plane buffer
* @stride: Plane stride
* @format: unpacker format for FE, packer format for WE
* @alignment: Alignment
* @reserved: Reserved field 0
* @reserved_1: Reserved field 1
* @reserved_2: Reserved field 2
* @num_path: Number of AXI Paths
*/
struct cam_cre_clk_bw_request {
__u64 budget_ns;
__u32 frame_cycles;
__u32 rt_flag;
__u64 uncompressed_bw;
__u64 compressed_bw;
__u32 num_paths;
struct cam_cre_io_buf_info {
__u32 direction;
__u32 res_id;
__u32 num_planes;
__u32 width;
__u32 height;
__u32 stride;
__u32 fence;
__u32 format;
__u32 alignment;
__u32 reserved;
__u32 reserved_1;
__u32 reserved_2;
struct cam_axi_per_path_bw_vote axi_path[1];
};
/**
@@ -133,28 +96,41 @@ struct cam_cre_clk_bw_request {
*
* @dev_type: NRT/RT Acquire
* @dev_name: Device name (CRE)
* @acquire_flag: Tells if CRE will process the secure buff or not.
* @secure_mode: Tells if CRE will process the secure buff or not.
* @batch_size: Batch size
* @num_in_res: Number of In resources
* @num_out_res: Number of Out resources
* @reserved: Reserved field 0
* @reserved_1: Reserved field 1
* @reserved_2: Reserved field 2
* @in_res: In resource info
* @in_res: Out resource info
*/
struct cam_cre_acquire_dev_info {
__u32 dev_type;
char dev_name[CAM_CRE_DEV_NAME_SIZE];
__u32 acquire_flag;
__u32 dev_type;
__u32 secure_mode;
__u32 batch_size;
__u32 num_in_res;
__u32 num_out_res;
__u32 reserved;
__u32 reserved_1;
__u32 reserved_2;
struct cam_cre_io_buf_info in_res[CAM_CRE_INPUT_IMAGES_MAX];
struct cam_cre_io_buf_info out_res[CAM_CRE_OUTPUT_IMAGES_MAX];
}__attribute__((__packed__));
/**
* struct cre_clk_bw_request_v2
*
* @budget_ns: Time required to process frame
* @frame_cycles: Frame cycles needed to process the frame
* @rt_flag: Flag to indicate real time stream
* @reserved: Reserved field 0
* @num_path: Number of AXI Paths
*/
struct cre_clk_bw_request_v2 {
__u64 budget_ns;
__u32 frame_cycles;
__u32 rt_flag;
__u32 reserved;
__u32 num_paths;
struct cam_axi_per_path_bw_vote axi_path[1];
};
#endif /* __UAPI_CAM_CRE_H__ */