| /* 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/slab.h> |
| #include "cam_io_util.h" |
| #include "cam_cdm_util.h" |
| #include "cam_vfe_hw_intf.h" |
| #include "cam_vfe_top.h" |
| #include "cam_vfe_top_ver2.h" |
| #include "cam_debug_util.h" |
| |
| #define CAM_VFE_HW_RESET_HW_AND_REG_VAL 0x00003F9F |
| #define CAM_VFE_HW_RESET_HW_VAL 0x00003F87 |
| |
| struct cam_vfe_top_ver2_common_data { |
| struct cam_hw_soc_info *soc_info; |
| struct cam_hw_intf *hw_intf; |
| struct cam_vfe_top_ver2_reg_offset_common *common_reg; |
| }; |
| |
| struct cam_vfe_top_ver2_priv { |
| struct cam_vfe_top_ver2_common_data common_data; |
| struct cam_vfe_camif *camif; |
| struct cam_isp_resource_node mux_rsrc[CAM_VFE_TOP_VER2_MUX_MAX]; |
| }; |
| |
| static int cam_vfe_top_mux_get_base(struct cam_vfe_top_ver2_priv *top_priv, |
| void *cmd_args, uint32_t arg_size) |
| { |
| uint32_t size = 0; |
| uint32_t mem_base = 0; |
| struct cam_isp_hw_get_cmd_update *cdm_args = cmd_args; |
| struct cam_cdm_utils_ops *cdm_util_ops = NULL; |
| |
| if (arg_size != sizeof(struct cam_isp_hw_get_cmd_update)) { |
| CAM_ERR(CAM_ISP, "Error! Invalid cmd size"); |
| return -EINVAL; |
| } |
| |
| if (!cdm_args || !cdm_args->res || !top_priv || |
| !top_priv->common_data.soc_info) { |
| CAM_ERR(CAM_ISP, "Error! Invalid args"); |
| return -EINVAL; |
| } |
| |
| cdm_util_ops = |
| (struct cam_cdm_utils_ops *)cdm_args->res->cdm_ops; |
| |
| if (!cdm_util_ops) { |
| CAM_ERR(CAM_ISP, "Invalid CDM ops"); |
| return -EINVAL; |
| } |
| |
| size = cdm_util_ops->cdm_required_size_changebase(); |
| /* since cdm returns dwords, we need to convert it into bytes */ |
| if ((size * 4) > cdm_args->cmd.size) { |
| CAM_ERR(CAM_ISP, "buf size:%d is not sufficient, expected: %d", |
| cdm_args->cmd.size, size); |
| return -EINVAL; |
| } |
| |
| mem_base = CAM_SOC_GET_REG_MAP_CAM_BASE( |
| top_priv->common_data.soc_info, VFE_CORE_BASE_IDX); |
| CAM_DBG(CAM_ISP, "core %d mem_base 0x%x", |
| top_priv->common_data.soc_info->index, mem_base); |
| |
| cdm_util_ops->cdm_write_changebase( |
| cdm_args->cmd.cmd_buf_addr, mem_base); |
| cdm_args->cmd.used_bytes = (size * 4); |
| |
| return 0; |
| } |
| |
| static int cam_vfe_top_mux_get_reg_update( |
| struct cam_vfe_top_ver2_priv *top_priv, |
| void *cmd_args, uint32_t arg_size) |
| { |
| struct cam_isp_hw_get_cmd_update *cmd_update = cmd_args; |
| |
| if (cmd_update->res->process_cmd) |
| return cmd_update->res->process_cmd(cmd_update->res, |
| CAM_ISP_HW_CMD_GET_REG_UPDATE, cmd_args, arg_size); |
| |
| return -EINVAL; |
| } |
| |
| int cam_vfe_top_get_hw_caps(void *device_priv, |
| void *get_hw_cap_args, uint32_t arg_size) |
| { |
| return -EPERM; |
| } |
| |
| int cam_vfe_top_init_hw(void *device_priv, |
| void *init_hw_args, uint32_t arg_size) |
| { |
| return -EPERM; |
| } |
| |
| int cam_vfe_top_reset(void *device_priv, |
| void *reset_core_args, uint32_t arg_size) |
| { |
| struct cam_vfe_top_ver2_priv *top_priv = device_priv; |
| struct cam_hw_soc_info *soc_info = NULL; |
| struct cam_vfe_top_ver2_reg_offset_common *reg_common = NULL; |
| uint32_t *reset_reg_args = reset_core_args; |
| uint32_t reset_reg_val; |
| |
| if (!top_priv || !reset_reg_args) { |
| CAM_ERR(CAM_ISP, "Invalid arguments"); |
| return -EINVAL; |
| } |
| |
| switch (*reset_reg_args) { |
| case CAM_VFE_HW_RESET_HW_AND_REG: |
| reset_reg_val = CAM_VFE_HW_RESET_HW_AND_REG_VAL; |
| break; |
| default: |
| reset_reg_val = CAM_VFE_HW_RESET_HW_VAL; |
| break; |
| } |
| |
| CAM_DBG(CAM_ISP, "reset reg value: %x", reset_reg_val); |
| soc_info = top_priv->common_data.soc_info; |
| reg_common = top_priv->common_data.common_reg; |
| |
| /* Mask All the IRQs except RESET */ |
| cam_io_w_mb((1 << 31), |
| CAM_SOC_GET_REG_MAP_START(soc_info, VFE_CORE_BASE_IDX) + 0x5C); |
| |
| /* Reset HW */ |
| cam_io_w_mb(reset_reg_val, |
| CAM_SOC_GET_REG_MAP_START(soc_info, VFE_CORE_BASE_IDX) + |
| reg_common->global_reset_cmd); |
| |
| CAM_DBG(CAM_ISP, "Reset HW exit"); |
| return 0; |
| } |
| |
| int cam_vfe_top_reserve(void *device_priv, |
| void *reserve_args, uint32_t arg_size) |
| { |
| struct cam_vfe_top_ver2_priv *top_priv; |
| struct cam_vfe_acquire_args *args; |
| struct cam_vfe_hw_vfe_in_acquire_args *acquire_args; |
| uint32_t i; |
| int rc = -EINVAL; |
| |
| if (!device_priv || !reserve_args) { |
| CAM_ERR(CAM_ISP, "Error! Invalid input arguments"); |
| return -EINVAL; |
| } |
| |
| top_priv = (struct cam_vfe_top_ver2_priv *)device_priv; |
| args = (struct cam_vfe_acquire_args *)reserve_args; |
| acquire_args = &args->vfe_in; |
| |
| |
| for (i = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) { |
| if (top_priv->mux_rsrc[i].res_id == acquire_args->res_id && |
| top_priv->mux_rsrc[i].res_state == |
| CAM_ISP_RESOURCE_STATE_AVAILABLE) { |
| |
| if (acquire_args->res_id == CAM_ISP_HW_VFE_IN_CAMIF) { |
| rc = cam_vfe_camif_ver2_acquire_resource( |
| &top_priv->mux_rsrc[i], |
| args); |
| if (rc) |
| break; |
| } |
| |
| top_priv->mux_rsrc[i].cdm_ops = acquire_args->cdm_ops; |
| top_priv->mux_rsrc[i].tasklet_info = args->tasklet; |
| top_priv->mux_rsrc[i].res_state = |
| CAM_ISP_RESOURCE_STATE_RESERVED; |
| acquire_args->rsrc_node = |
| &top_priv->mux_rsrc[i]; |
| |
| rc = 0; |
| break; |
| } |
| } |
| |
| return rc; |
| |
| } |
| |
| int cam_vfe_top_release(void *device_priv, |
| void *release_args, uint32_t arg_size) |
| { |
| struct cam_vfe_top_ver2_priv *top_priv; |
| struct cam_isp_resource_node *mux_res; |
| |
| if (!device_priv || !release_args) { |
| CAM_ERR(CAM_ISP, "Error! Invalid input arguments"); |
| return -EINVAL; |
| } |
| |
| top_priv = (struct cam_vfe_top_ver2_priv *)device_priv; |
| mux_res = (struct cam_isp_resource_node *)release_args; |
| |
| CAM_DBG(CAM_ISP, "Resource in state %d", mux_res->res_state); |
| if (mux_res->res_state < CAM_ISP_RESOURCE_STATE_RESERVED) { |
| CAM_ERR(CAM_ISP, "Error! Resource in Invalid res_state :%d", |
| mux_res->res_state); |
| return -EINVAL; |
| } |
| mux_res->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE; |
| |
| return 0; |
| } |
| |
| int cam_vfe_top_start(void *device_priv, |
| void *start_args, uint32_t arg_size) |
| { |
| struct cam_vfe_top_ver2_priv *top_priv; |
| struct cam_isp_resource_node *mux_res; |
| int rc = 0; |
| |
| if (!device_priv || !start_args) { |
| CAM_ERR(CAM_ISP, "Error! Invalid input arguments"); |
| return -EINVAL; |
| } |
| |
| top_priv = (struct cam_vfe_top_ver2_priv *)device_priv; |
| mux_res = (struct cam_isp_resource_node *)start_args; |
| |
| if (mux_res->start) { |
| rc = mux_res->start(mux_res); |
| } else { |
| CAM_ERR(CAM_ISP, "Invalid res id:%d", mux_res->res_id); |
| rc = -EINVAL; |
| } |
| |
| return rc; |
| } |
| |
| int cam_vfe_top_stop(void *device_priv, |
| void *stop_args, uint32_t arg_size) |
| { |
| struct cam_vfe_top_ver2_priv *top_priv; |
| struct cam_isp_resource_node *mux_res; |
| int rc = 0; |
| |
| if (!device_priv || !stop_args) { |
| CAM_ERR(CAM_ISP, "Error! Invalid input arguments"); |
| return -EINVAL; |
| } |
| |
| top_priv = (struct cam_vfe_top_ver2_priv *)device_priv; |
| mux_res = (struct cam_isp_resource_node *)stop_args; |
| |
| if (mux_res->res_id == CAM_ISP_HW_VFE_IN_CAMIF || |
| (mux_res->res_id >= CAM_ISP_HW_VFE_IN_RDI0 && |
| mux_res->res_id <= CAM_ISP_HW_VFE_IN_RDI3)) { |
| rc = mux_res->stop(mux_res); |
| } else { |
| CAM_ERR(CAM_ISP, "Invalid res id:%d", mux_res->res_id); |
| rc = -EINVAL; |
| } |
| |
| return rc; |
| |
| } |
| |
| int cam_vfe_top_read(void *device_priv, |
| void *read_args, uint32_t arg_size) |
| { |
| return -EPERM; |
| } |
| |
| int cam_vfe_top_write(void *device_priv, |
| void *write_args, uint32_t arg_size) |
| { |
| return -EPERM; |
| } |
| |
| int cam_vfe_top_process_cmd(void *device_priv, uint32_t cmd_type, |
| void *cmd_args, uint32_t arg_size) |
| { |
| int rc = 0; |
| struct cam_vfe_top_ver2_priv *top_priv; |
| |
| if (!device_priv || !cmd_args) { |
| CAM_ERR(CAM_ISP, "Error! Invalid arguments"); |
| return -EINVAL; |
| } |
| top_priv = (struct cam_vfe_top_ver2_priv *)device_priv; |
| |
| switch (cmd_type) { |
| case CAM_ISP_HW_CMD_GET_CHANGE_BASE: |
| rc = cam_vfe_top_mux_get_base(top_priv, cmd_args, arg_size); |
| break; |
| case CAM_ISP_HW_CMD_GET_REG_UPDATE: |
| rc = cam_vfe_top_mux_get_reg_update(top_priv, cmd_args, |
| arg_size); |
| break; |
| default: |
| rc = -EINVAL; |
| CAM_ERR(CAM_ISP, "Error! Invalid cmd:%d", cmd_type); |
| break; |
| } |
| |
| return rc; |
| } |
| |
| int cam_vfe_top_ver2_init( |
| struct cam_hw_soc_info *soc_info, |
| struct cam_hw_intf *hw_intf, |
| void *top_hw_info, |
| struct cam_vfe_top **vfe_top_ptr) |
| { |
| int i, j, rc = 0; |
| struct cam_vfe_top_ver2_priv *top_priv = NULL; |
| struct cam_vfe_top_ver2_hw_info *ver2_hw_info = top_hw_info; |
| struct cam_vfe_top *vfe_top; |
| |
| vfe_top = kzalloc(sizeof(struct cam_vfe_top), GFP_KERNEL); |
| if (!vfe_top) { |
| CAM_DBG(CAM_ISP, "Error! Failed to alloc for vfe_top"); |
| rc = -ENOMEM; |
| goto end; |
| } |
| |
| top_priv = kzalloc(sizeof(struct cam_vfe_top_ver2_priv), |
| GFP_KERNEL); |
| if (!top_priv) { |
| CAM_DBG(CAM_ISP, "Error! Failed to alloc for vfe_top_priv"); |
| rc = -ENOMEM; |
| goto free_vfe_top; |
| } |
| vfe_top->top_priv = top_priv; |
| |
| for (i = 0, j = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) { |
| top_priv->mux_rsrc[i].res_type = CAM_ISP_RESOURCE_VFE_IN; |
| top_priv->mux_rsrc[i].hw_intf = hw_intf; |
| top_priv->mux_rsrc[i].res_state = |
| CAM_ISP_RESOURCE_STATE_AVAILABLE; |
| if (ver2_hw_info->mux_type[i] == CAM_VFE_CAMIF_VER_2_0) { |
| top_priv->mux_rsrc[i].res_id = |
| CAM_ISP_HW_VFE_IN_CAMIF; |
| |
| rc = cam_vfe_camif_ver2_init(hw_intf, soc_info, |
| &ver2_hw_info->camif_hw_info, |
| &top_priv->mux_rsrc[i]); |
| if (rc) |
| goto deinit_resources; |
| } else { |
| /* set the RDI resource id */ |
| top_priv->mux_rsrc[i].res_id = |
| CAM_ISP_HW_VFE_IN_RDI0 + j++; |
| |
| rc = cam_vfe_rdi_ver2_init(hw_intf, soc_info, |
| &ver2_hw_info->rdi_hw_info, |
| &top_priv->mux_rsrc[i]); |
| if (rc) |
| goto deinit_resources; |
| } |
| } |
| |
| vfe_top->hw_ops.get_hw_caps = cam_vfe_top_get_hw_caps; |
| vfe_top->hw_ops.init = cam_vfe_top_init_hw; |
| vfe_top->hw_ops.reset = cam_vfe_top_reset; |
| vfe_top->hw_ops.reserve = cam_vfe_top_reserve; |
| vfe_top->hw_ops.release = cam_vfe_top_release; |
| vfe_top->hw_ops.start = cam_vfe_top_start; |
| vfe_top->hw_ops.stop = cam_vfe_top_stop; |
| vfe_top->hw_ops.read = cam_vfe_top_read; |
| vfe_top->hw_ops.write = cam_vfe_top_write; |
| vfe_top->hw_ops.process_cmd = cam_vfe_top_process_cmd; |
| *vfe_top_ptr = vfe_top; |
| |
| top_priv->common_data.soc_info = soc_info; |
| top_priv->common_data.hw_intf = hw_intf; |
| top_priv->common_data.common_reg = ver2_hw_info->common_reg; |
| |
| return rc; |
| |
| deinit_resources: |
| for (--i; i >= 0; i--) { |
| if (ver2_hw_info->mux_type[i] == CAM_VFE_CAMIF_VER_2_0) { |
| if (cam_vfe_camif_ver2_deinit(&top_priv->mux_rsrc[i])) |
| CAM_ERR(CAM_ISP, "Camif Deinit failed"); |
| } else { |
| if (cam_vfe_rdi_ver2_deinit(&top_priv->mux_rsrc[i])) |
| CAM_ERR(CAM_ISP, "RDI Deinit failed"); |
| } |
| top_priv->mux_rsrc[i].res_state = |
| CAM_ISP_RESOURCE_STATE_UNAVAILABLE; |
| } |
| |
| kfree(vfe_top->top_priv); |
| free_vfe_top: |
| kfree(vfe_top); |
| end: |
| return rc; |
| } |
| |
| int cam_vfe_top_ver2_deinit(struct cam_vfe_top **vfe_top_ptr) |
| { |
| int i, rc = 0; |
| struct cam_vfe_top_ver2_priv *top_priv = NULL; |
| struct cam_vfe_top *vfe_top; |
| |
| if (!vfe_top_ptr) { |
| CAM_ERR(CAM_ISP, "Error! Invalid input"); |
| return -EINVAL; |
| } |
| |
| vfe_top = *vfe_top_ptr; |
| if (!vfe_top) { |
| CAM_ERR(CAM_ISP, "Error! vfe_top NULL"); |
| return -ENODEV; |
| } |
| |
| top_priv = vfe_top->top_priv; |
| if (!top_priv) { |
| CAM_ERR(CAM_ISP, "Error! vfe_top_priv NULL"); |
| rc = -ENODEV; |
| goto free_vfe_top; |
| } |
| |
| for (i = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) { |
| top_priv->mux_rsrc[i].res_state = |
| CAM_ISP_RESOURCE_STATE_UNAVAILABLE; |
| if (top_priv->mux_rsrc[i].res_id == |
| CAM_ISP_HW_VFE_IN_CAMIF) { |
| rc = cam_vfe_camif_ver2_deinit(&top_priv->mux_rsrc[i]); |
| if (rc) |
| CAM_ERR(CAM_ISP, "Camif deinit failed rc=%d", |
| rc); |
| } else { |
| rc = cam_vfe_rdi_ver2_deinit(&top_priv->mux_rsrc[i]); |
| if (rc) |
| CAM_ERR(CAM_ISP, "RDI deinit failed rc=%d", rc); |
| } |
| } |
| |
| kfree(vfe_top->top_priv); |
| |
| free_vfe_top: |
| kfree(vfe_top); |
| *vfe_top_ptr = NULL; |
| |
| return rc; |
| } |
| |