// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Rockchip ISP1 Driver - ISP Subdevice
*
* Copyright ( C ) 2019 Collabora , Ltd .
*
* Based on Rockchip ISP1 driver by Rockchip Electronics Co . , Ltd .
* Copyright ( C ) 2017 Rockchip Electronics Co . , Ltd .
*/
#include <linux/iopoll.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <media/v4l2-event.h>
#include "rkisp1-common.h"
#define RKISP1_DEF_SINK_PAD_FMT MEDIA_BUS_FMT_SRGGB10_1X10
#define RKISP1_DEF_SRC_PAD_FMT MEDIA_BUS_FMT_YUYV8_2X8
#define RKISP1_ISP_DEV_NAME RKISP1_DRIVER_NAME "_isp"
/*
* NOTE : MIPI controller and input MUX are also configured in this file .
* This is because ISP Subdev describes not only ISP submodule ( input size ,
* format , output size , format ) , but also a virtual route device .
*/
/*
* There are many variables named with format / frame in below code ,
* please see here for their meaning .
* Cropping in the sink pad defines the image region from the sensor .
* Cropping in the source pad defines the region for the Image Stabilizer ( IS )
*
* Cropping regions of ISP
*
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* | Sensor image |
* | + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |
* | | CIF_ISP_ACQ ( for black level ) | |
* | | sink pad format | |
* | | + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | |
* | | | CIF_ISP_OUT | | |
* | | | sink pad crop | | |
* | | | + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | | |
* | | | | CIF_ISP_IS | | | |
* | | | | source pad crop and format | | | |
* | | | + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | | |
* | | + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | |
* | + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
*/
/* -----------------------------------------------------------------------------
* Media block control ( i . MX8MP only )
*/
#define ISP_DEWARP_CONTROL 0 x0138
#define ISP_DEWARP_CONTROL_MIPI_CSI2_HS_POLARITY BIT(22 )
#define ISP_DEWARP_CONTROL_MIPI_CSI2_VS_SEL_RISING (0 << 20 )
#define ISP_DEWARP_CONTROL_MIPI_CSI2_VS_SEL_NEGATIVE (1 << 20 )
#define ISP_DEWARP_CONTROL_MIPI_CSI2_VS_SEL_POSITIVE (2 << 20 )
#define ISP_DEWARP_CONTROL_MIPI_CSI2_VS_SEL_FALLING (3 << 20 )
#define ISP_DEWARP_CONTROL_MIPI_CSI2_VS_SEL_MASK GENMASK(21 , 20 )
#define ISP_DEWARP_CONTROL_MIPI_ISP2_LEFT_JUST_MODE BIT(19 )
#define ISP_DEWARP_CONTROL_MIPI_ISP2_DATA_TYPE(dt) ((dt) << 13 )
#define ISP_DEWARP_CONTROL_MIPI_ISP2_DATA_TYPE_MASK GENMASK(18 , 13 )
#define ISP_DEWARP_CONTROL_MIPI_CSI1_HS_POLARITY BIT(12 )
#define ISP_DEWARP_CONTROL_MIPI_CSI1_VS_SEL_RISING (0 << 10 )
#define ISP_DEWARP_CONTROL_MIPI_CSI1_VS_SEL_NEGATIVE (1 << 10 )
#define ISP_DEWARP_CONTROL_MIPI_CSI1_VS_SEL_POSITIVE (2 << 10 )
#define ISP_DEWARP_CONTROL_MIPI_CSI1_VS_SEL_FALLING (3 << 10 )
#define ISP_DEWARP_CONTROL_MIPI_CSI1_VS_SEL_MASK GENMASK(11 , 10 )
#define ISP_DEWARP_CONTROL_MIPI_ISP1_LEFT_JUST_MODE BIT(9 )
#define ISP_DEWARP_CONTROL_MIPI_ISP1_DATA_TYPE(dt) ((dt) << 3 )
#define ISP_DEWARP_CONTROL_MIPI_ISP1_DATA_TYPE_MASK GENMASK(8 , 3 )
#define ISP_DEWARP_CONTROL_GPR_ISP_1_DISABLE BIT(1 )
#define ISP_DEWARP_CONTROL_GPR_ISP_0_DISABLE BIT(0 )
static int rkisp1_gasket_enable(struct rkisp1_device *rkisp1,
struct media_pad *source)
{
struct v4l2_subdev *source_sd;
struct v4l2_mbus_frame_desc fd;
unsigned int dt;
u32 mask;
u32 val;
int ret;
/*
* Configure and enable the gasket with the CSI - 2 data type . Set the
* vsync polarity as active high , as that is what the ISP is configured
* to expect in ISP_ACQ_PROP . Enable left justification , as the i . MX8MP
* ISP has a 16 - bit wide input and expects data to be left - aligned .
*/
source_sd = media_entity_to_v4l2_subdev(source->entity);
ret = v4l2_subdev_call(source_sd, pad, get_frame_desc,
source->index, &fd);
if (ret) {
dev_err(rkisp1->dev,
"failed to get frame descriptor from '%s':%u: %d\n" ,
source_sd->name, 0 , ret);
return ret;
}
if (fd.num_entries != 1 ) {
dev_err(rkisp1->dev, "invalid frame descriptor for '%s':%u\n" ,
source_sd->name, 0 );
return -EINVAL;
}
dt = fd.entry[0 ].bus.csi2.dt;
if (rkisp1->gasket_id == 0 ) {
mask = ISP_DEWARP_CONTROL_MIPI_CSI1_HS_POLARITY
| ISP_DEWARP_CONTROL_MIPI_CSI1_VS_SEL_MASK
| ISP_DEWARP_CONTROL_MIPI_ISP1_LEFT_JUST_MODE
| ISP_DEWARP_CONTROL_MIPI_ISP1_DATA_TYPE_MASK
| ISP_DEWARP_CONTROL_GPR_ISP_0_DISABLE;
val = ISP_DEWARP_CONTROL_MIPI_CSI1_VS_SEL_POSITIVE
| ISP_DEWARP_CONTROL_MIPI_ISP1_LEFT_JUST_MODE
| ISP_DEWARP_CONTROL_MIPI_ISP1_DATA_TYPE(dt);
} else {
mask = ISP_DEWARP_CONTROL_MIPI_CSI2_HS_POLARITY
| ISP_DEWARP_CONTROL_MIPI_CSI2_VS_SEL_MASK
| ISP_DEWARP_CONTROL_MIPI_ISP2_LEFT_JUST_MODE
| ISP_DEWARP_CONTROL_MIPI_ISP2_DATA_TYPE_MASK
| ISP_DEWARP_CONTROL_GPR_ISP_1_DISABLE;
val = ISP_DEWARP_CONTROL_MIPI_CSI2_VS_SEL_POSITIVE
| ISP_DEWARP_CONTROL_MIPI_ISP2_LEFT_JUST_MODE
| ISP_DEWARP_CONTROL_MIPI_ISP2_DATA_TYPE(dt);
}
regmap_update_bits(rkisp1->gasket, ISP_DEWARP_CONTROL, mask, val);
return 0 ;
}
static void rkisp1_gasket_disable(struct rkisp1_device *rkisp1)
{
u32 mask;
u32 val;
if (rkisp1->gasket_id == 1 ) {
mask = ISP_DEWARP_CONTROL_MIPI_ISP2_LEFT_JUST_MODE
| ISP_DEWARP_CONTROL_MIPI_ISP2_DATA_TYPE_MASK
| ISP_DEWARP_CONTROL_GPR_ISP_1_DISABLE;
val = ISP_DEWARP_CONTROL_GPR_ISP_1_DISABLE;
} else {
mask = ISP_DEWARP_CONTROL_MIPI_ISP1_LEFT_JUST_MODE
| ISP_DEWARP_CONTROL_MIPI_ISP1_DATA_TYPE_MASK
| ISP_DEWARP_CONTROL_GPR_ISP_0_DISABLE;
val = ISP_DEWARP_CONTROL_GPR_ISP_0_DISABLE;
}
regmap_update_bits(rkisp1->gasket, ISP_DEWARP_CONTROL, mask, val);
}
/* ----------------------------------------------------------------------------
* Camera Interface registers configurations
*/
/*
* Image Stabilization .
* This should only be called when configuring CIF
* or at the frame end interrupt
*/
static void rkisp1_config_ism(struct rkisp1_isp *isp,
const struct v4l2_subdev_state *sd_state)
{
const struct v4l2_rect *src_crop =
v4l2_subdev_state_get_crop(sd_state,
RKISP1_ISP_PAD_SOURCE_VIDEO);
struct rkisp1_device *rkisp1 = isp->rkisp1;
u32 val;
rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_RECENTER, 0 );
rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_MAX_DX, 0 );
rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_MAX_DY, 0 );
rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_DISPLACE, 0 );
rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_H_OFFS, src_crop->left);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_V_OFFS, src_crop->top);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_H_SIZE, src_crop->width);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_V_SIZE, src_crop->height);
/* IS(Image Stabilization) is always on, working as output crop */
rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_CTRL, 1 );
val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_CTRL);
val |= RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD;
rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val);
}
/*
* configure ISP blocks with input format , size . . . . . .
*/
static int rkisp1_config_isp(struct rkisp1_isp *isp,
const struct v4l2_subdev_state *sd_state,
enum v4l2_mbus_type mbus_type, u32 mbus_flags)
{
struct rkisp1_device *rkisp1 = isp->rkisp1;
u32 isp_ctrl = 0 , irq_mask = 0 , acq_mult = 0 , acq_prop = 0 ;
const struct rkisp1_mbus_info *sink_fmt;
const struct rkisp1_mbus_info *src_fmt;
const struct v4l2_mbus_framefmt *src_frm;
const struct v4l2_mbus_framefmt *sink_frm;
const struct v4l2_rect *sink_crop;
sink_frm = v4l2_subdev_state_get_format(sd_state,
RKISP1_ISP_PAD_SINK_VIDEO);
sink_crop = v4l2_subdev_state_get_crop(sd_state,
RKISP1_ISP_PAD_SINK_VIDEO);
src_frm = v4l2_subdev_state_get_format(sd_state,
RKISP1_ISP_PAD_SOURCE_VIDEO);
sink_fmt = rkisp1_mbus_info_get_by_code(sink_frm->code);
src_fmt = rkisp1_mbus_info_get_by_code(src_frm->code);
if (sink_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
acq_mult = 1 ;
if (src_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
if (mbus_type == V4L2_MBUS_BT656)
isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_RAW_PICT_ITU656;
else
isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_RAW_PICT;
} else {
rkisp1_write(rkisp1, RKISP1_CIF_ISP_DEMOSAIC,
RKISP1_CIF_ISP_DEMOSAIC_TH(0 xc));
if (mbus_type == V4L2_MBUS_BT656)
isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_BAYER_ITU656;
else
isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_BAYER_ITU601;
}
} else if (sink_fmt->pixel_enc == V4L2_PIXEL_ENC_YUV) {
acq_mult = 2 ;
if (mbus_type == V4L2_MBUS_CSI2_DPHY) {
isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU601;
} else {
if (mbus_type == V4L2_MBUS_BT656)
isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU656;
else
isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU601;
}
irq_mask |= RKISP1_CIF_ISP_DATA_LOSS;
}
/* Set up input acquisition properties */
if (mbus_type == V4L2_MBUS_BT656 || mbus_type == V4L2_MBUS_PARALLEL) {
if (mbus_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_POS_EDGE;
switch (sink_fmt->bus_width) {
case 8 :
acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_8B_ZERO;
break ;
case 10 :
acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_10B_ZERO;
break ;
case 12 :
acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_12B;
break ;
default :
dev_err(rkisp1->dev, "Invalid bus width %u\n" ,
sink_fmt->bus_width);
return -EINVAL;
}
}
if (mbus_type == V4L2_MBUS_PARALLEL) {
if (mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_VSYNC_LOW;
if (mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_HSYNC_LOW;
}
rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, isp_ctrl);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_PROP,
acq_prop | sink_fmt->yuv_seq |
RKISP1_CIF_ISP_ACQ_PROP_BAYER_PAT(sink_fmt->bayer_pat) |
RKISP1_CIF_ISP_ACQ_PROP_FIELD_SEL_ALL);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_NR_FRAMES, 0 );
/* Acquisition Size */
rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_H_OFFS, 0 );
rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_V_OFFS, 0 );
rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_H_SIZE,
acq_mult * sink_frm->width);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_V_SIZE, sink_frm->height);
/* ISP Out Area */
rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_H_OFFS, sink_crop->left);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_V_OFFS, sink_crop->top);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_H_SIZE, sink_crop->width);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_V_SIZE, sink_crop->height);
irq_mask |= RKISP1_CIF_ISP_FRAME | RKISP1_CIF_ISP_V_START |
RKISP1_CIF_ISP_PIC_SIZE_ERROR;
rkisp1_write(rkisp1, RKISP1_CIF_ISP_IMSC, irq_mask);
if (src_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
rkisp1_params_disable(&rkisp1->params);
} else {
const struct v4l2_mbus_framefmt *src_frm;
src_frm = v4l2_subdev_state_get_format(sd_state,
RKISP1_ISP_PAD_SOURCE_VIDEO);
rkisp1_params_pre_configure(&rkisp1->params, sink_fmt->bayer_pat,
src_frm->quantization,
src_frm->ycbcr_enc);
}
isp->sink_fmt = sink_fmt;
return 0 ;
}
/* Configure MUX */
static void rkisp1_config_path(struct rkisp1_isp *isp,
enum v4l2_mbus_type mbus_type)
{
struct rkisp1_device *rkisp1 = isp->rkisp1;
u32 dpcl = rkisp1_read(rkisp1, RKISP1_CIF_VI_DPCL);
if (mbus_type == V4L2_MBUS_BT656 || mbus_type == V4L2_MBUS_PARALLEL)
dpcl |= RKISP1_CIF_VI_DPCL_IF_SEL_PARALLEL;
else if (mbus_type == V4L2_MBUS_CSI2_DPHY)
dpcl |= RKISP1_CIF_VI_DPCL_IF_SEL_MIPI;
rkisp1_write(rkisp1, RKISP1_CIF_VI_DPCL, dpcl);
}
/* Hardware configure Entry */
static int rkisp1_config_cif(struct rkisp1_isp *isp,
struct v4l2_subdev_state *sd_state,
enum v4l2_mbus_type mbus_type, u32 mbus_flags)
{
int ret;
ret = rkisp1_config_isp(isp, sd_state, mbus_type, mbus_flags);
if (ret)
return ret;
rkisp1_config_path(isp, mbus_type);
rkisp1_config_ism(isp, sd_state);
return 0 ;
}
static void rkisp1_isp_stop(struct rkisp1_isp *isp)
{
struct rkisp1_device *rkisp1 = isp->rkisp1;
u32 val;
/*
* ISP ( mi ) stop in mi frame end - > Stop ISP ( mipi ) - >
* Stop ISP ( isp ) - > wait for ISP isp off
*/
/* Mask MI and ISP interrupts */
rkisp1_write(rkisp1, RKISP1_CIF_ISP_IMSC, 0 );
rkisp1_write(rkisp1, RKISP1_CIF_MI_IMSC, 0 );
/* Flush posted writes */
rkisp1_read(rkisp1, RKISP1_CIF_MI_IMSC);
/*
* Wait until the IRQ handler has ended . The IRQ handler may get called
* even after this , but it will return immediately as the MI and ISP
* interrupts have been masked .
*/
synchronize_irq(rkisp1->irqs[RKISP1_IRQ_ISP]);
if (rkisp1->irqs[RKISP1_IRQ_ISP] != rkisp1->irqs[RKISP1_IRQ_MI])
synchronize_irq(rkisp1->irqs[RKISP1_IRQ_MI]);
/* Clear MI and ISP interrupt status */
rkisp1_write(rkisp1, RKISP1_CIF_ISP_ICR, ~0 );
rkisp1_write(rkisp1, RKISP1_CIF_MI_ICR, ~0 );
/* stop ISP */
val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_CTRL);
val &= ~(RKISP1_CIF_ISP_CTRL_ISP_INFORM_ENABLE |
RKISP1_CIF_ISP_CTRL_ISP_ENABLE);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val);
val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_CTRL);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL,
val | RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD);
readx_poll_timeout(readl, rkisp1->base_addr + RKISP1_CIF_ISP_RIS,
val, val & RKISP1_CIF_ISP_OFF, 20 , 100 );
rkisp1_write(rkisp1, RKISP1_CIF_VI_IRCL,
RKISP1_CIF_VI_IRCL_MIPI_SW_RST |
RKISP1_CIF_VI_IRCL_ISP_SW_RST);
rkisp1_write(rkisp1, RKISP1_CIF_VI_IRCL, 0 x0);
if (rkisp1->info->isp_ver == RKISP1_V_IMX8MP)
rkisp1_gasket_disable(rkisp1);
}
static void rkisp1_config_clk(struct rkisp1_isp *isp)
{
struct rkisp1_device *rkisp1 = isp->rkisp1;
u32 val = RKISP1_CIF_VI_ICCL_ISP_CLK | RKISP1_CIF_VI_ICCL_CP_CLK |
RKISP1_CIF_VI_ICCL_MRSZ_CLK | RKISP1_CIF_VI_ICCL_SRSZ_CLK |
RKISP1_CIF_VI_ICCL_JPEG_CLK | RKISP1_CIF_VI_ICCL_MI_CLK |
RKISP1_CIF_VI_ICCL_IE_CLK | RKISP1_CIF_VI_ICCL_MIPI_CLK |
RKISP1_CIF_VI_ICCL_DCROP_CLK;
rkisp1_write(rkisp1, RKISP1_CIF_VI_ICCL, val);
/* ensure sp and mp can run at the same time in V12 */
if (rkisp1->info->isp_ver == RKISP1_V12) {
val = RKISP1_CIF_CLK_CTRL_MI_Y12 | RKISP1_CIF_CLK_CTRL_MI_SP |
RKISP1_CIF_CLK_CTRL_MI_RAW0 | RKISP1_CIF_CLK_CTRL_MI_RAW1 |
RKISP1_CIF_CLK_CTRL_MI_READ | RKISP1_CIF_CLK_CTRL_MI_RAWRD |
RKISP1_CIF_CLK_CTRL_CP | RKISP1_CIF_CLK_CTRL_IE;
rkisp1_write(rkisp1, RKISP1_CIF_VI_ISP_CLK_CTRL_V12, val);
}
}
static int rkisp1_isp_start(struct rkisp1_isp *isp,
const struct v4l2_subdev_state *sd_state,
struct media_pad *source)
{
struct rkisp1_device *rkisp1 = isp->rkisp1;
const struct v4l2_mbus_framefmt *src_fmt;
const struct rkisp1_mbus_info *src_info;
u32 val;
int ret;
rkisp1_config_clk(isp);
if (rkisp1->info->isp_ver == RKISP1_V_IMX8MP) {
ret = rkisp1_gasket_enable(rkisp1, source);
if (ret)
return ret;
}
/* Activate ISP */
val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_CTRL);
val |= RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD |
RKISP1_CIF_ISP_CTRL_ISP_ENABLE |
RKISP1_CIF_ISP_CTRL_ISP_INFORM_ENABLE;
rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val);
src_fmt = v4l2_subdev_state_get_format(sd_state,
RKISP1_ISP_PAD_SOURCE_VIDEO);
src_info = rkisp1_mbus_info_get_by_code(src_fmt->code);
if (src_info->pixel_enc != V4L2_PIXEL_ENC_BAYER)
rkisp1_params_post_configure(&rkisp1->params);
return 0 ;
}
/* ----------------------------------------------------------------------------
* Subdev pad operations
*/
static inline struct rkisp1_isp *to_rkisp1_isp(struct v4l2_subdev *sd)
{
return container_of(sd, struct rkisp1_isp, sd);
}
static int rkisp1_isp_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
{
unsigned int i, dir;
int pos = 0 ;
if (code->pad == RKISP1_ISP_PAD_SINK_VIDEO) {
dir = RKISP1_ISP_SD_SINK;
} else if (code->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) {
dir = RKISP1_ISP_SD_SRC;
} else {
if (code->index > 0 )
return -EINVAL;
code->code = MEDIA_BUS_FMT_METADATA_FIXED;
return 0 ;
}
for (i = 0 ; ; i++) {
const struct rkisp1_mbus_info *fmt =
rkisp1_mbus_info_get_by_index(i);
if (!fmt)
return -EINVAL;
if (fmt->direction & dir)
pos++;
if (code->index == pos - 1 ) {
code->code = fmt->mbus_code;
if (fmt->pixel_enc == V4L2_PIXEL_ENC_YUV &&
dir == RKISP1_ISP_SD_SRC)
code->flags =
V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION;
return 0 ;
}
}
return -EINVAL;
}
static int rkisp1_isp_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_frame_size_enum *fse)
{
struct rkisp1_isp *isp = to_rkisp1_isp(sd);
const struct rkisp1_mbus_info *mbus_info;
if (fse->pad == RKISP1_ISP_PAD_SINK_PARAMS ||
fse->pad == RKISP1_ISP_PAD_SOURCE_STATS)
return -ENOTTY;
if (fse->index > 0 )
return -EINVAL;
mbus_info = rkisp1_mbus_info_get_by_code(fse->code);
if (!mbus_info)
return -EINVAL;
if (!(mbus_info->direction & RKISP1_ISP_SD_SINK) &&
fse->pad == RKISP1_ISP_PAD_SINK_VIDEO)
return -EINVAL;
if (!(mbus_info->direction & RKISP1_ISP_SD_SRC) &&
fse->pad == RKISP1_ISP_PAD_SOURCE_VIDEO)
return -EINVAL;
fse->min_width = RKISP1_ISP_MIN_WIDTH;
fse->max_width = isp->rkisp1->info->max_width;
fse->min_height = RKISP1_ISP_MIN_HEIGHT;
fse->max_height = isp->rkisp1->info->max_height;
return 0 ;
}
static int rkisp1_isp_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state)
{
struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
struct v4l2_rect *sink_crop, *src_crop;
/* Video. */
sink_fmt = v4l2_subdev_state_get_format(sd_state,
RKISP1_ISP_PAD_SINK_VIDEO);
sink_fmt->width = RKISP1_DEFAULT_WIDTH;
sink_fmt->height = RKISP1_DEFAULT_HEIGHT;
sink_fmt->field = V4L2_FIELD_NONE;
sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT;
sink_fmt->colorspace = V4L2_COLORSPACE_RAW;
sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
sink_crop = v4l2_subdev_state_get_crop(sd_state,
RKISP1_ISP_PAD_SINK_VIDEO);
sink_crop->width = RKISP1_DEFAULT_WIDTH;
sink_crop->height = RKISP1_DEFAULT_HEIGHT;
sink_crop->left = 0 ;
sink_crop->top = 0 ;
src_fmt = v4l2_subdev_state_get_format(sd_state,
RKISP1_ISP_PAD_SOURCE_VIDEO);
*src_fmt = *sink_fmt;
src_fmt->code = RKISP1_DEF_SRC_PAD_FMT;
src_fmt->colorspace = V4L2_COLORSPACE_SRGB;
src_fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
src_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
src_crop = v4l2_subdev_state_get_crop(sd_state,
RKISP1_ISP_PAD_SOURCE_VIDEO);
*src_crop = *sink_crop;
/* Parameters and statistics. */
sink_fmt = v4l2_subdev_state_get_format(sd_state,
RKISP1_ISP_PAD_SINK_PARAMS);
src_fmt = v4l2_subdev_state_get_format(sd_state,
RKISP1_ISP_PAD_SOURCE_STATS);
sink_fmt->width = 0 ;
sink_fmt->height = 0 ;
sink_fmt->field = V4L2_FIELD_NONE;
sink_fmt->code = MEDIA_BUS_FMT_METADATA_FIXED;
*src_fmt = *sink_fmt;
return 0 ;
}
static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp,
struct v4l2_subdev_state *sd_state,
struct v4l2_mbus_framefmt *format)
{
const struct rkisp1_mbus_info *sink_info;
const struct rkisp1_mbus_info *src_info;
struct v4l2_mbus_framefmt *sink_fmt;
struct v4l2_mbus_framefmt *src_fmt;
const struct v4l2_rect *src_crop;
bool set_csc;
sink_fmt = v4l2_subdev_state_get_format(sd_state,
RKISP1_ISP_PAD_SINK_VIDEO);
src_fmt = v4l2_subdev_state_get_format(sd_state,
RKISP1_ISP_PAD_SOURCE_VIDEO);
src_crop = v4l2_subdev_state_get_crop(sd_state,
RKISP1_ISP_PAD_SOURCE_VIDEO);
/*
* Media bus code . The ISP can operate in pass - through mode ( Bayer in ,
* Bayer out or YUV in , YUV out ) or process Bayer data to YUV , but
* can ' t convert from YUV to Bayer .
*/
sink_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
src_fmt->code = format->code;
src_info = rkisp1_mbus_info_get_by_code(src_fmt->code);
if (!src_info || !(src_info->direction & RKISP1_ISP_SD_SRC)) {
src_fmt->code = RKISP1_DEF_SRC_PAD_FMT;
src_info = rkisp1_mbus_info_get_by_code(src_fmt->code);
}
if (sink_info->pixel_enc == V4L2_PIXEL_ENC_YUV &&
src_info->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
src_fmt->code = sink_fmt->code;
src_info = sink_info;
}
/*
* The source width and height must be identical to the source crop
* size .
*/
src_fmt->width = src_crop->width;
src_fmt->height = src_crop->height;
/*
* Copy the color space for the sink pad . When converting from Bayer to
* YUV , default to a limited quantization range .
*/
src_fmt->colorspace = sink_fmt->colorspace;
src_fmt->xfer_func = sink_fmt->xfer_func;
src_fmt->ycbcr_enc = sink_fmt->ycbcr_enc;
if (sink_info->pixel_enc == V4L2_PIXEL_ENC_BAYER &&
src_info->pixel_enc == V4L2_PIXEL_ENC_YUV)
src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
else
src_fmt->quantization = sink_fmt->quantization;
/*
* Allow setting the source color space fields when the SET_CSC flag is
* set and the source format is YUV . If the sink format is YUV , don ' t
* set the color primaries , transfer function or YCbCr encoding as the
* ISP is bypassed in that case and passes YUV data through without
* modifications .
*
* The color primaries and transfer function are configured through the
* cross - talk matrix and tone curve respectively . Settings for those
* hardware blocks are conveyed through the ISP parameters buffer , as
* they need to combine color space information with other image tuning
* characteristics and can ' t thus be computed by the kernel based on the
* color space . The source pad colorspace and xfer_func fields are thus
* ignored by the driver , but can be set by userspace to propagate
* accurate color space information down the pipeline .
*/
set_csc = format->flags & V4L2_MBUS_FRAMEFMT_SET_CSC;
if (set_csc && src_info->pixel_enc == V4L2_PIXEL_ENC_YUV) {
if (sink_info->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
if (format->colorspace != V4L2_COLORSPACE_DEFAULT)
src_fmt->colorspace = format->colorspace;
if (format->xfer_func != V4L2_XFER_FUNC_DEFAULT)
src_fmt->xfer_func = format->xfer_func;
if (format->ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT)
src_fmt->ycbcr_enc = format->ycbcr_enc;
}
if (format->quantization != V4L2_QUANTIZATION_DEFAULT)
src_fmt->quantization = format->quantization;
}
*format = *src_fmt;
/*
* Restore the SET_CSC flag if it was set to indicate support for the
* CSC setting API .
*/
if (set_csc)
format->flags |= V4L2_MBUS_FRAMEFMT_SET_CSC;
}
static void rkisp1_isp_set_src_crop(struct rkisp1_isp *isp,
struct v4l2_subdev_state *sd_state,
struct v4l2_rect *r)
{
struct v4l2_mbus_framefmt *src_fmt;
const struct v4l2_rect *sink_crop;
struct v4l2_rect *src_crop;
src_crop = v4l2_subdev_state_get_crop(sd_state,
RKISP1_ISP_PAD_SOURCE_VIDEO);
sink_crop = v4l2_subdev_state_get_crop(sd_state,
RKISP1_ISP_PAD_SINK_VIDEO);
src_crop->left = ALIGN(r->left, 2 );
src_crop->width = ALIGN(r->width, 2 );
src_crop->top = r->top;
src_crop->height = r->height;
rkisp1_sd_adjust_crop_rect(src_crop, sink_crop);
*r = *src_crop;
/* Propagate to out format */
src_fmt = v4l2_subdev_state_get_format(sd_state,
RKISP1_ISP_PAD_SOURCE_VIDEO);
rkisp1_isp_set_src_fmt(isp, sd_state, src_fmt);
}
static void rkisp1_isp_set_sink_crop(struct rkisp1_isp *isp,
struct v4l2_subdev_state *sd_state,
struct v4l2_rect *r)
{
struct v4l2_rect *sink_crop, *src_crop;
const struct v4l2_mbus_framefmt *sink_fmt;
sink_crop = v4l2_subdev_state_get_crop(sd_state,
RKISP1_ISP_PAD_SINK_VIDEO);
sink_fmt = v4l2_subdev_state_get_format(sd_state,
RKISP1_ISP_PAD_SINK_VIDEO);
sink_crop->left = ALIGN(r->left, 2 );
sink_crop->width = ALIGN(r->width, 2 );
sink_crop->top = r->top;
sink_crop->height = r->height;
rkisp1_sd_adjust_crop(sink_crop, sink_fmt);
*r = *sink_crop;
/* Propagate to out crop */
src_crop = v4l2_subdev_state_get_crop(sd_state,
RKISP1_ISP_PAD_SOURCE_VIDEO);
rkisp1_isp_set_src_crop(isp, sd_state, src_crop);
}
static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp,
struct v4l2_subdev_state *sd_state,
struct v4l2_mbus_framefmt *format)
{
const struct rkisp1_mbus_info *mbus_info;
struct v4l2_mbus_framefmt *sink_fmt;
struct v4l2_rect *sink_crop;
bool is_yuv;
sink_fmt = v4l2_subdev_state_get_format(sd_state,
RKISP1_ISP_PAD_SINK_VIDEO);
sink_fmt->code = format->code;
mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SINK)) {
sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT;
mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
}
sink_fmt->width = clamp_t(u32, format->width,
RKISP1_ISP_MIN_WIDTH,
isp->rkisp1->info->max_width);
sink_fmt->height = clamp_t(u32, format->height,
RKISP1_ISP_MIN_HEIGHT,
isp->rkisp1->info->max_height);
/*
* Adjust the color space fields . Accept any color primaries and
* transfer function for both YUV and Bayer . For YUV any YCbCr encoding
* and quantization range is also accepted . For Bayer formats , the YCbCr
* encoding isn ' t applicable , and the quantization range can only be
* full .
*/
is_yuv = mbus_info->pixel_enc == V4L2_PIXEL_ENC_YUV;
sink_fmt->colorspace = format->colorspace ? :
(is_yuv ? V4L2_COLORSPACE_SRGB :
V4L2_COLORSPACE_RAW);
sink_fmt->xfer_func = format->xfer_func ? :
V4L2_MAP_XFER_FUNC_DEFAULT(sink_fmt->colorspace);
if (is_yuv) {
sink_fmt->ycbcr_enc = format->ycbcr_enc ? :
V4L2_MAP_YCBCR_ENC_DEFAULT(sink_fmt->colorspace);
sink_fmt->quantization = format->quantization ? :
V4L2_MAP_QUANTIZATION_DEFAULT(false , sink_fmt->colorspace,
sink_fmt->ycbcr_enc);
} else {
/*
* The YCbCr encoding isn ' t applicable for non - YUV formats , but
* V4L2 has no " no encoding " value . Hardcode it to Rec . 601 , it
* should be ignored by userspace .
*/
sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
}
*format = *sink_fmt;
/* Propagate to in crop */
sink_crop = v4l2_subdev_state_get_crop(sd_state,
RKISP1_ISP_PAD_SINK_VIDEO);
rkisp1_isp_set_sink_crop(isp, sd_state, sink_crop);
}
static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
struct rkisp1_isp *isp = to_rkisp1_isp(sd);
if (fmt->pad == RKISP1_ISP_PAD_SINK_VIDEO)
rkisp1_isp_set_sink_fmt(isp, sd_state, &fmt->format);
else if (fmt->pad == RKISP1_ISP_PAD_SOURCE_VIDEO)
rkisp1_isp_set_src_fmt(isp, sd_state, &fmt->format);
else
fmt->format = *v4l2_subdev_state_get_format(sd_state,
fmt->pad);
return 0 ;
}
static int rkisp1_isp_get_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
int ret = 0 ;
if (sel->pad != RKISP1_ISP_PAD_SOURCE_VIDEO &&
sel->pad != RKISP1_ISP_PAD_SINK_VIDEO)
return -EINVAL;
switch (sel->target) {
case V4L2_SEL_TGT_CROP_BOUNDS:
if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) {
struct v4l2_mbus_framefmt *fmt;
fmt = v4l2_subdev_state_get_format(sd_state, sel->pad);
sel->r.height = fmt->height;
sel->r.width = fmt->width;
sel->r.left = 0 ;
sel->r.top = 0 ;
} else {
sel->r = *v4l2_subdev_state_get_crop(sd_state,
RKISP1_ISP_PAD_SINK_VIDEO);
}
break ;
case V4L2_SEL_TGT_CROP:
sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad);
break ;
default :
ret = -EINVAL;
break ;
}
return ret;
}
static int rkisp1_isp_set_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
struct rkisp1_isp *isp = to_rkisp1_isp(sd);
int ret = 0 ;
if (sel->target != V4L2_SEL_TGT_CROP)
return -EINVAL;
dev_dbg(isp->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%ux%u\n" , __func__,
sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height);
if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO)
rkisp1_isp_set_sink_crop(isp, sd_state, &sel->r);
else if (sel->pad == RKISP1_ISP_PAD_SOURCE_VIDEO)
rkisp1_isp_set_src_crop(isp, sd_state, &sel->r);
else
ret = -EINVAL;
return ret;
}
static int rkisp1_subdev_link_validate(struct media_link *link)
{
if (link->sink->index == RKISP1_ISP_PAD_SINK_PARAMS)
return 0 ;
return v4l2_subdev_link_validate(link);
}
static const struct v4l2_subdev_pad_ops rkisp1_isp_pad_ops = {
.enum_mbus_code = rkisp1_isp_enum_mbus_code,
.enum_frame_size = rkisp1_isp_enum_frame_size,
.get_selection = rkisp1_isp_get_selection,
.set_selection = rkisp1_isp_set_selection,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = rkisp1_isp_set_fmt,
.link_validate = v4l2_subdev_link_validate_default,
};
/* ----------------------------------------------------------------------------
* Stream operations
*/
static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable)
{
struct rkisp1_isp *isp = to_rkisp1_isp(sd);
struct rkisp1_device *rkisp1 = isp->rkisp1;
struct v4l2_subdev_state *sd_state;
struct media_pad *source_pad;
struct media_pad *sink_pad;
enum v4l2_mbus_type mbus_type;
u32 mbus_flags;
int ret;
if (!enable) {
v4l2_subdev_call(rkisp1->source, video, s_stream, false );
rkisp1_isp_stop(isp);
return 0 ;
}
sink_pad = &isp->pads[RKISP1_ISP_PAD_SINK_VIDEO];
source_pad = media_pad_remote_pad_unique(sink_pad);
if (IS_ERR(source_pad)) {
dev_dbg(rkisp1->dev, "Failed to get source for ISP: %ld\n" ,
PTR_ERR(source_pad));
return -EPIPE;
}
rkisp1->source = media_entity_to_v4l2_subdev(source_pad->entity);
if (!rkisp1->source) {
/* This should really not happen, so is not worth a message. */
return -EPIPE;
}
if (rkisp1->source == &rkisp1->csi.sd) {
mbus_type = V4L2_MBUS_CSI2_DPHY;
mbus_flags = 0 ;
} else {
const struct rkisp1_sensor_async *asd;
struct v4l2_async_connection *asc;
asc = v4l2_async_connection_unique(rkisp1->source);
if (!asc)
return -EPIPE;
asd = container_of(asc, struct rkisp1_sensor_async, asd);
mbus_type = asd->mbus_type;
mbus_flags = asd->mbus_flags;
}
isp->frame_sequence = -1 ;
sd_state = v4l2_subdev_lock_and_get_active_state(sd);
ret = rkisp1_config_cif(isp, sd_state, mbus_type, mbus_flags);
if (ret)
goto out_unlock;
ret = rkisp1_isp_start(isp, sd_state, source_pad);
if (ret)
goto out_unlock;
ret = v4l2_subdev_call(rkisp1->source, video, s_stream, true );
if (ret) {
rkisp1_isp_stop(isp);
goto out_unlock;
}
out_unlock:
v4l2_subdev_unlock_state(sd_state);
return ret;
}
static int rkisp1_isp_subs_evt(struct v4l2_subdev *sd, struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
if (sub->type != V4L2_EVENT_FRAME_SYNC)
return -EINVAL;
/* V4L2_EVENT_FRAME_SYNC doesn't require an id, so zero should be set */
if (sub->id != 0 )
return -EINVAL;
return v4l2_event_subscribe(fh, sub, 0 , NULL);
}
static const struct media_entity_operations rkisp1_isp_media_ops = {
.link_validate = rkisp1_subdev_link_validate,
};
static const struct v4l2_subdev_video_ops rkisp1_isp_video_ops = {
.s_stream = rkisp1_isp_s_stream,
};
static const struct v4l2_subdev_core_ops rkisp1_isp_core_ops = {
.subscribe_event = rkisp1_isp_subs_evt,
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
};
static const struct v4l2_subdev_ops rkisp1_isp_ops = {
.core = &rkisp1_isp_core_ops,
.video = &rkisp1_isp_video_ops,
.pad = &rkisp1_isp_pad_ops,
};
static const struct v4l2_subdev_internal_ops rkisp1_isp_internal_ops = {
.init_state = rkisp1_isp_init_state,
};
int rkisp1_isp_register(struct rkisp1_device *rkisp1)
{
struct rkisp1_isp *isp = &rkisp1->isp;
struct media_pad *pads = isp->pads;
struct v4l2_subdev *sd = &isp->sd;
int ret;
isp->rkisp1 = rkisp1;
v4l2_subdev_init(sd, &rkisp1_isp_ops);
sd->internal_ops = &rkisp1_isp_internal_ops;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
sd->entity.ops = &rkisp1_isp_media_ops;
sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
sd->owner = THIS_MODULE;
strscpy(sd->name, RKISP1_ISP_DEV_NAME, sizeof (sd->name));
pads[RKISP1_ISP_PAD_SINK_VIDEO].flags = MEDIA_PAD_FL_SINK |
MEDIA_PAD_FL_MUST_CONNECT;
pads[RKISP1_ISP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
pads[RKISP1_ISP_PAD_SOURCE_VIDEO].flags = MEDIA_PAD_FL_SOURCE;
pads[RKISP1_ISP_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(&sd->entity, RKISP1_ISP_PAD_MAX, pads);
if (ret)
goto err_entity_cleanup;
ret = v4l2_subdev_init_finalize(sd);
if (ret)
goto err_subdev_cleanup;
ret = v4l2_device_register_subdev(&rkisp1->v4l2_dev, sd);
if (ret) {
dev_err(rkisp1->dev, "Failed to register isp subdev\n" );
goto err_subdev_cleanup;
}
return 0 ;
err_subdev_cleanup:
v4l2_subdev_cleanup(sd);
err_entity_cleanup:
media_entity_cleanup(&sd->entity);
isp->sd.v4l2_dev = NULL;
return ret;
}
void rkisp1_isp_unregister(struct rkisp1_device *rkisp1)
{
struct rkisp1_isp *isp = &rkisp1->isp;
if (!isp->sd.v4l2_dev)
return ;
v4l2_device_unregister_subdev(&isp->sd);
v4l2_subdev_cleanup(&isp->sd);
media_entity_cleanup(&isp->sd.entity);
}
/* ----------------------------------------------------------------------------
* Interrupt handlers
*/
static void rkisp1_isp_queue_event_sof(struct rkisp1_isp *isp)
{
struct v4l2_event event = {
.type = V4L2_EVENT_FRAME_SYNC,
};
event.u.frame_sync.frame_sequence = isp->frame_sequence;
v4l2_event_queue(isp->sd.devnode, &event);
}
irqreturn_t rkisp1_isp_isr(int irq, void *ctx)
{
struct device *dev = ctx;
struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
u32 status, isp_err;
if (!rkisp1->irqs_enabled)
return IRQ_NONE;
status = rkisp1_read(rkisp1, RKISP1_CIF_ISP_MIS);
if (!status)
return IRQ_NONE;
rkisp1_write(rkisp1, RKISP1_CIF_ISP_ICR, status);
/* Vertical sync signal, starting generating new frame */
if (status & RKISP1_CIF_ISP_V_START) {
rkisp1->isp.frame_sequence++;
rkisp1_isp_queue_event_sof(&rkisp1->isp);
if (status & RKISP1_CIF_ISP_FRAME) {
WARN_ONCE(1 , "irq delay is too long, buffers might not be in sync\n" );
rkisp1->debug.irq_delay++;
}
}
if (status & RKISP1_CIF_ISP_PIC_SIZE_ERROR) {
/* Clear pic_size_error */
isp_err = rkisp1_read(rkisp1, RKISP1_CIF_ISP_ERR);
if (isp_err & RKISP1_CIF_ISP_ERR_INFORM_SIZE)
rkisp1->debug.inform_size_error++;
if (isp_err & RKISP1_CIF_ISP_ERR_IS_SIZE)
rkisp1->debug.img_stabilization_size_error++;
if (isp_err & RKISP1_CIF_ISP_ERR_OUTFORM_SIZE)
rkisp1->debug.outform_size_error++;
rkisp1_write(rkisp1, RKISP1_CIF_ISP_ERR_CLR, isp_err);
} else if (status & RKISP1_CIF_ISP_DATA_LOSS) {
/* keep track of data_loss in debugfs */
rkisp1->debug.data_loss++;
}
if (status & RKISP1_CIF_ISP_FRAME) {
u32 isp_ris;
rkisp1->debug.complete_frames++;
/* New frame from the sensor received */
isp_ris = rkisp1_read(rkisp1, RKISP1_CIF_ISP_RIS);
if (isp_ris & RKISP1_STATS_MEAS_MASK)
rkisp1_stats_isr(&rkisp1->stats, isp_ris);
/*
* Then update changed configs . Some of them involve
* lot of register writes . Do those only one per frame .
* Do the updates in the order of the processing flow .
*/
rkisp1_params_isr(rkisp1);
}
return IRQ_HANDLED;
}
Messung V0.5 in Prozent C=96 H=97 G=96
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet am 2026-10-11)
¤
*© Formatika GbR, Deutschland