aboutsummaryrefslogtreecommitdiff
path: root/camera/isp/msm_isp_stats_util.c
diff options
context:
space:
mode:
authorwzedlare <vedatak01@gmail.com>2017-06-18 16:38:26 +0000
committerwzedlare <vedatak01@gmail.com>2017-06-19 16:57:11 +0000
commitc7d4e3fd588e3ba3d3fa4d5cfa224aa54bc288bf (patch)
treeb8b64cb9deb6832c1e41f58f0f143514beafc709 /camera/isp/msm_isp_stats_util.c
parent28c99c87b881bb664c44bb26e80a681f87d54e60 (diff)
p2a42: Import fully working kernel sourceHEADn7.1
Change-Id: Ia4c94f09e29843b1af34d466243378a357e97b70
Diffstat (limited to 'camera/isp/msm_isp_stats_util.c')
-rw-r--r--camera/isp/msm_isp_stats_util.c948
1 files changed, 948 insertions, 0 deletions
diff --git a/camera/isp/msm_isp_stats_util.c b/camera/isp/msm_isp_stats_util.c
new file mode 100644
index 00000000..0e5bd8a0
--- /dev/null
+++ b/camera/isp/msm_isp_stats_util.c
@@ -0,0 +1,948 @@
+/* Copyright (c) 2013-2016, 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 <linux/atomic.h>
+#include <media/v4l2-subdev.h>
+#include <lenovo_media/msmb_isp.h>
+#include "msm_isp_util.h"
+#include "msm_isp_axi_util.h"
+#include "msm_isp_stats_util.h"
+
+static inline void msm_isp_stats_cfg_wm_scratch(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info,
+ uint32_t pingpong_status)
+{
+ vfe_dev->hw_info->vfe_ops.stats_ops.update_ping_pong_addr(
+ vfe_dev->vfe_base, stream_info,
+ pingpong_status, vfe_dev->buf_mgr->scratch_buf_addr);
+}
+
+static inline void msm_isp_stats_cfg_stream_scratch(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info,
+ uint32_t pingpong_status)
+{
+ uint32_t stats_idx = STATS_IDX(stream_info->stream_handle);
+ uint32_t pingpong_bit;
+ uint32_t stats_pingpong_offset =
+ vfe_dev->hw_info->stats_hw_info->stats_ping_pong_offset[
+ stats_idx];
+
+ pingpong_bit = (~(pingpong_status >> stats_pingpong_offset) & 0x1);
+
+ msm_isp_stats_cfg_wm_scratch(vfe_dev, stream_info,
+ pingpong_status);
+ stream_info->buf[pingpong_bit] = NULL;
+}
+
+static int msm_isp_stats_cfg_ping_pong_address(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info, uint32_t pingpong_status)
+{
+ int rc = -1, vfe_id = 0;
+ struct msm_isp_buffer *buf;
+ uint32_t pingpong_bit = 0;
+ uint32_t stats_pingpong_offset;
+ uint32_t bufq_handle = stream_info->bufq_handle;
+ uint32_t stats_idx = STATS_IDX(stream_info->stream_handle);
+ struct dual_vfe_resource *dual_vfe_res = NULL;
+ struct msm_vfe_stats_stream *dual_vfe_stream_info = NULL;
+
+ if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type ||
+ stats_idx >= MSM_ISP_STATS_MAX) {
+ pr_err("%s Invalid stats index %d", __func__, stats_idx);
+ return -EINVAL;
+ }
+
+ stats_pingpong_offset =
+ vfe_dev->hw_info->stats_hw_info->stats_ping_pong_offset[
+ stats_idx];
+
+ pingpong_bit = (~(pingpong_status >> stats_pingpong_offset) & 0x1);
+
+ rc = vfe_dev->buf_mgr->ops->get_buf(vfe_dev->buf_mgr,
+ vfe_dev->pdev->id, bufq_handle, &buf);
+ if (rc == -EFAULT) {
+ msm_isp_halt_send_error(vfe_dev, ISP_EVENT_BUF_FATAL_ERROR);
+ return rc;
+ }
+ if (rc < 0 || NULL == buf)
+ vfe_dev->error_info.stats_framedrop_count[stats_idx]++;
+
+ if (buf && buf->num_planes != 1) {
+ pr_err("%s: Invalid buffer\n", __func__);
+ msm_isp_halt_send_error(vfe_dev, ISP_EVENT_BUF_FATAL_ERROR);
+ rc = -EINVAL;
+ goto buf_error;
+ }
+ if (vfe_dev->is_split) {
+ dual_vfe_res = vfe_dev->common_data->dual_vfe_res;
+ if (!dual_vfe_res->vfe_base[ISP_VFE0] ||
+ !dual_vfe_res->stats_data[ISP_VFE0] ||
+ !dual_vfe_res->vfe_base[ISP_VFE1] ||
+ !dual_vfe_res->stats_data[ISP_VFE1]) {
+ pr_err("%s:%d error vfe0 %p %p vfe1 %p %p\n", __func__,
+ __LINE__, dual_vfe_res->vfe_base[ISP_VFE0],
+ dual_vfe_res->stats_data[ISP_VFE0],
+ dual_vfe_res->vfe_base[ISP_VFE1],
+ dual_vfe_res->stats_data[ISP_VFE1]);
+ } else {
+ for (vfe_id = 0; vfe_id < MAX_VFE; vfe_id++) {
+ dual_vfe_stream_info = &dual_vfe_res->
+ stats_data[vfe_id]->
+ stream_info[stats_idx];
+ if (buf)
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ update_ping_pong_addr(
+ dual_vfe_res->vfe_base[vfe_id],
+ dual_vfe_stream_info,
+ pingpong_status,
+ buf->mapped_info[0].paddr +
+ dual_vfe_stream_info->
+ buffer_offset);
+ else
+ msm_isp_stats_cfg_stream_scratch(
+ vfe_dev,
+ dual_vfe_stream_info,
+ pingpong_status);
+
+ dual_vfe_stream_info->buf[pingpong_bit]
+ = buf;
+ }
+ }
+ } else {
+ if (buf)
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ update_ping_pong_addr(
+ vfe_dev->vfe_base, stream_info,
+ pingpong_status, buf->mapped_info[0].paddr +
+ stream_info->buffer_offset);
+ else
+ msm_isp_stats_cfg_stream_scratch(vfe_dev,
+ stream_info, pingpong_status);
+
+ stream_info->buf[pingpong_bit] = buf;
+ }
+
+ if (buf)
+ buf->pingpong_bit = pingpong_bit;
+ return 0;
+buf_error:
+ vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx);
+ return rc;
+}
+
+static int32_t msm_isp_stats_buf_divert(struct vfe_device *vfe_dev,
+ struct msm_isp_timestamp *ts,
+ struct msm_isp_event_data *buf_event,
+ struct msm_vfe_stats_stream *stream_info,
+ uint32_t *comp_stats_type_mask, uint32_t pingpong_status)
+{
+ int32_t rc = 0, frame_id = 0, drop_buffer = 0;
+ struct msm_isp_stats_event *stats_event = NULL;
+ struct msm_isp_sw_framskip *sw_skip = NULL;
+ int32_t buf_index = -1;
+ uint32_t pingpong_bit;
+ struct msm_isp_buffer *done_buf;
+ uint32_t stats_pingpong_offset;
+ uint32_t stats_idx;
+
+ if (!vfe_dev || !ts || !buf_event || !stream_info) {
+ pr_err("%s:%d failed: invalid params %p %p %p %p\n",
+ __func__, __LINE__, vfe_dev, ts, buf_event,
+ stream_info);
+ return -EINVAL;
+ }
+ frame_id = vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+ sw_skip = &stream_info->sw_skip;
+ stats_event = &buf_event->u.stats;
+
+ if (sw_skip->stats_type_mask &
+ (1 << stream_info->stats_type)) {
+ /* Hw stream output of this src is requested
+ for drop */
+ if (sw_skip->skip_mode == SKIP_ALL) {
+ /* drop all buffers */
+ drop_buffer = 1;
+ } else if (sw_skip->skip_mode == SKIP_RANGE &&
+ (sw_skip->min_frame_id <= frame_id &&
+ sw_skip->max_frame_id >= frame_id)) {
+ drop_buffer = 1;
+ } else if (frame_id > sw_skip->max_frame_id) {
+ memset(sw_skip, 0, sizeof
+ (struct msm_isp_sw_framskip));
+ }
+ }
+ stats_idx = STATS_IDX(stream_info->stream_handle);
+
+ stats_pingpong_offset =
+ vfe_dev->hw_info->stats_hw_info->stats_ping_pong_offset[
+ stats_idx];
+ pingpong_bit = (~(pingpong_status >> stats_pingpong_offset) & 0x1);
+
+ done_buf = stream_info->buf[pingpong_bit];
+
+ if (done_buf)
+ buf_index = done_buf->buf_idx;
+
+ rc = vfe_dev->buf_mgr->ops->update_put_buf_cnt(
+ vfe_dev->buf_mgr, vfe_dev->pdev->id, stream_info->bufq_handle,
+ buf_index, &ts->buf_time,
+ frame_id, pingpong_bit);
+
+ if (rc < 0) {
+ if (rc == -EFAULT)
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_PING_PONG_MISMATCH);
+ pr_err("stats_buf_divert: update put buf cnt fail\n");
+ return rc;
+ }
+
+ if (rc > 0) {
+ ISP_DBG("%s: vfe_id %d buf_id %d bufq %x put_cnt 1\n", __func__,
+ vfe_dev->pdev->id, buf_index,
+ stream_info->bufq_handle);
+ return rc;
+ }
+
+ /* Program next buffer */
+ rc = msm_isp_stats_cfg_ping_pong_address(vfe_dev, stream_info,
+ pingpong_status);
+ if (rc)
+ return rc;
+
+ if (drop_buffer && done_buf) {
+ rc = vfe_dev->buf_mgr->ops->buf_done(
+ vfe_dev->buf_mgr,
+ done_buf->bufq_handle,
+ done_buf->buf_idx, &ts->buf_time, frame_id, 0);
+ if (rc == -EFAULT)
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_BUF_FATAL_ERROR);
+ return rc;
+ }
+
+ if (done_buf) {
+ stats_event->stats_buf_idxs
+ [stream_info->stats_type] =
+ done_buf->buf_idx;
+ if (NULL == comp_stats_type_mask) {
+ stats_event->stats_mask =
+ 1 << stream_info->stats_type;
+ ISP_DBG("%s: stats frameid: 0x%x %d bufq %x\n",
+ __func__, buf_event->frame_id,
+ stream_info->stats_type, done_buf->bufq_handle);
+ msm_isp_send_event(vfe_dev,
+ ISP_EVENT_STATS_NOTIFY +
+ stream_info->stats_type,
+ buf_event);
+ } else {
+ *comp_stats_type_mask |=
+ 1 << stream_info->stats_type;
+ }
+ }
+
+ return rc;
+}
+
+static int32_t msm_isp_stats_configure(struct vfe_device *vfe_dev,
+ uint32_t stats_irq_mask, struct msm_isp_timestamp *ts,
+ uint32_t pingpong_status, bool is_composite)
+{
+ int i, rc = 0;
+ struct msm_isp_event_data buf_event;
+ struct msm_isp_stats_event *stats_event = &buf_event.u.stats;
+ struct msm_vfe_stats_stream *stream_info = NULL;
+ uint32_t comp_stats_type_mask = 0;
+ int result = 0;
+
+ memset(&buf_event, 0, sizeof(struct msm_isp_event_data));
+ buf_event.timestamp = ts->buf_time;
+ buf_event.frame_id = vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+
+ for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; i++) {
+ if (!(stats_irq_mask & (1 << i)))
+ continue;
+ stream_info = &vfe_dev->stats_data.stream_info[i];
+ if (stream_info->state == STATS_INACTIVE) {
+ pr_debug("%s: Warning! Stream already inactive. Drop irq handling\n",
+ __func__);
+ continue;
+ }
+
+ rc = msm_isp_stats_buf_divert(vfe_dev, ts,
+ &buf_event, stream_info,
+ is_composite ? &comp_stats_type_mask : NULL,
+ pingpong_status);
+ if (rc < 0) {
+ pr_err("%s:%d failed: stats buf divert rc %d\n",
+ __func__, __LINE__, rc);
+ result = rc;
+ }
+ }
+ if (is_composite && comp_stats_type_mask) {
+ ISP_DBG("%s:vfe_id %d comp_stats frameid %x,comp_mask %x\n",
+ __func__, vfe_dev->pdev->id, buf_event.frame_id,
+ comp_stats_type_mask);
+ stats_event->stats_mask = comp_stats_type_mask;
+ msm_isp_send_event(vfe_dev,
+ ISP_EVENT_COMP_STATS_NOTIFY, &buf_event);
+ comp_stats_type_mask = 0;
+ }
+ return result;
+}
+
+void msm_isp_process_stats_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ uint32_t pingpong_status, struct msm_isp_timestamp *ts)
+{
+ int j, rc;
+ uint32_t atomic_stats_mask = 0;
+ uint32_t stats_comp_mask = 0, stats_irq_mask = 0;
+ bool comp_flag = false;
+ uint32_t num_stats_comp_mask =
+ vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask;
+
+ stats_comp_mask = vfe_dev->hw_info->vfe_ops.stats_ops.
+ get_comp_mask(irq_status0, irq_status1);
+ stats_irq_mask = vfe_dev->hw_info->vfe_ops.stats_ops.
+ get_wm_mask(irq_status0, irq_status1);
+ if (!(stats_comp_mask || stats_irq_mask))
+ return;
+
+ ISP_DBG("%s: vfe %d status: 0x%x\n", __func__, vfe_dev->pdev->id,
+ irq_status0);
+
+ /* Clear composite mask irq bits, they will be restored by comp mask */
+ for (j = 0; j < num_stats_comp_mask; j++) {
+ stats_irq_mask &= ~atomic_read(
+ &vfe_dev->stats_data.stats_comp_mask[j]);
+ }
+
+ /* Process non-composite irq */
+ if (stats_irq_mask) {
+ rc = msm_isp_stats_configure(vfe_dev, stats_irq_mask, ts,
+ pingpong_status, comp_flag);
+ }
+
+ /* Process composite irq */
+ if (stats_comp_mask) {
+ for (j = 0; j < num_stats_comp_mask; j++) {
+ if (!(stats_comp_mask & (1 << j)))
+ continue;
+
+ atomic_stats_mask = atomic_read(
+ &vfe_dev->stats_data.stats_comp_mask[j]);
+
+ rc = msm_isp_stats_configure(vfe_dev, atomic_stats_mask,
+ ts, pingpong_status, !comp_flag);
+ }
+ }
+}
+
+int msm_isp_stats_create_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_request_cmd *stream_req_cmd)
+{
+ int rc = -1;
+ struct msm_vfe_stats_stream *stream_info = NULL;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ uint32_t stats_idx;
+
+ if (!(vfe_dev->hw_info->stats_hw_info->stats_capability_mask &
+ (1 << stream_req_cmd->stats_type))) {
+ pr_err("%s: Stats type not supported\n", __func__);
+ return rc;
+ }
+
+ stats_idx = vfe_dev->hw_info->vfe_ops.stats_ops.
+ get_stats_idx(stream_req_cmd->stats_type);
+
+ if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s Invalid stats index %d", __func__, stats_idx);
+ return -EINVAL;
+ }
+
+ stream_info = &stats_data->stream_info[stats_idx];
+ if (stream_info->state != STATS_AVALIABLE) {
+ pr_err("%s: Stats already requested\n", __func__);
+ return rc;
+ }
+
+ if (stream_req_cmd->framedrop_pattern >= MAX_SKIP) {
+ pr_err("%s: Invalid framedrop pattern\n", __func__);
+ return rc;
+ }
+
+ if (stream_req_cmd->irq_subsample_pattern >= MAX_SKIP) {
+ pr_err("%s: Invalid irq subsample pattern\n", __func__);
+ return rc;
+ }
+
+ stream_info->session_id = stream_req_cmd->session_id;
+ stream_info->stream_id = stream_req_cmd->stream_id;
+ stream_info->composite_flag = stream_req_cmd->composite_flag;
+ stream_info->stats_type = stream_req_cmd->stats_type;
+ stream_info->buffer_offset = stream_req_cmd->buffer_offset;
+ stream_info->framedrop_pattern = stream_req_cmd->framedrop_pattern;
+ stream_info->init_stats_frame_drop = stream_req_cmd->init_frame_drop;
+ stream_info->irq_subsample_pattern =
+ stream_req_cmd->irq_subsample_pattern;
+ stream_info->state = STATS_INACTIVE;
+
+ if ((vfe_dev->stats_data.stream_handle_cnt << 8) == 0)
+ vfe_dev->stats_data.stream_handle_cnt++;
+
+ stream_req_cmd->stream_handle =
+ (++vfe_dev->stats_data.stream_handle_cnt) << 8 | stats_idx;
+
+ stream_info->stream_handle = stream_req_cmd->stream_handle;
+ return 0;
+}
+
+int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = -1;
+ struct msm_vfe_stats_stream_request_cmd *stream_req_cmd = arg;
+ struct msm_vfe_stats_stream *stream_info = NULL;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ uint32_t framedrop_period;
+ uint32_t stats_idx;
+
+ rc = msm_isp_stats_create_stream(vfe_dev, stream_req_cmd);
+ if (rc < 0) {
+ pr_err("%s: create stream failed\n", __func__);
+ return rc;
+ }
+
+ stats_idx = STATS_IDX(stream_req_cmd->stream_handle);
+
+ if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s Invalid stats index %d", __func__, stats_idx);
+ return -EINVAL;
+ }
+
+ stream_info = &stats_data->stream_info[stats_idx];
+
+ framedrop_period = msm_isp_get_framedrop_period(
+ stream_req_cmd->framedrop_pattern);
+
+ if (stream_req_cmd->framedrop_pattern == SKIP_ALL)
+ stream_info->framedrop_pattern = 0x0;
+ else
+ stream_info->framedrop_pattern = 0x1;
+ stream_info->framedrop_period = framedrop_period - 1;
+
+ if (!stream_info->composite_flag)
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ cfg_wm_irq_mask(vfe_dev, stream_info);
+
+ if (stream_info->init_stats_frame_drop == 0)
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_reg(vfe_dev,
+ stream_info);
+
+ msm_isp_stats_cfg_stream_scratch(vfe_dev, stream_info,
+ VFE_PING_FLAG);
+ msm_isp_stats_cfg_stream_scratch(vfe_dev, stream_info,
+ VFE_PONG_FLAG);
+ return rc;
+}
+
+int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = -1;
+ struct msm_vfe_stats_stream_cfg_cmd stream_cfg_cmd;
+ struct msm_vfe_stats_stream_release_cmd *stream_release_cmd = arg;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ int stats_idx = STATS_IDX(stream_release_cmd->stream_handle);
+ struct msm_vfe_stats_stream *stream_info = NULL;
+
+ if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s Invalid stats index %d", __func__, stats_idx);
+ return -EINVAL;
+ }
+
+ stream_info = &stats_data->stream_info[stats_idx];
+ if (stream_info->state == STATS_AVALIABLE) {
+ pr_err("%s: stream already release\n", __func__);
+ return rc;
+ } else if (stream_info->state != STATS_INACTIVE) {
+ stream_cfg_cmd.enable = 0;
+ stream_cfg_cmd.num_streams = 1;
+ stream_cfg_cmd.stream_handle[0] =
+ stream_release_cmd->stream_handle;
+ rc = msm_isp_cfg_stats_stream(vfe_dev, &stream_cfg_cmd);
+ }
+
+ if (!stream_info->composite_flag)
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ clear_wm_irq_mask(vfe_dev, stream_info);
+
+ vfe_dev->hw_info->vfe_ops.stats_ops.clear_wm_reg(vfe_dev, stream_info);
+ memset(stream_info, 0, sizeof(struct msm_vfe_stats_stream));
+ return 0;
+}
+
+static int msm_isp_init_stats_ping_pong_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int rc = 0;
+ stream_info->bufq_handle =
+ vfe_dev->buf_mgr->ops->get_bufq_handle(
+ vfe_dev->buf_mgr, stream_info->session_id,
+ stream_info->stream_id);
+ if (stream_info->bufq_handle == 0) {
+ pr_err("%s: no buf configured for stream: 0x%x\n",
+ __func__, stream_info->stream_handle);
+ return -EINVAL;
+ }
+
+ if ((vfe_dev->is_split && vfe_dev->pdev->id == 1) ||
+ !vfe_dev->is_split) {
+ rc = msm_isp_stats_cfg_ping_pong_address(vfe_dev,
+ stream_info, VFE_PING_FLAG);
+ if (rc < 0) {
+ pr_err("%s: No free buffer for ping\n", __func__);
+ return rc;
+ }
+ rc = msm_isp_stats_cfg_ping_pong_address(vfe_dev,
+ stream_info, VFE_PONG_FLAG);
+ if (rc < 0) {
+ pr_err("%s: No free buffer for pong\n", __func__);
+ return rc;
+ }
+ }
+ return rc;
+}
+
+void msm_isp_update_stats_framedrop_reg(struct vfe_device *vfe_dev)
+{
+ int i;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ struct msm_vfe_stats_stream *stream_info = NULL;
+
+ for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; i++) {
+ stream_info = &stats_data->stream_info[i];
+ if (stream_info->state != STATS_ACTIVE)
+ continue;
+
+ if (stream_info->init_stats_frame_drop) {
+ stream_info->init_stats_frame_drop--;
+ if (stream_info->init_stats_frame_drop == 0) {
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_reg(
+ vfe_dev, stream_info);
+ }
+ }
+ }
+}
+
+void msm_isp_stats_stream_update(struct vfe_device *vfe_dev)
+{
+ int i;
+ uint32_t enable = 0;
+ uint8_t comp_flag = 0;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ struct msm_vfe_stats_ops *stats_ops =
+ &vfe_dev->hw_info->vfe_ops.stats_ops;
+
+ for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; i++) {
+ if (stats_data->stream_info[i].state == STATS_START_PENDING ||
+ stats_data->stream_info[i].state ==
+ STATS_STOP_PENDING) {
+ enable = stats_data->stream_info[i].state ==
+ STATS_START_PENDING ? 1 : 0;
+ stats_data->stream_info[i].state =
+ stats_data->stream_info[i].state ==
+ STATS_START_PENDING ?
+ STATS_STARTING : STATS_STOPPING;
+ vfe_dev->hw_info->vfe_ops.stats_ops.enable_module(
+ vfe_dev, BIT(i), enable);
+ comp_flag = stats_data->stream_info[i].composite_flag;
+ if (comp_flag)
+ stats_ops->cfg_comp_mask(vfe_dev, BIT(i),
+ (comp_flag - 1), enable);
+ } else if (stats_data->stream_info[i].state == STATS_STARTING ||
+ stats_data->stream_info[i].state == STATS_STOPPING) {
+ stats_data->stream_info[i].state =
+ stats_data->stream_info[i].state ==
+ STATS_STARTING ? STATS_ACTIVE : STATS_INACTIVE;
+ }
+ }
+ atomic_sub(1, &stats_data->stats_update);
+ if (!atomic_read(&stats_data->stats_update))
+ complete(&vfe_dev->stats_config_complete);
+}
+
+static int msm_isp_stats_wait_for_cfg_done(struct vfe_device *vfe_dev)
+{
+ int rc;
+ init_completion(&vfe_dev->stats_config_complete);
+ atomic_set(&vfe_dev->stats_data.stats_update, 2);
+ rc = wait_for_completion_timeout(
+ &vfe_dev->stats_config_complete,
+ msecs_to_jiffies(VFE_MAX_CFG_TIMEOUT));
+ if (rc == 0) {
+ pr_err("%s: wait timeout\n", __func__);
+ rc = -1;
+ } else {
+ rc = 0;
+ }
+ return rc;
+}
+
+static int msm_isp_stats_update_cgc_override(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i;
+ uint32_t stats_mask = 0, idx;
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
+
+ if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s Invalid stats index %d", __func__, idx);
+ return -EINVAL;
+ }
+ stats_mask |= 1 << idx;
+ }
+
+ if (vfe_dev->hw_info->vfe_ops.stats_ops.update_cgc_override) {
+ vfe_dev->hw_info->vfe_ops.stats_ops.update_cgc_override(
+ vfe_dev, stats_mask, stream_cfg_cmd->enable);
+ }
+ return 0;
+}
+
+int msm_isp_stats_reset(struct vfe_device *vfe_dev)
+{
+ int i = 0, rc = 0;
+ struct msm_vfe_stats_stream *stream_info = NULL;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ struct msm_isp_timestamp timestamp;
+
+ msm_isp_get_timestamp(&timestamp);
+
+ for (i = 0; i < MSM_ISP_STATS_MAX; i++) {
+ stream_info = &stats_data->stream_info[i];
+ if (stream_info->state != STATS_ACTIVE)
+ continue;
+
+ rc = vfe_dev->buf_mgr->ops->flush_buf(vfe_dev->buf_mgr,
+ vfe_dev->pdev->id, stream_info->bufq_handle,
+ MSM_ISP_BUFFER_FLUSH_ALL, &timestamp.buf_time,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
+ if (rc == -EFAULT) {
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_BUF_FATAL_ERROR);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+int msm_isp_stats_restart(struct vfe_device *vfe_dev)
+{
+ int i = 0;
+ struct msm_vfe_stats_stream *stream_info = NULL;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+
+ for (i = 0; i < MSM_ISP_STATS_MAX; i++) {
+ stream_info = &stats_data->stream_info[i];
+ if (stream_info->state < STATS_ACTIVE)
+ continue;
+ msm_isp_init_stats_ping_pong_reg(vfe_dev, stream_info);
+ }
+
+ return 0;
+}
+
+static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i, rc = 0;
+ uint32_t stats_mask = 0, idx;
+ uint32_t comp_stats_mask[MAX_NUM_STATS_COMP_MASK] = {0};
+ uint32_t num_stats_comp_mask = 0;
+ struct msm_vfe_stats_stream *stream_info;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ num_stats_comp_mask =
+ vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask;
+ rc = vfe_dev->hw_info->vfe_ops.stats_ops.check_streams(
+ stats_data->stream_info);
+ if (rc < 0)
+ return rc;
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
+
+ if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s Invalid stats index %d", __func__, idx);
+ return -EINVAL;
+ }
+
+ stream_info = &stats_data->stream_info[idx];
+ if (stream_info->stream_handle !=
+ stream_cfg_cmd->stream_handle[i]) {
+ pr_err("%s: Invalid stream handle: 0x%x received\n",
+ __func__, stream_cfg_cmd->stream_handle[i]);
+ continue;
+ }
+
+ if (stream_info->composite_flag > num_stats_comp_mask) {
+ pr_err("%s: comp grp %d exceed max %d\n",
+ __func__, stream_info->composite_flag,
+ num_stats_comp_mask);
+ return -EINVAL;
+ }
+ rc = msm_isp_init_stats_ping_pong_reg(vfe_dev, stream_info);
+ if (rc < 0) {
+ pr_err("%s: No buffer for stream%d\n", __func__, idx);
+ return rc;
+ }
+
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].active)
+ stream_info->state = STATS_START_PENDING;
+ else
+ stream_info->state = STATS_ACTIVE;
+
+ stats_data->num_active_stream++;
+ stats_mask |= 1 << idx;
+
+ if (stream_info->composite_flag > 0)
+ comp_stats_mask[stream_info->composite_flag-1] |=
+ 1 << idx;
+
+ ISP_DBG("%s: stats_mask %x %x active streams %d\n",
+ __func__, comp_stats_mask[0],
+ comp_stats_mask[1],
+ stats_data->num_active_stream);
+
+ }
+
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) {
+ rc = msm_isp_stats_wait_for_cfg_done(vfe_dev);
+ } else {
+ vfe_dev->hw_info->vfe_ops.stats_ops.enable_module(
+ vfe_dev, stats_mask, stream_cfg_cmd->enable);
+ for (i = 0; i < num_stats_comp_mask; i++) {
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_comp_mask(
+ vfe_dev, comp_stats_mask[i], i, 1);
+ }
+ }
+ return rc;
+}
+
+static int msm_isp_stop_stats_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i, rc = 0;
+ uint32_t stats_mask = 0, idx;
+ uint32_t comp_stats_mask[MAX_NUM_STATS_COMP_MASK] = {0};
+ uint32_t num_stats_comp_mask = 0;
+ struct msm_vfe_stats_stream *stream_info;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ struct msm_isp_timestamp timestamp;
+
+ msm_isp_get_timestamp(&timestamp);
+
+ num_stats_comp_mask =
+ vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask;
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+
+ idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
+
+ if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s Invalid stats index %d", __func__, idx);
+ return -EINVAL;
+ }
+
+ stream_info = &stats_data->stream_info[idx];
+ if (stream_info->stream_handle !=
+ stream_cfg_cmd->stream_handle[i]) {
+ pr_err("%s: Invalid stream handle: 0x%x received\n",
+ __func__, stream_cfg_cmd->stream_handle[i]);
+ continue;
+ }
+
+ if (stream_info->composite_flag > num_stats_comp_mask) {
+ pr_err("%s: comp grp %d exceed max %d\n",
+ __func__, stream_info->composite_flag,
+ num_stats_comp_mask);
+ return -EINVAL;
+ }
+
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].active)
+ stream_info->state = STATS_STOP_PENDING;
+ else
+ stream_info->state = STATS_INACTIVE;
+
+ stats_data->num_active_stream--;
+ stats_mask |= 1 << idx;
+
+ if (stream_info->composite_flag > 0)
+ comp_stats_mask[stream_info->composite_flag-1] |=
+ 1 << idx;
+
+ msm_isp_stats_cfg_stream_scratch(vfe_dev, stream_info,
+ VFE_PING_FLAG);
+ msm_isp_stats_cfg_stream_scratch(vfe_dev, stream_info,
+ VFE_PONG_FLAG);
+
+ ISP_DBG("%s: stats_mask %x %x active streams %d\n",
+ __func__, comp_stats_mask[0],
+ comp_stats_mask[1],
+ stats_data->num_active_stream);
+ }
+
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) {
+ rc = msm_isp_stats_wait_for_cfg_done(vfe_dev);
+ } else {
+ vfe_dev->hw_info->vfe_ops.stats_ops.enable_module(
+ vfe_dev, stats_mask, stream_cfg_cmd->enable);
+ for (i = 0; i < num_stats_comp_mask; i++) {
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_comp_mask(
+ vfe_dev, comp_stats_mask[i], i, 0);
+ }
+ }
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
+
+ if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s Invalid stats index %d", __func__, idx);
+ return -EINVAL;
+ }
+
+ stream_info = &stats_data->stream_info[idx];
+ rc = vfe_dev->buf_mgr->ops->flush_buf(vfe_dev->buf_mgr,
+ vfe_dev->pdev->id, stream_info->bufq_handle,
+ MSM_ISP_BUFFER_FLUSH_ALL, &timestamp.buf_time,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
+ if (rc == -EFAULT) {
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_BUF_FATAL_ERROR);
+ return rc;
+ }
+ }
+ return rc;
+}
+
+int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0;
+ struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd = arg;
+ if (vfe_dev->stats_data.num_active_stream == 0)
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_ub(vfe_dev);
+
+ if (stream_cfg_cmd->enable) {
+ msm_isp_stats_update_cgc_override(vfe_dev, stream_cfg_cmd);
+
+ rc = msm_isp_start_stats_stream(vfe_dev, stream_cfg_cmd);
+ } else {
+ rc = msm_isp_stop_stats_stream(vfe_dev, stream_cfg_cmd);
+
+ msm_isp_stats_update_cgc_override(vfe_dev, stream_cfg_cmd);
+ }
+
+ return rc;
+}
+
+int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0, i;
+ struct msm_vfe_stats_stream *stream_info;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ 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;
+
+ /*validate request*/
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info = &update_cmd->update_info[i];
+ /*check array reference bounds*/
+ if (STATS_IDX(update_info->stream_handle)
+ > vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s: stats idx %d out of bound!", __func__,
+ STATS_IDX(update_info->stream_handle));
+ return -EINVAL;
+ }
+ }
+
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info = &update_cmd->update_info[i];
+ stream_info = &stats_data->stream_info[
+ STATS_IDX(update_info->stream_handle)];
+ if (stream_info->stream_handle !=
+ update_info->stream_handle) {
+ pr_err("%s: stats stream handle %x %x mismatch!\n",
+ __func__, stream_info->stream_handle,
+ update_info->stream_handle);
+ continue;
+ }
+
+ switch (update_cmd->update_type) {
+ case UPDATE_STREAM_STATS_FRAMEDROP_PATTERN: {
+ uint32_t framedrop_period =
+ msm_isp_get_framedrop_period(
+ update_info->skip_pattern);
+ if (update_info->skip_pattern == SKIP_ALL)
+ stream_info->framedrop_pattern = 0x0;
+ else
+ stream_info->framedrop_pattern = 0x1;
+ stream_info->framedrop_period = framedrop_period - 1;
+ if (stream_info->init_stats_frame_drop == 0)
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_reg(
+ vfe_dev, stream_info);
+ break;
+ }
+ case UPDATE_STREAM_SW_FRAME_DROP: {
+ sw_skip_info = &update_info->sw_skip_info;
+ if (!stream_info->sw_skip.stream_src_mask)
+ stream_info->sw_skip = *sw_skip_info;
+
+ if (sw_skip_info->stats_type_mask != 0) {
+ /* No image buffer skip, only stats skip */
+ pr_debug("%s:%x skip type %x mode %d min %d max %d\n",
+ __func__, 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);
+ stream_info->sw_skip.stats_type_mask =
+ sw_skip_info->stats_type_mask;
+ }
+ break;
+ }
+
+ default:
+ pr_err("%s: Invalid update type\n", __func__);
+ return -EINVAL;
+ }
+ }
+ return rc;
+}
+
+void msm_isp_stats_disable(struct vfe_device *vfe_dev)
+{
+ int i;
+ unsigned int mask = 0;
+
+ if (!vfe_dev) {
+ pr_err("%s: error NULL ptr\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; i++)
+ mask |= 1 << i;
+
+ vfe_dev->hw_info->vfe_ops.stats_ops.enable_module(vfe_dev, mask, 0);
+}