blob: 8273298cdda6e20fdbc0b5be25e1967fa345e487 [file] [log] [blame]
/* 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 <linux/atomic.h>
#include <media/v4l2-subdev.h>
#include <media/msmb_isp.h>
#include "msm_isp_util_32.h"
#include "msm_isp_stats_util_32.h"
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,
struct msm_isp_buffer **done_buf)
{
int rc = -1;
struct msm_isp_buffer *buf;
uint32_t pingpong_bit = 0;
uint32_t bufq_handle = stream_info->bufq_handle;
uint32_t stats_pingpong_offset;
uint32_t stats_idx = STATS_IDX(stream_info->stream_handle);
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,
MSM_ISP_INVALID_BUF_INDEX, &buf);
if (rc < 0) {
vfe_dev->error_info.stats_framedrop_count[stats_idx]++;
return rc;
}
if (buf->num_planes != 1) {
pr_err("%s: Invalid buffer\n", __func__);
rc = -EINVAL;
goto buf_error;
}
vfe_dev->hw_info->vfe_ops.stats_ops.update_ping_pong_addr(
vfe_dev, stream_info,
pingpong_status, buf->mapped_info[0].paddr +
stream_info->buffer_offset);
if (stream_info->buf[pingpong_bit] && done_buf)
*done_buf = stream_info->buf[pingpong_bit];
stream_info->buf[pingpong_bit] = buf;
return 0;
buf_error:
vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
buf->bufq_handle, buf->buf_idx);
return rc;
}
void msm_isp_process_stats_irq(struct vfe_device *vfe_dev,
uint32_t irq_status0, uint32_t irq_status1,
struct msm_isp_timestamp *ts)
{
int i, j, rc;
struct msm_isp32_event_data buf_event;
struct msm_isp_stats_event *stats_event = &buf_event.u.stats;
struct msm_isp_buffer *done_buf;
struct msm_vfe_stats_stream *stream_info = NULL;
uint32_t pingpong_status;
uint32_t comp_stats_type_mask = 0, atomic_stats_mask = 0;
uint32_t stats_comp_mask = 0, stats_irq_mask = 0;
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: status: 0x%x\n", __func__, irq_status0);
/*
* If any of composite mask is set, clear irq bits from mask,
* they will be restored by comp mask
*/
if (stats_comp_mask) {
for (j = 0; j < num_stats_comp_mask; j++) {
stats_irq_mask &= ~atomic_read(
&vfe_dev->stats_data.stats_comp_mask[j]);
}
}
for (j = 0; j < num_stats_comp_mask; j++) {
atomic_stats_mask = atomic_read(
&vfe_dev->stats_data.stats_comp_mask[j]);
if (!stats_comp_mask) {
stats_irq_mask &= ~atomic_stats_mask;
} else {
/* restore irq bits from composite mask */
if (stats_comp_mask & (1 << j))
stats_irq_mask |= atomic_stats_mask;
}
/* if no irq bits set from this composite mask continue*/
if (!stats_irq_mask)
continue;
memset(&buf_event, 0, sizeof(struct msm_isp32_event_data));
buf_event.timestamp = ts->event_time;
buf_event.frame_id =
vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
buf_event.input_intf = VFE_PIX_0;
pingpong_status = vfe_dev->hw_info->
vfe_ops.stats_ops.get_pingpong_status(vfe_dev);
for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type;
i++) {
if (!(stats_irq_mask & (1 << i)))
continue;
stats_irq_mask &= ~(1 << i);
stream_info = &vfe_dev->stats_data.stream_info[i];
done_buf = NULL;
msm_isp_stats_cfg_ping_pong_address(vfe_dev,
stream_info, pingpong_status, &done_buf);
if (done_buf) {
rc = vfe_dev->buf_mgr->ops->buf_divert(
vfe_dev->buf_mgr, done_buf->bufq_handle,
done_buf->buf_idx, &ts->buf_time,
vfe_dev->axi_data.
src_info[VFE_PIX_0].frame_id);
if (rc != 0)
continue;
stats_event->stats_buf_idxs
[stream_info->stats_type] =
done_buf->buf_idx;
if (!stream_info->composite_flag) {
stats_event->stats_mask =
1 << stream_info->stats_type;
ISP_DBG("%s: stats frameid: 0x%x %d\n",
__func__, buf_event.frame_id,
stream_info->stats_type);
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;
}
}
}
if (comp_stats_type_mask) {
ISP_DBG("%s: comp_stats frameid: 0x%x, 0x%x\n",
__func__, 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;
}
}
}
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_AVAILABLE) {
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);
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_AVAILABLE) {
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;
}
rc = msm_isp_stats_cfg_ping_pong_address(vfe_dev,
stream_info, VFE_PING_FLAG, NULL);
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, NULL);
if (rc < 0) {
pr_err("%s: No free buffer for pong\n", __func__);
return rc;
}
return rc;
}
static void msm_isp_deinit_stats_ping_pong_reg(
struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
int i;
struct msm_isp_buffer *buf;
for (i = 0; i < 2; i++) {
buf = stream_info->buf[i];
if (buf)
vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
buf->bufq_handle, buf->buf_idx);
}
}
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 stats_mask = 0, comp_stats_mask = 0;
uint32_t enable = 0;
struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
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) {
stats_mask |= i;
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);
vfe_dev->hw_info->vfe_ops.stats_ops.cfg_comp_mask(
vfe_dev, BIT(i), enable);
} else if (stats_data->stream_info[i].state == STATS_STARTING ||
stats_data->stream_info[i].state == STATS_STOPPING) {
if (stats_data->stream_info[i].composite_flag)
comp_stats_mask |= i;
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;
if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) {
pr_err("%s invalid num_streams %d\n", __func__,
stream_cfg_cmd->num_streams);
return -EINVAL;
}
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;
}
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;
if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) {
pr_err("%s invalid num_streams %d\n", __func__,
stream_cfg_cmd->num_streams);
return -EINVAL;
}
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 type:%d stmID:0x%x\n",
__func__, idx, stream_info->stats_type,
stream_info->stream_id);
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], 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;
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;
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], 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];
msm_isp_deinit_stats_ping_pong_reg(vfe_dev, stream_info);
}
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->num_streams > MSM_ISP_STATS_MAX) {
pr_err("%s invalid num_streams %d\n", __func__,
stream_cfg_cmd->num_streams);
return -EINVAL;
}
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;
/*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;
}
default:
pr_err("%s: Invalid update type\n", __func__);
return -EINVAL;
}
}
return rc;
}