1417 lines
42 KiB
C
1417 lines
42 KiB
C
/* Copyright (c) 2013, 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 "msm_isp_util.h"
|
|
#include "msm_isp_axi_util.h"
|
|
|
|
#define SRC_TO_INTF(src) \
|
|
((src < RDI_INTF_0) ? VFE_PIX_0 : \
|
|
(VFE_RAW_0 + src - RDI_INTF_0))
|
|
|
|
#define HANDLE_TO_IDX(handle) (handle & 0xFF)
|
|
|
|
int msm_isp_axi_create_stream(
|
|
struct msm_vfe_axi_shared_data *axi_data,
|
|
struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
|
|
{
|
|
uint32_t i = stream_cfg_cmd->stream_src;
|
|
if (i >= VFE_AXI_SRC_MAX) {
|
|
pr_err("%s:%d invalid stream_src %d\n", __func__, __LINE__,
|
|
stream_cfg_cmd->stream_src);
|
|
return -EINVAL;
|
|
}
|
|
|
|
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 | i;
|
|
|
|
memset(&axi_data->stream_info[i], 0,
|
|
sizeof(struct msm_vfe_axi_stream));
|
|
spin_lock_init(&axi_data->stream_info[i].lock);
|
|
axi_data->stream_info[i].session_id = stream_cfg_cmd->session_id;
|
|
axi_data->stream_info[i].stream_id = stream_cfg_cmd->stream_id;
|
|
axi_data->stream_info[i].buf_divert = stream_cfg_cmd->buf_divert;
|
|
axi_data->stream_info[i].state = INACTIVE;
|
|
axi_data->stream_info[i].stream_handle =
|
|
stream_cfg_cmd->axi_stream_handle;
|
|
return 0;
|
|
}
|
|
|
|
void msm_isp_axi_destroy_stream(
|
|
struct msm_vfe_axi_shared_data *axi_data, int stream_idx)
|
|
{
|
|
if (axi_data->stream_info[stream_idx].state != AVALIABLE) {
|
|
axi_data->stream_info[stream_idx].state = AVALIABLE;
|
|
axi_data->stream_info[stream_idx].stream_handle = 0;
|
|
} else {
|
|
pr_err("%s: stream does not exist\n", __func__);
|
|
}
|
|
}
|
|
|
|
int msm_isp_validate_axi_request(struct msm_vfe_axi_shared_data *axi_data,
|
|
struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
|
|
{
|
|
int rc = -1, i;
|
|
struct msm_vfe_axi_stream *stream_info =
|
|
&axi_data->stream_info[
|
|
HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)];
|
|
|
|
switch (stream_cfg_cmd->output_format) {
|
|
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_SBGGR12:
|
|
case V4L2_PIX_FMT_SGBRG12:
|
|
case V4L2_PIX_FMT_SGRBG12:
|
|
case V4L2_PIX_FMT_SRGGB12:
|
|
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_JPEG:
|
|
case V4L2_PIX_FMT_META:
|
|
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:
|
|
case V4L2_PIX_FMT_NV16:
|
|
case V4L2_PIX_FMT_NV61:
|
|
stream_info->num_planes = 2;
|
|
stream_info->format_factor = 1.5 * ISP_Q2;
|
|
break;
|
|
/*TD: Add more image format*/
|
|
default:
|
|
pr_err("%s: Invalid output format\n", __func__);
|
|
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;
|
|
}
|
|
|
|
for (i = 0; i < stream_info->num_planes; i++) {
|
|
stream_info->plane_cfg[i] = stream_cfg_cmd->plane_cfg[i];
|
|
stream_info->max_width = max(stream_info->max_width,
|
|
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 plane_idx)
|
|
{
|
|
uint32_t size = 0;
|
|
struct msm_vfe_axi_plane_cfg *plane_cfg = stream_info->plane_cfg;
|
|
switch (stream_info->output_format) {
|
|
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:
|
|
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_QBGGR10:
|
|
case V4L2_PIX_FMT_QGBRG10:
|
|
case V4L2_PIX_FMT_QGRBG10:
|
|
case V4L2_PIX_FMT_QRGGB10:
|
|
/* 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:
|
|
/* TODO: fix me */
|
|
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 / 2;
|
|
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 / 8;
|
|
break;
|
|
case V4L2_PIX_FMT_NV16:
|
|
case V4L2_PIX_FMT_NV61:
|
|
size = plane_cfg[plane_idx].output_height *
|
|
plane_cfg[plane_idx].output_width;
|
|
break;
|
|
/*TD: Add more image format*/
|
|
default:
|
|
pr_err("%s: Invalid output format\n", __func__);
|
|
break;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
void msm_isp_axi_reserve_wm(struct msm_vfe_axi_shared_data *axi_data,
|
|
struct msm_vfe_axi_stream *stream_info)
|
|
{
|
|
int i, j;
|
|
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;
|
|
msm_isp_axi_get_plane_size(
|
|
stream_info, i);
|
|
axi_data->wm_image_size[j] =
|
|
stream_info->plane_cfg[i].output_height *
|
|
stream_info->plane_cfg[i].output_width;
|
|
axi_data->num_used_wm++;
|
|
break;
|
|
}
|
|
}
|
|
stream_info->wm[i] = j;
|
|
pr_err("%s reserved WM %d for stream %d session %d\n", __func__, j, stream_info->stream_id,
|
|
stream_info->session_id);
|
|
}
|
|
}
|
|
|
|
void msm_isp_axi_free_wm(struct msm_vfe_axi_shared_data *axi_data,
|
|
struct msm_vfe_axi_stream *stream_info)
|
|
{
|
|
int i;
|
|
for (i = 0; i < stream_info->num_planes; i++) {
|
|
axi_data->free_wm[stream_info->wm[i]] = 0;
|
|
axi_data->num_used_wm--;
|
|
}
|
|
}
|
|
|
|
void msm_isp_axi_reserve_comp_mask(
|
|
struct msm_vfe_axi_shared_data *axi_data,
|
|
struct msm_vfe_axi_stream *stream_info)
|
|
{
|
|
int i;
|
|
uint8_t comp_mask = 0;
|
|
for (i = 0; i < stream_info->num_planes; i++)
|
|
comp_mask |= 1 << stream_info->wm[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;
|
|
axi_data->composite_info[i].
|
|
stream_composite_mask = comp_mask;
|
|
axi_data->num_used_composite_mask++;
|
|
break;
|
|
}
|
|
}
|
|
stream_info->comp_mask_index = i;
|
|
return;
|
|
}
|
|
|
|
void msm_isp_axi_free_comp_mask(struct msm_vfe_axi_shared_data *axi_data,
|
|
struct msm_vfe_axi_stream *stream_info)
|
|
{
|
|
axi_data->composite_info[stream_info->comp_mask_index].
|
|
stream_composite_mask = 0;
|
|
axi_data->composite_info[stream_info->comp_mask_index].
|
|
stream_handle = 0;
|
|
axi_data->num_used_composite_mask--;
|
|
}
|
|
|
|
int msm_isp_axi_check_stream_state(
|
|
struct vfe_device *vfe_dev,
|
|
struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
|
|
{
|
|
int rc = 0, i;
|
|
unsigned long flags;
|
|
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
|
|
struct msm_vfe_axi_stream *stream_info;
|
|
enum msm_vfe_axi_state valid_state =
|
|
(stream_cfg_cmd->cmd == START_STREAM) ? INACTIVE : ACTIVE;
|
|
|
|
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]) >= MAX_NUM_STREAM) {
|
|
return -EINVAL;
|
|
}
|
|
stream_info = &axi_data->stream_info[
|
|
HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
|
|
spin_lock_irqsave(&stream_info->lock, flags);
|
|
if (stream_info->state != valid_state) {
|
|
if ((stream_info->state == PAUSING ||
|
|
stream_info->state == PAUSED ||
|
|
stream_info->state == RESUME_PENDING ||
|
|
stream_info->state == RESUMING) &&
|
|
(stream_cfg_cmd->cmd == STOP_STREAM ||
|
|
stream_cfg_cmd->cmd == STOP_IMMEDIATELY)) {
|
|
stream_info->state = ACTIVE;
|
|
} else {
|
|
spin_unlock_irqrestore(
|
|
&stream_info->lock, flags);
|
|
pr_err("%s: Invalid stream state\n", __func__);
|
|
rc = -EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&stream_info->lock, flags);
|
|
|
|
if (stream_cfg_cmd->cmd == START_STREAM) {
|
|
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: Stream has no valid buffer queue\n",
|
|
__func__);
|
|
rc = -EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
void msm_isp_update_framedrop_reg(struct vfe_device *vfe_dev)
|
|
{
|
|
int i;
|
|
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
|
|
struct msm_vfe_axi_stream *stream_info;
|
|
for (i = 0; i < MAX_NUM_STREAM; i++) {
|
|
stream_info = &axi_data->stream_info[i];
|
|
if (stream_info->state != ACTIVE)
|
|
continue;
|
|
|
|
if (stream_info->runtime_framedrop_update) {
|
|
stream_info->runtime_init_frame_drop--;
|
|
if (stream_info->runtime_init_frame_drop == 0) {
|
|
stream_info->runtime_framedrop_update = 0;
|
|
vfe_dev->hw_info->vfe_ops.axi_ops.
|
|
cfg_framedrop(vfe_dev, stream_info);
|
|
}
|
|
}
|
|
if (stream_info->stream_type == BURST_STREAM) {
|
|
stream_info->runtime_burst_frame_count--;
|
|
if (stream_info->runtime_burst_frame_count == 0) {
|
|
vfe_dev->hw_info->vfe_ops.axi_ops.
|
|
cfg_framedrop(vfe_dev, stream_info);
|
|
vfe_dev->hw_info->vfe_ops.core_ops.
|
|
reg_update(vfe_dev);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void msm_isp_reset_framedrop(struct vfe_device *vfe_dev,
|
|
struct msm_vfe_axi_stream *stream_info)
|
|
{
|
|
stream_info->runtime_init_frame_drop = stream_info->init_frame_drop;
|
|
stream_info->runtime_burst_frame_count =
|
|
stream_info->burst_frame_count;
|
|
stream_info->runtime_num_burst_capture =
|
|
stream_info->num_burst_capture;
|
|
stream_info->runtime_framedrop_update = stream_info->framedrop_update;
|
|
vfe_dev->hw_info->vfe_ops.axi_ops.cfg_framedrop(vfe_dev, stream_info);
|
|
}
|
|
|
|
void msm_isp_sof_notify(struct vfe_device *vfe_dev,
|
|
enum msm_vfe_input_src frame_src, struct msm_isp_timestamp *ts) {
|
|
struct msm_isp_event_data sof_event;
|
|
switch (frame_src) {
|
|
case VFE_PIX_0:
|
|
ISP_DBG("%s: PIX0 frame id: %lu\n", __func__,
|
|
vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
|
|
vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id++;
|
|
if (vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id == 0)
|
|
vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id = 1;
|
|
break;
|
|
case VFE_RAW_0:
|
|
case VFE_RAW_1:
|
|
case VFE_RAW_2:
|
|
ISP_DBG("%s: RDI%d frame id: %lu\n",
|
|
__func__, frame_src - VFE_RAW_0,
|
|
vfe_dev->axi_data.src_info[frame_src].frame_id);
|
|
vfe_dev->axi_data.src_info[frame_src].frame_id++;
|
|
if (vfe_dev->axi_data.src_info[frame_src].frame_id == 0)
|
|
vfe_dev->axi_data.src_info[frame_src].frame_id = 1;
|
|
break;
|
|
default:
|
|
pr_err("%s: invalid frame src %d received\n",
|
|
__func__, frame_src);
|
|
break;
|
|
}
|
|
|
|
sof_event.frame_id = vfe_dev->axi_data.src_info[frame_src].frame_id;
|
|
vfe_dev->frame_id = vfe_dev->axi_data.src_info[frame_src].frame_id;
|
|
vfe_dev->eof_event_occur = 0;
|
|
sof_event.timestamp = ts->event_time;
|
|
msm_isp_send_event(vfe_dev, ISP_EVENT_SOF, &sof_event);
|
|
}
|
|
|
|
void msm_isp_calculate_framedrop(
|
|
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 =
|
|
&axi_data->stream_info[
|
|
HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)];
|
|
uint32_t 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->framedrop_pattern = 0x0;
|
|
else
|
|
stream_info->framedrop_pattern = 0x1;
|
|
stream_info->framedrop_period = framedrop_period - 1;
|
|
|
|
if (stream_cfg_cmd->init_frame_drop < framedrop_period) {
|
|
stream_info->framedrop_pattern <<=
|
|
stream_cfg_cmd->init_frame_drop;
|
|
stream_info->init_frame_drop = 0;
|
|
stream_info->framedrop_update = 0;
|
|
} else {
|
|
stream_info->init_frame_drop = stream_cfg_cmd->init_frame_drop;
|
|
stream_info->framedrop_update = 1;
|
|
}
|
|
|
|
if (stream_cfg_cmd->burst_count > 0) {
|
|
stream_info->stream_type = BURST_STREAM;
|
|
stream_info->num_burst_capture =
|
|
stream_cfg_cmd->burst_count;
|
|
stream_info->burst_frame_count =
|
|
stream_cfg_cmd->init_frame_drop +
|
|
(stream_cfg_cmd->burst_count - 1) *
|
|
framedrop_period + 1;
|
|
} else {
|
|
stream_info->stream_type = CONTINUOUS_STREAM;
|
|
stream_info->burst_frame_count = 0;
|
|
stream_info->num_burst_capture = 0;
|
|
}
|
|
}
|
|
|
|
void msm_isp_calculate_bandwidth(
|
|
struct msm_vfe_axi_shared_data *axi_data,
|
|
struct msm_vfe_axi_stream *stream_info)
|
|
{
|
|
if (stream_info->stream_src < RDI_INTF_0) {
|
|
stream_info->bandwidth =
|
|
(axi_data->src_info[VFE_PIX_0].pixel_clock /
|
|
axi_data->src_info[VFE_PIX_0].width) *
|
|
stream_info->max_width;
|
|
stream_info->bandwidth = stream_info->bandwidth *
|
|
stream_info->format_factor / ISP_Q2;
|
|
} else {
|
|
int rdi = SRC_TO_INTF(stream_info->stream_src);
|
|
stream_info->bandwidth = axi_data->src_info[rdi].pixel_clock;
|
|
}
|
|
}
|
|
|
|
int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg)
|
|
{
|
|
int rc = 0, i;
|
|
uint32_t io_format = 0;
|
|
struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd = arg;
|
|
struct msm_vfe_axi_stream *stream_info;
|
|
|
|
rc = msm_isp_axi_create_stream(
|
|
&vfe_dev->axi_data, stream_cfg_cmd);
|
|
if (rc) {
|
|
pr_err("%s: create stream failed\n", __func__);
|
|
return rc;
|
|
}
|
|
|
|
rc = msm_isp_validate_axi_request(
|
|
&vfe_dev->axi_data, stream_cfg_cmd);
|
|
if (rc) {
|
|
pr_err("%s: Request validation failed\n", __func__);
|
|
if (HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle) < MAX_NUM_STREAM)
|
|
msm_isp_axi_destroy_stream(&vfe_dev->axi_data,HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle));
|
|
return rc;
|
|
}
|
|
|
|
stream_info = &vfe_dev->axi_data.
|
|
stream_info[HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)];
|
|
msm_isp_axi_reserve_wm(&vfe_dev->axi_data, 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_warn("%s: Overriding input format\n",
|
|
__func__);
|
|
|
|
io_format = stream_info->output_format;
|
|
}
|
|
vfe_dev->hw_info->vfe_ops.axi_ops.cfg_io_format(
|
|
vfe_dev, stream_info->stream_src, io_format);
|
|
}
|
|
|
|
msm_isp_calculate_framedrop(&vfe_dev->axi_data, stream_cfg_cmd);
|
|
|
|
if (stream_info->num_planes > 1) {
|
|
msm_isp_axi_reserve_comp_mask(
|
|
&vfe_dev->axi_data, stream_info);
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg)
|
|
{
|
|
int rc = 0, i;
|
|
struct msm_vfe_axi_stream_release_cmd *stream_release_cmd = arg;
|
|
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
|
|
struct msm_vfe_axi_stream *stream_info;
|
|
struct msm_vfe_axi_stream_cfg_cmd stream_cfg;
|
|
|
|
if (HANDLE_TO_IDX(stream_release_cmd->stream_handle) >= MAX_NUM_STREAM) {
|
|
pr_err("%s: Invalid stream handle\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
stream_info = &axi_data->stream_info[HANDLE_TO_IDX(stream_release_cmd->stream_handle)];
|
|
|
|
if (stream_info->state == AVALIABLE) {
|
|
pr_err("%s: Stream already released\n", __func__);
|
|
return -EINVAL;
|
|
} else if (stream_info->state != INACTIVE) {
|
|
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) {
|
|
vfe_dev->hw_info->vfe_ops.axi_ops.
|
|
clear_comp_mask(vfe_dev, stream_info);
|
|
msm_isp_axi_free_comp_mask(&vfe_dev->axi_data, stream_info);
|
|
} else {
|
|
vfe_dev->hw_info->vfe_ops.axi_ops.
|
|
clear_wm_irq_mask(vfe_dev, stream_info);
|
|
}
|
|
|
|
vfe_dev->hw_info->vfe_ops.axi_ops.clear_framedrop(vfe_dev, stream_info);
|
|
msm_isp_axi_free_wm(axi_data, stream_info);
|
|
|
|
msm_isp_axi_destroy_stream(&vfe_dev->axi_data,
|
|
HANDLE_TO_IDX(stream_release_cmd->stream_handle));
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void msm_isp_axi_stream_enable_cfg(
|
|
struct vfe_device *vfe_dev,
|
|
struct msm_vfe_axi_stream *stream_info)
|
|
{
|
|
int i;
|
|
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
|
|
if (stream_info->state == INACTIVE)
|
|
return;
|
|
for (i = 0; i < stream_info->num_planes; i++) {
|
|
if (stream_info->state == START_PENDING ||
|
|
stream_info->state == RESUME_PENDING)
|
|
vfe_dev->hw_info->vfe_ops.axi_ops.
|
|
enable_wm(vfe_dev, stream_info->wm[i], 1);
|
|
else
|
|
vfe_dev->hw_info->vfe_ops.axi_ops.
|
|
enable_wm(vfe_dev, stream_info->wm[i], 0);
|
|
}
|
|
|
|
if (stream_info->state == START_PENDING)
|
|
axi_data->num_active_stream++;
|
|
else if (stream_info->state == STOP_PENDING)
|
|
axi_data->num_active_stream--;
|
|
}
|
|
|
|
void msm_isp_axi_stream_update(struct vfe_device *vfe_dev)
|
|
{
|
|
int i;
|
|
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
|
|
for (i = 0; i < MAX_NUM_STREAM; i++) {
|
|
if (axi_data->stream_info[i].state == START_PENDING ||
|
|
axi_data->stream_info[i].state ==
|
|
STOP_PENDING) {
|
|
msm_isp_axi_stream_enable_cfg(
|
|
vfe_dev, &axi_data->stream_info[i]);
|
|
axi_data->stream_info[i].state =
|
|
axi_data->stream_info[i].state ==
|
|
START_PENDING ? STARTING : STOPPING;
|
|
} else if (axi_data->stream_info[i].state == STARTING ||
|
|
axi_data->stream_info[i].state == STOPPING) {
|
|
axi_data->stream_info[i].state =
|
|
axi_data->stream_info[i].state == STARTING ?
|
|
ACTIVE : INACTIVE;
|
|
}
|
|
}
|
|
|
|
if (vfe_dev->axi_data.pipeline_update == DISABLE_CAMIF ||
|
|
(vfe_dev->axi_data.pipeline_update ==
|
|
DISABLE_CAMIF_IMMEDIATELY)) {
|
|
vfe_dev->hw_info->vfe_ops.stats_ops.
|
|
enable_module(vfe_dev, 0xFF, 0);
|
|
vfe_dev->axi_data.pipeline_update = NO_UPDATE;
|
|
}
|
|
|
|
vfe_dev->axi_data.stream_update--;
|
|
if (vfe_dev->axi_data.stream_update == 0)
|
|
complete(&vfe_dev->stream_config_complete);
|
|
}
|
|
|
|
static void msm_isp_reload_ping_pong_offset(struct vfe_device *vfe_dev,
|
|
struct msm_vfe_axi_stream *stream_info)
|
|
{
|
|
int i, j;
|
|
struct msm_isp_buffer *buf;
|
|
uint32_t pingpong_flags[2]= {VFE_PING_FLAG, VFE_PONG_FLAG};
|
|
for (i = 0; i < 2; i++) {
|
|
buf = stream_info->buf[i];
|
|
for (j = 0; j < stream_info->num_planes; j++)
|
|
vfe_dev->hw_info->vfe_ops.axi_ops.update_ping_pong_addr(
|
|
vfe_dev, stream_info->wm[j],
|
|
pingpong_flags[i], buf->mapped_info[j].paddr +
|
|
stream_info->plane_cfg[j].plane_addr_offset);
|
|
}
|
|
}
|
|
|
|
void msm_isp_axi_cfg_update(struct vfe_device *vfe_dev)
|
|
{
|
|
int i, j;
|
|
uint32_t update_state;
|
|
unsigned long flags;
|
|
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
|
|
struct msm_vfe_axi_stream *stream_info;
|
|
for (i = 0; i < MAX_NUM_STREAM; i++) {
|
|
stream_info = &axi_data->stream_info[i];
|
|
if (stream_info->stream_type == BURST_STREAM ||
|
|
stream_info->state == AVALIABLE)
|
|
continue;
|
|
spin_lock_irqsave(&stream_info->lock, flags);
|
|
if (stream_info->state == PAUSING) {
|
|
/*AXI Stopped, apply update*/
|
|
stream_info->state = PAUSED;
|
|
msm_isp_reload_ping_pong_offset(vfe_dev, stream_info);
|
|
for (j = 0; j < stream_info->num_planes; j++)
|
|
vfe_dev->hw_info->vfe_ops.axi_ops.
|
|
cfg_wm_reg(vfe_dev, stream_info, j);
|
|
/*Resume AXI*/
|
|
stream_info->state = RESUME_PENDING;
|
|
msm_isp_axi_stream_enable_cfg(
|
|
vfe_dev, &axi_data->stream_info[i]);
|
|
stream_info->state = RESUMING;
|
|
} else if (stream_info->state == RESUMING) {
|
|
stream_info->runtime_output_format =
|
|
stream_info->output_format;
|
|
stream_info->state = ACTIVE;
|
|
}
|
|
spin_unlock_irqrestore(&stream_info->lock, flags);
|
|
}
|
|
|
|
update_state = atomic_dec_return(&axi_data->axi_cfg_update);
|
|
}
|
|
|
|
static void msm_isp_cfg_pong_address(struct vfe_device *vfe_dev,
|
|
struct msm_vfe_axi_stream *stream_info)
|
|
{
|
|
int i;
|
|
struct msm_isp_buffer *buf = stream_info->buf[0];
|
|
for (i = 0; i < stream_info->num_planes; i++)
|
|
vfe_dev->hw_info->vfe_ops.axi_ops.update_ping_pong_addr(
|
|
vfe_dev, stream_info->wm[i],
|
|
VFE_PONG_FLAG, buf->mapped_info[i].paddr +
|
|
stream_info->plane_cfg[i].plane_addr_offset);
|
|
stream_info->buf[1] = buf;
|
|
}
|
|
|
|
static void msm_isp_get_done_buf(struct vfe_device *vfe_dev,
|
|
struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status,
|
|
struct msm_isp_buffer **done_buf)
|
|
{
|
|
uint32_t pingpong_bit = 0, i;
|
|
pingpong_bit = (~(pingpong_status >> stream_info->wm[0]) & 0x1);
|
|
for (i = 0; i < stream_info->num_planes; i++) {
|
|
if (pingpong_bit !=
|
|
(~(pingpong_status >> stream_info->wm[i]) & 0x1)) {
|
|
pr_warn("%s: Write master ping pong mismatch. Status: 0x%x\n",
|
|
__func__, pingpong_status);
|
|
}
|
|
}
|
|
*done_buf = stream_info->buf[pingpong_bit];
|
|
}
|
|
|
|
static int msm_isp_cfg_ping_pong_address(struct vfe_device *vfe_dev,
|
|
struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status)
|
|
{
|
|
int i, rc = -1;
|
|
struct msm_isp_buffer *buf = NULL;
|
|
uint32_t pingpong_bit = 0;
|
|
uint32_t bufq_handle = stream_info->bufq_handle;
|
|
uint32_t stream_idx = HANDLE_TO_IDX(stream_info->stream_handle);
|
|
|
|
rc = vfe_dev->buf_mgr->ops->get_buf(vfe_dev->buf_mgr,
|
|
vfe_dev->pdev->id, bufq_handle, &buf);
|
|
if (rc < 0) {
|
|
vfe_dev->error_info.
|
|
stream_framedrop_count[stream_idx]++;
|
|
return rc;
|
|
}
|
|
|
|
if (buf->num_planes != stream_info->num_planes) {
|
|
pr_err("%s: Invalid buffer\n", __func__);
|
|
rc = -EINVAL;
|
|
goto buf_error;
|
|
}
|
|
|
|
for (i = 0; i < stream_info->num_planes; i++) {
|
|
vfe_dev->hw_info->vfe_ops.axi_ops.update_ping_pong_addr(
|
|
vfe_dev, stream_info->wm[i],
|
|
pingpong_status, buf->mapped_info[i].paddr +
|
|
stream_info->plane_cfg[i].plane_addr_offset);
|
|
}
|
|
pingpong_bit = (~(pingpong_status >> stream_info->wm[0]) & 0x1);
|
|
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;
|
|
}
|
|
|
|
static void msm_isp_process_done_buf(struct vfe_device *vfe_dev,
|
|
struct msm_vfe_axi_stream *stream_info, struct msm_isp_buffer *buf,
|
|
struct msm_isp_timestamp *ts)
|
|
{
|
|
int rc;
|
|
struct msm_isp_event_data buf_event;
|
|
uint32_t stream_idx = HANDLE_TO_IDX(stream_info->stream_handle);
|
|
uint32_t frame_id = vfe_dev->axi_data.
|
|
src_info[SRC_TO_INTF(stream_info->stream_src)].frame_id;
|
|
if (stream_idx >= MAX_NUM_STREAM) {
|
|
pr_err("%s: Invalid stream_idx", __func__);
|
|
return;
|
|
}
|
|
|
|
if (buf && ts) {
|
|
if (stream_info->buf_divert) {
|
|
rc = vfe_dev->buf_mgr->ops->buf_divert(vfe_dev->buf_mgr,
|
|
buf->bufq_handle, buf->buf_idx,
|
|
&ts->buf_time, frame_id);
|
|
/* Buf divert return value represent whether the buf
|
|
* can be diverted. A positive return value means
|
|
* other ISP hardware is still processing the frame.
|
|
*/
|
|
if (rc == 0) {
|
|
buf_event.frame_id = frame_id;
|
|
buf_event.timestamp = ts->buf_time;
|
|
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 =
|
|
stream_info->bufq_handle;
|
|
buf_event.u.buf_done.buf_idx = buf->buf_idx;
|
|
buf_event.u.buf_done.output_format =
|
|
stream_info->runtime_output_format;
|
|
msm_isp_send_event(vfe_dev,
|
|
ISP_EVENT_BUF_DIVERT + stream_idx,
|
|
&buf_event);
|
|
}
|
|
} else {
|
|
vfe_dev->buf_mgr->ops->buf_done(vfe_dev->buf_mgr,
|
|
buf->bufq_handle, buf->buf_idx,
|
|
&ts->buf_time, frame_id,
|
|
stream_info->runtime_output_format);
|
|
}
|
|
}
|
|
}
|
|
|
|
enum msm_isp_camif_update_state
|
|
msm_isp_get_camif_update_state(struct vfe_device *vfe_dev,
|
|
struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
|
|
{
|
|
int i;
|
|
struct msm_vfe_axi_stream *stream_info;
|
|
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
|
|
uint8_t pix_stream_cnt = 0, cur_pix_stream_cnt;
|
|
cur_pix_stream_cnt =
|
|
axi_data->src_info[VFE_PIX_0].pix_stream_count +
|
|
axi_data->src_info[VFE_PIX_0].raw_stream_count;
|
|
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
|
|
stream_info =
|
|
&axi_data->stream_info[
|
|
HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
|
|
if (stream_info->stream_src < RDI_INTF_0)
|
|
pix_stream_cnt++;
|
|
}
|
|
|
|
if (pix_stream_cnt) {
|
|
if (cur_pix_stream_cnt == 0 && pix_stream_cnt &&
|
|
stream_cfg_cmd->cmd == START_STREAM)
|
|
return ENABLE_CAMIF;
|
|
else if (cur_pix_stream_cnt &&
|
|
(cur_pix_stream_cnt - pix_stream_cnt) == 0 &&
|
|
stream_cfg_cmd->cmd == STOP_STREAM)
|
|
return DISABLE_CAMIF;
|
|
else if (cur_pix_stream_cnt &&
|
|
(cur_pix_stream_cnt - pix_stream_cnt) == 0 &&
|
|
stream_cfg_cmd->cmd == STOP_IMMEDIATELY)
|
|
return DISABLE_CAMIF_IMMEDIATELY;
|
|
}
|
|
return NO_UPDATE;
|
|
}
|
|
|
|
void msm_isp_update_camif_output_count(
|
|
struct vfe_device *vfe_dev,
|
|
struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
|
|
{
|
|
int i;
|
|
struct msm_vfe_axi_stream *stream_info;
|
|
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
|
|
|
|
if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
|
|
if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >= MAX_NUM_STREAM) {
|
|
return;
|
|
}
|
|
stream_info =
|
|
&axi_data->stream_info[
|
|
HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
|
|
if (stream_info->stream_src >= RDI_INTF_0)
|
|
return;
|
|
if (stream_info->stream_src == PIX_ENCODER ||
|
|
stream_info->stream_src == PIX_VIEWFINDER ||
|
|
stream_info->stream_src == IDEAL_RAW) {
|
|
if (stream_cfg_cmd->cmd == START_STREAM)
|
|
vfe_dev->axi_data.src_info[VFE_PIX_0].
|
|
pix_stream_count++;
|
|
else
|
|
vfe_dev->axi_data.src_info[VFE_PIX_0].
|
|
pix_stream_count--;
|
|
} else if (stream_info->stream_src == CAMIF_RAW) {
|
|
if (stream_cfg_cmd->cmd == START_STREAM)
|
|
vfe_dev->axi_data.src_info[VFE_PIX_0].
|
|
raw_stream_count++;
|
|
else
|
|
vfe_dev->axi_data.src_info[VFE_PIX_0].
|
|
raw_stream_count--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void msm_camera_io_dump_2(void __iomem *addr, int size)
|
|
{
|
|
char line_str[128], *p_str;
|
|
int i;
|
|
u32 *p = (u32 *) addr;
|
|
u32 data;
|
|
ISP_DBG("%s: %p %d\n", __func__, addr, size);
|
|
line_str[0] = '\0';
|
|
p_str = line_str;
|
|
for (i = 0; i < size/4; i++) {
|
|
if (i % 4 == 0) {
|
|
snprintf(p_str, 12, "%08x: ", (u32) p);
|
|
p_str += 10;
|
|
}
|
|
data = readl_relaxed(p++);
|
|
snprintf(p_str, 12, "%08x ", data);
|
|
p_str += 9;
|
|
if ((i + 1) % 4 == 0) {
|
|
ISP_DBG("%s\n", line_str);
|
|
line_str[0] = '\0';
|
|
p_str = line_str;
|
|
}
|
|
}
|
|
if (line_str[0] != '\0')
|
|
ISP_DBG("%s\n", line_str);
|
|
}
|
|
|
|
/*Factor in Q2 format*/
|
|
#define ISP_DEFAULT_FORMAT_FACTOR 6
|
|
#define ISP_BUS_UTILIZATION_FACTOR 6
|
|
static int msm_isp_update_stream_bandwidth(struct vfe_device *vfe_dev)
|
|
{
|
|
int i, rc = 0;
|
|
struct msm_vfe_axi_stream *stream_info;
|
|
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
|
|
uint32_t total_pix_bandwidth = 0, total_rdi_bandwidth = 0;
|
|
uint32_t num_pix_streams = 0;
|
|
uint64_t total_bandwidth = 0;
|
|
|
|
for (i = 0; i < MAX_NUM_STREAM; i++) {
|
|
stream_info = &axi_data->stream_info[i];
|
|
if (stream_info->state == ACTIVE ||
|
|
stream_info->state == START_PENDING) {
|
|
if (stream_info->stream_src < RDI_INTF_0) {
|
|
total_pix_bandwidth += stream_info->bandwidth;
|
|
num_pix_streams++;
|
|
} else {
|
|
total_rdi_bandwidth += stream_info->bandwidth;
|
|
}
|
|
}
|
|
}
|
|
if (num_pix_streams > 0)
|
|
total_pix_bandwidth = total_pix_bandwidth /
|
|
num_pix_streams * (num_pix_streams - 1) +
|
|
(unsigned long)axi_data->src_info[VFE_PIX_0].pixel_clock *
|
|
(ISP_DEFAULT_FORMAT_FACTOR / ISP_Q2);
|
|
total_bandwidth = total_pix_bandwidth + total_rdi_bandwidth;
|
|
|
|
rc = msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id,
|
|
total_bandwidth, total_bandwidth *
|
|
ISP_BUS_UTILIZATION_FACTOR / ISP_Q2);
|
|
if (rc < 0)
|
|
pr_err("%s: update failed\n", __func__);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int msm_isp_axi_wait_for_cfg_done(struct vfe_device *vfe_dev,
|
|
enum msm_isp_camif_update_state camif_update)
|
|
{
|
|
int rc;
|
|
unsigned long flags;
|
|
spin_lock_irqsave(&vfe_dev->shared_data_lock, flags);
|
|
init_completion(&vfe_dev->stream_config_complete);
|
|
vfe_dev->axi_data.pipeline_update = camif_update;
|
|
#if defined(CONFIG_SEC_CONFIGURE_ISP_STOP)
|
|
vfe_dev->axi_data.stream_update = 1;
|
|
#else
|
|
vfe_dev->axi_data.stream_update = 2;
|
|
#endif
|
|
spin_unlock_irqrestore(&vfe_dev->shared_data_lock, flags);
|
|
rc = wait_for_completion_interruptible_timeout(
|
|
&vfe_dev->stream_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_init_stream_ping_pong_reg(
|
|
struct vfe_device *vfe_dev,
|
|
struct msm_vfe_axi_stream *stream_info)
|
|
{
|
|
int rc = 0;
|
|
/*Set address for both PING & PONG register */
|
|
rc = msm_isp_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;
|
|
}
|
|
|
|
/* For burst stream of one capture, only one buffer
|
|
* is allocated. Duplicate ping buffer address to pong
|
|
* buffer to ensure hardware write to a valid address
|
|
*/
|
|
if (stream_info->stream_type == BURST_STREAM &&
|
|
stream_info->runtime_num_burst_capture <= 1) {
|
|
msm_isp_cfg_pong_address(vfe_dev, stream_info);
|
|
} else {
|
|
rc = msm_isp_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;
|
|
}
|
|
|
|
static void msm_isp_deinit_stream_ping_pong_reg(
|
|
struct vfe_device *vfe_dev,
|
|
struct msm_vfe_axi_stream *stream_info)
|
|
{
|
|
int i;
|
|
for (i = 0; i < 2; i++) {
|
|
struct msm_isp_buffer *buf;
|
|
buf = stream_info->buf[i];
|
|
if (buf)
|
|
vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
|
|
buf->bufq_handle, buf->buf_idx);
|
|
}
|
|
}
|
|
|
|
static void msm_isp_get_stream_wm_mask(
|
|
struct msm_vfe_axi_stream *stream_info,
|
|
uint32_t *wm_reload_mask)
|
|
{
|
|
int i;
|
|
for (i = 0; i < stream_info->num_planes; i++)
|
|
*wm_reload_mask |= (1 << stream_info->wm[i]);
|
|
}
|
|
|
|
#if defined(CONFIG_SEC_CONFIGURE_ISP_STOP)
|
|
|
|
static void msm_isp_axi_stream_update_new(struct vfe_device *vfe_dev, enum msm_isp_camif_update_state camif_update)
|
|
{
|
|
int i;
|
|
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
|
|
|
|
for (i = 0; i < MAX_NUM_STREAM; i++) {
|
|
if (axi_data->stream_info[i].state == START_PENDING || axi_data->stream_info[i].state == STOP_PENDING) {
|
|
msm_isp_axi_stream_enable_cfg(vfe_dev, &axi_data->stream_info[i]);
|
|
axi_data->stream_info[i].state = axi_data->stream_info[i].state == START_PENDING ? STARTING : STOPPING;
|
|
} else if (axi_data->stream_info[i].state == STARTING || axi_data->stream_info[i].state == STOPPING) {
|
|
axi_data->stream_info[i].state = axi_data->stream_info[i].state == STARTING ? ACTIVE : INACTIVE;
|
|
}
|
|
}
|
|
|
|
if (camif_update == DISABLE_CAMIF) {
|
|
vfe_dev->hw_info->vfe_ops.stats_ops.enable_module(vfe_dev, 0xFF, 0);
|
|
vfe_dev->axi_data.pipeline_update = NO_UPDATE;
|
|
}
|
|
|
|
}
|
|
#endif
|
|
static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev,
|
|
struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd,
|
|
enum msm_isp_camif_update_state camif_update)
|
|
{
|
|
int i, rc = 0;
|
|
uint8_t src_state, wait_for_complete = 0;
|
|
uint32_t wm_reload_mask = 0x0;
|
|
struct msm_vfe_axi_stream *stream_info;
|
|
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
|
|
|
|
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]) >= MAX_NUM_STREAM) {
|
|
return -EINVAL;
|
|
}
|
|
stream_info = &axi_data->stream_info[
|
|
HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
|
|
src_state = axi_data->src_info[
|
|
SRC_TO_INTF(stream_info->stream_src)].active;
|
|
|
|
msm_isp_calculate_bandwidth(axi_data, stream_info);
|
|
msm_isp_reset_framedrop(vfe_dev, stream_info);
|
|
msm_isp_get_stream_wm_mask(stream_info, &wm_reload_mask);
|
|
rc = msm_isp_init_stream_ping_pong_reg(vfe_dev, stream_info);
|
|
if (rc < 0) {
|
|
pr_err("%s: No buffer for stream%d\n", __func__,
|
|
HANDLE_TO_IDX(
|
|
stream_cfg_cmd->stream_handle[i]));
|
|
return rc;
|
|
}
|
|
|
|
stream_info->state = START_PENDING;
|
|
if (src_state) {
|
|
wait_for_complete = 1;
|
|
} else {
|
|
if (vfe_dev->dump_reg)
|
|
msm_camera_io_dump_2(vfe_dev->vfe_base, 0x900);
|
|
|
|
/*Configure AXI start bits to start immediately*/
|
|
msm_isp_axi_stream_enable_cfg(vfe_dev, stream_info);
|
|
stream_info->state = ACTIVE;
|
|
}
|
|
}
|
|
msm_isp_update_stream_bandwidth(vfe_dev);
|
|
vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(vfe_dev, wm_reload_mask);
|
|
vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev);
|
|
|
|
msm_isp_update_camif_output_count(vfe_dev, stream_cfg_cmd);
|
|
if (camif_update == ENABLE_CAMIF) {
|
|
vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id = 0;
|
|
vfe_dev->hw_info->vfe_ops.core_ops.
|
|
update_camif_state(vfe_dev, camif_update);
|
|
}
|
|
#if defined(CONFIG_SEC_CONFIGURE_ISP_STOP)
|
|
if (wait_for_complete){
|
|
msm_isp_axi_stream_update_new(vfe_dev, camif_update);
|
|
rc = msm_isp_axi_wait_for_cfg_done(vfe_dev, camif_update);
|
|
}
|
|
#else
|
|
if (wait_for_complete)
|
|
rc = msm_isp_axi_wait_for_cfg_done(vfe_dev, camif_update);
|
|
#endif
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int msm_isp_stop_axi_stream(struct vfe_device *vfe_dev,
|
|
struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd,
|
|
enum msm_isp_camif_update_state camif_update)
|
|
{
|
|
int i, rc = 0;
|
|
struct msm_vfe_axi_stream *stream_info;
|
|
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
|
|
|
|
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]) >= MAX_NUM_STREAM) {
|
|
return -EINVAL;
|
|
}
|
|
stream_info = &axi_data->stream_info[
|
|
HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
|
|
stream_info->state = STOP_PENDING;
|
|
}
|
|
#if defined(CONFIG_SEC_CONFIGURE_ISP_STOP)
|
|
pr_err("[richard] %s : state [%d], camif_update [%d]\n", __func__, stream_info->state, camif_update);
|
|
msm_isp_axi_stream_update_new(vfe_dev, DISABLE_CAMIF);
|
|
rc = msm_isp_axi_wait_for_cfg_done(vfe_dev, NO_UPDATE);
|
|
#else
|
|
rc = msm_isp_axi_wait_for_cfg_done(vfe_dev, camif_update);
|
|
#endif
|
|
if (rc < 0) {
|
|
pr_err("%s: wait for config done failed\n", __func__);
|
|
pr_err("%s:<DEBUG00>timeout:no frame from sensor\n", __func__);
|
|
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
|
|
stream_info = &axi_data->stream_info[
|
|
HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
|
|
stream_info->state = STOP_PENDING;
|
|
msm_isp_axi_stream_enable_cfg(
|
|
vfe_dev, stream_info);
|
|
stream_info->state = INACTIVE;
|
|
}
|
|
}
|
|
msm_isp_update_stream_bandwidth(vfe_dev);
|
|
|
|
if (camif_update == DISABLE_CAMIF)
|
|
vfe_dev->hw_info->vfe_ops.core_ops.
|
|
update_camif_state(vfe_dev, DISABLE_CAMIF);
|
|
else if (camif_update == DISABLE_CAMIF_IMMEDIATELY)
|
|
vfe_dev->hw_info->vfe_ops.core_ops.
|
|
update_camif_state(vfe_dev, DISABLE_CAMIF_IMMEDIATELY);
|
|
msm_isp_update_camif_output_count(vfe_dev, stream_cfg_cmd);
|
|
|
|
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
|
|
stream_info = &axi_data->stream_info[
|
|
HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
|
|
msm_isp_deinit_stream_ping_pong_reg(vfe_dev, stream_info);
|
|
}
|
|
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;
|
|
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
|
|
enum msm_isp_camif_update_state camif_update;
|
|
|
|
rc = msm_isp_axi_check_stream_state(vfe_dev, stream_cfg_cmd);
|
|
if (rc < 0) {
|
|
pr_err("%s: Invalid stream state\n", __func__);
|
|
return rc;
|
|
}
|
|
|
|
if (axi_data->num_active_stream == 0) {
|
|
/*Configure UB*/
|
|
vfe_dev->hw_info->vfe_ops.axi_ops.cfg_ub(vfe_dev);
|
|
}
|
|
camif_update = msm_isp_get_camif_update_state(vfe_dev, stream_cfg_cmd);
|
|
|
|
if (stream_cfg_cmd->cmd == START_STREAM)
|
|
rc = msm_isp_start_axi_stream(
|
|
vfe_dev, stream_cfg_cmd, camif_update);
|
|
else
|
|
rc = msm_isp_stop_axi_stream(
|
|
vfe_dev, stream_cfg_cmd, camif_update);
|
|
|
|
if (rc < 0)
|
|
pr_err("%s: start/stop stream failed\n", __func__);
|
|
return rc;
|
|
}
|
|
|
|
int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg)
|
|
{
|
|
int rc = 0, i, j;
|
|
struct msm_vfe_axi_stream *stream_info;
|
|
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
|
|
struct msm_vfe_axi_stream_update_cmd *update_cmd = arg;
|
|
struct msm_vfe_axi_stream_cfg_update_info *update_info;
|
|
|
|
if (update_cmd->update_type == UPDATE_STREAM_AXI_CONFIG &&
|
|
atomic_read(&axi_data->axi_cfg_update)) {
|
|
pr_err("%s: AXI stream config updating\n", __func__);
|
|
return -EBUSY;
|
|
}
|
|
|
|
/*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 = &update_cmd->update_info[i];
|
|
if (HANDLE_TO_IDX(update_info->stream_handle) >= MAX_NUM_STREAM) {
|
|
return -EINVAL;
|
|
}
|
|
stream_info = &axi_data->stream_info[
|
|
HANDLE_TO_IDX(update_info->stream_handle)];
|
|
if (stream_info->state != ACTIVE &&
|
|
stream_info->state != INACTIVE) {
|
|
pr_err("%s: Invalid stream state\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
if (stream_info->state == ACTIVE &&
|
|
stream_info->stream_type == BURST_STREAM) {
|
|
pr_err("%s: Cannot update active burst stream\n",
|
|
__func__);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < update_cmd->num_streams; i++) {
|
|
update_info = &update_cmd->update_info[i];
|
|
stream_info = &axi_data->stream_info[
|
|
HANDLE_TO_IDX(update_info->stream_handle)];
|
|
|
|
switch (update_cmd->update_type) {
|
|
case ENABLE_STREAM_BUF_DIVERT:
|
|
stream_info->buf_divert = 1;
|
|
break;
|
|
case DISABLE_STREAM_BUF_DIVERT:
|
|
stream_info->buf_divert = 0;
|
|
vfe_dev->buf_mgr->ops->flush_buf(vfe_dev->buf_mgr,
|
|
stream_info->bufq_handle,
|
|
MSM_ISP_BUFFER_FLUSH_DIVERTED);
|
|
break;
|
|
case UPDATE_STREAM_FRAMEDROP_PATTERN: {
|
|
uint32_t framedrop_period =
|
|
msm_isp_get_framedrop_period(
|
|
update_info->skip_pattern);
|
|
stream_info->runtime_init_frame_drop = 0;
|
|
stream_info->framedrop_pattern = 0x1;
|
|
stream_info->framedrop_period = framedrop_period - 1;
|
|
vfe_dev->hw_info->vfe_ops.axi_ops.
|
|
cfg_framedrop(vfe_dev, stream_info);
|
|
break;
|
|
}
|
|
case UPDATE_STREAM_AXI_CONFIG: {
|
|
for (j = 0; j < stream_info->num_planes; j++) {
|
|
stream_info->plane_cfg[j] =
|
|
update_info->plane_cfg[j];
|
|
}
|
|
stream_info->output_format = update_info->output_format;
|
|
if (stream_info->state == ACTIVE) {
|
|
stream_info->state = PAUSE_PENDING;
|
|
msm_isp_axi_stream_enable_cfg(
|
|
vfe_dev, stream_info);
|
|
stream_info->state = PAUSING;
|
|
atomic_set(&axi_data->axi_cfg_update,
|
|
UPDATE_REQUESTED);
|
|
} else {
|
|
for (j = 0; j < stream_info->num_planes; j++)
|
|
vfe_dev->hw_info->vfe_ops.axi_ops.
|
|
cfg_wm_reg(vfe_dev, stream_info, j);
|
|
stream_info->runtime_output_format =
|
|
stream_info->output_format;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
pr_err("%s: Invalid update type\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
void msm_isp_process_axi_irq(struct vfe_device *vfe_dev,
|
|
uint32_t irq_status0, uint32_t irq_status1,
|
|
struct msm_isp_timestamp *ts)
|
|
{
|
|
int i, rc = 0;
|
|
struct msm_isp_buffer *done_buf = NULL;
|
|
uint32_t comp_mask = 0, wm_mask = 0;
|
|
uint32_t pingpong_status, 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;
|
|
|
|
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);
|
|
pingpong_status =
|
|
vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(vfe_dev);
|
|
|
|
for (i = 0; i < axi_data->hw_info->num_comp_mask; i++) {
|
|
comp_info = &axi_data->composite_info[i];
|
|
if (comp_mask & (1 << i)) {
|
|
stream_idx = HANDLE_TO_IDX(comp_info->stream_handle);
|
|
if ((!comp_info->stream_handle) || (stream_idx >= MAX_NUM_STREAM)) {
|
|
pr_err("%s: Invalid handle for composite irq\n",
|
|
__func__);
|
|
} else {
|
|
stream_idx =
|
|
HANDLE_TO_IDX(comp_info->stream_handle);
|
|
stream_info =
|
|
&axi_data->stream_info[stream_idx];
|
|
ISP_DBG("%s: stream%d frame id: 0x%x\n",
|
|
__func__,
|
|
stream_idx, stream_info->frame_id);
|
|
stream_info->frame_id++;
|
|
|
|
if (stream_info->stream_type == BURST_STREAM)
|
|
stream_info->
|
|
runtime_num_burst_capture--;
|
|
|
|
msm_isp_get_done_buf(vfe_dev, stream_info,
|
|
pingpong_status, &done_buf);
|
|
if (stream_info->stream_type ==
|
|
CONTINUOUS_STREAM ||
|
|
stream_info->
|
|
runtime_num_burst_capture > 1) {
|
|
rc = msm_isp_cfg_ping_pong_address(
|
|
vfe_dev, stream_info,
|
|
pingpong_status);
|
|
}
|
|
if (done_buf && !rc)
|
|
msm_isp_process_done_buf(vfe_dev,
|
|
stream_info, done_buf, ts);
|
|
}
|
|
}
|
|
wm_mask &= ~(comp_info->stream_composite_mask);
|
|
}
|
|
|
|
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 >= MAX_NUM_STREAM)) {
|
|
pr_err("%s: Invalid handle for wm irq\n",
|
|
__func__);
|
|
continue;
|
|
}
|
|
stream_info = &axi_data->stream_info[stream_idx];
|
|
ISP_DBG("%s: stream%d frame id: 0x%x\n",
|
|
__func__,
|
|
stream_idx, stream_info->frame_id);
|
|
stream_info->frame_id++;
|
|
|
|
if (stream_info->stream_type == BURST_STREAM)
|
|
stream_info->runtime_num_burst_capture--;
|
|
|
|
msm_isp_get_done_buf(vfe_dev, stream_info,
|
|
pingpong_status, &done_buf);
|
|
if (stream_info->stream_type == CONTINUOUS_STREAM ||
|
|
stream_info->runtime_num_burst_capture > 1) {
|
|
rc = msm_isp_cfg_ping_pong_address(vfe_dev,
|
|
stream_info, pingpong_status);
|
|
}
|
|
if (done_buf && !rc)
|
|
msm_isp_process_done_buf(vfe_dev,
|
|
stream_info, done_buf, ts);
|
|
}
|
|
}
|
|
return;
|
|
}
|