blob: 057e87fab895534f88ffd2b81c25d4b7adcf7e86 [file] [log] [blame]
/* Copyright (c) 2013-2014, 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/mutex.h>
#include <linux/io.h>
#include <media/v4l2-subdev.h>
#include <linux/ratelimit.h>
#include "msm.h"
#include "msm_isp_util.h"
#include "msm_isp_axi_util.h"
#include "msm_isp_stats_util.h"
#include "msm_camera_io_util.h"
#define MAX_ISP_V4l2_EVENTS 100
static DEFINE_MUTEX(bandwidth_mgr_mutex);
static struct msm_isp_bandwidth_mgr isp_bandwidth_mgr;
#define MSM_ISP_MIN_AB 300000000
#define MSM_ISP_MIN_IB 450000000
#define VFE40_8974V2_VERSION 0x1001001A
static struct msm_bus_vectors msm_isp_init_vectors[] = {
{
.src = MSM_BUS_MASTER_VFE,
.dst = MSM_BUS_SLAVE_EBI_CH0,
.ab = 0,
.ib = 0,
},
};
static struct msm_bus_vectors msm_isp_ping_vectors[] = {
{
.src = MSM_BUS_MASTER_VFE,
.dst = MSM_BUS_SLAVE_EBI_CH0,
.ab = MSM_ISP_MIN_AB,
.ib = MSM_ISP_MIN_IB,
},
};
static struct msm_bus_vectors msm_isp_pong_vectors[] = {
{
.src = MSM_BUS_MASTER_VFE,
.dst = MSM_BUS_SLAVE_EBI_CH0,
.ab = MSM_ISP_MIN_AB,
.ib = MSM_ISP_MIN_IB,
},
};
static struct msm_bus_paths msm_isp_bus_client_config[] = {
{
ARRAY_SIZE(msm_isp_init_vectors),
msm_isp_init_vectors,
},
{
ARRAY_SIZE(msm_isp_ping_vectors),
msm_isp_ping_vectors,
},
{
ARRAY_SIZE(msm_isp_pong_vectors),
msm_isp_pong_vectors,
},
};
static struct msm_bus_scale_pdata msm_isp_bus_client_pdata = {
msm_isp_bus_client_config,
ARRAY_SIZE(msm_isp_bus_client_config),
.name = "msm_camera_isp",
};
static void msm_isp_print_fourcc_error(const char *origin,
uint32_t fourcc_format)
{
int i;
char text[5];
text[4] = '\0';
for (i = 0; i < 4; i++) {
text[i] = (char)(((fourcc_format) >> (i * 8)) & 0xFF);
if ((text[i] < '0') || (text[i] > 'z')) {
pr_err("%s: Invalid output format %d (unprintable)\n",
origin, fourcc_format);
return;
}
}
pr_err("%s: Invalid output format %s\n",
origin, text);
return;
}
int msm_isp_init_bandwidth_mgr(enum msm_isp_hw_client client)
{
int rc = 0;
mutex_lock(&bandwidth_mgr_mutex);
isp_bandwidth_mgr.client_info[client].active = 1;
if (isp_bandwidth_mgr.use_count++) {
mutex_unlock(&bandwidth_mgr_mutex);
return rc;
}
isp_bandwidth_mgr.bus_client =
msm_bus_scale_register_client(&msm_isp_bus_client_pdata);
if (!isp_bandwidth_mgr.bus_client) {
pr_err("%s: client register failed\n", __func__);
mutex_unlock(&bandwidth_mgr_mutex);
return -EINVAL;
}
isp_bandwidth_mgr.bus_vector_active_idx = 1;
msm_bus_scale_client_update_request(
isp_bandwidth_mgr.bus_client,
isp_bandwidth_mgr.bus_vector_active_idx);
mutex_unlock(&bandwidth_mgr_mutex);
return 0;
}
int msm_isp_update_bandwidth(enum msm_isp_hw_client client,
uint64_t ab, uint64_t ib)
{
int i;
struct msm_bus_paths *path;
mutex_lock(&bandwidth_mgr_mutex);
if (!isp_bandwidth_mgr.use_count ||
!isp_bandwidth_mgr.bus_client) {
pr_err("%s: bandwidth manager inactive\n", __func__);
return -EINVAL;
}
isp_bandwidth_mgr.client_info[client].ab = ab;
isp_bandwidth_mgr.client_info[client].ib = ib;
ALT_VECTOR_IDX(isp_bandwidth_mgr.bus_vector_active_idx);
path =
&(msm_isp_bus_client_pdata.usecase[
isp_bandwidth_mgr.bus_vector_active_idx]);
path->vectors[0].ab = MSM_ISP_MIN_AB;
path->vectors[0].ib = MSM_ISP_MIN_IB;
for (i = 0; i < MAX_ISP_CLIENT; i++) {
if (isp_bandwidth_mgr.client_info[i].active) {
path->vectors[0].ab +=
isp_bandwidth_mgr.client_info[i].ab;
path->vectors[0].ib +=
isp_bandwidth_mgr.client_info[i].ib;
}
}
msm_bus_scale_client_update_request(isp_bandwidth_mgr.bus_client,
isp_bandwidth_mgr.bus_vector_active_idx);
mutex_unlock(&bandwidth_mgr_mutex);
return 0;
}
void msm_isp_deinit_bandwidth_mgr(enum msm_isp_hw_client client)
{
mutex_lock(&bandwidth_mgr_mutex);
memset(&isp_bandwidth_mgr.client_info[client], 0,
sizeof(struct msm_isp_bandwidth_info));
if (--isp_bandwidth_mgr.use_count) {
mutex_unlock(&bandwidth_mgr_mutex);
return;
}
if (!isp_bandwidth_mgr.bus_client)
return;
msm_bus_scale_client_update_request(
isp_bandwidth_mgr.bus_client, 0);
msm_bus_scale_unregister_client(isp_bandwidth_mgr.bus_client);
isp_bandwidth_mgr.bus_client = 0;
mutex_unlock(&bandwidth_mgr_mutex);
}
uint32_t msm_isp_get_framedrop_period(
enum msm_vfe_frame_skip_pattern frame_skip_pattern)
{
switch (frame_skip_pattern) {
case NO_SKIP:
case EVERY_2FRAME:
case EVERY_3FRAME:
case EVERY_4FRAME:
case EVERY_5FRAME:
case EVERY_6FRAME:
case EVERY_7FRAME:
case EVERY_8FRAME:
return frame_skip_pattern + 1;
case EVERY_16FRAME:
return 16;
break;
case EVERY_32FRAME:
return 32;
break;
case SKIP_ALL:
return 1;
default:
return 1;
}
return 1;
}
static inline void msm_isp_get_timestamp(struct msm_isp_timestamp *time_stamp)
{
struct timespec ts;
ktime_get_ts(&ts);
time_stamp->buf_time.tv_sec = ts.tv_sec;
time_stamp->buf_time.tv_usec = ts.tv_nsec/1000;
do_gettimeofday(&(time_stamp->event_time));
}
static inline void msm_isp_get_vt_tstamp(struct vfe_device *vfe_dev,
struct msm_isp_timestamp *time_stamp)
{
uint32_t avtimer_msw_1st = 0, avtimer_lsw = 0;
uint32_t avtimer_msw_2nd = 0;
uint8_t iter = 0;
if (!vfe_dev->p_avtimer_msw || !vfe_dev->p_avtimer_lsw) {
pr_err("%s: ioremap failed\n", __func__);
return;
}
do {
avtimer_msw_1st = msm_camera_io_r(vfe_dev->p_avtimer_msw);
avtimer_lsw = msm_camera_io_r(vfe_dev->p_avtimer_lsw);
avtimer_msw_2nd = msm_camera_io_r(vfe_dev->p_avtimer_msw);
} while ((avtimer_msw_1st != avtimer_msw_2nd)
&& (iter++ < AVTIMER_ITERATION_CTR));
/*Just return if the MSW TimeStamps don't converge after
a few iterations Application needs to handle the zero TS values*/
if (iter >= AVTIMER_ITERATION_CTR) {
pr_err("%s: AVTimer MSW TS did not converge !!!\n", __func__);
return;
}
time_stamp->vt_time.tv_sec = avtimer_msw_1st;
time_stamp->vt_time.tv_usec = avtimer_lsw;
}
int msm_isp_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
int rc = 0;
rc = v4l2_event_subscribe(fh, sub, MAX_ISP_V4l2_EVENTS);
if (rc == 0) {
if (sub->type == V4L2_EVENT_ALL) {
int i;
vfe_dev->axi_data.event_mask = 0;
for (i = 0; i < ISP_EVENT_MAX; i++)
vfe_dev->axi_data.event_mask |= (1 << i);
} else {
int event_idx = sub->type - ISP_EVENT_BASE;
vfe_dev->axi_data.event_mask |= (1 << event_idx);
}
}
return rc;
}
int msm_isp_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
int rc = 0;
rc = v4l2_event_unsubscribe(fh, sub);
if (sub->type == V4L2_EVENT_ALL) {
vfe_dev->axi_data.event_mask = 0;
} else {
int event_idx = sub->type - ISP_EVENT_BASE;
vfe_dev->axi_data.event_mask &= ~(1 << event_idx);
}
return rc;
}
static int msm_isp_get_max_clk_rate(struct vfe_device *vfe_dev, long *rate)
{
int clk_idx = 0;
unsigned long max_value = ~0;
long round_rate = 0;
if (!vfe_dev || !rate) {
pr_err("%s:%d failed: vfe_dev %p rate %p\n", __func__, __LINE__,
vfe_dev, rate);
return -EINVAL;
}
*rate = 0;
if (!vfe_dev->hw_info) {
pr_err("%s:%d failed: vfe_dev->hw_info %p\n", __func__,
__LINE__, vfe_dev->hw_info);
return -EINVAL;
}
clk_idx = vfe_dev->hw_info->vfe_clk_idx;
if (clk_idx >= ARRAY_SIZE(vfe_dev->vfe_clk)) {
pr_err("%s:%d failed: clk_idx %d max array size %d\n",
__func__, __LINE__, clk_idx,
ARRAY_SIZE(vfe_dev->vfe_clk));
return -EINVAL;
}
round_rate = clk_round_rate(vfe_dev->vfe_clk[clk_idx], max_value);
if (round_rate < 0) {
pr_err("%s: Invalid vfe clock rate\n", __func__);
return -EINVAL;
}
*rate = round_rate;
return 0;
}
static int msm_isp_set_clk_rate(struct vfe_device *vfe_dev, long *rate)
{
int rc = 0;
int clk_idx = vfe_dev->hw_info->vfe_clk_idx;
long round_rate =
clk_round_rate(vfe_dev->vfe_clk[clk_idx], *rate);
if (round_rate < 0) {
pr_err("%s: Invalid vfe clock rate\n", __func__);
return round_rate;
}
rc = clk_set_rate(vfe_dev->vfe_clk[clk_idx], round_rate);
if (rc < 0) {
pr_err("%s: Vfe set rate error\n", __func__);
return rc;
}
*rate = round_rate;
return 0;
}
int msm_isp_cfg_pix(struct vfe_device *vfe_dev,
struct msm_vfe_input_cfg *input_cfg)
{
int rc = 0;
if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) {
pr_err("%s: pixel path is active\n", __func__);
return -EINVAL;
}
vfe_dev->axi_data.src_info[VFE_PIX_0].pixel_clock =
input_cfg->input_pix_clk;
vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux =
input_cfg->d.pix_cfg.input_mux;
vfe_dev->axi_data.src_info[VFE_PIX_0].width =
input_cfg->d.pix_cfg.camif_cfg.pixels_per_line;
rc = msm_isp_set_clk_rate(vfe_dev,
&vfe_dev->axi_data.src_info[VFE_PIX_0].pixel_clock);
if (rc < 0) {
pr_err("%s: clock set rate failed\n", __func__);
return rc;
}
vfe_dev->axi_data.src_info[VFE_PIX_0].input_format =
input_cfg->d.pix_cfg.input_format;
vfe_dev->hw_info->vfe_ops.core_ops.cfg_camif(
vfe_dev, &input_cfg->d.pix_cfg);
return rc;
}
int msm_isp_cfg_rdi(struct vfe_device *vfe_dev,
struct msm_vfe_input_cfg *input_cfg)
{
int rc = 0;
if (vfe_dev->axi_data.src_info[input_cfg->input_src].active) {
pr_err("%s: RAW%d path is active\n", __func__,
input_cfg->input_src - VFE_RAW_0);
return -EINVAL;
}
vfe_dev->axi_data.src_info[input_cfg->input_src].pixel_clock =
input_cfg->input_pix_clk;
vfe_dev->hw_info->vfe_ops.core_ops.cfg_rdi_reg(
vfe_dev, &input_cfg->d.rdi_cfg, input_cfg->input_src);
return rc;
}
int msm_isp_cfg_input(struct vfe_device *vfe_dev, void *arg)
{
int rc = 0;
struct msm_vfe_input_cfg *input_cfg = arg;
switch (input_cfg->input_src) {
case VFE_PIX_0:
rc = msm_isp_cfg_pix(vfe_dev, input_cfg);
break;
case VFE_RAW_0:
case VFE_RAW_1:
case VFE_RAW_2:
rc = msm_isp_cfg_rdi(vfe_dev, input_cfg);
break;
default:
pr_err("%s: Invalid input source\n", __func__);
rc = -EINVAL;
}
return rc;
}
long msm_isp_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
long rc = 0;
struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
/* Use real time mutex for hard real-time ioctls such as
* buffer operations and register updates.
* Use core mutex for other ioctls that could take
* longer time to complete such as start/stop ISP streams
* which blocks until the hardware start/stop streaming
*/
ISP_DBG("%s cmd: %d\n", __func__, _IOC_TYPE(cmd));
switch (cmd) {
case VIDIOC_MSM_VFE_REG_CFG: {
mutex_lock(&vfe_dev->realtime_mutex);
rc = msm_isp_proc_cmd(vfe_dev, arg);
mutex_unlock(&vfe_dev->realtime_mutex);
break;
}
case VIDIOC_MSM_ISP_REQUEST_BUF:
case VIDIOC_MSM_ISP_ENQUEUE_BUF:
case VIDIOC_MSM_ISP_RELEASE_BUF: {
mutex_lock(&vfe_dev->realtime_mutex);
rc = msm_isp_proc_buf_cmd(vfe_dev->buf_mgr, cmd, arg);
mutex_unlock(&vfe_dev->realtime_mutex);
break;
}
case VIDIOC_MSM_ISP_REQUEST_STREAM:
mutex_lock(&vfe_dev->core_mutex);
rc = msm_isp_request_axi_stream(vfe_dev, arg);
mutex_unlock(&vfe_dev->core_mutex);
break;
case VIDIOC_MSM_ISP_RELEASE_STREAM:
mutex_lock(&vfe_dev->core_mutex);
rc = msm_isp_release_axi_stream(vfe_dev, arg);
mutex_unlock(&vfe_dev->core_mutex);
break;
case VIDIOC_MSM_ISP_CFG_STREAM:
mutex_lock(&vfe_dev->core_mutex);
rc = msm_isp_cfg_axi_stream(vfe_dev, arg);
mutex_unlock(&vfe_dev->core_mutex);
break;
case VIDIOC_MSM_ISP_INPUT_CFG:
mutex_lock(&vfe_dev->core_mutex);
rc = msm_isp_cfg_input(vfe_dev, arg);
mutex_unlock(&vfe_dev->core_mutex);
break;
case VIDIOC_MSM_ISP_SET_SRC_STATE:
mutex_lock(&vfe_dev->core_mutex);
rc = msm_isp_set_src_state(vfe_dev, arg);
mutex_unlock(&vfe_dev->core_mutex);
break;
case VIDIOC_MSM_ISP_REQUEST_STATS_STREAM:
mutex_lock(&vfe_dev->core_mutex);
rc = msm_isp_request_stats_stream(vfe_dev, arg);
mutex_unlock(&vfe_dev->core_mutex);
break;
case VIDIOC_MSM_ISP_RELEASE_STATS_STREAM:
mutex_lock(&vfe_dev->core_mutex);
rc = msm_isp_release_stats_stream(vfe_dev, arg);
mutex_unlock(&vfe_dev->core_mutex);
break;
case VIDIOC_MSM_ISP_CFG_STATS_STREAM:
mutex_lock(&vfe_dev->core_mutex);
rc = msm_isp_cfg_stats_stream(vfe_dev, arg);
mutex_unlock(&vfe_dev->core_mutex);
break;
case VIDIOC_MSM_ISP_UPDATE_STREAM:
mutex_lock(&vfe_dev->core_mutex);
rc = msm_isp_update_axi_stream(vfe_dev, arg);
mutex_unlock(&vfe_dev->core_mutex);
break;
case MSM_SD_SHUTDOWN:
while (vfe_dev->vfe_open_cnt != 0)
msm_isp_close_node(sd, NULL);
break;
default:
pr_err_ratelimited("%s: Invalid ISP command\n", __func__);
rc = -EINVAL;
}
return rc;
}
static int msm_isp_send_hw_cmd(struct vfe_device *vfe_dev,
struct msm_vfe_reg_cfg_cmd *reg_cfg_cmd,
uint32_t *cfg_data, uint32_t cmd_len)
{
switch (reg_cfg_cmd->cmd_type) {
case VFE_WRITE: {
if (resource_size(vfe_dev->vfe_mem) <
(reg_cfg_cmd->u.rw_info.reg_offset +
reg_cfg_cmd->u.rw_info.len)) {
pr_err("%s: VFE_WRITE: Invalid length\n", __func__);
return -EINVAL;
}
msm_camera_io_memcpy(vfe_dev->vfe_base +
reg_cfg_cmd->u.rw_info.reg_offset,
cfg_data + reg_cfg_cmd->u.rw_info.cmd_data_offset/4,
reg_cfg_cmd->u.rw_info.len);
break;
}
case VFE_WRITE_MB: {
uint32_t *data_ptr = cfg_data +
reg_cfg_cmd->u.rw_info.cmd_data_offset/4;
if ((UINT_MAX - sizeof(*data_ptr) <
reg_cfg_cmd->u.rw_info.reg_offset) ||
(resource_size(vfe_dev->vfe_mem) <
reg_cfg_cmd->u.rw_info.reg_offset +
sizeof(*data_ptr))) {
pr_err("%s: VFE_WRITE_MB: Invalid length\n", __func__);
return -EINVAL;
}
msm_camera_io_w_mb(*data_ptr, vfe_dev->vfe_base +
reg_cfg_cmd->u.rw_info.reg_offset);
break;
}
case VFE_CFG_MASK: {
uint32_t temp;
if (resource_size(vfe_dev->vfe_mem) <
reg_cfg_cmd->u.mask_info.reg_offset)
return -EINVAL;
temp = msm_camera_io_r(vfe_dev->vfe_base +
reg_cfg_cmd->u.mask_info.reg_offset);
temp &= ~reg_cfg_cmd->u.mask_info.mask;
temp |= reg_cfg_cmd->u.mask_info.val;
if ((UINT_MAX - sizeof(temp) <
reg_cfg_cmd->u.mask_info.reg_offset) ||
(resource_size(vfe_dev->vfe_mem) <
reg_cfg_cmd->u.mask_info.reg_offset +
sizeof(temp))) {
pr_err("%s: VFE_CFG_MASK: Invalid length\n", __func__);
return -EINVAL;
}
msm_camera_io_w(temp, vfe_dev->vfe_base +
reg_cfg_cmd->u.mask_info.reg_offset);
break;
}
case VFE_WRITE_DMI_16BIT:
case VFE_WRITE_DMI_32BIT:
case VFE_WRITE_DMI_64BIT: {
int i;
uint32_t *hi_tbl_ptr = NULL, *lo_tbl_ptr = NULL;
uint32_t hi_val, lo_val, lo_val1;
if (reg_cfg_cmd->cmd_type == VFE_WRITE_DMI_64BIT) {
if ((UINT_MAX - reg_cfg_cmd->u.dmi_info.hi_tbl_offset <
reg_cfg_cmd->u.dmi_info.len) ||
(reg_cfg_cmd->u.dmi_info.hi_tbl_offset +
reg_cfg_cmd->u.dmi_info.len > cmd_len)) {
pr_err("Invalid Hi Table out of bounds\n");
return -EINVAL;
}
hi_tbl_ptr = cfg_data +
reg_cfg_cmd->u.dmi_info.hi_tbl_offset/4;
}
if (reg_cfg_cmd->u.dmi_info.lo_tbl_offset +
reg_cfg_cmd->u.dmi_info.len > cmd_len) {
pr_err("Invalid Lo Table out of bounds\n");
return -EINVAL;
}
lo_tbl_ptr = cfg_data +
reg_cfg_cmd->u.dmi_info.lo_tbl_offset/4;
if (reg_cfg_cmd->cmd_type == VFE_WRITE_DMI_64BIT)
reg_cfg_cmd->u.dmi_info.len =
reg_cfg_cmd->u.dmi_info.len / 2;
for (i = 0; i < reg_cfg_cmd->u.dmi_info.len/4; i++) {
lo_val = *lo_tbl_ptr++;
if (reg_cfg_cmd->cmd_type == VFE_WRITE_DMI_16BIT) {
lo_val1 = lo_val & 0x0000FFFF;
lo_val = (lo_val & 0xFFFF0000)>>16;
msm_camera_io_w(lo_val1, vfe_dev->vfe_base +
vfe_dev->hw_info->dmi_reg_offset + 0x4);
} else if (reg_cfg_cmd->cmd_type ==
VFE_WRITE_DMI_64BIT) {
lo_tbl_ptr++;
hi_val = *hi_tbl_ptr;
hi_tbl_ptr = hi_tbl_ptr + 2;
msm_camera_io_w(hi_val, vfe_dev->vfe_base +
vfe_dev->hw_info->dmi_reg_offset);
}
msm_camera_io_w(lo_val, vfe_dev->vfe_base +
vfe_dev->hw_info->dmi_reg_offset + 0x4);
}
break;
}
case VFE_READ_DMI_16BIT:
case VFE_READ_DMI_32BIT:
case VFE_READ_DMI_64BIT: {
int i;
uint32_t *hi_tbl_ptr = NULL, *lo_tbl_ptr = NULL;
uint32_t hi_val, lo_val, lo_val1;
if (reg_cfg_cmd->cmd_type == VFE_READ_DMI_64BIT) {
if (reg_cfg_cmd->u.dmi_info.hi_tbl_offset +
reg_cfg_cmd->u.dmi_info.len > cmd_len) {
pr_err("Invalid Hi Table out of bounds\n");
return -EINVAL;
}
hi_tbl_ptr = cfg_data +
reg_cfg_cmd->u.dmi_info.hi_tbl_offset/4;
}
if (reg_cfg_cmd->u.dmi_info.lo_tbl_offset +
reg_cfg_cmd->u.dmi_info.len > cmd_len) {
pr_err("Invalid Lo Table out of bounds\n");
return -EINVAL;
}
lo_tbl_ptr = cfg_data +
reg_cfg_cmd->u.dmi_info.lo_tbl_offset/4;
for (i = 0; i < reg_cfg_cmd->u.dmi_info.len/4; i++) {
if (reg_cfg_cmd->cmd_type == VFE_READ_DMI_64BIT) {
hi_val = msm_camera_io_r(vfe_dev->vfe_base +
vfe_dev->hw_info->dmi_reg_offset);
*hi_tbl_ptr++ = hi_val;
}
lo_val = msm_camera_io_r(vfe_dev->vfe_base +
vfe_dev->hw_info->dmi_reg_offset + 0x4);
if (reg_cfg_cmd->cmd_type == VFE_READ_DMI_16BIT) {
lo_val1 = msm_camera_io_r(vfe_dev->vfe_base +
vfe_dev->hw_info->dmi_reg_offset + 0x4);
lo_val |= lo_val1 << 16;
}
*lo_tbl_ptr++ = lo_val;
}
break;
}
case VFE_READ: {
int i;
uint32_t *data_ptr = cfg_data +
reg_cfg_cmd->u.rw_info.cmd_data_offset/4;
for (i = 0; i < reg_cfg_cmd->u.rw_info.len/4; i++) {
if ((data_ptr < cfg_data) ||
(UINT_MAX / sizeof(*data_ptr) <
(data_ptr - cfg_data)) ||
(sizeof(*data_ptr) * (data_ptr - cfg_data) >
cmd_len))
return -EINVAL;
*data_ptr++ = msm_camera_io_r(vfe_dev->vfe_base +
reg_cfg_cmd->u.rw_info.reg_offset);
reg_cfg_cmd->u.rw_info.reg_offset += 4;
}
break;
}
case GET_SOC_HW_VER:
*cfg_data = vfe_dev->soc_hw_version;
break;
case GET_MAX_CLK_RATE: {
int rc = 0;
if (cmd_len < sizeof(unsigned long)) {
pr_err("%s:%d failed: invalid cmd len %d exp %d\n",
__func__, __LINE__, cmd_len,
sizeof(unsigned long));
return -EINVAL;
}
rc = msm_isp_get_max_clk_rate(vfe_dev,
(unsigned long *)cfg_data);
if (rc < 0) {
pr_err("%s:%d failed: rc %d\n", __func__, __LINE__, rc);
return -EINVAL;
}
break;
}
}
return 0;
}
int msm_isp_proc_cmd(struct vfe_device *vfe_dev, void *arg)
{
int rc = 0, i;
struct msm_vfe_cfg_cmd2 *proc_cmd = arg;
struct msm_vfe_reg_cfg_cmd *reg_cfg_cmd;
uint32_t *cfg_data;
if (!proc_cmd->num_cfg) {
pr_err("%s: Passed num_cfg as 0\n", __func__);
return -EINVAL;
}
reg_cfg_cmd = kzalloc(sizeof(struct msm_vfe_reg_cfg_cmd)*
proc_cmd->num_cfg, GFP_KERNEL);
if (!reg_cfg_cmd) {
pr_err("%s: reg_cfg alloc failed\n", __func__);
rc = -ENOMEM;
goto reg_cfg_failed;
}
if (!proc_cmd->cmd_len) {
pr_err("%s: Passed cmd_len as 0\n", __func__);
rc = -EINVAL;
goto cfg_data_failed;
}
cfg_data = kzalloc(proc_cmd->cmd_len, GFP_KERNEL);
if (!cfg_data) {
pr_err("%s: cfg_data alloc failed\n", __func__);
rc = -ENOMEM;
goto cfg_data_failed;
}
if (copy_from_user(reg_cfg_cmd,
(void __user *)(proc_cmd->cfg_cmd),
sizeof(struct msm_vfe_reg_cfg_cmd) * proc_cmd->num_cfg)) {
rc = -EFAULT;
goto copy_cmd_failed;
}
if (copy_from_user(cfg_data,
(void __user *)(proc_cmd->cfg_data),
proc_cmd->cmd_len)) {
rc = -EFAULT;
goto copy_cmd_failed;
}
for (i = 0; i < proc_cmd->num_cfg; i++)
msm_isp_send_hw_cmd(vfe_dev, &reg_cfg_cmd[i],
cfg_data, proc_cmd->cmd_len);
if (copy_to_user(proc_cmd->cfg_data,
cfg_data, proc_cmd->cmd_len)) {
rc = -EFAULT;
goto copy_cmd_failed;
}
copy_cmd_failed:
kfree(cfg_data);
cfg_data_failed:
kfree(reg_cfg_cmd);
reg_cfg_failed:
return rc;
}
int msm_isp_send_event(struct vfe_device *vfe_dev,
uint32_t event_type,
struct msm_isp_event_data *event_data)
{
struct v4l2_event isp_event;
memset(&isp_event, 0, sizeof(struct v4l2_event));
isp_event.id = 0;
isp_event.type = event_type;
memcpy(&isp_event.u.data[0], event_data,
sizeof(struct msm_isp_event_data));
v4l2_event_queue(vfe_dev->subdev.sd.devnode, &isp_event);
return 0;
}
#define CAL_WORD(width, M, N) ((width * M + N - 1) / N)
int msm_isp_cal_word_per_line(uint32_t output_format,
uint32_t pixel_per_line)
{
int val = -1;
switch (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:
val = CAL_WORD(pixel_per_line, 1, 8);
break;
case V4L2_PIX_FMT_SBGGR10:
case V4L2_PIX_FMT_SGBRG10:
case V4L2_PIX_FMT_SGRBG10:
case V4L2_PIX_FMT_SRGGB10:
val = CAL_WORD(pixel_per_line, 5, 32);
break;
case V4L2_PIX_FMT_SBGGR12:
case V4L2_PIX_FMT_SGBRG12:
case V4L2_PIX_FMT_SGRBG12:
case V4L2_PIX_FMT_SRGGB12:
val = CAL_WORD(pixel_per_line, 3, 16);
break;
case V4L2_PIX_FMT_QBGGR10:
case V4L2_PIX_FMT_QGBRG10:
case V4L2_PIX_FMT_QGRBG10:
case V4L2_PIX_FMT_QRGGB10:
val = CAL_WORD(pixel_per_line, 1, 6);
break;
case V4L2_PIX_FMT_QBGGR12:
case V4L2_PIX_FMT_QGBRG12:
case V4L2_PIX_FMT_QGRBG12:
case V4L2_PIX_FMT_QRGGB12:
val = CAL_WORD(pixel_per_line, 1, 5);
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:
val = CAL_WORD(pixel_per_line, 1, 8);
break;
/*TD: Add more image format*/
default:
msm_isp_print_fourcc_error(__func__, output_format);
break;
}
return val;
}
enum msm_isp_pack_fmt msm_isp_get_pack_format(uint32_t output_format)
{
switch (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:
return MIPI;
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:
return QCOM;
default:
msm_isp_print_fourcc_error(__func__, output_format);
break;
}
return -EINVAL;
}
int msm_isp_get_bit_per_pixel(uint32_t output_format)
{
switch (output_format) {
case V4L2_PIX_FMT_Y4:
return 4;
case V4L2_PIX_FMT_Y6:
return 6;
case V4L2_PIX_FMT_SBGGR8:
case V4L2_PIX_FMT_SGBRG8:
case V4L2_PIX_FMT_SGRBG8:
case V4L2_PIX_FMT_SRGGB8:
case V4L2_PIX_FMT_QBGGR8:
case V4L2_PIX_FMT_QGBRG8:
case V4L2_PIX_FMT_QGRBG8:
case V4L2_PIX_FMT_QRGGB8:
case V4L2_PIX_FMT_JPEG:
case V4L2_PIX_FMT_META:
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV14:
case V4L2_PIX_FMT_NV41:
case V4L2_PIX_FMT_YVU410:
case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_YYUV:
case V4L2_PIX_FMT_YVYU:
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_VYUY:
case V4L2_PIX_FMT_YUV422P:
case V4L2_PIX_FMT_YUV411P:
case V4L2_PIX_FMT_Y41P:
case V4L2_PIX_FMT_YUV444:
case V4L2_PIX_FMT_YUV555:
case V4L2_PIX_FMT_YUV565:
case V4L2_PIX_FMT_YUV32:
case V4L2_PIX_FMT_YUV410:
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_GREY:
case V4L2_PIX_FMT_PAL8:
case MSM_V4L2_PIX_FMT_META:
return 8;
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:
case V4L2_PIX_FMT_Y10:
case V4L2_PIX_FMT_Y10BPACK:
return 10;
case V4L2_PIX_FMT_SBGGR12:
case V4L2_PIX_FMT_SGBRG12:
case V4L2_PIX_FMT_SGRBG12:
case V4L2_PIX_FMT_SRGGB12:
case V4L2_PIX_FMT_QBGGR12:
case V4L2_PIX_FMT_QGBRG12:
case V4L2_PIX_FMT_QGRBG12:
case V4L2_PIX_FMT_QRGGB12:
case V4L2_PIX_FMT_Y12:
return 12;
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
case V4L2_PIX_FMT_Y16:
return 16;
/*TD: Add more image format*/
default:
msm_isp_print_fourcc_error(__func__, output_format);
return -EINVAL;
}
}
void msm_isp_update_error_frame_count(struct vfe_device *vfe_dev)
{
struct msm_vfe_error_info *error_info = &vfe_dev->error_info;
error_info->info_dump_frame_count++;
if (error_info->info_dump_frame_count == 0)
error_info->info_dump_frame_count++;
}
void msm_isp_process_error_info(struct vfe_device *vfe_dev)
{
int i;
uint8_t num_stats_type =
vfe_dev->hw_info->stats_hw_info->num_stats_type;
struct msm_vfe_error_info *error_info = &vfe_dev->error_info;
static DEFINE_RATELIMIT_STATE(rs,
DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);
static DEFINE_RATELIMIT_STATE(rs_stats,
DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);
if (error_info->error_count == 1 ||
!(error_info->info_dump_frame_count % 100)) {
vfe_dev->hw_info->vfe_ops.core_ops.
process_error_status(vfe_dev);
error_info->error_mask0 = 0;
error_info->error_mask1 = 0;
error_info->camif_status = 0;
error_info->violation_status = 0;
for (i = 0; i < MAX_NUM_STREAM; i++) {
if (error_info->stream_framedrop_count[i] != 0 &&
__ratelimit(&rs)) {
pr_err("%s: Stream[%d]: dropped %d frames\n",
__func__, i,
error_info->stream_framedrop_count[i]);
error_info->stream_framedrop_count[i] = 0;
}
}
for (i = 0; i < num_stats_type; i++) {
if (error_info->stats_framedrop_count[i] != 0 &&
__ratelimit(&rs_stats)) {
pr_err("%s: Stats stream[%d]: dropped %d frames\n",
__func__, i,
error_info->stats_framedrop_count[i]);
error_info->stats_framedrop_count[i] = 0;
}
}
}
}
static inline void msm_isp_update_error_info(struct vfe_device *vfe_dev,
uint32_t error_mask0, uint32_t error_mask1)
{
vfe_dev->error_info.error_mask0 |= error_mask0;
vfe_dev->error_info.error_mask1 |= error_mask1;
vfe_dev->error_info.error_count++;
}
irqreturn_t msm_isp_process_irq(int irq_num, void *data)
{
unsigned long flags;
struct msm_vfe_tasklet_queue_cmd *queue_cmd;
struct vfe_device *vfe_dev = (struct vfe_device *) data;
uint32_t irq_status0, irq_status1;
uint32_t error_mask0, error_mask1;
vfe_dev->hw_info->vfe_ops.irq_ops.
read_irq_status(vfe_dev, &irq_status0, &irq_status1);
vfe_dev->hw_info->vfe_ops.core_ops.
get_error_mask(&error_mask0, &error_mask1);
error_mask0 &= irq_status0;
error_mask1 &= irq_status1;
irq_status0 &= ~error_mask0;
irq_status1 &= ~error_mask1;
if ((error_mask0 != 0) || (error_mask1 != 0))
msm_isp_update_error_info(vfe_dev, error_mask0, error_mask1);
if ((irq_status0 == 0) && (irq_status1 == 0) &&
(!((error_mask0 != 0) || (error_mask1 != 0)) &&
vfe_dev->error_info.error_count == 1)) {
ISP_DBG("%s: irq_status0 & 1 are both 0!\n", __func__);
return IRQ_HANDLED;
}
spin_lock_irqsave(&vfe_dev->tasklet_lock, flags);
queue_cmd = &vfe_dev->tasklet_queue_cmd[vfe_dev->taskletq_idx];
if (queue_cmd->cmd_used) {
pr_err_ratelimited("%s: Tasklet queue overflow: %d\n",
__func__, vfe_dev->pdev->id);
list_del(&queue_cmd->list);
} else {
atomic_add(1, &vfe_dev->irq_cnt);
}
queue_cmd->vfeInterruptStatus0 = irq_status0;
queue_cmd->vfeInterruptStatus1 = irq_status1;
msm_isp_get_timestamp(&queue_cmd->ts);
if (vfe_dev->vt_enable)
msm_isp_get_vt_tstamp(vfe_dev, &queue_cmd->ts);
queue_cmd->cmd_used = 1;
vfe_dev->taskletq_idx =
(vfe_dev->taskletq_idx + 1) % MSM_VFE_TASKLETQ_SIZE;
list_add_tail(&queue_cmd->list, &vfe_dev->tasklet_q);
spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags);
tasklet_schedule(&vfe_dev->vfe_tasklet);
return IRQ_HANDLED;
}
void msm_isp_do_tasklet(unsigned long data)
{
unsigned long flags;
struct vfe_device *vfe_dev = (struct vfe_device *) data;
struct msm_vfe_irq_ops *irq_ops = &vfe_dev->hw_info->vfe_ops.irq_ops;
struct msm_vfe_tasklet_queue_cmd *queue_cmd;
struct msm_isp_timestamp ts;
uint32_t irq_status0, irq_status1;
while (atomic_read(&vfe_dev->irq_cnt)) {
spin_lock_irqsave(&vfe_dev->tasklet_lock, flags);
queue_cmd = list_first_entry(&vfe_dev->tasklet_q,
struct msm_vfe_tasklet_queue_cmd, list);
if (!queue_cmd) {
atomic_set(&vfe_dev->irq_cnt, 0);
spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags);
return;
}
atomic_sub(1, &vfe_dev->irq_cnt);
list_del(&queue_cmd->list);
queue_cmd->cmd_used = 0;
irq_status0 = queue_cmd->vfeInterruptStatus0;
irq_status1 = queue_cmd->vfeInterruptStatus1;
ts = queue_cmd->ts;
spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags);
ISP_DBG("%s: status0: 0x%x status1: 0x%x\n",
__func__, irq_status0, irq_status1);
irq_ops->process_reset_irq(vfe_dev,
irq_status0, irq_status1);
irq_ops->process_halt_irq(vfe_dev,
irq_status0, irq_status1);
irq_ops->process_camif_irq(vfe_dev,
irq_status0, irq_status1, &ts);
irq_ops->process_axi_irq(vfe_dev,
irq_status0, irq_status1, &ts);
irq_ops->process_stats_irq(vfe_dev,
irq_status0, irq_status1, &ts);
irq_ops->process_reg_update(vfe_dev,
irq_status0, irq_status1, &ts);
msm_isp_process_error_info(vfe_dev);
}
}
int msm_isp_set_src_state(struct vfe_device *vfe_dev, void *arg)
{
struct msm_vfe_axi_src_state *src_state = arg;
if (src_state->input_src >= VFE_SRC_MAX)
return -EINVAL;
vfe_dev->axi_data.src_info[src_state->input_src].active =
src_state->src_active;
return 0;
}
int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
long rc;
ISP_DBG("%s\n", __func__);
mutex_lock(&vfe_dev->realtime_mutex);
mutex_lock(&vfe_dev->core_mutex);
if (vfe_dev->vfe_open_cnt == 1) {
pr_err("VFE already open\n");
mutex_unlock(&vfe_dev->core_mutex);
mutex_unlock(&vfe_dev->realtime_mutex);
return -ENODEV;
}
if (vfe_dev->hw_info->vfe_ops.core_ops.init_hw(vfe_dev) < 0) {
pr_err("%s: init hardware failed\n", __func__);
mutex_unlock(&vfe_dev->core_mutex);
mutex_unlock(&vfe_dev->realtime_mutex);
return -EBUSY;
}
rc = vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, ISP_RST_HARD);
if (rc <= 0) {
pr_err("%s: reset timeout\n", __func__);
mutex_unlock(&vfe_dev->core_mutex);
mutex_unlock(&vfe_dev->realtime_mutex);
return -EINVAL;
}
vfe_dev->vfe_hw_version = msm_camera_io_r(vfe_dev->vfe_base);
ISP_DBG("%s: HW Version: 0x%x\n", __func__, vfe_dev->vfe_hw_version);
vfe_dev->hw_info->vfe_ops.core_ops.init_hw_reg(vfe_dev);
vfe_dev->buf_mgr->ops->buf_mgr_init(vfe_dev->buf_mgr, "msm_isp", 28);
switch (vfe_dev->vfe_hw_version) {
case VFE40_8974V2_VERSION:
vfe_dev->soc_hw_version = msm_camera_io_r(vfe_dev->tcsr_base);
break;
default:
/* SOC HARDWARE VERSION NOT SUPPORTED */
vfe_dev->soc_hw_version = 0x00;
}
memset(&vfe_dev->axi_data, 0, sizeof(struct msm_vfe_axi_shared_data));
memset(&vfe_dev->stats_data, 0,
sizeof(struct msm_vfe_stats_shared_data));
memset(&vfe_dev->error_info, 0, sizeof(vfe_dev->error_info));
vfe_dev->axi_data.hw_info = vfe_dev->hw_info->axi_hw_info;
vfe_dev->vfe_open_cnt++;
vfe_dev->taskletq_idx = 0;
vfe_dev->vt_enable = 0;
vfe_dev->p_avtimer_lsw = NULL;
vfe_dev->p_avtimer_msw = NULL;
mutex_unlock(&vfe_dev->core_mutex);
mutex_unlock(&vfe_dev->realtime_mutex);
return 0;
}
int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
long rc;
struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
ISP_DBG("%s\n", __func__);
mutex_lock(&vfe_dev->realtime_mutex);
mutex_lock(&vfe_dev->core_mutex);
if (vfe_dev->vfe_open_cnt == 0) {
pr_err("%s: Invalid close\n", __func__);
mutex_unlock(&vfe_dev->core_mutex);
mutex_unlock(&vfe_dev->realtime_mutex);
return -ENODEV;
}
rc = vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev);
if (rc <= 0)
pr_err("%s: halt timeout rc=%ld\n", __func__, rc);
vfe_dev->buf_mgr->ops->buf_mgr_deinit(vfe_dev->buf_mgr);
vfe_dev->hw_info->vfe_ops.core_ops.release_hw(vfe_dev);
vfe_dev->vfe_open_cnt--;
if (vfe_dev->vt_enable) {
iounmap(vfe_dev->p_avtimer_lsw);
iounmap(vfe_dev->p_avtimer_msw);
#ifdef CONFIG_MSM_AVTIMER
avcs_core_disable_power_collapse(0);
#endif
vfe_dev->vt_enable = 0;
}
mutex_unlock(&vfe_dev->core_mutex);
mutex_unlock(&vfe_dev->realtime_mutex);
return 0;
}