| /* Copyright (c) 2013-2018, 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/io.h> |
| #include <media/v4l2-subdev.h> |
| #include <asm/div64.h> |
| #include "msm_isp_util.h" |
| #include "msm_isp_stats_util.h" |
| #include "msm_isp_axi_util.h" |
| #include "msm_isp48.h" |
| |
| #define HANDLE_TO_IDX(handle) (handle & 0xFF) |
| #define ISP_SOF_DEBUG_COUNT 0 |
| |
| #ifdef CONFIG_MSM_AVTIMER |
| static struct avtimer_fptr_t avtimer_func; |
| #endif |
| static void msm_isp_reload_ping_pong_offset( |
| struct msm_vfe_axi_stream *stream_info); |
| |
| static void __msm_isp_axi_stream_update( |
| struct msm_vfe_axi_stream *stream_info, |
| struct msm_isp_timestamp *ts); |
| |
| static int msm_isp_update_stream_bandwidth( |
| struct msm_vfe_axi_stream *stream_info, int enable); |
| |
| #define DUAL_VFE_AND_VFE1(s, v) ((s->stream_src < RDI_INTF_0) && \ |
| v->is_split && vfe_dev->pdev->id == ISP_VFE1) |
| |
| #define RDI_OR_NOT_DUAL_VFE(v, s) (!v->is_split || \ |
| ((s->stream_src >= RDI_INTF_0) && \ |
| (stream_info->stream_src <= RDI_INTF_2))) |
| |
| static int msm_isp_axi_create_stream(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_shared_data *axi_data, |
| struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd, |
| struct msm_vfe_axi_stream *stream_info) |
| { |
| uint32_t i = 0; |
| int rc = 0; |
| |
| if (stream_info->state != AVAILABLE) { |
| pr_err("%s:%d invalid state %d expected %d\n", |
| __func__, __LINE__, stream_info->state, |
| AVAILABLE); |
| return -EINVAL; |
| } |
| |
| if (stream_info->num_isp == 0) { |
| stream_info->session_id = stream_cfg_cmd->session_id; |
| stream_info->stream_id = stream_cfg_cmd->stream_id; |
| stream_info->buf_divert = stream_cfg_cmd->buf_divert; |
| stream_info->stream_src = stream_cfg_cmd->stream_src; |
| stream_info->controllable_output = |
| stream_cfg_cmd->controllable_output; |
| stream_info->activated_framedrop_period = |
| MSM_VFE_STREAM_STOP_PERIOD; |
| if (stream_cfg_cmd->controllable_output) |
| stream_cfg_cmd->frame_skip_pattern = SKIP_ALL; |
| INIT_LIST_HEAD(&stream_info->request_q); |
| } else { |
| /* check if the stream has been added for the vfe-device */ |
| if (stream_info->vfe_mask & (1 << vfe_dev->pdev->id)) { |
| pr_err("%s: stream %pK/%x is already added for vfe dev %d vfe_mask %x\n", |
| __func__, stream_info, stream_info->stream_id, |
| vfe_dev->pdev->id, stream_info->vfe_mask); |
| return -EINVAL; |
| } |
| if (stream_info->session_id != stream_cfg_cmd->session_id) { |
| pr_err("%s: dual stream session id mismatch %d/%d\n", |
| __func__, stream_info->session_id, |
| stream_cfg_cmd->session_id); |
| rc = -EINVAL; |
| } |
| if (stream_info->stream_id != stream_cfg_cmd->stream_id) { |
| pr_err("%s: dual stream stream id mismatch %d/%d\n", |
| __func__, stream_info->stream_id, |
| stream_cfg_cmd->stream_id); |
| rc = -EINVAL; |
| } |
| if (stream_info->controllable_output != |
| stream_cfg_cmd->controllable_output) { |
| pr_err("%s: dual stream controllable_op mismatch %d/%d\n", |
| __func__, stream_info->controllable_output, |
| stream_cfg_cmd->controllable_output); |
| rc = -EINVAL; |
| } |
| if (stream_info->buf_divert != stream_cfg_cmd->buf_divert) { |
| pr_err("%s: dual stream buf_divert mismatch %d/%d\n", |
| __func__, stream_info->buf_divert, |
| stream_cfg_cmd->buf_divert); |
| rc = -EINVAL; |
| } |
| if (rc) |
| return rc; |
| } |
| stream_info->vfe_dev[stream_info->num_isp] = vfe_dev; |
| stream_info->num_isp++; |
| |
| if ((axi_data->stream_handle_cnt << 8) == 0) |
| axi_data->stream_handle_cnt++; |
| |
| stream_cfg_cmd->axi_stream_handle = |
| (++axi_data->stream_handle_cnt) << 8 | stream_info->stream_src; |
| |
| ISP_DBG("%s: vfe %d handle %x\n", __func__, vfe_dev->pdev->id, |
| stream_cfg_cmd->axi_stream_handle); |
| |
| stream_info->stream_handle[stream_info->num_isp - 1] = |
| stream_cfg_cmd->axi_stream_handle; |
| stream_info->vfe_mask |= (1 << vfe_dev->pdev->id); |
| |
| if (!vfe_dev->is_split || stream_cfg_cmd->stream_src >= RDI_INTF_0 || |
| stream_info->num_isp == MAX_VFE) { |
| stream_info->state = INACTIVE; |
| |
| for (i = 0; i < MSM_ISP_COMP_IRQ_MAX; i++) |
| stream_info->composite_irq[i] = 0; |
| } |
| return 0; |
| } |
| |
| static void msm_isp_axi_destroy_stream( |
| struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) |
| { |
| int k; |
| int j; |
| int i; |
| int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); |
| |
| /* |
| * For the index being removed, shift everything to it's right by 1 |
| * so that the index being removed becomes the last index |
| */ |
| for (i = vfe_idx, k = vfe_idx + 1; k < stream_info->num_isp; k++, i++) { |
| stream_info->vfe_dev[i] = stream_info->vfe_dev[k]; |
| stream_info->stream_handle[i] = stream_info->stream_handle[k]; |
| stream_info->bandwidth[i] = stream_info->bandwidth[k]; |
| stream_info->max_width[i] = stream_info->max_width[k]; |
| stream_info->comp_mask_index[i] = |
| stream_info->comp_mask_index[k]; |
| for (j = 0; j < stream_info->num_planes; j++) { |
| stream_info->plane_cfg[i][j] = |
| stream_info->plane_cfg[k][j]; |
| stream_info->wm[i][j] = stream_info->wm[k][j]; |
| } |
| } |
| |
| stream_info->num_isp--; |
| stream_info->vfe_dev[stream_info->num_isp] = NULL; |
| stream_info->stream_handle[stream_info->num_isp] = 0; |
| stream_info->bandwidth[stream_info->num_isp] = 0; |
| stream_info->max_width[stream_info->num_isp] = 0; |
| stream_info->comp_mask_index[stream_info->num_isp] = -1; |
| stream_info->vfe_mask &= ~(1 << vfe_dev->pdev->id); |
| for (j = 0; j < stream_info->num_planes; j++) { |
| stream_info->wm[stream_info->num_isp][j] = -1; |
| memset(&stream_info->plane_cfg[stream_info->num_isp][j], |
| 0, sizeof( |
| stream_info->plane_cfg[stream_info->num_isp][j])); |
| } |
| |
| if (stream_info->num_isp == 0) { |
| /* release the bufq */ |
| for (k = 0; k < VFE_BUF_QUEUE_MAX; k++) |
| stream_info->bufq_handle[k] = 0; |
| stream_info->vfe_mask = 0; |
| stream_info->state = AVAILABLE; |
| } |
| } |
| |
| static int msm_isp_validate_axi_request(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream *stream_info, |
| struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd) |
| { |
| int rc = -1, i; |
| int vfe_idx; |
| struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; |
| |
| switch (stream_cfg_cmd->output_format) { |
| case V4L2_PIX_FMT_YUYV: |
| case V4L2_PIX_FMT_YVYU: |
| case V4L2_PIX_FMT_UYVY: |
| case V4L2_PIX_FMT_VYUY: |
| case V4L2_PIX_FMT_SBGGR8: |
| case V4L2_PIX_FMT_SGBRG8: |
| case V4L2_PIX_FMT_SGRBG8: |
| case V4L2_PIX_FMT_SRGGB8: |
| case V4L2_PIX_FMT_SBGGR10: |
| case V4L2_PIX_FMT_SGBRG10: |
| case V4L2_PIX_FMT_SGRBG10: |
| case V4L2_PIX_FMT_SRGGB10: |
| case V4L2_PIX_FMT_SBGGR10DPCM6: |
| case V4L2_PIX_FMT_SGBRG10DPCM6: |
| case V4L2_PIX_FMT_SGRBG10DPCM6: |
| case V4L2_PIX_FMT_SRGGB10DPCM6: |
| case V4L2_PIX_FMT_SBGGR10DPCM8: |
| case V4L2_PIX_FMT_SGBRG10DPCM8: |
| case V4L2_PIX_FMT_SGRBG10DPCM8: |
| case V4L2_PIX_FMT_SRGGB10DPCM8: |
| case V4L2_PIX_FMT_SBGGR12: |
| case V4L2_PIX_FMT_SGBRG12: |
| case V4L2_PIX_FMT_SGRBG12: |
| case V4L2_PIX_FMT_SRGGB12: |
| case V4L2_PIX_FMT_SBGGR14: |
| case V4L2_PIX_FMT_SGBRG14: |
| case V4L2_PIX_FMT_SGRBG14: |
| case V4L2_PIX_FMT_SRGGB14: |
| case V4L2_PIX_FMT_QBGGR8: |
| case V4L2_PIX_FMT_QGBRG8: |
| case V4L2_PIX_FMT_QGRBG8: |
| case V4L2_PIX_FMT_QRGGB8: |
| case V4L2_PIX_FMT_QBGGR10: |
| case V4L2_PIX_FMT_QGBRG10: |
| case V4L2_PIX_FMT_QGRBG10: |
| case V4L2_PIX_FMT_QRGGB10: |
| case V4L2_PIX_FMT_QBGGR12: |
| case V4L2_PIX_FMT_QGBRG12: |
| case V4L2_PIX_FMT_QGRBG12: |
| case V4L2_PIX_FMT_QRGGB12: |
| case V4L2_PIX_FMT_QBGGR14: |
| case V4L2_PIX_FMT_QGBRG14: |
| case V4L2_PIX_FMT_QGRBG14: |
| case V4L2_PIX_FMT_QRGGB14: |
| case V4L2_PIX_FMT_P16BGGR10: |
| case V4L2_PIX_FMT_P16GBRG10: |
| case V4L2_PIX_FMT_P16GRBG10: |
| case V4L2_PIX_FMT_P16RGGB10: |
| case V4L2_PIX_FMT_P16BGGR12: |
| case V4L2_PIX_FMT_P16GBRG12: |
| case V4L2_PIX_FMT_P16GRBG12: |
| case V4L2_PIX_FMT_P16RGGB12: |
| case V4L2_PIX_FMT_JPEG: |
| case V4L2_PIX_FMT_META: |
| case V4L2_PIX_FMT_META10: |
| case V4L2_PIX_FMT_GREY: |
| stream_info->num_planes = 1; |
| stream_info->format_factor = ISP_Q2; |
| break; |
| case V4L2_PIX_FMT_NV12: |
| case V4L2_PIX_FMT_NV21: |
| case V4L2_PIX_FMT_NV14: |
| case V4L2_PIX_FMT_NV41: |
| stream_info->num_planes = 2; |
| stream_info->format_factor = 1.5 * ISP_Q2; |
| break; |
| case V4L2_PIX_FMT_NV16: |
| case V4L2_PIX_FMT_NV61: |
| stream_info->num_planes = 2; |
| stream_info->format_factor = 2 * ISP_Q2; |
| break; |
| case V4L2_PIX_FMT_NV24: |
| case V4L2_PIX_FMT_NV42: |
| stream_info->num_planes = 2; |
| stream_info->format_factor = 3 * ISP_Q2; |
| break; |
| /*TD: Add more image format*/ |
| default: |
| msm_isp_print_fourcc_error(__func__, |
| stream_cfg_cmd->output_format); |
| return rc; |
| } |
| |
| if (axi_data->hw_info->num_wm - axi_data->num_used_wm < |
| stream_info->num_planes) { |
| pr_err("%s: No free write masters\n", __func__); |
| return rc; |
| } |
| |
| if ((stream_info->num_planes > 1) && |
| (axi_data->hw_info->num_comp_mask - |
| axi_data->num_used_composite_mask < 1)) { |
| pr_err("%s: No free composite mask\n", __func__); |
| return rc; |
| } |
| |
| if (stream_cfg_cmd->init_frame_drop >= MAX_INIT_FRAME_DROP) { |
| pr_err("%s: Invalid skip pattern\n", __func__); |
| return rc; |
| } |
| |
| if (stream_cfg_cmd->frame_skip_pattern >= MAX_SKIP) { |
| pr_err("%s: Invalid skip pattern\n", __func__); |
| return rc; |
| } |
| |
| vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); |
| |
| for (i = 0; i < stream_info->num_planes; i++) { |
| stream_info->plane_cfg[vfe_idx][i] = |
| stream_cfg_cmd->plane_cfg[i]; |
| stream_info->max_width[vfe_idx] = |
| max(stream_info->max_width[vfe_idx], |
| stream_cfg_cmd->plane_cfg[i].output_width); |
| } |
| |
| stream_info->output_format = stream_cfg_cmd->output_format; |
| stream_info->runtime_output_format = stream_info->output_format; |
| stream_info->stream_src = stream_cfg_cmd->stream_src; |
| stream_info->frame_based = stream_cfg_cmd->frame_base; |
| return 0; |
| } |
| |
| static uint32_t msm_isp_axi_get_plane_size( |
| struct msm_vfe_axi_stream *stream_info, int vfe_idx, int plane_idx) |
| { |
| uint32_t size = 0; |
| struct msm_vfe_axi_plane_cfg *plane_cfg = |
| stream_info->plane_cfg[vfe_idx]; |
| switch (stream_info->output_format) { |
| case V4L2_PIX_FMT_YUYV: |
| case V4L2_PIX_FMT_YVYU: |
| case V4L2_PIX_FMT_UYVY: |
| case V4L2_PIX_FMT_VYUY: |
| case V4L2_PIX_FMT_SBGGR8: |
| case V4L2_PIX_FMT_SGBRG8: |
| case V4L2_PIX_FMT_SGRBG8: |
| case V4L2_PIX_FMT_SRGGB8: |
| case V4L2_PIX_FMT_QBGGR8: |
| case V4L2_PIX_FMT_QGBRG8: |
| case V4L2_PIX_FMT_QGRBG8: |
| case V4L2_PIX_FMT_QRGGB8: |
| case V4L2_PIX_FMT_JPEG: |
| case V4L2_PIX_FMT_META: |
| case V4L2_PIX_FMT_GREY: |
| size = plane_cfg[plane_idx].output_height * |
| plane_cfg[plane_idx].output_width; |
| break; |
| case V4L2_PIX_FMT_SBGGR10: |
| case V4L2_PIX_FMT_SGBRG10: |
| case V4L2_PIX_FMT_SGRBG10: |
| case V4L2_PIX_FMT_SRGGB10: |
| case V4L2_PIX_FMT_SBGGR10DPCM6: |
| case V4L2_PIX_FMT_SGBRG10DPCM6: |
| case V4L2_PIX_FMT_SGRBG10DPCM6: |
| case V4L2_PIX_FMT_SRGGB10DPCM6: |
| case V4L2_PIX_FMT_SBGGR10DPCM8: |
| case V4L2_PIX_FMT_SGBRG10DPCM8: |
| case V4L2_PIX_FMT_SGRBG10DPCM8: |
| case V4L2_PIX_FMT_SRGGB10DPCM8: |
| case V4L2_PIX_FMT_QBGGR10: |
| case V4L2_PIX_FMT_QGBRG10: |
| case V4L2_PIX_FMT_QGRBG10: |
| case V4L2_PIX_FMT_QRGGB10: |
| case V4L2_PIX_FMT_META10: |
| /* TODO: fix me */ |
| size = plane_cfg[plane_idx].output_height * |
| plane_cfg[plane_idx].output_width; |
| break; |
| case V4L2_PIX_FMT_SBGGR12: |
| case V4L2_PIX_FMT_SGBRG12: |
| case V4L2_PIX_FMT_SGRBG12: |
| case V4L2_PIX_FMT_SRGGB12: |
| case V4L2_PIX_FMT_QBGGR12: |
| case V4L2_PIX_FMT_QGBRG12: |
| case V4L2_PIX_FMT_QGRBG12: |
| case V4L2_PIX_FMT_QRGGB12: |
| case V4L2_PIX_FMT_SBGGR14: |
| case V4L2_PIX_FMT_SGBRG14: |
| case V4L2_PIX_FMT_SGRBG14: |
| case V4L2_PIX_FMT_SRGGB14: |
| case V4L2_PIX_FMT_QBGGR14: |
| case V4L2_PIX_FMT_QGBRG14: |
| case V4L2_PIX_FMT_QGRBG14: |
| case V4L2_PIX_FMT_QRGGB14: |
| /* TODO: fix me */ |
| size = plane_cfg[plane_idx].output_height * |
| plane_cfg[plane_idx].output_width; |
| break; |
| case V4L2_PIX_FMT_P16BGGR10: |
| case V4L2_PIX_FMT_P16GBRG10: |
| case V4L2_PIX_FMT_P16GRBG10: |
| case V4L2_PIX_FMT_P16RGGB10: |
| case V4L2_PIX_FMT_P16BGGR12: |
| case V4L2_PIX_FMT_P16GBRG12: |
| case V4L2_PIX_FMT_P16GRBG12: |
| case V4L2_PIX_FMT_P16RGGB12: |
| size = plane_cfg[plane_idx].output_height * |
| plane_cfg[plane_idx].output_width; |
| break; |
| case V4L2_PIX_FMT_NV12: |
| case V4L2_PIX_FMT_NV21: |
| if (plane_cfg[plane_idx].output_plane_format == Y_PLANE) |
| size = plane_cfg[plane_idx].output_height * |
| plane_cfg[plane_idx].output_width; |
| else |
| size = plane_cfg[plane_idx].output_height * |
| plane_cfg[plane_idx].output_width; |
| break; |
| case V4L2_PIX_FMT_NV14: |
| case V4L2_PIX_FMT_NV41: |
| if (plane_cfg[plane_idx].output_plane_format == Y_PLANE) |
| size = plane_cfg[plane_idx].output_height * |
| plane_cfg[plane_idx].output_width; |
| else |
| size = plane_cfg[plane_idx].output_height * |
| plane_cfg[plane_idx].output_width; |
| break; |
| case V4L2_PIX_FMT_NV16: |
| case V4L2_PIX_FMT_NV61: |
| case V4L2_PIX_FMT_NV24: |
| case V4L2_PIX_FMT_NV42: |
| size = plane_cfg[plane_idx].output_height * |
| plane_cfg[plane_idx].output_width; |
| break; |
| /*TD: Add more image format*/ |
| default: |
| msm_isp_print_fourcc_error(__func__, |
| stream_info->output_format); |
| break; |
| } |
| return size; |
| } |
| |
| static void msm_isp_axi_reserve_wm(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream *stream_info) |
| { |
| struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; |
| int i, j; |
| int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); |
| |
| for (i = 0; i < stream_info->num_planes; i++) { |
| for (j = 0; j < axi_data->hw_info->num_wm; j++) { |
| if (!axi_data->free_wm[j]) { |
| axi_data->free_wm[j] = |
| stream_info->stream_handle[vfe_idx]; |
| axi_data->wm_image_size[j] = |
| msm_isp_axi_get_plane_size( |
| stream_info, vfe_idx, i); |
| axi_data->num_used_wm++; |
| break; |
| } |
| } |
| ISP_DBG("%s vfe %d stream_handle %x wm %d\n", __func__, |
| vfe_dev->pdev->id, |
| stream_info->stream_handle[vfe_idx], j); |
| stream_info->wm[vfe_idx][i] = j; |
| /* setup var to ignore bus error from RDI wm */ |
| if (stream_info->stream_src >= RDI_INTF_0) { |
| if (vfe_dev->hw_info->vfe_ops.core_ops. |
| set_bus_err_ign_mask) |
| vfe_dev->hw_info->vfe_ops.core_ops. |
| set_bus_err_ign_mask(vfe_dev, j, 1); |
| } |
| } |
| } |
| |
| void msm_isp_axi_free_wm(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream *stream_info) |
| { |
| struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; |
| int i; |
| int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); |
| |
| for (i = 0; i < stream_info->num_planes; i++) { |
| axi_data->free_wm[stream_info->wm[vfe_idx][i]] = 0; |
| axi_data->num_used_wm--; |
| if (stream_info->stream_src >= RDI_INTF_0) { |
| if (vfe_dev->hw_info->vfe_ops.core_ops. |
| set_bus_err_ign_mask) |
| vfe_dev->hw_info->vfe_ops.core_ops. |
| set_bus_err_ign_mask(vfe_dev, |
| stream_info->wm[vfe_idx][i], 0); |
| } |
| } |
| if (stream_info->stream_src <= IDEAL_RAW) |
| axi_data->num_pix_stream++; |
| else if (stream_info->stream_src < VFE_AXI_SRC_MAX) |
| axi_data->num_rdi_stream++; |
| } |
| |
| static void msm_isp_axi_reserve_comp_mask( |
| struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream *stream_info) |
| { |
| int i; |
| uint8_t comp_mask = 0; |
| struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; |
| int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); |
| |
| for (i = 0; i < stream_info->num_planes; i++) |
| comp_mask |= 1 << stream_info->wm[vfe_idx][i]; |
| |
| for (i = 0; i < axi_data->hw_info->num_comp_mask; i++) { |
| if (!axi_data->composite_info[i].stream_handle) { |
| axi_data->composite_info[i].stream_handle = |
| stream_info->stream_handle[vfe_idx]; |
| axi_data->composite_info[i]. |
| stream_composite_mask = comp_mask; |
| axi_data->num_used_composite_mask++; |
| break; |
| } |
| } |
| stream_info->comp_mask_index[vfe_idx] = i; |
| } |
| |
| static void msm_isp_axi_free_comp_mask(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream *stream_info) |
| { |
| struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; |
| int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); |
| |
| axi_data->composite_info[stream_info->comp_mask_index[vfe_idx]]. |
| stream_composite_mask = 0; |
| axi_data->composite_info[stream_info->comp_mask_index[vfe_idx]]. |
| stream_handle = 0; |
| axi_data->num_used_composite_mask--; |
| } |
| |
| /** |
| * msm_isp_cfg_framedrop_reg() - Program the period and pattern |
| * @stream_info: The stream for which programming is done |
| * |
| * This function calculates the period and pattern to be configured |
| * for the stream based on the current frame id of the stream's input |
| * source and the initial framedrops. |
| * |
| * Returns void. |
| */ |
| static void msm_isp_cfg_framedrop_reg( |
| struct msm_vfe_axi_stream *stream_info) |
| { |
| struct vfe_device *vfe_dev = stream_info->vfe_dev[0]; |
| uint32_t runtime_init_frame_drop; |
| uint32_t framedrop_pattern = 0; |
| uint32_t framedrop_period = MSM_VFE_STREAM_STOP_PERIOD; |
| enum msm_vfe_input_src frame_src = SRC_TO_INTF(stream_info->stream_src); |
| int i; |
| |
| if (vfe_dev->axi_data.src_info[frame_src].frame_id >= |
| stream_info->init_frame_drop) |
| runtime_init_frame_drop = 0; |
| else |
| runtime_init_frame_drop = stream_info->init_frame_drop - |
| vfe_dev->axi_data.src_info[frame_src].frame_id; |
| |
| if (!runtime_init_frame_drop) |
| framedrop_period = stream_info->current_framedrop_period; |
| |
| if (framedrop_period != MSM_VFE_STREAM_STOP_PERIOD) |
| framedrop_pattern = 0x1; |
| |
| if (WARN_ON(framedrop_period == 0)) |
| pr_err("%s framedrop_period is 0", __func__); |
| |
| for (i = 0; i < stream_info->num_isp; i++) { |
| vfe_dev = stream_info->vfe_dev[i]; |
| vfe_dev->hw_info->vfe_ops.axi_ops.cfg_framedrop( |
| vfe_dev, stream_info, framedrop_pattern, |
| framedrop_period); |
| } |
| |
| ISP_DBG("%s: stream %x src %x framedrop pattern %x period %u\n", |
| __func__, |
| stream_info->stream_handle[0], stream_info->stream_src, |
| framedrop_pattern, framedrop_period); |
| |
| stream_info->requested_framedrop_period = framedrop_period; |
| } |
| |
| static int msm_isp_composite_irq(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream *stream_info, |
| enum msm_isp_comp_irq_types irq) |
| { |
| /* interrupt recv on same vfe w/o recv on other vfe */ |
| if (stream_info->composite_irq[irq] & (1 << vfe_dev->pdev->id)) { |
| msm_isp_dump_ping_pong_mismatch(vfe_dev); |
| pr_err("%s: irq %d out of sync for dual vfe on vfe %d\n", |
| __func__, irq, vfe_dev->pdev->id); |
| return -EINVAL; |
| } |
| |
| stream_info->composite_irq[irq] |= (1 << vfe_dev->pdev->id); |
| if (stream_info->composite_irq[irq] != stream_info->vfe_mask) |
| return 1; |
| |
| stream_info->composite_irq[irq] = 0; |
| |
| return 0; |
| } |
| |
| /** |
| * msm_isp_update_framedrop_reg() - Update frame period pattern on h/w |
| * @stream_info: Stream for which update is to be performed |
| * |
| * If the period and pattern needs to be updated for a stream then it is |
| * updated here. Updates happen if initial frame drop reaches 0 or burst |
| * streams have been provided new skip pattern from user space. |
| * |
| * Returns void |
| */ |
| static void msm_isp_update_framedrop_reg(struct msm_vfe_axi_stream *stream_info) |
| { |
| if (stream_info->stream_type == BURST_STREAM) { |
| if (stream_info->runtime_num_burst_capture == 0 || |
| (stream_info->runtime_num_burst_capture == 1 && |
| stream_info->activated_framedrop_period == 1)) |
| stream_info->current_framedrop_period = |
| MSM_VFE_STREAM_STOP_PERIOD; |
| } |
| |
| if (stream_info->undelivered_request_cnt > 0) |
| stream_info->current_framedrop_period = |
| MSM_VFE_STREAM_STOP_PERIOD; |
| |
| /* |
| * re-configure the period pattern, only if it's not already |
| * set to what we want |
| */ |
| if (stream_info->current_framedrop_period != |
| stream_info->requested_framedrop_period) { |
| msm_isp_cfg_framedrop_reg(stream_info); |
| } |
| } |
| |
| void msm_isp_process_reg_upd_epoch_irq(struct vfe_device *vfe_dev, |
| enum msm_vfe_input_src frame_src, |
| enum msm_isp_comp_irq_types irq, |
| struct msm_isp_timestamp *ts) |
| { |
| int i; |
| struct msm_vfe_axi_stream *stream_info; |
| unsigned long flags; |
| int ret; |
| |
| for (i = 0; i < VFE_AXI_SRC_MAX; i++) { |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, i); |
| if (SRC_TO_INTF(stream_info->stream_src) != |
| frame_src) { |
| continue; |
| } |
| if (stream_info->state == AVAILABLE || |
| stream_info->state == INACTIVE) |
| continue; |
| |
| spin_lock_irqsave(&stream_info->lock, flags); |
| |
| ret = msm_isp_composite_irq(vfe_dev, stream_info, irq); |
| if (ret) { |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| if (ret < 0) { |
| msm_isp_halt_send_error(vfe_dev, |
| ISP_EVENT_PING_PONG_MISMATCH); |
| return; |
| } |
| continue; |
| } |
| |
| switch (irq) { |
| case MSM_ISP_COMP_IRQ_REG_UPD: |
| stream_info->activated_framedrop_period = |
| stream_info->requested_framedrop_period; |
| __msm_isp_axi_stream_update(stream_info, ts); |
| break; |
| case MSM_ISP_COMP_IRQ_EPOCH: |
| if (stream_info->state == ACTIVE) |
| msm_isp_update_framedrop_reg(stream_info); |
| break; |
| default: |
| WARN(1, "Invalid irq %d\n", irq); |
| } |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| } |
| } |
| |
| /** |
| * msm_isp_reset_framedrop() - Compute the framedrop period pattern |
| * @vfe_dev: Device for which the period and pattern is computed |
| * @stream_info: The stream for the which period and pattern is generated |
| * |
| * This function is called when stream starts or is reset. It's main |
| * purpose is to setup the runtime parameters of framedrop required |
| * for the stream. |
| * |
| * Returms void |
| */ |
| void msm_isp_reset_framedrop(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream *stream_info) |
| { |
| uint32_t framedrop_period = 0; |
| |
| stream_info->runtime_num_burst_capture = stream_info->num_burst_capture; |
| |
| /** |
| * only reset none controllable output stream, since the |
| * controllable stream framedrop period will be controlled |
| * by the request frame api |
| */ |
| if (!stream_info->controllable_output) { |
| framedrop_period = |
| msm_isp_get_framedrop_period( |
| stream_info->frame_skip_pattern); |
| if (stream_info->frame_skip_pattern == SKIP_ALL) |
| stream_info->current_framedrop_period = |
| MSM_VFE_STREAM_STOP_PERIOD; |
| else |
| stream_info->current_framedrop_period = |
| framedrop_period; |
| } |
| |
| msm_isp_cfg_framedrop_reg(stream_info); |
| ISP_DBG("%s: init frame drop: %d\n", __func__, |
| stream_info->init_frame_drop); |
| ISP_DBG("%s: num_burst_capture: %d\n", __func__, |
| stream_info->runtime_num_burst_capture); |
| } |
| |
| void msm_isp_check_for_output_error(struct vfe_device *vfe_dev, |
| struct msm_isp_timestamp *ts, struct msm_isp_sof_info *sof_info) |
| { |
| struct msm_vfe_axi_stream *stream_info; |
| struct msm_vfe_axi_shared_data *axi_data; |
| int i; |
| uint32_t stream_idx; |
| |
| if (!vfe_dev || !sof_info) { |
| pr_err("%s %d failed: vfe_dev %pK sof_info %pK\n", __func__, |
| __LINE__, vfe_dev, sof_info); |
| return; |
| } |
| sof_info->regs_not_updated = 0; |
| sof_info->reg_update_fail_mask = 0; |
| sof_info->stream_get_buf_fail_mask = 0; |
| |
| axi_data = &vfe_dev->axi_data; |
| |
| for (i = 0; i < RDI_INTF_0; i++) { |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, |
| i); |
| stream_idx = HANDLE_TO_IDX(stream_info->stream_handle[0]); |
| |
| /* |
| * Process drop only if controllable ACTIVE PIX stream && |
| * reg_not_updated |
| * OR stream is in RESUMING state. |
| * Other cases there is no drop to report, so continue. |
| */ |
| if (!((stream_info->state == ACTIVE && |
| stream_info->controllable_output && |
| (SRC_TO_INTF(stream_info->stream_src) == |
| VFE_PIX_0)) || |
| stream_info->state == RESUMING)) |
| continue; |
| |
| if (stream_info->controllable_output && |
| !vfe_dev->reg_updated) { |
| if (stream_info->undelivered_request_cnt) { |
| /* report that registers are not updated |
| * and return empty buffer for controllable |
| * outputs |
| */ |
| sof_info->regs_not_updated = |
| !vfe_dev->reg_updated; |
| pr_err("Drop frame no reg update\n"); |
| if (msm_isp_drop_frame(vfe_dev, stream_info, ts, |
| sof_info)) { |
| pr_err("drop frame failed\n"); |
| } |
| } |
| } |
| |
| if (stream_info->state == RESUMING && |
| !stream_info->controllable_output) { |
| ISP_DBG("%s: axi_updating_mask strm_id %x frm_id %d\n", |
| __func__, stream_idx, vfe_dev->axi_data. |
| src_info[SRC_TO_INTF(stream_info->stream_src)] |
| .frame_id); |
| sof_info->axi_updating_mask |= |
| 1 << stream_idx; |
| } |
| } |
| |
| vfe_dev->reg_updated = 0; |
| |
| /* report frame drop per stream */ |
| if (vfe_dev->error_info.framedrop_flag) { |
| for (i = 0; i < BUF_MGR_NUM_BUF_Q; i++) { |
| if (vfe_dev->error_info.stream_framedrop_count[i]) { |
| ISP_DBG("%s: get buf failed i %d\n", __func__, |
| i); |
| sof_info->stream_get_buf_fail_mask |= (1 << i); |
| vfe_dev->error_info. |
| stream_framedrop_count[i] = 0; |
| } |
| } |
| vfe_dev->error_info.framedrop_flag = 0; |
| } |
| } |
| |
| static void msm_isp_sync_dual_cam_frame_id( |
| struct vfe_device *vfe_dev, |
| struct master_slave_resource_info *ms_res, |
| enum msm_vfe_input_src frame_src, |
| struct msm_isp_timestamp *ts) |
| { |
| struct msm_vfe_src_info *src_info = |
| &vfe_dev->axi_data.src_info[frame_src]; |
| int i; |
| uint32_t frame_id = src_info->frame_id; |
| uint32_t master_time = 0, current_time; |
| |
| if (src_info->dual_hw_ms_info.sync_state == |
| ms_res->dual_sync_mode) { |
| (frame_src == VFE_PIX_0) ? src_info->frame_id += |
| vfe_dev->axi_data.src_info[frame_src]. |
| sof_counter_step : |
| src_info->frame_id++; |
| return; |
| } |
| |
| /* find highest frame id */ |
| for (i = 0; i < MAX_VFE * VFE_SRC_MAX; i++) { |
| if (ms_res->src_info[i] == NULL) |
| continue; |
| if (src_info == ms_res->src_info[i] || |
| ms_res->src_info[i]->active == 0) |
| continue; |
| if (frame_id >= ms_res->src_info[i]->frame_id) |
| continue; |
| frame_id = ms_res->src_info[i]->frame_id; |
| master_time = ms_res->src_info[i]-> |
| dual_hw_ms_info.sof_info.mono_timestamp_ms; |
| } |
| /* copy highest frame id to the intf based on sof delta */ |
| current_time = ts->buf_time.tv_sec * 1000 + |
| ts->buf_time.tv_usec / 1000; |
| |
| if (current_time > master_time && |
| (current_time - master_time) > ms_res->sof_delta_threshold) { |
| if (frame_src == VFE_PIX_0) |
| frame_id += vfe_dev->axi_data.src_info[frame_src]. |
| sof_counter_step; |
| else |
| frame_id += 1; |
| } else { |
| for (i = 0; i < MAX_VFE * VFE_SRC_MAX; i++) { |
| if (ms_res->src_info[i] == NULL) |
| continue; |
| if (src_info == ms_res->src_info[i] || |
| ((1 << ms_res->src_info[i]-> |
| dual_hw_ms_info.index) & |
| ms_res->active_src_mask) == 0) |
| continue; |
| if (ms_res->src_info[i]->frame_id == frame_id) |
| ms_res->src_sof_mask |= (1 << |
| ms_res->src_info[i]->dual_hw_ms_info.index); |
| } |
| } |
| ms_res->active_src_mask |= (1 << src_info->dual_hw_ms_info.index); |
| src_info->frame_id = frame_id; |
| src_info->dual_hw_ms_info.sync_state = MSM_ISP_DUAL_CAM_SYNC; |
| } |
| |
| void msm_isp_increment_frame_id(struct vfe_device *vfe_dev, |
| enum msm_vfe_input_src frame_src, struct msm_isp_timestamp *ts) |
| { |
| struct msm_vfe_src_info *src_info = NULL; |
| struct msm_vfe_sof_info *sof_info = NULL; |
| enum msm_vfe_dual_hw_type dual_hw_type; |
| enum msm_vfe_dual_hw_ms_type ms_type; |
| unsigned long flags; |
| int i; |
| struct master_slave_resource_info *ms_res = |
| &vfe_dev->common_data->ms_resource; |
| |
| spin_lock_irqsave(&vfe_dev->common_data->common_dev_data_lock, flags); |
| dual_hw_type = |
| vfe_dev->axi_data.src_info[frame_src].dual_hw_type; |
| ms_type = |
| vfe_dev->axi_data.src_info[frame_src]. |
| dual_hw_ms_info.dual_hw_ms_type; |
| |
| src_info = &vfe_dev->axi_data.src_info[frame_src]; |
| if (dual_hw_type == DUAL_HW_MASTER_SLAVE) { |
| msm_isp_sync_dual_cam_frame_id(vfe_dev, ms_res, frame_src, ts); |
| if (src_info->dual_hw_ms_info.sync_state == |
| MSM_ISP_DUAL_CAM_SYNC) { |
| /* |
| * for dual hw check that we recv sof from all |
| * linked intf |
| */ |
| if (ms_res->src_sof_mask & (1 << |
| src_info->dual_hw_ms_info.index)) { |
| pr_err_ratelimited("Frame out of sync on vfe %d\n", |
| vfe_dev->pdev->id); |
| /* |
| * set this isp as async mode to force |
| *it sync again at the next sof |
| */ |
| src_info->dual_hw_ms_info.sync_state = |
| MSM_ISP_DUAL_CAM_ASYNC; |
| /* |
| * set the other isp as async mode to force |
| * it sync again at the next sof |
| */ |
| for (i = 0; i < MAX_VFE * VFE_SRC_MAX; i++) { |
| if (ms_res->src_info[i] == NULL) |
| continue; |
| if (src_info == ms_res->src_info[i] || |
| ms_res->src_info[i]-> |
| active == 0) |
| continue; |
| ms_res->src_info[i]->dual_hw_ms_info. |
| sync_state = |
| MSM_ISP_DUAL_CAM_ASYNC; |
| } |
| } |
| ms_res->src_sof_mask |= (1 << |
| src_info->dual_hw_ms_info.index); |
| if (ms_res->active_src_mask == ms_res->src_sof_mask) |
| ms_res->src_sof_mask = 0; |
| } |
| sof_info = &vfe_dev->axi_data.src_info[frame_src]. |
| dual_hw_ms_info.sof_info; |
| sof_info->frame_id = vfe_dev->axi_data.src_info[frame_src]. |
| frame_id; |
| sof_info->timestamp_ms = ts->event_time.tv_sec * 1000 + |
| ts->event_time.tv_usec / 1000; |
| sof_info->mono_timestamp_ms = ts->buf_time.tv_sec * 1000 + |
| ts->buf_time.tv_usec / 1000; |
| spin_unlock_irqrestore(&vfe_dev->common_data-> |
| common_dev_data_lock, flags); |
| } else { |
| spin_unlock_irqrestore(&vfe_dev->common_data-> |
| common_dev_data_lock, flags); |
| if (frame_src == VFE_PIX_0) { |
| vfe_dev->axi_data.src_info[frame_src].frame_id += |
| vfe_dev->axi_data.src_info[frame_src]. |
| sof_counter_step; |
| ISP_DBG("%s: vfe %d sof_step %d\n", __func__, |
| vfe_dev->pdev->id, |
| vfe_dev->axi_data.src_info[frame_src]. |
| sof_counter_step); |
| } else { |
| vfe_dev->axi_data.src_info[frame_src].frame_id++; |
| } |
| } |
| |
| if (frame_src == VFE_PIX_0) { |
| if (vfe_dev->isp_page == NULL) |
| pr_err("Invalid ISP PAGE"); |
| else |
| vfe_dev->isp_page->kernel_sofid = |
| vfe_dev->axi_data.src_info[frame_src].frame_id; |
| |
| if (!src_info->frame_id && |
| !src_info->reg_update_frame_id && |
| ((src_info->frame_id - |
| src_info->reg_update_frame_id) > |
| (MAX_REG_UPDATE_THRESHOLD * |
| src_info->sof_counter_step))) { |
| pr_err("%s:%d reg_update not received for %d frames\n", |
| __func__, __LINE__, |
| src_info->frame_id - |
| src_info->reg_update_frame_id); |
| |
| msm_isp_halt_send_error(vfe_dev, |
| ISP_EVENT_REG_UPDATE_MISSING); |
| } |
| } |
| } |
| |
| static void msm_isp_update_pd_stats_idx(struct vfe_device *vfe_dev, |
| enum msm_vfe_input_src frame_src) |
| { |
| struct msm_vfe_axi_stream *pd_stream_info = NULL; |
| uint32_t pingpong_status = 0, pingpong_bit = 0; |
| struct msm_isp_buffer *done_buf = NULL; |
| int vfe_idx = -1; |
| unsigned long flags; |
| |
| if (frame_src < VFE_RAW_0 || frame_src > VFE_RAW_2) |
| return; |
| |
| pd_stream_info = msm_isp_get_stream_common_data(vfe_dev, |
| RDI_INTF_0 + frame_src - VFE_RAW_0); |
| |
| if (pd_stream_info && (pd_stream_info->state == ACTIVE) && |
| (pd_stream_info->rdi_input_type == |
| MSM_CAMERA_RDI_PDAF)) { |
| vfe_idx = msm_isp_get_vfe_idx_for_stream( |
| vfe_dev, pd_stream_info); |
| pingpong_status = vfe_dev->hw_info->vfe_ops.axi_ops. |
| get_pingpong_status(vfe_dev); |
| pingpong_bit = ((pingpong_status >> |
| pd_stream_info->wm[vfe_idx][0]) & 0x1); |
| done_buf = pd_stream_info->buf[pingpong_bit]; |
| spin_lock_irqsave(&vfe_dev->common_data-> |
| common_dev_data_lock, flags); |
| if (done_buf) |
| vfe_dev->common_data->pd_buf_idx = done_buf->buf_idx; |
| else |
| vfe_dev->common_data->pd_buf_idx = 0xF; |
| spin_unlock_irqrestore(&vfe_dev->common_data-> |
| common_dev_data_lock, flags); |
| } |
| } |
| |
| void msm_isp_notify(struct vfe_device *vfe_dev, uint32_t event_type, |
| enum msm_vfe_input_src frame_src, struct msm_isp_timestamp *ts) |
| { |
| struct msm_isp_event_data event_data; |
| struct msm_vfe_sof_info *sof_info = NULL, *self_sof = NULL; |
| enum msm_vfe_dual_hw_ms_type ms_type; |
| unsigned long flags; |
| |
| memset(&event_data, 0, sizeof(event_data)); |
| |
| switch (event_type) { |
| case ISP_EVENT_SOF: |
| if (frame_src == VFE_PIX_0) { |
| if (vfe_dev->isp_sof_debug < ISP_SOF_DEBUG_COUNT) |
| pr_err("%s: PIX0 frame id: %u\n", __func__, |
| vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id); |
| vfe_dev->isp_sof_debug++; |
| } else if (frame_src == VFE_RAW_0) { |
| if (vfe_dev->isp_raw0_debug < ISP_SOF_DEBUG_COUNT) |
| pr_err("%s: RAW_0 frame id: %u\n", __func__, |
| vfe_dev->axi_data.src_info[VFE_RAW_0].frame_id); |
| vfe_dev->isp_raw0_debug++; |
| } else if (frame_src == VFE_RAW_1) { |
| if (vfe_dev->isp_raw1_debug < ISP_SOF_DEBUG_COUNT) |
| pr_err("%s: RAW_1 frame id: %u\n", __func__, |
| vfe_dev->axi_data.src_info[VFE_RAW_1].frame_id); |
| vfe_dev->isp_raw1_debug++; |
| } else if (frame_src == VFE_RAW_2) { |
| if (vfe_dev->isp_raw2_debug < ISP_SOF_DEBUG_COUNT) |
| pr_err("%s: RAW_2 frame id: %u\n", __func__, |
| vfe_dev->axi_data.src_info[VFE_RAW_2].frame_id); |
| vfe_dev->isp_raw2_debug++; |
| } |
| |
| ISP_DBG("%s: vfe %d frame_src %d\n", __func__, |
| vfe_dev->pdev->id, frame_src); |
| |
| /* |
| * Cannot support dual_cam and framedrop same time in union. |
| * If need to support framedrop as well, move delta calculation |
| * to userspace |
| */ |
| spin_lock_irqsave( |
| &vfe_dev->common_data->common_dev_data_lock, |
| flags); |
| if (vfe_dev->common_data->ms_resource.dual_sync_mode == |
| MSM_ISP_DUAL_CAM_SYNC && |
| vfe_dev->axi_data.src_info[frame_src].dual_hw_type == |
| DUAL_HW_MASTER_SLAVE) { |
| struct master_slave_resource_info *ms_res = |
| &vfe_dev->common_data->ms_resource; |
| self_sof = &vfe_dev->axi_data.src_info[frame_src]. |
| dual_hw_ms_info.sof_info; |
| ms_type = vfe_dev->axi_data.src_info[frame_src]. |
| dual_hw_ms_info.dual_hw_ms_type; |
| /* only send back time delta for primatry intf */ |
| if (ms_res->primary_slv_idx > 0 && |
| ms_type == MS_TYPE_MASTER) |
| sof_info = &ms_res->src_info[ |
| ms_res->primary_slv_idx]-> |
| dual_hw_ms_info.sof_info; |
| if (ms_type != MS_TYPE_MASTER && |
| ms_res->master_index > 0) |
| sof_info = &ms_res->src_info[ |
| ms_res->master_index]-> |
| dual_hw_ms_info.sof_info; |
| if (sof_info) { |
| event_data.u.sof_info.ms_delta_info. |
| delta[0] = |
| self_sof->mono_timestamp_ms - |
| sof_info->mono_timestamp_ms; |
| event_data.u.sof_info.ms_delta_info. |
| num_delta_info = 1; |
| } |
| } |
| spin_unlock_irqrestore(&vfe_dev->common_data-> |
| common_dev_data_lock, flags); |
| if (frame_src == VFE_PIX_0) |
| msm_isp_check_for_output_error(vfe_dev, ts, |
| &event_data.u.sof_info); |
| /* |
| * Get and store the buf idx for PD stats |
| * this is to send the PD stats buffer address |
| * in BF stats done. |
| */ |
| msm_isp_update_pd_stats_idx(vfe_dev, frame_src); |
| break; |
| |
| default: |
| break; |
| } |
| |
| event_data.frame_id = vfe_dev->axi_data.src_info[frame_src].frame_id; |
| event_data.timestamp = ts->event_time; |
| event_data.mono_timestamp = ts->buf_time; |
| msm_isp_send_event(vfe_dev, event_type | frame_src, &event_data); |
| } |
| |
| /** |
| * msm_isp_calculate_framedrop() - Setup frame period and pattern |
| * @vfe_dev: vfe device. |
| * @stream_cfg_cmd: User space input parameter for perion/pattern. |
| * |
| * Initialize the h/w stream framedrop period and pattern sent |
| * by user space. |
| * |
| * Returns 0 on success else error code. |
| */ |
| static int msm_isp_calculate_framedrop( |
| struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd) |
| { |
| uint32_t framedrop_period = 0; |
| struct msm_vfe_axi_stream *stream_info = NULL; |
| |
| if (HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle) |
| < VFE_AXI_SRC_MAX) { |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, |
| HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)); |
| } else { |
| pr_err("%s: Invalid stream handle", __func__); |
| return -EINVAL; |
| } |
| if (!stream_info) { |
| pr_err("%s: Stream info is NULL\n", __func__); |
| return -EINVAL; |
| } |
| |
| framedrop_period = msm_isp_get_framedrop_period( |
| stream_cfg_cmd->frame_skip_pattern); |
| stream_info->frame_skip_pattern = |
| stream_cfg_cmd->frame_skip_pattern; |
| if (stream_cfg_cmd->frame_skip_pattern == SKIP_ALL) |
| stream_info->current_framedrop_period = |
| MSM_VFE_STREAM_STOP_PERIOD; |
| else |
| stream_info->current_framedrop_period = framedrop_period; |
| |
| stream_info->init_frame_drop = stream_cfg_cmd->init_frame_drop; |
| |
| if (stream_cfg_cmd->burst_count > 0) { |
| stream_info->stream_type = BURST_STREAM; |
| stream_info->num_burst_capture = |
| stream_cfg_cmd->burst_count; |
| } else { |
| stream_info->stream_type = CONTINUOUS_STREAM; |
| } |
| return 0; |
| } |
| |
| static void msm_isp_calculate_bandwidth( |
| struct msm_vfe_axi_stream *stream_info) |
| { |
| int bpp = 0; |
| struct vfe_device *vfe_dev; |
| struct msm_vfe_axi_shared_data *axi_data; |
| int i; |
| |
| for (i = 0; i < stream_info->num_isp; i++) { |
| vfe_dev = stream_info->vfe_dev[i]; |
| axi_data = &vfe_dev->axi_data; |
| if (stream_info->stream_src < RDI_INTF_0) { |
| stream_info->bandwidth[i] = |
| (vfe_dev->vfe_clk_info[ |
| vfe_dev->hw_info->vfe_clk_idx].clk_rate / |
| axi_data->src_info[VFE_PIX_0].width) * |
| stream_info->max_width[i]; |
| stream_info->bandwidth[i] = |
| (unsigned long)stream_info->bandwidth[i] * |
| stream_info->format_factor / ISP_Q2; |
| } else { |
| int rdi = SRC_TO_INTF(stream_info->stream_src); |
| |
| bpp = msm_isp_get_bit_per_pixel( |
| stream_info->output_format); |
| if (rdi < VFE_SRC_MAX) { |
| stream_info->bandwidth[i] = |
| (vfe_dev->vfe_clk_info[ |
| vfe_dev->hw_info->vfe_clk_idx].clk_rate / |
| 8) * bpp; |
| } else { |
| pr_err("%s: Invalid rdi interface\n", __func__); |
| } |
| } |
| } |
| } |
| |
| #ifdef CONFIG_MSM_AVTIMER |
| /** |
| * msm_isp_set_avtimer_fptr() - Set avtimer function pointer |
| * @avtimer: struct of type avtimer_fptr_t to hold function pointer. |
| * |
| * Initialize the function pointers sent by the avtimer driver |
| * |
| */ |
| void msm_isp_set_avtimer_fptr(struct avtimer_fptr_t avtimer) |
| { |
| avtimer_func.fptr_avtimer_open = avtimer.fptr_avtimer_open; |
| avtimer_func.fptr_avtimer_enable = avtimer.fptr_avtimer_enable; |
| avtimer_func.fptr_avtimer_get_time = avtimer.fptr_avtimer_get_time; |
| } |
| EXPORT_SYMBOL(msm_isp_set_avtimer_fptr); |
| |
| void msm_isp_start_avtimer(void) |
| { |
| if (avtimer_func.fptr_avtimer_open && |
| avtimer_func.fptr_avtimer_enable) { |
| avtimer_func.fptr_avtimer_open(); |
| avtimer_func.fptr_avtimer_enable(1); |
| } |
| } |
| void msm_isp_stop_avtimer(void) |
| { |
| if (avtimer_func.fptr_avtimer_enable) { |
| avtimer_func.fptr_avtimer_enable(0); |
| } |
| } |
| |
| void msm_isp_get_avtimer_ts( |
| struct msm_isp_timestamp *time_stamp) |
| { |
| int rc = 0; |
| uint32_t avtimer_usec = 0; |
| uint64_t avtimer_tick = 0; |
| |
| if (avtimer_func.fptr_avtimer_get_time) { |
| rc = avtimer_func.fptr_avtimer_get_time(&avtimer_tick); |
| if (rc < 0) { |
| pr_err_ratelimited("%s: Error: Invalid AVTimer Tick, rc=%d\n", |
| __func__, rc); |
| /* In case of error return zero AVTimer Tick Value */ |
| time_stamp->vt_time.tv_sec = 0; |
| time_stamp->vt_time.tv_usec = 0; |
| } else { |
| avtimer_usec = do_div(avtimer_tick, USEC_PER_SEC); |
| time_stamp->vt_time.tv_sec = (uint32_t)(avtimer_tick); |
| time_stamp->vt_time.tv_usec = avtimer_usec; |
| pr_debug("%s: AVTimer TS = %u:%u\n", __func__, |
| (uint32_t)(avtimer_tick), avtimer_usec); |
| } |
| } |
| } |
| #else |
| void msm_isp_start_avtimer(void) |
| { |
| pr_err("AV Timer is not supported\n"); |
| } |
| |
| void msm_isp_get_avtimer_ts( |
| struct msm_isp_timestamp *time_stamp) |
| { |
| struct timespec ts; |
| |
| pr_debug("%s: AVTimer driver not available using system time\n", |
| __func__); |
| |
| get_monotonic_boottime(&ts); |
| time_stamp->vt_time.tv_sec = ts.tv_sec; |
| time_stamp->vt_time.tv_usec = ts.tv_nsec/1000; |
| } |
| #endif |
| |
| int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg) |
| { |
| int rc = 0, i = 0; |
| uint32_t io_format = 0; |
| struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd = arg; |
| struct msm_vfe_axi_stream *stream_info; |
| |
| if (stream_cfg_cmd->stream_src >= VFE_AXI_SRC_MAX) { |
| pr_err("%s:%d invalid stream_src %d\n", __func__, __LINE__, |
| stream_cfg_cmd->stream_src); |
| return -EINVAL; |
| } |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, |
| stream_cfg_cmd->stream_src); |
| |
| rc = msm_isp_axi_create_stream(vfe_dev, |
| &vfe_dev->axi_data, stream_cfg_cmd, stream_info); |
| if (rc) { |
| pr_err("%s: create stream failed\n", __func__); |
| return rc; |
| } |
| |
| rc = msm_isp_validate_axi_request( |
| vfe_dev, stream_info, stream_cfg_cmd); |
| if (rc) { |
| msm_isp_axi_destroy_stream(vfe_dev, stream_info); |
| pr_err("%s: Request validation failed\n", __func__); |
| return rc; |
| } |
| |
| stream_info->rdi_input_type = stream_cfg_cmd->rdi_input_type; |
| vfe_dev->reg_update_requested &= |
| ~(BIT(SRC_TO_INTF(stream_info->stream_src))); |
| |
| msm_isp_axi_reserve_wm(vfe_dev, stream_info); |
| |
| if (stream_info->stream_src < RDI_INTF_0) { |
| io_format = vfe_dev->axi_data.src_info[VFE_PIX_0].input_format; |
| if (stream_info->stream_src == CAMIF_RAW || |
| stream_info->stream_src == IDEAL_RAW) { |
| if (stream_info->stream_src == CAMIF_RAW && |
| io_format != stream_info->output_format) |
| pr_debug("%s: Overriding input format\n", |
| __func__); |
| |
| io_format = stream_info->output_format; |
| } |
| rc = vfe_dev->hw_info->vfe_ops.axi_ops.cfg_io_format( |
| vfe_dev, stream_info->stream_src, io_format); |
| if (rc) { |
| pr_err("%s: cfg io format failed\n", __func__); |
| goto done; |
| } |
| } |
| |
| if (!stream_info->controllable_output) { |
| /* |
| * check that the parameters passed from second vfe is same |
| * as first vfe, do this only for non controllable stream |
| * right now because user driver has bug where it sends |
| * mismatch info for controllable streams |
| */ |
| if (stream_info->num_isp > 1) { |
| if (stream_cfg_cmd->init_frame_drop != |
| stream_info->init_frame_drop) { |
| pr_err("%s: stream %d init drop mismatch %d/%d\n", |
| __func__, stream_info->stream_id, |
| stream_info->init_frame_drop, |
| stream_cfg_cmd->init_frame_drop); |
| rc = -EINVAL; |
| } |
| if (stream_cfg_cmd->frame_skip_pattern != |
| stream_info->frame_skip_pattern) { |
| pr_err("%s: stream %d skip pattern mismatch %d/%d\n", |
| __func__, stream_info->stream_id, |
| stream_info->frame_skip_pattern, |
| stream_cfg_cmd->frame_skip_pattern); |
| rc = -EINVAL; |
| } |
| if (stream_info->stream_type == CONTINUOUS_STREAM && |
| stream_cfg_cmd->burst_count > 0) { |
| pr_err("%s: stream %d stream type mismatch\n", |
| __func__, stream_info->stream_id); |
| rc = -EINVAL; |
| } |
| if (stream_info->stream_type == BURST_STREAM && |
| stream_info->num_burst_capture != |
| stream_cfg_cmd->burst_count) { |
| pr_err("%s: stream %d stream burst count mismatch %d/%d\n", |
| __func__, stream_info->stream_id, |
| stream_info->num_burst_capture, |
| stream_cfg_cmd->burst_count); |
| rc = -EINVAL; |
| } |
| } else { |
| rc = msm_isp_calculate_framedrop(vfe_dev, |
| stream_cfg_cmd); |
| } |
| if (rc) |
| goto done; |
| } else { |
| stream_info->stream_type = BURST_STREAM; |
| stream_info->num_burst_capture = 0; |
| stream_info->frame_skip_pattern = NO_SKIP; |
| stream_info->init_frame_drop = stream_cfg_cmd->init_frame_drop; |
| stream_info->current_framedrop_period = |
| MSM_VFE_STREAM_STOP_PERIOD; |
| } |
| if (stream_cfg_cmd->vt_enable && !vfe_dev->vt_enable) { |
| vfe_dev->vt_enable = stream_cfg_cmd->vt_enable; |
| msm_isp_start_avtimer(); |
| } |
| |
| if (stream_info->num_planes > 1) |
| msm_isp_axi_reserve_comp_mask(vfe_dev, stream_info); |
| |
| for (i = 0; i < stream_info->num_planes; i++) { |
| vfe_dev->hw_info->vfe_ops.axi_ops. |
| cfg_wm_reg(vfe_dev, stream_info, i); |
| |
| vfe_dev->hw_info->vfe_ops.axi_ops. |
| cfg_wm_xbar_reg(vfe_dev, stream_info, i); |
| } |
| if (stream_info->state == INACTIVE) { |
| /* initialize the WM ping pong with scratch buffer */ |
| msm_isp_cfg_stream_scratch(stream_info, VFE_PING_FLAG); |
| msm_isp_cfg_stream_scratch(stream_info, VFE_PONG_FLAG); |
| } |
| done: |
| if (rc) { |
| msm_isp_axi_free_wm(vfe_dev, stream_info); |
| msm_isp_axi_destroy_stream(vfe_dev, stream_info); |
| } |
| return rc; |
| } |
| |
| int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg) |
| { |
| int rc = 0, i = 0; |
| struct msm_vfe_axi_stream_release_cmd *stream_release_cmd = arg; |
| struct msm_vfe_axi_stream *stream_info; |
| struct msm_vfe_axi_stream_cfg_cmd stream_cfg; |
| int vfe_idx; |
| |
| if (HANDLE_TO_IDX(stream_release_cmd->stream_handle) >= |
| VFE_AXI_SRC_MAX) { |
| pr_err("%s: Invalid stream handle\n", __func__); |
| return -EINVAL; |
| } |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, |
| HANDLE_TO_IDX(stream_release_cmd->stream_handle)); |
| |
| vfe_idx = msm_isp_get_vfe_idx_for_stream_user(vfe_dev, stream_info); |
| if (vfe_idx == -ENOTTY || |
| stream_release_cmd->stream_handle != |
| stream_info->stream_handle[vfe_idx]) { |
| pr_err("%s: Invalid stream %pK handle %x/%x vfe_idx %d vfe_dev %d num_isp %d\n", |
| __func__, stream_info, |
| stream_release_cmd->stream_handle, |
| vfe_idx != -ENOTTY ? |
| stream_info->stream_handle[vfe_idx] : 0, vfe_idx, |
| vfe_dev->pdev->id, stream_info->num_isp); |
| return -EINVAL; |
| } |
| |
| if (stream_info->state != INACTIVE && stream_info->state != AVAILABLE) { |
| stream_cfg.cmd = STOP_STREAM; |
| stream_cfg.num_streams = 1; |
| stream_cfg.stream_handle[0] = stream_release_cmd->stream_handle; |
| msm_isp_cfg_axi_stream(vfe_dev, (void *) &stream_cfg); |
| } |
| |
| for (i = 0; i < stream_info->num_planes; i++) { |
| vfe_dev->hw_info->vfe_ops.axi_ops. |
| clear_wm_reg(vfe_dev, stream_info, i); |
| |
| vfe_dev->hw_info->vfe_ops.axi_ops. |
| clear_wm_xbar_reg(vfe_dev, stream_info, i); |
| } |
| |
| if (stream_info->num_planes > 1) |
| msm_isp_axi_free_comp_mask(vfe_dev, stream_info); |
| |
| vfe_dev->hw_info->vfe_ops.axi_ops.clear_framedrop(vfe_dev, stream_info); |
| msm_isp_axi_free_wm(vfe_dev, stream_info); |
| |
| msm_isp_axi_destroy_stream(vfe_dev, stream_info); |
| |
| return rc; |
| } |
| |
| void msm_isp_release_all_axi_stream(struct vfe_device *vfe_dev) |
| { |
| struct msm_vfe_axi_stream_release_cmd |
| stream_release_cmd[VFE_AXI_SRC_MAX]; |
| struct msm_vfe_axi_stream_cfg_cmd stream_cfg_cmd; |
| struct msm_vfe_axi_stream *stream_info; |
| int i; |
| int vfe_idx; |
| int num_stream = 0; |
| unsigned long flags; |
| |
| stream_cfg_cmd.cmd = STOP_STREAM; |
| stream_cfg_cmd.num_streams = 0; |
| |
| for (i = 0; i < VFE_AXI_SRC_MAX; i++) { |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, i); |
| spin_lock_irqsave(&stream_info->lock, flags); |
| vfe_idx = msm_isp_get_vfe_idx_for_stream_user( |
| vfe_dev, stream_info); |
| if (-ENOTTY == vfe_idx) { |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| continue; |
| } |
| stream_release_cmd[num_stream++].stream_handle = |
| stream_info->stream_handle[vfe_idx]; |
| if (stream_info->state == INACTIVE) { |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| continue; |
| } |
| stream_cfg_cmd.stream_handle[ |
| stream_cfg_cmd.num_streams] = |
| stream_info->stream_handle[vfe_idx]; |
| stream_cfg_cmd.num_streams++; |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| } |
| if (stream_cfg_cmd.num_streams) |
| msm_isp_cfg_axi_stream(vfe_dev, (void *) &stream_cfg_cmd); |
| |
| for (i = 0; i < num_stream; i++) |
| msm_isp_release_axi_stream(vfe_dev, &stream_release_cmd[i]); |
| } |
| |
| static void msm_isp_axi_stream_enable_cfg( |
| struct msm_vfe_axi_stream *stream_info) |
| { |
| int enable_wm = 0; |
| struct vfe_device *vfe_dev; |
| struct msm_vfe_axi_shared_data *axi_data; |
| uint32_t stream_idx = stream_info->stream_src; |
| int k; |
| int i; |
| |
| WARN_ON(stream_idx >= VFE_AXI_SRC_MAX); |
| |
| WARN_ON(stream_info->state != START_PENDING && |
| stream_info->state != RESUME_PENDING && |
| stream_info->state != STOP_PENDING && |
| stream_info->state != PAUSE_PENDING); |
| |
| if (stream_info->state == START_PENDING || |
| stream_info->state == RESUME_PENDING) { |
| enable_wm = 1; |
| } else { |
| enable_wm = 0; |
| } |
| |
| for (k = 0; k < stream_info->num_isp; k++) { |
| vfe_dev = stream_info->vfe_dev[k]; |
| axi_data = &vfe_dev->axi_data; |
| for (i = 0; i < stream_info->num_planes; i++) { |
| vfe_dev->hw_info->vfe_ops.axi_ops.enable_wm( |
| vfe_dev->vfe_base, |
| stream_info->wm[k][i], enable_wm); |
| if (enable_wm) |
| continue; |
| /* |
| * Issue a reg update for Raw Snapshot Case |
| * since we dont have reg update ack |
| */ |
| if (vfe_dev->axi_data.src_info[VFE_PIX_0]. |
| raw_stream_count > 0 |
| && vfe_dev->axi_data.src_info[VFE_PIX_0]. |
| stream_count == 0) { |
| if (stream_info->stream_src == CAMIF_RAW || |
| stream_info->stream_src == IDEAL_RAW) { |
| vfe_dev->hw_info->vfe_ops.core_ops. |
| reg_update(vfe_dev, |
| VFE_PIX_0); |
| } |
| } |
| } |
| if (stream_info->state == START_PENDING) |
| axi_data->num_active_stream++; |
| else if (stream_info->state == STOP_PENDING) |
| axi_data->num_active_stream--; |
| } |
| } |
| |
| static void __msm_isp_axi_stream_update( |
| struct msm_vfe_axi_stream *stream_info, |
| struct msm_isp_timestamp *ts) |
| { |
| int j; |
| int intf = SRC_TO_INTF(stream_info->stream_src); |
| struct vfe_device *vfe_dev; |
| int k; |
| |
| switch (stream_info->state) { |
| case UPDATING: |
| stream_info->state = ACTIVE; |
| complete_all(&stream_info->active_comp); |
| break; |
| case STOP_PENDING: |
| msm_isp_axi_stream_enable_cfg(stream_info); |
| stream_info->state = STOPPING; |
| break; |
| case START_PENDING: |
| msm_isp_axi_stream_enable_cfg(stream_info); |
| stream_info->state = STARTING; |
| break; |
| case STOPPING: |
| stream_info->state = INACTIVE; |
| for (k = 0; k < MSM_ISP_COMP_IRQ_MAX; k++) |
| stream_info->composite_irq[k] = 0; |
| complete_all(&stream_info->inactive_comp); |
| break; |
| case STARTING: |
| stream_info->state = ACTIVE; |
| complete_all(&stream_info->active_comp); |
| break; |
| case PAUSING: |
| stream_info->state = PAUSED; |
| msm_isp_reload_ping_pong_offset(stream_info); |
| for (j = 0; j < stream_info->num_planes; j++) { |
| for (k = 0; k < stream_info->num_isp; k++) { |
| vfe_dev = stream_info->vfe_dev[k]; |
| vfe_dev->hw_info->vfe_ops.axi_ops. |
| cfg_wm_reg(vfe_dev, stream_info, j); |
| } |
| } |
| stream_info->state = RESUME_PENDING; |
| msm_isp_axi_stream_enable_cfg(stream_info); |
| stream_info->state = RESUMING; |
| break; |
| case RESUMING: |
| stream_info->runtime_output_format = stream_info->output_format; |
| stream_info->state = ACTIVE; |
| complete_all(&stream_info->active_comp); |
| for (j = 0; j < stream_info->num_isp; j++) { |
| /* notify that all streams have been updated */ |
| msm_isp_notify(stream_info->vfe_dev[j], |
| ISP_EVENT_STREAM_UPDATE_DONE, intf, ts); |
| atomic_set(&stream_info->vfe_dev[j]-> |
| axi_data.axi_cfg_update[intf], 0); |
| } |
| stream_info->update_vfe_mask = 0; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void msm_isp_axi_stream_update(struct vfe_device *vfe_dev, |
| enum msm_vfe_input_src frame_src, |
| struct msm_isp_timestamp *ts) |
| { |
| int i; |
| unsigned long flags; |
| struct msm_vfe_axi_stream *stream_info; |
| |
| for (i = 0; i < VFE_AXI_SRC_MAX; i++) { |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, i); |
| if (SRC_TO_INTF(stream_info->stream_src) != |
| frame_src) { |
| ISP_DBG("%s stream_src %d frame_src %d\n", __func__, |
| SRC_TO_INTF( |
| stream_info->stream_src), |
| frame_src); |
| continue; |
| } |
| if (stream_info->state == AVAILABLE) |
| continue; |
| spin_lock_irqsave(&stream_info->lock, flags); |
| __msm_isp_axi_stream_update(stream_info, ts); |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| } |
| } |
| |
| static void msm_isp_reload_ping_pong_offset( |
| struct msm_vfe_axi_stream *stream_info) |
| { |
| int i, j; |
| uint32_t bit; |
| struct msm_isp_buffer *buf; |
| int32_t buf_size_byte = 0; |
| int32_t word_per_line = 0; |
| int k; |
| struct vfe_device *vfe_dev; |
| |
| for (k = 0; k < stream_info->num_isp; k++) { |
| vfe_dev = stream_info->vfe_dev[k]; |
| for (i = 0; i < 2; i++) { |
| buf = stream_info->buf[i]; |
| if (!buf) |
| continue; |
| |
| bit = i ? 0 : 1; |
| |
| for (j = 0; j < stream_info->num_planes; j++) { |
| word_per_line = msm_isp_cal_word_per_line( |
| stream_info->output_format, stream_info-> |
| plane_cfg[k][j].output_stride); |
| if (word_per_line < 0) { |
| /* 0 means no prefetch*/ |
| word_per_line = 0; |
| buf_size_byte = 0; |
| } else { |
| buf_size_byte = (word_per_line * 8 * |
| stream_info->plane_cfg[k][j]. |
| output_scan_lines) - stream_info-> |
| plane_cfg[k][j].plane_addr_offset; |
| } |
| |
| vfe_dev->hw_info->vfe_ops.axi_ops. |
| update_ping_pong_addr( |
| vfe_dev->vfe_base, |
| stream_info->wm[k][j], |
| bit, |
| buf->mapped_info[j].paddr + |
| stream_info->plane_cfg[k][j]. |
| plane_addr_offset, |
| buf_size_byte); |
| } |
| } |
| } |
| } |
| |
| static int msm_isp_update_deliver_count(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_bit, |
| struct msm_isp_buffer *done_buf) |
| { |
| int rc = 0; |
| |
| if (!stream_info->controllable_output) |
| goto done; |
| |
| if (!stream_info->undelivered_request_cnt || |
| (done_buf == NULL)) { |
| pr_err_ratelimited("%s:%d error undelivered_request_cnt 0\n", |
| __func__, __LINE__); |
| rc = -EINVAL; |
| goto done; |
| } else { |
| if ((done_buf->is_drop_reconfig == 1) && |
| (stream_info->sw_ping_pong_bit == -1)) { |
| goto done; |
| } |
| /*After wm reload, we get bufdone for ping buffer*/ |
| if (stream_info->sw_ping_pong_bit == -1) |
| stream_info->sw_ping_pong_bit = 0; |
| if (done_buf->is_drop_reconfig != 1) |
| stream_info->undelivered_request_cnt--; |
| if (pingpong_bit != stream_info->sw_ping_pong_bit) { |
| pr_err("%s:%d ping pong bit actual %d sw %d\n", |
| __func__, __LINE__, pingpong_bit, |
| stream_info->sw_ping_pong_bit); |
| rc = -EINVAL; |
| goto done; |
| } |
| stream_info->sw_ping_pong_bit ^= 1; |
| } |
| done: |
| return rc; |
| } |
| |
| void msm_isp_halt_send_error(struct vfe_device *vfe_dev, uint32_t event) |
| { |
| struct msm_isp_event_data error_event; |
| struct msm_vfe_axi_halt_cmd halt_cmd; |
| struct vfe_device *temp_dev = NULL; |
| uint32_t irq_status0 = 0, irq_status1 = 0; |
| |
| if (atomic_read(&vfe_dev->error_info.overflow_state) != |
| NO_OVERFLOW) |
| /* Recovery is already in Progress */ |
| return; |
| |
| if (event == ISP_EVENT_PING_PONG_MISMATCH && |
| vfe_dev->axi_data.recovery_count < MAX_RECOVERY_THRESHOLD) { |
| pr_err("%s: ping pong mismatch on vfe%d recovery count %d\n", |
| __func__, vfe_dev->pdev->id, |
| vfe_dev->axi_data.recovery_count); |
| msm_isp_process_overflow_irq(vfe_dev, |
| &irq_status0, &irq_status1, 1); |
| vfe_dev->axi_data.recovery_count++; |
| return; |
| } |
| memset(&halt_cmd, 0, sizeof(struct msm_vfe_axi_halt_cmd)); |
| memset(&error_event, 0, sizeof(struct msm_isp_event_data)); |
| halt_cmd.stop_camif = 1; |
| halt_cmd.overflow_detected = 0; |
| halt_cmd.blocking_halt = 0; |
| |
| pr_err("%s: vfe%d fatal error!\n", __func__, vfe_dev->pdev->id); |
| |
| atomic_set(&vfe_dev->error_info.overflow_state, |
| HALT_ENFORCED); |
| |
| vfe_dev->hw_info->vfe_ops.core_ops.set_halt_restart_mask(vfe_dev); |
| if (vfe_dev->is_split) { |
| int other_vfe_id = (vfe_dev->pdev->id == ISP_VFE0 ? |
| ISP_VFE1 : ISP_VFE0); |
| temp_dev = vfe_dev->common_data-> |
| dual_vfe_res->vfe_dev[other_vfe_id]; |
| atomic_set(&temp_dev->error_info.overflow_state, |
| HALT_ENFORCED); |
| temp_dev->hw_info->vfe_ops.core_ops. |
| set_halt_restart_mask(temp_dev); |
| } |
| error_event.frame_id = |
| vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id; |
| |
| msm_isp_send_event(vfe_dev, event, &error_event); |
| } |
| |
| int msm_isp_print_ping_pong_address(struct vfe_device *vfe_dev, |
| unsigned long fault_addr) |
| { |
| int i, j; |
| struct msm_isp_buffer *buf = NULL; |
| uint32_t pingpong_bit; |
| struct msm_vfe_axi_stream *stream_info = NULL; |
| int k; |
| |
| for (j = 0; j < VFE_AXI_SRC_MAX; j++) { |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, j); |
| if (stream_info->state == INACTIVE || |
| stream_info->state == AVAILABLE) |
| continue; |
| |
| for (pingpong_bit = 0; pingpong_bit < 2; pingpong_bit++) { |
| dma_addr_t temp; |
| |
| buf = stream_info->buf[pingpong_bit]; |
| if (buf == NULL) { |
| pr_err("%s: buf NULL for stream %x num_isp %d\n", |
| __func__, |
| stream_info->stream_src, |
| stream_info->num_isp); |
| continue; |
| } |
| temp = buf->mapped_info[0].paddr + |
| buf->mapped_info[0].len; |
| pr_err("%s: stream %x ping bit %d uses buffer %pK-%pK, num_isp %d\n", |
| __func__, stream_info->stream_src, |
| pingpong_bit, |
| &buf->mapped_info[0].paddr, &temp, |
| stream_info->num_isp); |
| |
| for (i = 0; i < stream_info->num_planes; i++) { |
| for (k = 0; k < stream_info->num_isp; k++) { |
| pr_debug( |
| "%s: stream_id %x ping-pong %d plane %d start_addr %pK addr_offset %x len %zx stride %d scanline %d\n" |
| , __func__, stream_info->stream_id, |
| pingpong_bit, i, |
| (void *)buf->mapped_info[i].paddr, |
| stream_info-> |
| plane_cfg[k][i].plane_addr_offset, |
| buf->mapped_info[i].len, |
| stream_info-> |
| plane_cfg[k][i].output_stride, |
| stream_info-> |
| plane_cfg[k][i].output_scan_lines |
| ); |
| } |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| static struct msm_isp_buffer *msm_isp_get_stream_buffer( |
| struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream *stream_info) |
| { |
| int rc = 0; |
| uint32_t bufq_handle = 0; |
| struct msm_isp_buffer *buf = NULL; |
| struct msm_vfe_frame_request_queue *queue_req; |
| uint32_t buf_index = MSM_ISP_INVALID_BUF_INDEX; |
| |
| if (!stream_info->controllable_output) { |
| bufq_handle = stream_info->bufq_handle |
| [VFE_BUF_QUEUE_DEFAULT]; |
| } else { |
| queue_req = list_first_entry_or_null( |
| &stream_info->request_q, |
| struct msm_vfe_frame_request_queue, list); |
| if (!queue_req) |
| return buf; |
| |
| bufq_handle = stream_info-> |
| bufq_handle[queue_req->buff_queue_id]; |
| |
| if (!bufq_handle || |
| stream_info->request_q_cnt <= 0) { |
| pr_err_ratelimited("%s: Drop request. Shared stream is stopped.\n", |
| __func__); |
| return buf; |
| } |
| buf_index = queue_req->buf_index; |
| queue_req->cmd_used = 0; |
| list_del(&queue_req->list); |
| stream_info->request_q_cnt--; |
| } |
| rc = vfe_dev->buf_mgr->ops->get_buf(vfe_dev->buf_mgr, |
| vfe_dev->pdev->id, bufq_handle, buf_index, &buf); |
| |
| if (rc == -EFAULT) { |
| msm_isp_halt_send_error(vfe_dev, |
| ISP_EVENT_BUF_FATAL_ERROR); |
| return buf; |
| } |
| if (rc < 0) |
| return buf; |
| |
| if (buf->num_planes != stream_info->num_planes) { |
| pr_err("%s: Invalid buffer\n", __func__); |
| vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr, |
| bufq_handle, buf->buf_idx); |
| buf = NULL; |
| } |
| return buf; |
| } |
| |
| int msm_isp_cfg_offline_ping_pong_address(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status, |
| uint32_t buf_idx) |
| { |
| int i, rc = 0; |
| struct msm_isp_buffer *buf = NULL; |
| uint32_t pingpong_bit; |
| uint32_t buffer_size_byte = 0; |
| int32_t word_per_line = 0; |
| dma_addr_t paddr; |
| uint32_t bufq_handle = 0; |
| int vfe_idx; |
| |
| bufq_handle = stream_info->bufq_handle[VFE_BUF_QUEUE_DEFAULT]; |
| |
| if (!vfe_dev->is_split) { |
| rc = vfe_dev->buf_mgr->ops->get_buf_by_index( |
| vfe_dev->buf_mgr, bufq_handle, buf_idx, &buf); |
| if (rc < 0 || !buf) { |
| pr_err("%s: No fetch buffer rc= %d buf= %pK\n", |
| __func__, rc, buf); |
| return -EINVAL; |
| } |
| |
| if (buf->num_planes != stream_info->num_planes) { |
| pr_err("%s: Invalid buffer\n", __func__); |
| vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr, |
| bufq_handle, buf->buf_idx); |
| return -EINVAL; |
| } |
| vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); |
| pingpong_bit = ((pingpong_status >> |
| stream_info->wm[vfe_idx][0]) & 0x1); |
| |
| for (i = 0; i < stream_info->num_planes; i++) { |
| word_per_line = msm_isp_cal_word_per_line( |
| stream_info->output_format, |
| stream_info->plane_cfg[vfe_idx][i]. |
| output_stride); |
| if (word_per_line < 0) { |
| /* 0 means no prefetch*/ |
| word_per_line = 0; |
| buffer_size_byte = 0; |
| } else { |
| buffer_size_byte = (word_per_line * 8 * |
| stream_info->plane_cfg[vfe_idx][i]. |
| output_scan_lines) - |
| stream_info-> |
| plane_cfg[vfe_idx][i].plane_addr_offset; |
| } |
| paddr = buf->mapped_info[i].paddr; |
| |
| vfe_dev->hw_info->vfe_ops.axi_ops. |
| update_ping_pong_addr( |
| vfe_dev->vfe_base, stream_info->wm[vfe_idx][i], |
| pingpong_bit, paddr + |
| stream_info-> |
| plane_cfg[vfe_idx][i].plane_addr_offset, |
| buffer_size_byte); |
| stream_info->buf[!pingpong_bit] = buf; |
| buf->pingpong_bit = !pingpong_bit; |
| } |
| buf->state = MSM_ISP_BUFFER_STATE_DEQUEUED; |
| stream_info->buf[!pingpong_bit] = buf; |
| buf->pingpong_bit = !pingpong_bit; |
| } |
| return rc; |
| |
| } |
| |
| static int msm_isp_cfg_ping_pong_address( |
| struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status, |
| struct msm_isp_buffer *buf) |
| { |
| int i; |
| int j; |
| uint32_t pingpong_bit; |
| struct vfe_device *vfe_dev = stream_info->vfe_dev[0]; |
| uint32_t buffer_size_byte = 0; |
| int32_t word_per_line = 0; |
| dma_addr_t paddr; |
| |
| |
| /* Isolate pingpong_bit from pingpong_status */ |
| pingpong_bit = ((pingpong_status >> |
| stream_info->wm[0][0]) & 0x1); |
| |
| /* return if buffer already present */ |
| if (stream_info->buf[!pingpong_bit]) { |
| pr_err("stream %x buffer already set for pingpong %d\n", |
| stream_info->stream_src, !pingpong_bit); |
| return 1; |
| } |
| |
| if (buf == NULL) |
| buf = msm_isp_get_stream_buffer(vfe_dev, stream_info); |
| |
| if (!buf) { |
| msm_isp_cfg_stream_scratch(stream_info, pingpong_status); |
| return 0; |
| } |
| |
| for (i = 0; i < stream_info->num_planes; i++) { |
| paddr = buf->mapped_info[i].paddr; |
| ISP_DBG( |
| "%s: vfe %d config buf %d to pingpong %d stream %x\n", |
| __func__, vfe_dev->pdev->id, |
| buf->buf_idx, !pingpong_bit, |
| stream_info->stream_id); |
| for (j = 0; j < stream_info->num_isp; j++) { |
| vfe_dev = stream_info->vfe_dev[j]; |
| word_per_line = |
| msm_isp_cal_word_per_line( |
| stream_info->output_format, |
| stream_info->plane_cfg[j][i].output_stride); |
| if (word_per_line < 0) { |
| /* 0 means no prefetch*/ |
| word_per_line = 0; |
| buffer_size_byte = 0; |
| } else { |
| buffer_size_byte = |
| (word_per_line * 8 * |
| stream_info->plane_cfg[j][i]. |
| output_scan_lines) - |
| stream_info->plane_cfg[j][i]. |
| plane_addr_offset; |
| } |
| vfe_dev->hw_info->vfe_ops.axi_ops. |
| update_ping_pong_addr( |
| vfe_dev->vfe_base, |
| stream_info->wm[j][i], |
| pingpong_bit, paddr + |
| stream_info->plane_cfg[j][i]. |
| plane_addr_offset, |
| buffer_size_byte); |
| } |
| } |
| stream_info->buf[!pingpong_bit] = buf; |
| buf->pingpong_bit = !pingpong_bit; |
| return 0; |
| } |
| |
| static void msm_isp_handle_done_buf_frame_id_mismatch( |
| struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info, |
| struct msm_isp_buffer *buf, struct timeval *time_stamp, |
| uint32_t frame_id) |
| { |
| struct msm_isp_event_data error_event; |
| int ret = 0; |
| |
| memset(&error_event, 0, sizeof(error_event)); |
| error_event.frame_id = |
| vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id; |
| error_event.u.error_info.err_type = |
| ISP_ERROR_FRAME_ID_MISMATCH; |
| if (stream_info->buf_divert) |
| vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr, |
| buf->bufq_handle, buf->buf_idx); |
| else |
| ret = vfe_dev->buf_mgr->ops->buf_done(vfe_dev->buf_mgr, |
| buf->bufq_handle, buf->buf_idx, time_stamp, |
| frame_id, |
| stream_info->runtime_output_format); |
| if (ret == -EFAULT) { |
| msm_isp_halt_send_error(vfe_dev, ISP_EVENT_BUF_FATAL_ERROR); |
| return; |
| } |
| msm_isp_send_event(vfe_dev, ISP_EVENT_ERROR, |
| &error_event); |
| pr_err("%s: Error! frame id mismatch!! 1st buf frame %d,curr frame %d\n", |
| __func__, buf->frame_id, frame_id); |
| vfe_dev->buf_mgr->frameId_mismatch_recovery = 1; |
| } |
| |
| static int msm_isp_process_done_buf(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream *stream_info, struct msm_isp_buffer *buf, |
| struct timeval *time_stamp, uint32_t frame_id) |
| { |
| int rc; |
| unsigned long flags; |
| struct msm_isp_event_data buf_event; |
| uint32_t stream_idx = stream_info->stream_src; |
| uint32_t buf_src; |
| uint8_t drop_frame = 0; |
| struct msm_isp_bufq *bufq = NULL; |
| |
| memset(&buf_event, 0, sizeof(buf_event)); |
| |
| if (stream_idx >= VFE_AXI_SRC_MAX) { |
| pr_err_ratelimited("%s: Invalid stream_idx", __func__); |
| return -EINVAL; |
| } |
| |
| if (SRC_TO_INTF(stream_info->stream_src) >= VFE_SRC_MAX) { |
| pr_err_ratelimited("%s: Invalid stream index, put buf back to vb2 queue\n", |
| __func__); |
| rc = vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr, |
| buf->bufq_handle, buf->buf_idx); |
| return -EINVAL; |
| } |
| |
| if (stream_info->stream_type != BURST_STREAM && |
| (stream_info->sw_skip.stream_src_mask & |
| (1 << stream_info->stream_src))) { |
| /* Hw stream output of this src is requested for drop */ |
| if (stream_info->sw_skip.skip_mode == SKIP_ALL) { |
| /* drop all buffers */ |
| drop_frame = 1; |
| } else if (stream_info->sw_skip.skip_mode == SKIP_RANGE && |
| (stream_info->sw_skip.min_frame_id <= frame_id && |
| stream_info->sw_skip.max_frame_id >= frame_id)) { |
| drop_frame = 1; |
| } else if (frame_id > stream_info->sw_skip.max_frame_id) { |
| spin_lock_irqsave(&stream_info->lock, flags); |
| memset(&stream_info->sw_skip, 0, |
| sizeof(struct msm_isp_sw_framskip)); |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| } |
| } |
| |
| rc = vfe_dev->buf_mgr->ops->get_buf_src(vfe_dev->buf_mgr, |
| buf->bufq_handle, &buf_src); |
| if (rc != 0) { |
| pr_err_ratelimited("%s: Error getting buf_src\n", __func__); |
| return -EINVAL; |
| } |
| |
| if (drop_frame) { |
| buf->buf_debug.put_state[ |
| buf->buf_debug.put_state_last] = |
| MSM_ISP_BUFFER_STATE_DROP_SKIP; |
| buf->buf_debug.put_state_last ^= 1; |
| if (stream_info->buf_divert) |
| vfe_dev->buf_mgr->ops->put_buf( |
| vfe_dev->buf_mgr, |
| buf->bufq_handle, buf->buf_idx); |
| else |
| rc = vfe_dev->buf_mgr->ops->buf_done( |
| vfe_dev->buf_mgr, |
| buf->bufq_handle, buf->buf_idx, |
| time_stamp, frame_id, |
| stream_info->runtime_output_format); |
| |
| if (rc == -EFAULT) { |
| msm_isp_halt_send_error(vfe_dev, |
| ISP_EVENT_BUF_FATAL_ERROR); |
| return rc; |
| } |
| if (!rc) { |
| ISP_DBG("%s:%d vfe_id %d Buffer dropped %d\n", |
| __func__, __LINE__, vfe_dev->pdev->id, |
| frame_id); |
| /* |
| * Return rc which is 0 at this point so that |
| * we can cfg ping pong and we can continue |
| * streaming |
| */ |
| return rc; |
| } |
| } |
| |
| buf_event.frame_id = frame_id; |
| buf_event.timestamp = *time_stamp; |
| buf_event.u.buf_done.session_id = stream_info->session_id; |
| buf_event.u.buf_done.stream_id = stream_info->stream_id; |
| buf_event.u.buf_done.handle = buf->bufq_handle; |
| buf_event.u.buf_done.buf_idx = buf->buf_idx; |
| buf_event.u.buf_done.output_format = |
| stream_info->runtime_output_format; |
| if (vfe_dev->fetch_engine_info.is_busy && |
| SRC_TO_INTF(stream_info->stream_src) == VFE_PIX_0) { |
| vfe_dev->fetch_engine_info.is_busy = 0; |
| } |
| |
| if (stream_info->buf_divert && |
| buf_src != MSM_ISP_BUFFER_SRC_SCRATCH) { |
| |
| bufq = vfe_dev->buf_mgr->ops->get_bufq(vfe_dev->buf_mgr, |
| buf->bufq_handle); |
| if (!bufq) { |
| pr_err("%s: Invalid bufq buf_handle %x\n", |
| __func__, buf->bufq_handle); |
| return -EINVAL; |
| } |
| |
| /* divert native buffers */ |
| vfe_dev->buf_mgr->ops->buf_divert(vfe_dev->buf_mgr, |
| buf->bufq_handle, buf->buf_idx, time_stamp, |
| frame_id); |
| |
| if ((bufq != NULL) && bufq->buf_type == ISP_SHARE_BUF) |
| msm_isp_send_event(vfe_dev->common_data-> |
| dual_vfe_res->vfe_dev[ISP_VFE1], |
| ISP_EVENT_BUF_DIVERT, &buf_event); |
| else |
| msm_isp_send_event(vfe_dev, |
| ISP_EVENT_BUF_DIVERT, &buf_event); |
| } else { |
| ISP_DBG("%s: vfe_id %d send buf done buf-id %d bufq %x\n", |
| __func__, vfe_dev->pdev->id, buf->buf_idx, |
| buf->bufq_handle); |
| msm_isp_send_event(vfe_dev, ISP_EVENT_BUF_DONE, |
| &buf_event); |
| buf->buf_debug.put_state[ |
| buf->buf_debug.put_state_last] = |
| MSM_ISP_BUFFER_STATE_PUT_BUF; |
| buf->buf_debug.put_state_last ^= 1; |
| rc = vfe_dev->buf_mgr->ops->buf_done(vfe_dev->buf_mgr, |
| buf->bufq_handle, buf->buf_idx, time_stamp, |
| frame_id, stream_info->runtime_output_format); |
| if (rc == -EFAULT) { |
| msm_isp_halt_send_error(vfe_dev, |
| ISP_EVENT_BUF_FATAL_ERROR); |
| return rc; |
| } |
| } |
| |
| return 0; |
| } |
| |
| int msm_isp_drop_frame(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream *stream_info, struct msm_isp_timestamp *ts, |
| struct msm_isp_sof_info *sof_info) |
| { |
| struct msm_isp_buffer *done_buf = NULL; |
| uint32_t pingpong_status; |
| unsigned long flags; |
| struct msm_isp_bufq *bufq = NULL; |
| uint32_t pingpong_bit; |
| int vfe_idx; |
| int rc = -1; |
| |
| if (!vfe_dev || !stream_info || !ts || !sof_info) { |
| pr_err("%s %d vfe_dev %pK stream_info %pK ts %pK op_info %pK\n", |
| __func__, __LINE__, vfe_dev, stream_info, ts, |
| sof_info); |
| return -EINVAL; |
| } |
| vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); |
| |
| pingpong_status = |
| ~vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(vfe_dev); |
| |
| spin_lock_irqsave(&stream_info->lock, flags); |
| pingpong_bit = |
| (~(pingpong_status >> stream_info->wm[vfe_idx][0]) & 0x1); |
| done_buf = stream_info->buf[pingpong_bit]; |
| if (done_buf && |
| (stream_info->composite_irq[MSM_ISP_COMP_IRQ_EPOCH] == 0)) { |
| if ((stream_info->sw_ping_pong_bit != -1) && |
| !vfe_dev->reg_updated) { |
| rc = msm_isp_cfg_ping_pong_address( |
| stream_info, ~pingpong_status, done_buf); |
| if (rc < 0) { |
| ISP_DBG("%s: Error configuring ping_pong\n", |
| __func__); |
| bufq = vfe_dev->buf_mgr->ops->get_bufq( |
| vfe_dev->buf_mgr, |
| done_buf->bufq_handle); |
| if (!bufq) { |
| spin_unlock_irqrestore( |
| &stream_info->lock, |
| flags); |
| pr_err("%s: Invalid bufq buf_handle %x\n", |
| __func__, |
| done_buf->bufq_handle); |
| return -EINVAL; |
| } |
| sof_info->reg_update_fail_mask_ext |= |
| (bufq->bufq_handle & 0xFF); |
| } |
| } |
| /*Avoid Drop Frame and re-issue pingpong cfg*/ |
| /*this notify is per ping and pong buffer*/ |
| done_buf->is_drop_reconfig = 1; |
| stream_info->current_framedrop_period = 1; |
| /*Avoid Multiple request frames for single SOF*/ |
| vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame = false; |
| |
| if (stream_info->current_framedrop_period != |
| stream_info->requested_framedrop_period) { |
| msm_isp_cfg_framedrop_reg(stream_info); |
| } |
| } |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| |
| /* if buf done will not come, we need to process it ourself */ |
| if (stream_info->activated_framedrop_period == |
| MSM_VFE_STREAM_STOP_PERIOD) { |
| /* no buf done come */ |
| msm_isp_process_axi_irq_stream(vfe_dev, stream_info, |
| pingpong_status, ts); |
| if (done_buf) |
| done_buf->is_drop_reconfig = 0; |
| } |
| return 0; |
| } |
| |
| /** |
| * msm_isp_input_disable() - Disable the input for given vfe |
| * @vfe_dev: The vfe device whose input is to be disabled |
| * |
| * Returns - void |
| * |
| * If stream count on an input line is 0 then disable the input |
| */ |
| static void msm_isp_input_disable(struct vfe_device *vfe_dev, int cmd_type) |
| { |
| struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; |
| int stream_count; |
| int total_stream_count = 0; |
| int i; |
| struct msm_vfe_src_info *src_info; |
| int ext_read = |
| (axi_data->src_info[VFE_PIX_0].input_mux == EXTERNAL_READ); |
| |
| for (i = 0; i < VFE_SRC_MAX; i++) |
| total_stream_count += axi_data->src_info[i].stream_count + |
| axi_data->src_info[i].raw_stream_count; |
| |
| for (i = 0; i < VFE_SRC_MAX; i++) { |
| stream_count = axi_data->src_info[i].stream_count + |
| axi_data->src_info[i].raw_stream_count; |
| if (stream_count) |
| continue; |
| if (axi_data->src_info[i].active == 0) |
| continue; |
| /* deactivate the input line */ |
| axi_data->src_info[i].active = 0; |
| src_info = &axi_data->src_info[i]; |
| |
| if (src_info->dual_hw_type == DUAL_HW_MASTER_SLAVE) { |
| struct master_slave_resource_info *ms_res = |
| &vfe_dev->common_data->ms_resource; |
| unsigned long flags; |
| |
| spin_lock_irqsave( |
| &vfe_dev->common_data->common_dev_data_lock, |
| flags); |
| if (src_info->dual_hw_ms_info.index == |
| ms_res->master_index) |
| ms_res->master_index = -1; |
| if (src_info->dual_hw_ms_info.index == |
| ms_res->primary_slv_idx) |
| ms_res->primary_slv_idx = -1; |
| ms_res->active_src_mask &= ~(1 << |
| src_info->dual_hw_ms_info.index); |
| ms_res->src_sof_mask &= ~(1 << |
| src_info->dual_hw_ms_info.index); |
| ms_res->src_info[src_info->dual_hw_ms_info.index] = |
| NULL; |
| ms_res->num_src--; |
| if (ms_res->num_src == 0) |
| ms_res->dual_sync_mode = MSM_ISP_DUAL_CAM_ASYNC; |
| src_info->dual_hw_ms_info.sync_state = |
| MSM_ISP_DUAL_CAM_ASYNC; |
| src_info->dual_hw_type = DUAL_NONE; |
| src_info->dual_hw_ms_info.index = -1; |
| spin_unlock_irqrestore( |
| &vfe_dev->common_data->common_dev_data_lock, |
| flags); |
| } |
| if (i != VFE_PIX_0 || ext_read) |
| continue; |
| if (total_stream_count == 0 || cmd_type == STOP_IMMEDIATELY) |
| vfe_dev->hw_info->vfe_ops.core_ops. |
| update_camif_state(vfe_dev, |
| DISABLE_CAMIF_IMMEDIATELY); |
| else |
| vfe_dev->hw_info->vfe_ops.core_ops. |
| update_camif_state(vfe_dev, |
| DISABLE_CAMIF); |
| } |
| /* |
| * halt and reset hardware if all streams are disabled, in this case |
| * ispif is halted immediately as well |
| */ |
| if (total_stream_count == 0) { |
| vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 1); |
| msm_isp_flush_tasklet(vfe_dev); |
| vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, 0, 1); |
| if (msm_vfe_is_vfe48(vfe_dev)) |
| vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, |
| 0, 1); |
| vfe_dev->hw_info->vfe_ops.core_ops.init_hw_reg(vfe_dev); |
| } |
| |
| } |
| |
| /** |
| * msm_isp_input_enable() - Enable the input for given vfe |
| * @vfe_dev: The vfe device whose input is to be enabled |
| * |
| * Returns - void |
| * |
| * Enable inout line if it is not enabled |
| */ |
| static void msm_isp_input_enable(struct vfe_device *vfe_dev, |
| int sync_frame_id_src) |
| { |
| struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; |
| int ext_read = |
| (axi_data->src_info[VFE_PIX_0].input_mux == EXTERNAL_READ); |
| int stream_count; |
| int i; |
| |
| for (i = 0; i < VFE_SRC_MAX; i++) { |
| stream_count = axi_data->src_info[i].stream_count + |
| axi_data->src_info[i].raw_stream_count; |
| if (stream_count == 0) |
| continue; |
| if (axi_data->src_info[i].active) |
| continue; |
| /* activate the input since it is deactivated */ |
| axi_data->src_info[i].frame_id = 0; |
| if (axi_data->src_info[i].input_mux != EXTERNAL_READ) |
| axi_data->src_info[i].active = 1; |
| if (i >= VFE_RAW_0 && sync_frame_id_src) { |
| /* |
| * Incase PIX and RDI streams are part |
| * of same session, this will ensure |
| * RDI stream will have same frame id |
| * as of PIX stream |
| */ |
| axi_data->src_info[i].frame_id = |
| axi_data->src_info[VFE_PIX_0].frame_id; |
| } |
| /* when start reset overflow state and cfg ub for this intf */ |
| vfe_dev->hw_info->vfe_ops.axi_ops.cfg_ub(vfe_dev, i); |
| atomic_set(&vfe_dev->error_info.overflow_state, |
| NO_OVERFLOW); |
| if (i != VFE_PIX_0 || ext_read) |
| continue; |
| /* for camif input the camif needs enabling */ |
| vfe_dev->hw_info->vfe_ops.core_ops. |
| update_camif_state(vfe_dev, ENABLE_CAMIF); |
| } |
| } |
| |
| /** |
| * msm_isp_update_intf_stream_cnt() - Update the stream count in axi interface |
| * @stream_info: The stream that is either being enabled/disabled |
| * @enable: 0 means stream is being disabled, else enabled |
| * |
| * Returns - void |
| */ |
| static void msm_isp_update_intf_stream_cnt( |
| struct msm_vfe_axi_stream *stream_info, |
| int enable) |
| { |
| int i; |
| |
| switch (stream_info->stream_src) { |
| case PIX_ENCODER: |
| case PIX_VIEWFINDER: |
| case PIX_VIDEO: |
| case IDEAL_RAW: |
| case RDI_INTF_0: |
| case RDI_INTF_1: |
| case RDI_INTF_2: |
| for (i = 0; i < stream_info->num_isp; i++) { |
| if (enable) |
| stream_info->vfe_dev[i]->axi_data.src_info[ |
| SRC_TO_INTF(stream_info->stream_src)]. |
| stream_count++; |
| else |
| stream_info->vfe_dev[i]->axi_data.src_info[ |
| SRC_TO_INTF(stream_info->stream_src)]. |
| stream_count--; |
| } |
| break; |
| case CAMIF_RAW: |
| for (i = 0; i < stream_info->num_isp; i++) { |
| if (enable) |
| stream_info->vfe_dev[i]->axi_data.src_info[ |
| SRC_TO_INTF(stream_info->stream_src)]. |
| raw_stream_count++; |
| else |
| stream_info->vfe_dev[i]->axi_data.src_info[ |
| SRC_TO_INTF(stream_info->stream_src)]. |
| raw_stream_count--; |
| } |
| break; |
| default: |
| WARN(1, "Invalid steam src %d\n", stream_info->stream_src); |
| } |
| } |
| |
| /*Factor in Q2 format*/ |
| #define ISP_DEFAULT_FORMAT_FACTOR 6 |
| #define ISP_BUS_UTILIZATION_FACTOR 6 |
| static int msm_isp_update_stream_bandwidth( |
| struct msm_vfe_axi_stream *stream_info, int enable) |
| { |
| int i, rc = 0; |
| uint64_t total_bandwidth = 0; |
| int vfe_idx; |
| struct vfe_device *vfe_dev; |
| |
| for (i = 0; i < stream_info->num_isp; i++) { |
| vfe_dev = stream_info->vfe_dev[i]; |
| vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, |
| stream_info); |
| if (enable) { |
| total_bandwidth = |
| vfe_dev->total_bandwidth + |
| stream_info->bandwidth[vfe_idx]; |
| } else { |
| total_bandwidth = vfe_dev->total_bandwidth - |
| stream_info->bandwidth[vfe_idx]; |
| } |
| vfe_dev->total_bandwidth = total_bandwidth; |
| rc = msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id, |
| (total_bandwidth + vfe_dev->hw_info->min_ab), |
| (total_bandwidth + vfe_dev->hw_info->min_ib)); |
| |
| if (rc < 0) |
| pr_err("%s: update failed rc %d stream src %d vfe dev %d\n", |
| __func__, rc, stream_info->stream_src, |
| vfe_dev->pdev->id); |
| } |
| return rc; |
| } |
| |
| int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg) |
| { |
| int i, rc = 0; |
| uint32_t intf; |
| unsigned long flags; |
| struct msm_vfe_axi_stream *stream_info; |
| struct msm_vfe_dual_lpm_mode *ab_ib_vote = NULL; |
| |
| ab_ib_vote = (struct msm_vfe_dual_lpm_mode *)arg; |
| if (!ab_ib_vote) { |
| pr_err("%s: ab_ib_vote is NULL !!!\n", __func__); |
| rc = -1; |
| return rc; |
| } |
| if (ab_ib_vote->num_src >= VFE_AXI_SRC_MAX) { |
| pr_err("%s: ab_ib_vote num_src is exceeding limit\n", |
| __func__); |
| rc = -1; |
| return rc; |
| } |
| if (ab_ib_vote->num_src >= VFE_AXI_SRC_MAX) { |
| pr_err("%s: ab_ib_vote num_src is exceeding limit\n", |
| __func__); |
| rc = -1; |
| return rc; |
| } |
| if (ab_ib_vote->lpm_mode) { |
| for (i = 0; i < ab_ib_vote->num_src; i++) { |
| stream_info = |
| msm_isp_get_stream_common_data(vfe_dev, |
| ab_ib_vote->stream_src[i]); |
| if (stream_info == NULL) |
| continue; |
| /* loop all stream on current session */ |
| spin_lock_irqsave(&stream_info->lock, flags); |
| intf = SRC_TO_INTF(stream_info->stream_src); |
| vfe_dev->axi_data.src_info[intf].lpm = |
| ab_ib_vote->lpm_mode; |
| if (stream_info->lpm_mode || |
| stream_info->state == INACTIVE) { |
| spin_unlock_irqrestore(&stream_info->lock, |
| flags); |
| continue; |
| } |
| stream_info->lpm_mode = ab_ib_vote->lpm_mode; |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| msm_isp_update_stream_bandwidth(stream_info, 0); |
| } |
| } else { |
| for (i = 0; i < ab_ib_vote->num_src; i++) { |
| stream_info = |
| msm_isp_get_stream_common_data(vfe_dev, |
| ab_ib_vote->stream_src[i]); |
| if (stream_info == NULL) |
| continue; |
| spin_lock_irqsave(&stream_info->lock, flags); |
| intf = SRC_TO_INTF(stream_info->stream_src); |
| vfe_dev->axi_data.src_info[intf].lpm = |
| ab_ib_vote->lpm_mode; |
| if (stream_info->lpm_mode == 0 || |
| stream_info->state == INACTIVE) { |
| spin_unlock_irqrestore(&stream_info->lock, |
| flags); |
| continue; |
| } |
| stream_info->lpm_mode = 0; |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| msm_isp_update_stream_bandwidth(stream_info, 1); |
| } |
| } |
| return rc; |
| } |
| |
| static int msm_isp_init_stream_ping_pong_reg( |
| struct msm_vfe_axi_stream *stream_info) |
| { |
| int rc = 0; |
| |
| /* Set address for both PING & PO NG register */ |
| rc = msm_isp_cfg_ping_pong_address( |
| stream_info, VFE_PING_FLAG, NULL); |
| /* No buffer available on start is not error */ |
| if (rc == -ENOMEM && stream_info->stream_type != BURST_STREAM) |
| return 0; |
| if (rc < 0) { |
| pr_err("%s: No free buffer for ping\n", |
| __func__); |
| return rc; |
| } |
| if (stream_info->stream_type != BURST_STREAM || |
| stream_info->runtime_num_burst_capture > 1) { |
| rc = msm_isp_cfg_ping_pong_address( |
| stream_info, VFE_PONG_FLAG, NULL); |
| /* No buffer available on start is not error */ |
| if (rc == -ENOMEM) |
| return 0; |
| } |
| |
| if (rc < 0) { |
| pr_err("%s: No free buffer for pong\n", |
| __func__); |
| return rc; |
| } |
| |
| return rc; |
| } |
| |
| static void msm_isp_get_stream_wm_mask( |
| struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream *stream_info, |
| uint32_t *wm_reload_mask) |
| { |
| int i; |
| int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); |
| |
| for (i = 0; i < stream_info->num_planes; i++) |
| *wm_reload_mask |= (1 << stream_info->wm[vfe_idx][i]); |
| } |
| |
| int msm_isp_axi_halt(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_halt_cmd *halt_cmd) |
| { |
| int rc = 0; |
| |
| if (atomic_read(&vfe_dev->error_info.overflow_state) == |
| OVERFLOW_DETECTED) |
| pr_err("%s: VFE%d Bus overflow detected: start recovery!\n", |
| __func__, vfe_dev->pdev->id); |
| |
| /* take care of pending items in tasklet before halt */ |
| msm_isp_flush_tasklet(vfe_dev); |
| |
| if (halt_cmd->stop_camif) { |
| vfe_dev->hw_info->vfe_ops.core_ops. |
| update_camif_state(vfe_dev, |
| DISABLE_CAMIF_IMMEDIATELY); |
| } |
| rc |= vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, |
| halt_cmd->blocking_halt); |
| |
| return rc; |
| } |
| |
| int msm_isp_axi_reset(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_reset_cmd *reset_cmd) |
| { |
| int rc = 0, i, k; |
| struct msm_vfe_axi_stream *stream_info; |
| struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; |
| uint32_t bufq_handle = 0, bufq_id = 0; |
| struct msm_isp_timestamp timestamp; |
| struct msm_vfe_frame_request_queue *queue_req; |
| unsigned long flags; |
| int vfe_idx; |
| |
| if (!reset_cmd) { |
| pr_err("%s: NULL pointer reset cmd %pK\n", __func__, reset_cmd); |
| rc = -1; |
| return rc; |
| } |
| |
| msm_isp_get_timestamp(×tamp, vfe_dev); |
| |
| for (i = 0; i < VFE_AXI_SRC_MAX; i++) { |
| stream_info = msm_isp_get_stream_common_data( |
| vfe_dev, i); |
| if (stream_info->stream_src >= VFE_AXI_SRC_MAX) { |
| rc = -1; |
| pr_err("%s invalid stream src = %d\n", |
| __func__, |
| stream_info->stream_src); |
| break; |
| } |
| if (stream_info->state == AVAILABLE || |
| stream_info->state == INACTIVE) |
| continue; |
| |
| /* handle dual stream on ISP_VFE1 turn */ |
| if (stream_info->num_isp > 1 && |
| vfe_dev->pdev->id == ISP_VFE0) |
| continue; |
| |
| /* set ping pong to scratch before flush */ |
| spin_lock_irqsave(&stream_info->lock, flags); |
| msm_isp_cfg_stream_scratch(stream_info, |
| VFE_PING_FLAG); |
| msm_isp_cfg_stream_scratch(stream_info, |
| VFE_PONG_FLAG); |
| stream_info->undelivered_request_cnt = 0; |
| spin_unlock_irqrestore(&stream_info->lock, |
| flags); |
| while (!list_empty(&stream_info->request_q)) { |
| queue_req = list_first_entry_or_null( |
| &stream_info->request_q, |
| struct msm_vfe_frame_request_queue, list); |
| if (queue_req) { |
| queue_req->cmd_used = 0; |
| list_del(&queue_req->list); |
| } |
| } |
| for (bufq_id = 0; bufq_id < VFE_BUF_QUEUE_MAX; |
| bufq_id++) { |
| bufq_handle = stream_info->bufq_handle[bufq_id]; |
| if (!bufq_handle) |
| continue; |
| rc = vfe_dev->buf_mgr->ops->flush_buf( |
| vfe_dev->buf_mgr, |
| bufq_handle, MSM_ISP_BUFFER_FLUSH_ALL, |
| ×tamp.buf_time, |
| reset_cmd->frame_id); |
| if (rc == -EFAULT) { |
| msm_isp_halt_send_error(vfe_dev, |
| ISP_EVENT_BUF_FATAL_ERROR); |
| return rc; |
| } |
| } |
| |
| for (k = 0; k < stream_info->num_isp; k++) { |
| struct vfe_device *temp_vfe_dev = |
| stream_info->vfe_dev[k]; |
| vfe_idx = msm_isp_get_vfe_idx_for_stream( |
| temp_vfe_dev, stream_info); |
| if (stream_info->num_planes > 1) { |
| temp_vfe_dev->hw_info->vfe_ops.axi_ops. |
| cfg_comp_mask(temp_vfe_dev, |
| stream_info); |
| } else { |
| temp_vfe_dev->hw_info->vfe_ops.axi_ops. |
| cfg_wm_irq_mask(temp_vfe_dev, |
| stream_info); |
| } |
| axi_data = &temp_vfe_dev->axi_data; |
| axi_data->src_info[SRC_TO_INTF(stream_info-> |
| stream_src)].frame_id = |
| reset_cmd->frame_id; |
| } |
| msm_isp_reset_burst_count_and_frame_drop( |
| vfe_dev, stream_info); |
| } |
| |
| vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, |
| 0, reset_cmd->blocking); |
| /* |
| * call reset a second time for vfe48, calling |
| * only once causes bus error on camif enable |
| */ |
| if (msm_vfe_is_vfe48(vfe_dev)) |
| vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, |
| 0, reset_cmd->blocking); |
| |
| if (rc < 0) |
| pr_err("%s Error! reset hw Timed out\n", __func__); |
| |
| return 0; |
| } |
| |
| int msm_isp_axi_restart(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_restart_cmd *restart_cmd) |
| { |
| int rc = 0, i, k, j; |
| struct msm_vfe_axi_stream *stream_info; |
| uint32_t wm_reload_mask[MAX_VFE] = {0, 0}; |
| unsigned long flags; |
| int vfe_idx; |
| |
| vfe_dev->buf_mgr->frameId_mismatch_recovery = 0; |
| for (i = 0; i < VFE_AXI_SRC_MAX; i++) { |
| stream_info = msm_isp_get_stream_common_data( |
| vfe_dev, i); |
| if (stream_info->state == AVAILABLE || |
| stream_info->state == INACTIVE) |
| continue; |
| /* handle dual stream on ISP_VFE1 turn */ |
| if (stream_info->num_isp > 1 && |
| vfe_dev->pdev->id == ISP_VFE0) |
| continue; |
| spin_lock_irqsave(&stream_info->lock, flags); |
| for (j = 0; j < MSM_ISP_COMP_IRQ_MAX; j++) |
| stream_info->composite_irq[j] = 0; |
| for (k = 0; k < stream_info->num_isp; k++) { |
| struct vfe_device *temp_vfe_dev = |
| stream_info->vfe_dev[k]; |
| vfe_idx = msm_isp_get_vfe_idx_for_stream( |
| temp_vfe_dev, stream_info); |
| for (j = 0; j < stream_info->num_planes; j++) |
| temp_vfe_dev->hw_info->vfe_ops.axi_ops. |
| enable_wm( |
| temp_vfe_dev->vfe_base, |
| stream_info->wm[vfe_idx][j], 1); |
| msm_isp_get_stream_wm_mask(temp_vfe_dev, stream_info, |
| &wm_reload_mask[temp_vfe_dev->pdev->id]); |
| } |
| msm_isp_init_stream_ping_pong_reg(stream_info); |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| } |
| |
| for (k = 0; k < MAX_VFE; k++) { |
| struct vfe_device *temp_vfe_dev = |
| vfe_dev->common_data->dual_vfe_res->vfe_dev[k]; |
| if (wm_reload_mask[k]) |
| temp_vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm( |
| temp_vfe_dev, |
| temp_vfe_dev->vfe_base, wm_reload_mask[k]); |
| } |
| |
| vfe_dev->hw_info->vfe_ops.axi_ops.restart(vfe_dev, 0, |
| restart_cmd->enable_camif); |
| |
| return rc; |
| } |
| |
| static int msm_isp_axi_update_cgc_override(struct vfe_device *vfe_dev_ioctl, |
| struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd, |
| uint8_t cgc_override) |
| { |
| int i = 0, j = 0; |
| struct msm_vfe_axi_stream *stream_info; |
| int k; |
| struct vfe_device *vfe_dev; |
| int vfe_idx; |
| |
| if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM) |
| return -EINVAL; |
| |
| for (i = 0; i < stream_cfg_cmd->num_streams; i++) { |
| if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >= |
| VFE_AXI_SRC_MAX) { |
| return -EINVAL; |
| } |
| stream_info = msm_isp_get_stream_common_data(vfe_dev_ioctl, |
| HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])); |
| if (!stream_info) { |
| pr_err("%s: stream_info is NULL", __func__); |
| return -EINVAL; |
| } |
| for (j = 0; j < stream_info->num_planes; j++) { |
| for (k = 0; k < stream_info->num_isp; k++) { |
| vfe_dev = stream_info->vfe_dev[k]; |
| if (!vfe_dev->hw_info->vfe_ops.axi_ops. |
| update_cgc_override) |
| continue; |
| vfe_idx = msm_isp_get_vfe_idx_for_stream( |
| vfe_dev, stream_info); |
| vfe_dev->hw_info->vfe_ops.axi_ops. |
| update_cgc_override(vfe_dev, |
| stream_info->wm[vfe_idx][j], |
| cgc_override); |
| } |
| } |
| } |
| return 0; |
| } |
| |
| /** |
| * msm_isp_axi_wait_for_stream_cfg_done() - Wait for a stream completion |
| * @stream_info: The stream to wait on |
| * @active: Reset means wait for stream to be INACTIVE else wait for ACTIVE |
| * |
| * Returns - 0 on success else error code |
| */ |
| static int msm_isp_axi_wait_for_stream_cfg_done( |
| struct msm_vfe_axi_stream *stream_info, int active) |
| { |
| int rc = -1; |
| unsigned long flags; |
| |
| /* No need to wait if stream is already in required state */ |
| spin_lock_irqsave(&stream_info->lock, flags); |
| if (active && ACTIVE == stream_info->state) |
| rc = 0; |
| if (!active && INACTIVE == stream_info->state) |
| rc = 0; |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| if (rc == 0) |
| return rc; |
| |
| rc = wait_for_completion_timeout( |
| active ? &stream_info->active_comp : |
| &stream_info->inactive_comp, |
| msecs_to_jiffies(VFE_MAX_CFG_TIMEOUT)); |
| |
| if (rc <= 0) { |
| rc = rc ? rc : -ETIMEDOUT; |
| pr_err("%s: wait for stream %x/%x state %d config failed %d\n", |
| __func__, |
| stream_info->stream_id, |
| stream_info->stream_src, |
| stream_info->state, |
| rc); |
| rc = -EINVAL; |
| } else { |
| rc = 0; |
| } |
| return rc; |
| } |
| |
| /** |
| * msm_isp_axi_wait_for_streams() - Wait for completion of a number of streams |
| * @streams: The streams to wait on |
| * @num_stream: Number of streams to wait on |
| * @active: Reset means wait for stream to be INACTIVE else wait for ACTIVE |
| * |
| * Returns - 0 on success else error code |
| */ |
| static int msm_isp_axi_wait_for_streams(struct msm_vfe_axi_stream **streams, |
| int num_stream, int active) |
| { |
| int i; |
| int rc = 0; |
| struct msm_vfe_axi_stream *stream_info; |
| |
| for (i = 0; i < num_stream; i++) { |
| stream_info = streams[i]; |
| rc |= msm_isp_axi_wait_for_stream_cfg_done(stream_info, active); |
| } |
| return rc; |
| } |
| |
| static int __msm_isp_check_stream_state(struct msm_vfe_axi_stream *stream_info, |
| int cmd) |
| { |
| switch (stream_info->state) { |
| case AVAILABLE: |
| return -EINVAL; |
| case PAUSING: |
| case RESUMING: |
| case RESUME_PENDING: |
| case ACTIVE: |
| case PAUSED: |
| if (cmd != 0) |
| return -EALREADY; |
| break; |
| case INACTIVE: |
| if (cmd == 0) |
| return -EALREADY; |
| break; |
| /* |
| * stream cannot be in following states since we always |
| * wait in ioctl for stream to be active or inactive |
| */ |
| case UPDATING: |
| case START_PENDING: |
| case STARTING: |
| case STOPPING: |
| case STOP_PENDING: |
| case PAUSE_PENDING: |
| default: |
| WARN(1, "Invalid state %d\n", stream_info->state); |
| } |
| return 0; |
| } |
| |
| |
| static void __msm_isp_stop_axi_streams(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream **streams, int num_streams, int cmd_type) |
| { |
| int i; |
| struct msm_vfe_axi_shared_data *axi_data; |
| struct msm_isp_timestamp timestamp; |
| uint32_t bufq_id = 0, bufq_handle = 0; |
| struct msm_vfe_axi_stream *stream_info; |
| unsigned long flags; |
| uint32_t intf; |
| int rc; |
| struct vfe_device *update_vfes[MAX_VFE] = {NULL, NULL}; |
| int k; |
| |
| msm_isp_get_timestamp(×tamp, vfe_dev); |
| |
| for (i = 0; i < num_streams; i++) { |
| stream_info = streams[i]; |
| msm_isp_update_intf_stream_cnt(stream_info, 0); |
| for (k = 0; k < stream_info->num_isp; k++) { |
| vfe_dev = stream_info->vfe_dev[k]; |
| update_vfes[vfe_dev->pdev->id] = vfe_dev; |
| } |
| } |
| for (k = 0; k < MAX_VFE; k++) { |
| if (!update_vfes[k]) |
| continue; |
| msm_isp_input_disable(update_vfes[k], cmd_type); |
| } |
| |
| for (i = 0; i < num_streams; i++) { |
| stream_info = streams[i]; |
| spin_lock_irqsave(&stream_info->lock, flags); |
| /* |
| * since we can get here from start axi stream error path due |
| * to which the stream may be intermittent state like |
| * STARTING/START_PENDING, force the stream to move out of |
| * intermittent state so it can be made INACTIVE. The |
| * intermittent states update variables so better to go through |
| * those state transitions instead of directly forcing stream to |
| * be INACTIVE |
| */ |
| memset(&stream_info->sw_skip, 0, |
| sizeof(struct msm_isp_sw_framskip)); |
| intf = SRC_TO_INTF(stream_info->stream_src); |
| if (stream_info->lpm_mode == 0 && |
| stream_info->state != PAUSED) { |
| while (stream_info->state != ACTIVE) |
| __msm_isp_axi_stream_update(stream_info, |
| ×tamp); |
| } |
| msm_isp_cfg_stream_scratch(stream_info, VFE_PING_FLAG); |
| msm_isp_cfg_stream_scratch(stream_info, VFE_PONG_FLAG); |
| stream_info->undelivered_request_cnt = 0; |
| for (k = 0; k < stream_info->num_isp; k++) { |
| vfe_dev = stream_info->vfe_dev[k]; |
| if (stream_info->num_planes > 1) |
| vfe_dev->hw_info->vfe_ops.axi_ops. |
| clear_comp_mask(vfe_dev, stream_info); |
| else |
| vfe_dev->hw_info->vfe_ops.axi_ops. |
| clear_wm_irq_mask(vfe_dev, stream_info); |
| } |
| init_completion(&stream_info->inactive_comp); |
| stream_info->state = STOP_PENDING; |
| if (stream_info->lpm_mode || |
| stream_info->state == PAUSED) { |
| /* don't wait for reg update */ |
| while (stream_info->state != INACTIVE) |
| __msm_isp_axi_stream_update(stream_info, |
| ×tamp); |
| } |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| } |
| |
| for (k = 0; k < MAX_VFE; k++) { |
| if (!update_vfes[k]) |
| continue; |
| vfe_dev = update_vfes[k]; |
| /* make sure all stats are stopped if camif is stopped */ |
| if (vfe_dev->axi_data.src_info[VFE_PIX_0].active == 0) |
| msm_isp_stop_all_stats_stream(vfe_dev); |
| } |
| |
| for (i = 0; i < num_streams; i++) { |
| stream_info = streams[i]; |
| spin_lock_irqsave(&stream_info->lock, flags); |
| intf = SRC_TO_INTF(stream_info->stream_src); |
| if (((stream_info->stream_type == BURST_STREAM) && |
| stream_info->runtime_num_burst_capture == 0) || |
| (stream_info->vfe_dev[0]->axi_data.src_info[intf]. |
| active == 0)) { |
| while (stream_info->state != INACTIVE) |
| __msm_isp_axi_stream_update( |
| stream_info, ×tamp); |
| } |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| } |
| |
| rc = msm_isp_axi_wait_for_streams(streams, num_streams, 0); |
| if (rc) { |
| pr_err("%s: wait for stream comp failed, retry...\n", __func__); |
| for (i = 0; i < num_streams; i++) { |
| stream_info = streams[i]; |
| if (stream_info->state == INACTIVE) |
| continue; |
| spin_lock_irqsave(&stream_info->lock, flags); |
| __msm_isp_axi_stream_update(stream_info, |
| ×tamp); |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| } |
| rc = msm_isp_axi_wait_for_streams(streams, num_streams, 0); |
| if (rc) { |
| pr_err("%s: wait for stream comp failed, force streams to inactive\n", |
| __func__); |
| for (i = 0; i < num_streams; i++) { |
| stream_info = streams[i]; |
| if (stream_info->state == INACTIVE) |
| continue; |
| spin_lock_irqsave(&stream_info->lock, flags); |
| while (stream_info->state != INACTIVE) |
| __msm_isp_axi_stream_update( |
| stream_info, ×tamp); |
| spin_unlock_irqrestore(&stream_info->lock, |
| flags); |
| } |
| } |
| } |
| /* clear buffers that are dequeued */ |
| for (i = 0; i < num_streams; i++) { |
| stream_info = streams[i]; |
| if (stream_info->lpm_mode == 0) |
| msm_isp_update_stream_bandwidth(stream_info, 0); |
| for (bufq_id = 0; bufq_id < VFE_BUF_QUEUE_MAX; bufq_id++) { |
| bufq_handle = stream_info->bufq_handle[bufq_id]; |
| if (!bufq_handle) |
| continue; |
| vfe_dev = stream_info->vfe_dev[0]; |
| rc = vfe_dev->buf_mgr->ops->flush_buf( |
| vfe_dev->buf_mgr, |
| bufq_handle, MSM_ISP_BUFFER_FLUSH_ALL, |
| ×tamp.buf_time, 0); |
| if (rc == -EFAULT) |
| msm_isp_halt_send_error(vfe_dev, |
| ISP_EVENT_BUF_FATAL_ERROR); |
| } |
| } |
| |
| for (i = 0; i < num_streams; i++) { |
| stream_info = streams[i]; |
| intf = SRC_TO_INTF(stream_info->stream_src); |
| for (k = 0; k < stream_info->num_isp; k++) { |
| vfe_dev = stream_info->vfe_dev[k]; |
| axi_data = &vfe_dev->axi_data; |
| if (axi_data->src_info[intf].stream_count == 0) |
| vfe_dev->reg_update_requested &= |
| ~(BIT(intf)); |
| } |
| } |
| } |
| |
| static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl, |
| struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd) |
| { |
| int i, rc = 0; |
| uint8_t src_state; |
| uint32_t wm_reload_mask[MAX_VFE] = {0, 0}; |
| struct msm_vfe_axi_stream *stream_info; |
| uint32_t src_mask = 0; |
| unsigned long flags; |
| struct msm_vfe_axi_stream *streams[MAX_NUM_STREAM]; |
| int num_streams = 0; |
| struct msm_isp_timestamp timestamp; |
| struct vfe_device *update_vfes[MAX_VFE] = {NULL, NULL}; |
| int k; |
| struct vfe_device *vfe_dev; |
| struct msm_vfe_axi_shared_data *axi_data = &vfe_dev_ioctl->axi_data; |
| uint32_t intf; |
| |
| if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM) |
| return -EINVAL; |
| |
| msm_isp_get_timestamp(×tamp, vfe_dev_ioctl); |
| mutex_lock(&vfe_dev_ioctl->buf_mgr->lock); |
| for (i = 0; i < stream_cfg_cmd->num_streams; i++) { |
| if (stream_cfg_cmd->stream_handle[i] == 0) |
| continue; |
| stream_info = msm_isp_get_stream_common_data(vfe_dev_ioctl, |
| HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])); |
| if (!stream_info) { |
| pr_err("%s: stream_info is NULL", __func__); |
| mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); |
| return -EINVAL; |
| } |
| if (SRC_TO_INTF(stream_info->stream_src) < VFE_SRC_MAX) |
| src_state = axi_data->src_info[ |
| SRC_TO_INTF(stream_info->stream_src)].active; |
| |
| else { |
| ISP_DBG("%s: invalid src info index\n", __func__); |
| rc = -EINVAL; |
| mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); |
| goto error; |
| } |
| spin_lock_irqsave(&stream_info->lock, flags); |
| rc = __msm_isp_check_stream_state(stream_info, 1); |
| if (-EALREADY == rc) { |
| rc = 0; |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| continue; |
| } |
| if (rc) { |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); |
| goto error; |
| } |
| |
| msm_isp_calculate_bandwidth(stream_info); |
| for (k = 0; k < stream_info->num_isp; k++) { |
| msm_isp_get_stream_wm_mask(stream_info->vfe_dev[k], |
| stream_info, &wm_reload_mask[ |
| stream_info->vfe_dev[k]->pdev->id]); |
| src_state = stream_info->vfe_dev[k]->axi_data.src_info[ |
| SRC_TO_INTF(stream_info->stream_src)].active; |
| if (update_vfes[stream_info->vfe_dev[k]->pdev->id]) |
| continue; |
| update_vfes[stream_info->vfe_dev[k]->pdev->id] = |
| stream_info->vfe_dev[k]; |
| } |
| msm_isp_reset_framedrop(vfe_dev_ioctl, stream_info); |
| rc = msm_isp_init_stream_ping_pong_reg(stream_info); |
| if (rc < 0) { |
| pr_err("%s: No buffer for stream%d\n", __func__, |
| HANDLE_TO_IDX( |
| stream_cfg_cmd->stream_handle[i])); |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); |
| goto error; |
| } |
| for (k = 0; k < stream_info->num_isp; k++) { |
| vfe_dev = stream_info->vfe_dev[k]; |
| if (stream_info->num_planes > 1) { |
| vfe_dev->hw_info->vfe_ops.axi_ops. |
| cfg_comp_mask(vfe_dev, stream_info); |
| } else { |
| vfe_dev->hw_info->vfe_ops.axi_ops. |
| cfg_wm_irq_mask(vfe_dev, stream_info); |
| } |
| } |
| intf = SRC_TO_INTF(stream_info->stream_src); |
| stream_info->lpm_mode = vfe_dev_ioctl-> |
| axi_data.src_info[intf].lpm; |
| if (stream_info->lpm_mode == 0) { |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| msm_isp_update_stream_bandwidth(stream_info, 1); |
| spin_lock_irqsave(&stream_info->lock, flags); |
| } |
| init_completion(&stream_info->active_comp); |
| stream_info->state = START_PENDING; |
| msm_isp_update_intf_stream_cnt(stream_info, 1); |
| |
| ISP_DBG("%s, Stream 0x%x src_state %d on vfe %d\n", __func__, |
| stream_info->stream_src, src_state, |
| vfe_dev_ioctl->pdev->id); |
| if (src_state) { |
| src_mask |= (1 << SRC_TO_INTF(stream_info->stream_src)); |
| if (stream_info->lpm_mode) { |
| while (stream_info->state != ACTIVE) |
| __msm_isp_axi_stream_update( |
| stream_info, ×tamp); |
| } |
| } else { |
| for (k = 0; k < stream_info->num_isp; k++) { |
| vfe_dev = stream_info->vfe_dev[k]; |
| |
| if (vfe_dev->dump_reg) |
| msm_camera_io_dump(vfe_dev->vfe_base, |
| 0x1000, 1); |
| } |
| |
| /* Configure AXI start bits to start immediately */ |
| while (stream_info->state != ACTIVE) |
| __msm_isp_axi_stream_update( |
| stream_info, ×tamp); |
| |
| for (k = 0; k < stream_info->num_isp; k++) { |
| vfe_dev = stream_info->vfe_dev[k]; |
| vfe_dev->hw_info->vfe_ops.core_ops.reg_update( |
| vfe_dev, |
| SRC_TO_INTF(stream_info->stream_src)); |
| } |
| } |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| streams[num_streams++] = stream_info; |
| } |
| mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); |
| |
| for (i = 0; i < MAX_VFE; i++) { |
| vfe_dev = update_vfes[i]; |
| if (!vfe_dev) |
| continue; |
| vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(vfe_dev, |
| vfe_dev->vfe_base, wm_reload_mask[i]); |
| |
| msm_isp_input_enable(vfe_dev, |
| stream_cfg_cmd->sync_frame_id_src); |
| } |
| |
| rc = msm_isp_axi_wait_for_streams(streams, num_streams, 1); |
| if (rc < 0) { |
| pr_err("%s: wait for config done failed\n", __func__); |
| goto error; |
| } |
| |
| return 0; |
| error: |
| __msm_isp_stop_axi_streams(vfe_dev_ioctl, streams, num_streams, |
| STOP_STREAM); |
| |
| return rc; |
| } |
| |
| static int msm_isp_stop_axi_stream(struct vfe_device *vfe_dev_ioctl, |
| struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd) |
| { |
| int i, rc = 0; |
| struct msm_vfe_axi_stream *stream_info = NULL; |
| struct msm_vfe_axi_stream *streams[MAX_NUM_STREAM]; |
| int num_streams = 0; |
| unsigned long flags; |
| |
| if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM || |
| stream_cfg_cmd->num_streams == 0) |
| return -EINVAL; |
| |
| for (i = 0; i < stream_cfg_cmd->num_streams; i++) { |
| if (stream_cfg_cmd->stream_handle[i] == 0) |
| continue; |
| stream_info = msm_isp_get_stream_common_data(vfe_dev_ioctl, |
| HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])); |
| if (!stream_info) { |
| pr_err("%s: stream_info is NULL", __func__); |
| return -EINVAL; |
| } |
| spin_lock_irqsave(&stream_info->lock, flags); |
| rc = __msm_isp_check_stream_state(stream_info, 0); |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| if (rc) { |
| /* |
| * continue stopping other streams as error here means |
| * stream is already not active |
| */ |
| rc = 0; |
| continue; |
| } |
| streams[num_streams++] = stream_info; |
| } |
| __msm_isp_stop_axi_streams(vfe_dev_ioctl, streams, num_streams, |
| stream_cfg_cmd->cmd); |
| |
| return rc; |
| } |
| |
| int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg) |
| { |
| int rc = 0; |
| struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd = arg; |
| uint32_t stream_idx[MAX_NUM_STREAM]; |
| int i; |
| int vfe_idx; |
| struct msm_vfe_axi_stream *stream_info; |
| |
| memset(stream_idx, 0, sizeof(stream_idx)); |
| |
| for (i = 0; i < stream_cfg_cmd->num_streams; i++) { |
| if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >= |
| VFE_AXI_SRC_MAX) |
| return -EINVAL; |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, |
| HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])); |
| if (!stream_info) { |
| pr_err("%s: stream_info is NULL", __func__); |
| return -EINVAL; |
| } |
| vfe_idx = msm_isp_get_vfe_idx_for_stream_user(vfe_dev, |
| stream_info); |
| if (vfe_idx == -ENOTTY || stream_info->stream_handle[vfe_idx] != |
| stream_cfg_cmd->stream_handle[i]) { |
| pr_err("%s: Invalid stream handle %x vfe_idx %d expected %x\n", |
| __func__, stream_cfg_cmd->stream_handle[i], |
| vfe_idx, |
| (vfe_idx != -ENOTTY) ? |
| stream_info->stream_handle[vfe_idx] : 0); |
| return -EINVAL; |
| } |
| /* check for duplicate stream handle */ |
| if (stream_idx[stream_info->stream_src] == |
| stream_cfg_cmd->stream_handle[i]) |
| stream_cfg_cmd->stream_handle[i] = 0; |
| else |
| stream_idx[stream_info->stream_src] = |
| stream_cfg_cmd->stream_handle[i]; |
| } |
| if (stream_cfg_cmd->cmd == START_STREAM) { |
| msm_isp_axi_update_cgc_override(vfe_dev, stream_cfg_cmd, 1); |
| |
| rc = msm_isp_start_axi_stream( |
| vfe_dev, stream_cfg_cmd); |
| } else { |
| rc = msm_isp_stop_axi_stream( |
| vfe_dev, stream_cfg_cmd); |
| |
| msm_isp_axi_update_cgc_override(vfe_dev, stream_cfg_cmd, 0); |
| |
| /* |
| * Use different ret value to not overwrite the error from |
| * msm_isp_stop_axi_stream |
| */ |
| if (vfe_dev->axi_data.num_active_stream == 0) |
| vfe_dev->hvx_cmd = HVX_DISABLE; |
| if (vfe_dev->is_split) { |
| struct vfe_device *vfe_temp = |
| vfe_dev->common_data-> |
| dual_vfe_res->vfe_dev[ISP_VFE0]; |
| if (vfe_temp->axi_data.num_active_stream == 0) |
| vfe_temp->hvx_cmd = HVX_DISABLE; |
| } |
| } |
| |
| if (rc < 0) |
| pr_err("%s: start/stop %d stream failed\n", __func__, |
| stream_cfg_cmd->cmd); |
| return rc; |
| } |
| |
| static int msm_isp_return_empty_buffer(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream *stream_info, uint32_t user_stream_id, |
| uint32_t frame_id, uint32_t buf_index, |
| enum msm_vfe_input_src frame_src) |
| { |
| int rc = -1; |
| struct msm_isp_buffer *buf = NULL; |
| uint32_t bufq_handle = 0; |
| uint32_t stream_idx; |
| struct msm_isp_event_data error_event; |
| struct msm_isp_timestamp timestamp; |
| |
| if (!vfe_dev || !stream_info) { |
| pr_err("%s %d failed: vfe_dev %pK stream_info %pK\n", __func__, |
| __LINE__, vfe_dev, stream_info); |
| return -EINVAL; |
| } |
| |
| stream_idx = stream_info->stream_src; |
| if (!stream_info->controllable_output) |
| return -EINVAL; |
| |
| if (frame_src >= VFE_SRC_MAX) { |
| pr_err("%s: Invalid frame_src %d", __func__, frame_src); |
| return -EINVAL; |
| } |
| |
| if (stream_idx >= VFE_AXI_SRC_MAX) { |
| pr_err("%s: Invalid stream_idx", __func__); |
| return rc; |
| } |
| |
| if (user_stream_id == stream_info->stream_id) |
| bufq_handle = stream_info->bufq_handle[VFE_BUF_QUEUE_DEFAULT]; |
| else |
| bufq_handle = stream_info->bufq_handle[VFE_BUF_QUEUE_SHARED]; |
| |
| |
| rc = vfe_dev->buf_mgr->ops->get_buf(vfe_dev->buf_mgr, |
| vfe_dev->pdev->id, bufq_handle, buf_index, &buf); |
| if (rc == -EFAULT) { |
| msm_isp_halt_send_error(vfe_dev, ISP_EVENT_BUF_FATAL_ERROR); |
| return rc; |
| } |
| |
| if (rc < 0 || buf == NULL) { |
| pr_err("Skip framedrop report due to no buffer\n"); |
| return rc; |
| } |
| |
| msm_isp_get_timestamp(×tamp, vfe_dev); |
| buf->buf_debug.put_state[buf->buf_debug.put_state_last] = |
| MSM_ISP_BUFFER_STATE_DROP_REG; |
| buf->buf_debug.put_state_last ^= 1; |
| rc = vfe_dev->buf_mgr->ops->buf_done(vfe_dev->buf_mgr, |
| buf->bufq_handle, buf->buf_idx, |
| ×tamp.buf_time, frame_id, |
| stream_info->runtime_output_format); |
| if (rc == -EFAULT) { |
| msm_isp_halt_send_error(vfe_dev, |
| ISP_EVENT_BUF_FATAL_ERROR); |
| return rc; |
| } |
| |
| memset(&error_event, 0, sizeof(error_event)); |
| error_event.frame_id = frame_id; |
| error_event.u.error_info.err_type = ISP_ERROR_RETURN_EMPTY_BUFFER; |
| error_event.u.error_info.session_id = stream_info->session_id; |
| error_event.u.error_info.stream_id_mask = |
| 1 << (bufq_handle & 0xFF); |
| msm_isp_send_event(vfe_dev, ISP_EVENT_ERROR, &error_event); |
| |
| return 0; |
| } |
| |
| static int msm_isp_request_frame(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream *stream_info, uint32_t user_stream_id, |
| uint32_t frame_id, uint32_t buf_index) |
| { |
| struct msm_vfe_axi_stream_request_cmd stream_cfg_cmd; |
| struct msm_vfe_frame_request_queue *queue_req; |
| uint32_t pingpong_status; |
| unsigned long flags; |
| int rc = 0; |
| enum msm_vfe_input_src frame_src = 0; |
| int k; |
| uint32_t wm_mask = 0; |
| int vfe_idx; |
| uint32_t pingpong_bit = 0; |
| |
| if (!vfe_dev || !stream_info) { |
| pr_err("%s %d failed: vfe_dev %pK stream_info %pK\n", __func__, |
| __LINE__, vfe_dev, stream_info); |
| return -EINVAL; |
| } |
| |
| /* return early for dual vfe0 */ |
| if (stream_info->num_isp > 1 && vfe_dev->pdev->id == ISP_VFE0) |
| return 0; |
| |
| if (stream_info->stream_src >= VFE_AXI_SRC_MAX) { |
| pr_err("%s:%d invalid stream src %d\n", __func__, __LINE__, |
| stream_info->stream_src); |
| return -EINVAL; |
| } |
| |
| frame_src = SRC_TO_INTF(stream_info->stream_src); |
| pingpong_status = vfe_dev->hw_info-> |
| vfe_ops.axi_ops.get_pingpong_status(vfe_dev); |
| /* |
| * If PIX stream is active then RDI path uses SOF frame ID of PIX |
| * In case of standalone RDI streaming, SOF are used from |
| * individual intf. |
| */ |
| /* |
| * If frame_id = 1 then no eof check is needed |
| */ |
| if (vfe_dev->axi_data.src_info[frame_src].active && |
| frame_src == VFE_PIX_0 && |
| vfe_dev->axi_data.src_info[frame_src].accept_frame == false) { |
| pr_debug("%s:%d invalid time to request frame %d\n", |
| __func__, __LINE__, frame_id); |
| goto error; |
| } |
| if ((vfe_dev->axi_data.src_info[frame_src].active && (frame_id != |
| vfe_dev->axi_data.src_info[frame_src].frame_id + vfe_dev-> |
| axi_data.src_info[frame_src].sof_counter_step)) || |
| ((!vfe_dev->axi_data.src_info[frame_src].active))) { |
| pr_debug("%s:%d invalid frame id %d cur frame id %d pix %d\n", |
| __func__, __LINE__, frame_id, |
| vfe_dev->axi_data.src_info[frame_src].frame_id, |
| vfe_dev->axi_data.src_info[frame_src].active); |
| goto error; |
| } |
| if (stream_info->undelivered_request_cnt >= MAX_BUFFERS_IN_HW) { |
| pr_debug("%s:%d invalid undelivered_request_cnt %d frame id %d\n", |
| __func__, __LINE__, |
| stream_info->undelivered_request_cnt, |
| vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id); |
| goto error; |
| } |
| if ((frame_src == VFE_PIX_0) && !stream_info->undelivered_request_cnt && |
| MSM_VFE_STREAM_STOP_PERIOD != |
| stream_info->activated_framedrop_period) { |
| pr_debug("%s:%d vfe %d frame_id %d prev_pattern %x stream_id %x\n", |
| __func__, __LINE__, vfe_dev->pdev->id, frame_id, |
| stream_info->activated_framedrop_period, |
| stream_info->stream_id); |
| |
| rc = msm_isp_return_empty_buffer(vfe_dev, stream_info, |
| user_stream_id, frame_id, buf_index, frame_src); |
| if (rc < 0) |
| pr_err("%s:%d failed: return_empty_buffer src %d\n", |
| __func__, __LINE__, frame_src); |
| stream_info->current_framedrop_period = |
| MSM_VFE_STREAM_STOP_PERIOD; |
| msm_isp_cfg_framedrop_reg(stream_info); |
| return 0; |
| } |
| |
| spin_lock_irqsave(&stream_info->lock, flags); |
| vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); |
| /* When wm reloaded, pingpong status register would be stale, pingpong |
| * status would be updated only after AXI_DONE interrupt processed. |
| * So, we should avoid reading value from pingpong status register |
| * until buf_done happens for ping buffer. |
| */ |
| if ((stream_info->undelivered_request_cnt == 1) && |
| (stream_info->sw_ping_pong_bit != -1)) { |
| pingpong_status = |
| vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status( |
| vfe_dev); |
| pingpong_bit = ((pingpong_status >> |
| stream_info->wm[vfe_idx][0]) & 0x1); |
| if (stream_info->sw_ping_pong_bit == !pingpong_bit) { |
| ISP_DBG("%s:Return Empty Buffer stream id 0x%X\n", |
| __func__, stream_info->stream_id); |
| rc = msm_isp_return_empty_buffer(vfe_dev, stream_info, |
| user_stream_id, frame_id, buf_index, |
| frame_src); |
| spin_unlock_irqrestore(&stream_info->lock, |
| flags); |
| return 0; |
| } |
| } |
| |
| queue_req = &stream_info->request_queue_cmd[stream_info->request_q_idx]; |
| if (queue_req->cmd_used) { |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| pr_err_ratelimited("%s: Request queue overflow.\n", __func__); |
| return -EINVAL; |
| } |
| |
| if (user_stream_id == stream_info->stream_id) |
| queue_req->buff_queue_id = VFE_BUF_QUEUE_DEFAULT; |
| else |
| queue_req->buff_queue_id = VFE_BUF_QUEUE_SHARED; |
| |
| if (!stream_info->bufq_handle[queue_req->buff_queue_id]) { |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| pr_err("%s:%d request frame failed on hw stream 0x%x, request stream %d due to no bufq idx: %d\n", |
| __func__, __LINE__, |
| stream_info->stream_handle[0], |
| user_stream_id, queue_req->buff_queue_id); |
| return 0; |
| } |
| queue_req->buf_index = buf_index; |
| queue_req->cmd_used = 1; |
| |
| stream_info->request_q_idx = |
| (stream_info->request_q_idx + 1) % MSM_VFE_REQUESTQ_SIZE; |
| list_add_tail(&queue_req->list, &stream_info->request_q); |
| stream_info->request_q_cnt++; |
| |
| stream_info->undelivered_request_cnt++; |
| stream_cfg_cmd.axi_stream_handle = stream_info->stream_handle[vfe_idx]; |
| stream_cfg_cmd.frame_skip_pattern = NO_SKIP; |
| stream_cfg_cmd.init_frame_drop = 0; |
| stream_cfg_cmd.burst_count = stream_info->request_q_cnt; |
| |
| if (stream_info->undelivered_request_cnt == 1) { |
| rc = msm_isp_cfg_ping_pong_address(stream_info, |
| VFE_PING_FLAG, NULL); |
| if (rc) { |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| stream_info->undelivered_request_cnt--; |
| pr_err_ratelimited("%s:%d fail to cfg HAL buffer\n", |
| __func__, __LINE__); |
| return rc; |
| } |
| |
| for (k = 0; k < stream_info->num_isp; k++) { |
| wm_mask = 0; |
| msm_isp_get_stream_wm_mask(stream_info->vfe_dev[k], |
| stream_info, &wm_mask); |
| stream_info->vfe_dev[k]-> |
| hw_info->vfe_ops.axi_ops.reload_wm( |
| stream_info->vfe_dev[k], |
| stream_info->vfe_dev[k]->vfe_base, wm_mask); |
| |
| } |
| /* sw_ping_pong_bit is updated only when AXI_DONE. |
| * so now reset this bit to -1. |
| */ |
| stream_info->sw_ping_pong_bit = -1; |
| } else if (stream_info->undelivered_request_cnt == 2) { |
| if (stream_info->sw_ping_pong_bit == -1) { |
| /* This means wm is reloaded & ping buffer is |
| * already configured. And AXI_DONE for ping |
| * is still pending. So, config pong buffer |
| * now. |
| */ |
| rc = msm_isp_cfg_ping_pong_address(stream_info, |
| VFE_PONG_FLAG, NULL); |
| } else { |
| rc = msm_isp_cfg_ping_pong_address( |
| stream_info, pingpong_status, NULL); |
| } |
| if (rc) { |
| stream_info->undelivered_request_cnt--; |
| spin_unlock_irqrestore(&stream_info->lock, |
| flags); |
| pr_err_ratelimited("%s:%d fail to cfg HAL buffer\n", |
| __func__, __LINE__); |
| return rc; |
| } |
| } else { |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| stream_info->undelivered_request_cnt--; |
| pr_err_ratelimited("%s: Invalid undeliver frame count %d\n", |
| __func__, stream_info->undelivered_request_cnt); |
| return -EINVAL; |
| } |
| |
| rc = msm_isp_calculate_framedrop(vfe_dev, &stream_cfg_cmd); |
| if (rc == 0) |
| msm_isp_reset_framedrop(vfe_dev, stream_info); |
| |
| /*Avoid Multiple request frames for single SOF*/ |
| vfe_dev->axi_data.src_info[frame_src].accept_frame = false; |
| |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| |
| return rc; |
| error: |
| rc = msm_isp_return_empty_buffer(vfe_dev, stream_info, |
| user_stream_id, frame_id, buf_index, frame_src); |
| if (rc < 0) |
| pr_err("%s:%d failed: return_empty_buffer src %d\n", |
| __func__, __LINE__, frame_src); |
| return 0; |
| |
| } |
| |
| static int msm_isp_add_buf_queue(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream *stream_info, uint32_t stream_id) |
| { |
| int rc = 0; |
| uint32_t bufq_id = 0; |
| unsigned long flags; |
| |
| if (stream_id == stream_info->stream_id) |
| bufq_id = VFE_BUF_QUEUE_DEFAULT; |
| else |
| bufq_id = VFE_BUF_QUEUE_SHARED; |
| |
| spin_lock_irqsave(&stream_info->lock, flags); |
| |
| if (stream_info->bufq_handle[bufq_id] == 0) { |
| stream_info->bufq_handle[bufq_id] = |
| vfe_dev->buf_mgr->ops->get_bufq_handle(vfe_dev->buf_mgr, |
| stream_info->session_id, stream_id); |
| if (stream_info->bufq_handle[bufq_id] == 0) { |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| pr_err("%s: failed: No valid buffer queue for stream: 0x%x\n", |
| __func__, stream_id); |
| return -EINVAL; |
| } |
| } else { |
| uint32_t bufq_handle = vfe_dev->buf_mgr->ops->get_bufq_handle( |
| vfe_dev->buf_mgr, |
| stream_info->session_id, |
| stream_id); |
| if (bufq_handle != stream_info->bufq_handle[bufq_id]) { |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| pr_err("%s: Stream %x already has buffer q %x cannot add handle %x\n", |
| __func__, stream_id, |
| stream_info->bufq_handle[bufq_id], bufq_handle); |
| return -EINVAL; |
| } |
| } |
| |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| |
| ISP_DBG("%d: Add bufq handle:0x%x, idx:%d, for stream %d on VFE %d\n", |
| __LINE__, stream_info->bufq_handle[bufq_id], |
| bufq_id, stream_info->stream_handle[0], |
| vfe_dev->pdev->id); |
| |
| return rc; |
| } |
| |
| static void msm_isp_remove_buf_queue(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream *stream_info, uint32_t stream_id) |
| { |
| uint32_t bufq_id = 0; |
| unsigned long flags; |
| |
| if (stream_id == stream_info->stream_id) |
| bufq_id = VFE_BUF_QUEUE_DEFAULT; |
| else |
| bufq_id = VFE_BUF_QUEUE_SHARED; |
| |
| spin_lock_irqsave(&stream_info->lock, flags); |
| |
| if (stream_info->bufq_handle[bufq_id]) { |
| stream_info->bufq_handle[bufq_id] = 0; |
| if (stream_info->state == ACTIVE) { |
| init_completion(&stream_info->active_comp); |
| stream_info->state = UPDATING; |
| } |
| } |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| if (stream_info->state == UPDATING) |
| msm_isp_axi_wait_for_stream_cfg_done(stream_info, 1); |
| |
| } |
| |
| /** |
| * msm_isp_stream_axi_cfg_update() - Apply axi config update to a stream |
| * @vfe_dev: The vfe device on which the update is to be applied |
| * @stream_info: Stream for which update is to be applied |
| * @update_info: Parameters of the update |
| * |
| * Returns - 0 on success else error code |
| * |
| * For dual vfe stream apply the update once update for both vfe is |
| * received. |
| */ |
| static int msm_isp_stream_axi_cfg_update(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream *stream_info, |
| struct msm_vfe_axi_stream_cfg_update_info *update_info) |
| { |
| int j; |
| int k; |
| unsigned long flags; |
| int vfe_idx; |
| |
| spin_lock_irqsave(&stream_info->lock, flags); |
| if (stream_info->state != ACTIVE) { |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| pr_err("Invalid stream state for axi update %d\n", |
| stream_info->state); |
| return -EINVAL; |
| } |
| if (stream_info->update_vfe_mask) { |
| if (stream_info->update_vfe_mask & (1 << vfe_dev->pdev->id)) { |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| pr_err("%s: Stream %pK/%x Update already in progress for vfe %d\n", |
| __func__, stream_info, stream_info->stream_src, |
| vfe_dev->pdev->id); |
| return -EINVAL; |
| } |
| } |
| vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); |
| |
| for (j = 0; j < stream_info->num_planes; j++) |
| stream_info->plane_cfg[vfe_idx][j] = update_info->plane_cfg[j]; |
| |
| stream_info->update_vfe_mask |= (1 << vfe_dev->pdev->id); |
| /* wait for update from all vfe's under stream before applying */ |
| if (stream_info->update_vfe_mask != stream_info->vfe_mask) { |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| return 0; |
| } |
| |
| atomic_set(&vfe_dev->axi_data.axi_cfg_update[ |
| SRC_TO_INTF(stream_info->stream_src)], 1); |
| stream_info->output_format = update_info->output_format; |
| init_completion(&stream_info->active_comp); |
| if (((vfe_dev->hw_info->runtime_axi_update == 0) || |
| (vfe_dev->dual_vfe_enable == 1))) { |
| stream_info->state = PAUSE_PENDING; |
| msm_isp_axi_stream_enable_cfg(stream_info); |
| stream_info->state = PAUSING; |
| } else { |
| for (j = 0; j < stream_info->num_planes; j++) { |
| for (k = 0; k < stream_info->num_isp; k++) { |
| vfe_dev = stream_info->vfe_dev[k]; |
| vfe_dev->hw_info->vfe_ops.axi_ops. |
| cfg_wm_reg(vfe_dev, stream_info, j); |
| } |
| } |
| stream_info->state = RESUMING; |
| } |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| return 0; |
| } |
| |
| int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) |
| { |
| int rc = 0, i, j, k; |
| struct msm_vfe_axi_stream *stream_info; |
| struct msm_vfe_axi_stream_update_cmd *update_cmd = arg; |
| struct msm_vfe_axi_stream_cfg_update_info *update_info = NULL; |
| struct msm_isp_sw_framskip *sw_skip_info = NULL; |
| unsigned long flags; |
| struct msm_isp_timestamp timestamp; |
| uint32_t frame_id; |
| int vfe_idx; |
| |
| /*num_stream is uint32 and update_info[] bound by MAX_NUM_STREAM*/ |
| if (update_cmd->num_streams > MAX_NUM_STREAM) |
| return -EINVAL; |
| |
| for (i = 0; i < update_cmd->num_streams; i++) { |
| update_info = (struct msm_vfe_axi_stream_cfg_update_info *) |
| &update_cmd->update_info[i]; |
| /*check array reference bounds*/ |
| if (HANDLE_TO_IDX(update_info->stream_handle) >= |
| VFE_AXI_SRC_MAX) { |
| return -EINVAL; |
| } |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, |
| HANDLE_TO_IDX(update_info->stream_handle)); |
| if (!stream_info) { |
| pr_err("%s: stream_info is null", __func__); |
| return -EINVAL; |
| } |
| if (SRC_TO_INTF(stream_info->stream_src) >= VFE_SRC_MAX) |
| continue; |
| if (stream_info->state != ACTIVE && |
| stream_info->state != INACTIVE && |
| update_cmd->update_type != |
| UPDATE_STREAM_REQUEST_FRAMES && |
| update_cmd->update_type != |
| UPDATE_STREAM_REMOVE_BUFQ && |
| update_cmd->update_type != |
| UPDATE_STREAM_SW_FRAME_DROP && |
| update_cmd->update_type != |
| UPDATE_STREAM_REQUEST_FRAMES_VER2) { |
| pr_err("%s: Invalid stream state %d, update cmd %d\n", |
| __func__, stream_info->state, |
| stream_info->stream_id); |
| return -EINVAL; |
| } |
| if (update_cmd->update_type == UPDATE_STREAM_AXI_CONFIG && |
| stream_info->state != ACTIVE) { |
| pr_err("%s: AXI stream config updating\n", __func__); |
| return -EBUSY; |
| } |
| } |
| |
| switch (update_cmd->update_type) { |
| case ENABLE_STREAM_BUF_DIVERT: |
| for (i = 0; i < update_cmd->num_streams; i++) { |
| update_info = |
| (struct msm_vfe_axi_stream_cfg_update_info *) |
| &update_cmd->update_info[i]; |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, |
| HANDLE_TO_IDX(update_info->stream_handle)); |
| if (!stream_info) { |
| pr_err("%s: stream_info is null", __func__); |
| return -EINVAL; |
| } |
| stream_info->buf_divert = 1; |
| } |
| break; |
| case DISABLE_STREAM_BUF_DIVERT: |
| for (i = 0; i < update_cmd->num_streams; i++) { |
| update_info = |
| (struct msm_vfe_axi_stream_cfg_update_info *) |
| &update_cmd->update_info[i]; |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, |
| HANDLE_TO_IDX(update_info->stream_handle)); |
| if (!stream_info) { |
| pr_err("%s: stream_info is null", __func__); |
| return -EINVAL; |
| } |
| stream_info->buf_divert = 0; |
| msm_isp_get_timestamp(×tamp, vfe_dev); |
| frame_id = vfe_dev->axi_data.src_info[ |
| SRC_TO_INTF(stream_info->stream_src)].frame_id; |
| /* set ping pong address to scratch before flush */ |
| spin_lock_irqsave(&stream_info->lock, flags); |
| msm_isp_cfg_stream_scratch(stream_info, |
| VFE_PING_FLAG); |
| msm_isp_cfg_stream_scratch(stream_info, |
| VFE_PONG_FLAG); |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| rc = vfe_dev->buf_mgr->ops->flush_buf( |
| vfe_dev->buf_mgr, |
| stream_info->bufq_handle |
| [VFE_BUF_QUEUE_DEFAULT], |
| MSM_ISP_BUFFER_FLUSH_DIVERTED, |
| ×tamp.buf_time, frame_id); |
| if (rc == -EFAULT) { |
| msm_isp_halt_send_error(vfe_dev, |
| ISP_EVENT_BUF_FATAL_ERROR); |
| return rc; |
| } |
| } |
| break; |
| case UPDATE_STREAM_FRAMEDROP_PATTERN: { |
| for (i = 0; i < update_cmd->num_streams; i++) { |
| uint32_t framedrop_period = |
| msm_isp_get_framedrop_period( |
| update_info->skip_pattern); |
| update_info = |
| (struct msm_vfe_axi_stream_cfg_update_info *) |
| &update_cmd->update_info[i]; |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, |
| HANDLE_TO_IDX(update_info->stream_handle)); |
| if (!stream_info) { |
| pr_err("%s: stream_info is null", __func__); |
| return -EINVAL; |
| } |
| spin_lock_irqsave(&stream_info->lock, flags); |
| /* no change then break early */ |
| if (stream_info->current_framedrop_period == |
| framedrop_period) { |
| spin_unlock_irqrestore(&stream_info->lock, |
| flags); |
| break; |
| } |
| if (stream_info->controllable_output) { |
| pr_err("Controllable output streams does not support custom frame skip pattern\n"); |
| spin_unlock_irqrestore(&stream_info->lock, |
| flags); |
| return -EINVAL; |
| } |
| if (update_info->skip_pattern == SKIP_ALL) |
| stream_info->current_framedrop_period = |
| MSM_VFE_STREAM_STOP_PERIOD; |
| else |
| stream_info->current_framedrop_period = |
| framedrop_period; |
| if (stream_info->stream_type != BURST_STREAM) |
| msm_isp_cfg_framedrop_reg(stream_info); |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| } |
| break; |
| } |
| case UPDATE_STREAM_SW_FRAME_DROP: { |
| for (i = 0; i < update_cmd->num_streams; i++) { |
| update_info = |
| (struct msm_vfe_axi_stream_cfg_update_info *) |
| &update_cmd->update_info[i]; |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, |
| HANDLE_TO_IDX(update_info->stream_handle)); |
| if (!stream_info) { |
| pr_err("%s: stream_info is null", __func__); |
| return -EINVAL; |
| } |
| sw_skip_info = &update_info->sw_skip_info; |
| if (sw_skip_info && |
| sw_skip_info->stream_src_mask != 0) { |
| /* SW image buffer drop */ |
| pr_debug("%x sw skip type %x mode %d min %d max %d\n", |
| stream_info->stream_id, |
| sw_skip_info->stats_type_mask, |
| sw_skip_info->skip_mode, |
| sw_skip_info->min_frame_id, |
| sw_skip_info->max_frame_id); |
| spin_lock_irqsave(&stream_info->lock, flags); |
| stream_info->sw_skip = *sw_skip_info; |
| spin_unlock_irqrestore(&stream_info->lock, |
| flags); |
| } |
| } |
| break; |
| } |
| case UPDATE_STREAM_AXI_CONFIG: { |
| for (i = 0; i < update_cmd->num_streams; i++) { |
| update_info = |
| (struct msm_vfe_axi_stream_cfg_update_info *) |
| &update_cmd->update_info[i]; |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, |
| HANDLE_TO_IDX(update_info->stream_handle)); |
| if (!stream_info) { |
| pr_err("%s: stream_info is null", __func__); |
| return -EINVAL; |
| } |
| rc = msm_isp_stream_axi_cfg_update(vfe_dev, stream_info, |
| update_info); |
| if (rc) |
| return rc; |
| } |
| break; |
| } |
| case UPDATE_STREAM_REQUEST_FRAMES: { |
| for (i = 0; i < update_cmd->num_streams; i++) { |
| update_info = |
| (struct msm_vfe_axi_stream_cfg_update_info *) |
| &update_cmd->update_info[i]; |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, |
| HANDLE_TO_IDX(update_info->stream_handle)); |
| if (!stream_info) { |
| pr_err("%s: stream_info is null", __func__); |
| return -EINVAL; |
| } |
| mutex_lock(&vfe_dev->buf_mgr->lock); |
| rc = msm_isp_request_frame(vfe_dev, stream_info, |
| update_info->user_stream_id, |
| update_info->frame_id, |
| MSM_ISP_INVALID_BUF_INDEX); |
| mutex_unlock(&vfe_dev->buf_mgr->lock); |
| if (rc) |
| pr_err("%s failed to request frame!\n", |
| __func__); |
| } |
| break; |
| } |
| case UPDATE_STREAM_ADD_BUFQ: { |
| for (i = 0; i < update_cmd->num_streams; i++) { |
| update_info = |
| (struct msm_vfe_axi_stream_cfg_update_info *) |
| &update_cmd->update_info[i]; |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, |
| HANDLE_TO_IDX(update_info->stream_handle)); |
| if (!stream_info) { |
| pr_err("%s: stream_info is null", __func__); |
| return -EINVAL; |
| } |
| rc = msm_isp_add_buf_queue(vfe_dev, stream_info, |
| update_info->user_stream_id); |
| if (rc) |
| pr_err("%s failed to add bufq!\n", __func__); |
| } |
| break; |
| } |
| case UPDATE_STREAM_REMOVE_BUFQ: { |
| for (i = 0; i < update_cmd->num_streams; i++) { |
| update_info = |
| (struct msm_vfe_axi_stream_cfg_update_info *) |
| &update_cmd->update_info[i]; |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, |
| HANDLE_TO_IDX(update_info->stream_handle)); |
| if (!stream_info) { |
| pr_err("%s: stream_info is null", __func__); |
| return -EINVAL; |
| } |
| msm_isp_remove_buf_queue(vfe_dev, stream_info, |
| update_info->user_stream_id); |
| pr_debug("%s, Remove bufq for Stream 0x%x\n", |
| __func__, stream_info->stream_id); |
| } |
| break; |
| } |
| case UPDATE_STREAM_REQUEST_FRAMES_VER2: { |
| struct msm_vfe_axi_stream_cfg_update_info_req_frm *req_frm = |
| &update_cmd->req_frm_ver2; |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, |
| HANDLE_TO_IDX(req_frm->stream_handle)); |
| if (!stream_info) { |
| pr_err("%s: stream_info is null", __func__); |
| return -EINVAL; |
| } |
| mutex_lock(&vfe_dev->buf_mgr->lock); |
| rc = msm_isp_request_frame(vfe_dev, stream_info, |
| req_frm->user_stream_id, |
| req_frm->frame_id, |
| req_frm->buf_index); |
| mutex_unlock(&vfe_dev->buf_mgr->lock); |
| if (rc) |
| pr_err("%s failed to request frame!\n", |
| __func__); |
| break; |
| } |
| case UPDATE_STREAM_OFFLINE_AXI_CONFIG: { |
| for (i = 0; i < update_cmd->num_streams; i++) { |
| update_info = |
| (struct msm_vfe_axi_stream_cfg_update_info *) |
| &update_cmd->update_info[i]; |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, |
| HANDLE_TO_IDX(update_info->stream_handle)); |
| if (!stream_info) { |
| pr_err("%s: stream_info is null", __func__); |
| return -EINVAL; |
| } |
| vfe_idx = msm_isp_get_vfe_idx_for_stream( |
| vfe_dev, stream_info); |
| for (j = 0; j < stream_info->num_planes; j++) { |
| stream_info->plane_cfg[vfe_idx][j] = |
| update_info->plane_cfg[j]; |
| for (k = 0; k < stream_info->num_isp; k++) { |
| vfe_dev = stream_info->vfe_dev[k]; |
| vfe_dev->hw_info->vfe_ops.axi_ops. |
| cfg_wm_reg(vfe_dev, |
| stream_info, j); |
| } |
| } |
| } |
| break; |
| } |
| default: |
| pr_err("%s: Invalid update type %d\n", __func__, |
| update_cmd->update_type); |
| return -EINVAL; |
| } |
| |
| return rc; |
| } |
| |
| void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev, |
| struct msm_vfe_axi_stream *stream_info, |
| uint32_t pingpong_status, |
| struct msm_isp_timestamp *ts) |
| { |
| int rc = -1; |
| uint32_t pingpong_bit = 0, i; |
| struct msm_isp_buffer *done_buf = NULL; |
| unsigned long flags; |
| struct timeval *time_stamp; |
| uint32_t frame_id, buf_index = -1; |
| int vfe_idx; |
| |
| if (!ts) { |
| pr_err("%s: Error! Invalid argument\n", __func__); |
| return; |
| } |
| |
| if (vfe_dev->vt_enable) { |
| msm_isp_get_avtimer_ts(ts); |
| time_stamp = &ts->vt_time; |
| } else { |
| time_stamp = &ts->buf_time; |
| } |
| |
| frame_id = vfe_dev->axi_data. |
| src_info[SRC_TO_INTF(stream_info->stream_src)].frame_id; |
| |
| spin_lock_irqsave(&stream_info->lock, flags); |
| vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); |
| pingpong_bit = (~(pingpong_status >> |
| stream_info->wm[vfe_idx][0]) & 0x1); |
| for (i = 0; i < stream_info->num_planes; i++) { |
| if (pingpong_bit != |
| (~(pingpong_status >> |
| stream_info->wm[vfe_idx][i]) & 0x1)) { |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| msm_isp_dump_ping_pong_mismatch(vfe_dev); |
| pr_err("%s: Write master ping pong mismatch. Status: 0x%x %x\n", |
| __func__, pingpong_status, |
| stream_info->stream_src); |
| msm_isp_halt_send_error(vfe_dev, |
| ISP_EVENT_PING_PONG_MISMATCH); |
| return; |
| } |
| } |
| if (stream_info->state == INACTIVE) { |
| WARN_ON(stream_info->buf[pingpong_bit] != NULL); |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| return; |
| } |
| |
| /* composite the irq for dual vfe */ |
| rc = msm_isp_composite_irq(vfe_dev, stream_info, |
| MSM_ISP_COMP_IRQ_PING_BUFDONE + pingpong_bit); |
| if (rc) { |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| if (rc < 0) |
| msm_isp_halt_send_error(vfe_dev, |
| ISP_EVENT_PING_PONG_MISMATCH); |
| return; |
| } |
| |
| done_buf = stream_info->buf[pingpong_bit]; |
| |
| if (vfe_dev->buf_mgr->frameId_mismatch_recovery == 1) { |
| if (done_buf) { |
| if (done_buf->is_drop_reconfig == 1) |
| done_buf->is_drop_reconfig = 0; |
| } |
| pr_err_ratelimited("%s: Mismatch Recovery in progress, drop frame!\n", |
| __func__); |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| return; |
| } |
| |
| if (done_buf) |
| buf_index = done_buf->buf_idx; |
| |
| ISP_DBG("%s: vfe %d: stream 0x%x, frame id %d, pingpong bit %d\n", |
| __func__, |
| vfe_dev->pdev->id, |
| stream_info->stream_id, |
| frame_id, |
| pingpong_bit); |
| |
| stream_info->frame_id++; |
| stream_info->buf[pingpong_bit] = NULL; |
| |
| if (stream_info->controllable_output && |
| (done_buf != NULL) && |
| (stream_info->sw_ping_pong_bit == -1) && |
| (done_buf->is_drop_reconfig == 1)) { |
| /* When wm reloaded and corresponding reg_update fail |
| * then buffer is reconfig as PING buffer. so, avoid |
| * NULL assignment to PING buffer and eventually |
| * next AXI_DONE or buf_done can be successful |
| */ |
| stream_info->buf[pingpong_bit] = done_buf; |
| } |
| |
| if (stream_info->stream_type == CONTINUOUS_STREAM || |
| stream_info->runtime_num_burst_capture > 1) { |
| rc = msm_isp_cfg_ping_pong_address( |
| stream_info, pingpong_status, NULL); |
| if (rc < 0) |
| ISP_DBG("%s: Error configuring ping_pong\n", |
| __func__); |
| } else if (done_buf && (done_buf->is_drop_reconfig != 1)) { |
| msm_isp_cfg_stream_scratch(stream_info, pingpong_status); |
| } |
| |
| if (!done_buf) { |
| if (stream_info->buf_divert) { |
| vfe_dev->error_info.stream_framedrop_count[ |
| stream_info->bufq_handle[ |
| VFE_BUF_QUEUE_DEFAULT] & 0xFF]++; |
| vfe_dev->error_info.framedrop_flag = 1; |
| } |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| return; |
| } |
| |
| if (stream_info->stream_type == BURST_STREAM && |
| stream_info->runtime_num_burst_capture) { |
| ISP_DBG("%s: burst_frame_count: %d\n", |
| __func__, |
| stream_info->runtime_num_burst_capture); |
| stream_info->runtime_num_burst_capture--; |
| } |
| |
| rc = msm_isp_update_deliver_count(vfe_dev, stream_info, |
| pingpong_bit, done_buf); |
| if (rc) { |
| if (done_buf->is_drop_reconfig == 1) |
| done_buf->is_drop_reconfig = 0; |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| pr_err_ratelimited("%s:VFE%d get done buf fail\n", |
| __func__, vfe_dev->pdev->id); |
| msm_isp_halt_send_error(vfe_dev, |
| ISP_EVENT_PING_PONG_MISMATCH); |
| return; |
| } |
| |
| |
| if ((done_buf->frame_id != frame_id) && |
| vfe_dev->axi_data.enable_frameid_recovery) { |
| if (done_buf->is_drop_reconfig == 1) |
| done_buf->is_drop_reconfig = 0; |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| msm_isp_handle_done_buf_frame_id_mismatch(vfe_dev, |
| stream_info, done_buf, time_stamp, frame_id); |
| return; |
| } |
| |
| if (done_buf->is_drop_reconfig == 1) { |
| /* When ping/pong buf is already reconfigured |
| * then dont issue buf-done for current buffer |
| */ |
| done_buf->is_drop_reconfig = 0; |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| } else { |
| spin_unlock_irqrestore(&stream_info->lock, flags); |
| msm_isp_process_done_buf(vfe_dev, stream_info, |
| done_buf, time_stamp, frame_id); |
| } |
| } |
| |
| void msm_isp_process_axi_irq(struct vfe_device *vfe_dev, |
| uint32_t irq_status0, uint32_t irq_status1, |
| uint32_t pingpong_status, struct msm_isp_timestamp *ts) |
| { |
| int i, rc = 0; |
| uint32_t comp_mask = 0, wm_mask = 0; |
| uint32_t stream_idx; |
| struct msm_vfe_axi_stream *stream_info; |
| struct msm_vfe_axi_composite_info *comp_info; |
| struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; |
| int wm; |
| |
| comp_mask = vfe_dev->hw_info->vfe_ops.axi_ops. |
| get_comp_mask(irq_status0, irq_status1); |
| wm_mask = vfe_dev->hw_info->vfe_ops.axi_ops. |
| get_wm_mask(irq_status0, irq_status1); |
| if (!(comp_mask || wm_mask)) |
| return; |
| |
| ISP_DBG("%s: status: 0x%x\n", __func__, irq_status0); |
| |
| for (i = 0; i < axi_data->hw_info->num_comp_mask; i++) { |
| rc = 0; |
| comp_info = &axi_data->composite_info[i]; |
| wm_mask &= ~(comp_info->stream_composite_mask); |
| if (comp_mask & (1 << i)) { |
| stream_idx = HANDLE_TO_IDX(comp_info->stream_handle); |
| if ((!comp_info->stream_handle) || |
| (stream_idx >= VFE_AXI_SRC_MAX)) { |
| pr_err_ratelimited("%s: Invalid handle for composite irq\n", |
| __func__); |
| for (wm = 0; wm < axi_data->hw_info->num_wm; |
| wm++) |
| if (comp_info->stream_composite_mask & |
| (1 << wm)) |
| msm_isp_cfg_wm_scratch(vfe_dev, |
| wm, pingpong_status); |
| continue; |
| } |
| stream_idx = HANDLE_TO_IDX(comp_info->stream_handle); |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, |
| stream_idx); |
| |
| msm_isp_process_axi_irq_stream(vfe_dev, stream_info, |
| pingpong_status, ts); |
| |
| } |
| } |
| |
| for (i = 0; i < axi_data->hw_info->num_wm; i++) { |
| if (wm_mask & (1 << i)) { |
| stream_idx = HANDLE_TO_IDX(axi_data->free_wm[i]); |
| if ((!axi_data->free_wm[i]) || |
| (stream_idx >= VFE_AXI_SRC_MAX)) { |
| pr_err("%s: Invalid handle for wm irq\n", |
| __func__); |
| msm_isp_cfg_wm_scratch(vfe_dev, i, |
| pingpong_status); |
| continue; |
| } |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, |
| stream_idx); |
| |
| msm_isp_process_axi_irq_stream(vfe_dev, stream_info, |
| pingpong_status, ts); |
| } |
| } |
| } |
| |
| void msm_isp_axi_disable_all_wm(struct vfe_device *vfe_dev) |
| { |
| struct msm_vfe_axi_stream *stream_info; |
| struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; |
| int i, j; |
| int vfe_idx; |
| |
| if (!vfe_dev || !axi_data) { |
| pr_err("%s: error %pK %pK\n", __func__, vfe_dev, axi_data); |
| return; |
| } |
| |
| for (i = 0; i < VFE_AXI_SRC_MAX; i++) { |
| stream_info = msm_isp_get_stream_common_data(vfe_dev, i); |
| |
| if (stream_info->state != ACTIVE) |
| continue; |
| |
| vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, |
| stream_info); |
| for (j = 0; j < stream_info->num_planes; j++) |
| vfe_dev->hw_info->vfe_ops.axi_ops.enable_wm( |
| vfe_dev->vfe_base, |
| stream_info->wm[vfe_idx][j], 0); |
| } |
| } |