| /* 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" |
| #include "cam_trace.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) |
| { |
| trace_cam_cdm_cb("FD", status); |
| 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); |
| |
| 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_WARN(CAM_FD, "HW reset timeout time_left=%d", time_left); |
| |
| cam_fd_soc_register_write(soc_info, CAM_FD_REG_CORE, |
| hw_static_info->core_regs.control, 0x1); |
| |
| 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); |
| |
| 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_WARN(CAM_FD, "HW halt timeout time_left=%d", time_left); |
| |
| 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; |
| |
| /* Insert start trigger command into CDM as post config commands. */ |
| num_cmds = cam_fd_cdm_write_reg_val_pair(reg_val_pair, 0, |
| hw_static_info->core_regs.control, 0x2); |
| size = ctx_hw_private->cdm_ops->cdm_required_size_reg_random( |
| num_cmds/2); |
| 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); |
| |
| prestart_args->post_config_buf_size = size * 4; |
| |
| 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; |
| } |
| |
| trace_cam_irq_activated("FD", irq_type); |
| |
| 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; |
| 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; |
| |
| cdm_streamon: |
| 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); |
| |
| 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 = NULL; |
| 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; |
| } |
| |
| 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 > 0) { |
| rc = 0; |
| goto positive_ref_cnt; |
| } |
| |
| 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 = (struct cam_fd_core *)fd_hw->core_info; |
| |
| /* With the ref_cnt correct, this should never happen */ |
| WARN_ON(!fd_core); |
| |
| fd_core->core_state = CAM_FD_CORE_STATE_POWERDOWN; |
| |
| positive_ref_cnt: |
| 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); |
| } |
| } |
| |
| 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_halt(fd_hw); |
| if (rc) { |
| CAM_ERR(CAM_FD, "Failed in HALT rc=%d", rc); |
| return rc; |
| } |
| |
| 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; |
| } |
| |
| 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; |
| } |