msm: camera: Add Face Detection driver
Enable FD (Face Detection) driver to perform FD HW
functionality. This driver enables HW to process a
given frame to detect faces present in the fov.
Change-Id: I5c1b985d5f65f5613dcd39fb3b51169ad3442b76
Signed-off-by: Pavan Kumar Chilamkurthi <pchilamk@codeaurora.org>
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/Makefile b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/Makefile
new file mode 100644
index 0000000..c3e706d
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/Makefile
@@ -0,0 +1,13 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_core
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sync
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_smmu
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cdm
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_fd
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_fd/fd_hw_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw
+ccflags-y += -Idrivers/media/platform/msm/camera
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_fd_hw_dev.o cam_fd_hw_core.o cam_fd_hw_soc.o
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.c b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.c
new file mode 100644
index 0000000..51fcdcaa
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.c
@@ -0,0 +1,1125 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "cam_fd_hw_core.h"
+#include "cam_fd_hw_soc.h"
+
+#define CAM_FD_REG_VAL_PAIR_SIZE 256
+
+static uint32_t cam_fd_cdm_write_reg_val_pair(uint32_t *buffer,
+ uint32_t index, uint32_t reg_offset, uint32_t reg_value)
+{
+ buffer[index++] = reg_offset;
+ buffer[index++] = reg_value;
+
+ CAM_DBG(CAM_FD, "FD_CDM_CMD: Base[FD_CORE] Offset[0x%8x] Value[0x%8x]",
+ reg_offset, reg_value);
+
+ return index;
+}
+
+static void cam_fd_hw_util_cdm_callback(uint32_t handle, void *userdata,
+ enum cam_cdm_cb_status status, uint32_t cookie)
+{
+ CAM_DBG(CAM_FD, "CDM hdl=%x, udata=%pK, status=%d, cookie=%d",
+ handle, userdata, status, cookie);
+}
+
+static void cam_fd_hw_util_enable_power_on_settings(struct cam_hw_info *fd_hw)
+{
+ struct cam_hw_soc_info *soc_info = &fd_hw->soc_info;
+ struct cam_fd_hw_static_info *hw_static_info =
+ ((struct cam_fd_core *)fd_hw->core_info)->hw_static_info;
+
+ if (hw_static_info->enable_errata_wa.single_irq_only == false) {
+ /* Enable IRQs here */
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.irq_mask,
+ hw_static_info->irq_mask);
+ }
+
+ /* QoS settings */
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.vbif_req_priority,
+ hw_static_info->qos_priority);
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.vbif_priority_level,
+ hw_static_info->qos_priority_level);
+}
+
+int cam_fd_hw_util_get_hw_caps(struct cam_hw_info *fd_hw,
+ struct cam_fd_hw_caps *hw_caps)
+{
+ struct cam_hw_soc_info *soc_info = &fd_hw->soc_info;
+ struct cam_fd_hw_static_info *hw_static_info =
+ ((struct cam_fd_core *)fd_hw->core_info)->hw_static_info;
+ uint32_t reg_value;
+
+ if (!hw_static_info) {
+ CAM_ERR(CAM_FD, "Invalid hw info data");
+ return -EINVAL;
+ }
+
+ reg_value = cam_fd_soc_register_read(soc_info, CAM_FD_REG_CORE,
+ hw_static_info->core_regs.version);
+ hw_caps->core_version.major =
+ CAM_BITS_MASK_SHIFT(reg_value, 0xf00, 0x8);
+ hw_caps->core_version.minor =
+ CAM_BITS_MASK_SHIFT(reg_value, 0xf0, 0x4);
+ hw_caps->core_version.incr =
+ CAM_BITS_MASK_SHIFT(reg_value, 0xf, 0x0);
+
+ reg_value = cam_fd_soc_register_read(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.wrapper_version);
+ hw_caps->wrapper_version.major =
+ CAM_BITS_MASK_SHIFT(reg_value, 0xf0000000, 0x1c);
+ hw_caps->wrapper_version.minor =
+ CAM_BITS_MASK_SHIFT(reg_value, 0xfff0000, 0x10);
+ hw_caps->wrapper_version.incr =
+ CAM_BITS_MASK_SHIFT(reg_value, 0xffff, 0x0);
+
+ hw_caps->raw_results_available =
+ hw_static_info->results.raw_results_available;
+ hw_caps->supported_modes = hw_static_info->supported_modes;
+
+ CAM_DBG(CAM_FD, "core:%d.%d.%d wrapper:%d.%d.%d intermediate:%d",
+ hw_caps->core_version.major, hw_caps->core_version.minor,
+ hw_caps->core_version.incr, hw_caps->wrapper_version.major,
+ hw_caps->wrapper_version.minor, hw_caps->wrapper_version.incr,
+ hw_caps->raw_results_available);
+
+ return 0;
+}
+
+static int cam_fd_hw_util_fdwrapper_sync_reset(struct cam_hw_info *fd_hw)
+{
+ struct cam_fd_core *fd_core = (struct cam_fd_core *)fd_hw->core_info;
+ struct cam_fd_hw_static_info *hw_static_info = fd_core->hw_static_info;
+ struct cam_hw_soc_info *soc_info = &fd_hw->soc_info;
+ long time_left;
+
+ /* Before triggering reset to HW, clear the reset complete */
+ reinit_completion(&fd_core->reset_complete);
+
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_CORE,
+ hw_static_info->core_regs.control, 0x1);
+
+ if (hw_static_info->enable_errata_wa.single_irq_only) {
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.irq_mask,
+ CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_RESET_DONE));
+ }
+
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.sw_reset, 0x1);
+
+ time_left = wait_for_completion_timeout(&fd_core->reset_complete,
+ msecs_to_jiffies(CAM_FD_HW_HALT_RESET_TIMEOUT));
+ if (time_left <= 0) {
+ CAM_ERR(CAM_FD, "HW reset wait failed time_left=%d", time_left);
+ return -EPERM;
+ }
+
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_CORE,
+ hw_static_info->core_regs.control, 0x0);
+
+ CAM_DBG(CAM_FD, "FD Wrapper SW Sync Reset complete");
+
+ return 0;
+}
+
+
+static int cam_fd_hw_util_fdwrapper_halt(struct cam_hw_info *fd_hw)
+{
+ struct cam_fd_core *fd_core = (struct cam_fd_core *)fd_hw->core_info;
+ struct cam_fd_hw_static_info *hw_static_info = fd_core->hw_static_info;
+ struct cam_hw_soc_info *soc_info = &fd_hw->soc_info;
+ long time_left;
+
+ /* Before triggering halt to HW, clear halt complete */
+ reinit_completion(&fd_core->halt_complete);
+
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_CORE,
+ hw_static_info->core_regs.control, 0x1);
+
+ if (hw_static_info->enable_errata_wa.single_irq_only) {
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.irq_mask,
+ CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_HALT_DONE));
+ }
+
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.hw_stop, 0x1);
+
+ time_left = wait_for_completion_timeout(&fd_core->halt_complete,
+ msecs_to_jiffies(CAM_FD_HW_HALT_RESET_TIMEOUT));
+ if (time_left <= 0) {
+ CAM_ERR(CAM_FD, "HW halt wait failed time_left=%d", time_left);
+ return -EPERM;
+ }
+
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_CORE,
+ hw_static_info->core_regs.control, 0x0);
+
+ CAM_DBG(CAM_FD, "FD Wrapper Halt complete");
+
+ return 0;
+}
+
+static int cam_fd_hw_util_processcmd_prestart(struct cam_hw_info *fd_hw,
+ struct cam_fd_hw_cmd_prestart_args *prestart_args)
+{
+ struct cam_hw_soc_info *soc_info = &fd_hw->soc_info;
+ struct cam_fd_hw_static_info *hw_static_info =
+ ((struct cam_fd_core *)fd_hw->core_info)->hw_static_info;
+ struct cam_fd_ctx_hw_private *ctx_hw_private =
+ prestart_args->ctx_hw_private;
+ uint32_t size, size_required = 0;
+ uint32_t mem_base;
+ uint32_t *cmd_buf_addr = prestart_args->cmd_buf_addr;
+ uint32_t reg_val_pair[CAM_FD_REG_VAL_PAIR_SIZE];
+ uint32_t num_cmds = 0;
+ int i;
+ struct cam_fd_hw_io_buffer *io_buf;
+ struct cam_fd_hw_req_private *req_private;
+ uint32_t available_size = prestart_args->size;
+ bool work_buffer_configured = false;
+
+ if (!ctx_hw_private || !cmd_buf_addr) {
+ CAM_ERR(CAM_FD, "Invalid input prestart args %pK %pK",
+ ctx_hw_private, cmd_buf_addr);
+ return -EINVAL;
+ }
+
+ if (prestart_args->get_raw_results &&
+ !hw_static_info->results.raw_results_available) {
+ CAM_ERR(CAM_FD, "Raw results not supported %d %d",
+ prestart_args->get_raw_results,
+ hw_static_info->results.raw_results_available);
+ return -EINVAL;
+ }
+
+ req_private = &prestart_args->hw_req_private;
+ req_private->ctx_hw_private = prestart_args->ctx_hw_private;
+ req_private->request_id = prestart_args->request_id;
+ req_private->get_raw_results = prestart_args->get_raw_results;
+ req_private->fd_results = NULL;
+ req_private->raw_results = NULL;
+
+ /* Start preparing CDM register values that KMD has to insert */
+ num_cmds = cam_fd_cdm_write_reg_val_pair(reg_val_pair, num_cmds,
+ hw_static_info->core_regs.control, 0x1);
+ num_cmds = cam_fd_cdm_write_reg_val_pair(reg_val_pair, num_cmds,
+ hw_static_info->core_regs.control, 0x0);
+
+ for (i = 0; i < CAM_FD_MAX_IO_BUFFERS; i++) {
+ io_buf = &prestart_args->input_buf[i];
+
+ if (io_buf->valid == false)
+ break;
+
+ if (io_buf->io_cfg->direction != CAM_BUF_INPUT) {
+ CAM_ERR(CAM_FD, "Incorrect direction %d %d",
+ io_buf->io_cfg->direction, CAM_BUF_INPUT);
+ return -EINVAL;
+ }
+
+ switch (io_buf->io_cfg->resource_type) {
+ case CAM_FD_INPUT_PORT_ID_IMAGE: {
+ if ((num_cmds + 2) > CAM_FD_REG_VAL_PAIR_SIZE) {
+ CAM_ERR(CAM_FD,
+ "Invalid reg_val pair size %d, %d",
+ num_cmds, CAM_FD_REG_VAL_PAIR_SIZE);
+ return -EINVAL;
+ }
+
+ num_cmds = cam_fd_cdm_write_reg_val_pair(
+ reg_val_pair, num_cmds,
+ hw_static_info->core_regs.image_addr,
+ io_buf->io_addr[0]);
+ break;
+ }
+ default:
+ CAM_ERR(CAM_FD, "Invalid resource type %d",
+ io_buf->io_cfg->resource_type);
+ return -EINVAL;
+ }
+ }
+
+ for (i = 0; i < CAM_FD_MAX_IO_BUFFERS; i++) {
+ io_buf = &prestart_args->output_buf[i];
+
+ if (io_buf->valid == false)
+ break;
+
+ if (io_buf->io_cfg->direction != CAM_BUF_OUTPUT) {
+ CAM_ERR(CAM_FD, "Incorrect direction %d %d",
+ io_buf->io_cfg->direction, CAM_BUF_INPUT);
+ return -EINVAL;
+ }
+
+ switch (io_buf->io_cfg->resource_type) {
+ case CAM_FD_OUTPUT_PORT_ID_RESULTS: {
+ uint32_t face_results_offset;
+
+ size_required = hw_static_info->results.max_faces *
+ hw_static_info->results.per_face_entries * 4;
+
+ if (io_buf->io_cfg->planes[0].plane_stride <
+ size_required) {
+ CAM_ERR(CAM_FD, "Invalid results size %d %d",
+ io_buf->io_cfg->planes[0].plane_stride,
+ size_required);
+ return -EINVAL;
+ }
+
+ req_private->fd_results =
+ (struct cam_fd_results *)io_buf->cpu_addr[0];
+
+ face_results_offset =
+ (uint8_t *)&req_private->fd_results->faces[0] -
+ (uint8_t *)req_private->fd_results;
+
+ if (hw_static_info->ro_mode_supported) {
+ if ((num_cmds + 4) > CAM_FD_REG_VAL_PAIR_SIZE) {
+ CAM_ERR(CAM_FD,
+ "Invalid reg_val size %d, %d",
+ num_cmds,
+ CAM_FD_REG_VAL_PAIR_SIZE);
+ return -EINVAL;
+ }
+ /*
+ * Face data actually starts 16bytes later in
+ * the io buffer Check cam_fd_results.
+ */
+ num_cmds = cam_fd_cdm_write_reg_val_pair(
+ reg_val_pair, num_cmds,
+ hw_static_info->core_regs.result_addr,
+ io_buf->io_addr[0] +
+ face_results_offset);
+ num_cmds = cam_fd_cdm_write_reg_val_pair(
+ reg_val_pair, num_cmds,
+ hw_static_info->core_regs.ro_mode,
+ 0x1);
+
+ req_private->ro_mode_enabled = true;
+ } else {
+ req_private->ro_mode_enabled = false;
+ }
+ break;
+ }
+ case CAM_FD_OUTPUT_PORT_ID_RAW_RESULTS: {
+ size_required =
+ hw_static_info->results.raw_results_entries *
+ sizeof(uint32_t);
+
+ if (io_buf->io_cfg->planes[0].plane_stride <
+ size_required) {
+ CAM_ERR(CAM_FD, "Invalid results size %d %d",
+ io_buf->io_cfg->planes[0].plane_stride,
+ size_required);
+ return -EINVAL;
+ }
+
+ req_private->raw_results =
+ (uint32_t *)io_buf->cpu_addr[0];
+ break;
+ }
+ case CAM_FD_OUTPUT_PORT_ID_WORK_BUFFER: {
+ if ((num_cmds + 2) > CAM_FD_REG_VAL_PAIR_SIZE) {
+ CAM_ERR(CAM_FD,
+ "Invalid reg_val pair size %d, %d",
+ num_cmds, CAM_FD_REG_VAL_PAIR_SIZE);
+ return -EINVAL;
+ }
+
+ num_cmds = cam_fd_cdm_write_reg_val_pair(
+ reg_val_pair, num_cmds,
+ hw_static_info->core_regs.work_addr,
+ io_buf->io_addr[0]);
+
+ work_buffer_configured = true;
+ break;
+ }
+ default:
+ CAM_ERR(CAM_FD, "Invalid resource type %d",
+ io_buf->io_cfg->resource_type);
+ return -EINVAL;
+ }
+ }
+
+ if (!req_private->fd_results || !work_buffer_configured) {
+ CAM_ERR(CAM_FD, "Invalid IO Buffers results=%pK work=%d",
+ req_private->fd_results, work_buffer_configured);
+ return -EINVAL;
+ }
+
+ /* First insert CHANGE_BASE command */
+ size = ctx_hw_private->cdm_ops->cdm_required_size_changebase();
+ /* since cdm returns dwords, we need to convert it into bytes */
+ if ((size * 4) > available_size) {
+ CAM_ERR(CAM_FD, "buf size:%d is not sufficient, expected: %d",
+ prestart_args->size, size);
+ return -EINVAL;
+ }
+
+ mem_base = CAM_SOC_GET_REG_MAP_CAM_BASE(soc_info,
+ ((struct cam_fd_soc_private *)soc_info->soc_private)->
+ regbase_index[CAM_FD_REG_CORE]);
+
+ ctx_hw_private->cdm_ops->cdm_write_changebase(cmd_buf_addr, mem_base);
+ cmd_buf_addr += size;
+ available_size -= (size * 4);
+
+ size = ctx_hw_private->cdm_ops->cdm_required_size_reg_random(
+ num_cmds/2);
+ /* cdm util returns dwords, need to convert to bytes */
+ if ((size * 4) > available_size) {
+ CAM_ERR(CAM_FD, "Insufficient size:%d , expected size:%d",
+ available_size, size);
+ return -ENOMEM;
+ }
+ ctx_hw_private->cdm_ops->cdm_write_regrandom(cmd_buf_addr, num_cmds/2,
+ reg_val_pair);
+ cmd_buf_addr += size;
+ available_size -= (size * 4);
+
+ /* Update pre_config_buf_size in bytes */
+ prestart_args->pre_config_buf_size =
+ prestart_args->size - available_size;
+
+ /*
+ * Currently, no post config commands, we trigger HW start directly
+ * from start(). Start trigger command can be inserted into CDM
+ * as post config commands.
+ */
+ prestart_args->post_config_buf_size = 0;
+
+ CAM_DBG(CAM_FD, "PreConfig [%pK %d], PostConfig[%pK %d]",
+ prestart_args->cmd_buf_addr, prestart_args->pre_config_buf_size,
+ cmd_buf_addr, prestart_args->post_config_buf_size);
+
+ for (i = 0; i < (prestart_args->pre_config_buf_size +
+ prestart_args->post_config_buf_size) / 4; i++)
+ CAM_DBG(CAM_FD, "CDM KMD Commands [%d] : [%pK] [0x%x]", i,
+ &prestart_args->cmd_buf_addr[i],
+ prestart_args->cmd_buf_addr[i]);
+
+ return 0;
+}
+
+static int cam_fd_hw_util_processcmd_frame_done(struct cam_hw_info *fd_hw,
+ struct cam_fd_hw_frame_done_args *frame_done_args)
+{
+ struct cam_fd_core *fd_core = (struct cam_fd_core *)fd_hw->core_info;
+ struct cam_fd_hw_static_info *hw_static_info = fd_core->hw_static_info;
+ struct cam_fd_hw_req_private *req_private;
+ uint32_t base, face_cnt;
+ uint32_t *buffer;
+ int i;
+
+ spin_lock(&fd_core->spin_lock);
+ if ((fd_core->core_state != CAM_FD_CORE_STATE_IDLE) ||
+ (fd_core->results_valid == false) ||
+ !fd_core->hw_req_private) {
+ CAM_ERR(CAM_FD,
+ "Invalid state for results state=%d, results=%d %pK",
+ fd_core->core_state, fd_core->results_valid,
+ fd_core->hw_req_private);
+ spin_unlock(&fd_core->spin_lock);
+ return -EINVAL;
+ }
+ fd_core->core_state = CAM_FD_CORE_STATE_READING_RESULTS;
+ req_private = fd_core->hw_req_private;
+ spin_unlock(&fd_core->spin_lock);
+
+ /*
+ * Copy the register value as is into output buffers.
+ * Wehter we are copying the output data by reading registers or
+ * programming output buffer directly to HW must be transparent to UMD.
+ * In case HW supports writing face count value directly into
+ * DDR memory in future, these values should match.
+ */
+ req_private->fd_results->face_count =
+ cam_fd_soc_register_read(&fd_hw->soc_info, CAM_FD_REG_CORE,
+ hw_static_info->core_regs.result_cnt);
+
+ face_cnt = req_private->fd_results->face_count & 0x3F;
+
+ if (face_cnt > hw_static_info->results.max_faces) {
+ CAM_WARN(CAM_FD, "Face count greater than max %d %d",
+ face_cnt, hw_static_info->results.max_faces);
+ face_cnt = hw_static_info->results.max_faces;
+ }
+
+ CAM_DBG(CAM_FD, "ReqID[%lld] Faces Detected = %d",
+ req_private->request_id, face_cnt);
+
+ /*
+ * We need to read the face data information from registers only
+ * if one of below is true
+ * 1. RO mode is not set. i.e FD HW doesn't write face data into
+ * DDR memory
+ * 2. On the current chipset, results written into DDR memory by FD HW
+ * are not gauranteed to be correct
+ */
+ if (!req_private->ro_mode_enabled ||
+ hw_static_info->enable_errata_wa.ro_mode_results_invalid) {
+ buffer = (uint32_t *)&req_private->fd_results->faces[0];
+ base = hw_static_info->core_regs.results_reg_base;
+
+ /*
+ * Write register values as is into face data buffer. Its UMD
+ * driver responsibility to interpret the data and extract face
+ * properties from output buffer. Think in case output buffer
+ * is directly programmed to HW, then KMD has no control to
+ * extract the face properties and UMD anyway has to extract
+ * face properties. So we follow the same approach and keep
+ * this transparent to UMD.
+ */
+ for (i = 0;
+ i < (face_cnt *
+ hw_static_info->results.per_face_entries); i++) {
+ *buffer = cam_fd_soc_register_read(&fd_hw->soc_info,
+ CAM_FD_REG_CORE, base + (i * 0x4));
+ CAM_DBG(CAM_FD, "FaceData[%d] : 0x%x", i / 4, *buffer);
+ buffer++;
+ }
+ }
+
+ if (req_private->get_raw_results &&
+ req_private->raw_results &&
+ hw_static_info->results.raw_results_available) {
+ buffer = req_private->raw_results;
+ base = hw_static_info->core_regs.raw_results_reg_base;
+
+ for (i = 0;
+ i < hw_static_info->results.raw_results_entries;
+ i++) {
+ *buffer = cam_fd_soc_register_read(&fd_hw->soc_info,
+ CAM_FD_REG_CORE, base + (i * 0x4));
+ CAM_DBG(CAM_FD, "RawData[%d] : 0x%x", i, *buffer);
+ buffer++;
+ }
+ }
+
+ spin_lock(&fd_core->spin_lock);
+ fd_core->hw_req_private = NULL;
+ fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
+ spin_unlock(&fd_core->spin_lock);
+
+ return 0;
+}
+
+irqreturn_t cam_fd_hw_irq(int irq_num, void *data)
+{
+ struct cam_hw_info *fd_hw = (struct cam_hw_info *)data;
+ struct cam_fd_core *fd_core;
+ struct cam_hw_soc_info *soc_info;
+ struct cam_fd_hw_static_info *hw_static_info;
+ uint32_t reg_value;
+ enum cam_fd_hw_irq_type irq_type = CAM_FD_IRQ_FRAME_DONE;
+ uint32_t num_irqs = 0;
+
+ if (!fd_hw) {
+ CAM_ERR(CAM_FD, "Invalid data in IRQ callback");
+ return -EINVAL;
+ }
+
+ fd_core = (struct cam_fd_core *) fd_hw->core_info;
+ soc_info = &fd_hw->soc_info;
+ hw_static_info = fd_core->hw_static_info;
+
+ reg_value = cam_fd_soc_register_read(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.irq_status);
+
+ CAM_DBG(CAM_FD, "FD IRQ status 0x%x", reg_value);
+
+ if (reg_value & CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_HALT_DONE)) {
+ complete_all(&fd_core->halt_complete);
+ irq_type = CAM_FD_IRQ_HALT_DONE;
+ num_irqs++;
+ }
+
+ if (reg_value & CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_RESET_DONE)) {
+ complete_all(&fd_core->reset_complete);
+ irq_type = CAM_FD_IRQ_RESET_DONE;
+ num_irqs++;
+ }
+
+ if (reg_value & CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_FRAME_DONE)) {
+ complete_all(&fd_core->processing_complete);
+ irq_type = CAM_FD_IRQ_FRAME_DONE;
+ num_irqs++;
+ }
+
+ /*
+ * We should never get an IRQ callback with no or more than one mask.
+ * Validate first to make sure nothing going wrong.
+ */
+ if (num_irqs != 1) {
+ CAM_ERR(CAM_FD,
+ "Invalid number of IRQs, value=0x%x, num_irqs=%d",
+ reg_value, num_irqs);
+ return -EINVAL;
+ }
+
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.irq_clear,
+ hw_static_info->irq_mask);
+
+ if (irq_type == CAM_FD_IRQ_HALT_DONE) {
+ /*
+ * Do not send HALT IRQ callback to Hw Mgr,
+ * a reset would always follow
+ */
+ return IRQ_HANDLED;
+ }
+
+ spin_lock(&fd_core->spin_lock);
+ /* Do not change state to IDLE on HALT IRQ. Reset must follow halt */
+ if ((irq_type == CAM_FD_IRQ_RESET_DONE) ||
+ (irq_type == CAM_FD_IRQ_FRAME_DONE)) {
+
+ fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
+ if (irq_type == CAM_FD_IRQ_FRAME_DONE)
+ fd_core->results_valid = true;
+
+ CAM_DBG(CAM_FD, "FD IRQ type %d, state=%d",
+ irq_type, fd_core->core_state);
+ }
+ spin_unlock(&fd_core->spin_lock);
+
+ if (fd_core->irq_cb.cam_fd_hw_mgr_cb)
+ fd_core->irq_cb.cam_fd_hw_mgr_cb(fd_core->irq_cb.data,
+ irq_type);
+
+ return IRQ_HANDLED;
+}
+
+int cam_fd_hw_get_hw_caps(void *hw_priv, void *get_hw_cap_args,
+ uint32_t arg_size)
+{
+ struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
+ struct cam_fd_core *fd_core;
+ struct cam_fd_hw_caps *fd_hw_caps =
+ (struct cam_fd_hw_caps *)get_hw_cap_args;
+
+ if (!hw_priv || !get_hw_cap_args) {
+ CAM_ERR(CAM_FD, "Invalid input pointers %pK %pK",
+ hw_priv, get_hw_cap_args);
+ return -EINVAL;
+ }
+
+ fd_core = (struct cam_fd_core *)fd_hw->core_info;
+ *fd_hw_caps = fd_core->hw_caps;
+
+ CAM_DBG(CAM_FD, "core:%d.%d wrapper:%d.%d mode:%d, raw:%d",
+ fd_hw_caps->core_version.major,
+ fd_hw_caps->core_version.minor,
+ fd_hw_caps->wrapper_version.major,
+ fd_hw_caps->wrapper_version.minor,
+ fd_hw_caps->supported_modes,
+ fd_hw_caps->raw_results_available);
+
+ return 0;
+}
+
+int cam_fd_hw_init(void *hw_priv, void *init_hw_args, uint32_t arg_size)
+{
+ struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
+ struct cam_fd_core *fd_core;
+ struct cam_fd_hw_init_args *init_args =
+ (struct cam_fd_hw_init_args *)init_hw_args;
+ int rc = 0;
+
+ if (!fd_hw || !init_args) {
+ CAM_ERR(CAM_FD, "Invalid argument %pK %pK", fd_hw, init_args);
+ return -EINVAL;
+ }
+
+ if (arg_size != sizeof(struct cam_fd_hw_init_args)) {
+ CAM_ERR(CAM_FD, "Invalid arg size %d, %d", arg_size,
+ sizeof(struct cam_fd_hw_init_args));
+ return -EINVAL;
+ }
+
+ fd_core = (struct cam_fd_core *)fd_hw->core_info;
+
+ mutex_lock(&fd_hw->hw_mutex);
+ CAM_DBG(CAM_FD, "FD HW Init ref count before %d", fd_hw->open_count);
+
+ if (fd_hw->open_count > 0) {
+ rc = 0;
+ mutex_unlock(&fd_hw->hw_mutex);
+ goto cdm_streamon;
+ }
+
+ rc = cam_fd_soc_enable_resources(&fd_hw->soc_info);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Enable SOC failed, rc=%d", rc);
+ goto unlock_return;
+ }
+
+ rc = cam_fd_hw_reset(hw_priv, NULL, 0);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Reset Failed, rc=%d", rc);
+ goto disable_soc;
+ }
+
+ cam_fd_hw_util_enable_power_on_settings(fd_hw);
+
+ fd_hw->hw_state = CAM_HW_STATE_POWER_UP;
+ fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
+ fd_hw->open_count++;
+ CAM_DBG(CAM_FD, "FD HW Init ref count after %d", fd_hw->open_count);
+
+ mutex_unlock(&fd_hw->hw_mutex);
+
+cdm_streamon:
+ if (init_args->ctx_hw_private) {
+ struct cam_fd_ctx_hw_private *ctx_hw_private =
+ init_args->ctx_hw_private;
+
+ rc = cam_cdm_stream_on(ctx_hw_private->cdm_handle);
+ if (rc) {
+ CAM_ERR(CAM_FD, "CDM StreamOn fail :handle=0x%x, rc=%d",
+ ctx_hw_private->cdm_handle, rc);
+ return rc;
+ }
+ }
+
+ return rc;
+
+disable_soc:
+ if (cam_fd_soc_disable_resources(&fd_hw->soc_info))
+ CAM_ERR(CAM_FD, "Error in disable soc resources");
+unlock_return:
+ mutex_unlock(&fd_hw->hw_mutex);
+ return rc;
+}
+
+int cam_fd_hw_deinit(void *hw_priv, void *deinit_hw_args, uint32_t arg_size)
+{
+ struct cam_hw_info *fd_hw = hw_priv;
+ struct cam_fd_core *fd_core;
+ struct cam_fd_hw_deinit_args *deinit_args =
+ (struct cam_fd_hw_deinit_args *)deinit_hw_args;
+ int rc = 0;
+
+ if (!fd_hw || !deinit_hw_args) {
+ CAM_ERR(CAM_FD, "Invalid argument");
+ return -EINVAL;
+ }
+
+ if (arg_size != sizeof(struct cam_fd_hw_deinit_args)) {
+ CAM_ERR(CAM_FD, "Invalid arg size %d, %d", arg_size,
+ sizeof(struct cam_fd_hw_deinit_args));
+ return -EINVAL;
+ }
+
+ fd_core = (struct cam_fd_core *)fd_hw->core_info;
+
+ if (deinit_args->ctx_hw_private) {
+ struct cam_fd_ctx_hw_private *ctx_hw_private =
+ deinit_args->ctx_hw_private;
+
+ rc = cam_cdm_stream_off(ctx_hw_private->cdm_handle);
+ if (rc) {
+ CAM_ERR(CAM_FD,
+ "Failed in CDM StreamOff, handle=0x%x, rc=%d",
+ ctx_hw_private->cdm_handle, rc);
+ return rc;
+ }
+ }
+
+ mutex_lock(&fd_hw->hw_mutex);
+
+ if (fd_hw->open_count == 0) {
+ mutex_unlock(&fd_hw->hw_mutex);
+ CAM_ERR(CAM_FD, "Error Unbalanced deinit");
+ return -EFAULT;
+ }
+
+ fd_hw->open_count--;
+ CAM_DBG(CAM_FD, "FD HW ref count=%d", fd_hw->open_count);
+
+ if (fd_hw->open_count) {
+ rc = 0;
+ goto unlock_return;
+ }
+
+ rc = cam_fd_soc_disable_resources(&fd_hw->soc_info);
+ if (rc)
+ CAM_ERR(CAM_FD, "Failed in Disable SOC, rc=%d", rc);
+
+ fd_hw->hw_state = CAM_HW_STATE_POWER_DOWN;
+ fd_core->core_state = CAM_FD_CORE_STATE_POWERDOWN;
+
+unlock_return:
+ mutex_unlock(&fd_hw->hw_mutex);
+ return rc;
+}
+
+int cam_fd_hw_reset(void *hw_priv, void *reset_core_args, uint32_t arg_size)
+{
+ struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
+ struct cam_fd_core *fd_core;
+ int rc;
+
+ if (!fd_hw) {
+ CAM_ERR(CAM_FD, "Invalid input handle");
+ return -EINVAL;
+ }
+
+ fd_core = (struct cam_fd_core *)fd_hw->core_info;
+
+ spin_lock(&fd_core->spin_lock);
+ if (fd_core->core_state == CAM_FD_CORE_STATE_RESET_PROGRESS) {
+ CAM_ERR(CAM_FD, "Reset not allowed in %d state",
+ fd_core->core_state);
+ spin_unlock(&fd_core->spin_lock);
+ return -EINVAL;
+ }
+
+ fd_core->results_valid = false;
+ fd_core->core_state = CAM_FD_CORE_STATE_RESET_PROGRESS;
+ spin_unlock(&fd_core->spin_lock);
+
+ rc = cam_fd_hw_util_fdwrapper_sync_reset(fd_hw);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in RESET rc=%d", rc);
+ return rc;
+ }
+
+ spin_lock(&fd_core->spin_lock);
+ fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
+ spin_unlock(&fd_core->spin_lock);
+
+ return rc;
+}
+
+int cam_fd_hw_start(void *hw_priv, void *hw_start_args, uint32_t arg_size)
+{
+ struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
+ struct cam_fd_core *fd_core;
+ struct cam_fd_hw_static_info *hw_static_info;
+ struct cam_fd_hw_cmd_start_args *start_args =
+ (struct cam_fd_hw_cmd_start_args *)hw_start_args;
+ struct cam_fd_ctx_hw_private *ctx_hw_private;
+ int rc;
+
+ if (!hw_priv || !start_args) {
+ CAM_ERR(CAM_FD, "Invalid input args %pK %pK", hw_priv,
+ start_args);
+ return -EINVAL;
+ }
+
+ if (arg_size != sizeof(struct cam_fd_hw_cmd_start_args)) {
+ CAM_ERR(CAM_FD, "Invalid arg size %d, %d", arg_size,
+ sizeof(struct cam_fd_hw_cmd_start_args));
+ return -EINVAL;
+ }
+
+ fd_core = (struct cam_fd_core *)fd_hw->core_info;
+ hw_static_info = fd_core->hw_static_info;
+
+ spin_lock(&fd_core->spin_lock);
+ if (fd_core->core_state != CAM_FD_CORE_STATE_IDLE) {
+ CAM_ERR(CAM_FD, "Cannot start in %d state",
+ fd_core->core_state);
+ spin_unlock(&fd_core->spin_lock);
+ return -EINVAL;
+ }
+
+ /*
+ * We are about to start FD HW processing, save the request
+ * private data which is being processed by HW. Once the frame
+ * processing is finished, process_cmd(FRAME_DONE) should be called
+ * with same hw_req_private as input.
+ */
+ fd_core->hw_req_private = start_args->hw_req_private;
+ fd_core->core_state = CAM_FD_CORE_STATE_PROCESSING;
+ fd_core->results_valid = false;
+ spin_unlock(&fd_core->spin_lock);
+
+ ctx_hw_private = start_args->ctx_hw_private;
+
+ /* Before starting HW process, clear processing complete */
+ reinit_completion(&fd_core->processing_complete);
+
+ if (hw_static_info->enable_errata_wa.single_irq_only) {
+ cam_fd_soc_register_write(&fd_hw->soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.irq_mask,
+ CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_FRAME_DONE));
+ }
+
+ if (start_args->num_hw_update_entries > 0) {
+ struct cam_cdm_bl_request *cdm_cmd = ctx_hw_private->cdm_cmd;
+ struct cam_hw_update_entry *cmd;
+ int i;
+
+ cdm_cmd->cmd_arrary_count = start_args->num_hw_update_entries;
+ cdm_cmd->type = CAM_CDM_BL_CMD_TYPE_MEM_HANDLE;
+ cdm_cmd->flag = false;
+ cdm_cmd->userdata = NULL;
+ cdm_cmd->cookie = 0;
+
+ for (i = 0 ; i <= start_args->num_hw_update_entries; i++) {
+ cmd = (start_args->hw_update_entries + i);
+ cdm_cmd->cmd[i].bl_addr.mem_handle = cmd->handle;
+ cdm_cmd->cmd[i].offset = cmd->offset;
+ cdm_cmd->cmd[i].len = cmd->len;
+ }
+
+ rc = cam_cdm_submit_bls(ctx_hw_private->cdm_handle, cdm_cmd);
+ if (rc) {
+ CAM_ERR(CAM_FD,
+ "Failed to submit cdm commands, rc=%d", rc);
+ goto error;
+ }
+ } else {
+ CAM_ERR(CAM_FD, "Invalid number of hw update entries");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ cam_fd_soc_register_write(&fd_hw->soc_info, CAM_FD_REG_CORE,
+ hw_static_info->core_regs.control, 0x2);
+
+ return 0;
+error:
+ spin_lock(&fd_core->spin_lock);
+ fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
+ spin_unlock(&fd_core->spin_lock);
+
+ return rc;
+}
+
+int cam_fd_hw_halt_reset(void *hw_priv, void *stop_args, uint32_t arg_size)
+{
+ struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
+ struct cam_fd_core *fd_core;
+ int rc;
+
+ if (!fd_hw) {
+ CAM_ERR(CAM_FD, "Invalid input handle");
+ return -EINVAL;
+ }
+
+ fd_core = (struct cam_fd_core *)fd_hw->core_info;
+
+ spin_lock(&fd_core->spin_lock);
+ if ((fd_core->core_state == CAM_FD_CORE_STATE_POWERDOWN) ||
+ (fd_core->core_state == CAM_FD_CORE_STATE_RESET_PROGRESS)) {
+ CAM_ERR(CAM_FD, "Reset not allowed in %d state",
+ fd_core->core_state);
+ spin_unlock(&fd_core->spin_lock);
+ return -EINVAL;
+ }
+
+ fd_core->results_valid = false;
+ fd_core->core_state = CAM_FD_CORE_STATE_RESET_PROGRESS;
+ spin_unlock(&fd_core->spin_lock);
+
+ rc = cam_fd_hw_util_fdwrapper_halt(fd_hw);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in HALT rc=%d", rc);
+ return rc;
+ }
+
+ /* HALT must be followed by RESET */
+ rc = cam_fd_hw_util_fdwrapper_sync_reset(fd_hw);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in RESET rc=%d", rc);
+ return rc;
+ }
+
+ spin_lock(&fd_core->spin_lock);
+ fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
+ spin_unlock(&fd_core->spin_lock);
+
+ return rc;
+}
+
+int cam_fd_hw_reserve(void *hw_priv, void *hw_reserve_args, uint32_t arg_size)
+{
+ struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
+ int rc = -EINVAL;
+ struct cam_fd_ctx_hw_private *ctx_hw_private;
+ struct cam_fd_hw_reserve_args *reserve_args =
+ (struct cam_fd_hw_reserve_args *)hw_reserve_args;
+ struct cam_cdm_acquire_data cdm_acquire;
+ struct cam_cdm_bl_request *cdm_cmd;
+ int i;
+
+ if (!fd_hw || !reserve_args) {
+ CAM_ERR(CAM_FD, "Invalid input %pK, %pK", fd_hw, reserve_args);
+ return -EINVAL;
+ }
+
+ if (arg_size != sizeof(struct cam_fd_hw_reserve_args)) {
+ CAM_ERR(CAM_FD, "Invalid arg size %d, %d", arg_size,
+ sizeof(struct cam_fd_hw_reserve_args));
+ return -EINVAL;
+ }
+
+ cdm_cmd = kzalloc(((sizeof(struct cam_cdm_bl_request)) +
+ ((CAM_FD_MAX_HW_ENTRIES - 1) *
+ sizeof(struct cam_cdm_bl_cmd))), GFP_KERNEL);
+ if (!cdm_cmd)
+ return -ENOMEM;
+
+ ctx_hw_private = kzalloc(sizeof(struct cam_fd_ctx_hw_private),
+ GFP_KERNEL);
+ if (!ctx_hw_private) {
+ kfree(cdm_cmd);
+ return -ENOMEM;
+ }
+
+ memset(&cdm_acquire, 0, sizeof(cdm_acquire));
+ strlcpy(cdm_acquire.identifier, "fd", sizeof("fd"));
+ cdm_acquire.cell_index = fd_hw->soc_info.index;
+ cdm_acquire.handle = 0;
+ cdm_acquire.userdata = ctx_hw_private;
+ cdm_acquire.cam_cdm_callback = cam_fd_hw_util_cdm_callback;
+ cdm_acquire.id = CAM_CDM_VIRTUAL;
+ cdm_acquire.base_array_cnt = fd_hw->soc_info.num_reg_map;
+ for (i = 0; i < fd_hw->soc_info.num_reg_map; i++)
+ cdm_acquire.base_array[i] = &fd_hw->soc_info.reg_map[i];
+
+ rc = cam_cdm_acquire(&cdm_acquire);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed to acquire the CDM HW");
+ goto error;
+ }
+
+ ctx_hw_private->hw_ctx = reserve_args->hw_ctx;
+ ctx_hw_private->fd_hw = fd_hw;
+ ctx_hw_private->mode = reserve_args->mode;
+ ctx_hw_private->cdm_handle = cdm_acquire.handle;
+ ctx_hw_private->cdm_ops = cdm_acquire.ops;
+ ctx_hw_private->cdm_cmd = cdm_cmd;
+
+ reserve_args->ctx_hw_private = ctx_hw_private;
+
+ CAM_DBG(CAM_FD, "private=%pK, hw_ctx=%pK, mode=%d, cdm_handle=0x%x",
+ ctx_hw_private, ctx_hw_private->hw_ctx, ctx_hw_private->mode,
+ ctx_hw_private->cdm_handle);
+
+ return 0;
+error:
+ kfree(ctx_hw_private);
+ kfree(cdm_cmd);
+ return rc;
+}
+
+int cam_fd_hw_release(void *hw_priv, void *hw_release_args, uint32_t arg_size)
+{
+ struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
+ int rc = -EINVAL;
+ struct cam_fd_ctx_hw_private *ctx_hw_private;
+ struct cam_fd_hw_release_args *release_args =
+ (struct cam_fd_hw_release_args *)hw_release_args;
+
+ if (!fd_hw || !release_args) {
+ CAM_ERR(CAM_FD, "Invalid input %pK, %pK", fd_hw, release_args);
+ return -EINVAL;
+ }
+
+ if (arg_size != sizeof(struct cam_fd_hw_release_args)) {
+ CAM_ERR(CAM_FD, "Invalid arg size %d, %d", arg_size,
+ sizeof(struct cam_fd_hw_release_args));
+ return -EINVAL;
+ }
+
+ ctx_hw_private =
+ (struct cam_fd_ctx_hw_private *)release_args->ctx_hw_private;
+
+ rc = cam_cdm_release(ctx_hw_private->cdm_handle);
+ if (rc)
+ CAM_ERR(CAM_FD, "Release cdm handle failed, handle=0x%x, rc=%d",
+ ctx_hw_private->cdm_handle, rc);
+
+ kfree(ctx_hw_private);
+ release_args->ctx_hw_private = NULL;
+
+ return 0;
+}
+
+int cam_fd_hw_process_cmd(void *hw_priv, uint32_t cmd_type,
+ void *cmd_args, uint32_t arg_size)
+{
+ struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
+ int rc = -EINVAL;
+
+ if (!hw_priv || !cmd_args ||
+ (cmd_type >= CAM_FD_HW_CMD_MAX)) {
+ CAM_ERR(CAM_FD, "Invalid arguments %pK %pK %d", hw_priv,
+ cmd_args, cmd_type);
+ return -EINVAL;
+ }
+
+ switch (cmd_type) {
+ case CAM_FD_HW_CMD_REGISTER_CALLBACK: {
+ struct cam_fd_hw_cmd_set_irq_cb *irq_cb_args;
+ struct cam_fd_core *fd_core =
+ (struct cam_fd_core *)fd_hw->core_info;
+
+ if (sizeof(struct cam_fd_hw_cmd_set_irq_cb) != arg_size) {
+ CAM_ERR(CAM_FD, "cmd_type %d, size mismatch %d",
+ cmd_type, arg_size);
+ break;
+ }
+
+ irq_cb_args = (struct cam_fd_hw_cmd_set_irq_cb *)cmd_args;
+ fd_core->irq_cb.cam_fd_hw_mgr_cb =
+ irq_cb_args->cam_fd_hw_mgr_cb;
+ fd_core->irq_cb.data = irq_cb_args->data;
+ rc = 0;
+ break;
+ }
+ case CAM_FD_HW_CMD_PRESTART: {
+ struct cam_fd_hw_cmd_prestart_args *prestart_args;
+
+ if (sizeof(struct cam_fd_hw_cmd_prestart_args) != arg_size) {
+ CAM_ERR(CAM_FD, "cmd_type %d, size mismatch %d",
+ cmd_type, arg_size);
+ break;
+ }
+
+ prestart_args = (struct cam_fd_hw_cmd_prestart_args *)cmd_args;
+ rc = cam_fd_hw_util_processcmd_prestart(fd_hw, prestart_args);
+ break;
+ }
+ case CAM_FD_HW_CMD_FRAME_DONE: {
+ struct cam_fd_hw_frame_done_args *cmd_frame_results;
+
+ if (sizeof(struct cam_fd_hw_frame_done_args) !=
+ arg_size) {
+ CAM_ERR(CAM_FD, "cmd_type %d, size mismatch %d",
+ cmd_type, arg_size);
+ break;
+ }
+
+ cmd_frame_results =
+ (struct cam_fd_hw_frame_done_args *)cmd_args;
+ rc = cam_fd_hw_util_processcmd_frame_done(fd_hw,
+ cmd_frame_results);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.h b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.h
new file mode 100644
index 0000000..35bf6b6
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.h
@@ -0,0 +1,244 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_FD_HW_CORE_H_
+#define _CAM_FD_HW_CORE_H_
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <media/cam_defs.h>
+#include <media/cam_fd.h>
+
+#include "cam_common_util.h"
+#include "cam_debug_util.h"
+#include "cam_io_util.h"
+#include "cam_cpas_api.h"
+#include "cam_cdm_intf_api.h"
+#include "cam_fd_hw_intf.h"
+#include "cam_fd_hw_soc.h"
+
+#define CAM_FD_IRQ_TO_MASK(irq) (1 << (irq))
+#define CAM_FD_MASK_TO_IRQ(mask, irq) ((mask) >> (irq))
+
+#define CAM_FD_HW_HALT_RESET_TIMEOUT 3000
+
+/**
+ * enum cam_fd_core_state - FD Core internal states
+ *
+ * @CAM_FD_CORE_STATE_POWERDOWN : Indicates FD core is powered down
+ * @CAM_FD_CORE_STATE_IDLE : Indicates FD HW is in idle state.
+ * Core can be in this state when it is
+ * ready to process frames or when
+ * processing is finished and results are
+ * available
+ * @CAM_FD_CORE_STATE_PROCESSING : Indicates FD core is processing frame
+ * @CAM_FD_CORE_STATE_READING_RESULTS : Indicates results are being read from
+ * FD core
+ * @CAM_FD_CORE_STATE_RESET_PROGRESS : Indicates FD Core is in reset state
+ */
+enum cam_fd_core_state {
+ CAM_FD_CORE_STATE_POWERDOWN,
+ CAM_FD_CORE_STATE_IDLE,
+ CAM_FD_CORE_STATE_PROCESSING,
+ CAM_FD_CORE_STATE_READING_RESULTS,
+ CAM_FD_CORE_STATE_RESET_PROGRESS,
+};
+
+/**
+ * struct cam_fd_ctx_hw_private : HW private information for a specific hw ctx.
+ * This information is populated by HW layer on
+ * reserve() and given back to HW Mgr as private
+ * data for the hw context. This private_data
+ * has to be passed by HW Mgr layer while
+ * further HW layer calls
+ *
+ * @hw_ctx : Corresponding hw_ctx pointer
+ * @fd_hw : FD HW info pointer
+ * @cdm_handle : CDM Handle for this context
+ * @cdm_ops : CDM Ops
+ * @cdm_cmd : CDM command pointer
+ * @mode : Mode this context is running
+ * @curr_req_private : Current Request information
+ *
+ */
+struct cam_fd_ctx_hw_private {
+ void *hw_ctx;
+ struct cam_hw_info *fd_hw;
+ uint32_t cdm_handle;
+ struct cam_cdm_utils_ops *cdm_ops;
+ struct cam_cdm_bl_request *cdm_cmd;
+ enum cam_fd_hw_mode mode;
+ struct cam_fd_hw_req_private *curr_req_private;
+};
+
+/**
+ * struct cam_fd_core_regs : FD HW Core register offsets info
+ *
+ * @version : Offset of version register
+ * @control : Offset of control register
+ * @result_cnt : Offset of result count register
+ * @result_addr : Offset of results address register
+ * @image_addr : Offset of image address register
+ * @work_addr : Offset of work address register
+ * @ro_mode : Offset of ro_mode register
+ * @results_reg_base : Offset of results_reg_base register
+ * @raw_results_reg_base : Offset of raw_results_reg_base register
+ *
+ */
+struct cam_fd_core_regs {
+ uint32_t version;
+ uint32_t control;
+ uint32_t result_cnt;
+ uint32_t result_addr;
+ uint32_t image_addr;
+ uint32_t work_addr;
+ uint32_t ro_mode;
+ uint32_t results_reg_base;
+ uint32_t raw_results_reg_base;
+};
+
+/**
+ * struct cam_fd_core_regs : FD HW Wrapper register offsets info
+ *
+ * @wrapper_version : Offset of wrapper_version register
+ * @cgc_disable : Offset of cgc_disable register
+ * @hw_stop : Offset of hw_stop register
+ * @sw_reset : Offset of sw_reset register
+ * @vbif_req_priority : Offset of vbif_req_priority register
+ * @vbif_priority_level : Offset of vbif_priority_level register
+ * @vbif_done_status : Offset of vbif_done_status register
+ * @irq_mask : Offset of irq mask register
+ * @irq_status : Offset of irq status register
+ * @irq_clear : Offset of irq clear register
+ *
+ */
+struct cam_fd_wrapper_regs {
+ uint32_t wrapper_version;
+ uint32_t cgc_disable;
+ uint32_t hw_stop;
+ uint32_t sw_reset;
+ uint32_t vbif_req_priority;
+ uint32_t vbif_priority_level;
+ uint32_t vbif_done_status;
+ uint32_t irq_mask;
+ uint32_t irq_status;
+ uint32_t irq_clear;
+};
+
+/**
+ * struct cam_fd_hw_errata_wa : FD HW Errata workaround enable/dsiable info
+ *
+ * @single_irq_only : Whether to enable only one irq at any time
+ * @ro_mode_enable_always : Whether to enable ro mode always
+ * @ro_mode_results_invalid : Whether results written directly into output
+ * memory by HW are valid or not
+ */
+struct cam_fd_hw_errata_wa {
+ bool single_irq_only;
+ bool ro_mode_enable_always;
+ bool ro_mode_results_invalid;
+};
+
+/**
+ * struct cam_fd_hw_results_prop : FD HW Results properties
+ *
+ * @max_faces : Maximum number of faces supported
+ * @per_face_entries : Number of register with properties for each face
+ * @raw_results_entries : Number of raw results entries for the full search
+ * @raw_results_available : Whether raw results available on this HW
+ *
+ */
+struct cam_fd_hw_results_prop {
+ uint32_t max_faces;
+ uint32_t per_face_entries;
+ uint32_t raw_results_entries;
+ bool raw_results_available;
+};
+
+/**
+ * struct cam_fd_hw_static_info : FD HW information based on HW version
+ *
+ * @core_version : Core version of FD HW
+ * @wrapper_version : Wrapper version of FD HW
+ * @core_regs : Register offset information for core registers
+ * @wrapper_regs : Register offset information for wrapper registers
+ * @results : Information about results available on this HW
+ * @enable_errata_wa : Errata workaround information
+ * @irq_mask : IRQ mask to enable
+ * @qos_priority : QoS priority setting for this chipset
+ * @qos_priority_level : QoS priority level setting for this chipset
+ * @supported_modes : Supported HW modes on this HW version
+ * @ro_mode_supported : Whether RO mode is supported on this HW
+ *
+ */
+struct cam_fd_hw_static_info {
+ struct cam_hw_version core_version;
+ struct cam_hw_version wrapper_version;
+ struct cam_fd_core_regs core_regs;
+ struct cam_fd_wrapper_regs wrapper_regs;
+ struct cam_fd_hw_results_prop results;
+ struct cam_fd_hw_errata_wa enable_errata_wa;
+ uint32_t irq_mask;
+ uint32_t qos_priority;
+ uint32_t qos_priority_level;
+ uint32_t supported_modes;
+ bool ro_mode_supported;
+};
+
+/**
+ * struct cam_fd_core : FD HW core data structure
+ *
+ * @hw_static_info : HW information specific to version
+ * @hw_caps : HW capabilities
+ * @core_state : Current HW state
+ * @processing_complete : Whether processing is complete
+ * @reset_complete : Whether reset is complete
+ * @halt_complete : Whether halt is complete
+ * @hw_req_private : Request that is being currently processed by HW
+ * @results_valid : Whether HW frame results are available to get
+ * @spin_lock : Mutex to protect shared data in hw layer
+ * @irq_cb : HW Manager callback information
+ *
+ */
+struct cam_fd_core {
+ struct cam_fd_hw_static_info *hw_static_info;
+ struct cam_fd_hw_caps hw_caps;
+ enum cam_fd_core_state core_state;
+ struct completion processing_complete;
+ struct completion reset_complete;
+ struct completion halt_complete;
+ struct cam_fd_hw_req_private *hw_req_private;
+ bool results_valid;
+ spinlock_t spin_lock;
+ struct cam_fd_hw_cmd_set_irq_cb irq_cb;
+};
+
+int cam_fd_hw_util_get_hw_caps(struct cam_hw_info *fd_hw,
+ struct cam_fd_hw_caps *hw_caps);
+irqreturn_t cam_fd_hw_irq(int irq_num, void *data);
+
+int cam_fd_hw_get_hw_caps(void *hw_priv, void *get_hw_cap_args,
+ uint32_t arg_size);
+int cam_fd_hw_init(void *hw_priv, void *init_hw_args, uint32_t arg_size);
+int cam_fd_hw_deinit(void *hw_priv, void *deinit_hw_args, uint32_t arg_size);
+int cam_fd_hw_reset(void *hw_priv, void *reset_core_args, uint32_t arg_size);
+int cam_fd_hw_reserve(void *hw_priv, void *hw_reserve_args, uint32_t arg_size);
+int cam_fd_hw_release(void *hw_priv, void *hw_release_args, uint32_t arg_size);
+int cam_fd_hw_start(void *hw_priv, void *hw_start_args, uint32_t arg_size);
+int cam_fd_hw_halt_reset(void *hw_priv, void *stop_args, uint32_t arg_size);
+int cam_fd_hw_read(void *hw_priv, void *read_args, uint32_t arg_size);
+int cam_fd_hw_write(void *hw_priv, void *write_args, uint32_t arg_size);
+int cam_fd_hw_process_cmd(void *hw_priv, uint32_t cmd_type,
+ void *cmd_args, uint32_t arg_size);
+
+#endif /* _CAM_FD_HW_CORE_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_dev.c b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_dev.c
new file mode 100644
index 0000000..803da76
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_dev.c
@@ -0,0 +1,223 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include "cam_subdev.h"
+#include "cam_fd_hw_intf.h"
+#include "cam_fd_hw_core.h"
+#include "cam_fd_hw_soc.h"
+#include "cam_fd_hw_v41.h"
+
+static int cam_fd_hw_dev_probe(struct platform_device *pdev)
+{
+ struct cam_hw_info *fd_hw;
+ struct cam_hw_intf *fd_hw_intf;
+ struct cam_fd_core *fd_core;
+ const struct of_device_id *match_dev = NULL;
+ struct cam_fd_hw_static_info *hw_static_info = NULL;
+ int rc = 0;
+ struct cam_fd_hw_init_args init_args;
+ struct cam_fd_hw_deinit_args deinit_args;
+
+ fd_hw_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL);
+ if (!fd_hw_intf)
+ return -ENOMEM;
+
+ fd_hw = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL);
+ if (!fd_hw) {
+ kfree(fd_hw_intf);
+ return -ENOMEM;
+ }
+
+ fd_core = kzalloc(sizeof(struct cam_fd_core), GFP_KERNEL);
+ if (!fd_core) {
+ kfree(fd_hw);
+ kfree(fd_hw_intf);
+ return -ENOMEM;
+ }
+
+ fd_hw_intf->hw_priv = fd_hw;
+ fd_hw->core_info = fd_core;
+
+ fd_hw->hw_state = CAM_HW_STATE_POWER_DOWN;
+ fd_hw->soc_info.pdev = pdev;
+ fd_hw->soc_info.dev = &pdev->dev;
+ fd_hw->soc_info.dev_name = pdev->name;
+ fd_hw->open_count = 0;
+ mutex_init(&fd_hw->hw_mutex);
+ spin_lock_init(&fd_hw->hw_lock);
+ init_completion(&fd_hw->hw_complete);
+
+ spin_lock_init(&fd_core->spin_lock);
+ init_completion(&fd_core->processing_complete);
+ init_completion(&fd_core->halt_complete);
+ init_completion(&fd_core->reset_complete);
+
+ fd_hw_intf->hw_ops.get_hw_caps = cam_fd_hw_get_hw_caps;
+ fd_hw_intf->hw_ops.init = cam_fd_hw_init;
+ fd_hw_intf->hw_ops.deinit = cam_fd_hw_deinit;
+ fd_hw_intf->hw_ops.reset = cam_fd_hw_reset;
+ fd_hw_intf->hw_ops.reserve = cam_fd_hw_reserve;
+ fd_hw_intf->hw_ops.release = cam_fd_hw_release;
+ fd_hw_intf->hw_ops.start = cam_fd_hw_start;
+ fd_hw_intf->hw_ops.stop = cam_fd_hw_halt_reset;
+ fd_hw_intf->hw_ops.read = NULL;
+ fd_hw_intf->hw_ops.write = NULL;
+ fd_hw_intf->hw_ops.process_cmd = cam_fd_hw_process_cmd;
+ fd_hw_intf->hw_type = CAM_HW_FD;
+
+ match_dev = of_match_device(pdev->dev.driver->of_match_table,
+ &pdev->dev);
+ if (!match_dev || !match_dev->data) {
+ CAM_ERR(CAM_FD, "No Of_match data, %pK", match_dev);
+ rc = -EINVAL;
+ goto free_memory;
+ }
+ hw_static_info = (struct cam_fd_hw_static_info *)match_dev->data;
+ fd_core->hw_static_info = hw_static_info;
+
+ CAM_DBG(CAM_FD, "HW Static Info : version core[%d.%d] wrapper[%d.%d]",
+ hw_static_info->core_version.major,
+ hw_static_info->core_version.minor,
+ hw_static_info->wrapper_version.major,
+ hw_static_info->wrapper_version.minor);
+
+ rc = cam_fd_soc_init_resources(&fd_hw->soc_info, cam_fd_hw_irq, fd_hw);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed to init soc, rc=%d", rc);
+ goto free_memory;
+ }
+
+ fd_hw_intf->hw_idx = fd_hw->soc_info.index;
+
+ memset(&init_args, 0x0, sizeof(init_args));
+ memset(&deinit_args, 0x0, sizeof(deinit_args));
+ rc = cam_fd_hw_init(fd_hw, &init_args, sizeof(init_args));
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed to hw init, rc=%d", rc);
+ goto deinit_platform_res;
+ }
+
+ rc = cam_fd_hw_util_get_hw_caps(fd_hw, &fd_core->hw_caps);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed to get hw caps, rc=%d", rc);
+ goto deinit_hw;
+ }
+
+ rc = cam_fd_hw_deinit(fd_hw, &deinit_args, sizeof(deinit_args));
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed to deinit hw, rc=%d", rc);
+ goto deinit_platform_res;
+ }
+
+ platform_set_drvdata(pdev, fd_hw_intf);
+ CAM_DBG(CAM_FD, "FD-%d probe successful", fd_hw_intf->hw_idx);
+
+ return rc;
+
+deinit_hw:
+ if (cam_fd_hw_deinit(fd_hw, &deinit_args, sizeof(deinit_args)))
+ CAM_ERR(CAM_FD, "Failed in hw deinit");
+deinit_platform_res:
+ if (cam_fd_soc_deinit_resources(&fd_hw->soc_info))
+ CAM_ERR(CAM_FD, "Failed in soc deinit");
+ mutex_destroy(&fd_hw->hw_mutex);
+free_memory:
+ kfree(fd_hw);
+ kfree(fd_hw_intf);
+ kfree(fd_core);
+
+ return rc;
+}
+
+static int cam_fd_hw_dev_remove(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct cam_hw_intf *fd_hw_intf;
+ struct cam_hw_info *fd_hw;
+ struct cam_fd_core *fd_core;
+
+ fd_hw_intf = platform_get_drvdata(pdev);
+ if (!fd_hw_intf) {
+ CAM_ERR(CAM_FD, "Invalid fd_hw_intf from pdev");
+ return -EINVAL;
+ }
+
+ fd_hw = fd_hw_intf->hw_priv;
+ if (!fd_hw) {
+ CAM_ERR(CAM_FD, "Invalid fd_hw from fd_hw_intf");
+ rc = -ENODEV;
+ goto free_fd_hw_intf;
+ }
+
+ fd_core = (struct cam_fd_core *)fd_hw->core_info;
+ if (!fd_core) {
+ CAM_ERR(CAM_FD, "Invalid fd_core from fd_hw");
+ rc = -EINVAL;
+ goto deinit_platform_res;
+ }
+
+ kfree(fd_core);
+
+deinit_platform_res:
+ rc = cam_fd_soc_deinit_resources(&fd_hw->soc_info);
+ if (rc)
+ CAM_ERR(CAM_FD, "Error in FD soc deinit, rc=%d", rc);
+
+ mutex_destroy(&fd_hw->hw_mutex);
+ kfree(fd_hw);
+
+free_fd_hw_intf:
+ kfree(fd_hw_intf);
+
+ return rc;
+}
+
+static const struct of_device_id cam_fd_hw_dt_match[] = {
+ {
+ .compatible = "qcom,fd41",
+ .data = &cam_fd_wrapper120_core410_info,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cam_fd_hw_dt_match);
+
+static struct platform_driver cam_fd_hw_driver = {
+ .probe = cam_fd_hw_dev_probe,
+ .remove = cam_fd_hw_dev_remove,
+ .driver = {
+ .name = "cam_fd_hw",
+ .owner = THIS_MODULE,
+ .of_match_table = cam_fd_hw_dt_match,
+ },
+};
+
+static int __init cam_fd_hw_init_module(void)
+{
+ return platform_driver_register(&cam_fd_hw_driver);
+}
+
+static void __exit cam_fd_hw_exit_module(void)
+{
+ platform_driver_unregister(&cam_fd_hw_driver);
+}
+
+module_init(cam_fd_hw_init_module);
+module_exit(cam_fd_hw_exit_module);
+MODULE_DESCRIPTION("CAM FD HW driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_intf.h b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_intf.h
new file mode 100644
index 0000000..aae7648
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_intf.h
@@ -0,0 +1,289 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_FD_HW_INTF_H_
+#define _CAM_FD_HW_INTF_H_
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <media/cam_cpas.h>
+#include <media/cam_req_mgr.h>
+#include <media/cam_fd.h>
+
+#include "cam_io_util.h"
+#include "cam_soc_util.h"
+#include "cam_hw.h"
+#include "cam_hw_intf.h"
+#include "cam_subdev.h"
+#include "cam_cpas_api.h"
+#include "cam_hw_mgr_intf.h"
+#include "cam_debug_util.h"
+
+#define CAM_FD_MAX_IO_BUFFERS 5
+#define CAM_FD_MAX_HW_ENTRIES 5
+
+/**
+ * enum cam_fd_hw_type - Enum for FD HW type
+ *
+ * @CAM_HW_FD : FaceDetection HW type
+ */
+enum cam_fd_hw_type {
+ CAM_HW_FD,
+};
+
+/**
+ * enum cam_fd_hw_mode - Mode in which HW can run
+ *
+ * @CAM_FD_MODE_FACEDETECTION : Face Detection mode in which face search
+ * is done on the given frame
+ * @CAM_FD_MODE_PYRAMID : Pyramid mode where a pyramid image is generated
+ * from an input image
+ */
+enum cam_fd_hw_mode {
+ CAM_FD_MODE_FACEDETECTION = 0x1,
+ CAM_FD_MODE_PYRAMID = 0x2,
+};
+
+/**
+ * enum cam_fd_priority - FD priority levels
+ *
+ * @CAM_FD_PRIORITY_HIGH : Indicates high priority client, driver prioritizes
+ * frame requests coming from contexts with HIGH
+ * priority compared to context with normal priority
+ * @CAM_FD_PRIORITY_NORMAL : Indicates normal priority client
+ */
+enum cam_fd_priority {
+ CAM_FD_PRIORITY_HIGH = 0x0,
+ CAM_FD_PRIORITY_NORMAL,
+};
+
+/**
+ * enum cam_fd_hw_irq_type - FD HW IRQ types
+ *
+ * @CAM_FD_IRQ_FRAME_DONE : Indicates frame processing is finished
+ * @CAM_FD_IRQ_HALT_DONE : Indicates HW halt is finished
+ * @CAM_FD_IRQ_RESET_DONE : Indicates HW reset is finished
+ */
+enum cam_fd_hw_irq_type {
+ CAM_FD_IRQ_FRAME_DONE,
+ CAM_FD_IRQ_HALT_DONE,
+ CAM_FD_IRQ_RESET_DONE,
+};
+
+/**
+ * enum cam_fd_hw_cmd_type - FD HW layer custom commands
+ *
+ * @CAM_FD_HW_CMD_PRESTART : Command to process pre-start settings
+ * @CAM_FD_HW_CMD_FRAME_DONE : Command to process frame done settings
+ * @CAM_FD_HW_CMD_UPDATE_SOC : Command to process soc update
+ * @CAM_FD_HW_CMD_REGISTER_CALLBACK : Command to set hw mgr callback
+ * @CAM_FD_HW_CMD_MAX : Indicates max cmd
+ */
+enum cam_fd_hw_cmd_type {
+ CAM_FD_HW_CMD_PRESTART,
+ CAM_FD_HW_CMD_FRAME_DONE,
+ CAM_FD_HW_CMD_UPDATE_SOC,
+ CAM_FD_HW_CMD_REGISTER_CALLBACK,
+ CAM_FD_HW_CMD_MAX,
+};
+
+/**
+ * struct cam_fd_hw_io_buffer : FD HW IO Buffer information
+ *
+ * @valid : Whether this IO Buf configuration is valid
+ * @io_cfg : IO Configuration information
+ * @num_buf : Number planes in io_addr, cpu_addr array
+ * @io_addr : Array of IO address information for planes
+ * @cpu_addr : Array of CPU address information for planes
+ */
+struct cam_fd_hw_io_buffer {
+ bool valid;
+ struct cam_buf_io_cfg *io_cfg;
+ uint32_t num_buf;
+ uint64_t io_addr[CAM_PACKET_MAX_PLANES];
+ uint64_t cpu_addr[CAM_PACKET_MAX_PLANES];
+};
+
+/**
+ * struct cam_fd_hw_req_private : FD HW layer's private information
+ * specific to a request
+ *
+ * @ctx_hw_private : FD HW layer's ctx specific private data
+ * @request_id : Request ID corresponding to this private information
+ * @get_raw_results : Whether to get raw results for this request
+ * @ro_mode_enabled : Whether RO mode is enabled for this request
+ * @fd_results : Pointer to save face detection results
+ * @raw_results : Pointer to save face detection raw results
+ */
+struct cam_fd_hw_req_private {
+ void *ctx_hw_private;
+ uint64_t request_id;
+ bool get_raw_results;
+ bool ro_mode_enabled;
+ struct cam_fd_results *fd_results;
+ uint32_t *raw_results;
+};
+
+/**
+ * struct cam_fd_hw_reserve_args : Reserve args for this HW context
+ *
+ * @hw_ctx : HW context for which reserve is requested
+ * @mode : Mode for which this reserve is requested
+ * @ctx_hw_private : Pointer to save HW layer's private information specific
+ * to this hw context. This has to be passed while calling
+ * further HW layer calls
+ */
+struct cam_fd_hw_reserve_args {
+ void *hw_ctx;
+ enum cam_fd_hw_mode mode;
+ void *ctx_hw_private;
+};
+
+/**
+ * struct cam_fd_hw_release_args : Release args for this HW context
+ *
+ * @hw_ctx : HW context for which release is requested
+ * @ctx_hw_private : HW layer's private information specific to this hw context
+ */
+struct cam_fd_hw_release_args {
+ void *hw_ctx;
+ void *ctx_hw_private;
+};
+
+/**
+ * struct cam_fd_hw_init_args : Init args for this HW context
+ *
+ * @hw_ctx : HW context for which init is requested
+ * @ctx_hw_private : HW layer's private information specific to this hw context
+ */
+struct cam_fd_hw_init_args {
+ void *hw_ctx;
+ void *ctx_hw_private;
+};
+
+/**
+ * struct cam_fd_hw_deinit_args : Deinit args for this HW context
+ *
+ * @hw_ctx : HW context for which deinit is requested
+ * @ctx_hw_private : HW layer's private information specific to this hw context
+ */
+struct cam_fd_hw_deinit_args {
+ void *hw_ctx;
+ void *ctx_hw_private;
+};
+
+/**
+ * struct cam_fd_hw_cmd_prestart_args : Prestart command args
+ *
+ * @hw_ctx : HW context which submitted this prestart
+ * @ctx_hw_private : HW layer's private information specific to
+ * this hw context
+ * @request_id : Request ID corresponds to this pre-start command
+ * @get_raw_results : Whether to get raw results for this request
+ * @input_buf : Input IO Buffer information for this request
+ * @output_buf : Output IO Buffer information for this request
+ * @cmd_buf_addr : Command buffer address to fill kmd commands
+ * @size : Size available in command buffer
+ * @pre_config_buf_size : Buffer size filled with commands by KMD that has
+ * to be inserted before umd commands
+ * @post_config_buf_size : Buffer size filled with commands by KMD that has
+ * to be inserted after umd commands
+ * @hw_req_private : HW layer's private information specific to
+ * this request
+ */
+struct cam_fd_hw_cmd_prestart_args {
+ void *hw_ctx;
+ void *ctx_hw_private;
+ uint64_t request_id;
+ bool get_raw_results;
+ struct cam_fd_hw_io_buffer input_buf[CAM_FD_MAX_IO_BUFFERS];
+ struct cam_fd_hw_io_buffer output_buf[CAM_FD_MAX_IO_BUFFERS];
+ uint32_t *cmd_buf_addr;
+ uint32_t size;
+ uint32_t pre_config_buf_size;
+ uint32_t post_config_buf_size;
+ struct cam_fd_hw_req_private hw_req_private;
+};
+
+/**
+ * struct cam_fd_hw_cmd_start_args : Start command args
+ *
+ * @hw_ctx : HW context which submitting start command
+ * @ctx_hw_private : HW layer's private information specific to
+ * this hw context
+ * @hw_req_private : HW layer's private information specific to
+ * this request
+ * @hw_update_entries : HW update entries corresponds to this request
+ * @num_hw_update_entries : Number of hw update entries
+ */
+struct cam_fd_hw_cmd_start_args {
+ void *hw_ctx;
+ void *ctx_hw_private;
+ struct cam_fd_hw_req_private *hw_req_private;
+ struct cam_hw_update_entry *hw_update_entries;
+ uint32_t num_hw_update_entries;
+};
+
+/**
+ * struct cam_fd_hw_stop_args : Stop command args
+ *
+ * @hw_ctx : HW context which submitting stop command
+ * @ctx_hw_private : HW layer's private information specific to this hw context
+ * @request_id : Request ID that need to be stopped
+ * @hw_req_private : HW layer's private information specific to this request
+ */
+struct cam_fd_hw_stop_args {
+ void *hw_ctx;
+ void *ctx_hw_private;
+ uint64_t request_id;
+ struct cam_fd_hw_req_private *hw_req_private;
+};
+
+/**
+ * struct cam_fd_hw_frame_done_args : Frame done command args
+ *
+ * @hw_ctx : HW context which submitting frame done request
+ * @ctx_hw_private : HW layer's private information specific to this hw context
+ * @request_id : Request ID that need to be stopped
+ * @hw_req_private : HW layer's private information specific to this request
+ */
+struct cam_fd_hw_frame_done_args {
+ void *hw_ctx;
+ void *ctx_hw_private;
+ uint64_t request_id;
+ struct cam_fd_hw_req_private *hw_req_private;
+};
+
+/**
+ * struct cam_fd_hw_reset_args : Reset command args
+ *
+ * @hw_ctx : HW context which submitting reset command
+ * @ctx_hw_private : HW layer's private information specific to this hw context
+ */
+struct cam_fd_hw_reset_args {
+ void *hw_ctx;
+ void *ctx_hw_private;
+};
+
+/**
+ * struct cam_fd_hw_cmd_set_irq_cb : Set IRQ callback command args
+ *
+ * @cam_fd_hw_mgr_cb : HW Mgr's callback pointer
+ * @data : HW Mgr's private data
+ */
+struct cam_fd_hw_cmd_set_irq_cb {
+ int (*cam_fd_hw_mgr_cb)(void *data, enum cam_fd_hw_irq_type irq_type);
+ void *data;
+};
+
+#endif /* _CAM_FD_HW_INTF_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.c b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.c
new file mode 100644
index 0000000..9045dc1
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.c
@@ -0,0 +1,285 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include "cam_fd_hw_core.h"
+#include "cam_fd_hw_soc.h"
+
+static void cam_fd_hw_util_cpas_callback(uint32_t handle, void *userdata,
+ enum cam_camnoc_irq_type event_type, uint32_t event_data)
+{
+ CAM_DBG(CAM_FD, "CPAS hdl=%d, udata=%pK, event=%d, event_data=%d",
+ handle, userdata, event_type, event_data);
+}
+
+static int cam_fd_hw_soc_util_setup_regbase_indices(
+ struct cam_hw_soc_info *soc_info)
+{
+ struct cam_fd_soc_private *soc_private =
+ (struct cam_fd_soc_private *)soc_info->soc_private;
+ uint32_t index;
+ int rc, i;
+
+ for (i = 0; i < CAM_FD_REG_MAX; i++)
+ soc_private->regbase_index[i] = -1;
+
+ if ((soc_info->num_mem_block > CAM_SOC_MAX_BLOCK) ||
+ (soc_info->num_mem_block != CAM_FD_REG_MAX)) {
+ CAM_ERR(CAM_FD, "Invalid num_mem_block=%d",
+ soc_info->num_mem_block);
+ return -EINVAL;
+ }
+
+ rc = cam_common_util_get_string_index(soc_info->mem_block_name,
+ soc_info->num_mem_block, "fd_core", &index);
+ if ((rc == 0) && (index < CAM_FD_REG_MAX)) {
+ soc_private->regbase_index[CAM_FD_REG_CORE] = index;
+ } else {
+ CAM_ERR(CAM_FD, "regbase not found for FD_CORE, rc=%d, %d %d",
+ rc, index, CAM_FD_REG_MAX);
+ return -EINVAL;
+ }
+
+ rc = cam_common_util_get_string_index(soc_info->mem_block_name,
+ soc_info->num_mem_block, "fd_wrapper", &index);
+ if ((rc == 0) && (index < CAM_FD_REG_MAX)) {
+ soc_private->regbase_index[CAM_FD_REG_WRAPPER] = index;
+ } else {
+ CAM_ERR(CAM_FD, "regbase not found FD_WRAPPER, rc=%d, %d %d",
+ rc, index, CAM_FD_REG_MAX);
+ return -EINVAL;
+ }
+
+ CAM_DBG(CAM_FD, "Reg indices : CORE=%d, WRAPPER=%d",
+ soc_private->regbase_index[CAM_FD_REG_CORE],
+ soc_private->regbase_index[CAM_FD_REG_WRAPPER]);
+
+ return 0;
+}
+
+static int cam_fd_soc_set_clk_flags(struct cam_hw_soc_info *soc_info)
+{
+ int i, rc = 0;
+
+ if (soc_info->num_clk > CAM_SOC_MAX_CLK) {
+ CAM_ERR(CAM_FD, "Invalid num clk %d", soc_info->num_clk);
+ return -EINVAL;
+ }
+
+ /* set memcore and mem periphery logic flags to 0 */
+ for (i = 0; i < soc_info->num_clk; i++) {
+ if ((strcmp(soc_info->clk_name[i], "fd_core_clk") == 0) ||
+ (strcmp(soc_info->clk_name[i], "fd_core_uar_clk") ==
+ 0)) {
+ rc = cam_soc_util_set_clk_flags(soc_info, i,
+ CLKFLAG_NORETAIN_MEM);
+ if (rc)
+ CAM_ERR(CAM_FD,
+ "Failed in NORETAIN_MEM i=%d, rc=%d",
+ i, rc);
+
+ cam_soc_util_set_clk_flags(soc_info, i,
+ CLKFLAG_NORETAIN_PERIPH);
+ if (rc)
+ CAM_ERR(CAM_FD,
+ "Failed in NORETAIN_PERIPH i=%d, rc=%d",
+ i, rc);
+ }
+ }
+
+ return rc;
+}
+
+void cam_fd_soc_register_write(struct cam_hw_soc_info *soc_info,
+ enum cam_fd_reg_base reg_base, uint32_t reg_offset, uint32_t reg_value)
+{
+ struct cam_fd_soc_private *soc_private =
+ (struct cam_fd_soc_private *)soc_info->soc_private;
+ int32_t reg_index = soc_private->regbase_index[reg_base];
+
+ CAM_DBG(CAM_FD, "FD_REG_WRITE: Base[%d] Offset[0x%8x] Value[0x%8x]",
+ reg_base, reg_offset, reg_value);
+
+ cam_io_w_mb(reg_value,
+ soc_info->reg_map[reg_index].mem_base + reg_offset);
+}
+
+uint32_t cam_fd_soc_register_read(struct cam_hw_soc_info *soc_info,
+ enum cam_fd_reg_base reg_base, uint32_t reg_offset)
+{
+ struct cam_fd_soc_private *soc_private =
+ (struct cam_fd_soc_private *)soc_info->soc_private;
+ int32_t reg_index = soc_private->regbase_index[reg_base];
+ uint32_t reg_value;
+
+ reg_value = cam_io_r_mb(
+ soc_info->reg_map[reg_index].mem_base + reg_offset);
+
+ CAM_DBG(CAM_FD, "FD_REG_READ: Base[%d] Offset[0x%8x] Value[0x%8x]",
+ reg_base, reg_offset, reg_value);
+
+ return reg_value;
+}
+
+int cam_fd_soc_enable_resources(struct cam_hw_soc_info *soc_info)
+{
+ struct cam_fd_soc_private *soc_private = soc_info->soc_private;
+ struct cam_ahb_vote ahb_vote;
+ struct cam_axi_vote axi_vote;
+ int rc;
+
+ ahb_vote.type = CAM_VOTE_ABSOLUTE;
+ ahb_vote.vote.level = CAM_SVS_VOTE;
+ axi_vote.compressed_bw = 7200000;
+ axi_vote.uncompressed_bw = 7200000;
+ rc = cam_cpas_start(soc_private->cpas_handle, &ahb_vote, &axi_vote);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Error in CPAS START, rc=%d", rc);
+ return -EFAULT;
+ }
+
+ rc = cam_soc_util_enable_platform_resource(soc_info, true, CAM_SVS_VOTE,
+ true);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Error enable platform failed, rc=%d", rc);
+ goto stop_cpas;
+ }
+
+ return rc;
+
+stop_cpas:
+ if (cam_cpas_stop(soc_private->cpas_handle))
+ CAM_ERR(CAM_FD, "Error in CPAS STOP");
+
+ return rc;
+}
+
+
+int cam_fd_soc_disable_resources(struct cam_hw_soc_info *soc_info)
+{
+ struct cam_fd_soc_private *soc_private;
+ int rc = 0;
+
+ if (!soc_info) {
+ CAM_ERR(CAM_FD, "Invalid soc_info param");
+ return -EINVAL;
+ }
+ soc_private = soc_info->soc_private;
+
+ rc = cam_soc_util_disable_platform_resource(soc_info, true, true);
+ if (rc) {
+ CAM_ERR(CAM_FD, "disable platform resources failed, rc=%d", rc);
+ return rc;
+ }
+
+ rc = cam_cpas_stop(soc_private->cpas_handle);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Error in CPAS STOP, handle=0x%x, rc=%d",
+ soc_private->cpas_handle, rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+int cam_fd_soc_init_resources(struct cam_hw_soc_info *soc_info,
+ irq_handler_t irq_handler, void *private_data)
+{
+ struct cam_fd_soc_private *soc_private;
+ struct cam_cpas_register_params cpas_register_param;
+ int rc;
+
+ rc = cam_soc_util_get_dt_properties(soc_info);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in get_dt_properties, rc=%d", rc);
+ return rc;
+ }
+
+ rc = cam_soc_util_request_platform_resource(soc_info, irq_handler,
+ private_data);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in request_platform_resource rc=%d",
+ rc);
+ return rc;
+ }
+
+ rc = cam_fd_soc_set_clk_flags(soc_info);
+ if (rc) {
+ CAM_ERR(CAM_FD, "failed in set_clk_flags rc=%d", rc);
+ goto release_res;
+ }
+
+ soc_private = kzalloc(sizeof(struct cam_fd_soc_private), GFP_KERNEL);
+ if (!soc_private) {
+ rc = -ENOMEM;
+ goto release_res;
+ }
+ soc_info->soc_private = soc_private;
+
+ rc = cam_fd_hw_soc_util_setup_regbase_indices(soc_info);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in setup regbase, rc=%d", rc);
+ goto free_soc_private;
+ }
+
+ memset(&cpas_register_param, 0, sizeof(cpas_register_param));
+ strlcpy(cpas_register_param.identifier, "fd", CAM_HW_IDENTIFIER_LENGTH);
+ cpas_register_param.cell_index = soc_info->index;
+ cpas_register_param.dev = &soc_info->pdev->dev;
+ cpas_register_param.userdata = private_data;
+ cpas_register_param.cam_cpas_client_cb = cam_fd_hw_util_cpas_callback;
+
+ rc = cam_cpas_register_client(&cpas_register_param);
+ if (rc) {
+ CAM_ERR(CAM_FD, "CPAS registration failed");
+ goto free_soc_private;
+ }
+ soc_private->cpas_handle = cpas_register_param.client_handle;
+ CAM_DBG(CAM_FD, "CPAS handle=%d", soc_private->cpas_handle);
+
+ return rc;
+
+free_soc_private:
+ kfree(soc_info->soc_private);
+ soc_info->soc_private = NULL;
+release_res:
+ cam_soc_util_release_platform_resource(soc_info);
+
+ return rc;
+}
+
+int cam_fd_soc_deinit_resources(struct cam_hw_soc_info *soc_info)
+{
+ struct cam_fd_soc_private *soc_private =
+ (struct cam_fd_soc_private *)soc_info->soc_private;
+ int rc;
+
+ rc = cam_cpas_unregister_client(soc_private->cpas_handle);
+ if (rc)
+ CAM_ERR(CAM_FD, "Unregister cpas failed, handle=%d, rc=%d",
+ soc_private->cpas_handle, rc);
+
+ rc = cam_soc_util_release_platform_resource(soc_info);
+ if (rc)
+ CAM_ERR(CAM_FD, "release platform failed, rc=%d", rc);
+
+ kfree(soc_info->soc_private);
+ soc_info->soc_private = NULL;
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.h b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.h
new file mode 100644
index 0000000..4a22293
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.h
@@ -0,0 +1,53 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_FD_HW_SOC_H_
+#define _CAM_FD_HW_SOC_H_
+
+#include "cam_soc_util.h"
+
+/**
+ * enum cam_fd_reg_base - Enum for FD register sets
+ *
+ * @CAM_FD_REG_CORE : Indicates FD Core register space
+ * @CAM_FD_REG_WRAPPER : Indicates FD Wrapper register space
+ * @CAM_FD_REG_MAX : Max number of register sets supported
+ */
+enum cam_fd_reg_base {
+ CAM_FD_REG_CORE,
+ CAM_FD_REG_WRAPPER,
+ CAM_FD_REG_MAX
+};
+
+/**
+ * struct cam_fd_soc_private : FD private SOC information
+ *
+ * @regbase_index : Mapping between Register base enum to register index in SOC
+ * @cpas_handle : CPAS handle
+ *
+ */
+struct cam_fd_soc_private {
+ int32_t regbase_index[CAM_FD_REG_MAX];
+ uint32_t cpas_handle;
+};
+
+int cam_fd_soc_init_resources(struct cam_hw_soc_info *soc_info,
+ irq_handler_t irq_handler, void *private_data);
+int cam_fd_soc_deinit_resources(struct cam_hw_soc_info *soc_info);
+int cam_fd_soc_enable_resources(struct cam_hw_soc_info *soc_info);
+int cam_fd_soc_disable_resources(struct cam_hw_soc_info *soc_info);
+uint32_t cam_fd_soc_register_read(struct cam_hw_soc_info *soc_info,
+ enum cam_fd_reg_base reg_base, uint32_t reg_offset);
+void cam_fd_soc_register_write(struct cam_hw_soc_info *soc_info,
+ enum cam_fd_reg_base reg_base, uint32_t reg_offset, uint32_t reg_value);
+
+#endif /* _CAM_FD_HW_SOC_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_v41.h b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_v41.h
new file mode 100644
index 0000000..70448bb
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_v41.h
@@ -0,0 +1,70 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_FD_HW_V41_H_
+#define _CAM_FD_HW_V41_H_
+
+static struct cam_fd_hw_static_info cam_fd_wrapper120_core410_info = {
+ .core_version = {
+ .major = 4,
+ .minor = 1,
+ .incr = 0,
+ },
+ .wrapper_version = {
+ .major = 1,
+ .minor = 2,
+ .incr = 0,
+ },
+ .core_regs = {
+ .version = 0x38,
+ .control = 0x0,
+ .result_cnt = 0x4,
+ .result_addr = 0x20,
+ .image_addr = 0x24,
+ .work_addr = 0x28,
+ .ro_mode = 0x34,
+ .results_reg_base = 0x400,
+ .raw_results_reg_base = 0x800,
+ },
+ .wrapper_regs = {
+ .wrapper_version = 0x0,
+ .cgc_disable = 0x4,
+ .hw_stop = 0x8,
+ .sw_reset = 0x10,
+ .vbif_req_priority = 0x20,
+ .vbif_priority_level = 0x24,
+ .vbif_done_status = 0x34,
+ .irq_mask = 0x50,
+ .irq_status = 0x54,
+ .irq_clear = 0x58,
+ },
+ .results = {
+ .max_faces = 35,
+ .per_face_entries = 4,
+ .raw_results_available = true,
+ .raw_results_entries = 512,
+ },
+ .enable_errata_wa = {
+ .single_irq_only = true,
+ .ro_mode_enable_always = true,
+ .ro_mode_results_invalid = true,
+ },
+ .irq_mask = CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_FRAME_DONE) |
+ CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_HALT_DONE) |
+ CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_RESET_DONE),
+ .qos_priority = 4,
+ .qos_priority_level = 4,
+ .supported_modes = CAM_FD_MODE_FACEDETECTION | CAM_FD_MODE_PYRAMID,
+ .ro_mode_supported = true,
+};
+
+#endif /* _CAM_FD_HW_V41_H_ */