// SPDX-License-Identifier: GPL-2.0
|
/* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd. */
|
|
#include <linux/delay.h>
|
#include <linux/pm_runtime.h>
|
#include <linux/slab.h>
|
#include <media/v4l2-common.h>
|
#include <media/v4l2-event.h>
|
#include <media/v4l2-fh.h>
|
#include <media/v4l2-ioctl.h>
|
#include <media/v4l2-subdev.h>
|
#include <media/videobuf2-dma-contig.h>
|
#include <linux/dma-iommu.h>
|
#include <linux/rk-camera-module.h>
|
#include "dev.h"
|
#include "regs.h"
|
|
static inline
|
struct rkisp_bridge_buf *to_bridge_buf(struct rkisp_ispp_buf *dbufs)
|
{
|
return container_of(dbufs, struct rkisp_bridge_buf, dbufs);
|
}
|
|
/* compatible with MI frame end are triggered before ISP frame end */
|
static void reg_buf_wait_for_stats(struct rkisp_bridge_device *dev,
|
struct rkisp_ispp_reg *reg_buf,
|
struct rkisp_isp2x_stat_buffer *tmp_statsbuf)
|
{
|
s32 retry = 10;
|
|
do {
|
if (reg_buf->frame_id > tmp_statsbuf->frame_id)
|
usleep_range(1000, 1200);
|
else
|
break;
|
} while (retry-- > 0);
|
|
if (retry < 0)
|
v4l2_err(&dev->sd, "reg id(%d) don't match stats id(%d)\n",
|
reg_buf->frame_id, tmp_statsbuf->frame_id);
|
}
|
|
static void dump_dbg_reg(struct rkisp_bridge_device *dev, struct rkisp_ispp_reg *reg_buf)
|
{
|
struct rkisp_isp2x_stat_buffer *tmp_statsbuf;
|
struct rkisp_hw_dev *hw = dev->ispdev->hw_dev;
|
u32 offset = 0, size;
|
|
tmp_statsbuf = (struct rkisp_isp2x_stat_buffer *)dev->ispdev->stats_vdev.tmp_statsbuf.vaddr;
|
reg_buf_wait_for_stats(dev, reg_buf, tmp_statsbuf);
|
memset(reg_buf->isp_offset, -1, sizeof(reg_buf->isp_offset));
|
memset(reg_buf->ispp_offset, -1, sizeof(reg_buf->ispp_offset));
|
memset(reg_buf->isp_size, 0, sizeof(reg_buf->isp_offset));
|
memset(reg_buf->isp_stats_size, 0, sizeof(reg_buf->isp_offset));
|
memset(reg_buf->ispp_size, 0, sizeof(reg_buf->ispp_offset));
|
if (rkisp_debug_reg & ISP2X_MODULE_DPCC) {
|
size = 4 + ISP_DPCC0_PDAF_FORWARD_MED - ISP_DPCC0_MODE;
|
reg_buf->isp_size[ISP2X_ID_DPCC] = size;
|
reg_buf->isp_offset[ISP2X_ID_DPCC] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_DPCC0_MODE, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_BLS) {
|
size = 4 + ISP_BLS_D_MEASURED - ISP_BLS_CTRL;
|
reg_buf->isp_size[ISP2X_ID_BLS] = size;
|
reg_buf->isp_offset[ISP2X_ID_BLS] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_BLS_CTRL, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_SDG) {
|
size = 4 + ISP_GAMMA_B_Y_16 - ISP_GAMMA_DX_LO;
|
reg_buf->isp_size[ISP2X_ID_SDG] = size;
|
reg_buf->isp_offset[ISP2X_ID_SDG] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_GAMMA_DX_LO, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_SIHST) {
|
size = 4 + ISP_HIST_HIST3_DBG2 - ISP_HIST_HIST_CTRL;
|
reg_buf->isp_size[ISP2X_ID_SIHST] = size;
|
reg_buf->isp_offset[ISP2X_ID_SIHST] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_HIST_HIST_CTRL, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_LSC) {
|
size = 4 + ISP_LSC_STATUS - ISP_LSC_CTRL;
|
reg_buf->isp_size[ISP2X_ID_LSC] = size;
|
reg_buf->isp_offset[ISP2X_ID_LSC] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_LSC_CTRL, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_AWB_GAIN) {
|
size = 4 + CIF_ISP_AWB_GAIN_RB_V12 - CIF_ISP_AWB_GAIN_G_V12;
|
reg_buf->isp_size[ISP2X_ID_AWB_GAIN] = size;
|
reg_buf->isp_offset[ISP2X_ID_AWB_GAIN] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + CIF_ISP_AWB_GAIN_G_V12, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_CCM) {
|
size = 4 + ISP_CCM_BOUND_BIT - ISP_CCM_CTRL;
|
reg_buf->isp_size[ISP2X_ID_CCM] = size;
|
reg_buf->isp_offset[ISP2X_ID_CCM] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_CCM_CTRL, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_GOC) {
|
size = 4 + ISP_GAMMA_OUT_Y40 - ISP_GAMMA_OUT_CTRL;
|
reg_buf->isp_size[ISP2X_ID_GOC] = size;
|
reg_buf->isp_offset[ISP2X_ID_GOC] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_GAMMA_OUT_CTRL, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_CPROC) {
|
size = 4 + CPROC_HUE - CPROC_CTRL;
|
reg_buf->isp_size[ISP2X_ID_CPROC] = size;
|
reg_buf->isp_offset[ISP2X_ID_CPROC] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + CPROC_CTRL, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_SIAF) {
|
size = 4 + ISP_AFM_LUM_C - ISP_AFM_CTRL;
|
reg_buf->isp_size[ISP2X_ID_SIAF] = size;
|
reg_buf->isp_offset[ISP2X_ID_SIAF] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_AFM_CTRL, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_SIAWB) {
|
size = 4 + CIF_ISP_AWB_MEAN_V10 - CIF_ISP_AWB_PROP_V10;
|
reg_buf->isp_size[ISP2X_ID_SIAWB] = size;
|
reg_buf->isp_offset[ISP2X_ID_SIAWB] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + CIF_ISP_AWB_PROP_V10, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_IE) {
|
size = 4 + CIF_IMG_EFF_SHARPEN - CIF_IMG_EFF_CTRL;
|
reg_buf->isp_size[ISP2X_ID_IE] = size;
|
reg_buf->isp_offset[ISP2X_ID_IE] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + CIF_IMG_EFF_CTRL, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_YUVAE) {
|
size = 4 + ISP_YUVAE_RO_DBG3 - ISP_YUVAE_CTRL;
|
reg_buf->isp_size[ISP2X_ID_YUVAE] = size;
|
reg_buf->isp_offset[ISP2X_ID_YUVAE] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_YUVAE_CTRL, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_WDR) {
|
size = 4 + ISP_WDR_BLKMEAN8_ROW9_4TO7 - ISP_WDR_CTRL;
|
reg_buf->isp_size[ISP2X_ID_WDR] = size;
|
reg_buf->isp_offset[ISP2X_ID_WDR] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_WDR_CTRL, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_RK_IESHARP) {
|
size = 4 + CIF_RKSHARP_UV_GAUSS_OTHER_COE33_COE35 - CIF_RKSHARP_CTRL;
|
reg_buf->isp_size[ISP2X_ID_RK_IESHARP] = size;
|
reg_buf->isp_offset[ISP2X_ID_RK_IESHARP] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + CIF_RKSHARP_CTRL, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_RAWAF) {
|
size = 4 + ISP_RAWAF_INT_STATE - ISP_RAWAF_CTRL;
|
reg_buf->isp_size[ISP2X_ID_RAWAF] = size;
|
reg_buf->isp_offset[ISP2X_ID_RAWAF] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_RAWAF_CTRL, size);
|
offset += size;
|
|
size = ISP2X_RAWAF_SUMDATA_NUM * sizeof(tmp_statsbuf->params.rawaf.ramdata[0]);
|
reg_buf->isp_size[ISP2X_ID_RAWAF] += size;
|
reg_buf->isp_stats_size[ISP2X_ID_RAWAF] = size;
|
if (tmp_statsbuf->frame_id == reg_buf->frame_id)
|
memcpy(®_buf->reg[offset], &tmp_statsbuf->params.rawaf.ramdata[0], size);
|
else
|
memset(®_buf->reg[offset], 0, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_RAWAE0) {
|
size = 4 + ISP_RAWAE_LITE_RO_DBG2 - ISP_RAWAE_LITE_CTRL;
|
reg_buf->isp_size[ISP2X_ID_RAWAE0] = size;
|
reg_buf->isp_offset[ISP2X_ID_RAWAE0] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_RAWAE_LITE_CTRL, size);
|
offset += size;
|
|
size = ISP2X_RAWAELITE_MEAN_NUM * sizeof(tmp_statsbuf->params.rawae0.data[0]);
|
reg_buf->isp_size[ISP2X_ID_RAWAE0] += size;
|
reg_buf->isp_stats_size[ISP2X_ID_RAWAE0] = size;
|
if (tmp_statsbuf->frame_id == reg_buf->frame_id)
|
memcpy(®_buf->reg[offset], &tmp_statsbuf->params.rawae0.data[0], size);
|
else
|
memset(®_buf->reg[offset], 0, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_RAWAE1) {
|
size = 4 + RAWAE_BIG_RO_DBG3 - RAWAE_BIG_CTRL;
|
reg_buf->isp_size[ISP2X_ID_RAWAE1] = size;
|
reg_buf->isp_offset[ISP2X_ID_RAWAE1] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + RAWAE_BIG1_BASE, size);
|
offset += size;
|
|
size = ISP2X_RAWAEBIG_MEAN_NUM * sizeof(tmp_statsbuf->params.rawae1.data[0]);
|
reg_buf->isp_size[ISP2X_ID_RAWAE1] += size;
|
reg_buf->isp_stats_size[ISP2X_ID_RAWAE1] = size;
|
if (tmp_statsbuf->frame_id == reg_buf->frame_id)
|
memcpy(®_buf->reg[offset], &tmp_statsbuf->params.rawae1.data[0], size);
|
else
|
memset(®_buf->reg[offset], 0, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_RAWAE2) {
|
size = 4 + RAWAE_BIG_RO_DBG3 - RAWAE_BIG_CTRL;
|
reg_buf->isp_size[ISP2X_ID_RAWAE2] = size;
|
reg_buf->isp_offset[ISP2X_ID_RAWAE2] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + RAWAE_BIG2_BASE, size);
|
offset += size;
|
|
size = ISP2X_RAWAEBIG_MEAN_NUM * sizeof(tmp_statsbuf->params.rawae2.data[0]);
|
reg_buf->isp_size[ISP2X_ID_RAWAE2] += size;
|
reg_buf->isp_stats_size[ISP2X_ID_RAWAE2] = size;
|
if (tmp_statsbuf->frame_id == reg_buf->frame_id)
|
memcpy(®_buf->reg[offset], &tmp_statsbuf->params.rawae2.data[0], size);
|
else
|
memset(®_buf->reg[offset], 0, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_RAWAE3) {
|
size = 4 + RAWAE_BIG_RO_DBG3 - RAWAE_BIG_CTRL;
|
reg_buf->isp_size[ISP2X_ID_RAWAE3] = size;
|
reg_buf->isp_offset[ISP2X_ID_RAWAE3] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + RAWAE_BIG3_BASE, size);
|
offset += size;
|
|
size = ISP2X_RAWAEBIG_MEAN_NUM * sizeof(tmp_statsbuf->params.rawae3.data[0]);
|
reg_buf->isp_size[ISP2X_ID_RAWAE3] += size;
|
reg_buf->isp_stats_size[ISP2X_ID_RAWAE3] = size;
|
if (tmp_statsbuf->frame_id == reg_buf->frame_id)
|
memcpy(®_buf->reg[offset], &tmp_statsbuf->params.rawae3.data[0], size);
|
else
|
memset(®_buf->reg[offset], 0, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_RAWAWB) {
|
size = 4 + ISP_RAWAWB_RAM_CTRL - ISP_RAWAWB_CTRL;
|
reg_buf->isp_size[ISP2X_ID_RAWAWB] = size;
|
reg_buf->isp_offset[ISP2X_ID_RAWAWB] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_RAWAWB_CTRL, size);
|
offset += size;
|
|
size = ISP2X_RAWAWB_RAMDATA_NUM * sizeof(tmp_statsbuf->params.rawawb.ramdata[0]);
|
reg_buf->isp_size[ISP2X_ID_RAWAWB] += size;
|
reg_buf->isp_stats_size[ISP2X_ID_RAWAWB] = size;
|
if (tmp_statsbuf->frame_id == reg_buf->frame_id)
|
memcpy(®_buf->reg[offset], &tmp_statsbuf->params.rawawb.ramdata[0], size);
|
else
|
memset(®_buf->reg[offset], 0, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_RAWHIST0) {
|
size = 4 + ISP_RAWHIST_LITE_WEIGHT - ISP_RAWHIST_LITE_CTRL;
|
reg_buf->isp_size[ISP2X_ID_RAWHIST0] = size;
|
reg_buf->isp_offset[ISP2X_ID_RAWHIST0] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_RAWHIST_LITE_CTRL, size);
|
offset += size;
|
|
size = ISP2X_HIST_BIN_N_MAX * sizeof(tmp_statsbuf->params.rawhist0.hist_bin[0]);
|
reg_buf->isp_size[ISP2X_ID_RAWHIST0] += size;
|
reg_buf->isp_stats_size[ISP2X_ID_RAWHIST0] = size;
|
if (tmp_statsbuf->frame_id == reg_buf->frame_id)
|
memcpy(®_buf->reg[offset], &tmp_statsbuf->params.rawhist0.hist_bin[0], size);
|
else
|
memset(®_buf->reg[offset], 0, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_RAWHIST1) {
|
size = 4 + ISP_RAWHIST_BIG_WEIGHT_BASE - ISP_RAWHIST_BIG_CTRL;
|
reg_buf->isp_size[ISP2X_ID_RAWHIST1] = size;
|
reg_buf->isp_offset[ISP2X_ID_RAWHIST1] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_RAWHIST_BIG1_BASE, size);
|
offset += size;
|
|
size = ISP2X_HIST_BIN_N_MAX * sizeof(tmp_statsbuf->params.rawhist1.hist_bin[0]);
|
reg_buf->isp_size[ISP2X_ID_RAWHIST1] += size;
|
reg_buf->isp_stats_size[ISP2X_ID_RAWHIST1] = size;
|
if (tmp_statsbuf->frame_id == reg_buf->frame_id)
|
memcpy(®_buf->reg[offset], &tmp_statsbuf->params.rawhist1.hist_bin[0], size);
|
else
|
memset(®_buf->reg[offset], 0, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_RAWHIST2) {
|
size = 4 + ISP_RAWHIST_BIG_WEIGHT_BASE - ISP_RAWHIST_BIG_CTRL;
|
reg_buf->isp_size[ISP2X_ID_RAWHIST2] = size;
|
reg_buf->isp_offset[ISP2X_ID_RAWHIST2] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_RAWHIST_BIG2_BASE, size);
|
offset += size;
|
|
size = ISP2X_HIST_BIN_N_MAX * sizeof(tmp_statsbuf->params.rawhist2.hist_bin[0]);
|
reg_buf->isp_size[ISP2X_ID_RAWHIST2] += size;
|
reg_buf->isp_stats_size[ISP2X_ID_RAWHIST2] = size;
|
if (tmp_statsbuf->frame_id == reg_buf->frame_id)
|
memcpy(®_buf->reg[offset], &tmp_statsbuf->params.rawhist2.hist_bin[0], size);
|
else
|
memset(®_buf->reg[offset], 0, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_RAWHIST3) {
|
size = 4 + ISP_RAWHIST_BIG_WEIGHT_BASE - ISP_RAWHIST_BIG_CTRL;
|
reg_buf->isp_size[ISP2X_ID_RAWHIST3] = size;
|
reg_buf->isp_offset[ISP2X_ID_RAWHIST3] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_RAWHIST_BIG3_BASE, size);
|
offset += size;
|
|
size = ISP2X_HIST_BIN_N_MAX * sizeof(tmp_statsbuf->params.rawhist3.hist_bin[0]);
|
reg_buf->isp_size[ISP2X_ID_RAWHIST3] += size;
|
reg_buf->isp_stats_size[ISP2X_ID_RAWHIST3] = size;
|
if (tmp_statsbuf->frame_id == reg_buf->frame_id)
|
memcpy(®_buf->reg[offset], &tmp_statsbuf->params.rawhist3.hist_bin[0], size);
|
else
|
memset(®_buf->reg[offset], 0, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_HDRMGE) {
|
size = 4 + ISP_HDRMGE_OVER_Y16 - ISP_HDRMGE_CTRL;
|
reg_buf->isp_size[ISP2X_ID_HDRMGE] = size;
|
reg_buf->isp_offset[ISP2X_ID_HDRMGE] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_HDRMGE_CTRL, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_RAWNR) {
|
size = 4 + ISP_RAWNR_RGBAIN_FLIP - ISP_RAWNR_CTRL;
|
reg_buf->isp_size[ISP2X_ID_RAWNR] = size;
|
reg_buf->isp_offset[ISP2X_ID_RAWNR] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_RAWNR_CTRL, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_HDRTMO) {
|
size = 4 + ISP_HDRTMO_HIST_RO31 - ISP_HDRTMO_CTRL;
|
reg_buf->isp_size[ISP2X_ID_HDRTMO] = size;
|
reg_buf->isp_offset[ISP2X_ID_HDRTMO] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_HDRTMO_CTRL, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_GIC) {
|
size = 4 + ISP_GIC_NOISE_CTRL1 - ISP_GIC_CONTROL;
|
reg_buf->isp_size[ISP2X_ID_GIC] = size;
|
reg_buf->isp_offset[ISP2X_ID_GIC] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_GIC_CONTROL, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_DHAZ) {
|
size = 4 + ISP_DHAZ_HIST_REG95 - ISP_DHAZ_CTRL;
|
reg_buf->isp_size[ISP2X_ID_DHAZ] = size;
|
reg_buf->isp_offset[ISP2X_ID_DHAZ] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_DHAZ_CTRL, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_3DLUT) {
|
size = 4 + ISP_3DLUT_UPDATE - ISP_3DLUT_CTRL;
|
reg_buf->isp_size[ISP2X_ID_3DLUT] = size;
|
reg_buf->isp_offset[ISP2X_ID_3DLUT] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_3DLUT_CTRL, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_LDCH) {
|
size = 4 + ISP_LDCH_STS - ISP_LDCH_STS;
|
reg_buf->isp_size[ISP2X_ID_LDCH] = size;
|
reg_buf->isp_offset[ISP2X_ID_LDCH] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_LDCH_STS, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_GAIN) {
|
size = 4 + ISP_GAIN_LUT8 - ISP_GAIN_CTRL;
|
reg_buf->isp_size[ISP2X_ID_GAIN] = size;
|
reg_buf->isp_offset[ISP2X_ID_GAIN] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_GAIN_CTRL, size);
|
offset += size;
|
}
|
|
if (rkisp_debug_reg & ISP2X_MODULE_DEBAYER) {
|
size = 4 + ISP_DEBAYER_C_FILTER - ISP_DEBAYER_CONTROL;
|
reg_buf->isp_size[ISP2X_ID_DEBAYER] = size;
|
reg_buf->isp_offset[ISP2X_ID_DEBAYER] = offset;
|
memcpy_fromio(®_buf->reg[offset], hw->base_addr + ISP_DEBAYER_CONTROL, size);
|
offset += size;
|
}
|
|
reg_buf->reg_size = offset;
|
}
|
|
static void rkisp_bridge_try_sendtohal(struct rkisp_device *dev)
|
{
|
struct rkisp_hw_dev *hw = dev->hw_dev;
|
struct rkisp_bridge_device *br_dev = &dev->br_dev;
|
struct rkisp_ispp_buf *cur_fbcgain = dev->cur_fbcgain;
|
struct rkisp_buffer *cur_spbuf = dev->cur_spbuf;
|
struct isp2x_ispgain_buf *buf;
|
struct rkisp_bridge_buf *bdgbuf;
|
struct vb2_buffer *vb2_buf;
|
u32 *vaddr, size;
|
u64 pic_ts, gain_ts, sp_ts;
|
|
if (cur_fbcgain && cur_spbuf) {
|
if ((cur_fbcgain->frame_id == cur_spbuf->vb.sequence) &&
|
(cur_fbcgain->index == cur_spbuf->dev_id)) {
|
vb2_buf = &cur_spbuf->vb.vb2_buf;
|
cur_fbcgain->buf_idx = vb2_buf->index;
|
buf = vb2_plane_vaddr(vb2_buf, 1);
|
buf->gain_dmaidx = cur_fbcgain->gain_dmaidx;
|
buf->mfbc_dmaidx = cur_fbcgain->mfbc_dmaidx;
|
buf->gain_size = cur_fbcgain->gain_size;
|
buf->mfbc_size = cur_fbcgain->mfbc_size;
|
buf->frame_id = cur_fbcgain->frame_id;
|
bdgbuf = to_bridge_buf(cur_fbcgain);
|
rkisp_finish_buffer(dev, &bdgbuf->dummy[GROUP_BUF_GAIN]);
|
vb2_buffer_done(&cur_spbuf->vb.vb2_buf, VB2_BUF_STATE_DONE);
|
list_add_tail(&cur_fbcgain->list, &hw->rpt_list);
|
dev->cur_fbcgain = NULL;
|
dev->cur_spbuf = NULL;
|
v4l2_dbg(3, rkisp_debug, &br_dev->sd,
|
"%s send mfbcgain buf to hal, frame_id %d\n",
|
__func__, cur_fbcgain->frame_id);
|
|
bdgbuf = to_bridge_buf(cur_fbcgain);
|
vaddr = bdgbuf->dummy[GROUP_BUF_PIC].vaddr;
|
size = bdgbuf->dummy[GROUP_BUF_PIC].size;
|
pic_ts = *(u64 *)(vaddr + size / 4 - 2);
|
|
vaddr = bdgbuf->dummy[GROUP_BUF_GAIN].vaddr;
|
size = bdgbuf->dummy[GROUP_BUF_GAIN].size;
|
gain_ts = *(u64 *)(vaddr + size / 4 - 2);
|
|
size = vb2_plane_size(&cur_spbuf->vb.vb2_buf, 0);
|
vaddr = (u32 *)vb2_plane_vaddr(&cur_spbuf->vb.vb2_buf, 0);
|
sp_ts = *(u64 *)(vaddr + size / 4 - 2);
|
if (abs(pic_ts - gain_ts) > 5000000LL || abs(pic_ts - sp_ts) > 5000000LL ||
|
abs(gain_ts - sp_ts) > 5000000LL) {
|
v4l2_info(&br_dev->sd,
|
"%s: frame %d, timestamp is not match (pic_ts %lld, gain_ts %lld, sp_ts %lld)\n",
|
__func__, cur_fbcgain->frame_id, pic_ts, gain_ts, sp_ts);
|
}
|
} else {
|
v4l2_info(&br_dev->sd,
|
"%s frame_id(%d, %d) or dev_id(%d, %d) is not match\n",
|
__func__,
|
cur_fbcgain->frame_id, cur_spbuf->vb.sequence,
|
cur_fbcgain->index, cur_spbuf->dev_id);
|
}
|
}
|
}
|
|
static void rkisp_bridge_save_fbcgain(struct rkisp_device *dev, struct rkisp_ispp_buf *fbcgain)
|
{
|
struct rkisp_hw_dev *hw = dev->hw_dev;
|
struct rkisp_bridge_device *br_dev = &dev->br_dev;
|
unsigned long lock_flags = 0;
|
|
spin_lock_irqsave(&hw->buf_lock, lock_flags);
|
if (dev->cur_fbcgain) {
|
v4l2_dbg(1, rkisp_debug, &br_dev->sd,
|
"%s old mfbcgain buf is exit, frame_id %d\n",
|
__func__, dev->cur_fbcgain->frame_id);
|
list_add_tail(&dev->cur_fbcgain->list, &hw->list);
|
dev->cur_fbcgain = NULL;
|
br_dev->dbg.frameloss++;
|
}
|
dev->cur_fbcgain = fbcgain;
|
rkisp_bridge_try_sendtohal(dev);
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
}
|
|
static void rkisp_bridge_work(struct work_struct *work)
|
{
|
struct rkisp_bridge_work *br_wk =
|
container_of(work, struct rkisp_bridge_work, work);
|
struct rkisp_bridge_device *dev = br_wk->dev;
|
|
struct rkisp_ispp_reg *reg_buf = (struct rkisp_ispp_reg *)br_wk->param;
|
|
dump_dbg_reg(dev, reg_buf);
|
|
kfree(br_wk);
|
}
|
|
static int frame_end(struct rkisp_bridge_device *dev, bool en, u32 state)
|
{
|
struct rkisp_device *ispdev = dev->ispdev;
|
struct rkisp_hw_dev *hw = ispdev->hw_dev;
|
struct v4l2_subdev *sd = v4l2_get_subdev_hostdata(&dev->sd);
|
unsigned long lock_flags = 0;
|
u64 ns = ktime_get_ns();
|
struct rkisp_bridge_buf *buf;
|
|
if (state == FRAME_IRQ && ispdev->cap_dev.is_done_early)
|
return 0;
|
dev->frame_early = false;
|
rkisp_dmarx_get_frame(dev->ispdev, &dev->dbg.id, NULL, NULL, true);
|
dev->dbg.interval = ns - dev->dbg.timestamp;
|
dev->dbg.timestamp = ns;
|
if (hw->cur_buf && hw->nxt_buf) {
|
if (!en) {
|
spin_lock_irqsave(&hw->buf_lock, lock_flags);
|
list_add_tail(&hw->cur_buf->list, &hw->list);
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
} else if (dev->ispdev->skip_frame > 0) {
|
dev->ispdev->skip_frame--;
|
spin_lock_irqsave(&hw->buf_lock, lock_flags);
|
list_add_tail(&hw->cur_buf->list, &hw->list);
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
} else {
|
u64 sof_ns = 0;
|
struct rkisp_ispp_reg *reg_buf = NULL;
|
|
ns = 0;
|
rkisp_dmarx_get_frame(dev->ispdev,
|
&hw->cur_buf->frame_id, &sof_ns, &ns, true);
|
if (!sof_ns)
|
sof_ns = 0;
|
if (!ns)
|
ns = ktime_get_ns();
|
hw->cur_buf->frame_timestamp = ns;
|
hw->cur_buf->index = dev->ispdev->dev_id;
|
v4l2_subdev_call(sd, core, ioctl, RKISP_ISPP_CMD_REQUEST_REGBUF,
|
®_buf);
|
if (reg_buf) {
|
struct rkisp_bridge_work *br_wk;
|
|
br_wk = kzalloc(sizeof(struct rkisp_bridge_work), GFP_ATOMIC);
|
if (br_wk) {
|
reg_buf->stat = ISP_ISPP_INUSE;
|
reg_buf->dev_id = hw->cur_buf->index;
|
reg_buf->frame_id = hw->cur_buf->frame_id;
|
reg_buf->sof_timestamp = sof_ns;
|
reg_buf->frame_timestamp = hw->cur_buf->frame_timestamp;
|
reg_buf->exposure = dev->ispdev->params_vdev.exposure;
|
|
br_wk->dev = dev;
|
br_wk->param = (void *)reg_buf;
|
INIT_WORK((struct work_struct *)&br_wk->work,
|
rkisp_bridge_work);
|
if (!queue_work(dev->wq, (struct work_struct *)&br_wk->work)) {
|
v4l2_err(&dev->sd, "queue work failed\n");
|
kfree(br_wk);
|
}
|
}
|
}
|
|
if (dev->ispdev->send_fbcgain) {
|
u32 *vaddr, size;
|
|
buf = to_bridge_buf(hw->cur_buf);
|
vaddr = buf->dummy[GROUP_BUF_PIC].vaddr;
|
size = buf->dummy[GROUP_BUF_PIC].size;
|
*(u64 *)(vaddr + size / 4 - 2) = ktime_get_ns();
|
|
vaddr = buf->dummy[GROUP_BUF_GAIN].vaddr;
|
size = buf->dummy[GROUP_BUF_GAIN].size;
|
*(u64 *)(vaddr + size / 4 - 2) = ktime_get_ns();
|
hw->cur_buf->mfbc_dmaidx = hw->cur_buf->didx[GROUP_BUF_PIC];
|
hw->cur_buf->gain_dmaidx = hw->cur_buf->didx[GROUP_BUF_GAIN];
|
hw->cur_buf->is_move_judge = true;
|
rkisp_bridge_save_fbcgain(dev->ispdev, hw->cur_buf);
|
} else {
|
hw->cur_buf->is_move_judge = false;
|
v4l2_subdev_call(sd, video, s_rx_buffer, hw->cur_buf, NULL);
|
}
|
}
|
hw->cur_buf = NULL;
|
} else {
|
v4l2_dbg(1, rkisp_debug, &dev->sd, "no buf, lost frame:%d\n", dev->dbg.id);
|
dev->dbg.frameloss++;
|
}
|
|
if (hw->nxt_buf) {
|
hw->cur_buf = hw->nxt_buf;
|
hw->nxt_buf = NULL;
|
}
|
|
return 0;
|
}
|
|
static enum hrtimer_restart rkisp_bridge_frame_done_early(struct hrtimer *timer)
|
{
|
struct rkisp_bridge_device *br =
|
container_of(timer, struct rkisp_bridge_device, frame_qst);
|
struct rkisp_device *dev = br->ispdev;
|
enum hrtimer_restart ret = HRTIMER_NORESTART;
|
u32 ycnt, line = dev->cap_dev.wait_line;
|
u32 seq, time, max_time = 1000000;
|
u64 ns = ktime_get_ns();
|
|
time = (u32)(ns - br->fs_ns);
|
ycnt = rkisp_read(dev, ISP_MPFBC_ENC_POS, true) & 0x3ff;
|
ycnt *= 8;
|
rkisp_dmarx_get_frame(dev, &seq, NULL, NULL, true);
|
if (!br->en || dev->isp_state == ISP_STOP) {
|
goto end;
|
} else if (ycnt < line) {
|
if (!ycnt)
|
ns = max_time;
|
else
|
ns = time * (line - ycnt) / ycnt;
|
if (ns > max_time)
|
ns = max_time;
|
hrtimer_forward(timer, timer->base->get_time(), ns_to_ktime(ns));
|
ret = HRTIMER_RESTART;
|
} else {
|
v4l2_dbg(3, rkisp_debug, &dev->v4l2_dev,
|
"%s seq:%d line:%d ycnt:%d time:%dus\n",
|
__func__, seq, line, ycnt, time / 1000);
|
frame_end(br, br->en, FRAME_WORK);
|
}
|
end:
|
return ret;
|
}
|
|
static int config_gain(struct rkisp_bridge_device *dev)
|
{
|
u32 w = dev->crop.width;
|
u32 h = dev->crop.height;
|
u32 val;
|
|
val = ALIGN(w, 64) * ALIGN(h, 128) >> 4;
|
rkisp_write(dev->ispdev, MI_GAIN_WR_SIZE, val, false);
|
val = ALIGN((w + 3) >> 2, 16);
|
rkisp_write(dev->ispdev, MI_GAIN_WR_LENGTH, val, false);
|
rkisp_set_bits(dev->ispdev, MI_WR_CTRL2,
|
0, SW_GAIN_WR_AUTOUPD, true);
|
return 0;
|
}
|
|
static int config_mpfbc(struct rkisp_bridge_device *dev)
|
{
|
struct rkisp_hw_dev *hw = dev->ispdev->hw_dev;
|
u32 h = hw->max_in.h ? hw->max_in.h : dev->crop.height;
|
u32 ctrl = 0;
|
|
if (dev->work_mode & ISP_ISPP_QUICK) {
|
rkisp_set_bits(dev->ispdev, CTRL_SWS_CFG,
|
0, SW_ISP2PP_PIPE_EN, true);
|
ctrl = SW_MPFBC_MAINISP_MODE;
|
if (dev->ispdev->hw_dev->nxt_buf)
|
ctrl |= SW_MPFBC_PINGPONG_EN;
|
}
|
|
rkisp_write(dev->ispdev, ISP_MPFBC_VIR_WIDTH, 0, true);
|
rkisp_write(dev->ispdev, ISP_MPFBC_VIR_HEIGHT, ALIGN(h, 16), true);
|
|
ctrl |= (dev->work_mode & ISP_ISPP_422) | SW_MPFBC_EN;
|
rkisp_write(dev->ispdev, ISP_MPFBC_BASE, ctrl, true);
|
rkisp_set_bits(dev->ispdev, MI_WR_CTRL, MI_LUM_BURST_MASK,
|
MI_MIPI_LUM_BURST16, true);
|
dev->ispdev->irq_ends_mask |= ISP_FRAME_MPFBC;
|
return 0;
|
}
|
|
static void disable_mpfbc(struct rkisp_bridge_device *dev)
|
{
|
if (dev->ispdev->hw_dev->is_single)
|
rkisp_clear_bits(dev->ispdev, ISP_MPFBC_BASE,
|
SW_MPFBC_EN, true);
|
}
|
|
static bool is_stopped_mpfbc(struct rkisp_bridge_device *dev)
|
{
|
bool en = true;
|
|
if (dev->ispdev->hw_dev->is_single)
|
en = is_mpfbc_stopped(dev->ispdev->base_addr);
|
return en;
|
}
|
|
static struct rkisp_bridge_ops mpfbc_ops = {
|
.config = config_mpfbc,
|
.disable = disable_mpfbc,
|
.is_stopped = is_stopped_mpfbc,
|
};
|
|
static struct rkisp_bridge_config mpfbc_cfg = {
|
.frame_end_id = MI_MPFBC_FRAME,
|
.reg = {
|
.y0_base = ISP_MPFBC_HEAD_PTR,
|
.uv0_base = ISP_MPFBC_PAYL_PTR,
|
.y1_base = ISP_MPFBC_HEAD_PTR2,
|
.uv1_base = ISP_MPFBC_PAYL_PTR2,
|
.g0_base = MI_GAIN_WR_BASE,
|
.g1_base = MI_GAIN_WR_BASE2,
|
|
.y0_base_shd = ISP_MPFBC_HEAD_PTR,
|
.uv0_base_shd = ISP_MPFBC_PAYL_PTR,
|
.g0_base_shd = MI_GAIN_WR_BASE_SHD,
|
},
|
};
|
|
static int config_mp(struct rkisp_bridge_device *dev)
|
{
|
u32 w = dev->crop.width;
|
u32 h = dev->crop.height;
|
u32 val;
|
|
if (dev->work_mode & ISP_ISPP_QUICK) {
|
rkisp_set_bits(dev->ispdev, CTRL_SWS_CFG, 0,
|
SW_ISP2PP_PIPE_EN, true);
|
if (dev->ispdev->hw_dev->nxt_buf)
|
rkisp_set_bits(dev->ispdev, CIF_MI_CTRL, 0,
|
CIF_MI_MP_PINGPONG_ENABLE, true);
|
}
|
|
val = w * h;
|
rkisp_write(dev->ispdev, CIF_MI_MP_Y_SIZE_INIT, val, false);
|
val = (dev->work_mode & ISP_ISPP_422) ? val : val / 2;
|
rkisp_write(dev->ispdev, CIF_MI_MP_CB_SIZE_INIT, val, false);
|
rkisp_write(dev->ispdev, CIF_MI_MP_CR_SIZE_INIT, 0, false);
|
rkisp_write(dev->ispdev, CIF_MI_MP_Y_OFFS_CNT_INIT, 0, false);
|
rkisp_write(dev->ispdev, CIF_MI_MP_CB_OFFS_CNT_INIT, 0, false);
|
rkisp_write(dev->ispdev, CIF_MI_MP_CR_OFFS_CNT_INIT, 0, false);
|
|
rkisp_write(dev->ispdev, ISP_MPFBC_BASE,
|
dev->work_mode & ISP_ISPP_422, true);
|
rkisp_set_bits(dev->ispdev, CIF_MI_CTRL, MI_CTRL_MP_FMT_MASK,
|
MI_CTRL_MP_WRITE_YUV_SPLA | CIF_MI_CTRL_MP_ENABLE |
|
CIF_MI_MP_AUTOUPDATE_ENABLE, true);
|
dev->ispdev->irq_ends_mask |= ISP_FRAME_MP;
|
return 0;
|
}
|
|
static void disable_mp(struct rkisp_bridge_device *dev)
|
{
|
if (dev->ispdev->hw_dev->is_single)
|
rkisp_clear_bits(dev->ispdev, CIF_MI_CTRL,
|
CIF_MI_CTRL_MP_ENABLE |
|
CIF_MI_CTRL_RAW_ENABLE, true);
|
}
|
|
static bool is_stopped_mp(struct rkisp_bridge_device *dev)
|
{
|
bool en = true;
|
|
if (dev->ispdev->hw_dev->is_single)
|
en = mp_is_stream_stopped(dev->ispdev->base_addr);
|
return en;
|
}
|
|
static struct rkisp_bridge_ops mp_ops = {
|
.config = config_mp,
|
.disable = disable_mp,
|
.is_stopped = is_stopped_mp,
|
};
|
|
static struct rkisp_bridge_config mp_cfg = {
|
.frame_end_id = MI_MP_FRAME,
|
.reg = {
|
.y0_base = MI_MP_WR_Y_BASE,
|
.uv0_base = MI_MP_WR_CB_BASE,
|
.y1_base = MI_MP_WR_Y_BASE2,
|
.uv1_base = MI_MP_WR_CB_BASE2,
|
.g0_base = MI_GAIN_WR_BASE,
|
.g1_base = MI_GAIN_WR_BASE2,
|
|
.y0_base_shd = MI_MP_WR_Y_BASE_SHD,
|
.uv0_base_shd = MI_MP_WR_CB_BASE_SHD,
|
.g0_base_shd = MI_GAIN_WR_BASE_SHD,
|
},
|
};
|
|
static void free_bridge_buf(struct rkisp_bridge_device *dev)
|
{
|
struct rkisp_hw_dev *hw = dev->ispdev->hw_dev;
|
struct rkisp_bridge_buf *buf;
|
struct rkisp_ispp_buf *dbufs;
|
unsigned long lock_flags = 0;
|
int i, j;
|
|
spin_lock_irqsave(&hw->buf_lock, lock_flags);
|
if (--hw->buf_init_cnt > 0) {
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
return;
|
}
|
|
v4l2_dbg(1, rkisp_debug, &dev->ispdev->v4l2_dev,
|
"%s\n", __func__);
|
|
if (hw->cur_buf) {
|
list_add_tail(&hw->cur_buf->list, &hw->list);
|
if (hw->cur_buf == hw->nxt_buf)
|
hw->nxt_buf = NULL;
|
hw->cur_buf = NULL;
|
}
|
|
if (hw->nxt_buf) {
|
list_add_tail(&hw->nxt_buf->list, &hw->list);
|
hw->nxt_buf = NULL;
|
}
|
|
if (dev->ispdev->cur_fbcgain) {
|
list_add_tail(&dev->ispdev->cur_fbcgain->list, &hw->list);
|
dev->ispdev->cur_fbcgain = NULL;
|
}
|
|
while (!list_empty(&hw->rpt_list)) {
|
dbufs = list_first_entry(&hw->rpt_list,
|
struct rkisp_ispp_buf, list);
|
list_del(&dbufs->list);
|
list_add_tail(&dbufs->list, &hw->list);
|
}
|
|
while (!list_empty(&hw->list)) {
|
dbufs = list_first_entry(&hw->list,
|
struct rkisp_ispp_buf, list);
|
list_del(&dbufs->list);
|
}
|
|
hw->is_buf_init = false;
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
for (i = 0; i < BRIDGE_BUF_MAX; i++) {
|
buf = &hw->bufs[i];
|
for (j = 0; j < GROUP_BUF_MAX; j++)
|
rkisp_free_buffer(dev->ispdev, &buf->dummy[j]);
|
}
|
|
rkisp_free_common_dummy_buf(dev->ispdev);
|
}
|
|
static int init_buf(struct rkisp_bridge_device *dev, u32 pic_size, u32 gain_size)
|
{
|
struct v4l2_subdev *sd = v4l2_get_subdev_hostdata(&dev->sd);
|
struct rkisp_hw_dev *hw = dev->ispdev->hw_dev;
|
struct rkisp_bridge_buf *buf;
|
struct rkisp_dummy_buffer *dummy;
|
int i, j, val, ret = 0;
|
unsigned long lock_flags = 0;
|
|
spin_lock_irqsave(&hw->buf_lock, lock_flags);
|
if (++hw->buf_init_cnt > 1) {
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
return 0;
|
}
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
|
v4l2_dbg(1, rkisp_debug, &dev->ispdev->v4l2_dev,
|
"%s pic size:%d gain size:%d\n",
|
__func__, pic_size, gain_size);
|
|
INIT_LIST_HEAD(&hw->list);
|
for (i = 0; i < dev->buf_num; i++) {
|
buf = &hw->bufs[i];
|
for (j = 0; j < GROUP_BUF_MAX; j++) {
|
dummy = &buf->dummy[j];
|
dummy->is_need_vaddr = true;
|
dummy->is_need_dbuf = true;
|
dummy->size = PAGE_ALIGN(!j ? pic_size : gain_size);
|
ret = rkisp_alloc_buffer(dev->ispdev, dummy);
|
if (ret)
|
goto err;
|
buf->dbufs.dbuf[j] = dummy->dbuf;
|
buf->dbufs.didx[j] = i * GROUP_BUF_MAX + j;
|
buf->dbufs.gain_size = PAGE_ALIGN(gain_size);
|
buf->dbufs.mfbc_size = PAGE_ALIGN(pic_size);
|
}
|
list_add_tail(&buf->dbufs.list, &hw->list);
|
ret = v4l2_subdev_call(sd, video, s_rx_buffer, &buf->dbufs, NULL);
|
if (ret)
|
goto err;
|
}
|
|
for (i = 0; i < hw->dev_num; i++) {
|
struct rkisp_device *isp = hw->isp[i];
|
|
if (!(isp->isp_inp & INP_CSI))
|
continue;
|
ret = rkisp_alloc_common_dummy_buf(isp);
|
if (ret < 0)
|
goto err;
|
else
|
break;
|
}
|
|
hw->cur_buf = list_first_entry(&hw->list, struct rkisp_ispp_buf, list);
|
list_del(&hw->cur_buf->list);
|
buf = to_bridge_buf(hw->cur_buf);
|
val = buf->dummy[GROUP_BUF_PIC].dma_addr;
|
rkisp_write(dev->ispdev, dev->cfg->reg.y0_base, val, true);
|
val += dev->cfg->offset;
|
rkisp_write(dev->ispdev, dev->cfg->reg.uv0_base, val, true);
|
val = buf->dummy[GROUP_BUF_GAIN].dma_addr;
|
rkisp_write(dev->ispdev, dev->cfg->reg.g0_base, val, true);
|
|
if (!list_empty(&hw->list)) {
|
hw->nxt_buf = list_first_entry(&hw->list,
|
struct rkisp_ispp_buf, list);
|
list_del(&hw->nxt_buf->list);
|
}
|
if (hw->nxt_buf && (dev->work_mode & ISP_ISPP_QUICK)) {
|
buf = to_bridge_buf(hw->nxt_buf);
|
val = buf->dummy[GROUP_BUF_PIC].dma_addr;
|
rkisp_write(dev->ispdev, dev->cfg->reg.y1_base, val, true);
|
val += dev->cfg->offset;
|
rkisp_write(dev->ispdev, dev->cfg->reg.uv1_base, val, true);
|
val = buf->dummy[GROUP_BUF_GAIN].dma_addr;
|
rkisp_write(dev->ispdev, dev->cfg->reg.g1_base, val, true);
|
rkisp_set_bits(dev->ispdev, MI_WR_CTRL2,
|
0, SW_GAIN_WR_PINGPONG, true);
|
}
|
|
rkisp_set_bits(dev->ispdev, CIF_VI_DPCL, 0,
|
CIF_VI_DPCL_CHAN_MODE_MP |
|
CIF_VI_DPCL_MP_MUX_MRSZ_MI, true);
|
rkisp_set_bits(dev->ispdev, MI_WR_CTRL, 0,
|
CIF_MI_CTRL_INIT_BASE_EN |
|
CIF_MI_CTRL_INIT_OFFSET_EN, true);
|
rkisp_set_bits(dev->ispdev, MI_IMSC, 0,
|
dev->cfg->frame_end_id, true);
|
|
spin_lock_irqsave(&hw->buf_lock, lock_flags);
|
hw->is_buf_init = true;
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
return 0;
|
err:
|
free_bridge_buf(dev);
|
v4l2_err(&dev->sd, "%s fail:%d\n", __func__, ret);
|
return ret;
|
}
|
|
static int config_mode(struct rkisp_bridge_device *dev)
|
{
|
struct rkisp_hw_dev *hw = dev->ispdev->hw_dev;
|
u32 w = hw->max_in.w ? hw->max_in.w : dev->crop.width;
|
u32 h = hw->max_in.h ? hw->max_in.h : dev->crop.height;
|
u32 offs = w * h;
|
u32 pic_size = 0, gain_size;
|
|
if (dev->work_mode == ISP_ISPP_INIT_FAIL) {
|
free_bridge_buf(dev);
|
return 0;
|
}
|
|
if (!dev->linked || !dev->ispdev->isp_inp) {
|
v4l2_err(&dev->sd,
|
"invalid: link:%d or isp input:0x%x\n",
|
dev->linked,
|
dev->ispdev->isp_inp);
|
return -EINVAL;
|
}
|
|
v4l2_dbg(1, rkisp_debug, &dev->sd,
|
"work mode:0x%x buf num:%d\n",
|
dev->work_mode, dev->buf_num);
|
|
if (dev->work_mode & ISP_ISPP_FBC) {
|
dev->ops = &mpfbc_ops;
|
dev->cfg = &mpfbc_cfg;
|
} else {
|
dev->ops = &mp_ops;
|
dev->cfg = &mp_cfg;
|
}
|
|
gain_size = ALIGN(w, 64) * ALIGN(h, 128) >> 4;
|
if (dev->work_mode & ISP_ISPP_FBC) {
|
w = ALIGN(w, 16);
|
h = ALIGN(h, 16);
|
offs = w * h >> 4;
|
pic_size = offs;
|
}
|
if (dev->work_mode & ISP_ISPP_422)
|
pic_size += w * h * 2;
|
else
|
pic_size += w * h * 3 >> 1;
|
dev->cfg->offset = offs;
|
|
pic_size += RKISP_MOTION_DECT_TS_SIZE;
|
gain_size += RKISP_MOTION_DECT_TS_SIZE;
|
return init_buf(dev, pic_size, gain_size);
|
}
|
|
static void crop_on(struct rkisp_bridge_device *dev)
|
{
|
struct rkisp_device *ispdev = dev->ispdev;
|
u32 src_w = ispdev->isp_sdev.out_crop.width;
|
u32 src_h = ispdev->isp_sdev.out_crop.height;
|
u32 dest_w = dev->crop.width;
|
u32 dest_h = dev->crop.height;
|
u32 left = dev->crop.left;
|
u32 top = dev->crop.top;
|
u32 ctrl = CIF_DUAL_CROP_CFG_UPD;
|
|
if (ispdev->isp_ver == ISP_V20 &&
|
ispdev->rd_mode == HDR_RDBK_FRAME1 &&
|
ispdev->isp_sdev.in_fmt.fmt_type == FMT_BAYER)
|
src_h += RKMODULE_EXTEND_LINE;
|
|
rkisp_write(ispdev, CIF_DUAL_CROP_M_H_OFFS, left, false);
|
rkisp_write(ispdev, CIF_DUAL_CROP_M_V_OFFS, top, false);
|
rkisp_write(ispdev, CIF_DUAL_CROP_M_H_SIZE, dest_w, false);
|
rkisp_write(ispdev, CIF_DUAL_CROP_M_V_SIZE, dest_h, false);
|
ctrl |= rkisp_read(ispdev, CIF_DUAL_CROP_CTRL, true);
|
if (src_w == dest_w && src_h == dest_h)
|
ctrl &= ~(CIF_DUAL_CROP_MP_MODE_YUV | CIF_DUAL_CROP_MP_MODE_RAW);
|
else
|
ctrl |= CIF_DUAL_CROP_MP_MODE_YUV;
|
rkisp_write(ispdev, CIF_DUAL_CROP_CTRL, ctrl, false);
|
}
|
|
static void crop_off(struct rkisp_bridge_device *dev)
|
{
|
struct rkisp_device *ispdev = dev->ispdev;
|
u32 ctrl = CIF_DUAL_CROP_GEN_CFG_UPD;
|
|
ctrl = rkisp_read(ispdev, CIF_DUAL_CROP_CTRL, true);
|
ctrl &= ~(CIF_DUAL_CROP_MP_MODE_YUV | CIF_DUAL_CROP_MP_MODE_RAW);
|
rkisp_write(ispdev, CIF_DUAL_CROP_CTRL, ctrl, false);
|
}
|
|
static int bridge_start(struct rkisp_bridge_device *dev)
|
{
|
struct rkisp_device *ispdev = dev->ispdev;
|
struct rkisp_stream *sp_stream;
|
|
sp_stream = &dev->ispdev->cap_dev.stream[RKISP_STREAM_SP];
|
crop_on(dev);
|
config_gain(dev);
|
dev->ops->config(dev);
|
rkisp_start_spstream(sp_stream);
|
|
dev->ispdev->skip_frame = 0;
|
dev->en = true;
|
|
ispdev->cap_dev.is_done_early = false;
|
if (ispdev->send_fbcgain)
|
ispdev->cap_dev.wait_line = 0;
|
if (ispdev->cap_dev.wait_line) {
|
if (ispdev->cap_dev.wait_line < dev->crop.height / 4)
|
ispdev->cap_dev.wait_line = dev->crop.height / 4;
|
ispdev->cap_dev.is_done_early = true;
|
}
|
return 0;
|
}
|
|
static int bridge_stop(struct rkisp_bridge_device *dev)
|
{
|
struct rkisp_stream *sp_stream;
|
int ret;
|
u32 irq;
|
|
sp_stream = &dev->ispdev->cap_dev.stream[RKISP_STREAM_SP];
|
dev->stopping = true;
|
dev->ops->disable(dev);
|
rkisp_stop_spstream(sp_stream);
|
hdr_stop_dmatx(dev->ispdev);
|
ret = wait_event_timeout(dev->done, !dev->en,
|
msecs_to_jiffies(1000));
|
if (!ret)
|
v4l2_warn(&dev->sd,
|
"%s timeout ret:%d\n", __func__, ret);
|
crop_off(dev);
|
dev->stopping = false;
|
dev->en = false;
|
irq = dev->cfg->frame_end_id;
|
irq = (irq == MI_MPFBC_FRAME) ? ISP_FRAME_MPFBC : ISP_FRAME_MP;
|
dev->ispdev->irq_ends_mask &= ~irq;
|
drain_workqueue(dev->wq);
|
|
/* make sure ispp last frame done */
|
if (dev->work_mode & ISP_ISPP_QUICK) {
|
rkisp_clear_bits(dev->ispdev, MI_IMSC,
|
dev->cfg->frame_end_id, true);
|
usleep_range(20000, 25000);
|
}
|
return 0;
|
}
|
|
static int bridge_start_stream(struct v4l2_subdev *sd)
|
{
|
struct rkisp_bridge_device *dev = v4l2_get_subdevdata(sd);
|
int ret = -EINVAL;
|
|
if (WARN_ON(dev->en))
|
return -EBUSY;
|
|
if (dev->ispdev->isp_sdev.out_fmt.fmt_type == FMT_BAYER) {
|
v4l2_err(sd, "no support raw from isp to ispp\n");
|
goto free_buf;
|
}
|
|
if (dev->ispdev->isp_inp & INP_CSI ||
|
dev->ispdev->isp_inp & INP_DVP ||
|
dev->ispdev->isp_inp & INP_LVDS ||
|
dev->ispdev->isp_inp & INP_CIF) {
|
/* Always update sensor info in case media topology changed */
|
ret = rkisp_update_sensor_info(dev->ispdev);
|
if (ret < 0) {
|
v4l2_err(sd, "update sensor info failed %d\n", ret);
|
goto free_buf;
|
}
|
}
|
|
/* enable clocks/power-domains */
|
ret = dev->ispdev->pipe.open(&dev->ispdev->pipe, &sd->entity, true);
|
if (ret < 0)
|
goto free_buf;
|
|
hdr_config_dmatx(dev->ispdev);
|
ret = bridge_start(dev);
|
if (ret)
|
goto close_pipe;
|
|
/* start sub-devices */
|
ret = dev->ispdev->pipe.set_stream(&dev->ispdev->pipe, true);
|
if (ret < 0)
|
goto stop_bridge;
|
|
ret = media_pipeline_start(&sd->entity, &dev->ispdev->pipe.pipe);
|
if (ret < 0)
|
goto pipe_stream_off;
|
|
return 0;
|
pipe_stream_off:
|
dev->ispdev->pipe.set_stream(&dev->ispdev->pipe, false);
|
stop_bridge:
|
bridge_stop(dev);
|
close_pipe:
|
dev->ispdev->pipe.close(&dev->ispdev->pipe);
|
hdr_destroy_buf(dev->ispdev);
|
free_buf:
|
free_bridge_buf(dev);
|
v4l2_err(&dev->sd, "%s fail:%d\n", __func__, ret);
|
return ret;
|
}
|
|
static void bridge_destroy_buf(struct rkisp_bridge_device *dev)
|
{
|
free_bridge_buf(dev);
|
hdr_destroy_buf(dev->ispdev);
|
}
|
|
static int bridge_stop_stream(struct v4l2_subdev *sd)
|
{
|
struct rkisp_bridge_device *dev = v4l2_get_subdevdata(sd);
|
|
bridge_stop(dev);
|
media_pipeline_stop(&sd->entity);
|
dev->ispdev->pipe.set_stream(&dev->ispdev->pipe, false);
|
dev->ispdev->pipe.close(&dev->ispdev->pipe);
|
bridge_destroy_buf(dev);
|
return 0;
|
}
|
|
static int bridge_get_set_fmt(struct v4l2_subdev *sd,
|
struct v4l2_subdev_pad_config *cfg,
|
struct v4l2_subdev_format *fmt)
|
{
|
struct rkisp_bridge_device *dev = v4l2_get_subdevdata(sd);
|
|
if (!fmt)
|
return -EINVAL;
|
|
/* get isp out format */
|
fmt->pad = RKISP_ISP_PAD_SOURCE_PATH;
|
fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
|
return v4l2_subdev_call(&dev->ispdev->isp_sdev.sd,
|
pad, get_fmt, NULL, fmt);
|
}
|
|
static int bridge_set_selection(struct v4l2_subdev *sd,
|
struct v4l2_subdev_pad_config *cfg,
|
struct v4l2_subdev_selection *sel)
|
{
|
struct rkisp_bridge_device *dev = v4l2_get_subdevdata(sd);
|
struct rkisp_isp_subdev *isp_sd = &dev->ispdev->isp_sdev;
|
u32 src_w = isp_sd->out_crop.width;
|
u32 src_h = isp_sd->out_crop.height;
|
struct v4l2_rect *crop;
|
|
if (!sel)
|
return -EINVAL;
|
if (sel->target != V4L2_SEL_TGT_CROP)
|
return -EINVAL;
|
|
crop = &sel->r;
|
crop->left = clamp_t(u32, crop->left, 0, src_w);
|
crop->top = clamp_t(u32, crop->top, 0, src_h);
|
crop->width = clamp_t(u32, crop->width,
|
CIF_ISP_OUTPUT_W_MIN, src_w - crop->left);
|
crop->height = clamp_t(u32, crop->height,
|
CIF_ISP_OUTPUT_H_MIN, src_h - crop->top);
|
|
dev->crop = *crop;
|
return 0;
|
}
|
|
static int bridge_get_selection(struct v4l2_subdev *sd,
|
struct v4l2_subdev_pad_config *cfg,
|
struct v4l2_subdev_selection *sel)
|
{
|
struct rkisp_bridge_device *dev = v4l2_get_subdevdata(sd);
|
struct rkisp_isp_subdev *isp_sd = &dev->ispdev->isp_sdev;
|
struct v4l2_rect *crop;
|
|
if (!sel)
|
return -EINVAL;
|
|
crop = &sel->r;
|
switch (sel->target) {
|
case V4L2_SEL_TGT_CROP_BOUNDS:
|
*crop = isp_sd->out_crop;
|
break;
|
case V4L2_SEL_TGT_CROP:
|
*crop = dev->crop;
|
break;
|
default:
|
return -EINVAL;
|
}
|
|
return 0;
|
}
|
|
static int bridge_s_rx_buffer(struct v4l2_subdev *sd,
|
void *buf, unsigned int *size)
|
{
|
struct rkisp_bridge_device *dev = v4l2_get_subdevdata(sd);
|
struct rkisp_hw_dev *hw = dev->ispdev->hw_dev;
|
struct rkisp_ispp_buf *dbufs = buf;
|
unsigned long lock_flags = 0;
|
|
spin_lock_irqsave(&hw->buf_lock, lock_flags);
|
/* size isn't using now */
|
if (!dbufs || !hw->buf_init_cnt) {
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
return -EINVAL;
|
}
|
list_add_tail(&dbufs->list, &hw->list);
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
return 0;
|
}
|
|
static int bridge_s_stream(struct v4l2_subdev *sd, int on)
|
{
|
struct rkisp_bridge_device *dev = v4l2_get_subdevdata(sd);
|
struct rkisp_hw_dev *hw = dev->ispdev->hw_dev;
|
int ret = 0;
|
|
v4l2_dbg(1, rkisp_debug, sd,
|
"%s %d\n", __func__, on);
|
|
mutex_lock(&hw->dev_lock);
|
if (on) {
|
memset(&dev->dbg, 0, sizeof(dev->dbg));
|
atomic_inc(&dev->ispdev->cap_dev.refcnt);
|
ret = bridge_start_stream(sd);
|
} else {
|
if (dev->en)
|
ret = bridge_stop_stream(sd);
|
atomic_dec(&dev->ispdev->cap_dev.refcnt);
|
}
|
mutex_unlock(&hw->dev_lock);
|
|
return ret;
|
}
|
|
static int bridge_s_power(struct v4l2_subdev *sd, int on)
|
{
|
int ret = 0;
|
|
v4l2_dbg(1, rkisp_debug, sd,
|
"%s %d\n", __func__, on);
|
|
if (on)
|
ret = v4l2_pipeline_pm_use(&sd->entity, 1);
|
else
|
ret = v4l2_pipeline_pm_use(&sd->entity, 0);
|
|
return ret;
|
}
|
|
static long bridge_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
{
|
struct rkisp_bridge_device *dev = v4l2_get_subdevdata(sd);
|
struct rkisp_ispp_mode *mode;
|
struct max_input *max_in;
|
long ret = 0;
|
|
switch (cmd) {
|
case RKISP_ISPP_CMD_SET_FMT:
|
max_in = arg;
|
dev->ispdev->hw_dev->max_in = *max_in;
|
break;
|
case RKISP_ISPP_CMD_SET_MODE:
|
mode = arg;
|
dev->work_mode = mode->work_mode;
|
dev->buf_num = mode->buf_num;
|
ret = config_mode(dev);
|
rkisp_chk_tb_over(dev->ispdev);
|
break;
|
default:
|
ret = -ENOIOCTLCMD;
|
}
|
|
return ret;
|
}
|
|
static const struct v4l2_subdev_pad_ops bridge_pad_ops = {
|
.set_fmt = bridge_get_set_fmt,
|
.get_fmt = bridge_get_set_fmt,
|
.get_selection = bridge_get_selection,
|
.set_selection = bridge_set_selection,
|
};
|
|
static const struct v4l2_subdev_video_ops bridge_video_ops = {
|
.s_rx_buffer = bridge_s_rx_buffer,
|
.s_stream = bridge_s_stream,
|
};
|
|
static const struct v4l2_subdev_core_ops bridge_core_ops = {
|
.s_power = bridge_s_power,
|
.ioctl = bridge_ioctl,
|
};
|
|
static struct v4l2_subdev_ops bridge_v4l2_ops = {
|
.core = &bridge_core_ops,
|
.video = &bridge_video_ops,
|
.pad = &bridge_pad_ops,
|
};
|
|
int rkisp_bridge_get_fbcbuf_fd(struct rkisp_device *dev, struct isp2x_buf_idxfd *idxfd)
|
{
|
struct rkisp_hw_dev *hw = dev->hw_dev;
|
struct rkisp_bridge_device *br_dev = &dev->br_dev;
|
struct rkisp_bridge_buf *buf;
|
struct rkisp_dummy_buffer *dummy;
|
unsigned long lock_flags = 0;
|
int i, j, buf_idx;
|
|
spin_lock_irqsave(&hw->buf_lock, lock_flags);
|
if (!hw->is_buf_init) {
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
return -EAGAIN;
|
}
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
|
buf_idx = 0;
|
for (i = 0; i < br_dev->buf_num; i++) {
|
buf = &hw->bufs[i];
|
for (j = 0; j < GROUP_BUF_MAX; j++) {
|
dummy = &buf->dummy[j];
|
buf->dbufs.dfd[j] = dma_buf_fd(dummy->dbuf, O_CLOEXEC);
|
get_dma_buf(buf->dbufs.dbuf[j]);
|
idxfd->index[buf_idx] = buf->dbufs.didx[j];
|
idxfd->dmafd[buf_idx] = buf->dbufs.dfd[j];
|
buf_idx++;
|
}
|
}
|
|
idxfd->buf_num = buf_idx;
|
|
return 0;
|
}
|
|
void rkisp_bridge_sendtopp_buffer(struct rkisp_device *dev, u32 dev_id, u32 buf_idx)
|
{
|
struct rkisp_hw_dev *hw = dev->hw_dev;
|
struct rkisp_bridge_device *br_dev = &dev->br_dev;
|
struct v4l2_subdev *sd = v4l2_get_subdev_hostdata(&br_dev->sd);
|
struct rkisp_ispp_buf *cur_buf, *cur_buf_tmp, *find_buf;
|
struct rkisp_bridge_buf *buf;
|
unsigned long lock_flags = 0;
|
bool find_flg = false;
|
u32 *vaddr, size;
|
u64 pic_ts, gain_ts;
|
|
spin_lock_irqsave(&hw->buf_lock, lock_flags);
|
list_for_each_entry(cur_buf, &hw->rpt_list, list) {
|
if (cur_buf->index == dev_id && cur_buf->buf_idx == buf_idx) {
|
find_flg = true;
|
break;
|
}
|
}
|
|
if (find_flg) {
|
list_del(&cur_buf->list);
|
find_buf = cur_buf;
|
list_for_each_entry_safe(cur_buf, cur_buf_tmp, &hw->rpt_list, list) {
|
if ((cur_buf->frame_id < find_buf->frame_id) &&
|
(cur_buf->index == find_buf->index)) {
|
list_del_init(&cur_buf->list);
|
v4l2_dbg(3, rkisp_debug, &br_dev->sd,
|
"%s send buffer to pp, frame_id %d\n",
|
__func__, cur_buf->frame_id);
|
|
buf = to_bridge_buf(cur_buf);
|
rkisp_prepare_buffer(dev, &buf->dummy[GROUP_BUF_GAIN]);
|
vaddr = buf->dummy[GROUP_BUF_PIC].vaddr;
|
size = buf->dummy[GROUP_BUF_PIC].size;
|
pic_ts = *(u64 *)(vaddr + size / 4 - 2);
|
|
vaddr = buf->dummy[GROUP_BUF_GAIN].vaddr;
|
size = buf->dummy[GROUP_BUF_GAIN].size;
|
gain_ts = *(u64 *)(vaddr + size / 4 - 2);
|
if (abs(pic_ts - gain_ts) > 5000000LL) {
|
v4l2_info(&br_dev->sd,
|
"%s: frame %d, timestamp is not match (pic_ts %lld, gain_ts %lld)",
|
__func__, cur_buf->frame_id, pic_ts, gain_ts);
|
}
|
cur_buf->is_move_judge = true;
|
v4l2_subdev_call(sd, video, s_rx_buffer, cur_buf, NULL);
|
}
|
}
|
|
v4l2_dbg(3, rkisp_debug, &br_dev->sd,
|
"%s send buffer to pp, frame_id %d\n",
|
__func__, find_buf->frame_id);
|
|
buf = to_bridge_buf(find_buf);
|
rkisp_prepare_buffer(dev, &buf->dummy[GROUP_BUF_GAIN]);
|
vaddr = buf->dummy[GROUP_BUF_PIC].vaddr;
|
size = buf->dummy[GROUP_BUF_PIC].size;
|
pic_ts = *(u64 *)(vaddr + size / 4 - 2);
|
|
vaddr = buf->dummy[GROUP_BUF_GAIN].vaddr;
|
size = buf->dummy[GROUP_BUF_GAIN].size;
|
gain_ts = *(u64 *)(vaddr + size / 4 - 2);
|
if (abs(pic_ts - gain_ts) > 5000000LL) {
|
v4l2_info(&br_dev->sd,
|
"%s: frame %d, timestamp is not match (pic_ts %lld, gain_ts %lld)",
|
__func__, find_buf->frame_id, pic_ts, gain_ts);
|
}
|
find_buf->is_move_judge = true;
|
v4l2_subdev_call(sd, video, s_rx_buffer, find_buf, NULL);
|
}
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
}
|
|
void rkisp_bridge_save_spbuf(struct rkisp_device *dev, struct rkisp_buffer *sp_buf)
|
{
|
struct rkisp_hw_dev *hw = dev->hw_dev;
|
struct rkisp_bridge_device *br_dev = &dev->br_dev;
|
unsigned long lock_flags = 0;
|
|
spin_lock_irqsave(&hw->buf_lock, lock_flags);
|
if (dev->cur_spbuf) {
|
v4l2_dbg(1, rkisp_debug, &br_dev->sd,
|
"%s old sp buf is exit, frame_id %d\n",
|
__func__, dev->cur_spbuf->vb.sequence);
|
rkisp_spbuf_queue(&dev->cap_dev.stream[RKISP_STREAM_SP], dev->cur_spbuf);
|
dev->cur_spbuf = NULL;
|
dev->cap_dev.stream[RKISP_STREAM_SP].dbg.frameloss++;
|
}
|
dev->cur_spbuf = sp_buf;
|
rkisp_bridge_try_sendtohal(dev);
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
}
|
|
void rkisp_bridge_stop_spstream(struct rkisp_device *dev)
|
{
|
struct rkisp_hw_dev *hw = dev->hw_dev;
|
unsigned long lock_flags = 0;
|
|
spin_lock_irqsave(&hw->buf_lock, lock_flags);
|
if (dev->cur_spbuf) {
|
rkisp_spbuf_queue(&dev->cap_dev.stream[RKISP_STREAM_SP], dev->cur_spbuf);
|
dev->cur_spbuf = NULL;
|
}
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
}
|
|
void rkisp_bridge_update_mi(struct rkisp_device *dev, u32 isp_mis)
|
{
|
struct rkisp_bridge_device *br = &dev->br_dev;
|
struct rkisp_hw_dev *hw = dev->hw_dev;
|
struct rkisp_bridge_buf *buf;
|
unsigned long lock_flags = 0;
|
u32 val;
|
|
if (dev->isp_ver != ISP_V20 || !br->en ||
|
br->work_mode & ISP_ISPP_QUICK ||
|
isp_mis & CIF_ISP_FRAME)
|
return;
|
|
br->fs_ns = ktime_get_ns();
|
spin_lock_irqsave(&hw->buf_lock, lock_flags);
|
if (!hw->nxt_buf && !list_empty(&hw->list)) {
|
hw->nxt_buf = list_first_entry(&hw->list,
|
struct rkisp_ispp_buf, list);
|
list_del(&hw->nxt_buf->list);
|
}
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
|
if (hw->nxt_buf) {
|
buf = to_bridge_buf(hw->nxt_buf);
|
val = buf->dummy[GROUP_BUF_PIC].dma_addr;
|
rkisp_write(dev, br->cfg->reg.y0_base, val, true);
|
val += br->cfg->offset;
|
rkisp_write(dev, br->cfg->reg.uv0_base, val, true);
|
val = buf->dummy[GROUP_BUF_GAIN].dma_addr;
|
rkisp_write(dev, br->cfg->reg.g0_base, val, true);
|
}
|
|
if (dev->cap_dev.is_done_early && !br->frame_early) {
|
br->frame_early = true;
|
hrtimer_start(&br->frame_qst, ns_to_ktime(1000000), HRTIMER_MODE_REL);
|
}
|
|
v4l2_dbg(2, rkisp_debug, &br->sd,
|
"update pic(shd:0x%x base:0x%x) gain(shd:0x%x base:0x%x)\n",
|
rkisp_read(dev, br->cfg->reg.y0_base_shd, true),
|
rkisp_read(dev, br->cfg->reg.y0_base, true),
|
rkisp_read(dev, br->cfg->reg.g0_base_shd, true),
|
rkisp_read(dev, br->cfg->reg.g0_base, true));
|
}
|
|
void rkisp_bridge_isr(u32 *mis_val, struct rkisp_device *dev)
|
{
|
struct rkisp_bridge_device *bridge = &dev->br_dev;
|
void __iomem *base = dev->base_addr;
|
u32 irq;
|
|
if (!bridge->en)
|
return;
|
|
if (!bridge->cfg ||
|
(bridge->cfg &&
|
!(*mis_val & bridge->cfg->frame_end_id)))
|
return;
|
|
irq = bridge->cfg->frame_end_id;
|
*mis_val &= ~irq;
|
writel(irq, base + CIF_MI_ICR);
|
|
if (bridge->stopping) {
|
if (bridge->ops->is_stopped(bridge)) {
|
bridge->en = false;
|
bridge->stopping = false;
|
wake_up(&bridge->done);
|
}
|
}
|
|
irq = (irq == MI_MPFBC_FRAME) ? ISP_FRAME_MPFBC : ISP_FRAME_MP;
|
if (!(bridge->work_mode & ISP_ISPP_QUICK)) {
|
frame_end(bridge, bridge->en, FRAME_IRQ);
|
if (!bridge->en)
|
dev->irq_ends_mask &= ~irq;
|
}
|
rkisp_check_idle(dev, irq);
|
}
|
|
int rkisp_register_bridge_subdev(struct rkisp_device *dev,
|
struct v4l2_device *v4l2_dev)
|
{
|
struct rkisp_bridge_device *bridge = &dev->br_dev;
|
struct v4l2_subdev *sd;
|
struct media_entity *source, *sink;
|
int ret;
|
|
memset(bridge, 0, sizeof(*bridge));
|
if (dev->isp_ver != ISP_V20)
|
return 0;
|
|
bridge->ispdev = dev;
|
sd = &bridge->sd;
|
v4l2_subdev_init(sd, &bridge_v4l2_ops);
|
//sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
sd->entity.obj_type = 0;
|
snprintf(sd->name, sizeof(sd->name), "%s", BRIDGE_DEV_NAME);
|
bridge->pad.flags = MEDIA_PAD_FL_SINK;
|
ret = media_entity_pads_init(&sd->entity, 1, &bridge->pad);
|
if (ret < 0)
|
return ret;
|
sd->owner = THIS_MODULE;
|
v4l2_set_subdevdata(sd, bridge);
|
sd->grp_id = GRP_ID_ISP_BRIDGE;
|
ret = v4l2_device_register_subdev(v4l2_dev, sd);
|
if (ret < 0) {
|
v4l2_err(sd, "Failed to register subdev\n");
|
goto free_media;
|
}
|
bridge->crop = dev->isp_sdev.out_crop;
|
/* bridge links */
|
bridge->linked = true;
|
source = &dev->isp_sdev.sd.entity;
|
sink = &sd->entity;
|
ret = media_create_pad_link(source, RKISP_ISP_PAD_SOURCE_PATH,
|
sink, 0, bridge->linked);
|
init_waitqueue_head(&bridge->done);
|
bridge->wq = alloc_workqueue("rkisp bridge workqueue",
|
WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
|
hrtimer_init(&bridge->frame_qst, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
bridge->frame_qst.function = rkisp_bridge_frame_done_early;
|
return ret;
|
|
free_media:
|
media_entity_cleanup(&sd->entity);
|
return ret;
|
}
|
|
void rkisp_unregister_bridge_subdev(struct rkisp_device *dev)
|
{
|
struct v4l2_subdev *sd = &dev->br_dev.sd;
|
|
if (dev->isp_ver != ISP_V20)
|
return;
|
v4l2_device_unregister_subdev(sd);
|
media_entity_cleanup(&sd->entity);
|
}
|
|
void rkisp_get_bridge_sd(struct platform_device *dev,
|
struct v4l2_subdev **sd)
|
{
|
struct rkisp_device *isp_dev = platform_get_drvdata(dev);
|
|
if (isp_dev)
|
*sd = &isp_dev->br_dev.sd;
|
else
|
*sd = NULL;
|
}
|
EXPORT_SYMBOL(rkisp_get_bridge_sd);
|